Today's debugger command is "dps" (display pointers and symbols). You might be familiar with the "dds" command. While dds will always dump a DWORD, dps will dump pointers, where the pointer size is determined by the target. (I used to use dds because I only debugged 32 bit machines, but after debugging a 64 machine for an hour, I forced myself to always dps.)
dps can be used the following tasks:
Have you ever had to debug a function which took an abstract base class as a parameter and wanted to know which derived object you were dealing with without stepping into one of its functions? I know I have. You can use dps to dump the table. Let's take the following sample program and set a breakpoint on DoIt() to see the command in action.
#include <windows.h> class IBase { public: virtual ULONG FuncOne(VOID) =0; virtual ULONG FuncTwo(VOID) =0; }; class DerivedOne : public IBase { public: virtual ULONG FuncOne(VOID) { return 1; }; virtual ULONG FuncTwo(VOID) { return 2; }; }; class DerivedTwo : public IBase { public: virtual ULONG FuncOne(VOID) { return 101; }; virtual ULONG FuncTwo(VOID) { return 102; }; }; VOID DoIt(IBase* Base) { Base->FuncOne(); } int _cdecl main(int argc, char *argv[]) { DerivedOne d1; DerivedTwo d2; DoIt(&d1); DoIt(&d2); return 0; }
This is the invocation of DoIt(&d1).
0:000> g DoIt foo!DoIt: 01001180 8bff mov edi,edi 0:000> dt Base Local var @ 0x6ff70 Type IBase* 0x0006ff74 +0x000 __VFN_table : 0x010010bc 0:000> dps 0x010010bc l2 010010bc 01001210 foo!DerivedOne::FuncOne [d:\work\foo\main.cpp @ 11] 010010c0 01001230 foo!DerivedOne::FuncTwo [d:\work\foo\main.cpp @ 12]
And we see that the vtable contains function pointers from the class DerivedOne. This is the invocation of DoIt(&d2):
0:000> g DoIt foo!DoIt: 01001180 8bff mov edi,edi 0:000> dt Base Local var @ 0x6ff70 Type IBase* 0x0006ff78 +0x000 __VFN_table : 0x010010c4 0:000> dps 0x010010c4 l2 010010c4 01001280 foo!DerivedTwo::FuncOne [d:\work\foo\main.cpp @ 17] 010010c8 010012a0 foo!DerivedTwo::FuncTwo [d:\work\foo\main.cpp @ 18]
NOTE: While using this technique, you have to be aware that the compiler can fold functions from different classes into one function in the image and have the different vtables point to the shared function. This can lead to false positives since you can see ne class's symbols intermixed with another class's in the same vtable.
Now let's look at a jump table, which is exactly like a vtable except it is typically setup programattically at runtime or initialized at compile time explicitly by the provider of the table. KMDF drivers call into the KMDF runtime through a jump table, a variable named WdfFunctions [1]. Let's also look at a particular call to a function (WdfWmiInstanceGetDevice()) and see the function being invoked [2].
[1] 0: kd> dps wmiunittest!WdfFunctions f83840a8 f1da0440 Wdf01000!imp_WdfChildListCreate f83840ac f1da07d0 Wdf01000!imp_WdfChildListGetDevice f83840b0 f1da1e40 Wdf01000!imp_WdfChildListRetrievePdo [...] [2] 0: kd> u wmiunittest!WdfWmiInstanceGetDevice WmiUnitTest!WdfWmiInstanceGetDevice: f8380bd0 8bff mov edi,edi f8380bd2 55 push ebp f8380bd3 8bec mov ebp,esp f8380bd5 8b4508 mov eax,[ebp+0x8] f8380bd8 50 push eax f8380bd9 8b0db84638f8 mov ecx,[WmiUnitTest!WdfDriverGlobals (f83846b8)] f8380bdf 51 push ecx f8380be0 ff15884638f8 call dword ptr [WmiUnitTest!WdfFunctions+0x5e0 (f8384688)] 0: kd> dps WmiUnitTest!WdfFunctions+0x5e0 l1 f8384688 f1dec0c0 Wdf01000!imp_WdfWmiInstanceGetDevice
Finally, you can use dps to try to reconstruct a call stack. Let's say that you had a buffer overflow bug and you overwrote the return value on the stack and bugchecked on the return, obliterating any evidence of the faulty function. You can sometimes use dps to recover the remaining stack by dumping esp (on x86). Note that dps @esp is the same as kb if the stack is not corrupted ;).
In this callstack [1], I broke into kd by hitting PrintScreen/SysRq on the target machine. Then I dumped ChildEBP [2] and esp [3] to see what callstack looked like when using dps.
[1] 0: kd> k ChildEBP RetAddr 8055635c f85b90ce nt!RtlpBreakWithStatusInstruction 8055639c 804db90f i8042prt!I8042KeyboardInterruptService+0x30d 8055639c f85a9062 nt!KiInterruptDispatch+0x45 80556450 804dcbef intelppm!AcpiC1Idle+0x12 80556454 00000000 nt!KiIdleLoop+0x10 [2] 0: kd> dps 8055635c 8055635c 00000202 80556360 f85b90ce i8042prt!I8042KeyboardInterruptService+0x30d 80556364 00000002 80556368 820c5490 8055636c 8212fa58 80556370 80556450 nt!_KiDoubleFaultStack+0x2d50 80556374 8212fc5c 80556378 151870e0 8055637c 371870e0 80556380 00000000 80556384 80556368 nt!_KiDoubleFaultStack+0x2c68 80556388 805563b0 nt!_KiDoubleFaultStack+0x2cb0 8055638c ffffffff 80556390 f85ba274 i8042prt!except_handler3 80556394 f85ba7a8 i8042prt!`string'+0x154 80556398 00000000 8055639c 805563c0 nt!_KiDoubleFaultStack+0x2cc0 805563a0 804db90f nt!KiInterruptDispatch+0x45 805563a4 820c5490 805563a8 8212f998 805563ac 00010009 805563b0 00000193 805563b4 00000000 805563b8 82187002 805563bc 00000193 805563c0 80556450 nt!_KiDoubleFaultStack+0x2d50 805563c4 f85a9062 intelppm!AcpiC1Idle+0x12 [...] [3] 0: kd> dps @esp 80556360 f85b90ce i8042prt!I8042KeyboardInterruptService+0x30d 80556364 00000002 80556368 820c5490 8055636c 8212fa58 80556370 80556450 nt!_KiDoubleFaultStack+0x2d50 80556374 8212fc5c 80556378 151870e0 8055637c 371870e0 80556380 00000000 80556384 80556368 nt!_KiDoubleFaultStack+0x2c68 80556388 805563b0 nt!_KiDoubleFaultStack+0x2cb0 8055638c ffffffff 80556390 f85ba274 i8042prt!except_handler3 80556394 f85ba7a8 i8042prt!`string'+0x154 80556398 00000000 8055639c 805563c0 nt!_KiDoubleFaultStack+0x2cc0 805563a0 804db90f nt!KiInterruptDispatch+0x45 805563a4 820c5490 805563a8 8212f998 805563ac 00010009 805563b0 00000193 805563b4 00000000 805563b8 82187002 805563bc 00000193 805563c0 80556450 nt!_KiDoubleFaultStack+0x2d50 805563c4 f85a9062 intelppm!AcpiC1Idle+0x12 [...]