Capturing DbgPrint calls in Windows Vista
A couple of days ago I downloaded and installed the Windows Vista Beta 2 for the first time. One of the first things that caught my eye was a new export in the kernel, called DbgSetDebugPrintCallback. It looked like Microsoft had implemented a standardized way to capture DbgPrint calls. I tried to find some documentation describing the new routine but I could not find anything anywhere, so I decided to do some reversing.
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 one single internal pointer to one 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 turned out to be really simple. Determining the exact type of the third parameter (or rather the first one) turned out to be a bit more work. Since there is quite a bit of code involved I will 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 am not sure if Microsoft intends to ever document DbgSetDebugPrintCallback or not. At this point there seems to be no documentation and I have not been able to find the routine in any include files or lib files. To get a pointer to the DbgSetDebugPrintCallback routine we can simply 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 anyway.