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 티스토리 가입하기!
'Unload'에 해당되는 글 1건
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