ba
is a very powerful tool for the “who touched my data?” sort of question. Start by reading through this: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/ba--break-on-access-
Once you know about ba
, you’ll discover that the tricky bit is knowing what parameters to pass to it: You need to know exactly which address is modified, and you need to know what size the access is.
If you dump out the contents of a KEVENT, you’ll notice that there’s a bunch of stuff, but (you might have to take my word for it) the most interesting field here is the SignalState member:
typedef struct _DISPATCHER_HEADER {
. . . a bunch of stuff . . .
LONG SignalState;
LIST_ENTRY WaitListHead;
} DISPATCHER_HEADER;
That’s what we want, although there’s so much stuff in there that it’s almost impossible to manually find the offset of SignalState. Fortunately, we don’t have to: just ask windbg to do it for us:
kd> dt -v KEVENT .
+0x000 Header :
. . . a bunch of stuff . . .
+0x004 SignalState : Int4B
+0x008 WaitListHead : struct _LIST_ENTRY, 2 elements, 0x10 bytes
Now we know that SignalState is at offset 0x000+0x004 from the start of the structure, and we know it’s a 4Byte integer. That gives us the address and size of the access. So if your KEVENT is at address 0xffffd688899592e0, just use the breakpoint ba w4 (0xffffd688899592e0+4)
. The w4
is because it’s a 4-byte integer, and the +4
is because SignalState is 4 bytes into the KEVENT structure.
If everything goes right, you should get a breakpoint immediately after this instruction in nt!KeSetEvent at the callstack where someone signals your event:
kd> ub @rip L1
nt!KeSetEvent+0x72:
fffff806`3fad3e12 c7430401000000 mov dword ptr [rbx+4],1
It will also break in any other time someone changes the event state, e.g., KeClearEvent or even KeWaitForSingleObject if it’s an auto-reset event. If you don’t like that, you can add an extra bit of logic to skip the breakpoint if the event is being cleared. If rbx+4
was just set to 0, then the event is cleared, so we can modify the breakpoint thusly:
kd> ba w4 (0xffffd688899592e0+4) ".if (dwo(@rbx+4) == 0) { gc }"
which, after breaking in, checks if the dword (aka 4 byte integer) at rbx+4 is equal to zero. If it is, resume execution.