BLOG main image
분류 전체보기 (17)
Life (2)
Dump Analysis (9)
Reversing (1)
Windows (1)
Book (2)
Reference (2)
Visitors up to today!
Today hit, Yesterday hit
daisy rss
tistory 티스토리 가입하기!
'2018/08/13'에 해당되는 글 1건
2018. 8. 13. 23:49

이번 덤프는 지속적으로 CPU 사용율이 100%가 유지되는 행(Hang) 증상으로 수집된 덤프다.

이처럼 행과 같은 특수한 상황을 분석하기 위해서는 행 증상이 발생한 상태에서 강제로 BSOD를 발생시켜 덤프를 수집해야 한다.

대체로 행이나 메모리 누수 같은 문제는 당시 시스템 상황을 잘 분석해야 하므로 분석이 까다롭다.

참고로 이번에는 64비트 덤프로 준비해봤다. 혹시 64비트 덤프를 처음 보더라도 분석 방법은 동일하니 긴장하지 말고 시작해보자.

 

강제로 발생시킨 덤프이기 때문에 !analyze 명령이나 현재 스레드의 콜 스택 정보는 큰 의미가 없다.

행 덤프를 생성할 당시의 시스템 상황을 분석해내는 것이 핵심이다.

우선적으로 !running -it 명령으로 덤프 발생 당시 실행 중이던 스레드와 콜 스택 정보를 확인해보자.

kd> !running -it

 

System Processors:  (000000000000000f)

  Idle Processors:  (0000000000000000) (0000000000000000) (0000000000000000) (0000000000000000)

 

       Prcbs             Current         (pri) Next            (pri) Idle

  0    fffff80002bf8e80  fffffa802b447b60 ( 8)                       fffff80002c06cc0  ................

 

 # Child-SP          RetAddr           Call Site

00 fffff880`02d2fcf8 fffff880`02e4d435 nt!KeBugCheckEx

01 fffff880`02d2fd00 fffff800`02d23cce MyDrv+0x1435

02 fffff880`02d2fd40 fffff800`02a77fe6 nt!PspSystemThreadStartup+0x5a

03 fffff880`02d2fd80 00000000`00000000 nt!KiStartSystemThread+0x16

 

  1    fffff880009e8180  fffffa802b3ae060 ( 9)                       fffff880009f2fc0  ................

 

 # Child-SP          RetAddr           Call Site

00 fffff880`041a5660 fffff800`02d44578 nt!ExfAcquirePushLockShared+0x21

01 fffff880`041a56e0 fffff800`02d8fef6 nt!ExpGetProcessInformation+0xaf2

02 fffff880`041a5830 fffff800`02d90949 nt!ExpQuerySystemInformation+0xfb4

03 fffff880`041a5be0 fffff800`02a858d3 nt!NtQuerySystemInformation+0x4d

04 fffff880`041a5c20 00000000`7733167a nt!KiSystemServiceCopyEnd+0x13

05 00000000`0504fd58 00000000`00000000 0x7733167a

 

  2    fffff88003763180  fffffa802b3f8b60 ( 9)                       fffff8800376dfc0  ................

 

 # Child-SP          RetAddr           Call Site

00 fffff880`057031a8 fffff800`02c7f3e0 kdcom+0x4c5f

01 fffff880`057031b0 00000000`00000002 nt!KdLogBuffer

02 fffff880`057031b8 00000000`00026200 0x2

03 fffff880`057031c0 00000000`00000001 0x26200

04 fffff880`057031c8 fffff800`00b9ca13 0x1

05 fffff880`057031d0 fffff800`00b9be4b kdcom+0x4a13

06 fffff880`05703210 fffff800`00b99699 kdcom+0x3e4b

07 fffff880`05703250 fffff800`00b9cf1b kdcom+0x1699

08 fffff880`057033a0 fffff800`02a9013d kdcom+0x4f1b

09 fffff880`057034d0 fffff800`02a92a3f nt!KdPollBreakIn+0xec

0a fffff880`05703520 fffff800`02a92741 nt!KeUpdateRunTime+0x13f

0b fffff880`05703550 fffff800`02d43f00 nt!KiSecondaryClockInterrupt+0x131

0c fffff880`057036e0 fffff800`02d8fef6 nt!ExpGetProcessInformation+0x472

0d fffff880`05703830 fffff800`02d90949 nt!ExpQuerySystemInformation+0xfb4

0e fffff880`05703be0 fffff800`02a858d3 nt!NtQuerySystemInformation+0x4d

0f fffff880`05703c20 00000000`7733167a nt!KiSystemServiceCopyEnd+0x13

10 00000000`0e30fd58 00000000`00000000 0x7733167a

 

  3    fffff880037d3180  fffffa802b407060 ( 9)                       fffff880037ddfc0  ................

 

 # Child-SP          RetAddr           Call Site

00 fffff880`057d46b0 fffff800`02d43f24 nt!ObReferenceObjectSafe+0xf

01 fffff880`057d46e0 fffff800`02d8fef6 nt!ExpGetProcessInformation+0x496

02 fffff880`057d4830 fffff800`02d90949 nt!ExpQuerySystemInformation+0xfb4

03 fffff880`057d4be0 fffff800`02a858d3 nt!NtQuerySystemInformation+0x4d

04 fffff880`057d4c20 00000000`7733167a nt!KiSystemServiceCopyEnd+0x13

05 00000000`0fdffd58 00000000`00000000 0x7733167a


1~3번 프로세서에서 동작 중인 스레드는 NtQuerySystemInformation 함수를 수행 중이다. 0번 프로세서의 스레드는 강제 덤프 수집을 위해 인위적으로 BSOD를 발생시킨 스레드라 분석에서 제외한다.

NtQuerySystemInformation 함수의 원형은 다음과 같다.

NTSTATUS WINAPI NtQuerySystemInformation(

_In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,

_Inout_   PVOID                    SystemInformation,

_In_      ULONG                    SystemInformationLength,

_Out_opt_ PULONG                   ReturnLength

);

유저 프로세스에서 NtQuerySystemInformation 함수를 사용해서 시스템 정보를 쿼리할 경우 요청한 SystemInformationClass에 맞는 커널 내부 함수가 호출된다.

내부에서 ExpGetProcessInformation 함수가 호출된 것으로 보아 프로세스 정보와 관련된 요청이었을 것이다.

1~3번 프로세서에서 모두 NtQuerySystemInformation 함수가 동작 중이었으므로 이 NtQuerySystemInformation 함수에서 지속적으로 CPU를 사용해서 행을 유발했을 가능성이 매우 높다.

우선 동작 중인 1번 프로세서의 스레드인 fffffa802b3ae060로 컨텍스트를 맞춰 SystemInformationClass가 어떤 값이었는지 확인해보자

kd> .thread fffffa802b3ae060

Implicit thread is now fffffa80`2b3ae060

 

kd> kv

  *** Stack trace for last set context - .thread/.cxr resets it

 # Child-SP          RetAddr           : Args to Child                                                           : Call Site

 00 fffff880`041a5660 fffff800`02d44578 : fffff880`00000000 00000000`00000000 00000000`00002500 00000000`00000005 : nt!ExfAcquirePushLockShared+0x21

01 fffff880`041a56e0 fffff800`02d8fef6 : 00000000`02170000 00000000`00010000 fffff880`041a5870 00000000`00000000 : nt!ExpGetProcessInformation+0xaf2  // 2) 프로세스 정보 획득 함수 호출

02 fffff880`041a5830 fffff800`02d90949 : 00000000`02170000 00000000`00000000 00000000`0504fed0 00000000`00000000 : nt!ExpQuerySystemInformation+0xfb4 // 1) SystemInformationClass 따라 분기

03 fffff880`041a5be0 fffff800`02a858d3 : 00000000`00000001 00000000`0504ecb8 00000000`00000001 000007ff`fffdc000 : nt!NtQuerySystemInformation+0x4d

04 fffff880`041a5c20 00000000`7733167a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`041a5c20)

05 00000000`0504fd58 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x7733167a


64비트 콜 스택이므로 첫 번째 파라미터를 찾기가 쉽지 않다. 64비트에서는 스택 포인터인 rsp 기준으로 동작하기 때문인데 이로 인해 k 명령으로 보이는 파라미터 값들은 실제 값과는 다르게 표시될 수 있다(5.3.2 숨겨진 콜 스택 케이스 참고).

그래도 최소한 1)번 쯤에서 SystemInformationClass를 비교하는 부분이 있을 것 같다. ExpQuerySystemInformation+0xfb4 부분을 디스어셈블링해서 살펴보자.

kd> u ExpQuerySystemInformation+0xfb4-3a L10

nt!ExpQuerySystemInformation+0xf78:

fffff800`02d8feba 0000            add     byte ptr [rax],al

fffff800`02d8febc 89473c          mov     dword ptr [rdi+3Ch],eax

fffff800`02d8febf eb05            jmp     nt!ExpQuerySystemInformation+0xf84 (fffff800`02d8fec6)

fffff800`02d8fec1 e9c5f2ffff      jmp     nt!ExpQuerySystemInformation+0x249 (fffff800`02d8f18b)

fffff800`02d8fec6 894c2440        mov     dword ptr [rsp+40h],ecx

fffff800`02d8feca 8b742444        mov     esi,dword ptr [rsp+44h]

fffff800`02d8fece e9b1f2ffff      jmp     nt!ExpQuerySystemInformation+0x242 (fffff800`02d8f184)

fffff800`02d8fed3 4183fa05        cmp     r10d,5 // 1) SystemInformationClass 5인지 비교

fffff800`02d8fed7 0f85b93e0500    jne     nt! ?? ::NNGAKEGL::`string'+0x58c58 (fffff800`02de3d96) // 2) 5 아니면 다음 위치로 이동

fffff800`02d8fedd 32c0            xor     al,al

fffff800`02d8fedf 88442420        mov     byte ptr [rsp+20h],al

fffff800`02d8fee3 4533c9          xor     r9d,r9d

fffff800`02d8fee6 4c8d442440      lea     r8,[rsp+40h]

fffff800`02d8feeb 418bd5          mov     edx,r13d

fffff800`02d8feee 488bcf          mov     rcx,rdi

fffff800`02d8fef1 e89a3bfbff      call    nt!ExpGetProcessInformation (fffff800`02d43a90)       // 3) 5 경우 ExpGetProcessInformation 함수 호출



SystemInformationClass가 5번일 경우 ExpGetProcessInformation 함수가 호출된다.

한 번에 찾아서 다행이다. 콜 스택을 보면 ExpGetProcessInformation 함수가 호출되었으므로 첫 번째 파라미터인 SystemInformationClass 5.

그렇다면 이제 SystemInformationClass 5번이 무엇을 의미하는지 알아야 한다.

typedef enum SYSTEM_INFORMATION_CLASS {

    SystemBasicInformation,              // 0

    SystemProcessorInformation,          // 1

    SystemPerformanceInformation,        // 2

    SystemTimeOfDayInformation,          // 3

    SystemPathInformation,               // 4

    SystemProcessesAndThreadsInformation,      // 5

    ... ...


SYSTEM_INFORMATION_CLASS enum 값을 확인해보면 5 SystemProcessesAndThreadsInformation을 의미한다.

뭔가 프로세스나 스레드 관련된 정보를 얻을 때 사용하는 값이라고 추측해 볼 수 있다.

앞서 3개의 스레드 모두 ExpGetProcessInformation 함수 안에서 동작이 완료되지 않고 있었다. ExpGetProcessInformation  함수가 문제 발생 위치일 가능성이 높은 상황이다.

uf /c 명령으로 ExpGetProcessInformation 내부에서 호출되는 함수를 살펴보자.

kd> uf /c ExpGetProcessInformation

nt!ExpGetProcessInformation (fffff800`02d43a90)

  nt!ExpGetProcessInformation+0x7a (fffff800`02d43b0a):

    call to nt!KeFlushProcessWriteBuffers (fffff800`02a5bf7c)

  nt!ExpGetProcessInformation+0xc9 (fffff800`02d43b58):

    call to nt!PsGetProcessSessionId (fffff800`02a4bf94)

  nt!ExpGetProcessInformation+0x146 (fffff800`02d43bd5):

    call to nt!ExpCopyProcessInfo (fffff800`02d44c04)

  nt!ExpGetProcessInformation+0x1d6 (fffff800`02d43c65):

    call to nt!PsGetNextProcessThread (fffff800`02d278a8)

  nt!ExpGetProcessInformation+0x24a (fffff800`02d43cd8):

    call to nt!KeQueryValuesThread (fffff800`02a5bb50)

  nt!ExpGetProcessInformation+0x491 (fffff800`02d43f1f):

    call to nt!ObReferenceObjectSafe (fffff800`02aa2c00)

  nt!ExpGetProcessInformation+0x4ea (fffff800`02d43f78):

    call to nt!ObfDereferenceObject (fffff800`02a90440)

  nt!ExpGetProcessInformation+0x541 (fffff800`02d43fcf):

    call to nt!SeLocateProcessImageName (fffff800`02d279bc)

  nt!ExpGetProcessInformation+0x631 (fffff800`02d440be):

    call to nt!memmove (fffff800`02a7cff0)

  nt!ExpGetProcessInformation+0x6bb (fffff800`02d44148):

    call to nt!ExFreePoolWithTag (fffff800`02bb1d90)

  nt!ExpGetProcessInformation+0x76f (fffff800`02d441fc):

    call to nt!PsGetNextProcess (fffff800`02d44928)   // 지연 의심 함수

  nt!ExpGetProcessInformation+0x820 (fffff800`02d442ad):

    call to nt!ExFreePoolWithTag (fffff800`02bb1d90)

  nt!ExpGetProcessInformation+0xaaa (fffff800`02d44530):

    call to nt!KeSynchronizeWithThreadInitialization (fffff800`02b25310)

  nt!ExpGetProcessInformation+0xaed (fffff800`02d44573):

    call to nt!ExfAcquirePushLockShared (fffff800`02ab6f20)

    ... ... 



PsGetNextProcessThread 함수를 지연 의심 함수로 언급했는데 그 이유는 조금 뒤에 설명하겠다. 우선 이 함수의 원형은 다음과 같다.

PETHREAD

PsGetNextProcessThread (

    IN PEPROCESS Process,

    IN PETHREAD Thread

    );


첫 번째 파라미터가 프로세스 오브젝트인 것을 기억하자. 이제 PsGetNextProcessThread  함수를 디스어셈블링해서 살펴보자.

kd> u PsGetNextProcessThread L20

nt!PsGetNextProcessThread:

fffff800`02d278a8 48895c2408      mov     qword ptr [rsp+8],rbx

fffff800`02d278ad 48896c2410      mov     qword ptr [rsp+10h],rbp

fffff800`02d278b2 4889742418      mov     qword ptr [rsp+18h],rsi

fffff800`02d278b7 57              push    rdi

fffff800`02d278b8 4154            push    r12

fffff800`02d278ba 4155            push    r13

fffff800`02d278bc 4156            push    r14

fffff800`02d278be 4157            push    r15

fffff800`02d278c0 4883ec20        sub     rsp,20h

fffff800`02d278c4 65488b3c2588010000 mov   rdi,qword ptr gs:[188h]

fffff800`02d278cd 4533ff          xor     r15d,r15d

fffff800`02d278d0 488bf2          mov     rsi,rdx

fffff800`02d278d3 66ff8fc4010000  dec     word ptr [rdi+1C4h]

fffff800`02d278da 4d8bf7          mov     r14,r15

fffff800`02d278dd 4c8da108030000  lea     r12,[rcx+308h]     // 1) 프로세스의 스레드 리스트 획득

fffff800`02d278e4 418bef          mov     ebp,r15d

fffff800`02d278e7 4c8da960010000  lea     r13,[rcx+160h]

fffff800`02d278ee 33c0            xor     eax,eax

fffff800`02d278f0 418d4f11        lea     ecx,[r15+11h]

fffff800`02d278f4 f0490fb14d00    lock cmpxchg qword ptr [r13],rcx

fffff800`02d278fa 0f8594000000    jne     nt!PsGetNextProcessThread+0xec (fffff800`02d27994)

fffff800`02d27900 493bf7          cmp     rsi,r15

fffff800`02d27903 7575            jne     nt!PsGetNextProcessThread+0xd2 (fffff800`02d2797a)

fffff800`02d27905 498b1c24        mov     rbx,qword ptr [r12] // 2) 스레드 리스트의 Flink 획득

fffff800`02d27909 493bdc          cmp     rbx,r12 // 3) 리스트의 끝이면 종료하고 아니면 계속 루프를 돌게 되는 비교문

fffff800`02d2790c 747f            je      nt!PsGetNextProcessThread+0xe5 (fffff800`02d2798d)

fffff800`02d2790e 4c8db3e0fbffff  lea     r14,[rbx-420h]

fffff800`02d27915 498bce          mov     rcx,r14

fffff800`02d27918 e8e3b2d7ff      call    nt!ObReferenceObjectSafe (fffff800`02aa2c00)

fffff800`02d2791d 413ac7          cmp     al,r15b

fffff800`02d27920 0f8485000000    je      nt!PsGetNextProcessThread+0x103 (fffff800`02d279ab)

fffff800`02d27926 bb01000000      mov     ebx,1


1)번을 보면 rcx+308을 참조하는 명령이 있는데 rcx 는 첫 번째 파라미터다. 이는 프로세스 오브젝트이므로 EPROCESS구조체의 +308 필드를 확인하면 된다.

kd> dt _EPROCESS ThreadListHead

nt!_EPROCESS

   +0x308 ThreadListHead : _LIST_ENTRY


1)~3)번을 보면 EPROCESS 구조체의 +308 위치에 있는 ThreadListHead 리스트의 스레드 목록을 열거하고 있다.

링크드 리스트의 경우 리스트 목록이 증가하면 리스트 열거시 목록과 비례해서 성능 지연이 발생한다. 따라서 앞서 이 함수를 지연 의심 함수로 언급한 것이다.

혹시 시스템에 스레드가 엄청 많이 생성되어 ThreadListHead 리스트가 엄청 증가한 것은 아닐까?

이를 검증하려면 시스템의 모든 프로세스를 확인해서 이상한 부분은 없는지 살펴봐야 한다.

!for_each_process 명령을 사용하면 전체 프로세스의 간략한 정보를 알 수 있다.

kd> !for_each_process

PROCESS fffffa8000cda400

    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000

    DirBase: 00187000  ObjectTable: fffff8a0000017e0  HandleCount: 501525.

    Image: System

 

    PROCESS fffffa8002243b30

    SessionId: none  Cid: 0118    Peb: 7fffffd8000  ParentCid: 0004

    DirBase: 24f55000  ObjectTable: fffff8a0004cce60  HandleCount:  32.

    Image: smss.exe

 

    PROCESS fffffa80021476f0

    SessionId: 0  Cid: 017c    Peb: 7fffffd7000  ParentCid: 016c

    DirBase: 1cc2d000  ObjectTable: fffff8a000113630  HandleCount: 443.

    Image: csrss.exe

 

    PROCESS fffffa8002c02060

    SessionId: 0  Cid: 01a4    Peb: 7fffffdd000  ParentCid: 016c

    DirBase: 1c833000  ObjectTable: fffff8a005cca010  HandleCount:  77.

    Image: wininit.exe

 

    PROCESS fffffa80028d1060

    SessionId: 1  Cid: 01b0    Peb: 7fffffd7000  ParentCid: 019c

    DirBase: 19f96000  ObjectTable: fffff8a005ce5c20  HandleCount: 511.

    Image: csrss.exe

 

    PROCESS fffffa8002c08060

    SessionId: 1  Cid: 01e8    Peb: 7fffffdf000  ParentCid: 019c

    DirBase: 1999c000  ObjectTable: fffff8a005dae700  HandleCount: 121.

    Image: winlogon.exe

 

    PROCESS fffffa8002c874a0

    SessionId: 0  Cid: 0218    Peb: 7fffffdf000  ParentCid: 01a4

    DirBase: 1b6cc000  ObjectTable: fffff8a001242560  HandleCount: 230.

    Image: services.exe

 

    PROCESS fffffa8002c95770

    SessionId: 0  Cid: 0224    Peb: 7fffffdf000  ParentCid: 01a4

    DirBase: 1aaa4000  ObjectTable: fffff8a005d78ec0  HandleCount: 569.

    Image: lsass.exe

 

    PROCESS fffffa8002c9ab30

    SessionId: 0  Cid: 022c    Peb: 7fffffdf000  ParentCid: 01a4

    DirBase: 1a72e000  ObjectTable: fffff8a005d7e9f0  HandleCount: 148.

    Image: lsm.exe

    ... ... 


이럴수가! 첫 번째 System 프로세스의 HandleCount를 보니 501,525개다. 50만 개가 넘는 핸들이 무지막지하게 열려 있는 상태다(보통의 정상적인 프로세스라면 수백~수천개 정도의 핸들만 유지하므로 비정상적인 수치다).

아무래도 시스템에 핸들 누수(Leak)가 발생하고 있는 심각한 상태로 의심된다.

이제 !handle 명령을 통해 Sysem 프로세스인 fffffa8000cda400 모든 핸들 내역을 확인해보자.

kd> !handle 0 f fffffa80`075a8990 

... ...

3734: Object: fffffa80014da060  GrantedAccess: 001fffff Entry: fffff8a00300bcd0

Object: fffffa80014da060  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014da030 (new version)

        HandleCount: 1  PointerCount: 1

 

        3738: Object: fffffa80014dab60  GrantedAccess: 001fffff Entry: fffff8a00300bce0

Object: fffffa80014dab60  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014dab30 (new version)

        HandleCount: 1  PointerCount: 1

 

        373c: Object: fffffa80014da660  GrantedAccess: 001fffff Entry: fffff8a00300bcf0

Object: fffffa80014da660  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014da630 (new version)

        HandleCount: 1  PointerCount: 1

 

        3740: Object: fffffa80014db060  GrantedAccess: 001fffff Entry: fffff8a00300bd00

Object: fffffa80014db060  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014db030 (new version)

        HandleCount: 1  PointerCount: 1

 

        3744: Object: fffffa80014dbb60  GrantedAccess: 001fffff Entry: fffff8a00300bd10

Object: fffffa80014dbb60  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014dbb30 (new version)

        HandleCount: 1  PointerCount: 1

 

        3748: Object: fffffa80014db660  GrantedAccess: 001fffff Entry: fffff8a00300bd20

Object: fffffa80014db660  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014db630 (new version)

        HandleCount: 1  PointerCount: 1

 

        374c: Object: fffffa80014dc060  GrantedAccess: 001fffff Entry: fffff8a00300bd30

Object: fffffa80014dc060  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014dc030 (new version)

        HandleCount: 1  PointerCount: 1

 

        3750: Object: fffffa80014dcb60  GrantedAccess: 001fffff Entry: fffff8a00300bd40

Object: fffffa80014dcb60  Type: (fffffa8000cdac40) Thread

    ObjectHeader: fffffa80014dcb30 (new version)

        HandleCount: 1  PointerCount: 1

        ... ...


대부분 스레드 핸들로 확인된다. 참조 카운트인 HandleCount PointerCount는 각각 1이다.

50만 개가 넘는 스레드 핸들이 아직 열려 있는 상태이므로 ThreadListHead 리스트는 이미 열거가 불가능할 정도로 수 많은 스레드 목록이 추가된 상태일 것이다. 의심이 확신이 되는 순간이다.

이제 시스템을 이렇게 엉망으로 만든 범인을 찾기 위해 스레드를 자세히 확인해야 한다. 임의의 스레드 하나를 선택해 상태를 살펴보자.

kd> !thread fffffa80014dcb60  

THREAD fffffa80014dcb60  Cid 0004.38d4  Teb: 0000000000000000 Win32Thread: 0000000000000000 TERMINATED             // 종료된 상태

Not impersonating

DeviceMap                 fffff8a000008aa0

Owning Process            fffffa8000cda400       Image:         System

Attached Process          N/A            Image:         N/A

Wait Start TickCount      0              Ticks: 40554 (0:00:10:33.656)

Context Switch Count      1              IdealProcessor: 3             

UserTime                  00:00:00.000

KernelTime                00:00:00.000

Win32 Start Address MyDrv (0xfffff88002e4d3e0)

Stack Init 0 Current fffff880031a69f0

Base fffff880031a7000 Limit fffff880031a1000 Call 0

Priority 8 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5


이미 종료된 TERMINATED 상태의 스레드다. 이어 다른 스레드도 몇 개 확인해보니 모두TERMINATED 상태다. 참조 카운트가 아직 남아 있으므로 오픈된 핸들을 해제하지 않고 스레드가 종료된 상황으로 보인다.

왜냐하면 스레드가 종료될 때 참조 카운트가 0이 아니면 스레드가 제거되지 않고 TERMINATED 상태로 남기 때문이다.

50만개의 스레드가 시스템을 재부팅하기 전까지는 영원히 죽지 못하는 좀비 상태가 돼버렸다.

스레드 핸들이 모두 System 프로세스의 핸들 테이블에 있는 부분에 집중할 필요가 있다.

System 프로세스의 핸들 테이블은 커널 핸들의 테이블로 보통 커널 드라이버에서 nt!PsCreateSystemThread 함수를 통해 시스템 스레드를 생성할 경우 여기에 핸들이 생성된다.

이런 상황이면 커널 핸들을 생성하고 해제하지 않는 커널 드라이버가 범인일 가능성이 매우 높다.

스레드의 Win32 Start Address 정보를 보면 좀비 상태의 스레드 시작 주소가 MyDrv 모듈의 주소로 확인된다. MyDrv 모듈에서 생성한 스레드가 해제되고 있지 않은 상황이다. 따라서 스레드 생성 주체인 MyDrv 모듈이 강력한 용의자다.

최종 확인을 위해 !stacks 명령으로 전체 콜 스택에서 MyDrv의 동작을 살펴보자.

kd> !stacks 2 MyDrv

Proc.Thread  .Thread  Ticks   ThreadState Blocker

                            [fffff80002c071c0 Idle]

                            [fffffa8000cda400 System]

   4.1eac64  fffffa8003143b60 ffff623d READY      nt!KiSwapContext+0x7a

                                        nt!KiQuantumEnd+0x1b4

                                        nt!KiDispatchInterruptContinue+0x16

                                        nt!KiDpcInterruptBypass+0x13

                                        nt!KiSecondaryClockInterrupt+0x1a8

                                        nt!ExfAcquirePushLockExclusive+0xd2

                                        nt!PspInsertThread+0x829

                                        nt!PspCreateThread+0x246

                                        nt!PsCreateSystemThread+0x125

                                        MyDrv+0x1505

                                        nt!PspSystemThreadStartup+0x5a

                                        nt!KiStartSystemThread+0x16

                                        ... ... 


역시 System 프로세스 중 fffffa8003143b60 스레드에서 지속적으로 시스템 스레드를 생산하고 있다!

이렇게 생성한 스레드를 정상적으로 해제하지 않아 좀비로 만든 것이다.

결국 행이 발생한 원인은 MyDrv 모듈에서 수 많은 시스템 스레드를 생성하면서 해제하지 않았고, 이어 NtQuerySystemInformation 함수 내부에서는 다량의 스레드를 열거하느라 CPU 사용율을 증가시켰기 때문이다.

MyDrv 모듈에 의해 좀비 스레드는 계속 증가하는데 NtQuerySystemInformation 함수는 매우 빈번하게 호출되므로 시스템은 아마 사용이 불가능할 정도로 끔찍하게 느려졌을 것이다.

어쨌든 원인을 찾아 정말 다행이다. 이제 남은 일은 MyDrv 모듈에서 시스템 스레드를 생성하고 해제하지 않은 부분을 찾아 문제를 고치는 것이다.


'Dump Analysis' 카테고리의 다른 글

[0x133] DPC_WATCHDOG_VIOLATION  (0) 2018.07.29
[0xC5] 해제 리스트 손상  (0) 2018.07.19
[0xC5] 풀 헤더 손상  (0) 2018.07.16
[0x1A] 페이지 손상  (0) 2018.07.12
[0x50] 해제된 핸들  (0) 2018.07.09
prev"" #1 next