Monthly Archive for 7월, 2009

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

USB Interrupt 전송 Drop에 의한 Stack Overflow

3: kd> !analyze -v
*****************************************
*                        Bugcheck Analysis       
*                                                           
*****************************************

UNEXPECTED_KERNEL_MODE_TRAP (7f)
This means a trap occurred in kernel mode, and it’s a trap of a kind
that the kernel isn’t allowed to have/catch (bound trap) or that
is always instant death (double fault).  The first number in the
bugcheck params is the number of the trap (8 = double fault, etc)
Consult an Intel x86 family manual to learn more about what these
traps are. Here is a *portion* of those codes:
If kv shows a taskGate
        use .tss on the part before the colon, then kv.
Else if kv shows a trapframe
        use .trap on that value
Else
        .trap on the appropriate frame will show where the trap was taken
        (on x86, this will be the ebp that goes with the procedure KiTrap)
Endif
kb will then show the corrected stack.
Arguments:
Arg1: 00000008, EXCEPTION_DOUBLE_FAULT
Arg2: f7727d70
Arg3: 00000000
Arg4: 00000000

Debugging Details:
——————

PEB is paged out (Peb.Ldr = 7ffdc00c).  Type “.hh dbgerr001″ for details
PEB is paged out (Peb.Ldr = 7ffdc00c).  Type “.hh dbgerr001″ for details

BUGCHECK_STR:  0×7f_8

TSS:  00000028 — (.tss 0×28)
eax=0000bb40 ebx=b3c91454 ecx=00000000 edx=00000001 esi=b3c91400 edi=0000002d
eip=804e90d6 esp=b3c90f98 ebp=b3c91024 iopl=0         nv up di ng nz ac po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010092
nt!KeContextFromKframes+0×10:
804e90d6 53              push    ebx
Resetting default scope

DEFAULT_BUCKET_ID:  CODE_CORRUPTION

PROCESS_NAME:  app.exe

EXCEPTION_RECORD:  b3c91400 — (.exr 0xffffffffb3c91400)
ExceptionAddress: 80503a3f (nt!DebugService+0×0000001b)
   ExceptionCode: 80000003 (Break instruction exception)
  ExceptionFlags: 00000000
NumberParameters: 3
   Parameter[0]: 00000001
   Parameter[1]: b3c91530
   Parameter[2]: 0000002d

TRAP_FRAME:  b3c91454 — (.trap 0xffffffffb3c91454)
ErrCode = 00000000
eax=00000001 ebx=ffffffff ecx=b3c91530 edx=0000002d esi=0000002d edi=00000000
eip=80503a40 esp=b3c914c8 ebp=b3c914dc iopl=0         nv up ei pl nz ac pe cy
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000217
nt!DebugService+0×1c:
80503a40 5b              pop     ebx
Resetting default scope

LAST_CONTROL_TRANSFER:  from 804fbcef to 804e90d6

STACK_TEXT: 
b3c91024 804fbcef b3c91454 00000000 b3c910fc nt!KeContextFromKframes+0×10
b3c913e4 804e0403 b3c91400 00000000 b3c91454 nt!KiDispatchException+0×82
b3c9144c 804e0b5c b3c914dc 80503a3f badb0d00 nt!CommonDispatchException+0×4d
b3c9144c 80503a40 b3c914dc 80503a3f badb0d00 nt!KiTrap03+0xae
b3c914dc 80503a6f 00000001 b3c91530 0000002d nt!DebugService+0×1c
b3c914f8 80503e97 b3c91518 ffffffff 00000000 nt!DebugPrint+0×1c
b3c9174c 80503ee3 80503ee6 ffffffff 00000000 nt!vDbgPrintExWithPrefix+0×101
b3c91768 b2e8aac0 b2e93360 b2e93320 8550a3d8 nt!DbgPrint+0×1a
WARNING: Stack unwind information not available. Following frames may be wrong.
b3c91788 b2e8a98e b2e93320 89e0cd00 8643bde0 Driver4+0×14ac0
b3c9179c b2e8b9d6 8643bde0 89e0cd00 87dca810 Driver4+0×1498e
b3c917c8 b2e8d382 89e0cd00 8643bde0 8643bebc Driver4+0×159d6
b3c91a08 b2e7baf8 89e0cd00 8643bde0 b3c91a30 Driver4+0×17382
b3c91a58 b4487c29 89e0cd00 8643bde0 87dbe000 Driver4+0×5af8
b3c91a84 b4486d87 89e0cd00 8643bde0 00000000 Driver3+0×7c29
b3c91aa4 b5765796 00000000 87dca810 8a6ccc50 Driver3+0×6d87
b3c91ac0 b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c91b08 b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c91b48 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28
b3c91b78 b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c91b8c bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c91ba0 bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
b3c91bc4 bac60d70 8643bde0 b3c91be4 00000001 Driver2+0×18ac
b3c91bdc 804e33d9 00000000 8643bde0 87dca810 Driver2+0×1d70
b3c91c00 b5765796 00000000 87dca810 8a6ccc50 nt!IopfCallDriver+0×31
b3c91c1c b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c91c64 b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c91ca4 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28
b3c91cd4 b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c91ce8 bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c91cfc bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
b3c91d20 bac60d70 8643bde0 b3c91d40 00000001 Driver2+0×18ac
b3c91d38 804e33d9 00000000 8643bde0 87dca810 Driver2+0×1d70
b3c91d5c b5765796 00000000 87dca810 8a6ccc50 nt!IopfCallDriver+0×31
b3c91d78 b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c91dc0 b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c91e00 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28

b3c91e30 b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c91e44 bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c91e58 bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
b3c91e7c bac60d70 8643bde0 b3c91e9c 00000001 Driver2+0×18ac
b3c91e94 804e33d9 00000000 8643bde0 87dca810 Driver2+0×1d70
b3c91eb8 b5765796 00000000 87dca810 8a6ccc50 nt!IopfCallDriver+0×31
b3c91ed4 b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c91f1c b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c91f5c 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28
b3c91f8c b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c91fa0 bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c91fb4 bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
b3c91fd8 bac60d70 8643bde0 b3c91ff8 00000001 Driver2+0×18ac
b3c91ff0 804e33d9 00000000 8643bde0 87dca810 Driver2+0×1d70
b3c92014 b5765796 00000000 87dca810 8a6ccc50 nt!IopfCallDriver+0×31
b3c92030 b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c92078 b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c920b8 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28
b3c920e8 b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c920fc bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c92110 bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
b3c92134 bac60d70 8643bde0 b3c92154 00000001 Driver2+0×18ac
b3c9214c 804e33d9 00000000 8643bde0 87dca810 Driver2+0×1d70
b3c92170 b5765796 00000000 87dca810 8a6ccc50 nt!IopfCallDriver+0×31
b3c9218c b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c921d4 b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c92214 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28
b3c92244 b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c92258 bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c9226c bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
b3c92290 bac60d70 8643bde0 b3c922b0 00000001 Driver2+0×18ac
b3c922a8 804e33d9 00000000 8643bde0 87dca810 Driver2+0×1d70
b3c922cc b5765796 00000000 87dca810 8a6ccc50 nt!IopfCallDriver+0×31
b3c922e8 b5766176 89df432c 00000000 8a6ccc50 Driver1+0×9796
b3c92330 b5765c28 89df432c 00000001 87dca801 Driver1+0xa176
b3c92370 804e38ff 00000000 8643bde0 00000000 Driver1+0×9c28
b3c923a0 b4486dd8 8643bde0 89e65958 c0000001 nt!IopfCompleteRequest+0xa2
b3c923b4 bac601fb 8643bde0 8a3d8cf4 c0000056 Driver3+0×6dd8
b3c923c8 bac608ac 8643bde0 89df432c 8a3d8cf4 Driver2+0×11fb
STACK_COMMAND:  .tss 0×28 ; kb

CHKIMG_EXTENSION: !chkimg -lo 50 -d !nt
    804e33b2-804e33b7  6 bytes - nt!IopfCallDriver
 [ fe 4a 23 8a 42 23:e9 39 39 fa 33 cc ]
    …생략
175 errors : !nt (804e33b2-80507724)

MODULE_NAME: Driver3

IMAGE_NAME:  Driver3.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  4a5c23df

FOLLOWUP_NAME:  MachineOwner

MEMORY_CORRUPTOR:  PATCH_Driver3

FAILURE_BUCKET_ID:  MEMORY_CORRUPTION_PATCH_Driver3

BUCKET_ID:  MEMORY_CORRUPTION_PATCH_Driver3

Followup: MachineOwner
———
보시는 그대로 Stack Overflow 입니다.( Stack Overflow 분석은 이곳을 참고하세요.) 특징적인 부분은 IofCallDriver를 후킹했다는 점입니다.
그럼 왜 Stack Overflow가 발생한것인가 한번 찾아보죠.

이러한 Stack의 경우 반복적인 부분을 유심히 관찰하는게 정말 중요합니다. Driver3+0×6dd8에서 호출한 IopfCompleteRequest 호출도중 Driver1+0×9c28가 호출됐다는 점에서 Completion Routine임을 추정할 수 있습니다. 문제는 이 Completion Routine은 다시 IopfCallDriver( Hooking Function이아닌 그냥 일반 호출 처럼 보이는 이유는 Bypass 되었다고 가정하시면 됩니다. Stack 구성에 대한 이야기는 다음에 또 얘기 하도록 하겠습니다.)를 호출한다는 점입니다. 그렇다면 왜 이렇게 구성이 되었을까?

IopfCallDriver가 후킹되었고 그 내부에서 IopfCompleteRequest를 호출했다는 점을 볼때 해당 I/O을 Drop 하는 목적임을 알 수 있습니다. 즉 다음 Stack으로 해당 I/O를 전달하지 않게 하기 위한 것이지요. 위의 Stack을 볼때 해당 I/O가 Drop 당했을 경우 또는 원하는 Data를 얻지 못할 경우를 대비해 Driver1 드라이버의 개발자는 다시 Irp를 전달하도록 드라이버를 구성하였음을 알 수 있습니다. 이러한 현상이 반복되면서 Stack Overflow는 발생하게 됩니다.

IopfCallDriver를 Hooking한 이유는 I/O Drop이 목적이라면 Driver1 드라이버는 왜 Completion Routine에서 Irp재 전송하는가 !!
그 해답은 Usb Data 전송 방식에 있습니다.

USB에는 크게 4가지 전송방식이 있습니다.

- Control
- Bulk
- Interrupt
- Isochronous

여기서 사용한 방식, 즉 Irp에 Urb를 담아 I/O를 전달하고 그 I/O가 비동기 적으로 Completion 될때 데이터를 받아오고 그리고 그 Data 값에 따라서 Completion Routine에서 재요청 또는 완료를 시도하는 방식, 바로 Bulk Or Interrupt 방식입니다.

아래 코드를 보면 Urb에 Header에 URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER( 0×00000009 ) 값을 넣어주는 코드를 확인 할 수 있습니다.
3: kd> ub Driver1+0×9796 L30
Driver1+0×96df:
b57656df b89a0000c0      mov     eax,0C000009Ah
b57656e4 e9c0000000      jmp     Driver1+0×97a9 (b57657a9)
b57656e9 8b45fc          mov     eax,dword ptr [ebp-4]
b57656ec 89868c000000    mov     dword ptr [esi+8Ch],eax
b57656f2 8b868c000000    mov     eax,dword ptr [esi+8Ch]
b57656f8 33d2            xor     edx,edx
b57656fa f775fc          div     eax,dword ptr [ebp-4]
b57656fd 0faf45fc        imul    eax,dword ptr [ebp-4]
b5765701 89868c000000    mov     dword ptr [esi+8Ch],eax
b5765707 8b462c          mov     eax,dword ptr [esi+2Ch]
b576570a 6a01            push    1
b576570c 66c740020900    mov     word ptr [eax+2],9
b5765712 8b462c          mov     eax,dword ptr [esi+2Ch]
b5765715 66c7004800      mov     word ptr [eax],48h
b576571a 8b87b8000000    mov     eax,dword ptr [edi+0B8h]
b5765720 8b4e2c          mov     ecx,dword ptr [esi+2Ch]
b5765723 8b440318        mov     eax,dword ptr [ebx+eax+18h]
b5765727 5b              pop     ebx
b5765728 894110          mov     dword ptr [ecx+10h],eax
b576572b 8b462c          mov     eax,dword ptr [esi+2Ch]
b576572e 8b8e8c000000    mov     ecx,dword ptr [esi+8Ch]
b5765734 68a05976b5      push    offset Driver1+0×99a0 (b57659a0)
b5765739 894818          mov     dword ptr [eax+18h],ecx
b576573c 8b462c          mov     eax,dword ptr [esi+2Ch]
b576573f 83602000        and     dword ptr [eax+20h],0
b5765743 8b462c          mov     eax,dword ptr [esi+2Ch]
b5765746 8b8e94000000    mov     ecx,dword ptr [esi+94h]
b576574c 89481c          mov     dword ptr [eax+1Ch],ecx
b576574f 8b462c          mov     eax,dword ptr [esi+2Ch]
b5765752 c7401402000000  mov     dword ptr [eax+14h],2
b5765759 8b462c          mov     eax,dword ptr [esi+2Ch]
b576575c 83602400        and     dword ptr [eax+24h],0
b5765760 ff762c          push    dword ptr [esi+2Ch]
b5765763 895e0c          mov     dword ptr [esi+0Ch],ebx
b5765766 ff7634          push    dword ptr [esi+34h]
b5765769 56              push    esi
b576576a e85b0e0000      call    Driver1+0xa5ca (b57665ca)
b576576f 8b8714010000    mov     eax,dword ptr [edi+114h]
b5765775 8b4d24          mov     ecx,dword ptr [ebp+24h]
b5765778 8d44080c        lea     eax,[eax+ecx+0Ch]
b576577c 50              push    eax
b576577d 56              push    esi
b576577e ff7518          push    dword ptr [ebp+18h]
b5765781 57              push    edi
b5765782 e829000000      call    Driver1+0×97b0 (b57657b0)
b5765787 8b5634          mov     edx,dword ptr [esi+34h]
b576578a 8b8fa4000000    mov     ecx,dword ptr [edi+0A4h]
b5765790 ff1558c375b5    call    dword ptr [Driver1+0x358 (b575c358)] // IopfCallDriver

Enjoy Debugging

DDoS 대란 - 하드가 벽돌되면 한번 해보세요.

※ 이 글의 아래 부분에서 설명하는 복구 방식은 기본적인 파티션만을 복구하여 데이터만을 백업하도록 하는 방식이며 시스템에 따라 안정적이지 않을 수 도 있으므로 최후의 방법으로만 사용하시기 바랍니다. 또한 복구에 관한 부분은 전문적인 복구 업체를 통해서 하시는 것이 바람직합니다.

최근 DDoS 대란에 의해서 피해가 속출하고 있는 기사가 굉장히 많이 올라고 있습니다. ( 저희 회사 긴급대응팀도 그일로 밤을 지세고 있습니다. )  이미 관련 전용 백신들이 많이 나와서 어느 정도 수습되어가는 분위기로 보입니다.

치료 전용 백신 :
- 잉카인터넷 전용 백신
- 안철수연구소 전용 백신
- 안철수연구소 DDoS 실시간 차단 프로그램
- 하우리 전용 백신
- 바이러스체이서

이번 DDoS 샘플을 보면 굉장히 재미 있는 코드가 들어 있더군요. 그래서 몇가지 테스트를 해보았습니다. 실제 파일 파괴를 어떻게 시키는지 말이죠 .

일단 가상 PC에 감염을 시켜봤습니다. 그리고 MBR을 확인해 보면 아래와 같이 특정 문자열이 박히는걸 확인할 수 있습니다.

Boot Code :
4d 65 6d 6f 72 79 20 6f - 66 20 74 68 65 20 49 6e
64 65 70 65 6e 64 65 6e - 63 65 20 44 61 79 00 00 <== Memory of the Independence Day
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 55 55 55 55 - 55 55 55 55 55 55 55 55
55 55 55 55 55 55 55 55 - 55 55 55 55 55 55 55 55
…( 생략 )

Memory Of the Independance Day라는 문자열이 가장 상위에 박히게 됩니다. 그 아래로 1MB 사이즈 만큼의 Block이 0×55로 채워지게 되지요. ( 정확히 0×800개의 섹터가 깨지게 됩니다. )

또 특이한점은 아래와 같이 특정확장자를 가지는 파일을 .gz로 변경하고 깨트려버리게 되죠 .

만약 이러한 상태에서 Reboot을 하면 어찌 될까 ??

리부팅을 하면 아래와 같이 멋지게 부팅이 안되는것을 확인할 수 있습니다 .

 

벽돌로 돌변한 Hard를 다른 PC에 달아 확인해보자 !! .

다른 PC어 넣다는다는 가정하에 다른 가상머신으로 해당 Hard Image를 달아서 구현을 해볼 수있다.

가상머신에 벽돌이된 Hard Image를 장착하고 부팅을 해보면 정상적으로 2개의 하드가 인식되지 않는다는것을 알 수 있다 .

 

최소한의 데이터 복구를 위해서 우리가 손쉽게 접할수 있는 Open Source 복구 툴인 TestDisk를 사용해서 복구를 진행해보도록 하자 .
TestDisk Download : http://www.cgsecurity.org/wiki/TestDisk_Download

 

1. TestDisk 를 실행하면 위와 같은 모습의 콘솔 Application이 실행 됩니다. 그냥 바로 Create 선택하고 Enter를 누릅니다.

2. 내가 복구 하고자 하는 2번째 벽돌 Hard Disk와 Proceed를 선택 후 Enter를 누릅니다.

3. 자신의 PC가 어떤건지를 선택해 줍니다. 대부분의 Windows 사용 PC는 Inter 계열이므로 그냥 인텔을 선택해 눌러줍니다.

4. 이제 Disk 분석을 위해서 Analyse를 선택하고 Enter를 누릅니다.

5. Quick Search라는 좌측 하단에 문자열이 보입니다. Quick Search의 경우 큰 의미가 없기 때문에( Quick Search에 의해서 잡히지 않음 ) 그냥 Enter를 누릅니다.

6. Vista 하위 버젼 윈도우 인지를 선택하고 Enter를 누릅니다.

7. Quick Search가 실행되면 그냥 Enter를 눌러 중지합니다.

8. 역시 그냥 Enter를 눌러 넘어 갑니다.

9. 이제 부터 정말 중요한 Deeper Search를 시작합니다. Deeper Search가 기본적으로 선택 되어 있기 때문에 그냥 Enter만 누르면 됩니다.

10 . Deeper Search가 시작되고 그후에 위 와 같이 파티션이 보여질때까지 기다립니다. Deep Search 후 파티션을 정상적으로 발견하면 위와 같은 화면이 나타납니다. 이 상태에서도 아무 조작 없이 그냥 Enter를 눌러주면 됩니다.

11. 파티션 테이블을 복구하는 부분입니다. 바로 악성코드가 지워버리는 부분이지요. Write를 선택하고 Y/N 문구가 나오면 Y를 눌러 파티션 테이블을 복구 합니다.

12. 파티션 복구가 완료되면 Boot Sector 부분을 복구 해야합니다. 위에서 보여지는 봐와 같이 대부분의 경우 Backup Boot Sector는 살아있습니다. Backup BS 선택후 Enter를 누르면 Backup Boot Sector를 이용해서 복구 여부를 묻습니다. 물로 Y를 누르고 다음 상태로 진행합니다.

13. 이제 복구가 완료 되었습니다. 컴퓨터를 리부팅하면 읽어지지 않던 벽돌 하드가 정상적으로 읽어지는 것을 확인 할 수 있을 겁니다.

 

위와 같이 파티션이 복구되어 보이게 됩니다. 이제 중요 데이터를 USB로 Backup하는 일만하면 최소한의 복구를 완료 할 수 있습니다.

이렇게 하면 최소한의 복구는 할 수 있지만. 

복구는 복구 전문업체에게 악성코드 치료는 전문 보안업체에게

맞기는것이 가장 바람직하다.
혹시나 모험을 원하신다면 도전해 보시길......!!! 

EXCEPTION_GP_FAULT & CR4

주민등록증 분실신고를 위해 ( 어제 지갑을 잃어버려서… 아 놔 ) 관련 사이트에 접속하였습니다. 이때 아름답게 발생하는 Blue Screen 먼지 궁금해서 열어보니 조금 재미있는 BugCheck 이더군요.

1: kd> !analyze -v
*******************************
* *
* Bugcheck Analysis *
* *
*******************************

UNEXPECTED_KERNEL_MODE_TRAP (7f)
This means a trap occurred in kernel mode, and it’s a trap of a kind
that the kernel isn’t allowed to have/catch (bound trap) or that
is always instant death (double fault). The first number in the
bugcheck params is the number of the trap (8 = double fault, etc)
Consult an Intel x86 family manual to learn more about what these
traps are. Here is a *portion* of those codes:
If kv shows a taskGate
use .tss on the part before the colon, then kv.
Else if kv shows a trapframe
use .trap on that value
Else
.trap on the appropriate frame will show where the trap was taken
(on x86, this will be the ebp that goes with the procedure KiTrap)
Endif
kb will then show the corrected stack.
Arguments:
Arg1: 0000000d, EXCEPTION_GP_FAULT
Arg2: 00000000
Arg3: 00000000
Arg4: 00000000

Debugging Details:
------------------

PEB is paged out (Peb.Ldr = 7ffda00c). Type ".hh dbgerr001" for details
PEB is paged out (Peb.Ldr = 7ffda00c). Type ".hh dbgerr001" for details

BUGCHECK_STR: 0x7f_d

DEFAULT_BUCKET_ID: DRIVER_FAULT

PROCESS_NAME: BBBBBBBB.exe

LAST_CONTROL_TRANSFER: from f77ef498 to 804e322c

STACK_TEXT:
b4052b60 f77ef498 badb0d00 f771b560 80703d43 nt!KiSystemFatalException+0xf
WARNING: Stack unwind information not available. Following frames may be wrong.
b4052bf8 f77efb15 00000001 00000004 00000000 AAAAAA+0×498
b4052c14 f77f56e1 83668758 87d4cdc8 87e4f3c8 AAAAAA+0xb15
b4052c2c 804e33d9 fdede8f8 83668700 80703410 AAAAAA+0×66e1
b4052c50 8057287c 836687c8 87d4cdc8 83668758 nt!IopfCallDriver+0×31
b4052c64 805823d0 fdede8f8 83668758 87d4cdc8 nt!IopSynchronousServiceTail+0×70
b4052d00 8058a9d2 000000dc 00000000 00000000 nt!IopXxxControlFile+0×5ef
b4052d34 804df99f 000000dc 00000000 00000000 nt!NtDeviceIoControlFile+0×2a
b4052d34 7c93e4f4 000000dc 00000000 00000000 nt!KiFastCallEntry+0xfc
0012f75c 00000000 00000000 00000000 00000000 0×7c93e4f4
STACK_COMMAND: kb

FOLLOWUP_IP:
AAAAAA+498
f77ef498 0f22e0 mov cr4,eax

SYMBOL_STACK_INDEX: 1

SYMBOL_NAME: AAAAAA+498

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: AAAAAA

IMAGE_NAME: AAAAAA.SYS

DEBUG_FLR_IMAGE_TIMESTAMP: 49c71317

FAILURE_BUCKET_ID: 0×7f_d_AAAAAA+498

BUCKET_ID: 0×7f_d_AAAAAA+498

Followup: MachineOwner
———

 

EXCEPTION_GP_FAULT 가 발생했는데도 이상하게 .trap, .tss 을 보여주지 않습니다. EXCEPTION_GP_FAULT가 발생했을 때는 먼저 Trap Frame을 확보하는것이 가장 중요 합니다.  왜냐면 EXCEPTION_GP_FAULT이 발생하는 경우는 System Register와 관계 있는 경우 또는 Memory 처리 등과 관계되는 경우가 많기 때문에 Trap 이 발생한 정확한 위치 및 Register 값이 필요하기 때문입니다.  일단 Trap Frame을 확보하기 위해서 수동 Stack Trace를 해보겠습니다.

1: kd> !thread
THREAD 85466020 Cid 1348.0220 Teb: 7ffdf000 Win32Thread: ed351008 RUNNING on processor 1
IRP List:
83668758: (0006,0094) Flags: 00000070 Mdl: 00000000
Not impersonating
DeviceMap e1b187f0
Owning Process fd6cd5b8 Image: CKSetup32.exe
Attached Process N/A Image: N/A
Wait Start TickCount 1384396 Ticks: 0
Context Switch Count 427 LargeStack
UserTime 00:00:00.031
KernelTime 00:00:00.046
Win32 Start Address 0x0040990a
Start Address 0x7c8106f5
Stack Init b4053000 Current b4052bcc Base b4053000 Limit b404f000 Call 0
Priority 9 BasePriority 8 PriorityDecrement 0 DecrementCount 16
ChildEBP RetAddr Args to Child
b4052b60 f77ef498 badb0d00 f771b560 80703d43 nt!KiSystemFatalException+0xf (FPO: [0,0] TrapFrame @ b4052b60)
WARNING: Stack unwind information not available. Following frames may be wrong.
b4052bf8 f77efb15 00000001 00000004 00000000 AAAAAA+0×498
b4052c14 f77f56e1 83668758 87d4cdc8 87e4f3c8 AAAAAA+0xb15
b4052c2c 804e33d9 fdede8f8 83668700 80703410 AAAAAA+0×66e1
b4052c50 8057287c 836687c8 87d4cdc8 83668758 nt!IopfCallDriver+0×31 (FPO: [0,0,0])
b4052c64 805823d0 fdede8f8 83668758 87d4cdc8 nt!IopSynchronousServiceTail+0×70 (FPO: [7,0,4])
b4052d00 8058a9d2 000000dc 00000000 00000000 nt!IopXxxControlFile+0×5ef (FPO: [Non-Fpo])
b4052d34 804df99f 000000dc 00000000 00000000 nt!NtDeviceIoControlFile+0×2a (FPO: [10,0,0])
b4052d34 7c93e4f4 000000dc 00000000 00000000 nt!KiFastCallEntry+0xfc (FPO: [0,0] TrapFrame @ b4052d64)
0012f75c 00000000 00000000 00000000 00000000 0×7c93e4f4
dps b404f000 b4053000
... 생략

b4052b40 b4052b60
b4052b44 804e322c nt!KiSystemFatalException+0xf
b4052b48 0000007f
b4052b4c 0000000d
b4052b50 00000000
b4052b54 00000000
b4052b58 00000000
b4052b5c b4052b60
b4052b60 b4052bf8
b4052b64 f77ef498 AAAAAA+0×498
b4052b68 badb0d00
b4052b6c f771b560
b4052b70 80703d43 hal!HalpDispatchInterrupt+0xbb
b4052b74 b4052bf4
b4052b78 0000002c
b4052b7c 4e2fb5e7
b4052b80 5a6d154c
b4052b84 538c8da2
b4052b88 00000064
b4052b8c 804e3442 nt!KiInsertTimerTable+0×1b
b4052b90 fffe0000
b4052b94 ffff0023
b4052b98 5d330023

b4052b9c f771b560
b4052ba0 00000000
b4052ba4 000006d1
b4052ba8 804dbe25 nt!KiIpiSend+0×27
b4052bac b4052cf0
b4052bb0 00000030
b4052bb4 8a1bca8c
b4052bb8 00000093
b4052bbc f77f2800 AAAAAA+0×3800
b4052bc0 b4052bf8
… 생략

 

0023을 이용해서 Trap Frame을 발견했습니다.  Trap Frame Search 를 위해서 0023을 사용하는 이유는 http://www.insidewindows.kr/?p=69 게시물을 참조하시면 됩니다.

1: kd> .trap b4052b98-0x38
ErrCode = 00000000
eax=000006d1 ebx=f77f2800 ecx=00000000 edx=f771b560 esi=00000093 edi=8a1bca8c
eip=f77ef498 esp=b4052bd4 ebp=b4052bf8 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010206
AAAAAA+0x498:
f77ef498 0f22e0 mov cr4,eax
1: kd> uf f77ef490
AAAAAA+0x490:
f77ef490 0f20e0 mov eax,cr4
f77ef493 25f7070000 and eax,7F7h

f77ef498 0f22e0 mov cr4,eax
f77ef49b b800040000 mov eax,400h
f77ef4a0 0f23f8 mov dr7,eax
f77ef4a3 b800000000 mov eax,0
f77ef4a8 0f23d8 mov dr3,eax
f77ef4ab 0f23d0 mov dr2,eax
f77ef4ae c3 ret

 

대략 CR4 Register의 Debugging Extensions를 Disable 해주는 보안 코드인것으로 생각 됩니다.

1: kd> .formats cr4
Hex: 000026d9
Decimal: 9945
Octal: 00000023331
Binary: 00000000 00000000 00100110 11011001
Chars: ..&.
Time: Thu Jan 01 11:45:45 1970
Float: low 1.39359e-041 high 0
Double: 4.91348e-320
1: kd> .formats eax
Evaluate expression:
Hex: 000006d1
Decimal: 1745
Octal: 00000003321
Binary: 00000000 00000000 00000110 11010001
Chars: ….
Time: Thu Jan 01 09:29:05 1970
Float: low 2.44527e-042 high 0
Double: 8.62145e-321

 

현재 CR4 Register의 값을 EAX의 값으로 변경하고자 하내요 그렇다면 변경되는 Flag를 체크해봐야 합니다.

00000000 00000000 00100110 11011001
00000000 00000000 00000110 11010001

두개의 Flag를 비교하면 2개의 Bit가 변경되는 것을 알수 있습니다. 3번 Bit는 변경을 원하는 DE( Debugging Extensions ) bit 이고 13번 Bit인 VME( Virtual-8086 Mode Extensions ) bit 입니다. 3번 Bit 이외에 변경 되는 13번 Bit를 유심히보면 이 Bugcheck의 해답이 보입니다.

 

 

 VME bit에 관한 자료를 찾아 보면 VME Bit에 대해 자세히 알 수 있습니다.  간략히 설명하면 VME를 사용하고 있는 도중에 관련 Bit를 변경하게 되면 General Protection Fault가 발생하게 되는 거죠.

Enjoy Debugging

User Mode에서 Hidden Process 탐지

User Mode에서 Hidden Process를 탐지하는 방법을 한가지 소개하도록 하겠습니다. 굉장히 간단하고 나름대로 막강합니다.

원리는 간단합니다. Process를 Hidden 한다고해서 Process Open이 되지 않는것은 아니기 때문에 이러한 원리를 이용해 OpenProcess와 EnumProcess를 이용하면 쉽게 Hidden Process를 탐지 할 수 있습니다.

void FindProcess( DWORD dwType )
{
DWORD dwProcesses[1024], dwNeeded;
DWORD dwProcCount;
DWORD dwPID;
DWORD itCount;

HMODULE hModule;
HANDLE hProcess;
BOOL bHide;

if ( !EnumProcesses( dwProcesses, sizeof(dwProcesses), &dwNeeded ) )
return;

dwProcCount = dwNeeded / sizeof(DWORD);

for ( dwPID = 0x0c ; dwPID < 0xFFFF ; dwPID += 4 )
{
char strName[MAX_PATH] = “”;

bHide = TRUE;
hModule = NULL;
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, dwPID );

if ( hProcess == NULL )
continue;

// dwProcesses 안에 존재하지 않으면 Hidden 이 아니고 존재하지 않으며 Hidden Process

for (itCount = 0 ; itCount < dwProcCount ; itCount++ )
{
if ( dwPID == dwProcesses[ itCount ] )
{
bHide = FALSE;
break;
}
}

if( EnumProcessModules( hProcess, &hModule, sizeof(hModule), &dwNeeded))
{
GetModuleFileNameEx( hProcess, hModule, strName, sizeof( strName ) );

if ( ( bHide ) && ( dwType & FIND_HIDDEN ) )
{
printf(”PID : %-5d , Name : %16s %s\n”, dwPID, strName, “–[Hidden]–” );
}
else if ( dwType & FIND_NORMAL )
{
printf(”PID : %-5d , Name : %16s %s\n”, dwPID, strName, “” );
}
}
else if ( dwType & FIND_ZOMBIE )
{
GetProcessImageFileName( hProcess, strName, sizeof( strName ) );
printf(”PID : %-5d , Name : %16s %s\n”, dwPID, strName, “–[Zombie]–”);
}
CloseHandle( hProcess );

}
}

간단하죠 !! ^^

Source code : findhidden