성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] 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...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
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/Visual Studio - HeapFree x86의 동작 분석</h1> <p> 들어가기에 앞서 우선 (개인적으로 ^^;) 헷갈리는 JUMP 기계어 코드를 정리합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <a target='tab' href='http://faydoc.tripod.com/cpu/jge.htm'>http://faydoc.tripod.com/cpu/jge.htm</a> JNE == Jump short if not equal (ZF = 0), Visual Studio의 경우 ZR = 0 JGE == Jump short if greater or equal (SF = OF), Visual Studio의 경우 PL = OV JAE == Jump short if above or equal (CF = 0), Visual Studio의 경우 CY = 0 JL == Jump short if less (SF <> OF), Visual Studio의 경우 PL != OV </pre> <br /> 또한 Visual Studio의 경우 Flags 레지스터의 값을 디버깅 중에 확인하려면 "Debug" / "Windows" / "Registers(Ctrl + Alt + G)"를 선택해 "EFL" 값을 봐도 되지만, 기왕이면 각각의 플래그 값의 확인을 더욱 용이하게 할 수 있도록 다음과 같이 마우스 우 클릭해 나오는 메뉴에서 "Flags" 값을 check하면,<br /> <br /> <img alt='vs_heap_free_1.png' src='/SysWebRes/bbs/vs_heap_free_1.png' /><br /> <br /> 개별 필드 값을 아래의 <a target='tab' href='https://en.wikipedia.org/wiki/FLAGS_register'>단축 용어</a>로 나눠서 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > OV (overflow) UP (Direction) EI (Enable Interrupt) PL (Positive) ZR (Zero) AC (Auxilary Carry) PE (Parity Even) CY (Carry) </pre> <br /> 그 외에 레지스터 명칭 중 낯선 것 2개 소개합니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <a target='tab' href='https://www.tortall.net/projects/yasm/manual/html/arch-x86-registers.html'>SIL</a> == ESI 레지스터의 하위 8비트 DIL == EDI 레지스터의 하위 8비트 </pre> <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;' > // Windows 10 1909 - Visual C++ x86 #include <iostream> #include <windows.h> int main() { int size = 20; HANDLE hHandle = <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapcreate'>HeapCreate</a>(0, 0, 8192); printf("hHandle == 0x%x\n", hHandle); LPVOID pVoid1 = <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc'>HeapAlloc</a>(hHandle, 0, size); printf("pVoid1 == 0x%x\n", pVoid1); memset(pVoid1, 0xff, size); LPVOID pVoid2 = HeapAlloc(hHandle, 0, size + 1); printf("pVoid2 == 0x%x\n", pVoid2); memset(pVoid2, 0xee, size + 1); LPVOID pVoid3 = HeapAlloc(hHandle, 0, size + 2); printf("pVoid3 == 0x%x\n", pVoid3); memset(pVoid3, 0xcc, size + 2); LPVOID pVoid4 = HeapAlloc(hHandle, 0, 1); printf("pVoid4 == 0x%x\n", pVoid4); memset(pVoid4, 0xaa, 1); <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree'>HeapFree</a>(hHandle, 0, pVoid1); HeapFree(hHandle, 0, pVoid2); HeapFree(hHandle, 0, pVoid3); HeapFree(hHandle, 0, pVoid4); <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapdestroy'>HeapDestroy</a>(hHandle); printf("Exited!"); return 0; } </pre> <br /> 첫 번째 HeapFree(hHandle, 0, pVoid1)에서 BP를 잡고 실행했을 때 (제 경우) 각각의 포인터 값이 다음과 같이 나왔습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > hHandle == 0x19a0000 pVoid1 == 0x19a04b0 pVoid2 == 0x19a04d0 pVoid3 == 0x19a04f0 pVoid4 == 0x19a0510 </pre> <br /> 여기서부터 Disassembly(Alt + G) 창을 띄워 아래의 "call esi" 명령까지 F11 키를 눌러 이동합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 65: HeapFree(hHandle, 0, pVoid1); 00E3110D 8B 35 04 20 E3 00 mov esi,dword ptr [__imp__HeapFree@12 (0E32004h)] 00E31113 83 C4 08 add esp,8 00E31116 C6 07 AA mov byte ptr [edi],0AAh 00E31119 FF 75 F8 push dword ptr [pVoid1] // 세 번째 인자 00E3111C 6A 00 push 0 // 두 번째 인자: dwFlags == 0 00E3111E FF 75 FC push dword ptr [hHandle] // 첫 번째 인자: hHandle 00E31121 FF D6 <span style='color: blue; font-weight: bold'>call esi </span></pre> <br /> 그럼 IAT 테이블로 진입하게 되고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > _HeapFreeStub@12: 76BB1A70 8B FF mov edi,edi 76BB1A72 55 push ebp 76BB1A73 8B EC mov ebp,esp 76BB1A75 5D pop ebp 76BB1A76 FF 25 E0 10 C2 76 <span style='color: blue; font-weight: bold'>jmp dword ptr [__imp__HeapFree@12 (76C210E0h)]</span> </pre> <br /> 이제부터 본격적인 HeapFree 코드로 넘어갑니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 771DDBD0 8B FF mov edi,edi 771DDBD2 55 push ebp 771DDBD3 8B EC mov ebp,esp 771DDBD5 83 E4 F8 and esp,0FFFFFFF8h 771DDBD8 56 push esi 771DDBD9 57 push edi 771DDBDA 8B 7D 10 mov edi,dword ptr [ebp+10h] // edi == ebp + 10h == 세 번째 인자 pVoid1 771DDBDD 85 FF test edi,edi 771DDBDF 74 3D je RtlFreeHeap+4Eh (771DDC1Eh) 771DDBE1 8B 75 08 mov esi,dword ptr [ebp+8] // esi == ebp + 8h == 첫 번째 인자 hHandle 771DDBE4 85 F6 test esi,esi 771DDBE6 0F 84 26 34 05 00 je _RtlCaptureStackContext@12+7EF2h (77231012h) 771DDBEC 81 7E 08 EE DD EE DD cmp dword ptr [esi+8],0DDEEDDEEh 771DDBF3 8B D7 mov edx,edi // _RtlpFreeHeapInternal로 첫 번째 인자 전달 (pVoid1) 771DDBF5 8B CE mov ecx,esi // _RtlpFreeHeapInternal로 두 번째 인자 전달 (hHandle) 771DDBF7 0F 84 2B 34 05 00 je _RtlCaptureStackContext@12+7F08h (77231028h) 771DDBFD F6 05 EC E8 2B 77 02 test byte ptr [_RtlpHpHeapFeatures (772BE8ECh)],2 771DDC04 0F 85 2B 34 05 00 jne _RtlCaptureStackContext@12+7F15h (77231035h) 771DDC0A 6A 00 push 0 // _RtlpFreeHeapInternal로 세 번째 인자 전달 771DDC0C 6A 00 push 0 // _RtlpFreeHeapInternal로 네 번째 인자 전달 771DDC0E FF 75 0C push dword ptr [ebp+0Ch] // _RtlpFreeHeapInternal로 다섯 번째를 HeapFree의 두 번째 인자로 전달 771DDC11 E8 42 89 04 00 <span style='color: blue; font-weight: bold'>call _RtlpFreeHeapInternal@20 (77226558h)</span> // ==> call 771DDC16 5F pop edi 771DDC17 5E pop esi 771DDC18 8B E5 mov esp,ebp 771DDC1A 5D pop ebp 771DDC1B C2 0C 00 ret 0Ch 771DDC1E 5F pop edi 771DDC1F B8 01 00 00 00 mov eax,1 771DDC24 5E pop esi 771DDC25 8B E5 mov esp,ebp 771DDC27 5D pop ebp 771DDC28 C2 0C 00 ret 0Ch </pre> <br /> 위의 코드는 별로 중요한 것이 없고 그냥 _RtlpFreeHeapInternal를 아래와 같은 식으로 호출하는 역할만 한다고 봐도 무방합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > _RtlpFreeHeapInternal(hHandle, pVoid1, 0, 0, 0); </pre> <br /> 이후 _RtlpFreeHeapInternal 코드로 넘어가서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>_RtlpFreeHeapInternal@20:</span> 77226558 8B FF mov edi,edi 7722655A 55 push ebp 7722655B 8B EC mov ebp,esp 7722655D 83 EC 40 sub esp,40h 77226560 53 push ebx 77226561 56 push esi 77226562 8B F1 mov esi,ecx // esi == hHandle 77226564 33 DB xor ebx,ebx // ebx = 0 77226566 8B C2 mov eax,edx // eax == pVoid1 77226568 89 75 F8 mov dword ptr [ebp-8],esi // local_var1 == esi == hHandle 7722656B 57 push edi 7722656C 89 45 F0 mov dword ptr [ebp-10h],eax // local_var2 == pVoid1 7722656F 8B FB mov edi,ebx // edi == 0 77226571 81 7E 08 EE DD EE DD cmp dword ptr [esi+8],0DDEEDDEEh // cmp [hHandle + offset 8], 0DDEEDDEE // cmp [0x019A0008] == 0xffeeffee // ZR = 0 77226578 0F 85 89 00 00 00 <span style='color: blue; font-weight: bold'>jne _RtlpFreeHeapInternal@20+0AFh (77226607h)</span> // ==> jump ...[생략]... 772265E1 5F pop edi 772265E2 5E pop esi 772265E3 5B pop ebx 772265E4 C9 leave 772265E5 C2 0C 00 ret 0Ch </pre> <br /> 기본적인 변수 설정만 한 후 "jne _RtlpFreeHeapInternal@20+0AFh" 코드로 jump합니다. 이제부터 의미 있는 코드들이 나오는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 77226607 F7 46 44 00 00 00 01 test dword ptr [esi+44h],1000000h // [hHandle + offset 44h] == 00000000 // 0x0 and 1000000h, ZR = 1 7722660E 8B 4D 08 mov ecx,dword ptr [ebp+8] // ecx = _RtlpFreeHeapInternal의 세 번째 인자 == 0 77226611 0F 85 B6 06 00 00 jne _RtlpFreeHeapInternal@20+775h (77226CCDh) 77226617 F6 46 48 01 test byte ptr [esi+48h],1 // [hHandle + offset 48h] == 00000000 // 0x0 and 1 (ZR = 1) 7722661B 74 0B je _RtlpFreeHeapInternal@20+0D0h (77226628h) // ==> jump to 77226628 ...[생략]... ...[생략]... ...[생략]... 77226628 A8 07 test al,7 // eax == pVoid1(0x19a04b0), al == 0xb0 // 0xb0 and 0x07 == 0x0 (ZR = 1) 7722662A 75 2A jne _RtlpFreeHeapInternal@20+0FEh (77226656h) 7722662C 8D 78 F8 <span style='color: blue; font-weight: bold'>lea edi,[eax-8] // pVoid - 8 == 0x019A04A8 0x019A04A8 2cdebacd // pVoid - 8 위치 0x019A04AC 0c00bbf9 // pVoid - 4 위치 0x019A04B0 ffffffff // pVoid 포인터가 가리키는 위치 - 데이터 구분을 위해 일부러 '0xff'로 채움 0x019A04B4 ffffffff 0x019A04B8 ffffffff</span> 7722662F 80 7F 07 05 cmp byte ptr [edi+7],5 // [edi + 7] == 0x0c // cmp 0c, 5 == (ZF = 0) 77226633 75 09 jne _RtlpFreeHeapInternal@20+0E6h (7722663Eh) // ==> jump to 7722663E ...[생략]... ...[생략]... ...[생략]... 7722663E F6 47 07 3F test byte ptr [edi+7],3Fh // 0xc and 0x3f (ZR = 0) 77226642 75 20 jne _RtlpFreeHeapInternal@20+10Ch (77226664h) // ==> jump to 77226664 ...[생략]... ...[생략]... ...[생략]... 77226664 85 FF test edi,edi // edi == 0x019A04A8 (pVoid - 8) (ZR = 0) 77226666 75 28 jne _RtlpFreeHeapInternal@20+138h (77226690h) // ==> jump to 77226690 ...[생략]... ...[생략]... ...[생략]... 77226690 8B 45 F0 mov eax,dword ptr [ebp-10h] // eax == [ebp - 10h] == local_var2 == pVoid1 77226693 80 78 FF 05 cmp byte ptr [eax-1],5 // eax == 019a04b0, (byte)[eax - 1] == 0x0c // cmp == 0x0c - 0x05 == 0x7 == (ZR = 0) 77226697 0F 85 45 01 00 00 jne _RtlpFreeHeapInternal@20+28Ah (772267E2h) // ==> jump to 772267E2 ...[생략]... ...[생략]... ...[생략]... 772267E2 38 5F 07 cmp byte ptr [edi+7],bl // [edi + 7] == 0x0c, BL == 0 // cmp == (ZR = 0, OV = 0, PL = 0) 772267E5 0F 8D DF 04 00 00 jge _RtlpFreeHeapInternal@20+772h (77226CCAh) // ==> jump to 77226CCA ...[생략]... ...[생략]... ...[생략]... 77226CCA 8B 4D 08 mov ecx,dword ptr [ebp+8] // ecx = _RtlpFreeHeapInternal의 세 번째 인자 == 0 77226CCD 8B D1 mov edx,ecx // edx = 0 77226CCF 8B CE mov ecx,esi // ecx = hHandle 77226CD1 50 push eax 77226CD2 57 push edi 77226CD3 83 CA 02 or edx,2 77226CD6 E8 95 6F FB FF <span style='color: blue; font-weight: bold'>call @RtlpFreeHeap@16 (771DDC70h)</span> ...[생략]... </pre> <br /> 보는 바와 같이 "pVoid"가 가리키고 있는 포인터보다 8바이트 앞선 위치를 edi 레지스터에 저장 후, 실질적으로는 dword('pVoid - 4') 위치의 상위 첫 바이트를 대상으로 다양한 체크를 하고 있습니다. 별다른 이상이 없다면 RtlpFreeHeap 함수를 다음과 같은 형식으로 호출하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > RtlpFreeHeap(hHandle, 0, pVoid, pVoid - 8, 0 | 2); </pre> <br /> 나중에 잠깐 다루겠지만, dword('pVoid - 4') 위치의 상위 첫 바이트가 이상이 없다고 해도 RtlpFreeHeap 내부에서 한 번 더 dword(pVoid - 8), dword(pVoid - 4) 위치의 값을 테스트하게 되고 그 과정에서 변조가 판단되면 비정상 종료가 발생하게 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 일부러 비정상 종료를 한번 유도해 볼까요? ^^ HeapFree를 하기 전 다음과 같은 코드를 넣어두고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > *(((BYTE*)pVoid1) - 4) = 0x01; </pre> <br /> 실행하면, dword(pVoid - 4)의 최하위 바이트가 바뀌게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0x00E004A8 05010004 0x00E004AC 0c0001<span style='color: blue; font-weight: bold'>01</span> 0x00E004B0 ffffffff 0x00E004B4 ffffffff </pre> <br /> ASLR이나, Heap의 다양한 내부 상태 값으로 인해 저 하나의 값이 어떻게 영향을 미칠지는 알 수 없습니다. 우선, 이런 식의 변조를 했을 때 RtlReportFatalFailure로 끝나는 사례를 먼저 살펴보겠습니다. (참고로, 운에 따라 비정상 종료하지 않는 경우도 있습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ntdll.dll!_RtlReportFatalFailure@4() Unknown ntdll.dll!_RtlReportCriticalFailure@12() Unknown ntdll.dll!_RtlpReportHeapFailure@4() Unknown ntdll.dll!_RtlpHpHeapHandleError@12() Unknown ntdll.dll!_RtlpLogHeapFailure@24() Unknown ntdll.dll!_RtlpAnalyzeHeapFailure@12() Unknown ntdll.dll!@RtlpFreeHeap@16() Unknown ntdll.dll!_RtlpFreeHeapInternal@20() Unknown ntdll.dll!RtlFreeHeap() Unknown > ConsoleApplication1.exe!main() Line 60 C++ </pre> <br /> 이렇게 되면 Visual Studio 디버깅 중에는 다음과 같은 식의 예외 메시지 창이 출력됩니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Unhandled exception at <span style='color: blue; font-weight: bold'>0x7727F94D</span> (ntdll.dll) in ConsoleApplication1.exe: 0xC0000374: A heap has been corrupted (parameters: <span style='color: blue; font-weight: bold'>0x772BB960</span>). occurred </div><br /> <br /> 정확히 "heap" 영역이 "corrupted"되었다고 알려주고 있으며 이 상황을 유발한 몇몇 문맥 정보를 ("parameters: 0x......")로 알려주고 있습니다. 실제로 이 주소의 메모리 값을 확인해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>0x772BB960</span> 00000002 0x772BB964 000003d0 0x772BB968 00000004 0x772BB96C 00e00000 // hHandle 0x772BB970 00e001e8 0x772BB974 00000000 0x772BB978 00000000 0x772BB97C 00000000 <span style='color: blue; font-weight: bold'>0x772BB980 00e00000 // hHandle 0x772BB984 00e004a8 // (pVoid - 8) 주소</span> 0x772BB988 00000000 0x772BB98C 00000000 </pre> <br /> 저렇게 Heap handle 값과 Free를 시도하려고 했던 주소의 (헤더 위치로 보정된) pVoid 주솟값을 알 수 있습니다. 따라서, 원래 HeapFree로 전달한 값이 00e004a8 + 0x8 == e004b0 임을 알 수 있습니다.<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;' > 0x013B04A8 05010004 0x013B04AC 0c0014<span style='color: blue; font-weight: bold'>01</span> 0x013B04B0 ffffffff 0x013B04B4 ffffffff </pre> <br /> 역시 마찬가지로 하위 1바이트만 0x01 값으로 채운 경우인데 다음과 같이 오류 메시지 및 callstack이 다릅니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Unhandled exception at <span style='color: blue; font-weight: bold'>771DE00D</span> (ntdll.dll) in ConsoleApplication1.exe: 0xC0000005: Access violation reading location <span style='color: blue; font-weight: bold'>0x00C9FDF2</span>. </div><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ntdll.dll!@RtlpFreeHeap@16() Unknown ntdll.dll!_RtlpFreeHeapInternal@20() Unknown ntdll.dll!RtlFreeHeap() Unknown > ConsoleApplication1.exe!main() Line 60 C++ </pre> <br /> 실제로 0x773CE00D 위치의 코드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 771DE002 8B 4F 4C mov ecx,dword ptr [edi+4Ch] 771DE005 8B C1 mov eax,ecx 771DE007 C1 E8 14 shr eax,14h 771DE00A 22 47 52 and al,byte ptr [edi+52h] // edi == 0x013b0000 <span style='color: blue; font-weight: bold'>771DE00D</span> 32 43 02 <span style='color: blue; font-weight: bold'>xor al,byte ptr [ebx+2]</span> // ebx == 0x00C9FDF0 == (invalid memory pointer) </pre> <br /> xor의 두 번째 오퍼랜드의 주소가 (디버거에서 알려준) 0x00C9FDF2이고 이 주솟값의 영역은 한 번도 할당한 적이 없으므로 Access Violation 예외가 발생한 것입니다. 이전에 살펴 본 _RtlReportFatalFailure의 경우 문맥 정보를 통해 해제하려는 주솟값을 알아낼 수 있었지만, AV 예외가 발생한 이번에는 그 주소를 알기 쉽지 않습니다. 우선, 대충 각 callstack의 frame 별로 레지스터를 조사해 보면 다음과 같이 주솟값이 (운이 좋게) 나오는 것을 볼 수 있습니다. (또는, 각각의 <a target='tab' href='http://www.sysnet.pe.kr/2/0/10832'>함수 내부 코드를 통해 stack에 보관된 값을 찾아내는 식으로 조사</a>할 수 있습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [ntdll.dll!@RtlpFreeHeap@16()] esi == 013B04A8 == pVoid - 8 [ntdll.dll!_RtlpFreeHeapInternal@20()] edi == 013B04A8 == pVoid - 8 [ntdll.dll!RtlFreeHeap()] edi == 013B04B0 == pVoid </pre> <br /> 물론, 위의 상황은 운영체제의 버전/패치에 따라 달라질 수 있으므로 적당하게 취할 수 있는 센스를 발휘해야 합니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이 정도면, Heap corruption 관련 예외가 발생했을 때 어떤 메모리를 해제하려고 했는지는 쉽게(?) 찾을 수 있을 것입니다. 다음번 글에서는 이번에 다뤘던 정보를 바탕으로 지난 글의 덤프 분석 사례를, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12058'>http://www.sysnet.pe.kr/2/0/12058</a> </pre> <br /> 좀 더 깊이 있게 들어가 보겠습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마지막으로, pVoid + 8, pVoid + 4 위치의 값을 체크하는 코드를 아래에 (필요한 정도로만) 정리합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 772267E2 38 5F 07 cmp byte ptr [edi+7],bl // from 77226697 // [edi + 7] == 8c, BL == 0 // cmp == (ZF = 0, OV = 0, PL = 1) 772267E5 0F 8D DF 04 00 00 jge _RtlpFreeHeapInternal@20+772h (77226CCAh) 772267EB 8B CF mov ecx,edi // ecx == 0x01433A70 772267ED C1 E9 03 shr ecx,3 // ecx == 0x0028674E 772267F0 33 0F xor ecx,dword ptr [edi] // ecx == 0x0028674E xor 0x2e3b9086 == 0x2e13f7c8 772267F2 33 0D 14 E9 2B 77 xor ecx,dword ptr [_RtlpLFHKey (772BE914h)] // [772BE914h] == 2f1df7c8 // 0x2e13f7c8 xor 2f1df7c8 == 10E0000 772267F8 33 CE xor ecx,esi // 10E0000 xor g_hHeap == 01420000 == 4C0000 772267FA 66 85 C9 test cx,cx // cx == 0000 (ZR == 1) 772267FD 0F 85 B4 04 00 00 jne _RtlpFreeHeapInternal@20+75Fh (77226CB7h) // 하위 4바이트가 0이 아니라면 _RtlpLogHeapFailure 77226803 C1 E9 0D shr ecx,0Dh // ecx == 0x260 77226806 8B C7 mov eax,edi // 0x01433A70 (pVoid - 8) 77226808 2B C1 sub eax,ecx // 0x01433A70 - 0x260 = 0x1433810 7722680A 8B 08 mov ecx,dword ptr [eax] // ecx == [0x1433810] 위치의 값 7722680C 89 4D F4 mov dword ptr [ebp-0Ch],ecx // [ebp - 0Ch] == ecx == [0x1433810] 위치의 값 7722680F 85 C9 test ecx,ecx // cx == 00fbcbc8 (ZR == 0) 77226811 0F 84 A0 04 00 00 je _RtlpFreeHeapInternal@20+75Fh (77226CB7h) // ecx == 0이면 _RtlpLogHeapFailure // 두 번째 쿠키 값 검증 77226817 8B 47 04 mov eax,dword ptr [edi+4] // eax == 8c001265 7722681A 8B 71 04 mov esi,dword ptr [ecx+4] // esi == [[0x1433810] 위치의 값 + offset 4] 7722681D C1 E8 08 shr eax,8 77226820 0F B7 D0 movzx edx,ax 77226823 8B 01 mov eax,dword ptr [ecx] 77226825 89 75 EC mov dword ptr [ebp-14h],esi 77226828 89 5D E4 mov dword ptr [ebp-1Ch],ebx 7722682B 89 55 E0 mov dword ptr [ebp-20h],edx 7722682E 8B 00 mov eax,dword ptr [eax] 77226830 8B 48 0C mov ecx,dword ptr [eax+0Ch] 77226833 8B 46 10 mov eax,dword ptr [esi+10h] 77226836 33 C1 xor eax,ecx 77226838 89 4D D8 mov dword ptr [ebp-28h],ecx 7722683B 33 C6 xor eax,esi 7722683D 33 05 14 E9 2B 77 xor eax,dword ptr [_RtlpLFHKey (772BE914h)] 77226843 8B F0 mov esi,eax 77226845 0F B7 C0 movzx eax,ax 77226848 C1 EE 10 shr esi,10h 7722684B 0F AF F2 imul esi,edx 7722684E 03 75 EC add esi,dword ptr [ebp-14h] 77226851 03 C6 add eax,esi 77226853 3B C7 cmp eax,edi 77226855 74 1A je _RtlpFreeHeapInternal@20+319h (77226871h) 77226857 8B 51 0C mov edx,dword ptr [ecx+0Ch] 7722685A 53 push ebx 7722685B 53 push ebx 7722685C 53 push ebx 7722685D 57 push edi 7722685E 6A 03 push 3 77226860 59 pop ecx 77226861 E8 A9 A4 06 00 call _RtlpLogHeapFailure@24 (77290D0Fh) // ==> failure </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8107
(왼쪽의 숫자를 입력해야 합니다.)