Debugger commands (dps, dpp) that make my life easier (part 5)

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:

  • Dumping a vtable
  • Dumping a jump table (which is essentially the same as a vtable, just constructed differently
  • Dumping a call stack

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
[...]