如何监控内核回调
How to monitor Kernel callbacks
我将如何监控内核回调?我特别感兴趣的是监视来自内核回调 table 的回调函数。我试图找出哪个 user32 API 调用触发了哪个回调函数。
我不相信我可以使用调试器看到这些调用,所以可以选择 ETW 跟踪吗?
我使用内核调试器进行了一些快速测试(在 Windows 10 上),因为它非常需要获取哪些用户调用在哪个回调函数中结束。
我使用 notepad.exe 作为目标,因为它有 GUI。
2: kd> !process 0 0 notepad.exe
PROCESS ffffb987185d9080
SessionId: 1 Cid: 20ec Peb: c21923b000 ParentCid: 141c
DirBase: 5510e002 ObjectTable: ffffa80636fcf340 HandleCount: 239.
Image: notepad.exe
_EPROCESS
结构位于 0xffffb987185d9080,其 _PEB
位于 0xc21923b000。第一个用于为 notepad.exe 设置断点,第二个用于查看内核回调 table.
在 nt!KeUserModeCallback
上设置 BP
2: kd> bp /p ffffb987185d9080 nt!KeUserModeCallback; g
我们知道这个函数的 first parameter 是内核回调的索引 table:
NTSTATUS KeUserModeCallback (
IN ULONG ApiNumber,
IN PVOID InputBuffer,
IN ULONG InputLength,
OUT PVOID *OutputBuffer,
IN PULONG OutputLength
);
内核回调 table 可直接从 _PEB
结构访问,使用 KernelCallbackTable
字段:
1: kd> dt _peb c21923b000 KernelC*
wintypes!_PEB
+0x058 KernelCallbackTable : 0x00007ffc`12831070 Void
1: kd> dps 0x00007ffc`12831070
00007ffc`12831070 00007ffc`127c2710 USER32!_fnCOPYDATA
00007ffc`12831078 00007ffc`128299f0 USER32!_fnCOPYGLOBALDATA
00007ffc`12831080 00007ffc`127c0b90 USER32!_fnDWORD
00007ffc`12831088 00007ffc`127c69f0 USER32!_fnNCDESTROY
00007ffc`12831090 00007ffc`127cda60 USER32!_fnDWORDOPTINLPMSG
00007ffc`12831098 00007ffc`1282a220 USER32!_fnINOUTDRAG
00007ffc`128310a0 00007ffc`127c7f20 USER32!_fnGETTEXTLENGTHS
00007ffc`128310a8 00007ffc`12829ec0 USER32!_fnINCNTOUTSTRING
00007ffc`128310b0 00007ffc`12829f80 USER32!_fnINCNTOUTSTRINGNULL
00007ffc`128310b8 00007ffc`127c9690 USER32!_fnINLPCOMPAREITEMSTRUCT
00007ffc`128310c0 00007ffc`127c2b70 USER32!__fnINLPCREATESTRUCT
00007ffc`128310c8 00007ffc`1282a040 USER32!_fnINLPDELETEITEMSTRUCT
00007ffc`128310d0 00007ffc`127cfdf0 USER32!__fnINLPDRAWITEMSTRUCT
00007ffc`128310d8 00007ffc`1282a0a0 USER32!_fnINLPHELPINFOSTRUCT
00007ffc`128310e0 00007ffc`1282a0a0 USER32!_fnINLPHELPINFOSTRUCT
00007ffc`128310e8 00007ffc`1282a1a0 USER32!_fnINLPMDICREATESTRUCT
有趣的是,这个table也有一个象征性的名字,即USER32!apfnDispatch
:
1: kd> ln 0x00007ffc`12831070
(0x00007ffc`12831070) USER32!apfnDispatch
1: kd> ? USER32!apfnDispatch
Evaluate expression: 140720619065456 = 00007ffc`12831070
通过所有这些我们可以在 nt!KeUserModeCallback
:
上设置一个日志记录断点
1: kd> bp /p ffffb987185d9080 nt!KeUserModeCallback ".printf \"RCX: %p --> %y\n\", @rcx, poi(user32!apfnDispatch + (@rcx * 8)); k; g"
这会打印 ApiNumber(在 RCX 中)和与内核回调中的数字关联的函数名称 table,后跟堆栈跟踪。转储示例:
RCX: 0000000000000016 --> USER32!_fnINOUTLPPOINT5 (00007ffc`127c3d30)
# Child-SP RetAddr Call Site
00 ffffb80f`ac76dd08 fffffe32`37a3d2ef nt!KeUserModeCallback
01 ffffb80f`ac76dd10 fffffe32`37a095d4 win32kfull!SfnINOUTLPWINDOWPOS+0x29f
02 ffffb80f`ac76de50 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ac76df10 fffffe32`379e0ed9 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ac76e060 fffffe32`379df20c win32kfull!xxxCalcValidRects+0x32d
05 ffffb80f`ac76e200 fffffe32`379ac9c5 win32kfull!xxxEndDeferWindowPosEx+0x1ac
06 ffffb80f`ac76e2e0 fffffe32`37a25033 win32kfull!xxxProcessDesktopRecalc+0x221
07 ffffb80f`ac76e3b0 fffffe32`37a261e8 win32kfull!xxxProcessEventMessage+0x39b
08 ffffb80f`ac76e6e0 fffffe32`37a06211 win32kfull!xxxScanSysQueue+0xd48
09 ffffb80f`ac76ef20 fffffe32`37a050e2 win32kfull!xxxRealInternalGetMessage+0xef1
0a ffffb80f`ac76f3f0 fffffe32`373e6276 win32kfull!NtUserGetMessage+0x92
0b ffffb80f`ac76f480 fffff805`28c08bb5 win32k!NtUserGetMessage+0x16
0c ffffb80f`ac76f4c0 00007ffc`11b11104 nt!KiSystemServiceCopyEnd+0x25
0d 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
0e 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
0f 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
10 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
11 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
12 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
RCX: 000000000000001b --> USER32!_fnINSTRING (00007ffc`127c1ce0)
# Child-SP RetAddr Call Site
00 ffffb80f`ac76dda8 fffffe32`37997895 nt!KeUserModeCallback
01 ffffb80f`ac76ddb0 fffffe32`37a095d4 win32kfull!SfnINSTRINGNULL+0x2b5
02 ffffb80f`ac76e140 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ac76e200 fffffe32`37a0cc10 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ac76e350 fffffe32`37a24ebd win32kfull!xxxSendMessage+0x2c
05 ffffb80f`ac76e3b0 fffffe32`37a261e8 win32kfull!xxxProcessEventMessage+0x225
06 ffffb80f`ac76e6e0 fffffe32`37a06211 win32kfull!xxxScanSysQueue+0xd48
07 ffffb80f`ac76ef20 fffffe32`37a050e2 win32kfull!xxxRealInternalGetMessage+0xef1
08 ffffb80f`ac76f3f0 fffffe32`373e6276 win32kfull!NtUserGetMessage+0x92
09 ffffb80f`ac76f480 fffff805`28c08bb5 win32k!NtUserGetMessage+0x16
0a ffffb80f`ac76f4c0 00007ffc`11b11104 nt!KiSystemServiceCopyEnd+0x25
0b 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
0c 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
0d 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
0e 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
0f 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
10 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
RCX: 000000000000006a --> USER32!_fnINLPUAHDRAWMENU (00007ffc`127c61b0)
# Child-SP RetAddr Call Site
00 ffffb80f`ad457888 fffffe32`37a4e22d nt!KeUserModeCallback
01 ffffb80f`ad457890 fffffe32`37a095d4 win32kfull!SfnINLPUAHDRAWMENU+0x20d
02 ffffb80f`ad4579c0 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ad457a80 fffffe32`37a0cc10 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ad457bd0 fffffe32`379a852f win32kfull!xxxSendMessage+0x2c
05 ffffb80f`ad457c30 fffffe32`379a81b8 win32kfull!xxxSendUAHMenuMessage+0x3f
06 ffffb80f`ad457c80 fffffe32`379a6a45 win32kfull!xxxPaintMenuBar+0x174
07 ffffb80f`ad457d20 fffffe32`373e7152 win32kfull!NtUserPaintMenuBar+0xe5
08 ffffb80f`ad457d80 fffff805`28c08bb5 win32k!NtUserPaintMenuBar+0x2a
09 ffffb80f`ad457dd0 00007ffc`11b12c64 nt!KiSystemServiceCopyEnd+0x25
0a 000000c2`1947efd8 00007ffc`0f12a919 win32u!NtUserPaintMenuBar+0x14
0b 000000c2`1947efe0 00007ffc`0f1271f4 uxtheme!CThemeWnd::NcPaint+0x239
0c 000000c2`1947f130 00007ffc`0f12b809 uxtheme!OnDwpNcActivate+0x54
0d 000000c2`1947f170 00007ffc`0f12b271 uxtheme!_ThemeDefWindowProc+0x589
0e 000000c2`1947f350 00007ffc`127ac7e3 uxtheme!ThemeDefWindowProcW+0x11
0f 000000c2`1947f390 00007ff6`0803bb57 USER32!DefWindowProcW+0x1a3
10 000000c2`1947f3f0 00007ffc`127ae858 notepad!NPWndProc+0x557
11 000000c2`1947f730 00007ffc`127ae3dc USER32!UserCallWinProcCheckWow+0x2f8
12 000000c2`1947f8c0 00007ffc`127c0bc3 USER32!DispatchClientMessage+0x9c
13 000000c2`1947f920 00007ffc`14070c54 USER32!_fnDWORD+0x33
14 000000c2`1947f980 00007ffc`11b11104 ntdll!KiUserCallbackDispatcherContinue
15 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
16 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
17 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
18 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
19 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
1a 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
RCX: 000000000000006b --> USER32!__fnINLPUAHDRAWMENUITEM (00007ffc`127c4190)
# Child-SP RetAddr Call Site
00 ffffb80f`ad457538 fffffe32`37a4e835 nt!KeUserModeCallback
01 ffffb80f`ad457540 fffffe32`37a095d4 win32kfull!SfnINLPUAHDRAWMENUITEM+0x255
02 ffffb80f`ad457700 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ad4577c0 fffffe32`37a0cc10 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ad457910 fffffe32`379ab397 win32kfull!xxxSendMessage+0x2c
05 ffffb80f`ad457970 fffffe32`379ab118 win32kfull!xxxSendMenuDrawItemMessage+0x17f
06 ffffb80f`ad457ab0 fffffe32`379aaf34 win32kfull!xxxDrawMenuItem+0x130
07 ffffb80f`ad457b80 fffffe32`379a8203 win32kfull!xxxMenuDraw+0x224
08 ffffb80f`ad457c80 fffffe32`379a6a45 win32kfull!xxxPaintMenuBar+0x1bf
09 ffffb80f`ad457d20 fffffe32`373e7152 win32kfull!NtUserPaintMenuBar+0xe5
0a ffffb80f`ad457d80 fffff805`28c08bb5 win32k!NtUserPaintMenuBar+0x2a
0b ffffb80f`ad457dd0 00007ffc`11b12c64 nt!KiSystemServiceCopyEnd+0x25
0c 000000c2`1947efd8 00007ffc`0f12a919 win32u!NtUserPaintMenuBar+0x14
0d 000000c2`1947efe0 00007ffc`0f1271f4 uxtheme!CThemeWnd::NcPaint+0x239
0e 000000c2`1947f130 00007ffc`0f12b809 uxtheme!OnDwpNcActivate+0x54
0f 000000c2`1947f170 00007ffc`0f12b271 uxtheme!_ThemeDefWindowProc+0x589
10 000000c2`1947f350 00007ffc`127ac7e3 uxtheme!ThemeDefWindowProcW+0x11
11 000000c2`1947f390 00007ff6`0803bb57 USER32!DefWindowProcW+0x1a3
12 000000c2`1947f3f0 00007ffc`127ae858 notepad!NPWndProc+0x557
13 000000c2`1947f730 00007ffc`127ae3dc USER32!UserCallWinProcCheckWow+0x2f8
14 000000c2`1947f8c0 00007ffc`127c0bc3 USER32!DispatchClientMessage+0x9c
15 000000c2`1947f920 00007ffc`14070c54 USER32!_fnDWORD+0x33
16 000000c2`1947f980 00007ffc`11b11104 ntdll!KiUserCallbackDispatcherContinue
17 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
18 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
19 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
1a 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
1b 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
1c 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
如果你有一个像 user32!GetMessageW
这样的函数,它会变得有点复杂,回调函数取决于传递给 GetMessageW
的参数。
在这种情况下,您需要返回调用它的框架:
RCX: 000000000000001c --> USER32!__fnINDEVICECHANGE (00007ffc`127cdd70)
# Child-SP RetAddr Call Site
00 ffffb80f`ac76e8a8 fffffe32`37999cab nt!KeUserModeCallback
01 ffffb80f`ac76e8b0 fffffe32`37a095d4 win32kfull!SfnINDEVICECHANGE+0x28b
02 ffffb80f`ac76ec40 fffffe32`37a08634 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ac76ed00 fffffe32`37a06078 win32kfull!xxxReceiveMessage+0x3b4
04 ffffb80f`ac76ef20 fffffe32`37a050e2 win32kfull!xxxRealInternalGetMessage+0xd58
05 ffffb80f`ac76f3f0 fffffe32`373e6276 win32kfull!NtUserGetMessage+0x92
06 ffffb80f`ac76f480 fffff805`28c08bb5 win32k!NtUserGetMessage+0x16
07 ffffb80f`ac76f4c0 00007ffc`11b11104 nt!KiSystemServiceCopyEnd+0x25
08 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
09 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
0a 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
0b 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
0c 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
0d 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
让我们回到调用函数的帧(帧 0xa):
1: kd> .frame /r 0xa
0a 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
rax=ffffb80fac76e8e4 rbx=000002870361237c rcx=000000000000001c
rdx=ffffb80fac76ea00 rsi=00007ff608030000 rdi=0000000000000000
rip=00007ff60803c3ac rsp=000000c21947fa70 rbp=000000c21947fab9
r8=0000000000000068 r9=ffffb80fac76e908 r10=0000000000000000
r11=ffffb80fac76e800 r12=0000000000000000 r13=0000000000000000
r14=0000000000020375 r15=00007ff608030000
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
notepad!wWinMain+0x2b4:
00007ff6`0803c3ac 0f1f440000 nop dword ptr [rax+rax]
这是反汇编:
00007ff6`0803c399 4533c9 xor r9d,r9d
00007ff6`0803c39c 488d4d0f lea rcx,[rbp+0Fh]
00007ff6`0803c3a0 4533c0 xor r8d,r8d
00007ff6`0803c3a3 33d2 xor edx,edx
00007ff6`0803c3a5 48ff15f4c80100 call qword ptr [notepad!_imp_GetMessageW (00007ff6`08058ca0)]
00007ff6`0803c3ac 0f1f440000 nop dword ptr [rax+rax] ; frame pointer here
GetMessageW
的第一个参数是一个指向 MSG 结构的指针,因此在这种情况下您可以看到它来自 RBP+0x0f:
1: kd> db 000000c21947fab9 + f
000000c2`1947fac8 10 03 03 00 00 00 00 00-00 04 00 00 00 00 00 00 ................
000000c2`1947fad8 be ba 00 00 00 00 00 00-c0 2f 67 03 87 02 00 00 ........./g.....
000000c2`1947fae8 96 6e 07 00 ab 02 00 00-80 01 00 00 00 00 00 00 .n..............
000000c2`1947faf8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
000000c2`1947fb08 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
000000c2`1947fb18 b6 59 05 08 f6 7f 00 00-01 00 00 00 00 00 00 00 .Y..............
000000c2`1947fb28 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
000000c2`1947fb38 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
在这种情况下,WM_ 消息是 0x400。
除了内核调试器,有趣的是所有对 nt!KeUserModeCallback
的调用都被对 nt!EtwTraceBeginCallback
和 nt!EtwTraceEndCallback
的调用包围(下面是来自 win32Kfull.sys
的示例fnHkINLPMSG
函数)
.text:00000001C009D429 call cs:__imp_EtwTraceBeginCallback
.text:00000001C009D430 nop dword ptr [rax+rax+00h]
.text:00000001C009D435 lea rax, [rsp+158h+arg_10]
.text:00000001C009D43D mov [rsp+158h+BugCheckParameter4], rax
.text:00000001C009D442 lea r9, [rsp+158h+var_110]
.text:00000001C009D447 mov r8d, 58h ; 'X'
.text:00000001C009D44D lea rdx, [rsp+158h+var_E8]
.text:00000001C009D452 lea ecx, [r8-29h]
.text:00000001C009D456 call cs:__imp_KeUserModeCallback
.text:00000001C009D45D nop dword ptr [rax+rax+00h]
.text:00000001C009D462 mov r12d, eax
.text:00000001C009D465 mov ecx, 2Fh ; '/'
.text:00000001C009D46A call cs:__imp_EtwTraceEndCallback
所以使用 ETW 绝对是可能的。虽然我没有测试过,但我怀疑事件的输出非常“原始”,没有提到任何函数名称,因此在从 ETW 获得输出后可能需要做更多的工作。
我将如何监控内核回调?我特别感兴趣的是监视来自内核回调 table 的回调函数。我试图找出哪个 user32 API 调用触发了哪个回调函数。
我不相信我可以使用调试器看到这些调用,所以可以选择 ETW 跟踪吗?
我使用内核调试器进行了一些快速测试(在 Windows 10 上),因为它非常需要获取哪些用户调用在哪个回调函数中结束。
我使用 notepad.exe 作为目标,因为它有 GUI。
2: kd> !process 0 0 notepad.exe
PROCESS ffffb987185d9080
SessionId: 1 Cid: 20ec Peb: c21923b000 ParentCid: 141c
DirBase: 5510e002 ObjectTable: ffffa80636fcf340 HandleCount: 239.
Image: notepad.exe
_EPROCESS
结构位于 0xffffb987185d9080,其 _PEB
位于 0xc21923b000。第一个用于为 notepad.exe 设置断点,第二个用于查看内核回调 table.
在 nt!KeUserModeCallback
2: kd> bp /p ffffb987185d9080 nt!KeUserModeCallback; g
我们知道这个函数的 first parameter 是内核回调的索引 table:
NTSTATUS KeUserModeCallback (
IN ULONG ApiNumber,
IN PVOID InputBuffer,
IN ULONG InputLength,
OUT PVOID *OutputBuffer,
IN PULONG OutputLength
);
内核回调 table 可直接从 _PEB
结构访问,使用 KernelCallbackTable
字段:
1: kd> dt _peb c21923b000 KernelC*
wintypes!_PEB
+0x058 KernelCallbackTable : 0x00007ffc`12831070 Void
1: kd> dps 0x00007ffc`12831070
00007ffc`12831070 00007ffc`127c2710 USER32!_fnCOPYDATA
00007ffc`12831078 00007ffc`128299f0 USER32!_fnCOPYGLOBALDATA
00007ffc`12831080 00007ffc`127c0b90 USER32!_fnDWORD
00007ffc`12831088 00007ffc`127c69f0 USER32!_fnNCDESTROY
00007ffc`12831090 00007ffc`127cda60 USER32!_fnDWORDOPTINLPMSG
00007ffc`12831098 00007ffc`1282a220 USER32!_fnINOUTDRAG
00007ffc`128310a0 00007ffc`127c7f20 USER32!_fnGETTEXTLENGTHS
00007ffc`128310a8 00007ffc`12829ec0 USER32!_fnINCNTOUTSTRING
00007ffc`128310b0 00007ffc`12829f80 USER32!_fnINCNTOUTSTRINGNULL
00007ffc`128310b8 00007ffc`127c9690 USER32!_fnINLPCOMPAREITEMSTRUCT
00007ffc`128310c0 00007ffc`127c2b70 USER32!__fnINLPCREATESTRUCT
00007ffc`128310c8 00007ffc`1282a040 USER32!_fnINLPDELETEITEMSTRUCT
00007ffc`128310d0 00007ffc`127cfdf0 USER32!__fnINLPDRAWITEMSTRUCT
00007ffc`128310d8 00007ffc`1282a0a0 USER32!_fnINLPHELPINFOSTRUCT
00007ffc`128310e0 00007ffc`1282a0a0 USER32!_fnINLPHELPINFOSTRUCT
00007ffc`128310e8 00007ffc`1282a1a0 USER32!_fnINLPMDICREATESTRUCT
有趣的是,这个table也有一个象征性的名字,即USER32!apfnDispatch
:
1: kd> ln 0x00007ffc`12831070
(0x00007ffc`12831070) USER32!apfnDispatch
1: kd> ? USER32!apfnDispatch
Evaluate expression: 140720619065456 = 00007ffc`12831070
通过所有这些我们可以在 nt!KeUserModeCallback
:
1: kd> bp /p ffffb987185d9080 nt!KeUserModeCallback ".printf \"RCX: %p --> %y\n\", @rcx, poi(user32!apfnDispatch + (@rcx * 8)); k; g"
这会打印 ApiNumber(在 RCX 中)和与内核回调中的数字关联的函数名称 table,后跟堆栈跟踪。转储示例:
RCX: 0000000000000016 --> USER32!_fnINOUTLPPOINT5 (00007ffc`127c3d30)
# Child-SP RetAddr Call Site
00 ffffb80f`ac76dd08 fffffe32`37a3d2ef nt!KeUserModeCallback
01 ffffb80f`ac76dd10 fffffe32`37a095d4 win32kfull!SfnINOUTLPWINDOWPOS+0x29f
02 ffffb80f`ac76de50 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ac76df10 fffffe32`379e0ed9 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ac76e060 fffffe32`379df20c win32kfull!xxxCalcValidRects+0x32d
05 ffffb80f`ac76e200 fffffe32`379ac9c5 win32kfull!xxxEndDeferWindowPosEx+0x1ac
06 ffffb80f`ac76e2e0 fffffe32`37a25033 win32kfull!xxxProcessDesktopRecalc+0x221
07 ffffb80f`ac76e3b0 fffffe32`37a261e8 win32kfull!xxxProcessEventMessage+0x39b
08 ffffb80f`ac76e6e0 fffffe32`37a06211 win32kfull!xxxScanSysQueue+0xd48
09 ffffb80f`ac76ef20 fffffe32`37a050e2 win32kfull!xxxRealInternalGetMessage+0xef1
0a ffffb80f`ac76f3f0 fffffe32`373e6276 win32kfull!NtUserGetMessage+0x92
0b ffffb80f`ac76f480 fffff805`28c08bb5 win32k!NtUserGetMessage+0x16
0c ffffb80f`ac76f4c0 00007ffc`11b11104 nt!KiSystemServiceCopyEnd+0x25
0d 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
0e 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
0f 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
10 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
11 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
12 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
RCX: 000000000000001b --> USER32!_fnINSTRING (00007ffc`127c1ce0)
# Child-SP RetAddr Call Site
00 ffffb80f`ac76dda8 fffffe32`37997895 nt!KeUserModeCallback
01 ffffb80f`ac76ddb0 fffffe32`37a095d4 win32kfull!SfnINSTRINGNULL+0x2b5
02 ffffb80f`ac76e140 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ac76e200 fffffe32`37a0cc10 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ac76e350 fffffe32`37a24ebd win32kfull!xxxSendMessage+0x2c
05 ffffb80f`ac76e3b0 fffffe32`37a261e8 win32kfull!xxxProcessEventMessage+0x225
06 ffffb80f`ac76e6e0 fffffe32`37a06211 win32kfull!xxxScanSysQueue+0xd48
07 ffffb80f`ac76ef20 fffffe32`37a050e2 win32kfull!xxxRealInternalGetMessage+0xef1
08 ffffb80f`ac76f3f0 fffffe32`373e6276 win32kfull!NtUserGetMessage+0x92
09 ffffb80f`ac76f480 fffff805`28c08bb5 win32k!NtUserGetMessage+0x16
0a ffffb80f`ac76f4c0 00007ffc`11b11104 nt!KiSystemServiceCopyEnd+0x25
0b 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
0c 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
0d 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
0e 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
0f 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
10 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
RCX: 000000000000006a --> USER32!_fnINLPUAHDRAWMENU (00007ffc`127c61b0)
# Child-SP RetAddr Call Site
00 ffffb80f`ad457888 fffffe32`37a4e22d nt!KeUserModeCallback
01 ffffb80f`ad457890 fffffe32`37a095d4 win32kfull!SfnINLPUAHDRAWMENU+0x20d
02 ffffb80f`ad4579c0 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ad457a80 fffffe32`37a0cc10 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ad457bd0 fffffe32`379a852f win32kfull!xxxSendMessage+0x2c
05 ffffb80f`ad457c30 fffffe32`379a81b8 win32kfull!xxxSendUAHMenuMessage+0x3f
06 ffffb80f`ad457c80 fffffe32`379a6a45 win32kfull!xxxPaintMenuBar+0x174
07 ffffb80f`ad457d20 fffffe32`373e7152 win32kfull!NtUserPaintMenuBar+0xe5
08 ffffb80f`ad457d80 fffff805`28c08bb5 win32k!NtUserPaintMenuBar+0x2a
09 ffffb80f`ad457dd0 00007ffc`11b12c64 nt!KiSystemServiceCopyEnd+0x25
0a 000000c2`1947efd8 00007ffc`0f12a919 win32u!NtUserPaintMenuBar+0x14
0b 000000c2`1947efe0 00007ffc`0f1271f4 uxtheme!CThemeWnd::NcPaint+0x239
0c 000000c2`1947f130 00007ffc`0f12b809 uxtheme!OnDwpNcActivate+0x54
0d 000000c2`1947f170 00007ffc`0f12b271 uxtheme!_ThemeDefWindowProc+0x589
0e 000000c2`1947f350 00007ffc`127ac7e3 uxtheme!ThemeDefWindowProcW+0x11
0f 000000c2`1947f390 00007ff6`0803bb57 USER32!DefWindowProcW+0x1a3
10 000000c2`1947f3f0 00007ffc`127ae858 notepad!NPWndProc+0x557
11 000000c2`1947f730 00007ffc`127ae3dc USER32!UserCallWinProcCheckWow+0x2f8
12 000000c2`1947f8c0 00007ffc`127c0bc3 USER32!DispatchClientMessage+0x9c
13 000000c2`1947f920 00007ffc`14070c54 USER32!_fnDWORD+0x33
14 000000c2`1947f980 00007ffc`11b11104 ntdll!KiUserCallbackDispatcherContinue
15 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
16 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
17 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
18 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
19 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
1a 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
RCX: 000000000000006b --> USER32!__fnINLPUAHDRAWMENUITEM (00007ffc`127c4190)
# Child-SP RetAddr Call Site
00 ffffb80f`ad457538 fffffe32`37a4e835 nt!KeUserModeCallback
01 ffffb80f`ad457540 fffffe32`37a095d4 win32kfull!SfnINLPUAHDRAWMENUITEM+0x255
02 ffffb80f`ad457700 fffffe32`37a091c2 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ad4577c0 fffffe32`37a0cc10 win32kfull!xxxSendTransformableMessageTimeout+0x282
04 ffffb80f`ad457910 fffffe32`379ab397 win32kfull!xxxSendMessage+0x2c
05 ffffb80f`ad457970 fffffe32`379ab118 win32kfull!xxxSendMenuDrawItemMessage+0x17f
06 ffffb80f`ad457ab0 fffffe32`379aaf34 win32kfull!xxxDrawMenuItem+0x130
07 ffffb80f`ad457b80 fffffe32`379a8203 win32kfull!xxxMenuDraw+0x224
08 ffffb80f`ad457c80 fffffe32`379a6a45 win32kfull!xxxPaintMenuBar+0x1bf
09 ffffb80f`ad457d20 fffffe32`373e7152 win32kfull!NtUserPaintMenuBar+0xe5
0a ffffb80f`ad457d80 fffff805`28c08bb5 win32k!NtUserPaintMenuBar+0x2a
0b ffffb80f`ad457dd0 00007ffc`11b12c64 nt!KiSystemServiceCopyEnd+0x25
0c 000000c2`1947efd8 00007ffc`0f12a919 win32u!NtUserPaintMenuBar+0x14
0d 000000c2`1947efe0 00007ffc`0f1271f4 uxtheme!CThemeWnd::NcPaint+0x239
0e 000000c2`1947f130 00007ffc`0f12b809 uxtheme!OnDwpNcActivate+0x54
0f 000000c2`1947f170 00007ffc`0f12b271 uxtheme!_ThemeDefWindowProc+0x589
10 000000c2`1947f350 00007ffc`127ac7e3 uxtheme!ThemeDefWindowProcW+0x11
11 000000c2`1947f390 00007ff6`0803bb57 USER32!DefWindowProcW+0x1a3
12 000000c2`1947f3f0 00007ffc`127ae858 notepad!NPWndProc+0x557
13 000000c2`1947f730 00007ffc`127ae3dc USER32!UserCallWinProcCheckWow+0x2f8
14 000000c2`1947f8c0 00007ffc`127c0bc3 USER32!DispatchClientMessage+0x9c
15 000000c2`1947f920 00007ffc`14070c54 USER32!_fnDWORD+0x33
16 000000c2`1947f980 00007ffc`11b11104 ntdll!KiUserCallbackDispatcherContinue
17 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
18 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
19 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
1a 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
1b 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
1c 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
如果你有一个像 user32!GetMessageW
这样的函数,它会变得有点复杂,回调函数取决于传递给 GetMessageW
的参数。
在这种情况下,您需要返回调用它的框架:
RCX: 000000000000001c --> USER32!__fnINDEVICECHANGE (00007ffc`127cdd70)
# Child-SP RetAddr Call Site
00 ffffb80f`ac76e8a8 fffffe32`37999cab nt!KeUserModeCallback
01 ffffb80f`ac76e8b0 fffffe32`37a095d4 win32kfull!SfnINDEVICECHANGE+0x28b
02 ffffb80f`ac76ec40 fffffe32`37a08634 win32kfull!xxxSendMessageToClient+0x114
03 ffffb80f`ac76ed00 fffffe32`37a06078 win32kfull!xxxReceiveMessage+0x3b4
04 ffffb80f`ac76ef20 fffffe32`37a050e2 win32kfull!xxxRealInternalGetMessage+0xd58
05 ffffb80f`ac76f3f0 fffffe32`373e6276 win32kfull!NtUserGetMessage+0x92
06 ffffb80f`ac76f480 fffff805`28c08bb5 win32k!NtUserGetMessage+0x16
07 ffffb80f`ac76f4c0 00007ffc`11b11104 nt!KiSystemServiceCopyEnd+0x25
08 000000c2`1947fa08 00007ffc`127c1b3e win32u!NtUserGetMessage+0x14
09 000000c2`1947fa10 00007ff6`0803c3ac USER32!GetMessageW+0x2e
0a 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
0b 000000c2`1947fb20 00007ffc`131b7034 notepad!__scrt_common_main_seh+0x106
0c 000000c2`1947fb60 00007ffc`14022651 KERNEL32!BaseThreadInitThunk+0x14
0d 000000c2`1947fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x21
让我们回到调用函数的帧(帧 0xa):
1: kd> .frame /r 0xa
0a 000000c2`1947fa70 00007ff6`080559b6 notepad!wWinMain+0x2b4
rax=ffffb80fac76e8e4 rbx=000002870361237c rcx=000000000000001c
rdx=ffffb80fac76ea00 rsi=00007ff608030000 rdi=0000000000000000
rip=00007ff60803c3ac rsp=000000c21947fa70 rbp=000000c21947fab9
r8=0000000000000068 r9=ffffb80fac76e908 r10=0000000000000000
r11=ffffb80fac76e800 r12=0000000000000000 r13=0000000000000000
r14=0000000000020375 r15=00007ff608030000
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
notepad!wWinMain+0x2b4:
00007ff6`0803c3ac 0f1f440000 nop dword ptr [rax+rax]
这是反汇编:
00007ff6`0803c399 4533c9 xor r9d,r9d
00007ff6`0803c39c 488d4d0f lea rcx,[rbp+0Fh]
00007ff6`0803c3a0 4533c0 xor r8d,r8d
00007ff6`0803c3a3 33d2 xor edx,edx
00007ff6`0803c3a5 48ff15f4c80100 call qword ptr [notepad!_imp_GetMessageW (00007ff6`08058ca0)]
00007ff6`0803c3ac 0f1f440000 nop dword ptr [rax+rax] ; frame pointer here
GetMessageW
的第一个参数是一个指向 MSG 结构的指针,因此在这种情况下您可以看到它来自 RBP+0x0f:
1: kd> db 000000c21947fab9 + f
000000c2`1947fac8 10 03 03 00 00 00 00 00-00 04 00 00 00 00 00 00 ................
000000c2`1947fad8 be ba 00 00 00 00 00 00-c0 2f 67 03 87 02 00 00 ........./g.....
000000c2`1947fae8 96 6e 07 00 ab 02 00 00-80 01 00 00 00 00 00 00 .n..............
000000c2`1947faf8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
000000c2`1947fb08 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
000000c2`1947fb18 b6 59 05 08 f6 7f 00 00-01 00 00 00 00 00 00 00 .Y..............
000000c2`1947fb28 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
000000c2`1947fb38 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
在这种情况下,WM_ 消息是 0x400。
除了内核调试器,有趣的是所有对 nt!KeUserModeCallback
的调用都被对 nt!EtwTraceBeginCallback
和 nt!EtwTraceEndCallback
的调用包围(下面是来自 win32Kfull.sys
的示例fnHkINLPMSG
函数)
.text:00000001C009D429 call cs:__imp_EtwTraceBeginCallback
.text:00000001C009D430 nop dword ptr [rax+rax+00h]
.text:00000001C009D435 lea rax, [rsp+158h+arg_10]
.text:00000001C009D43D mov [rsp+158h+BugCheckParameter4], rax
.text:00000001C009D442 lea r9, [rsp+158h+var_110]
.text:00000001C009D447 mov r8d, 58h ; 'X'
.text:00000001C009D44D lea rdx, [rsp+158h+var_E8]
.text:00000001C009D452 lea ecx, [r8-29h]
.text:00000001C009D456 call cs:__imp_KeUserModeCallback
.text:00000001C009D45D nop dword ptr [rax+rax+00h]
.text:00000001C009D462 mov r12d, eax
.text:00000001C009D465 mov ecx, 2Fh ; '/'
.text:00000001C009D46A call cs:__imp_EtwTraceEndCallback
所以使用 ETW 绝对是可能的。虽然我没有测试过,但我怀疑事件的输出非常“原始”,没有提到任何函数名称,因此在从 ETW 获得输出后可能需要做更多的工作。