Debugging Tips (13) static loading (implicit loading)된 DLL 강제로 Unload하기
C++/Debug 2013. 6. 20. 13:27뭐 이럴 일이 있을지는 잘 모르겠습니다만...
버그 트럭에 올라온 문의 글을 보고 한번 해봤습니다. ^^
먼저 샘플 소스코드를 보시면 다음과 같습니다.
LDR은 PEB에 있는데요... 아래 그림과 같은 구조입니다. (출처 : 이미지에 링크)
먼저 LDR을 찾아야겠죠. 우선 PEB부터 찾아봅니다.
0:002> !peb
PEB at 7efde000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes
ImageBaseAddress: 00ef0000
Ldr 779e0200
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 00564788 . 00589c50
Ldr.InLoadOrderModuleList: 005646f8 . 00589c40
Ldr.InMemoryOrderModuleList: 00564700 . 00589c48
Base TimeStamp Module
ef0000 51c28118 Jun 20 13:12:08 2013 D:\My Documents\4. TestCode\FreeLibraryTest\Debug\FreeLibraryTest.exe
778e0000 4ec49b8f Nov 17 14:28:47 2011 C:\Windows\SysWOW64\ntdll.dll
76e00000 50b83c89 Nov 30 13:56:41 2012 C:\Windows\syswow64\kernel32.dll
76150000 50b83c8a Nov 30 13:56:42 2012 C:\Windows\syswow64\KERNELBASE.dll
76490000 4ce7ba59 Nov 20 21:08:57 2010 C:\Windows\syswow64\USER32.dll
76d20000 4ce7ba53 Nov 20 21:08:51 2010 C:\Windows\syswow64\GDI32.dll
... 이하 생략
아항. PEB는 7efde000에 있군요. 그리고 LDR이 779e0200에 있다는 것도 알게 되었습니다.
PEB_LDR_DATA는 아래와 같습니다.
0:002> dt ntdll!_PEB_LDR_DATA 779e0200
+0x000 Length : 0x30
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x5646f8 - 0x589c40 ]
+0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x564700 - 0x589c48 ]
+0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x564788 - 0x589c50 ]
+0x024 EntryInProgress : (null)
+0x028 ShutdownInProgress : 0 ''
+0x02c ShutdownThreadId : (null)
저 링크드 리스트들은 _LDR_DATA_TABLE_ENTRY 구조체로 이루어져 있습니다. (XP는 _LDR_MODULE)
0:002> dt ntdll!_LDR_DATA_TABLE_ENTRY 0x5646f8
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x564778 - 0x779e020c ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x564780 - 0x779e0214 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x018 DllBase : 0x00ef0000 Void
+0x01c EntryPoint : 0x00f0107d Void
+0x020 SizeOfImage : 0x1b000
+0x024 FullDllName : _UNICODE_STRING "D:\My Documents\4. TestCode\FreeLibraryTest\Debug\FreeLibraryTest.exe"
+0x02c BaseDllName : _UNICODE_STRING "FreeLibraryTest.exe"
+0x034 Flags : 0x4000
+0x038 LoadCount : 0xffff
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x5883fc - 0x779e48c8 ]
+0x03c SectionPointer : 0x005883fc Void
+0x040 CheckSum : 0x779e48c8
+0x044 TimeDateStamp : 0x51c28118
+0x044 LoadedImports : 0x51c28118 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x564748 - 0x564748 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x564750 - 0x564750 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x58c3b8 - 0x565268 ]
+0x068 ContextInformation : 0x7791c944 Void
+0x06c OriginalBase : 0
저기 "LoadCount"라는 항목이 보이죠? 저놈이 오늘 패치할 메모리입니다.
링크드 리스트를 순회하면서 전체 리스트를 출력해봅니다.
0:002> dt ntdll!_LDR_DATA_TABLE_ENTRY -l InLoadOrderLinks.Flink 0x5646f8
InLoadOrderLinks.Flink at 0x005646f8
---------------------------------------------
+0x000 InLoadOrderLinks : [ 0x564778 - 0x779e020c ]
+0x000 Flink : 0x00564778 _LIST_ENTRY [ 0x564ab8 - 0x5646f8 ]
+0x004 Blink : 0x779e020c _LIST_ENTRY [ 0x5646f8 - 0x589c40 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x564780 - 0x779e0214 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x018 DllBase : 0x00ef0000 Void
+0x01c EntryPoint : 0x00f0107d Void
+0x020 SizeOfImage : 0x1b000
+0x024 FullDllName : _UNICODE_STRING "D:\My Documents\4. TestCode\FreeLibraryTest\Debug\FreeLibraryTest.exe"
+0x02c BaseDllName : _UNICODE_STRING "FreeLibraryTest.exe"
+0x034 Flags : 0x4000
+0x038 LoadCount : 0xffff
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x5883fc - 0x779e48c8 ]
+0x03c SectionPointer : 0x005883fc Void
+0x040 CheckSum : 0x779e48c8
+0x044 TimeDateStamp : 0x51c28118
+0x044 LoadedImports : 0x51c28118 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x564748 - 0x564748 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x564750 - 0x564750 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x58c3b8 - 0x565268 ]
+0x068 ContextInformation : 0x7791c944 Void
+0x06c OriginalBase : 0
+0x070 LoadTime : _LARGE_INTEGER 0x0
InLoadOrderLinks.Flink at 0x00564778
---------------------------------------------
+0x000 InLoadOrderLinks : [ 0x564ab8 - 0x5646f8 ]
+0x000 Flink : 0x00564ab8 _LIST_ENTRY [ 0x564ba0 - 0x564778 ]
+0x004 Blink : 0x005646f8 _LIST_ENTRY [ 0x564778 - 0x779e020c ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x564ac0 - 0x564700 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x564bb0 - 0x779e021c ]
+0x018 DllBase : 0x778e0000 Void
+0x01c EntryPoint : (null)
+0x020 SizeOfImage : 0x180000
+0x024 FullDllName : _UNICODE_STRING "C:\Windows\SysWOW64\ntdll.dll"
+0x02c BaseDllName : _UNICODE_STRING "ntdll.dll"
+0x034 Flags : 0x4004
+0x038 LoadCount : 0xffff
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x588d7c - 0x779e48c0 ]
+0x03c SectionPointer : 0x00588d7c Void
+0x040 CheckSum : 0x779e48c0
+0x044 TimeDateStamp : 0x4ec49b8f
+0x044 LoadedImports : 0x4ec49b8f Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x5647c8 - 0x5647c8 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x5647d0 - 0x5647d0 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x5647d8 - 0x5647d8 ]
+0x068 ContextInformation : (null)
+0x06c OriginalBase : 0x7de70000
+0x070 LoadTime : _LARGE_INTEGER 0x0
... 로딩된 모든 DLL들의 리스트가 출력되므로... 무지 깁니다. 이하 생략.
명시적으로 로딩된 DLL은 아래와 같이 LoadCount 가 1 이상입니다. (LoadLibrary 호출시마다 증가, FreeLibrary 호출시마다 감소)
InLoadOrderLinks.Flink at 0x005892c0
---------------------------------------------
+0x000 InLoadOrderLinks : [ 0x589340 - 0x589240 ]
+0x000 Flink : 0x00589340 _LIST_ENTRY [ 0x5893c0 - 0x5892c0 ]
+0x004 Blink : 0x00589240 _LIST_ENTRY [ 0x5892c0 - 0x5891c0 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x589348 - 0x589248 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x589350 - 0x5891d0 ]
+0x018 DllBase : 0x000c0000 Void
+0x01c EntryPoint : 0x000c2c73 Void
+0x020 SizeOfImage : 0xd000
+0x024 FullDllName : _UNICODE_STRING "D:\My Documents\4. TestCode\FreeLibraryTest\Debug\cryptmod.dll"
+0x02c BaseDllName : _UNICODE_STRING "cryptmod.dll"
+0x034 Flags : 0x84004
+0x038 LoadCount : 1
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x5894fc - 0x588e7c ]
+0x03c SectionPointer : 0x005894fc Void
+0x040 CheckSum : 0x588e7c
+0x044 TimeDateStamp : 0x51949480
+0x044 LoadedImports : 0x51949480 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x589310 - 0x589310 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x589318 - 0x589318 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x5a7760 - 0x5a7718 ]
+0x068 ContextInformation : 0x7791c944 Void
+0x06c OriginalBase : 0x10000000
+0x070 LoadTime : _LARGE_INTEGER 0x01ce6d6c`570ae158
LoadCount를 패치하여 똑같이 1로 만들어줄까요?
0:000> ew 0x00587da8+0x038 1
0:002> dw 0x00587da8+0x038 L1
00587de0 0001
0:002> dt ntdll!_LDR_DATA_TABLE_ENTRY 0x00587da8
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x587e28 - 0x587928 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x587e30 - 0x587930 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x588f50 - 0x588d50 ]
+0x018 DllBase : 0x10000000 Void
+0x01c EntryPoint : 0x1001d30d Void
+0x020 SizeOfImage : 0x2f000
+0x024 FullDllName : _UNICODE_STRING "D:\My Documents\4. TestCode\FreeLibraryTest\Debug\unloadtarget.dll"
+0x02c BaseDllName : _UNICODE_STRING "unloadtarget.dll"
+0x034 Flags : 0x84004
+0x038 LoadCount : 1
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x779e48a8 - 0x779e48a8 ]
+0x03c SectionPointer : 0x779e48a8 Void
+0x040 CheckSum : 0x779e48a8
+0x044 TimeDateStamp : 0x50ee7840
+0x044 LoadedImports : 0x50ee7840 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x587df8 - 0x587df8 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x587e00 - 0x587e00 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x58c370 - 0x5879d8 ]
+0x068 ContextInformation : 0x7791c944 Void
+0x06c OriginalBase : 0x10000000
+0x070 LoadTime : _LARGE_INTEGER 0x01ce6d6c`5708e581
이제 메시지박스를 클릭하여 "FreeLibrary"가 호출되도록 합니다.
"After FreeLibrary unloadtarget.dll" 메시지박스가 출력되었을 때 다시 브레이크포인트를 걸고 모듈이 언로드된 것을 확인합니다. ^^
0:002> g
...
(19c0.1a44): Break instruction exception - code 80000003 (first chance)
eax=7ef99000 ebx=00000000 ecx=00000000 edx=7797f85a esi=00000000 edi=00000000
eip=778f000c esp=02eefd68 ebp=02eefd94 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!DbgBreakPoint:
778f000c cc int 3
0:005> lmvm unloadtarget
start end module name
위의 메모리 패치 과정을 프로그램적으로 구현하면 되겠네요. ^^
'C++ > Debug' 카테고리의 다른 글
Debugging Tips (8) - Hang 걸린 Application 분석하기 (20130905 수정) (7) | 2013.09.05 |
---|---|
WinDbg에서 STL 분석하기 - "SDbgExt" (map, list, string 등등...) (0) | 2013.07.03 |
WinDbg에서 LastErrorCode (GetLastError) 확인하는 법 (0) | 2013.02.14 |
Debugging Tips (12) - 정확한 PDB가 없을때 메모리 덤프 분석하기 (0) | 2013.01.24 |
Debugging Tips (11) - 서비스(Service) 디버깅하기 (Vista 이후 OS) (2) | 2012.12.12 |