Uninformed: Informative Information for the Uninformed

Vol 8» 2007.Sep

Code Patching Support

Given PatchGuard's penchant for blocking attempts to patch the kernel, one would think that all kernel code is essentially expected to be fixed in stone at boot time. However, this is not really the case. There are a number of approved kernel patches that PatchGuard supports. For example, several functions (such as SwapContext) can be patched in approved ways if hypervisor support is enabled. In the case of SwapContext, for instance, a runtime patch is made to redirect execution to EnlightenedSwapContext through a jump instruction being written to the start of the routine. PatchGuard appears to detect and permits patches to these functions through special exemptions (one can observe the address of functions such as SwapContext being stored in the PatchGuard context at initialization time, presumed to be for such a purpose).

The code responsible for checking the integrity of the SwapContext patch is provided below. Because the check ensures that a branch can only occur to EnlightenedSwapContext, it would be difficult to utilize this code to perform an arbitrary patch at SwapContext.

cmp     rdi, [rbx+PATCHGUARD_CONTEXT.SwapContext]
jnz     short NotSwapContextExemption
cmp     byte ptr [rdi], 0EBh ; 'd' ; backward jmps (short)
jnz     short NotSwapContextExemption
cmp     byte ptr [rdi+1], 0F9h ; ''
jnz     short NotSwapContextExemption
cmp     byte ptr [rdi-5], 0E9h ; 'T' ; jmp (long)
jnz     short NotSwapContextExemption
mov     rcx, [rbx+PATCHGUARD_CONTEXT.EnlightenedSwapContext]
movsxd  rax, dword ptr [rdi-4]
sub     rcx, rdi
cmp     rax, rcx
jz      short BadSwapContextHook

There also exists a second set of patches that PatchGuard must allow for compatibility with older processors. Very early releases of x64 processors by Intel did not implement the prefetch instruction, and so the kernel has support for detecting an illegal opcode fault on a prefetch instruction, and reacting by patching out the prefetch opcode on-the-fly. However, this sort of on-the-fly patching is not normally permitted by PatchGuard (for obvious reasons), at least not without special support. During initialization, PatchGuard generates some code that executes a prefetch operation, and then checks whether the the count of patched prefix instructions was incremented after executing the patch code. Assuming that the processor is an older model without prefetch support, then a special exemption (the "prefetch whitelist") is activated the exempts a list of RVAs from the image base from PatchGuard's checks. This list of RVAs is stored in a binary resource appended to ntoskrnl.exe (named "PREFETCHWLIST").

The code for detecting if the prefetch exemption should be enabled at boot time is as follows (the result of the check is, for Windows Server 2008 Beta 3, stored at offset 2B1 into the PatchGuard context):

call    KeGetPrcb
mov     ecx, 2
cmp     [rax+63Dh], cl  ; Prcb->CpuVendor
mov     [rsp+0EC8h+var_D48], rax
jnz     short SkipEnablePrefetchPatchExemption
lea     rdx, [rsi+214h] ; PrefetchRoutineCode
mov     dword ptr [rdx], 0C3090D0Fh ; prefetch [rcx] ; ret
mov     ebx, cs:KiOpPrefetchPatchCount
lea     rcx, [rsp+0EC8h+arg_18]
call    rdx
mov     ecx, cs:KiOpPrefetchPatchCount
cmp     ebx, ecx
jz      short SkipEnablePrefetchPatchExemption

mov     [rsi+2B1h], dil ; EnablePrefetchPatchExemption

; Initialization continues ...
mov     eax, 100000h