[WinDBG] DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS Analyze (2)

Rainie
13 min readDec 28, 2022

--

上上一篇跟大家分享若已知造成 lock page 的 driver 是誰的話 ,dump 可以如何分析,這篇則是適用於你不知道是哪個 driver 造成問題時可以使用的分析手法。

首先,同樣使用 analyze -v 進行初步分析

!analyze -v

Arg3 提供了 locked pages 的 Memory Descriptor Lists (MDL) 結構,MDL 保存了當前申請 locked pages 的 virtual address 與 physical address 的映射關係,並確保這塊空間在資源未重新釋放前,永遠不會被 page out。

driver 申請 locked pages 的流程通常是先呼叫 IoAllocateMdl 分派 MDL 的空間,接著呼叫MmProbeAndLock 鎖住這塊空間,由 MDL 負責保留這些資訊,直到呼叫 MmUnlockPages 後釋放。

[補充] 如果想在 dump 上顯示目前被 MDL lock 住的記憶體大小,可以下 !vm 查看 Pages for MDLs 的數量

Ref : https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_mdl

MDL 的資料結構如下:

typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;

Step1. 使用 dt 把 _MDL 結構顯示出來

1: kd> dt nt!_MDL ffff8203c4e6c9e0
+0x000 Next : (null)
+0x008 Size : 0n1568
+0x00a MdlFlags : 0n259
+0x00c AllocationProcessorNumber : 0x3333
+0x00e Reserved : 0x3333
+0x010 Process : 0xffff8203`c29a0080 _EPROCESS
+0x018 MappedSystemVa : 0xffffe001`12c00000 Void
+0x020 StartVa : 0x00007ffb`77400000 Void
+0x028 ByteCount : 0xbe000
+0x02c ByteOffset : 0

其中要關注的地方是 _EPROCESS ,我們需要取得 _EPROCESS 裡用來存放已被鎖定的頁面資訊的 LockedPagesList 這個結構。

Step2. 使用 dx 把 _EPROCESS 結構解析出來,並找到 LockedPagesList 的位址

LockedPagesList 指向了存放 LOCK_HEADER 結構的 List 開頭,Windows 透過此串列紀錄哪些頁面已被鎖住。

1: kd> dx -id 0,0,ffff8203af07a200 -r1 ((ntkrnlmp!_EPROCESS *)0xffff8203c29a0080)
((ntkrnlmp!_EPROCESS *)0xffff8203c29a0080) : 0xffff8203c29a0080 [Type: _EPROCESS *]
[+0x000] Pcb [Type: _KPROCESS]
[+0x438] ProcessLock [Type: _EX_PUSH_LOCK]
[+0x440] UniqueProcessId : 0xe74 [Type: void *]
[+0x448] ActiveProcessLinks [Type: _LIST_ENTRY]
[+0x458] RundownProtect [Type: _EX_RUNDOWN_REF]
[+0x460] Flags2 : 0x100d080 [Type: unsigned long]
[+0x460 ( 0: 0)] JobNotReallyActive : 0x0 [Type: unsigned long]
[+0x460 ( 1: 1)] AccountingFolded : 0x0 [Type: unsigned long]
[+0x460 ( 2: 2)] NewProcessReported : 0x0 [Type: unsigned long]
[+0x460 ( 3: 3)] ExitProcessReported : 0x0 [Type: unsigned long]
[+0x460 ( 4: 4)] ReportCommitChanges : 0x0 [Type: unsigned long]
[+0x460 ( 5: 5)] LastReportMemory : 0x0 [Type: unsigned long]
..... 省略
[+0x608] LockedPagesList : 0xffff8203c6ce9d90 [Type: void *] <--- Here !!
[+0x610] ReadOperationCount : {1} [Type: _LARGE_INTEGER]
[+0x618] WriteOperationCount : {0} [Type: _LARGE_INTEGER]
[+0x620] OtherOperationCount : {305} [Type: _LARGE_INTEGER]
[+0x628] ReadTransferCount : {6441} [Type: _LARGE_INTEGER]
[+0x630] WriteTransferCount : {0} [Type: _LARGE_INTEGER]
[+0x638] OtherTransferCount : {6710} [Type: _LARGE_INTEGER]

[補充1] 以前似乎可以直接透過 !lockedpages [_EPROCESS ADDRESS] 拿到 LockedPagesList 的記憶體位址,但現在已經失效了

[補充2] Windbg 有正確 load 到 windows symbol 的話,針對_EPROCESS 結構已經有連結可以直接點,不需要自己敲指令!

Step3. 使用 dqdump 出 LockedPagesList 存放的第一個 LOCK_HEADER 的位址

1: kd> dq ffff8203c6ce9d90
ffff8203`c6ce9d90 ffff8203`c69187e0 00000000`00000000
ffff8203`c6ce9da0 00000000`000000be 00000000`00000000
ffff8203`c6ce9db0 00000000`00000001 00000000`00000000
ffff8203`c6ce9dc0 31526d73`02040000 00000000`00000000
ffff8203`c6ce9dd0 ffff8203`c6ce9ad1 00000001`20048125
ffff8203`c6ce9de0 00000000`00000000 00000000`00000000
ffff8203`c6ce9df0 ffff8203`c64dc770 00000000`00000000
ffff8203`c6ce9e00 31526d73`02040000 00000000`00000000

Step4. 使用 dt把 LOCK_HEADER 的結構顯示出來,找到 StackTrace

LOCK_HEADER 的 StackTrace 存放了有呼叫到 lock page 相關 API 的 call stack

1: kd> dt _LOCK_TRACKER ffff8203c69187e0
nt!_LOCK_TRACKER
+0x000 LockTrackerNode : _RTL_BALANCED_NODE
+0x018 Mdl : 0xffff8203`c4e6c9e0 _MDL
+0x020 StartVa : 0x00007ffb`77400000 Void
+0x028 Count : 0xbe
+0x030 Offset : 0
+0x034 Length : 0xbe000
+0x038 Who : 3
+0x03c Hash : 0xeaa6a156
+0x040 Page : 0x7a9ba
+0x048 StackTrace : [8] 0xfffff806`57b41611 Void
+0x088 Process : 0xffff8203`c29a0080 _EPROCESS

Step5. 使用 ub把 StackTrace 指令反組譯出來,就可以追蹤到呼叫 locked pages 的人了

1: kd> ub 0xfffff80657b41611
nt!MiAddMdlTracker+0xd5:
fffff806`57b415e9 4489ac2490000000 mov dword ptr [rsp+90h],r13d
fffff806`57b415f1 448d4240 lea r8d,[rdx+40h]
fffff806`57b415f5 e886efedff call nt!memset (fffff806`57a20580)
fffff806`57b415fa 4c8d8c2490000000 lea r9,[rsp+90h]
fffff806`57b41602 4d8bc6 mov r8,r14
fffff806`57b41605 ba08000000 mov edx,8
fffff806`57b4160a 33c9 xor ecx,ecx
fffff806`57b4160c e86fc2dbff call nt!RtlCaptureStackBackTrace (fffff806`578fd880)
1: kd> ub 0xfffff80657a40c2f
nt!MiProbeAndLockPages+0x1dd32e:
fffff806`57a40c0e 035720 add edx,dword ptr [rdi+20h]
fffff806`57a40c11 488bcf mov rcx,rdi
fffff806`57a40c14 8b4728 mov eax,dword ptr [rdi+28h]
fffff806`57a40c17 81e2ff0f0000 and edx,0FFFh
fffff806`57a40c1d 4805ff0f0000 add rax,0FFFh
fffff806`57a40c23 4803d0 add rdx,rax
fffff806`57a40c26 48c1ea0c shr rdx,0Ch
fffff806`57a40c2a e8e5081000 call nt!MiAddMdlTracker (fffff806`57b41514)
1: kd> ub 0xfffff806578638c9
nt!MmProbeAndLockPages+0xa:
fffff806`578638aa 41ba01000000 mov r10d,1
fffff806`578638b0 458bca mov r9d,r10d
fffff806`578638b3 450f44c8 cmove r9d,r8d
fffff806`578638b7 84d2 test dl,dl
fffff806`578638b9 458bc1 mov r8d,r9d
fffff806`578638bc 440f44d0 cmove r10d,eax
fffff806`578638c0 410fb6d2 movzx edx,r10b
fffff806`578638c4 e817000000 call nt!MiProbeAndLockPages (fffff806`578638e0)
1: kd> ub 0xfffff80657ff9e76
nt!VerifierMmProbeAndLockPages+0x96:
fffff806`57ff9e56 b9a10000c0 mov ecx,0C00000A1h
fffff806`57ff9e5b e8104494ff call nt!RtlRaiseStatus (fffff806`5793e270)
fffff806`57ff9e60 cc int 3
fffff806`57ff9e61 488b0548473600 mov rax,qword ptr [nt!pXdvMmProbeAndLockPages (fffff806`5835e5b0)]
fffff806`57ff9e68 448bc6 mov r8d,esi
fffff806`57ff9e6b 408ad7 mov dl,dil
fffff806`57ff9e6e 488bcb mov rcx,rbx
fffff806`57ff9e71 e8dac0a1ff call nt!guard_dispatch_icall (fffff806`57a15f50)
1: kd> ub 0xfffff800ed157bb7
Unable to load image \SystemRoot\system32\drivers\xxx.sys, Win32 error 0n2
xxx+0x7b9d:
fffff800`ed157b9d 488903 mov qword ptr [rbx],rax
fffff800`ed157ba0 4885c0 test rax,rax
fffff800`ed157ba3 7472 je xxx+0x7c17 (fffff800`ed157c17)
fffff800`ed157ba5 440f20c1 mov rcx,cr8
fffff800`ed157ba9 4533c0 xor r8d,r8d
fffff800`ed157bac 33d2 xor edx,edx
fffff800`ed157bae 488bc8 mov rcx,rax
fffff800`ed157bb1 ff1549860000 call qword ptr [xxx+0x10200 (fffff800`ed160200)]

在最後一個 ub 可以看到 callstack 定位在 xxx.sys (已先屏蔽我們 team 的 driver 名稱 😅),但我沒有 load symbol 所以無法顯示正確的 function 與行號。

這個方法就分享給有需要的朋友啦!如果有幫到你也可以底下留言讓我知道 ~

--

--