|Informative Information for the Uninformed|
Next: Reporting Verification Inconsistencies Up: Implementation Previous: Obfuscating the PatchGuard Contexts Contents
Executing the PatchGuard Verification Routine
Gathering the checksums and caching critical structure values is great, but it means absolutely nothing if there is no means by which it can be validated. To that effect, PatchGuard goes to great lengths to make the execution of the validation routine as covert as possible. This is accomplished through the use of misdirection and obfuscation.
After all of the sub-contexts have been initialized, but prior to encrypting the primary context, nt!KiInitializePatchGuard performs one of its more critical operations. In this phase, the routine that will be indirectly used to handle the PatchGuard verification is selected at random from an array of function pointers and is stored at offset 0x168 in the primary PatchGuard context. The functions found within the array have a very special purpose that will be discussed in more detail later in this section. For now, earmark the fact that a verification routine has been selected.
Following the selection of a verification routine, the primary PatchGuard context is encrypted as described in the previous section. After the encryption completes, a timer is initialized that makes use of a sub-context that was allocated early on in the PatchGuard initialization process by nt!KiInitializePatchGuard. The timer is initialized through a call to nt!KeInitializeTimer where the pointer to the timer structure that is passed in is actually part of the sub-context structure allocated earlier. Immediately following the initialized timer structure in memory at offset 0x88 is the word value 0x1131. When disassembled, these two bytes translate to a xor [rcx], edx instruction. If one looks closely at the first two bytes of nt!CmpAppendDllSection, one will see that its first instruction is composed of exactly those two bytes. Though not important at this juncture, it may be of use later.
With the timer structure initialized, PatchGuard begins the process of queuing the timer for execution by calling a function that has been labeled nt!PgInitializeTimer which is prototyped as shown below:
VOID PgInitializeTimer( IN PPATCHGUARD_CONTEXT Context, IN PVOID EncryptedContext, IN ULONG64 XorKey, IN ULONG UnknownZero);
Inside the nt!PgInitializeTimer routine, a few strange things occur. First, a DPC is initialized that uses the randomly selected verification routine described earlier in this section as the DeferredRoutine. The EncryptedContext pointer that is passed in as an argument is then XOR'd with the XorKey argument to produce a completely bogus pointer that is passed as the DeferredContext argument to nt!KeInitializeDpc. The end result is pseudo-code that looks something like this:
KeInitializeDpc( &Dpc, Context->TimerDpcRoutine, EncryptedContext ^ ~(XorKey << UnknownZero));
After the DPC has been initialized, a call is made to nt!KeSetTimer that queues the DPC for execution. The DueTime argument is randomly generated as to make it harder to signature with a defined upper bound in order to ensure that it is executed within a reasonable time frame. After setting the timer, nt!PgInitializeTimer returns to the caller.
With the timer initialized and set to execute, nt!KiInitializePatchGuard has completed its operation and returns to nt!KiFilterFiberContext. The divide error fault that caused the whole initialization process to start is corrected and execution is restored back to the instruction following the div in nt!KiDivide6432, thus allowing the kernel to boot as normal.
That's only half of the fun, though. The real question now is how the validation routine gets executed. It seems obvious that it's related to the DPC routine that was used when the timer was set, so the most logical place to look is there. Recalling from earlier in this section, nt!KiInitializePatchGuard selected a validation routine address from an array of routines at random. This array is found by looking at this disassembly from the PatchGuard initialization routine:
nt!KiDivide6432+0xec3: fffff800`01423e74 8bc1 mov eax,ecx fffff800`01423e76 488d0d83c1bdff lea rcx,[nt] fffff800`01423e7d 488b84c128044300 mov rax,[rcx+rax*8+0x430428]
Again, the same obfuscation technique that was used to hide the pool tag array is used here. By adding 0x430428 to the base address of nt, the array of DPC routines is revealed:
lkd> dqs nt+0x430428 L3 fffff800`01430428 fffff800`01033b10 nt!KiScanReadyQueues fffff800`01430430 fffff800`011010e0 nt!ExpTimeRefreshDpcRoutine fffff800`01430438 fffff800`0101dd10 nt!ExpTimeZoneDpcRoutine
This tells us the possible permutations for DPC routines that PatchGuard may use, but it doesn't tell us how this actually leads to the validation of the protection contexts. Logically, the next step is to attempt to understand how one of these routines operates based on the DeferredContext that is passed to is since it is known, from nt!PgInitializeTimer, that the DeferredContext argument will point to the PatchGuard context XOR'd with an encryption key. Of the three, routines, nt!ExpTimeRefreshDpcRoutine is the easiest to understand. The disassembly of the first few instructions of this function is shown below:
lkd> u nt!ExpTimeRefreshDpcRoutine nt!ExpTimeRefreshDpcRoutine: fffff800`011010e0 48894c2408 mov [rsp+0x8],rcx fffff800`011010e5 4883ec68 sub rsp,0x68 fffff800`011010e9 b801000000 mov eax,0x1 fffff800`011010ee 0fc102 xadd [rdx],eax fffff800`011010f1 ffc0 inc eax fffff800`011010f3 83f801 cmp eax,0x1
Deferred routines are prototyped as taking a pointer to the DPC that they are associated with as the first argument and the DeferredContext pointer as the second argument. The x64 calling convention tells us that this would equate to rcx pointing to the DPC structure and rdx pointing to the DeferredContext pointer. There's a problem though. The fourth instruction of the function attempts to perform an xadd on the first portion of the DeferredContext. As was stated earlier, the DeferredContext that is passed to the DPC routine is the result of an XOR operation with a pointer which products a completely bogus pointer. This should mean that the box would crash immediately upon de-referencing the pointer, right? It's obvious that the answer is no, and it's here that another case of misdirection is seen.
The fact of the matter is that nt!ExpTimeRefreshDpcRoutine, nt!ExpTimeZoneDpcRoutine, and nt!KiScanReadyQueues are all perfectly legitimate routines that have nothing directly to do with PatchGuard at all. Instead, they are used as an indirect means of executing the code that does have something to do with PatchGuard. The unique thing about these three routines is that they all three de-reference their DeferredContext pointer at some point as shown below:
lkd> u fffff800`01033b43 L1 nt!KiScanReadyQueues+0x33: fffff800`01033b43 8b02 mov eax,[rdx] lkd> u fffff800`0101dd1e L1 nt!ExpTimeZoneDpcRoutine+0xe: fffff800`0101dd1e 0fc102 xadd [rdx],eax
When the DeferredContext operation occurs a General Protection Fault exception is raised and is passed on to nt!KiGeneralProtectionFault. This routine then eventually leads to the execution of the exception handler that is associated with the routine that triggered the fault, such as nt!ExpTimeRefreshDpcRoutine. On x64, the exception handling code is completely different than what most people are used to on 32-bit. Rather than functions registering exception handlers at runtime, each function specifies its exception handlers at compile time in a way that allows them to be looked up through a standardize API routine, like nt!RtlLookupFunctionEntry. This API routine returns information about the function in the RUNTIME_FUNCTION structure which most importantly includes unwind information. The unwind information includes the address of the exception handler, if any. While this is mostly outside of the scope of this document, one can determine the address of nt!ExpTimeRefreshDpcRoutine's exception handler by doing the following in the debugger:
lkd> .fnent nt!ExpTimeRefreshDpcRoutine Debugger function entry 00000000`01cdaa4c for: (fffff800`011010e0) nt!ExpTimeRefreshDpcRoutine | (fffff800`011011d0) nt!ExpCenturyDpcRoutine Exact matches: nt!ExpTimeRefreshDpcRoutine = <no type information> BeginAddress = 00000000`001010e0 EndAddress = 00000000`0010110d UnwindInfoAddress = 00000000`00131274 lkd> u nt + dwo(nt + 00131277 + (by(nt + 00131276) * 2) + 13) nt!ExpTimeRefreshDpcRoutine+0x40: fffff800`01101120 8bc0 mov eax,eax fffff800`01101122 55 push rbp fffff800`01101123 4883ec30 sub rsp,0x30 fffff800`01101127 488bea mov rbp,rdx fffff800`0110112a 48894d50 mov [rbp+0x50],rcx
Looking more closely at this exception handler, it can be seen that it issues a call to nt!KeBugCheckEx under a certain condition with bug check code 0x109. This bug check code is what is used by PatchGuard to indicate that a critical structure has been tampered with, so this is a very good indication that this exception handler is at least either in whole, or in part, associated with PatchGuard.
The exception handlers for each of the three routines are roughly equivalent and perform the same operations. If the DeferredContext has not been tampered with unexpectedly then the exception handlers eventually call into the protection context's copy of the code from INITKDB, specifically the nt!FsRtlUninitializeSmallMcb. This routine calls into the symbol named nt!FsRtlMdlReadCompleteDevEx which is actually what is responsible for calling the various sub-context verification routines.