Debugger commands (!bpid) that make my life easier (part 6)


Today’s kernel debugger command is “!bpid”, break on process id. This
command will break into an application in the application context from within
the kernel debugger. Why would you want to do this? Well, you are not always
in the right user mode context when you break into the kernel debugger, it is
essentially random. You can easily set driver breakpoints, but setting up
an application breakpoint that responds to your driver is difficult.

For this topic, I will be using wdffeatured.sys, the KMDF equivalent of the
featured toaster.sys example in the DDK.

First, let’s take the simple example where everything is setup correctly. I
set a breakpoint on wdffeatured!ToasterEvtIoRead, ran toast.exe and sent a read
to the driver [1]. Note that toast.exe symbols are not loaded and we can reload
them because we are in the context of the app [2]. Once loaded, I can set a breakpoint on
the instruction after the ReadFile() call (ch = _getche();) using the source
window (which is why you don’t see the bp command in the log below), hit ‘g’ and see
my breakpoint hit [3].

[1] 1: kd> k
ChildEBP RetAddr
f233baac f1dc4d34 wdffeatured!ToasterEvtIoRead
f233bac8 f1dc9fe1 Wdf01000!FxFileObjectFileCreate::Invoke+0x34
[...]
f233bd38 804dd99f nt!NtReadFile+0x55d
f233bd38 7c90eb94 nt!KiFastCallEntry+0xfc
0006fea4 7c90e288 ntdll!KiFastSystemCallRet
0006fea8 7c801875 ntdll!NtReadFile+0xc
0006ff10 00401a20 kernel32!ReadFile+0x16c
WARNING: Stack unwind information not available. Following frames may be wrong.
0006ff7c 004020bf toast+0x1a20
0006ffc0 7c816d4f toast+0x20bf
0006fff0 00000000 kernel32!BaseProcessStart+0x23

[2] 1: kd> bu toast!main
Couldn't resolve error at 'toast!main'
1: kd> .reload
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
[...]
Loading User Symbols
[...]
Loading unloaded module list
[...]

1: kd> x toast!*
004011dc toast!GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR = struct _GUID {4d36e978-e325-11ce-bfc1-08002be10318}
0040400c toast!__security_cookie_complement = 0xffffb06e

[3] 1: kd> g

ToasterEvtIoRead: Request: 0x7E668C68, Queue: 0x7E685758
Breakpoint 1 hit
toast!main+0x408:
001b:00401a58 ff15e4104000 call dword ptr [toast!_imp___getche (004010e4)]

But let's say that I can't set such a convenient breakpoint or a I cannot
get to the target machine to execute the right command to get in the right
context in my driver (and for illustrative purposes, I restarted toast.exe so
that the debugger didn't have any symbols set from the previous excercise).
This is where !bpid comes in. First you must find the
process ID of your driver using the !process command [1]. The PID will be the
CID field. Next, you just call !bpid [PID] and you will eventually see a
breakpoint in the context of your application. From there, you need to reload
symbols [3], set any bp that you want, and go from there.


[1] 0: kd> !process 0 1 toast.exe
PROCESS 82235020 SessionId: 0 Cid: 0be8 Peb: 7ffdf000 ParentCid: 08bc
DirBase: 092d8000 ObjectTable: e209c258 HandleCount: 41.
Image: toast.exe
VadRoot 818bc160 Vads 36 Clone 0 Private 81. Modified 0. Locked 0.
DeviceMap e1c38800
Token e1f6c5b8
ElapsedTime 00:00:10.984
UserTime 00:00:00.031
KernelTime 00:00:00.078
[...]

[2] 0: kd> !bpid 0be8
Finding winlogon.exe (0)...
Waiting for winlogon.exe to break. This can take a couple of minutes...
Break instruction exception - code 80000003 (first chance)
Stepping to g_BreakinProcessId check...
Break into process be8 set. The next break should be in the desired process.
Break instruction exception - code 80000003 (first chance)
ntdll!DbgBreakPoint:
001b:7c901230 cc int 3

[3] 1: kd> k
ChildEBP RetAddr
003affb4 7c80b50b ntdll!DbgBreakPoint
003affec 00000000 kernel32!BaseThreadStart+0x37
1: kd> .reload
WARNING: Line information loading disabled
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
WARNING: Line information loading disabled
Loading Kernel Symbols
[...]
Loading User Symbols
[...]
Loading unloaded module list
[...]
1: kd> x toast!*
004011dc toast!GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR = struct _GUID {4d36e978-e325-11ce-bfc1-08002be10318}
0040400c toast!__security_cookie_complement = 0xffffb06e
[...]

!bpid also has a flag, "-a," which lets you start and attach a user mode
debugger on the target process. I have not used this flag much, but it can
be very useful in some scenarios.

Comments (2)

  1. Pavel Lebedinsky says:

    Another way to switch to the right process context so you can set user mode breakpoints is .process /i. Like !bpid, this will also require continuing the target before the target process will break in.

    The reason plain .process /p does not work here is because kd breakpoints are managed by the target kernel, and .process /p doesn’t change the target state – it only changes debugger’s local concept of the current process.