How would I go about monitoring Kernel callbacks? I am specifically interested in monitoring the callback functions from the Kernel callback table. I am trying to figure out which user32 API call triggers which callback function.
I do not believe I can see these calls using a debugger, so would an option be ETW tracing?
I did some quick tests (on Windows 10) using the kernel debugger since it's pretty much needed to get which user calls ends up in which callback function.
I'm using notepad.exe as a target since it has a GUI.
the
_EPROCESS
structure is at 0xffffb987185d9080 and its_PEB
is at 0xc21923b000. The first one will be useful to put a breakpoint just for notepad.exe and the second one to have a view on the kernel call back table.Setting a BP on
nt!KeUserModeCallback
We know that the first parameter for this function is an index into the kernel callback table:
The kernel callback table is accesssible directly from the
_PEB
structure, using theKernelCallbackTable
field:It's interesting to see that this table also have a symbolic name, namely
USER32!apfnDispatch
:With all this we can set a logging breakpoint on
nt!KeUserModeCallback
:This prints the ApiNumber (in RCX) and the name of the function associated to the number from the kernel callback table, followed by a stack trace. Dump example:
It get a bit more complicated if you have a function like
user32!GetMessageW
where the callback function depends on the argument passed toGetMessageW
.In this case you need to get back to the frame where it is called:
Le'ts get back the the frame where the function is called (frame 0xa):
Here's the disassembly:
The 1st argument to
GetMessageW
is a pointer to a MSG structure so you can see it comes from RBP+0x0f in this case:In this case the WM_ message is 0x400.
Beside the kernel debugger, it's interesting to see that all calls to
nt!KeUserModeCallback
are surrounded by calls tont!EtwTraceBeginCallback
andnt!EtwTraceEndCallback
(below an example fromwin32Kfull.sys
in thefnHkINLPMSG
function)So using ETW is definitely a possibility. Although I haven't tested it, I suspect the output from the event is quite "raw" without any mention of function names, so it probably requires more work after getting the output from ETW.