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 티스토리 가입하기!
'hang'에 해당되는 글 2건
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
2015. 1. 22. 23:57

아주 흥미로운 덤프를 분석하게 되서 공유한다.


거의 분석 불가능한 덤프였지만 다행히 분석이 완료되었다.

MyDrv.sys 를 언로드하려고 했지만 알 수 없는 이유로 실패한 상태로 메모리에 남아 있는 이슈여서 강제로 덤프를 생성하여 분석하게 된 Case 다.


참고 : 드라이버 로드시 ERROR_ALREADY_EXISTS(183) 에러



1. 일단 필터매니저에 등록된 필터 정보를 확인해 본다.

kd> !fltkd.frames

Frame List: fffff880013302c0
    FLTP_FRAME: fffffa80039e5010 "Frame 1" "429998.99 to 429999.280700"
    FLT_FILTER: fffffa8006dcc960 "SomeDrv" "[NoName]"
    FLTP_FRAME: fffffa8001b2aac0 "Frame 0" "0 to 429998.99"   
    FLT_FILTER: fffffa800290d900 "MyDrv" "326020"
    FLT_INSTANCE: fffffa80031b4330 "MyDrv Instance" "326020"
    FLT_INSTANCE: fffffa800699d170 "MyDrv Instance" "326020"
    FLT_INSTANCE: fffffa8003658b40 "MyDrv Instance" "326020"



어라, Frame 1 의 SomeDrv.sys 모듈의 상태가 이상하다.
Filter Instance 정보는 없고 Filter Object 정보만 남아 있다.


2. 일단 언로드되지 못하고 있는 불쌍한 MyDrv.sys 를 먼저 확인해 보자.

kd> dt _FLT_FILTER fffffa800290d900
fltmgr!_FLT_FILTER
    +0x000 Base : _FLT_OBJECT
    +0x020 Frame : 0xfffffa80`01b2aac0 _FLTP_FRAME   
    +0x028 Name : _UNICODE_STRING "MyDrv"
    +0x038 DefaultAltitude : _UNICODE_STRING "326020"
    +0x048 Flags : 2 ( FLTFL_FILTERING_INITIATED )
    +0x050 DriverObject : 0xfffffa80`03673c80 _DRIVER_OBJECT
    ... ...


kd> !drvobj 0xfffffa80`03673c80
Driver object (fffffa8003673c80) is for:
    \Driver\MyDrv
Driver Extension List: (id , addr)

Device Object list:
fffffa8002e8f550


kd> !devobj fffffa8002e8f550
Device object (fffffa8002e8f550) is for:
    MyDrv \Driver\MyDrv DriverObject fffffa8003673c80
Current Irp 00000000 RefCount 0 Type 00000022 Flags 00000040
Dacl fffff9a1000846e1 DevExt 00000000 DevObjExt fffffa8002e8f6a0
ExtensionFlags (0x00000801) DOE_UNLOAD_PENDING, DOE_DEFAULT_SD_PRESENT
Characteristics (0000000000)
Device queue is not busy.



역시 언로드되지 못하고 언로드 중인 상태로 남아 있다.
일단 내가 제일 의심스러워 MyDrv.sys 의 언로드 루틴을 검토했지만 버그로 보일만한 부분은 발견되지 않았다.


3. 이제 이상했던 SomeDrv.sys 모듈의 Filter Object 정보를 확인해보자.

kd> !fltkd.filter fffffa8006dcc960

FLT_FILTER: fffffa8006dcc960 "SomeDrv" "[NoName]"
    FLT_OBJECT: fffffa8006dcc960 [02000003] Filter DRAINING ZOMBIED

        RundownRef : 0x0000000000000001 (0) drained
        PointerCount : 0x00000001
        PrimaryLink : [fffffa800b400020-fffffa80039e50c0]
    Frame : fffffa80039e5010 "Frame 1"
    Flags : [00000002] FilteringInitiated
    DriverObject : 0xfffffa8003673c80
    FilterLink : [fffffa800b400020-fffffa80039e50c0]
    PreVolumeMount : 0000000000000000 (null)
    PostVolumeMount : 0000000000000000 (null)
    ... ...



Filte Object 가 "DRAINING ZOMBIED" 상태로 확인된다.
참고로 "DRAINING ZOMBIED" 플래그는 필터매니저가 언로드 과정 중에 내부적으로 설정하는 상태 값이다.
즉, 언로드 과정에서 이 플래그가 설정되는 것 자체는 정상적인 동작이다.


참고로, 미니필터에서 필터매니저에 Context 를 할당(FltAllocateContext )하거나 사용(FltSetStreamContext)하고 해제(FltReleaseContext)하지 않는 경우 필터매니저가 내부적으로 관리하는 ReferenceCount 가 맞지 않게 되어 언로드시 해당 미니필터의 Filter Object 가 "DRAINED" 상태로 언로드에 실패할 수 있다.
(언로드가 완료되지 못한 상태로 메모리에 남아 있게 된다)


다시 본론으로 돌아와서, 문제는 덤프 상의 SomeDrv.sys 는 이미 언로드되어 버린(메모리 상에 없는) 녀석인데도 "DRAINING ZOMBIED" 상태인 Filter Object 가 여전히 남아 있다는 것이다.
(언로드가 완료되면 Filter Object 는 제거되고, Filter Frame 에 더 이상 등록된 Filter 가 없으면 해당 Frame 도 제거되야 한다)

이미 SomeDrv.sys 는 언로드되어 메모리 상에서 없어졌기 때문에 이쯤되면 분석이 거의 불가능한 상태다.

SomeDrv.sys 가 "DRAINING ZOMBIED" 상태인 것과 MyDrv.sys 의 Unload Pending 증상 사이에 어떤 연관성이 있을까?

다행히 다른 시스템에서 MyDrv.sys 가 또 Unload Pending 에 빠진 덤프를 얻을 수 있었다.
SomeDrv.sys 도 있고 SomeDrv.sys 역시 동일하게 "DRAINING ZOMBIED" 상태에 빠져있다.


이쯤되니 다시 덤프를 꺼내 MyDrv.sys, SomeDrv.sys, 필터매니저(fltmgr.sys), 심지어 nt 커널까지 모든 걸 의심의 눈초리로 바라보며 다음과 같은 가설을 세워보기로 했다.


1) SomeDrv.sys 의 버그로 언로드시 "DRAINING ZOMBIED" 상태가 됨

2) SomeDrv.sys 가 "DRAINING ZOMBIED" 가 되는 과정에서 필터매니저나 nt 커널의 데이터가 손상됨

3) 데이터 손상으로 인한 필터매니저나 nt 커널의 오동작으로 MyDrv.sys 가 언로드되지 못함
 


음... 입증하기가 만만치 않아 보인다.
먼저 StopService() 호출 후 MyDrv.sys 의 언로드 루틴이 호출되기까지 언로드 과정을 분석해보니 다음과 같은 콜 스택으로 진행되었다.


08 MyDrv!DriverUnload
07 fltmgr!FltUnregisterFilter
06 MyDrv!MiniFilterUnload
05 fltmgr!FltpDoUnloadFilter
04 fltmgr!FltpMiniFilterDriverUnload
03 nt!IopLoadUnloadDriver                  (System)               
02 nt!IopUnloadDriver                         (Services.exe)
... ...
01 services!StopService



각 함수에서 하는 일을 정리해보면 다음과 같다.


1) 유저모드에서 MyDrv.sys 에 대해 StopService() 호출

2) nt 커널의 언로드 함수인 nt!IopUnloadDriver() 내부에서 언로드할 드라이버의 DeviceObject 에 "DOE_UNLOAD_PENDING"
플래그를 설정하고 WorkItem 으로 nt!IopLoadUnloadDriver() 호출
(이후 과정은 System Thread Context 에서 수행)


nt!IopUnloadDriver+0x37e (fffff800`01a79780):
    call to nt!IopCheckUnloadDriver (fffff800`0175c750)

kd> u nt!IopCheckUnloadDriver L25
nt!IopCheckUnloadDriver:
... ...
fffff800`0175c7bd 8bc3 mov eax,ebx
fffff800`0175c7bf eb49 jmp nt!IopCheckUnloadDriver+0xba (fffff800`0175c80a)
fffff800`0175c7c1 c60601 mov byte ptr [rsi],1
fffff800`0175c7c4 eb1c jmp nt!IopCheckUnloadDriver+0x92 (fffff800`0175c7e2)
fffff800`0175c7c6 488b8138010000 mov rax,qword ptr [rcx+138h]
fffff800`0175c7cd 83482001 or dword ptr [rax+20h],1                 DOE_UNLOAD_PENDING



3) nt!IopLoadUnloadDriver() 에서 필터매니저의 fltmgr!FltpMiniFilterDriverUnload() 호출

4) fltmgr!FltpMiniFilterDriverUnload() 에서 언로드할 드라이버를 찾아 fltmgr!FltpDoUnloadFilter() 호출

5) fltmgr!FltpDoUnloadFilter() 에서 필터매니저가 관리하는 Filter Object 에 "UnloadInProgress" 플래그를 설정하고 MyDrv.sys 의 언로드 함수인 MyDrv!MiniFilterUnload() 호출

kd> u fltmgr!FltpDoUnloadFilter L25
fltmgr!FltpDoUnloadFilter:
... ...
fffff880`01344d7f 41bc10001cc0 mov r12d,0C01C0010h
fffff880`01344d85 e9e7000000 jmp fltmgr!FltpDoUnloadFilter+0x161 (fffff880`01344e71)
fffff880`01344d8a 8bfa mov edi,edx
fffff880`01344d8c 83e701 and edi,1
fffff880`01344d8f 7404 je fltmgr!FltpDoUnloadFilter+0x85 (fffff880`01344d95)
fffff880`01344d91 83494801 or dword ptr [rcx+48h],1                 UnloadInProgress


6) MyDrv.sys 의 언로드 함수인 MyDrv!MiniFilterUnload() 에서 이후 언로드 작업 수행


이제 각 언로드 과정을 하나씩 검증해가며 문제가 없는지 분석해보자.

MyDrv.sys 의 DeviceObject 에 "DOE_UNLOAD_PENDING" 플래그는 이미 확인했으니 1)~2)번은 범인이 아니다.

6)번 과정인 MyDrv.sys 의 MyDrv!MiniFilterUnload() 함수는 MyDrv.sys 의 내부 언로드 관련 플래그를 확인해보니 아직 호출되지 않은 것으로 확인된다.

MyDrv.sys 도 범인이 아니고 가설대로면 nt 커널과 필터매니저 구간인 3)~5)번에 범인이 있을 것이다.


4. MyDrv.sys 의 Filter Object 상태를 확인해보자.

kd> !fltkd.filter fffffa800290d900

FLT_FILTER: fffffa800290d900 "MyDrv" "326020"
    FLT_OBJECT: fffffa800290d900 [02000000] Filter
        RundownRef : 0x0000000000000044 (34)
        PointerCount : 0x00000001
        PrimaryLink : [fffffa8004770020-fffffa8001cb5390]
    Frame : fffffa8001b2aac0 "Frame 0"
    Flags : [00000002] FilteringInitiated
        ... ...



응? Filter Object 에 "FilteringInitiated" 플래그만 있고 "UnloadInProgress" 플래그가 없다.
5)번 과정도 수행되지 않았다고 보는 것이 타당하다.
5)번 과정이 수행되었다면 Filter Object 의 플래그가 "FilteringInitiated UnloadInProgress" 로 변경되었을 것이다.

범인은 3)번이나 4)번일 가능성이 높다.

먼저 3)번의 nt!IopLoadUnloadDriver() 를 분석해보니 특별히 문제될만한 동작을 수행하는 함수가 아니었다.

그래서 4)번 fltmgr!FltpMiniFilterDriverUnload() 함수를 상세 분석해보기로 했다.


IDA 의 Hex-ray 없이 커널 함수를 디스어셈하여 분석하는건 굉장히 지루한 작업이지만 나에게 있어선 분석의 감을 잃지 않게 해주는 습관이기도 하다.


5. 마지막 남은 fltmgr!FltpMiniFilterDriverUnload() 함수를 분석해보자.

fltmgr!FltpMiniFilterDriverUnload:
fffff880`01344fb0 488bc4 mov rax,rsp
fffff880`01344fb3 48895808 mov qword ptr [rax+8],rbx
fffff880`01344fb7 48896810 mov qword ptr [rax+10h],rbp
fffff880`01344fbb 48897018 mov qword ptr [rax+18h],rsi
fffff880`01344fbf 48897820 mov qword ptr [rax+20h],rdi
fffff880`01344fc3 4155 push r13
fffff880`01344fc5 4883ec20 sub rsp,20h
fffff880`01344fc9 488be9 mov rbp,rcx ; rcx : DriverObject
fffff880`01344fcc ff154611feff call qword ptr [fltmgr!_imp_KeEnterCriticalRegion (fffff880`01326118)]
fffff880`01344fd2 488d0d7fb2feff lea rcx,[fltmgr!FltGlobals+0x58 (fffff880`01330258)]
                                                ; 0 FLT_FRAME.FilterUnloadLock (ERESOURCE)
fffff880`01344fd9 b201 mov dl,1
fffff880`01344fdb ff152f11feff call qword ptr [fltmgr!_imp_ExAcquireResourceSharedLite (fffff880`01326110)]
fffff880`01344fe1 488b3dd8b2feff mov rdi,qword ptr [fltmgr!FltGlobals+0xc0 (fffff880`013302c0)]
                                                ; 1 FLT_FRAME.Links
fffff880`01344fe8 4c8d2dd1b2feff lea r13,[fltmgr!FltGlobals+0xc0 (fffff880`013302c0)]
                                                ; 1 FLT_FRAME.Links->FLink (FLT_FRAME 0 .Links)
fffff880`01344fef eb43 jmp fltmgr!FltpMiniFilterDriverUnload+0x84 (fffff880`01345034)
fffff880`01344ff1 ff152111feff call qword ptr [fltmgr!_imp_KeEnterCriticalRegion (fffff880`01326118)]
fffff880`01344ff7 488d4f40 lea rcx,[rdi+40h]
                                                ; 1 FLT_FRAME.RegisteredFilters (ERESOURCE)
fffff880`01344ffb b201 mov dl,1
fffff880`01344ffd ff150d11feff call qword ptr [fltmgr!_imp_ExAcquireResourceSharedLite (fffff880`01326110)]
fffff880`01345003 4c8d9fa8000000 lea r11,[rdi+0A8h]
                                                ; 1 FLT_FRAME.RegisteredFilters.rList (FLT_FILTER.PrimaryLink)
fffff880`0134500a 498b03 mov rax,qword ptr [r11]
                                                ; FLT_FILTER.PrimaryLink.FLink (FLT_FILTER.PrimaryLink)
fffff880`0134500d eb0d jmp fltmgr!FltpMiniFilterDriverUnload+0x6c (fffff880`0134501c)
fffff880`0134500f 488d70f0 lea rsi,[rax-10h]                      ; FLT_FILTER
fffff880`01345013 48396e50 cmp qword ptr [rsi+50h],rbp    ; if( FLT_FILTER.DriverObject == DriverObject )
fffff880`01345017 744e je fltmgr!FltpMiniFilterDriverUnload+0xb7 (fffff880`01345067)        ; Success

fffff880`01345019 488b00 mov rax,qword ptr [rax]
fffff880`0134501c 493bc3 cmp rax,r11                             ; Filter List 끝인지 확인
fffff880`0134501f 75ee jne fltmgr!FltpMiniFilterDriverUnload+0x5f (fffff880`0134500f)
fffff880`01345021 488d4f40 lea rcx,[rdi+40h]                    ; 1 FLT_FRAME.RegisteredFilters (ERESOURCE)
fffff880`01345025 ff15fd10feff call qword ptr [fltmgr!_imp_ExReleaseResourceLite (fffff880`01326128)]
fffff880`0134502b ff15ef10feff call qword ptr [fltmgr!_imp_KeLeaveCriticalRegion (fffff880`01326120)]
fffff880`01345031 488b3f mov rdi,qword ptr [rdi]
fffff880`01345034 493bfd cmp rdi,r13                             ; Frame List 끝인지 확인
fffff880`01345037 75b8 jne fltmgr!FltpMiniFilterDriverUnload+0x41 (fffff880`01344ff1)
fffff880`01345039 488d0d18b2feff lea rcx,[fltmgr!FltGlobals+0x58 (fffff880`01330258)]
                                                                             ; 0 FLT_FRAME.FilterUnloadLock (ERESOURCE)
fffff880`01345040 ff15e210feff call qword ptr [fltmgr!_imp_ExReleaseResourceLite (fffff880`01326128)]
fffff880`01345046 ff15d410feff call qword ptr [fltmgr!_imp_KeLeaveCriticalRegion (fffff880`01326120)]
fffff880`0134504c 488b5c2430 mov rbx,qword ptr [rsp+30h]
fffff880`01345051 488b6c2438 mov rbp,qword ptr [rsp+38h]
fffff880`01345056 488b742440 mov rsi,qword ptr [rsp+40h]
fffff880`0134505b 488b7c2448 mov rdi,qword ptr [rsp+48h]
fffff880`01345060 4883c420 add rsp,20h
fffff880`01345064 415d pop r13
fffff880`01345066 c3 ret

fffff880`01345067 488bce mov rcx,rsi
fffff880`0134506a e8b136fdff call fltmgr!FltObjectReference (fffff880`01318720)
fffff880`0134506f 488d4f40 lea rcx,[rdi+40h]
fffff880`01345073 8bd8 mov ebx,eax
fffff880`01345075 ff15ad10feff call qword ptr [fltmgr!_imp_ExReleaseResourceLite (fffff880`01326128)]
fffff880`0134507b ff159f10feff call qword ptr [fltmgr!_imp_KeLeaveCriticalRegion (fffff880`01326120)]
fffff880`01345081 488d0dd0b1feff lea rcx,[fltmgr!FltGlobals+0x58 (fffff880`01330258)]
fffff880`01345088 ff159a10feff call qword ptr [fltmgr!_imp_ExReleaseResourceLite (fffff880`01326128)]
fffff880`0134508e ff158c10feff call qword ptr [fltmgr!_imp_KeLeaveCriticalRegion (fffff880`01326120)]
fffff880`01345094 85db test ebx,ebx
fffff880`01345096 78b4 js fltmgr!FltpMiniFilterDriverUnload+0x9c (fffff880`0134504c)
fffff880`01345098 41b001 mov r8b,1
fffff880`0134509b ba01000000 mov edx,1
fffff880`013450a0 488bce mov rcx,rsi
fffff880`013450a3 e868fcffff call fltmgr!FltpDoUnloadFilter (fffff880`01344d10)
                                                                            ; Call fltmgr!FltpDoUnloadFilter
fffff880`013450a8 eba2 jmp fltmgr!FltpMiniFilterDriverUnload+0x9c (fffff880`0134504c)
                                                                            ; Go to end of function


fltmgr!FltpMiniFilterDriverUnload() 함수 동작을 정리해보면 다음과 같다.


1) 필터매니저가 관리하는 FLT_FRAME 중 가장 마지막에 생성된 Frame 의 FLT_FILTER 를 검사(Frame 1부터)
    - fltmgr!FltGlobals 전역 변수에서 Frame 에 대한 리스트(FLT_FRAME.Links)를 얻음
    - FLT_FRAME.RegisteredFilters.rList 에서 Frame 에 등록된 Filter 에 대한 리스트(FLT_FILTER.PrimaryLink)를 얻음

2) 언로드 요청된 모듈의 DriverObject 와 FLT_FILTER 의 내부 필드인 DriverObject 의 값이 일치하는지 확인
    - FLT_FILTER.DriverObject

3) 일치할 경우 해당 모듈에 대해 fltmgr!FltpDoUnloadFilter() 를 호출해주고, 다를 경우 마지막 Frame 까지 확인



결론적으로 필터매니저에 등록된 미니필터들은 필터매니저에서 로직상 하나의 Frame 리스트로 관리된다고 볼 수 있다.

하나의 리스트를 순차적으로 검사하며 언로드를 수행하기 때문에 앞 쪽 리스트의 DriverObject 정보가 잘못되면 뒷 쪽 리스트의 미니필터가 언로드되지 못할 가능성이 있다.


이제 마지막 과정만 남았다.

함수 분석 결과 Frame 리스트에는 Frame 0 에 등록된 MyDrv.sys 보다 Frame 1 에 등록된 SomeDrv.sys 가 앞 쪽에 위치하고 있다.
필터매니저가 SomeDrv.sys 에 대해 정말로 잘못된 DriverObject 를 가지고 있었는지만 확인하면 된다.


6. 필터매니저가 보관하고 있는 SomeDrv.sys 의 정보를 확인해보자.

kd> !fltkd.frames

Frame List: fffff880013302c0
FLTP_FRAME: fffffa80039e5010 "Frame 1" "429998.99 to 429999.280700"
FLT_FILTER: fffffa8006dcc960"SomeDrv" "[NoName]"
FLTP_FRAME: fffffa8001b2aac0 "Frame 0" "0 to 429998.99"
FLT_FILTER: fffffa800290d900 "MyDrv" "326020"
FLT_INSTANCE: fffffa80031b4330 "MyDrv Instance" "326020"
FLT_INSTANCE: fffffa800699d170 "MyDrv Instance" "326020"
FLT_INSTANCE: fffffa8003658b40 "MyDrv Instance" "326020"


kd> dt _FLT_FILTER fffffa8006dcc960
fltmgr!_FLT_FILTER
    +0x000 Base : _FLT_OBJECT
    +0x020 Frame : 0xfffffa80`039e5010 _FLTP_FRAME
    +0x028 Name : _UNICODE_STRING "SomeDrv"
    +0x038 DefaultAltitude : _UNICODE_STRING ""
    +0x048 Flags : 2 ( FLTFL_FILTERING_INITIATED )
    +0x050 DriverObject : 0xfffffa80`03673c80 _DRIVER_OBJECT
    ... ...


kd> !drvobj 0xfffffa80`03673c80
Driver object (fffffa8003673c80) is for:
    \Driver\MyDrv
Driver Extension List: (id , addr)

Device Object list:
fffffa8002e8f550



역시, 필터매니저가 어떤 이유로 SomeDrv.sys 의 Filter Object 에 SomeDrv.sys 의 DriverObject 가 아닌 MyDrv.sys 의 DriverObject 를 설정해 놓았다.

결국 MyDrv.sys 를 StopService() 했을 때 필터매니저의 fltmgr!FltpDoUnloadFilter() 에서 언로드를 요청한 MyDrv.sys 가 아닌 SomeDrv.sys 를 언로드한 것이 원인이었다. OTL

언로드 과정 중 4)번 fltmgr!FltpDoUnloadFilter() 부터 잘못되버려 MyDrv.sys 의 언로드 함수인 MyDrv!MiniFilterUnload() 는 호출조차 되지 못한 것이다.


※ 이 필터매니저(fltmgr.sys) 버그는 Microsoft 에 Report 했고 최신 필터매니저 버전에서 fix 되었다.

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

[0xC5] 풀 헤더 손상  (0) 2018.07.16
[0x1A] 페이지 손상  (0) 2018.07.12
[0x50] 해제된 핸들  (0) 2018.07.09
[0x50] 숨겨진 콜 스택  (0) 2018.07.07
[0x50] UNICODE_STRING  (0) 2018.07.05
prev"" #1 next