DbgSetDebugPrintCallback - Capturing DbgPrint calls in Windows

By Arne Vidstrom

May 15, 2019

Windows Vista introduced a new export in the kernel, called DbgSetDebugPrintCallback. At the time, I realized that Microsoft had implemented a standardized way to capture DbgPrint calls. I tried to find some documentation describing the new routine, but I couldn't find anything anywhere, so I decided to do some reversing. The export is still undocumented and still present in Windows 10.

Reversing DbgSetDebugPrintCallback

First of all, I reversed the DbgSetDebugPrintCallback routine itself. It turned out that it takes two parameters. The first parameter is a pointer to the callback function we wish to register; the second one is a BOOLEAN that tells the routine whether to register or unregister the callback function.

The kernel keeps a single internal pointer to a single callback function at a time. When we register a new callback function, the internal pointer must be NULL. When we unregister a callback function, the kernel compares the pointer we pass with the internal pointer. For the unregistering to succeed the two pointers must be equal. The internal pointer is then reset to NULL.

To find out which parameters the callback function takes, I had to do some reversing of the kernel code that calls it. Determining the number of parameters and the exact meaning of two of them was pretty trivial. Determining the exact type of the third parameter (or rather the first one) turned out to be more work. Since there is quite a bit of code involved, I'll just continue with the full results of my work:

NTSTATUS DbgSetDebugPrintCallback(IN PDEBUGPRINT_CALLBACK_FUNCTION Function, IN BOOLEAN Mode);

Function - Pointer to the DebugPrintCallback routine to register or unregister.

Mode - TRUE to register the callback function; FALSE to unregister it.

void DebugPrintCallback(IN PANSI_STRING String, IN ULONG ComponentId, IN ULONG Level);

String - A pointer to the debugging message string.

ComponentID - See the documentation for DbgPrintEx.

Level - See the documentation for DbgPrintEx.

Using DbgSetDebugPrintCallback in practice

First of all, DbgSetDebugPrintCallback is a kernel routine, so it has to be called from kernel mode.

I'm not sure if Microsoft ever intends to document DbgSetDebugPrintCallback or not. At this point, there seems to be no documentation, and I haven't been able to find the routine in any include files or lib files. To get a pointer to the DbgSetDebugPrintCallback routine, we can call MmGetSystemRoutineAddress and ask for it.

One last note - if you plan to make it possible to unload your driver, you must not forget to unregister the callback function before the driver unloads. Obvious, but easy to forget.