Hi Tim,
Thanks for confirming I wasn’t going crazy - I can be a bit more at peace regarding its ambiguity. I’ll take note to call it for I/O buffer transfers…
I thought I’d try stepping through to see if I could wrestle out the complete rules for how it works from the disassembly - I found some interesting observations, especially regarding the Request
pointer - it appears to have all of its bits logically negated until the very moment it gets used!
My WdfRequestSetInformation
call happens from this kind of context:
VOID
kmdfHelloWorldEvtIoDeviceControl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
/*
<switch case statement here based on the `Function` field of the IoControlCode>
*/
WdfRequestSetInformation(Request, 4); /* <- Put breakpoint on here */
WdfRequestComplete(Request, STATUS_SUCCESS);
return;
}
}
If I stop in this function (Right on the WdfRequestSetInformation
call for instance), Request
is reported as:
kd> ?? Request
struct WDFREQUEST__ * 0x00004370`00a02f98
+0x000 unused : ??
Notice the address.
But then I step into the assembly of imp_WdfRequestSetInformation
:
Wdf01000!imp_WdfRequestSetInformation:
fffff805`30c23040 4056 push rsi
fffff805`30c23042 4883ec30 sub rsp,30h
fffff805`30c23046 498bf0 mov rsi,r8 ; r8 = "Information", copy r8 to rsi
fffff805`30c23049 4885d2 test rdx,rdx ; rdx = "Request", check not NULL
fffff805`30c2304c 0f8402aa0100 je Wdf01000!imp_WdfRequestSetInformation+0x1aa14 (fffff805`30c3da54) [br=0]
Straightforward enough, setup stack frame, r8
and rsi
have 4
(number of bytes), and rdx
has the Request
“pointer” (although it isn’t really!)
fffff805`30c23052 48895c2440 mov qword ptr [rsp+40h],rbx ; save rbx
fffff805`30c23057 33c9 xor ecx,ecx
fffff805`30c23059 488bda mov rbx,rdx ; copy "Request" into rbx, OK...
fffff805`30c2305c 48897c2450 mov qword ptr [rsp+50h],rdi
fffff805`30c23061 48f7d3 not rbx ; Invert the Request pointer???
fffff805`30c23064 4883e3f8 and rbx,0FFFFFFFFFFFFFFF8h ; Align it??
We invert, and then align the inverted pointer…
fffff805`30c23068 f6c201 test dl,1
fffff805`30c2306b 7540 jne Wdf01000!imp_WdfRequestSetInformation+0x6d (fffff805`30c230ad)
rdx
still has the original non-inverted address, and we’re checking its a one… if it is, it implies that the “real” pointer (the inverted one) is atleast even aligned, so we’re checking for alignment?
EDIT: My bad, its the other way around - the original non-inverted pointer LSB is 0, so the inverted will be mis-aligned - but ofcourse, the previous mask takes care of it.
In which case, is this some kind of tagging system?
fffff805`30c2306d b808100000 mov eax,1008h
fffff805`30c23072 66394308 cmp word ptr [rbx+8],ax ; Start dereferencing it!
fffff805`30c23076 0f85f2a90100 jne Wdf01000!imp_WdfRequestSetInformation+0x1aa2e (fffff805`30c3da6e)
Well, from here on it checks (I presume) the IRP packet is of the correct type in this context, and once it verifies that is the case, it puts it in the right place in the structure and leaves… more of that below too.
fffff805`30c2307c 48895c2458 mov qword ptr [rsp+58h],rbx
fffff805`30c23081 488b7b10 mov rdi,qword ptr [rbx+10h]
fffff805`30c23085 80bf4101000000 cmp byte ptr [rdi+141h],0
fffff805`30c2308c 0f85fea90100 jne Wdf01000!imp_WdfRequestSetInformation+0x1aa50 (fffff805`30c3da90)
fffff805`30c23092 488b8398000000 mov rax,qword ptr [rbx+98h]
fffff805`30c23099 48897038 mov qword ptr [rax+38h],rsi
fffff805`30c2309d 488b5c2440 mov rbx,qword ptr [rsp+40h]
fffff805`30c230a2 488b7c2450 mov rdi,qword ptr [rsp+50h]
fffff805`30c230a7 4883c430 add rsp,30h
fffff805`30c230ab 5e pop rsi
fffff805`30c230ac c3 ret
So, is this a technique to prevent users of WDF even attempting to dereference any pointer to the opaque WDFREQUEST
structure? I.e. WDFREQUEST
structures are always allocated at high addresses, so simply inverting them will make that address garbage, but technically in low address, where we can catch that access?