Tag Archive for 'Debugging'

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

[생활속의 Debugging] sptd.sys 는 왜 Registry를 감시 ??

데몬 Tools를 설치하게 되면 sptd.sys라는 녀석도 함께 설치됩니다. 이녀석은 머하는 녀석이지 ??
일단 무슨 함수를 Hooking하고 있을까 ?? dds 명령으로 KiServiceTable을 체크해 봤습니다.

804e6948 805b1900 nt!NtCreateIoCompletion
804e694c 805e72ca nt!NtCreateJobObject
804e6950 80638053 nt!NtCreateJobSet
804e6954 f750bb3a*** ERROR: Module load completed but symbols could not be loaded for sptd.sys
sptd+0×5b3a // nt!NtCreateKey

804e6958 805e898a nt!NtCreateMailslotFile
804e695c 8057dccd nt!NtCreateMutant
804e6960 805891c8 nt!NtCreateNamedPipeFile
….
804e69c4 8057e101 nt!NtDuplicateToken
804e69c8 8064ff97 nt!NtSetBootOptions
804e69cc f750bc7e sptd+0×5c7e // nt!NtEnumerateKey
804e69d0 8064ff6f nt!NtEnumerateSystemEnvironmentValuesEx
804e69d4 f750bff6 sptd+0×5ff6 // nt!NtEnumerateValueKey
804e69d8 8062d9b9 nt!NtExtendSection

804e6a84 806216bb nt!NtOpenIoCompletion
804e6a88 806382ab nt!NtOpenJobObject
804e6a8c f750ba18 sptd+0×5a18//nt!NtOpenKey
804e6a90 8057dd7b nt!NtOpenMutant
804e6a94 805b2aa6 nt!NtOpenObjectAuditAl

804e6b28 806510bb nt!NtQueryIntervalProfile
804e6b2c 8062177c nt!NtQueryIoCompletion
804e6b30 f750c0c0 sptd+0×60c0//nt!NtQueryKey
804e6b34 80655cc4 nt!NtQueryMultipleValueKey
804e6b38 80650a42 nt!NtQueryMutant

804e6b6c 805b1436 nt!NtQueryTimer
804e6b70 8058bb31 nt!NtQueryTimerResolution
804e6b74 f750bf58 sptd+0×5f58 // nt!NtQueryValueKey
804e6b78 80583657 nt!NtQueryVirtualMemory
804e6b7c 8057f676 nt!NtQueryVolumeInformationFile

804e6c84 805ec4ff nt!NtSetTimerResolution
804e6c88 805e2051 nt!NtSetUuidSeed
804e6c8c f750c148 sptd+0×6148 // nt!NtSetValueKey
804e6c90 80622901 nt!NtSetVolumeInformationFile
804e6c94 8064e983 nt!NtShutdownSystem

nt!NtCreateKey, nt!NtEnumerateKey, nt!NtEnumerateValueKey, nt!NtOpenKey, nt!NtQueryKey, nt!NtSetValueKey
레지스트 Key 관련 함수들을 주루룩 Hooking하고 있군요.. 무슨 이유에서인지 .. 설마 ?? Process도 감시하고 있나 ??

lkd> dd nt!PspCreateProcessNotifyRoutine
8056a6e0 e18312d7 e229edef e19f3057 e3270597
8056a6f0 00000000 00000000 00000000 00000000
8056a700 00000004 00000000 8a1fcec8 8814f7b8
8056a710 8a3d5f18 8a3d5030 8a3d5e00 00000000
8056a720 00000000 00000000 00000000 00000000
8056a730 00000000 00000000 00000000 00000000
8056a740 00000000 6d6f7441 00000000 00000001
8056a750 00000000 00000000 00000000 00000000
lkd> dd e18312d0
e18312d0 00000010 f75159ac 00000000 00590052
e18312e0 00020803 61564d43 e1028820 e10281f8
e18312f0 0c020802 6944624f e1012930 e18235b8
e1831300 0c020802 6944624f e2004688 8a2db338
e1831310 0c030802 6d4e624f 00300030 00300030
e1831320 00300030 00610034 0c030803 75737050
e1831330 00750061 00730064 00750074 00000062
e1831340 00010803 61564d43 0c050801 7346744e

lkd> u f75159ac
sptd+0xf9ac:

f75159ac 55 push ebp
f75159ad 8bec mov ebp,esp
f75159af 807d1000 cmp byte ptr [ebp+10h],0
f75159b3 0f85e8000000 jne sptd+0xfaa1 (f7515aa1)
f75159b9 56 push esi
f75159ba 57 push edi
f75159bb 33ff xor edi,edi
f75159bd ff154c6052f7 call dword ptr [sptd+0x2004c (f752604c)]

역시나 Process Notify를 등록하고 있군요 .  설마 File System도 Hooking 하는건 아닐까 ???

lkd> !drvobj ntfs 2
Driver object (8a312380) is for:
\FileSystem\Ntfs
DriverEntry: f7bd7184 Ntfs!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: f752a95e sptd
AddDevice: 00000000
Dispatch routines:
[00] IRP_MJ_CREATE 8a37c0e8 +0×8a37c0e8
[01] IRP_MJ_CREATE_NAMED_PIPE 804fb709 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE 8a37c0e8 +0×8a37c0e8
[03] IRP_MJ_READ 8a37c0e8 +0×8a37c0e8
[04] IRP_MJ_WRITE 8a37c0e8 +0×8a37c0e8
[05] IRP_MJ_QUERY_INFORMATION 8a37c0e8 +0×8a37c0e8
[06] IRP_MJ_SET_INFORMATION 8a37c0e8 +0×8a37c0e8
[07] IRP_MJ_QUERY_EA 8a37c0e8 +0×8a37c0e8
[08] IRP_MJ_SET_EA 8a37c0e8 +0×8a37c0e8
[09] IRP_MJ_FLUSH_BUFFERS 8a37c0e8 +0×8a37c0e8
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION 8a37c0e8 +0×8a37c0e8
[0b] IRP_MJ_SET_VOLUME_INFORMATION 8a37c0e8 +0×8a37c0e8
[0c] IRP_MJ_DIRECTORY_CONTROL 8a37c0e8 +0×8a37c0e8
[0d] IRP_MJ_FILE_SYSTEM_CONTROL 8a37c0e8 +0×8a37c0e8
[0e] IRP_MJ_DEVICE_CONTROL 8a37c0e8 +0×8a37c0e8

[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 804fb709 nt!IopInvalidDeviceRequest
[10] IRP_MJ_SHUTDOWN 8a37c0e8 +0×8a37c0e8
[11] IRP_MJ_LOCK_CONTROL 8a37c0e8 +0×8a37c0e8
[12] IRP_MJ_CLEANUP 8a37c0e8 +0×8a37c0e8

[13] IRP_MJ_CREATE_MAILSLOT 804fb709 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY 8a37c0e8 +0×8a37c0e8
[15] IRP_MJ_SET_SECURITY 8a37c0e8 +0×8a37c0e8

[16] IRP_MJ_POWER 804fb709 nt!IopInvalidDeviceRequest
[17] IRP_MJ_SYSTEM_CONTROL 804fb709 nt!IopInvalidDeviceRequest
[18] IRP_MJ_DEVICE_CHANGE 804fb709 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA 8a37c0e8 +0×8a37c0e8
[1a] IRP_MJ_SET_QUOTA 8a37c0e8 +0×8a37c0e8
[1b] IRP_MJ_PNP 8a37c0e8 +0×8a37c0e8

Fast I/O routines:
FastIoCheckIfPossible f7b8beda Ntfs!NtfsFastIoCheckIfPossible
FastIoRead f7b72b57 Ntfs!NtfsCopyReadA
FastIoWrite f7b91448 Ntfs!NtfsCopyWriteA
FastIoQueryBasicInfo f7b7848e Ntfs!NtfsFastQueryBasicInfo
FastIoQueryStandardInfo f7b76f7e Ntfs!NtfsFastQueryStdInfo
FastIoLock f7b920f2 Ntfs!NtfsFastLock
FastIoUnlockSingle f7b921f8 Ntfs!NtfsFastUnlockSingle
FastIoUnlockAll f7bcb6ae Ntfs!NtfsFastUnlockAll
FastIoUnlockAllByKey f7bcb7f3 Ntfs!NtfsFastUnlockAllByKey
AcquireFileForNtCreateSection f7b7283a Ntfs!NtfsAcquireForCreateSection
ReleaseFileForNtCreateSection f7b72881 Ntfs!NtfsReleaseForCreateSection
FastIoQueryNetworkOpenInfo f7bb9e1d Ntfs!NtfsFastQueryNetworkOpenInfo
AcquireForModWrite f7b7ea10 Ntfs!NtfsAcquireFileForModWrite
MdlRead f7bb9f31 Ntfs!NtfsMdlReadA
MdlReadComplete 805322b8 nt!FsRtlMdlReadCompleteDev
PrepareMdlWrite f7bba2ab Ntfs!NtfsPrepareMdlWriteA
MdlWriteComplete 8061d1db nt!FsRtlMdlWriteCompleteDev
FastIoQueryOpen f7b76db8 Ntfs!NtfsNetworkOpenCreate
AcquireForCcFlush f7b726e2 Ntfs!NtfsAcquireFileForCcFlush
ReleaseForCcFlush f7b72708 Ntfs!NtfsReleaseFileForCcFlush

역시나 sptd가 Major Function들을 과함하게 Hook 하고 있군요.  과연 이 녀석의 정체는 도대체 멀까요 ?? ㅜ.ㅜ
( 귀찮아서 리버싱은 ㅜ.ㅜ )

[생활속의 Debugging] dexplore.exe ?? ecx는 어디서 나온거지 ??

짜증나는 Link Error가 계속적으로 나타나길래 무언가 보기위해서 과감히 F1 키를 눌렀습니다. 근데 dexplore.exe가 Exception을 남기면서 죽어 버리는 것이 아닌가 -_- 젠장 ..  다행이도 Windbg가 기본 Debugger로 설정되어 있는지라 훔 -_- 안전하게 덤프를 생성( .dump /f filename ) … 무언가하고 유심히 덤프를 열어보니..

FAULTING_IP:
jscript+368b
772a368b 83bb4002000000 cmp dword ptr [ebx+240h],0
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 772a368b (jscript+0x0000368b)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000240
Attempt to read from address 00000240
FAULTING_THREAD: 00000ffcDEFAULT_BUCKET_ID: APPLICATION_FAULTPROCESS_NAME: dexplore.exeERROR_CODE: (NTSTATUS) 0xc0000005 - "0x%08lx"READ_ADDRESS: 00000240

BUGCHECK_STR: ACCESS_VIOLATION

NTGLOBALFLAG: 0
… 중략

jscript+368b 이 Instruction이 문제를 발생시켰군 ebx가 NULL 인가 ?? 진정 ??

0:000> r ebx
ebx=00000000

진정 NULL 이었다. 그래서 다시 그 Instruction 부분의 Code를 리버싱.. 고고싱 운좋게 Prologue 부분을 찾는데 성공 ( 난 정말 운이 좋은거 같다 -_- )

0:000> uf 772a3670
jscript+0x3670:
772a3670 8bff mov edi,edi
772a3672 55 push ebp
772a3673 8bec mov ebp,esp
772a3675 83ec50 sub esp,50h

772a3678 a128213077 mov eax,dword ptr [jscript!DllRegisterServer+0x2f9d8 (77302128)]
772a367d 53 push ebx
772a367e 56 push esi
772a367f 57 push edi
772a3680 8d7508 lea esi,[ebp+8]
772a3683 8d7dc4 lea edi,[ebp-3Ch]
772a3686 a5 movs dword ptr es:[edi],dword ptr [esi]
772a3687 a5 movs dword ptr es:[edi],dword ptr [esi]
772a3688 a5 movs dword ptr es:[edi],dword ptr [esi]
772a3689 8bd9 mov ebx,ecx
772a368b 83bb4002000000 cmp dword ptr [ebx+240h],0
772a3692 8945fc mov dword ptr [ebp-4],eax
772a3695 a5 movs dword ptr es:[edi],dword ptr [esi]
772a3696 7414 je jscript+0×36ac (772a36ac)
jscript+0x3698:
772a3698 8b8340020000 mov eax,dword ptr [ebx+240h]
772a369e 8b08 mov ecx,dword ptr [eax]
772a36a0 50 push eax
772a36a1 ff5114 call dword ptr [ecx+14h]
772a36a4 85c0 test eax,eax
772a36a6 0f84b6070200 je jscript!DllCanUnloadNow+0xe4c2 (772c3e62)
jscript+0x36ac:
772a36ac 33c0 xor eax,eax
jscript+0x36ae:
772a36ae 8b4dfc mov ecx,dword ptr [ebp-4]
772a36b1 5f pop edi
772a36b2 5e pop esi
772a36b3 5b pop ebx
772a36b4 e8c7f4ffff call jscript+0×2b80 (772a2b80)
772a36b9 c9 leave
772a36ba c21400 ret 14h

일단 ret 14h 구문을 봐서는 Fastcall 아니면 stdcall 이다.  ebx의 값에 왜 NULL 이 들어가 있는것인가 ?? ecx의 값이 들어가 있기 때문인데 그럼 ecx의 값은 어디서 ??? Fastcall로 가정 했을때 아마도 첫번째 Parameter가 아닐까 추정이 된다. ( Fastcall읜 ecx와 edx를 이용해서 Parameter를 넘긴다 . ) 간단히 NULL Pointer 체크를 해주는 코드가 없어서 발생한 것으로 생각이 되는군.

진정 MS도 간혹은 실수를 하는구나 ㅜ.ㅜ … 회사에 쌓여 있는 덤프들을 봐야하는데 난 지금 머하는 짓인지 ㅜ.ㅜ

Bugcheck Code 보기

 BugCheck이  발생하면 당황하시는 개발자 분들이 생각보다 많습니다.  종종 공포의 Blue Screen이라고 부르기도 하지요. 물론 Application 개발을 주로 하시는 개발자 분들은 경험하기 힘든 부분이기도하고요. BugCheck이 발생하면 아래와 같이 5개의 Stop Code 들을 보실 수 있을 겁니다.

STOP: 0x00000079 (0x00000002, 0x00000001, 0x00000002, 0x00000000)

가장 먼저 확인해야하는 부분은 바로 STOP 바로 옆의 Code 입니다. 이 부분의 코드를 보통 BugCheck Code 라고 하고 Bugcheck이 발생한 원인을 알려줍니다. 그 다음에 오는 가로 안의 4개의 코드는 해당 Bugcheck Code의 parameter입니다. 각각의 Bugcheck Code는 그에 해당하는 parameter를 가지고 있고 이러한 부분은 msdn의 Bugcheck codes Page를 확인하시면 도움이됩니다. Bugcheck 발생했을 시에는 Dump를 통해서 정확한 원인을 분석해 보는 것이 좋습니다.

[windbg]Display Dispatch Routines

해당 드라이버에 Dispatch Routine를 보고 싶을 때는 !drvobj Extension을 사용하면 유용합니다.

lkd> !drvobj \filesystem\ntfs 7
Driver object (8a32fe20) is for:
\FileSystem\Ntfs
Driver Extension List: (id , addr)Device Object list:
89e35560 89fab770 8a31d020 8a29af18 DriverEntry: f7bd7184 Ntfs!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: f752a95e sptd
AddDevice: 00000000
Dispatch routines:
[00] IRP_MJ_CREATE 8a3cba40 +0×8a3cba40
[01] IRP_MJ_CREATE_NAMED_PIPE 804fb709 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE 8a3cba40 +0×8a3cba40
[03] IRP_MJ_READ 8a3cba40 +0×8a3cba40
[04] IRP_MJ_WRITE 8a3cba40 +0×8a3cba40
[05] IRP_MJ_QUERY_INFORMATION 8a3cba40 +0×8a3cba40
[06] IRP_MJ_SET_INFORMATION 8a3cba40 +0×8a3cba40
[07] IRP_MJ_QUERY_EA 8a3cba40 +0×8a3cba40
[08] IRP_MJ_SET_EA 8a3cba40 +0×8a3cba40
[09] IRP_MJ_FLUSH_BUFFERS 8a3cba40 +0×8a3cba40
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION 8a3cba40 +0×8a3cba40
[0b] IRP_MJ_SET_VOLUME_INFORMATION 8a3cba40 +0×8a3cba40
[0c] IRP_MJ_DIRECTORY_CONTROL 8a3cba40 +0×8a3cba40
[0d] IRP_MJ_FILE_SYSTEM_CONTROL 8a3cba40 +0×8a3cba40
[0e] IRP_MJ_DEVICE_CONTROL 8a3cba40 +0×8a3cba40
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 804fb709 nt!IopInvalidDeviceRequest
[10] IRP_MJ_SHUTDOWN 8a3cba40 +0×8a3cba40
[11] IRP_MJ_LOCK_CONTROL 8a3cba40 +0×8a3cba40
[12] IRP_MJ_CLEANUP 8a3cba40 +0×8a3cba40
[13] IRP_MJ_CREATE_MAILSLOT 804fb709 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY 8a3cba40 +0×8a3cba40
[15] IRP_MJ_SET_SECURITY 8a3cba40 +0×8a3cba40
[16] IRP_MJ_POWER 804fb709 nt!IopInvalidDeviceRequest
[17] IRP_MJ_SYSTEM_CONTROL 804fb709 nt!IopInvalidDeviceRequest
[18] IRP_MJ_DEVICE_CHANGE 804fb709 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA 8a3cba40 +0×8a3cba40
[1a] IRP_MJ_SET_QUOTA 8a3cba40 +0×8a3cba40
[1b] IRP_MJ_PNP 8a3cba40 +0×8a3cba40
Fast I/O routines:
FastIoCheckIfPossible f7b8beda Ntfs!NtfsFastIoCheckIfPossible
FastIoRead f7b72b57 Ntfs!NtfsCopyReadA
FastIoWrite f7b91448 Ntfs!NtfsCopyWriteA
FastIoQueryBasicInfo f7b7848e Ntfs!NtfsFastQueryBasicInfo
FastIoQueryStandardInfo f7b76f7e Ntfs!NtfsFastQueryStdInfo
FastIoLock f7b920f2 Ntfs!NtfsFastLock
FastIoUnlockSingle f7b921f8 Ntfs!NtfsFastUnlockSingle
FastIoUnlockAll f7bcb6ae Ntfs!NtfsFastUnlockAll
FastIoUnlockAllByKey f7bcb7f3 Ntfs!NtfsFastUnlockAllByKey
AcquireFileForNtCreateSection f7b7283a Ntfs!NtfsAcquireForCreateSection
ReleaseFileForNtCreateSection f7b72881 Ntfs!NtfsReleaseForCreateSection
FastIoQueryNetworkOpenInfo f7bb9e1d Ntfs!NtfsFastQueryNetworkOpenInfo
AcquireForModWrite f7b7ea10 Ntfs!NtfsAcquireFileForModWrite
MdlRead f7bb9f31 Ntfs!NtfsMdlReadA
MdlReadComplete 805322b8 nt!FsRtlMdlReadCompleteDev
PrepareMdlWrite f7bba2ab Ntfs!NtfsPrepareMdlWriteA
MdlWriteComplete 8061d1db nt!FsRtlMdlWriteCompleteDev
FastIoQueryOpen f7b76db8 Ntfs!NtfsNetworkOpenCreate
AcquireForCcFlush f7b726e2 Ntfs!NtfsAcquireFileForCcFlush
ReleaseForCcFlush f7b72708 Ntfs!NtfsReleaseFileForCcFlush

종종 System Driver의 Hooking 여부를 확인하거나 Hooking 모듈의 정상적인 Injection을 확인할 때 활용하시면 도움이 되실꺼라 생각이되내요.