성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
글쓰기
제목
이름
암호
전자우편
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'>x86 메모리 덤프 분석 시 닷넷 메서드의 호출 인자 값 확인</h1> <p> x64 관련해서는 기록해 둔 것이 있는데,<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='http://www.sysnet.pe.kr/2/0/11348'>http://www.sysnet.pe.kr/2/0/11348</a> windbg - x64 SOS 확장의 !clrstack 명령어가 출력하는 Child SP 값의 의미 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11349'>http://www.sysnet.pe.kr/2/0/11349</a> </pre> <br /> x86이 없군요. ^^<br /> <br /> 예를 들면서 해볼까요? ^^ C# 프로그램을 작성하다 보면 EventWaitHandle 타입으로 WaitOne을 호출하며 대기하는 경우가 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Threading; namespace ConsoleApp1 { class Program { static void Main(string[] args) { ExitOrSleep(1000 * 50); } private static void ExitOrSleep(int timeout) { EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset); <span style='color: blue; font-weight: bold'>ewh.WaitOne(timeout, false);</span> } } } </pre> <br /> 바로 이 WaitOne을 호출했을 때 메모리 덤프를 뜬 것을 분석 시 timeout으로 주어진 시간을 찾는다고 가정해 봅니다.<br /> <br /> 다음은 WaitOne 호출에 따른 콜 스택을 보여줍니다.<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'>kv</span> # ChildEBP RetAddr <span style='color: blue; font-weight: bold'>Args to Child</span> 00 001beb64 77061293 00000001 001bef10 00000000 ntdll!NtWaitForMultipleObjects+0xc (FPO: [5,0,0]) 01 001becf8 72a0ff96 00000001 001bef10 00000001 KERNELBASE!WaitForMultipleObjectsEx+0x103 (FPO: [SEH]) <span style='color: blue; font-weight: bold'>02 001bed48 72a0fcd8 00000001 0000c350 00000001 clr!WaitForMultipleObjectsEx_SO_TOLERANT+0x3c (FPO: [Non-Fpo])</span> 03 001bedd4 72a0fdc9 00000001 001bef10 00000001 clr!Thread::DoAppropriateWaitWorker+0x237 (FPO: [5,25,0]) 04 001bee40 72a1011f 00000001 001bef10 00000001 clr!Thread::DoAppropriateWait+0x64 (FPO: [Non-Fpo]) 05 001bef48 70df9911 00000000 00000000 045e247c clr!WaitHandleNative::CorWaitOneNative+0x163 (FPO: [Non-Fpo]) <span style='color: blue; font-weight: bold'>06 001bef5c 70df98d8 00000000 0000c350 00000000 mscorlib_ni+0x429911</span> 07 001bef78 02990474 00000000 00000000 001bef94 mscorlib_ni+0x4298d8 WARNING: Frame IP not in any known module. Following frames may be wrong. 08 001bef88 7296eb16 0275e020 001befe8 72976e84 0x2990474 09 001bef94 72976e84 001bf024 001befd8 72b07020 clr!CallDescrWorkerInternal+0x34 0a 001befe8 729782f4 00000000 045e2440 00000000 clr!CallDescrWorkerWithHandler+0x6b (FPO: [Non-Fpo]) 0b 001bf050 72a2d34c 001bf144 57a56079 02704d0c clr!MethodDescCallSite::CallTargetWorker+0x16a (FPO: [Non-Fpo]) 0c 001bf17c 72a2d2b0 001bf1a0 00000000 57a56095 clr!RunMain+0x1ad (FPO: [Non-Fpo]) 0d 001bf3f0 72a2da26 00000000 57a56505 00020000 clr!Assembly::ExecuteMainMethod+0x124 (FPO: [1,149,0]) 0e 001bf8e8 72a2dac9 57a56845 00000000 00000000 clr!SystemDomain::ExecuteMainMethod+0x631 (FPO: [0,311,0]) 0f 001bf940 72a2d426 57a56885 00000000 72a50320 clr!ExecuteEXE+0x4c (FPO: [Non-Fpo]) 10 001bf980 72a5033c 57a568b9 00000000 72a50320 clr!_CorExeMainInternal+0xdc (FPO: [Non-Fpo]) 11 001bf9bc 737dd91b a2f79cdd 739e4df0 737dd8a0 clr!_CorExeMain+0x4d (FPO: [Non-Fpo]) 12 001bf9fc 739de879 739e4df0 737d0000 2bf7ae78 mscoreei!_CorExeMain+0x10e (FPO: [0,10,4]) 13 001bfa10 739e4df8 739e4df0 77188654 003a9000 mscoree!ShellShim__CorExeMain+0xa9 (FPO: [Non-Fpo]) 14 001bfa18 77188654 003a9000 77188630 dc4d9cdf mscoree!_CorExeMain_Exported+0x8 (FPO: [0,0,4]) 15 001bfa2c 77594a77 003a9000 b6694afe 00000000 kernel32!BaseThreadInitThunk+0x24 (FPO: [Non-Fpo]) 16 001bfa74 77594a47 ffffffff 775b9eee 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH]) 17 001bfa84 00000000 739e4df0 003a9000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo]) </pre> <br /> x86의 경우에는 WIN32 API가 따르는 기본 호출 규약인 __stdcall이 ecx, edx 등을 통한 레지스터를 이용하지 않기 때문에 스택에 값이 잘 남게 됩니다. 이 때문에 kv 명령어로 "Args to Child" 항목을 출력하면 넘겨진 인자 값을 쉽게 확인하는 수 있는 경우가 많습니다. 가령 이번 예제에서 50,000 (0xc350) 값을 WaitOne에 전달했는데 위에서 보는 바와 같이 2개의 호출 스택에서 그 값을 직접 확인할 수 있습니다. (실제로 x86 덤프에서 WaitOne에 전달한 시간을 알고 싶다면 clr!WaitForMultipleObjectsEx_SO_TOLERANT 함수의 "Args to Child"에 나타난 2번째 인자를 확인하면 거의 잘 맞을 것입니다.)<br /> <br /> 이런 찍는 방법 말고 ^^ 정식으로 한번 파고들어 보겠습니다. 우선, Native 호출 스택은 낯설을 테니 닷넷 호출 스택으로 출발해 보겠습니다. <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'>!clrstack</span> OS Thread Id: 0x92ec (0) Child SP IP Call Site 001bee74 7759ed3c [HelperMethodFrame_1OBJ: 001bee74] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean) 001bef58 70df9911 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean) 001bef70 70df98d8 System.Threading.WaitHandle.WaitOne(Int32, Boolean) 001bef84 <span style='color: blue; font-weight: bold'>02990474</span> ConsoleApp1.Program.Main(System.String[]) [E:\ConsoleApp1\ConsoleApp1\Program.cs @ 16] 001bf0f8 7296eb16 [GCFrame: 001bf0f8] </pre> <br /> Main 함수에서 WaitOne을 호출하고 있는데 이때 Main 함수의 IP 주솟값이 02990474입니다. 이것은 WaitOne 호출이 반환되면 시작할 IP 주소를 의미하는데 따라서 WaitOne을 호출할 당시의 명령어를 확인하고 싶다면 다음과 같은 식으로 할 수 있습니다.<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'>uf 02990474 -20</span> 02990454 0470 add al,70h 02990456 8bf0 mov esi,eax 02990458 6a01 push 1 0299045a 8bce mov ecx,esi 0299045c 33d2 xor edx,edx 0299045e e8c9ec356e call mscorlib_ni+0x31f12c (70cef12c) 02990463 6a00 <span style='color: blue; font-weight: bold'>push 0</span> 02990465 8bce <span style='color: blue; font-weight: bold'>mov ecx,esi</span> 02990467 ba50c30000 <span style='color: blue; font-weight: bold'>mov edx,0C350h</span> 0299046c 8b01 mov eax,dword ptr [ecx] 0299046e 8b402c mov eax,dword ptr [eax+2Ch] 02990471 ff5004 <span style='color: blue; font-weight: bold'>call dword ptr [eax+4]</span> <span style='color: blue; font-weight: bold'>02990474 5e pop esi</span> 02990475 5d pop ebp 02990476 c3 ret </pre> <br /> 이를 기반으로 WaitOne의 signature와 함께,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public virtual bool WaitOne(int millisecondsTimeout, bool exitContext); </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;' > mov ecx, esi = EventWaitHandle 인스턴스의 주솟값 mov edx, 0C350h = millisecondsTimeout push 0 = exitContext </pre> <br /> 그런데 벌써 시간 값이 0x0c350 상수로 나왔습니다. 그럼 여기서 끝낼 수도 있겠지만 대개의 경우 변숫값으로도 전달하는 경우도 많기 때문에 그런 상황을 가정하고 좀 더 파헤쳐 보겠습니다. 일단, 이번 함수 호출에서는 스택 상으로 인자 값을 확인할 방법이 없습니다. 왜냐하면 millisecondsTimeout 값이 volatile 레지스터인 edx로 전달되기 때문에 현재 CPU 상태에서 edx의 값이 다른 값으로 덮어써졌을 확률이 높습니다.<br /> <br /> 다음 함수로 넘어가 이번엔 WaitOne에서 InternalWaitOne 함수를 호출하는 상황을 보겠습니다.<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'>uf 70df98d8 -20</span> mscorlib_ni+0x4298b8: 70df98b8 0f8c429ac000 jl mscorlib_ni+0x1033300 (71a03300) Branch mscorlib_ni+0x4298be: 70df98be 8b7108 mov esi,dword ptr [ecx+8] 70df98c1 8bc2 mov eax,edx 70df98c3 c1f81f sar eax,1Fh 70df98c6 50 <span style='color: blue; font-weight: bold'>push eax</span> 70df98c7 52 <span style='color: blue; font-weight: bold'>push edx</span> 70df98c8 0fb65110 <span style='color: blue; font-weight: bold'>movzx edx,byte ptr [ecx+10h]</span> 70df98cc 0fb64508 movzx eax,byte ptr [ebp+8] 70df98d0 50 <span style='color: blue; font-weight: bold'>push eax</span> 70df98d1 8bce <span style='color: blue; font-weight: bold'>mov ecx,esi</span> 70df98d3 e818000000 <span style='color: blue; font-weight: bold'>call mscorlib_ni+0x4298f0 (70df98f0)</span> <span style='color: blue; font-weight: bold'>70df98d8 25ff000000 and eax,0FFh</span> 70df98dd 59 pop ecx 70df98de 5e pop esi 70df98df 5d pop ebp 70df98e0 c20400 ret 4 ...[생략]... </pre> <br /> 현재 "call mscorlib_ni+0x4298f0 (70df98f0)" 호출을 한 상태이고 해당 호출이 완료되면 70df98d8의 "and eax, 0FFh"를 실행할 예정입니다. 즉, 70df98f0 주소의 함수는 InternalWaitOne입니다. .NET Reflector로 InternalWaitOne의 signature를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal static bool InternalWaitOne(SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) </pre> <br /> 4개의 인자를 전달받고 있습니다. 그런데... 이상하군요. 위의 코드에 보면 push eax, push edx,..., push eax로 총 3번의 push와 함께 ecx, edx에 값을 설정하고 있습니다. 그렇다면 5개의 인자여야 하는데... 음... 여기서 해석이 안되는군요. (혹시 아시는 분은 덧글 부탁드립니다.) 하지만 일단 4개라 가정하고 어셈블리 코드에서 전달되는 인자의 값을 대략 다음과 같이 유추할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > push eax ; hasThreadAffinity push edx ; exitContext push eax ; ????? mov ecx, esi ; waitableSafeHandle movzx edx, byte ptr [ecx + 10h] ; millisecondsTimeout </pre> <br /> 우리가 알고 싶은 [ecx + 10h]는 이번에도 스택에 저장되어 있지 않으므로 값을 알아낼 수 없습니다. (ecx + 10h과 같은 연산인 걸로 봐서 아마도 ecx가 스택의 어느 지점을 가리키는 것이 아닌가 생각되지만 이를 알아내기 위해서는 70df98d8 단계까지의 코드를 해석해야 하므로 분석이 더뎌질 수 있습니다.) 따라서 이번에도 다시 InternalWaitOne으로 넘어갑니다. <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'>uf 70df9911 -21</span> mscorlib_ni+0x4298f0: 70df98f0 55 push ebp 70df98f1 8bec mov ebp,esp 70df98f3 56 push esi 70df98f4 90 nop 70df98f5 85c9 test ecx,ecx 70df98f7 0f84439ac000 je mscorlib_ni+0x1033340 (71a03340) Branch mscorlib_ni+0x4298fd: 70df98fd 81e2ff000000 and edx,0FFh 70df9903 52 <span style='color: blue; font-weight: bold'>push edx</span> 70df9904 0fb64508 movzx eax,byte ptr [ebp+8] 70df9908 50 <span style='color: blue; font-weight: bold'>push eax</span> 70df9909 8b550c <span style='color: blue; font-weight: bold'>mov edx,dword ptr [ebp+0Ch]</span> 70df990c e83f16efff <span style='color: blue; font-weight: bold'>call mscorlib_ni+0x31af50 (70ceaf50)</span> <span style='color: blue; font-weight: bold'>70df9911 8bf0 mov esi,eax</span> 70df9913 b901000000 mov ecx,1 70df9918 ba9b000000 mov edx,9Bh 70df991d e81607efff call mscorlib_ni+0x31a038 (70cea038) 70df9922 80b8380d000000 cmp byte ptr [eax+0D38h],0 70df9929 0f853f9ac000 jne mscorlib_ni+0x103336e (71a0336e) Branch </pre> <br /> InternalWaitOne에서 호출하는 System.Threading.WaitHandle.WaitOneNative의 signature는 InternalWaitOne과 동일합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [MethodImpl(MethodImplOptions.InternalCall), SecurityCritical] private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext); </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;' > ecx ; InternalWaitOne에서 넘긴 ecx를 그대로 전달 mov edx, dword ptr [ebp + 0ch] ; millisecondsTimeout and edx,0FFh push edx ; hasThreadAffinity movzx eax,byte ptr [ebp+8] push eax ; exitContext </pre> <br /> 드디어 나왔습니다. 이번에는 ebp + 0ch에 millisecondsTimeout 값이 담겨 있습니다. 따라서 InternalWaitOne 호출 당시의 ebp 값을 알아내면 됩니다. 우리가 살펴본 InternalWaitOne에 대해 !clrstack에서 살펴보면 IP 주솟값을 얻을 수 있었는데요.<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'>!clrstack</span> OS Thread Id: 0x92ec (0) Child SP IP Call Site 001bee74 7759ed3c [HelperMethodFrame_1OBJ: 001bee74] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean) 001bef58 <span style='color: blue; font-weight: bold'>70df9911</span> System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean) 001bef70 70df98d8 System.Threading.WaitHandle.WaitOne(Int32, Boolean) 001bef84 02990474 ConsoleApp1.Program.Main(System.String[]) [E:\ConsoleApp1\ConsoleApp1\Program.cs @ 16] 001bf0f8 7296eb16 [GCFrame: 001bf0f8] </pre> <br /> 전에 설명한 대로 70df9911 값은 InternalWaitOne이 WaitOneNative를 호출하고 돌아왔을 때 실행을 계속할 주소였습니다. 이를 염두에 두고 kv 호출 스택을 보면,<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'>kv</span> # <span style='color: blue; font-weight: bold'>ChildEBP</span> <span style='color: blue; font-weight: bold'>RetAddr</span> Args to Child 00 001beb64 77061293 00000001 001bef10 00000000 ntdll!NtWaitForMultipleObjects+0xc (FPO: [5,0,0]) 01 001becf8 72a0ff96 00000001 001bef10 00000001 KERNELBASE!WaitForMultipleObjectsEx+0x103 (FPO: [SEH]) 02 001bed48 72a0fcd8 00000001 0000c350 00000001 clr!WaitForMultipleObjectsEx_SO_TOLERANT+0x3c (FPO: [Non-Fpo]) 03 001bedd4 72a0fdc9 00000001 001bef10 00000001 clr!Thread::DoAppropriateWaitWorker+0x237 (FPO: [5,25,0]) 04 001bee40 72a1011f 00000001 001bef10 00000001 clr!Thread::DoAppropriateWait+0x64 (FPO: [Non-Fpo]) 05 001bef48 <span style='color: blue; font-weight: bold'>70df9911</span> 00000000 00000000 045e247c clr!WaitHandleNative::CorWaitOneNative+0x163 (FPO: [Non-Fpo]) 06 <span style='color: blue; font-weight: bold'>001bef5c</span> 70df98d8 00000000 0000c350 00000000 mscorlib_ni+0x429911 07 001bef78 02990474 00000000 00000000 001bef94 mscorlib_ni+0x4298d8 WARNING: Frame IP not in any known module. Following frames may be wrong. ...[생략]... 16 001bfa74 77594a47 ffffffff 775b9eee 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH]) 17 001bfa84 00000000 739e4df0 003a9000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo]) </pre> <br /> 70df9911 주솟값을 RetAddr로 표기한 WaitHandleNative::CorWaitOneNative 호출을 확인할 수 있습니다. 그 함수의 RetAddr라고 되어 있으니 그렇다면 그 아래의 "mscorlib_ni+0x429911" 호출이 InternalWaitOne에 해당합니다. (.NET Managed 함수 호출은 k 명령어에서 저런 식으로 이름이 보입니다.)<br /> <br /> 따라서 "mscorlib_ni+0x429911"의 ChildEBP로 나온 001bef5c 값이 InternalWaitOne 함수가 호출될 당시의 EBP 값이 됩니다. 그리하여 [ebp + 0ch]의 값은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > mov edx, dword ptr [ebp + 0ch] </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'>? 001bef5c + c</span> Evaluate expression: 1830760 = <span style='color: blue; font-weight: bold'>001bef68</span> 0:000> <span style='color: blue; font-weight: bold'>dd 001bef68</span> 001bef68 <span style='color: blue; font-weight: bold'>0000c350</span> 00000000 70e798a4 045e244c 001bef78 001bef88 02990474 00000000 00000000 001bef88 001bef94 7296eb16 0275e020 001befe8 001bef98 72976e84 001bf024 001befd8 72b07020 001befa8 001bf0f8 72976e3d 57a57eed 001bf14c 001befb8 001bf0b8 001bf06c 729d432a 001bf024 001befc8 00000000 57a57eed 001befa0 001bf0b8 001befd8 001bf16c 72b05370 2529ff9d 00000001 </pre> <br /> 원하던 바로 그 값(0000c350)입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 위에서 EBP 레지스터의 값을 k 명령어의 ChildEBP를 통해서 구하긴 했지만 돌이켜 보면 !clrstack의 "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;' > 0:000> !clrstack OS Thread Id: 0x92ec (0) <span style='color: blue; font-weight: bold'>Child SP</span> IP Call Site 001bee74 7759ed3c [HelperMethodFrame_1OBJ: 001bee74] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean) <span style='color: blue; font-weight: bold'>001bef58</span> 70df9911 System.Threading.WaitHandle.<span style='color: blue; font-weight: bold'>InternalWaitOne</span>(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean) 001bef70 70df98d8 System.Threading.WaitHandle.WaitOne(Int32, Boolean) 001bef84 02990474 ConsoleApp1.Program.Main(System.String[]) [E:\ConsoleApp1\ConsoleApp1\Program.cs @ 16] 001bf0f8 7296eb16 [GCFrame: 001bf0f8] </pre> <br /> InternalWaitOne의 Child SP가 001bef58인 것은 부모 함수(WaitOne)가 InternalWaitOne을 call한 시점의 SP를 의미합니다. 따라서 call 이후 prologue로 "push ebp", "mov ebp, esp"로 구성되는 EBP 프레임 레지스터의 값은 Child SP 값의 +4가 됩니다. 즉, 001bef58 + 4 = 001bef5c로 결국 ChildEBP의 값과 같게 됩니다.<br /> <br /> (그런데 좀 이상하군요. x64에서는 Child SP의 의미가 prologue가 모두 반영된 코드인데 x86에서는 프레임 스택 구성 전의 값을 가리키고 있다는 것이.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 사실 .NET 메서드들은 __clrcall 호출 규약을 따르기 때문에 ecx, edx의 활용으로 인자 추적이 살짝 어렵습니다. 따라서 쉽게 찾으려면 .NET 메서드에서 Win32 API 등의 __stdcall 호출 규약을 따르는 시점부터 찾는 것이 좋습니다. 그 외에, primitive 타입이 아닌 객체라면 !dso 명령어를 통해 출력되는 결과로부터 유추하는 것이 더 빠릅니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1381
(왼쪽의 숫자를 입력해야 합니다.)