Tag Archive for 'Message Hook'

Loader Lock, GetToSTA, Message Hook 에 의한 Application Hang

간혹 Application Hang 발생 시 COM Object의 Thread Model에 의한 Hang 현상이 발생하곤 합니다. 이러한 경우는 대부분 STA의 Message Loop과 Main Thread 사이에서 발생하는 Hang현상일 가능성이 높고 또한 Stack에는 어김 없이 GetToSTA 라는 Function 명이 보이게 되죠.

0:000> kvn
 # ChildEBP RetAddr  Args to Child             
00 00129d48 7c93df5a 7c7d25db 000004a8 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
01 00129d4c 7c7d25db 000004a8 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc (FPO: [3,0,0])
02 00129db0 7c7d2542 000004a8 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xa8 (FPO: [Non-Fpo])
03 00129dc4 769b0647 000004a8 ffffffff 001843e8 kernel32!WaitForSingleObject+0×12 (FPO: [2,0,0])
04 00129de0 76a91e50 001843e8 00195b68 00000000 ole32!GetToSTA+0×6f (FPO: [2,0,4])
05 00129e00 76a9108a 00129ec8 00129fd8 00184fa4 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0xf6 (FPO: [1,1,4])
06 00129ee0 769bce1a 00184fa4 00129fd8 00129fc8 ole32!CRpcChannelBuffer::SendReceive2+0xc8 (FPO: [3,50,4])
07 00129f4c 769bcdb2 00184fa4 00129fd8 00129fc8 ole32!CAptRpcChnl::SendReceive+0xab (FPO: [3,19,4])
08 00129fa0 77e04db5 00184fa4 00129fd8 00129fc8 ole32!CCtxComChnl::SendReceive+0×113 (FPO: [3,13,4])
09 00129fbc 77e04ead 0018aae4 0012a004 0600015b RPCRT4!NdrProxySendReceive+0×43 (FPO: [2,0,0])
0a 0012a398 77e04e42 76976228 76979370 0012a3d0 RPCRT4!NdrClientCall2+0×1fa (FPO: [Non-Fpo])
0b 0012a3b8 77d9a83b 00000010 00000003 0012a3e0 RPCRT4!ObjectStublessClient+0×8b (FPO: [2,0,4])
0c 0012a3c8 769a5ba3 0018aae4 0012a8cc 0012ae08 RPCRT4!ObjectStubless+0xf
0d 0012a3e0 769a20c8 0018aae4 00000001 00000000 ole32!CProcessActivator::GCOCallback+0×2b (FPO: [6,0,4])
0e 0012a400 769a207f 76a9714c 0012a728 00000000 ole32!CProcessActivator::AttemptActivation+0×2c (FPO: [7,0,0])
0f 0012a438 769a5c4a 76a9714c 0012a728 00000000 ole32!CProcessActivator::ActivateByContext+0×42 (FPO: [6,2,4])
10 0012a460 769a5a11 76a9714c 0012a8cc 0012ae08 ole32!CProcessActivator::GetClassObject+0×48 (FPO: [3,1,4])
11 0012a498 769a5a2b 0012a8cc 0012ae08 0012ae08 ole32!ActivationPropertiesIn::DelegateGetClassObject+0xf3 (FPO: [2,7,0])
12 0012a4c0 769a5a11 76a97114 00000001 0012ae08 ole32!CClientContextActivator::GetClassObject+0×88 (FPO: [3,3,0])
13 0012a4f8 769a58a5 0012a8cc 0012ae08 003a0043 ole32!ActivationPropertiesIn::DelegateGetClassObject+0xf3 (FPO: [2,7,0])
GetToSTA Stack이 보이면 일단 첫번째 Parameter를 통해서 Message Loop Thread를 찾을 수 있습니다.

0:000> dd 001843e8
001843e8  76a96fa0 00184370 00000954 00000c4c
001843f8  f519ce96 35030aaa 36910894 e95a1c1c
00184408  36910894 e95a1c1c 00004801 0c4c0954
00184418  88616a96 b4e88b85 00000103 00060176
00184428  001872c8 00000000 00000000 00000000
00184438  00000001 ffffffff 00184d10 0018871c
00184448  00000008 00000000 00000000 00000000
00184458  baadf00d 00070005 001844d8 f1eef1ee

0:000> ~
.  0  Id: 954.a10 Suspend: 1 Teb: 7ffdf000 Unfrozen
   1  Id: 954.2f8 Suspend: 1 Teb: 7ffde000 Unfrozen
   2  Id: 954.c4c Suspend: 1 Teb: 7ffdc000 Unfrozen
   3  Id: 954.e90 Suspend: 1 Teb: 7ffdd000 Unfrozen
   4  Id: 954.f8c Suspend: 1 Teb: 7ffdb000 Unfrozen
   5  Id: 954.7fc Suspend: 1 Teb: 7ffd9000 Unfrozen
   6  Id: 954.710 Suspend: 1 Teb: 7ffd8000 Unfrozen
   7  Id: 954.e00 Suspend: 1 Teb: 7ffd7000 Unfrozen
   8  Id: 954.bac Suspend: 1 Teb: 7ffd6000 Unfrozen
0:000> ~2s;kvn
eax=00000000 ebx=00000000 ecx=7ffdc000 edx=00000000 esi=7c9ae178 edi=00000000
eip=7c93e514 esp=028bf7f4 ebp=028bf87c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0048  es=1518  fs=003b  gs=5da8             efl=00000246
ntdll!KiFastSystemCallRet:
7c93e514 c3              ret
 # ChildEBP RetAddr  Args to Child             
00 028bf7f0 7c93df5a 7c94b24b 00000480 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
01 028bf7f4 7c94b24b 00000480 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc (FPO: [3,0,0])
02 028bf87c 7c931046 019ae178 7c944a53 7c9ae178 ntdll!RtlpWaitForCriticalSection+0×132 (FPO: [1,26,4])
03 028bf884 7c944a53 7c9ae178 00000000 028bf984 ntdll!RtlEnterCriticalSection+0×46 (FPO: [1,0,0])
04 028bf8c0 7c9468f0 00000000 00000000 028bf904 ntdll!LdrLockLoaderLock+0×146 (FPO: [Non-Fpo])
05 028bf934 7c9466b8 00000001 00000001 00000000 ntdll!LdrGetDllHandleEx+0×8b (FPO: [Non-Fpo])
06 028bf950 7c7de534 00000001 00000000 028bf9d4 ntdll!LdrGetDllHandle+0×18 (FPO: [4,0,0])
07 028bf9a0 7c7de64b 028bf9d4 00000000 028bfea0 kernel32!GetModuleHandleForUnicodeString+0×1d (FPO: [Non-Fpo])
08 028bfe24 7c7de4fc 00000001 00000002 028bfec8 kernel32!BasepGetModuleHandleExW+0×18e (FPO: [Non-Fpo])
09 028bfe3c 00c5ec0e 028bfec8 028bffdc 00c5ec1a kernel32!GetModuleHandleW+0×29 (FPO: [1,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
0a 028bfe90 7c93e473 028bfea0 00000074 00000074 hook1+0×1ec0e
0b 028bff10 77cf91be 77cf91f1 028bff54 00000000 ntdll!KiUserCallbackDispatcher+0×13 (FPO: [0,0,0])
0c 028bff14 77cf91f1 028bff54 00000000 00000000 USER32!NtUserGetMessage+0xc

0d 77cf91be 90909090 8b55ff8b 10558bec 0b144d8b USER32!GetMessageW+0×33 (FPO: [4,0,4])
0e 77cf91be 00000000 8b55ff8b 10558bec 0b144d8b 0×90909090

TID가 c4c 인 Thread를 보면 GetMessageW를 호출하고 있는 것을 볼 수 있습니다. 그리고 그위로 Loader Lock이 걸려 있다는 것을 확인 할 수 있습니다. Stack이 깨져 있

기 때문에 좀더 확실하게 Stack Thread를 해보죠.( ※ Stack이 깨져 보이는 이유는 정확한 Symbol이 없는 상태( FPO가 정확하지 않은 상태) 에서 Nt* 형태의 User To

Kernel Trap이 발생하는 함수를 호출했기 때문입니다. )
0:002> !teb
TEB at 7ffdc000
    ExceptionList:        028bf8b0
    StackBase:            028c0000
    StackLimit:           028bc000

    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7ffdc000
    EnvironmentPointer:   00000000
    ClientId:             00000954 . 00000c4c
    RpcHandle:            00000000
    Tls Storage:          00000000
    PEB Address:          7ffda000
    LastErrorValue:       126
    LastStatusValue:      c0000135
    Count Owned Locks:    0
    HardErrorMode:        0
0:002> dps 028bff14 028c0000
028bff14  77cf91be USER32!NtUserGetMessage+0xc
028bff18  77cf91f1 USER32!GetMessageW+0×33

028bff1c  028bff54
028bff20  00000000
028bff24  00000000
028bff28  00000000
028bff2c  77cf91c6 USER32!GetMessageW
028bff30  028bff70
028bff34  769c0471 ole32!CDllHost::STAWorkerLoop+0×72
028bff38  028bff54
028bff3c  00000000
028bff40  00000000
028bff44  00000000
028bff48  00000000
028bff4c  76a96c28 ole32!gATHost
028bff50  00000000
028bff54  00060176
028bff58  00000400
028bff5c  0000babe
028bff60  00195b6c
028bff64  005fd15b
028bff68  00000377
028bff6c  000002f5
028bff70  028bff8c
028bff74  769b6949 ole32!CDllHost::WorkerThread+0xc8
028bff78  00007530
028bff7c  7c7d2550 kernel32!WaitForSingleObjectEx
028bff80  00186fb0
028bff84  000005a8
028bff88  00187190
028bff8c  028bff94
028bff90  769b687c ole32!DLLHostThreadEntry+0xd
028bff94  028bffb4
028bff98  7698e3ee ole32!CRpcThread::WorkerLoop+0×1e
028bff9c  76a96c28 ole32!gATHost
028bffa0  00000000
028bffa4  00186fb0
028bffa8  76970000 ole32!_imp__RegQueryValueExA <PERF> (ole32+0×0)
028bffac  7698e456 ole32!CRpcThreadCache::RpcWorkerThreadEntry+0×1b
028bffb0  0012d22e
028bffb4  028bffec
028bffb8  7c7db729 kernel32!BaseThreadStart+0×37
028bffbc  00186fb0
028bffc0  00000000
028bffc4  0012d22e
028bffc8  00186fb0
028bffcc  7ffdc000
028bffd0  86faf600
028bffd4  028bffc0
028bffd8  8686d7c0
028bffdc  ffffffff
028bffe0  7c809ad8 kernel32!_except_handler3
028bffe4  7c7db730 kernel32!`string’+0×88
028bffe8  00000000
028bffec  00000000
028bfff0  00000000
028bfff4  7698e43b ole32!CRpcThreadCache::RpcWorkerThreadEntry
028bfff8  00186fb0
028bfffc  00000000
028c0000  00000000

RawStack에 보이는것과 같이 STA Work Thread Loop 인것을 확인 할 수 있습니다. 측 STA Thread의 Message를 처리 하기 위한 Loop라는 이야기 입니다. 실제로 769c0471을 Reverse 하면 GetMessage를 호출하는 것을 볼 수 있습니다.

0:002> ub 769c0471
ole32!CDllHost::STAWorkerLoop+0×2e:
769c045d mov     esi,dword ptr [ole32!_imp__GetMessageW (7697187c)]
769c0463 mov     dword ptr [ole32!gTimerId (76a9974c)],eax
769c0468 push    edi
769c0469 push    edi
769c046a lea     eax,[ebp-1Ch]
769c046d push    edi
769c046e push    eax
769c046f call    esi

그렇다면 STA Work Thread가 선점해야하는 Critical Section을 확인해 보죠.

0:002> !cs 7c9ae178
—————————————–
Critical section   = 0×7c9ae178 (ntdll!LdrpLoaderLock+0×0)
DebugInfo          = 0×7c9ae1a0
LOCKED
LockCount          = 0×7
OwningThread       = 0×00000a10
RecursionCount     = 0×2
LockSemaphore      = 0×480
SpinCount          = 0×00000000

STA Work Thread는 a10이 선점하고 있는 Critical Section을 기다리고 있군요. a10 Thread를 확인하면 최초의 Main Thread라는 것을 알 수 있습니다. 그렇다면 여기서 2 가지 의문만 풀면 문제를 해결 할 수 있게 됩니다.

1. GetMessageW 호출후 왜 Kernel To User Callback 에서 hook1.dll의 함수를 호출 했고 이 함수는 왜 Load Lock의 선점을 필요한 GetModuleHandleW를 호출 했는가 ?

2. Main Thread에서 기다리고 있는 Event는 어디서 해제를 해주어야하는가?
ntdll!KiUserCallbackDispatcher에서 hook1.dll의 함수를 호출했다는 것은 tacuil.dll에서 Hooking을 시도 했다는 의미로 보여 집니다. 그리고 GetMessage후에 KiUserCallbackDispatcher 가 호출되는 경우는 대부분 USER32!DispatchClientMessage( __fnDWORD를 통해서 호출 된다. )가 호출 되는 경우이지만, 이 경우는 또다른 Process에 의한 Message Hooking 모듈이 붙어 있을 가능성이 높음을 추정할 수 있습니다. 만약 Global Message Hooking 모듈이 존재 한다면 아래와 같은 스택이 구성되는 경우가 보통입니다.

Message Hooking 모듈 존재시 Stack 구성 예제

00 0007fbdc 7c93e473 0007fbec 00000080 00000080 USER32!__ClientLoadLibrary (FPO: [1,3,0])
01 0007fc68 77d1e1ad 77d1e18a 00000000 000000a0 ntdll!KiUserCallbackDispatcher+0×13 (FPO: [0,0,0])

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for F:\Program Files\NATEON\BIN\NateOnHook40u.dll -
02 0007fc90 1000120c 00020bd0 00000000 000000a0 USER32!NtUserCallNextHookEx+0xc
WARNING: Stack unwind information not available. Following frames may be wrong.

0d 0007fed8 01002a1b 0007fefc 00000000 00000000 USER32!NtUserGetMessage+0xc
0e 0007ff1c 01007511 01000000 00000000 000a2326 notepad!WinMain+0xe5 (FPO: [4,8,0])
0f 0007ffc0 7c7e7077 0043f6f2 0043f73a 7ffd8000 notepad!WinMainCRTStartup+0×174 (FPO: [Non-Fpo])
10 0007fff0 00000000 0100739d 00000000 78746341 kernel32!BaseProcessStart+0×23 (FPO: [Non-Fpo])

__ClientLoadLibray의 첫번째 Parameter
0:000> dc 0007fbec
0007fbec  00000080 00000058 00000001 b6138d60  ….X…….`…
0007fbfc  00000024 00000000 00560054 00000028  $…….T.V.(…
0007fc0c  00000000 0000001c 003a0046 0050005c  ……..F.:.\.P.
0007fc1c  006f0072 00720067 006d0061 00460020  r.o.g.r.a.m. .F.
0007fc2c  006c0069 00730065 0057005c 0072006f  i.l.e.s.\.W.o.r.
0007fc3c  004d006b 00730065 00650073 0067006e  k.M.e.s.s.e.n.g.
0007fc4c  00720065 0064005c 0072006f 0061006d  e.r.\.d.o.r.m.a.
0007fc5c  0074006e 0064002e 006c006c b6130000  n.t…d.l.l…..

__ClientLoadLibrary가 호출 되었을 가능성을 생각 할 때 tacuil.dll의 호출부의 첫번째 Data를 확인해 보도록하자.

0:002> dc 028bfea0
028bfea0  00000074 0000004c 00000001 a8e51844  t…L…….D…
028bfeb0  00000024 00000000 004a0048 028bfec8  $…….H.J…..
028bfec0  00000000 0000001c 003a0043 0057005c  ……..C.:.\.W.
028bfed0  004e0049 004f0044 00530057 0073005c  I.N.D.O.W.S.\.s.
028bfee0  00730079 00650074 0033006d 005c0032  y.s.t.e.m.3.2.\.
028bfef0  00770064 00680073 006e0065 00690067  d.w.s.h.e.n.g.i.
028bff00  0065006e 00300038 0044002e 004c004c  n.e.8.0…D.L.L.

거의 비슷한 형태임을 알 수 있죠. __ClientLoadLibary가 호출한 어떤 함수를 Hooking하고 있음을 추정할 수 있습니다.

0:002> uf /c USER32!__ClientLoadLibrary
USER32!__ClientLoadLibrary (77d08023)
  USER32!__ClientLoadLibrary+0×21 (77d08044):
    call to USER32!FixupCallbackPointers (77d0b1f4)
  USER32!__ClientLoadLibrary+0×2c (77d0804f):
    call to kernel32!LoadLibraryExW (7c7d1af5)

  USER32!__ClientLoadLibrary+0×41 (77d08064):
    call to USER32!InitUserApiHook (77d07eda)
  USER32!__ClientLoadLibrary+0×4b (77d0806e):
    call to kernel32!FreeLibrary (7c7dac7e)
  USER32!__ClientLoadLibrary+0×5d (77d08080):
    call to USER32!XyCallbackReturn (77cf94a4)

0:002> uf kernel32!LoadLibraryExW
hook1+0×1ebe8:
00c5ebe8 push    ebp
00c5ebe9 mov     ebp,esp
00c5ebeb push    ecx
00c5ebec push    ebx
00c5ebed push    esi
00c5ebee push    edi
00c5ebef call    hook1+0×11928 (00c51928)
00c5ebf4 mov     dword ptr [ebp-4],eax
00c5ebf7 xor     eax,eax
00c5ebf9 push    ebp
00c5ebfa push    offset hook1+0×1ec1a (00c5ec1a)
00c5ebff push    dword ptr fs:[eax]
00c5ec02 mov     dword ptr fs:[eax],esp
00c5ec05 mov     eax,dword ptr [ebp+8]
00c5ec08 push    eax
00c5ec09 call    hook1+0×11958 (00c51958)
00c5ec0e mov     esi,eax
00c5ec10 xor     eax,eax
00c5ec12 pop     edx
00c5ec13 pop     ecx
00c5ec14 pop     ecx
00c5ec15 mov     dword ptr fs:[eax],edx
00c5ec18 jmp     hook1+0×1ec26 (00c5ec26)

0:002> uf 00c51958
hook1+0×11958:
00c51958 jmp     dword ptr [hook1+0x2a1e4 (00c6a1e4)]

kernel32!GetModuleHandleW:
7c7de4dd mov     edi,edi
7c7de4df push    ebp
7c7de4e0 mov     ebp,esp
7c7de4e2 cmp     dword ptr [ebp+8],0
7c7de4e6 je      kernel32!GetModuleHandleW+0xb (7c802694)

kernel32!GetModuleHandleW+0×19:
7c7de4ec lea     eax,[ebp+8]
7c7de4ef push    eax
7c7de4f0 push    dword ptr [ebp+8]
7c7de4f3 push    2
7c7de4f5 push    1
7c7de4f7 call    kernel32!BasepGetModuleHandleExW (7c7de559)
7c7de4fc neg     eax
7c7de4fe sbb     eax,eax
7c7de500 and     eax,dword ptr [ebp+8]

kernel32!GetModuleHandleW+0×30:
7c7de503 pop     ebp
7c7de504 ret     4

kernel32!GetModuleHandleW+0xb:
7c802694 mov     eax,dword ptr fs:[00000018h]
7c80269a mov     eax,dword ptr [eax+30h]
7c80269d mov     eax,dword ptr [eax+8]
7c8026a0 jmp     kernel32!GetModuleHandleW+0×30 (7c7de503)
Loader Lock과 관련된 함수인 kernel32!LoadLibraryExW가 후킹되어 알 수 있고 또한 hook1+0×11958 (00c51958)을 통해서 kernel32!GetModuleHandleW가 호출 되고 있음을 추정할 수 있습니다.

그렇다면 GetModuleHandleW를 호출한 것이 문제의 원인 인가 ???

실제로 GetModuleHandleW를 호출하지 않고 바로 LoadLibraryExW를 호출하였다고 하더라도 문제는 맞찮가지죠. LoadLibraryExW 역시 Loader Lock를 사용하고 있기 때문입니다. 일단 STA Message Loop에서 Loader Lock이 걸리는 원인은 파악 된것 같습니다. 바로 Message Hooking 모듈이라는 것 때문입니다.

==> STA Message Loop 에서 Loader Lock을 사용하는 이유는 Message Hooking 모듈을 로드하기 위해서라는 것이 원인

그럼 이제 2번째 의문점을 파악해 보도록 하죠.
00 00129d48 7c93df5a 7c7d25db 000004a8 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
01 00129d4c 7c7d25db 000004a8 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc (FPO: [3,0,0])
02 00129db0 7c7d2542 000004a8 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xa8 (FPO: [Non-Fpo])
03 00129dc4 769b0647 000004a8 ffffffff 001843e8 kernel32!WaitForSingleObject+0×12 (FPO: [2,0,0])
04 00129de0 76a91e50 001843e8 00195b68 00000000 ole32!GetToSTA+0×6f (FPO: [2,0,4])
05 00129e00 76a9108a 00129ec8 00129fd8 00184fa4 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0xf6 (FPO: [1,1,4])
06 00129ee0 769bce1a 00184fa4 00129fd8 00129fc8 ole32!CRpcChannelBuffer::SendReceive2+0xc8 (FPO: [3,50,4])
07 00129f4c 769bcdb2 00184fa4 00129fd8 00129fc8 ole32!CAptRpcChnl::SendReceive+0xab (FPO: [3,19,4])

4a8 handle을 무한대기 하고 있는 것이 보입니다. STA Component 특성상 GetToSTA에서 기다리는 Event는 STA Message Loop Thread에서 Set 하게 되어 있습니다.

Thread1 Wait( GetToSTA ) ==> Thread2 Message 처리 ==> Thread2 SetEvent ==> Thread1 Release( GetToSTA )

이러한 과정을 반복하면서 STA Model은 Work Thread 동작 및 Message 처리를 모두 하도록 되어 있습니다.
0:002> uf /c ole32!CDllHost::WorkerThread
ole32!CDllHost::WorkerThread (769b6885)
  ole32!CDllHost::WorkerThread+0×27 (769b68ac):
    call to ole32!COleTls::TLSAllocData (7698de72)
  ole32!CDllHost::WorkerThread+0×5c (769b68e1):
    call to ole32!CoInitializeEx (7698ef7b)
  ole32!CDllHost::WorkerThread+0×70 (769b68f5):
    call to kernel32!InterlockedIncrement (7c7d9806)
  ole32!CDllHost::WorkerThread+0×76 (769b68fb):
    call to ole32!GetCurrentApartmentId (7698d26b)
  ole32!CDllHost::WorkerThread+0×7e (769b6903):
    call to kernel32!GetCurrentThreadId (7c7d97d0)
  ole32!CDllHost::WorkerThread+0×89 (769b690e):
    call to ole32!CDllHost::Marshal (769b6596)
  ole32!CDllHost::WorkerThread+0×9a (769b691f):
    call to ole32!GetCurrentApartmentToken (769b6961)
  ole32!CDllHost::WorkerThread+0xaf (769b6934):
    call to kernel32!SetEvent (7c7da0b7)

  ole32!CDllHost::WorkerThread+0xc3 (769b6944):
    call to ole32!CDllHost::STAWorkerLoop (769c042f)
  ole32!CDllHost::WorkerThread+0xbc (769f6ff4):
    call to ole32!CDllHost::MTAWorkerLoop (76a26ad0)
  ole32!CDllHost::WorkerThread+0xd8 (769f7004):
    call to ole32!wCoUninitialize (7698f1f2)
  ole32!CDllHost::WorkerThread+0xe6 (769f7012):
    call to kernel32!InterlockedDecrement (7c7d981a)
  ole32!CDllHost::WorkerThread+0xf9 (769f7025):
    call to kernel32!SetEvent (7c7da0b7)

WorkerThread에서 SetEvent을 호출 하는 것을 확인할 수 있습니다. 이제 이 Hang 현상의 답을 찾은거 같군요.

Loader Lock + STA Thread + Message Hooking 모듈 이라는 굉장히 평범하지 않는 내용이군요.

Enjoy Debugging

Message Hook Chain을 구하는 방법

Icesword라는 프로그램을 보면 Message Hook Chain을 구하는 기능을 가지고 있습니다. Message Hooking시 타모듈과의 충돌을 분석할때 유용하게 사용하곤합니다. 그렇다면 과연 이러한 Message Hook chain은 어디에 등록되어 있을까 ?

간단히 Win32 Thread의 Teb를 확인하면 추적이 가능합니다.

0: kd> !process 857295a0 3
PROCESS 857295a0 SessionId: 0 Cid: 0788 Peb: 7ffdd000 ParentCid: 06c4
DirBase: 064001e0 ObjectTable: e17d5a30 HandleCount: 45.
Image: notepad.exe
VadRoot 858351f0 Vads 67 Clone 0 Private 202. Modified 17. Locked 0.
DeviceMap e190da40
Token e1f29d48
ElapsedTime 00:00:03.062
UserTime 00:00:00.015
KernelTime 00:00:00.078
QuotaPoolUsage[PagedPool] 63204
QuotaPoolUsage[NonPagedPool] 2680
Working Set Sizes (now,min,max) (926, 50, 345) (3704KB, 200KB, 1380KB)
PeakWorkingSetSize 931
VirtualSize 31 Mb
PeakVirtualSize 36 Mb
PageFaultCount 1007
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 468

THREAD 85775518 Cid 0788.054c Teb: 7ffdf000 Win32Thread: e1ff3008 WAIT: (WrUserRequest) UserMode Non-Alertable
84976d98 SynchronizationEvent

일반적인 Notepad의 Process와 Thread입니다. 이 중에서 우리에게 필요한 부분인 Message Hook Chain에 대한 정보는 Thread의 TEB에 포함되어 있습니다.

0: kd> .process 857295a0
Implicit process is now 857295a0

0: kd> dt _TEB 7ffdf000
ntdll!_TEB
   +0×000 NtTib            : _NT_TIB
   +0×01c EnvironmentPointer : (null)
   +0×020 ClientId         : _CLIENT_ID
   +0×028 ActiveRpcHandle  : (null)
   +0×02c ThreadLocalStoragePointer : (null)
   +0×030 ProcessEnvironmentBlock : 0×7ffdd000 _PEB
   +0×034 LastErrorValue   : 0
   +0×038 CountOfOwnedCriticalSections : 0
   +0×03c CsrClientThread  : (null)
   +0×040 Win32ThreadInfo  : 0xe1ff3008
   +0×044 User32Reserved   : [26] 0
   +0×0ac UserReserved     : [5] 0
   +0×0c0 WOW32Reserved    : (null)
   +0×0c4 CurrentLocale    : 0×412
   +0×0c8 FpSoftwareStatusRegister : 0
   +0×0cc SystemReserved1  : [54] (null)
   +0×1a4 ExceptionCode    : 0
   +0×1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
 … 생략

Thread TEB를 확인하면 Win32ThreadInfo라는 녀석이 존재합니다.  기본적으로  PVOID 형을 가지고 있는데 물론 Undocument 된 부분이라 이렇게 표시 되죠 .
Win32ThreadInfo의 메모리에 있는 데이터는 MessageQueue, KeyboardLayout, Desktop, DesktopInfo, WindowList 등 Win32를 UI를 구동하는데 필요한 데이터들을 포함하고 있습니다.
그리고 Global Message Hook과 Local Message Hook에 대한 정보는 Win32ThreadInfo,  DesktopInfo 정보를 통해서 추적이 가능합니다.
0: kd> dt finter!_THREADINFO 0xe1ff3008
   +0×000 W32Thread        : W32THREAD
   +0×028 ptl              : (null)
   +0×02c ppi              : 0xe10531c8
   +0×030 pq               : 0xe1e4a9e8 _Q
   +0×034 spklActive       : 0xe147db58
   +0×038 pcti             : 0xbc638820
   +0×03c rpdesk           : 0×858afe88
   +0×040 pDeskInfo        : 0xbc630650 _DESKTOPINFO
   +0×044 pClientInfo      : 0×7ffdf6cc
   +0×048 TIF_flags        : 0×1100000
   +0×04c pstrAppName      : (null)
   +0×050 psmsSent         : (null)
   +0×054 psmsCurrent      : (null)
   +0×058 psmsReceiveList  : (null)
   +0×05c timeLast         : 2543531
   +0×060 idLast           : 0xe11bab48
   +0×064 cQuit            : 0
   +0×064 exitCode         : 0
   +0×068 hdesk            : 0×0000002c
   +0×06c cPaintsReady     : 0
   +0×070 cTimersReady     : 0
   +0×074 pMenuState       : (null)
   +0×078 ptdb             : (null)
   +0×078 pwinsta          : (null)
   +0×07c psiiList         : (null)
   +0×080 dwExpWinVer      : 0×400
   +0×084 dwCompatFlags    : 0
   +0×088 dwCompatFlags2   : 0
… 생략
   +0×0e0 fsReserveKeys    : 0
   +0×0e4 apEvent          : (null)
   +0×0e8 amdesk           : 0xf01ff
   +0×0ec cWindows         : 6
   +0×0f0 cVisWindows      : 0
   +0×0f4 aphkStart        : [16] (null)

간단히 정리한 구조체를 이용해서 Win32ThreadInfo의 데이터를 확인해 봤습니다. 이 중 _DESKTOPINFO의 값을 다시 확인해보죠

0: kd> dt _DESKTOPINFO 0xbc630650
finter!_DESKTOPINFO
   +0×000 pvDesktopBase    : 0xbc630000
   +0×004 pvDesktopLimit   : 0xbc930000
   +0×008 spwnd            : 0xbc6306e8
   +0×00c fsHooks          : 0×850
   +0×010 aphkStart        : [16] (null)
   +0×050 spwndShell       : 0xbc63ead8
   +0×054 ppiShellProcess  : 0xe1956448
   +0×058 spwndBkGnd       : 0xbc63f2e0
   +0×05c spwndTaskman     : 0xbc63e548
   +0×060 spwndProgman     : (null)
   +0×064 pvwplShellHook   : 0xe1ce8ea0
   +0×068 cntMBox          : 0

Win32ThreadInfo의 aphkStart Member는 Local Hook의 데이터를 나타내는 Array고 DesktopInfo의 aphkStart Member는 Global Hook의 데이터를 나타내는 Arrary 입니다. 각각 Array의 인자는 HOOK Structure를 나타내며 각 Index는 Hooking Type을 나타내도록 구성됩니다.
이제 이 Array 안의 데이터를 확인해보겠습니다.

0: kd> dps 0xe1ff3008+0xf4 L16
e1ff30fc  00000000
e1ff3100  00000000
e1ff3104  00000000
e1ff3108  bc636de0
e1ff310c  00000000
e1ff3110  00000000
e1ff3114  00000000
e1ff3118  00000000
e1ff311c  bc636e20
e1ff3120  00000000
e1ff3124  00000000
e1ff3128  00000000
e1ff312c  00000000
e1ff3130  00000000
e1ff3134  00000000
e1ff3138  00000000
e1ff313c  00000000
e1ff3140  00000000
e1ff3144  00000000
e1ff3148  00000000
e1ff314c  00000000
e1ff3150  00000000

0: kd> dps 0xbc630650+0×10 L16
bc630660  00000000
bc630664  00000000
bc630668  00000000
bc63066c  00000000
bc630670  bc640530
bc630674  00000000
bc630678  bc640570
bc63067c  00000000
bc630680  00000000
bc630684  00000000
bc630688  00000000
bc63068c  bc6404f0
bc630690  00000000
bc630694  00000000
bc630698  00000000
bc63069c  00000000
bc6306a0  bc63ead8
bc6306a4  e1956448
bc6306a8  bc63f2e0
bc6306ac  bc63e548
bc6306b0  00000000
bc6306b4  e1ce8ea0
0: kd> dt _HOOK bc636e20
npkfinder!_HOOK
   +0×000 head             : _THRDESKHEAD
   +0×014 phkNext          : (null)
   +0×018 iHook            : 7
   +0×01c offPfn           : 0×0000ff89
   +0×020 flags            : 2
   +0×024 ihmod            : 1
   +0×028 ptiHooked        : 0xe1ff3008 _THREADINFO
   +0×02c rpdesk           : (null)
이 중 하나를 열어서 보면 위와 같은 구조로 이루어져 있습니다. phkNexe 인자는 다음 Hook Chain을 가르키는 인자인데 현재는 하나의 Hooking Function만이 등록되어 있는 상태군요 .

IceSword의 경우는 이러한 원리( ?? ) 를 이용해서 아마도 Hook Chain을 보여주는것 같습니다.  위에 설명한  Hook Chain를 잘이용하면 아래와 같은 샘플도 만들수 있죠. ( 샘플소스는 개인적으로 문의해주세요 …)

조금이나마 도움이 되셨으면합니다.

Enjoy Debugging…