뭐 이럴 일이 있을지는 잘 모르겠습니다만...

버그 트럭에 올라온 문의 글을 보고 한번 해봤습니다. ^^


먼저 샘플 소스코드를 보시면 다음과 같습니다.



unloadtarget.dll 이 강제로 Unload하려는 DLL 되겠습니다~
FreeLibrary는 명시적으로 로딩된 DLL만 언로드하기 때문에 정적으로 로딩된 DLL은 언로드가 불가능하죠.
여기서 소개하는 방법은 LDR의 메모리를 패치하여 정적으로 로딩된 DLL을 동적 로딩된 DLL처럼 보이게 한 후 FreeLibrary로 언로드하는 방법입니다.

먼저 샘플 프로그램을 빌드하여 실행시킨 후 메시지박스가 뜬 상태에서 WinDbg를 붙입니다.

일단 unloadtarget이 로딩된 것을 확인해봅니다.
0:000> lmvm unloadtarget
start    end        module name
10000000 1002f000   unloadtarget    (deferred)             
    Image path: D:\My Documents\4. TestCode\FreeLibraryTest\Debug\unloadtarget.dll
    Image name: unloadtarget.dll
    Timestamp:        Thu Jan 10 17:13:52 2013 (50EE7840)
    CheckSum:         00000000
    ImageSize:        0002F000
    File version:     3.0.1.435
    Product version:  3.0.0.1
... 이하 생략


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을 찾아보면 아래와 같습니다.
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        : 0xffff
   +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

0:002> dw 0x00587da8+0x038 L1
00587de0  ffff

LoadCount가 0xffff (-1) 인데, 이것은 Implicit하게 로딩된 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


위의 메모리 패치 과정을 프로그램적으로 구현하면 되겠네요. ^^

Posted by kuaaan
,


사랑합니다. 편안히 잠드소서