성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>windbg - x64 덤프 분석 시 메서드의 인자 또는 로컬 변수의 값을 확인하는 방법</h1> <p> 전에도 관련 주제로 글을 쓴 적이 있는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > x64 콜 스택 인자 추적과 windbg의 Child-SP, RetAddr, Args to Child 값 확인 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/10832'>http://www.sysnet.pe.kr/2/0/10832</a> windbg - x64 역어셈블 코드에서 닷넷 메서드 호출의 인자를 확인하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11348'>https://www.sysnet.pe.kr/2/0/11348</a> windbg - x64 SOS 확장의 !clrstack 명령어가 출력하는 Child SP 값의 의미 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11349'>https://www.sysnet.pe.kr/2/0/11349</a> </pre> <br /> 그때는 직접 RSP의 값을 구해가면서, 또는 Child-SP 칼럼을 이용하면서 인자 값 추적을 했는데 이번에는 windbg의 ".frame" 명령어를 이용해 간단하게 해결해 보겠습니다. 우선 실습 예제를 정하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // x64 + Release 빌드 using System; using System.IO; class Program { static void Main(string[] args) { string path = "c:\\temp\\test.txt"; int argLen = args.Length; if (argLen >= 1) { path = args[0]; } FileProcess(path); } private static void FileProcess(string filePath) { FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); Console.WriteLine(fs.Length); fs.Close(); } } </pre> <br /> 실행해서 예외가 발생했을 때 비주얼 스튜디오에서 "Call Stack" 창에 열거된 함수 목록 중,<br /> <br /> <img alt='stack_frame_dbg_1.png' src='/SysWebRes/bbs/stack_frame_dbg_1.png' /><br /> <br /> 예외 발생은 가장 상단의 WinIOError 메서드이지만, 가장 하단의 "Program.Main" 항목을 더블 클릭해서 해당 메서드를 호출한 시점의 문맥으로 전환했기 때문에 우측의 "Watch" 창에 "args", "argLen", "path" 변수가 정상적으로 값을 담게 됩니다.<br /> <br /> 만약 위의 상태에서 "Call Stack" 창의 "Program.FileProcess" 메서드를 더블 클릭하면 역시 그 호출 스택의 문맥으로 변환하므로,<br /> <br /> <img alt='stack_frame_dbg_2.png' src='/SysWebRes/bbs/stack_frame_dbg_2.png' /><br /> <br /> 현재 선택된 콜 스택 프레임을 가리키는 화살표가 표시되고 그와 함께 해당 프레임에 속하지 않은 인자, 지역 변수는 Watch 창에서 회색 처리됩니다.<br /> <br /> 바로 이런 식의 프레임 문맥 전환을 할 수 있는 windbg 명령어가 "<a target='tab' href='https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-frame--set-local-context-'>.frame</a>"입니다. (전환된 문맥을 다시 해제하려면 "<a target='tab' href='https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-cxr--display-context-record-'>.cxr</a>" 명령을 사용합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그럼 저렇게 예외가 발생했을 때 뜬 덤프를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp\ConsoleApp1\bin\Release> <a target='tab' href='http://www.sysnet.pe.kr/2/0/12051'>procdump</a> -e -ma -x "c:\temp" "C:\temp\ConsoleApp1\bin\Release\ConsoleApp1.exe" ProcDump v9.0 - Sysinternals process dump utility Copyright (C) 2009-2017 Mark Russinovich and Andrew Richards Sysinternals - www.sysinternals.com Process: ConsoleApp1.exe (3412) CPU threshold: n/a Performance counter: n/a Commit threshold: n/a Threshold seconds: n/a Hung window check: Disabled Log debug strings: Disabled Exception monitor: Unhandled Exception filter: [Includes] * [Excludes] Terminate monitor: Disabled Cloning type: Disabled Concurrent limit: n/a Avoid outage: n/a Number of dumps: 1 Dump folder: c:\temp\ Dump filename/mask: PROCESSNAME_YYMMDD_HHMMSS Queue to WER: Disabled Kill after dump: Disabled Press Ctrl-C to end monitoring without terminating the process. [10:02:06] Exception: 04242420 [10:02:06] Exception: E0434352.CLR Unhandled Exception: System.IO.FileNotFoundException: Could not find file 'c:\temp\test.txt'. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access) at Program.FileProcess(String filePath) in C:\temp\ConsoleApp1\bin\Release\ConsoleApp1\Program.cs:line 23 at Program.Main(String[] args) in C:\temp\ConsoleApp1\bin\Release\ConsoleApp1\Program.cs:line 18 [10:02:06] Unhandled: E0434352.CLR [10:02:06] Dump 1 initiated: c:\temp\ConsoleApp1.exe_191202_100206.dmp [10:02:07] Dump 1 writing: Estimated dump file size is 96 MB. [10:02:07] Dump 1 complete: 96 MB written in 0.1 seconds [10:02:07] Dump count reached. </pre> <br /> windbg에서 열어, 덤프 생성 시점의 Native call stack과 Managed call stack을 각각 다음과 같이 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'><a target='tab' href='https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/k--kb--kc--kd--kp--kp--kv--display-stack-backtrace-'>k</a></span> # Child-SP RetAddr Call Site 00 00000053`6eb3e780 00007ff9`faeba3f1 KERNELBASE!RaiseException+0x69 01 00000053`6eb3e860 00007ff9`faebb864 clr!RaiseTheExceptionInternalOnly+0x31f 02 00000053`6eb3e980 00007ff9`f7bbbeac clr!IL_Throw+0x114 03 00000053`6eb3eb30 00007ff9`f7c0701a mscorlib_ni+0x53beac 04 00000053`6eb3eb80 00007ff9`f7be3f1d mscorlib_ni+0x58701a 05 00000053`6eb3ec50 00007ff9`9b950922 mscorlib_ni+0x563f1d 06 <span style='color: blue; font-weight: bold'>00000053`6eb3ed00</span> 00007ff9`9b9508c4 0x00007ff9`9b950922 07 00000053`6eb3ed40 00007ff9`faea6c53 0x00007ff9`9b9508c4 08 00000053`6eb3ed70 00007ff9`faea6b68 clr!CallDescrWorkerInternal+0x83 09 00000053`6eb3edb0 00007ff9`faea73d0 clr!CallDescrWorkerWithHandler+0x4e 0a 00000053`6eb3edf0 00007ff9`fb027002 clr!MethodDescCallSite::CallTargetWorker+0x102 0b 00000053`6eb3eef0 00007ff9`fb0279c3 clr!RunMain+0x25f 0c 00000053`6eb3f0d0 00007ff9`fb027877 clr!Assembly::ExecuteMainMethod+0xb7 0d 00000053`6eb3f3c0 00007ff9`fb0271c3 clr!SystemDomain::ExecuteMainMethod+0x643 0e 00000053`6eb3f9c0 00007ff9`fb027141 clr!ExecuteEXE+0x3f 0f 00000053`6eb3fa30 00007ff9`fb0285b4 clr!_CorExeMainInternal+0xb2 10 00000053`6eb3fac0 00007ff9`fc278c01 clr!CorExeMain+0x14 11 00000053`6eb3fb00 00007ff9`fd9ea56c mscoreei!CorExeMain+0x112 12 00000053`6eb3fb60 00007ffa`0b0f7bd4 mscoree!CorExeMain_Exported+0x6c 13 00000053`6eb3fb90 00007ffa`0c44ced1 kernel32!BaseThreadInitThunk+0x14 14 00000053`6eb3fbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 0:000> <span style='color: blue; font-weight: bold'>.loadby sos clr</span> 0:000> <span style='color: blue; font-weight: bold'>!clrstack</span> OS Thread Id: 0xa820 (0) Child SP IP Call Site 000000536eb3ea48 00007ffa09f8a839 [HelperMethodFrame: 000000536eb3ea48] 000000536eb3eb30 00007ff9f7bbbeac System.IO.__Error.WinIOError(Int32, System.String) 000000536eb3eb80 00007ff9f7c0701a System.IO.FileStream.Init(System.String, System.IO.FileMode, System.IO.FileAccess, Int32, Boolean, System.IO.FileShare, Int32, System.IO.FileOptions, SECURITY_ATTRIBUTES, System.String, Boolean, Boolean, Boolean) 000000536eb3ec50 00007ff9f7be3f1d System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess) <span style='color: blue; font-weight: bold'>000000536eb3ed00</span> 00007ff99b950922 Program.FileProcess(System.String) [c:\temp\ConsoleApp1\Program.cs @ 23] 000000536eb3ed40 00007ff99b9508c4 Program.Main(System.String[]) [c:\temp\ConsoleApp1\Program.cs @ 18] 000000536eb3ef58 00007ff9faea6c53 [GCFrame: 000000536eb3ef58] </pre> <br /> .frame 명령어는 native call stack의 '#' 칼럼 값을 필요로 하기 때문에 닷넷 메서드의 경우 k 명령어의 "RetAddr" 칼럼과 "!clrstack"의 "IP" 값을 이용해 적절하게 매칭해 구해야 합니다. (또는 간단하게 windbg가 자동으로 구해서 보여주는 Child-SP 값을 비교하면 됩니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > # Child-SP RetAddr Call Site ...[생략]... 02 00000053`6eb3e980 00007ff9`f7bbbeac clr!IL_Throw+0x114 03 00000053`6eb3eb30 00007ff9`f7c0701a mscorlib_ni+0x53beac ==> System.IO.__Error.WinIOError(Int32, System.String)) 04 00000053`6eb3eb80 00007ff9`f7be3f1d mscorlib_ni+0x58701a ==> System.IO.FileStream.Init 05 00000053`6eb3ec50 00007ff9`9b950922 mscorlib_ni+0x563f1d ==> System.IO.FileStream..ctor 06 <span style='color: blue; font-weight: bold'>00000053`6eb3ed00</span> 00007ff9`9b9508c4 0x00007ff9`9b950922 ==> Program.FileProcess 07 00000053`6eb3ed40 00007ff9`faea6c53 0x00007ff9`9b9508c4 ==> Program.Main </pre> <br /> 따라서, Program.FileProcess 메서드의 문맥으로 전환하고 싶다면 다음과 같이 명령어를 수행하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>.frame /c /r 6</span> 06 00000053`6eb3ed00 00007ff9`9b9508c4 0x00007ff9`9b950922 rax=0000000000000005 rbx=000000536eb3ee88 rcx=0000000000000050 rdx=000000536eb3e400 rsi=00000167136c2e20 rdi=00000167136c6008 <span style='color: blue; font-weight: bold'>rip=00007ff99b950922</span> <span style='color: blue; font-weight: bold'>rsp=000000536eb3ed00</span> rbp=000000536eb3ed90 r8=00007ff9fb06197a r9=0000000000000000 r10=0000000000000000 r11=0000000011a81750 r12=000000536eb3f110 r13=000000536eb3ee10 r14=000000536eb3ee88 r15=0000000000000004 iopl=0 nv up ei pl nz na pe nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000200 00007ff9`9b950922 488bcf mov rcx,rdi </pre> <br /> 이렇게 문맥 전환이 되었을 때 가장 도움이 되는 값은 바로 rsp와 rip입니다. rip의 경우 sos.dll의 "!u" 명령어와 결합하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!u 00007ff99b950922</span> Normal JIT generated code Program.FileProcess(System.String) Begin 00007ff99b9508f0, size 58 c:\temp\ConsoleApp1\Program.cs @ 22: 00007ff9`9b9508f0 57 push rdi 00007ff9`9b9508f1 56 push rsi 00007ff9`9b9508f2 4883ec28 sub rsp,28h 00007ff9`9b9508f6 488bf1 mov rsi,rcx 00007ff9`9b9508f9 48b9384d72f7f97f0000 mov rcx,offset mscorlib_ni+0xa4d38 (00007ff9`f7724d38) (MT: System.IO.FileStream) 00007ff9`9b950903 e868c9565f call clr!JIT_NewCrossContext_Portable (00007ff9`faebd270) 00007ff9`9b950908 488bf8 mov rdi,rax 00007ff9`9b95090b 488bcf mov rcx,rdi 00007ff9`9b95090e 488bd6 mov rdx,rsi 00007ff9`9b950911 41b803000000 mov r8d,3 00007ff9`9b950917 41b901000000 mov r9d,1 00007ff9`9b95091d e876f9ffff call 00007ff9`9b950298 c:\temp\ConsoleApp1\Program.cs @ 23: <span style='color: blue; font-weight: bold'>>>> 00007ff9`9b950922 488bcf mov rcx,rdi</span> 00007ff9`9b950925 e85627eeff call 00007ff9`9b833080 00007ff9`9b95092a 488bc8 mov rcx,rax 00007ff9`9b95092d e8ae20b25c call mscorlib_ni+0xdf29e0 (00007ff9`f84729e0) (System.Console.WriteLine(Int64), mdToken: 0000000006000b77) c:\temp\ConsoleApp1\Program.cs @ 24: 00007ff9`9b950932 488bcf mov rcx,rdi 00007ff9`9b950935 48b8d030839bf97f0000 mov rax,7FF99B8330D0h 00007ff9`9b95093f 4883c428 add rsp,28h 00007ff9`9b950943 5e pop rsi 00007ff9`9b950944 5f pop rdi 00007ff9`9b950945 48ffe0 jmp rax </pre> <br /> 보는 바와 같이 해당 메서드의 전체 코드를 덤프하고 예외가 발생한 시점의 명령어(위에서는 call 00007ff9`9b950298) 바로 다음 명령어의 위치를 ">>>" 기호로 친절하게 알려줍니다.<br /> <br /> 또한 rsp 값(위에서는 000000536eb3ed00)을 통해 해당 메서드가 수행되기까지 쌓인 스택의 내용을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dq /c1 @rsp</span> 00000053`6eb3ed00 00000167`136c6008 00000053`6eb3ed08 00000167`136c2e20 00000053`6eb3ed10 00000000`00000003 00000053`6eb3ed18 00000000`00000001 00000053`6eb3ed20 00000053`6eb3ee10 00000053`6eb3ed28 00000167`136c2e08 00000053`6eb3ed30 00000053`6eb3ef58 00000053`6eb3ed38 00007ff9`9b9508c4 00000053`6eb3ed40 00000167`136c2e20 00000053`6eb3ed48 00000000`00000000 00000053`6eb3ed50 00000167`136c38dc 00000053`6eb3ed58 00000053`6eb3eb30 00000053`6eb3ed60 00000053`6eb3ee58 00000053`6eb3ed68 00007ff9`faea6c53 00000053`6eb3ed70 00000167`136c2e08 00000053`6eb3ed78 00007ff9`9b844148 </pre> <br /> 재미있는 것은, sos.dll의 "!dso" 확장 명령어는 (현재 문맥의 rsp를 인지하지 않고) 덤프 시점의 rsp를 기준으로 닷넷 관련 객체를 덤프한다는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!dso</span> OS Thread Id: 0xa820 (0) RSP/REG Object Name rsi 00000167136c2e20 System.String c:\temp\test.txt rdi 00000167136c6008 System.IO.FileStream 000000536EB3E798 00000167136cc240 System.RuntimeType 000000536EB3E870 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3E888 00000167136cc1b0 System.Text.StringBuilder 000000536EB3E898 00000167136cc1b0 System.Text.StringBuilder 000000536EB3E8D8 00000167136c50b8 System.Globalization.CultureInfo 000000536EB3E8E0 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3E908 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3E960 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3E968 00000167136c7c38 System.Object[] (System.Object[]) 000000536EB3E970 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3E980 00000167136c50b8 System.Globalization.CultureInfo 000000536EB3E988 00000167136cc240 System.RuntimeType 000000536EB3E9A0 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3E9C0 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3E9D8 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3EA10 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3EA28 00000167136c50b8 System.Globalization.CultureInfo 000000536EB3EA30 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3EA88 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3EA90 00000167136c7c38 System.Object[] (System.Object[]) 000000536EB3EA98 00000167136c7c58 System.IO.FileNotFoundException 000000536EB3EAA0 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3EB30 00000167136c50b8 System.Globalization.CultureInfo 000000536EB3EB38 00000167136cc070 System.String Could not find file '{0}'. 000000536EB3EB40 00000167136c7c38 System.Object[] (System.Object[]) 000000536EB3EB70 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3EB88 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3EC10 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3EC18 00000167136c6008 System.IO.FileStream 000000536EB3EC38 00000167136c7690 System.String test.txt 000000536EB3EC50 00000167136c6008 System.IO.FileStream 000000536EB3EC58 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3ECA0 00000167136c7690 System.String test.txt 000000536EB3ECC0 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3ECD0 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3ECD8 00000167136c6008 System.IO.FileStream <span style='color: blue; font-weight: bold'>000000536EB3ED00 00000167136c6008 System.IO.FileStream</span> 000000536EB3ED08 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3ED28 00000167136c2e08 System.String[] 000000536EB3ED40 00000167136c2e20 System.String c:\temp\test.txt 000000536EB3ED70 00000167136c2e08 System.String[] 000000536EB3EE58 00000167136c2e08 System.String[] 000000536EB3F0E0 00000167136c2e08 System.String[] 000000536EB3F0E8 00000167136c2e08 System.String[] </pre> <br /> 보는 바와 같이 .frame 명령어로 인해 rsp == "000000536EB3ED00"로 전환이 되었지만, !dso는 그것에 상관없이 스택 덤프를 하고 있으므로 중간에 "FileNotFoundException"이나 파일 경로를 담고 있는 System.String 객체등을 보여주게 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 아쉽게도 Program.FileProcess 메서드의 경우 rsp를 통해 직접적으로 알 수 있는 인자나 로컬 변수가 없습니다. 반면 운이 좋다면 System.IO.FileStream의 생성자처럼,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!u 00007ff9f7be3f1d</span> preJIT generated code System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess) Begin 00007ff9f7be3ea0, size 8b 00007ff9`f7be3ea0 55 push rbp 00007ff9`f7be3ea1 4157 push r15 00007ff9`f7be3ea3 4156 push r14 00007ff9`f7be3ea5 57 push rdi 00007ff9`f7be3ea6 56 push rsi 00007ff9`f7be3ea7 53 push rbx 00007ff9`f7be3ea8 4883ec78 sub rsp,78h 00007ff9`f7be3eac 488dac24a0000000 lea rbp,[rsp+0A0h] 00007ff9`f7be3eb4 488bf9 mov rdi,rcx 00007ff9`f7be3eb7 488bf2 mov rsi,rdx 00007ff9`f7be3eba 418bd8 mov ebx,r8d 00007ff9`f7be3ebd 458bf1 mov r14d,r9d 00007ff9`f7be3ec0 488bce mov rcx,rsi 00007ff9`f7be3ec3 90 nop 00007ff9`f7be3ec4 e8a72b0200 call mscorlib_ni+0x586a70 (00007ff9`f7c06a70) (System.IO.Path.GetFileName(System.String), mdToken: 000000000600191a) 00007ff9`f7be3ec9 4c8bf8 mov r15,rax 00007ff9`f7be3ecc b901000000 mov ecx,1 00007ff9`f7be3ed1 90 nop 00007ff9`f7be3ed2 e8492c0200 call mscorlib_ni+0x586b20 (00007ff9`f7c06b20) (System.IO.FileStream.GetSecAttrs(System.IO.FileShare), mdToken: 0000000006001854) 00007ff9`f7be3ed7 33c9 xor ecx,ecx <span style='color: blue; font-weight: bold'>00007ff9`f7be3ed9 894c2420 mov dword ptr [rsp+20h],ecx 00007ff9`f7be3edd 894c2428 mov dword ptr [rsp+28h],ecx 00007ff9`f7be3ee1 c744243001000000 mov dword ptr [rsp+30h],1 00007ff9`f7be3ee9 c744243800100000 mov dword ptr [rsp+38h],1000h 00007ff9`f7be3ef1 894c2440 mov dword ptr [rsp+40h],ecx 00007ff9`f7be3ef5 4889442448 mov qword ptr [rsp+48h],rax 00007ff9`f7be3efa 4c897c2450 mov qword ptr [rsp+50h],r15 00007ff9`f7be3eff 894c2458 mov dword ptr [rsp+58h],ecx 00007ff9`f7be3f03 894c2460 mov dword ptr [rsp+60h],ecx 00007ff9`f7be3f07 894c2468 mov dword ptr [rsp+68h],ecx</span> 00007ff9`f7be3f0b 488bcf mov rcx,rdi 00007ff9`f7be3f0e 488bd6 mov rdx,rsi 00007ff9`f7be3f11 448bc3 mov r8d,ebx 00007ff9`f7be3f14 458bce mov r9d,r14d 00007ff9`f7be3f17 ff156310b4ff call qword ptr [mscorlib_ni+0xa4f80 (00007ff9`f7724f80)] <span style='color: blue; font-weight: bold'>>>> 00007ff9`f7be3f1d 90 nop</span> 00007ff9`f7be3f1e 488d65d8 lea rsp,[rbp-28h] 00007ff9`f7be3f22 5b pop rbx 00007ff9`f7be3f23 5e pop rsi 00007ff9`f7be3f24 5f pop rdi 00007ff9`f7be3f25 415e pop r14 00007ff9`f7be3f27 415f pop r15 00007ff9`f7be3f29 5d pop rbp 00007ff9`f7be3f2a c3 ret </pre> <br /> "rsp + [offset]"의 연산을 하고 있는 코드가 있다면 인자 값이나 로컬 변수의 값 추적이 쉬워집니다. 즉, 아래와 같은 식의 연산을 보게 되면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > mov dword ptr [rsp+20h],ecx </pre> <br /> 우선 저 메서드가 수행될 시점의 rsp 값을 알아야 하는데 바로 이럴 때 ".frame" 명령어를 사용하면 되는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>.cxr</span> Resetting default scope 0:000> <span style='color: blue; font-weight: bold'>.frame /c /r 5</span> 05 00000053`6eb3ec50 00007ff9`9b950922 mscorlib_ni+0x563f1d rax=0000000000000005 rbx=0000000000000003 rcx=0000000000000050 rdx=000000536eb3e400 rsi=00000167136c2e20 rdi=00000167136c6008 <span style='color: blue; font-weight: bold'>rip=00007ff9f7be3f1d</span> <span style='color: blue; font-weight: bold'>rsp=000000536eb3ec50</span> rbp=000000536eb3ecf0 r8=00007ff9fb06197a r9=0000000000000000 r10=0000000000000000 r11=0000000011a81750 r12=000000536eb3f110 r13=000000536eb3ee10 r14=0000000000000001 r15=00000167136c7690 iopl=0 nv up ei pl nz na pe nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000200 mscorlib_ni+0x563f1d: 00007ff9`f7be3f1d 90 nop </pre> <br /> 이로 인해 결국 다음의 코드 수행으로 인해 저장된 값들은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ff9`f7be3ed9 894c2420 mov dword ptr [rsp+20h],ecx 00007ff9`f7be3edd 894c2428 mov dword ptr [rsp+28h],ecx 00007ff9`f7be3ee1 c744243001000000 mov dword ptr [rsp+30h],1 00007ff9`f7be3ee9 c744243800100000 mov dword ptr [rsp+38h],1000h 00007ff9`f7be3ef1 894c2440 mov dword ptr [rsp+40h],ecx 00007ff9`f7be3ef5 4889442448 mov qword ptr [rsp+48h],rax 00007ff9`f7be3efa 4c897c2450 mov qword ptr [rsp+50h],r15 00007ff9`f7be3eff 894c2458 mov dword ptr [rsp+58h],ecx 00007ff9`f7be3f03 894c2460 mov dword ptr [rsp+60h],ecx 00007ff9`f7be3f07 894c2468 mov dword ptr [rsp+68h],ecx </pre> <br /> 이렇게 간단하게 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dq /c1 rsp+0x20 LA</span> 00000053`6eb3ec70 00000000`00000000 00000053`6eb3ec78 00000000`00000000 00000053`6eb3ec80 00000000`00000001 00000053`6eb3ec88 00000000`00001000 00000053`6eb3ec90 00000000`00000000 00000053`6eb3ec98 00000000`00000000 00000053`6eb3eca0 00000167`136c7690 00000053`6eb3eca8 00000000`00000000 00000053`6eb3ecb0 00000000`00000000 00000053`6eb3ecb8 00000167`00000000 </pre> <br /> 실제로 FileStream 생성자를 .NET Reflector로 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [SecuritySafeCritical] public FileStream(string path, FileMode mode, FileAccess access) : this(path, mode, access, FileShare.Read, 4096, FileOptions.None, Path.GetFileName(path), bFromProxy: false) { } </pre> <br /> 7번째 인자(rsp+48h)가 Path.GetFileName(path)이므로 System.String 참조 객체가 전달되는데, 그렇다면 당연히 sos.dll의 "!do" 확장 명령어로 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!do 00000167`136c7690</span> Name: System.String MethodTable: 00007ff9f76a59c0 EEClass: 00007ff9f7682ec0 Size: 42(0x2a) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll String: test.txt Fields: MT Field Offset Type VT Attr Value Name 00007ff9f76a85a0 4000285 8 System.Int32 1 instance 8 m_stringLength 00007ff9f76a6838 4000286 c System.Char 1 instance 74 m_firstChar 00007ff9f76a59c0 400028a e8 System.String 0 shared static Empty >> Domain:Value 0000016711a15f20:NotInit << </pre> <br /> 위와 같이 "test.txt" 파일명이 전달된 것을 확인할 수 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1638
(왼쪽의 숫자를 입력해야 합니다.)