|Informative Information for the Uninformed|
Next: Executing PatchGuard Without SEH Up: Protection Improvements Previous: Multiple Concurrent PatchGuard Check Contents
Like PatchGuard 2, and PatchGuard 1 before it, the third iteration of PatchGuard is primarily executed through an unhandled exception in a DPC routine which, through the use of a series of structured exception handlers, eventually results in the self-decrypting PatchGuard stub being called in non-paged pool memory (based off of the DPC arguments). This presented itself as a liability, as evidenced by the previous article  published on Uninformed on the subject of disabling PatchGuard (release 2). The problem with using SEH to trigger execution is that there are a number of points in the SEH dispatching mechanism that can easily be modified by an external caller in order to gain execution after an exception is raised, but before a registered exception handler itself might be called.
Previous techniques exploited this weakness by positioning themselves in after the access violation exception raised when a PatchGuard-repurposed DPC routine dereferenced a specially-crafted invalid pointer argument but before the SEH logic that invokes the PatchGuard check context in response to the access violation exception. Specifically, the operating system exported routine used by the Microsoft C/C++ compiler for all compiler-generated SEH frames, _C_specific_handler, was targeted by bypass attempts described in the aforementioned articles. As the SEH frame responsible for running PatchGuard appears to have been written in C for PatchGuard releases 1 and 2, _C_specific_handler would be called before the user-supplied SEH logic which would be responsible for executing the PatchGuard integrity check logic contained within the current PatchGuard context. At this point, a bypass technique need only abort the execution of the PatchGuard check routine and cleanly extricate itself from the call stack to a known-good location in order to disable PatchGuard.
However, in order for such a bypass mechanism to properly function, one would need to ensure that the particular exception being examined by _C_specific_handler is in fact PatchGuard and not a legitimate kernel mode exception. Applying a PatchGuard-style bypass to the latter case would be disastrous and almost certainly result in the system crashing or being corrupted immediately after the fact. Given this, positively identifying an exception from the exception dispatcher interception point is key to any bypass technique built upon exception dispatcher redirection. While the previous two PatchGuard releases made identifying PatchGuard a trivial task. In both cases, a special form of invalid address, a ``non-canonical address'', is dereferenced to trigger the access violation that ultimately results in PatchGuard's check context being executed2.2.
The advantage of using a non-canonical address is clear when one examines the PatchGuard execution environment for a moment. In Windows kernel mode programming, it is not generally possible to blindly dereference a bogus kernel mode pointer. This often results in a sequence of events that bring down the system, depending on where the dereferenced location is. A non-canonical address is a special (undocumented) exception to this rule, as the processor reports the exception via a general protection fault and not the typical page fault mechanism. In this case, the operating system reports the exception as an access violation related to an access of the highest kernel address (0xFFFFFFFFFFFFFFFF). This distinct signature can be used to locate and disable PatchGuard in a relatively safe fashion, as bogus kernel mode addresses should never make it to SEH dispatching (in kernel mode) unless the system is about to crash due to a fatal driver or kernel bug (PatchGuard being a special case). Thus, it was previously possible to positively identify PatchGuard by looking for an access violation that referenced 0xFFFFFFFFFFFFFFFF.
PatchGuard 3 improves the situation somewhat by performing some pre-filtering of the exception data through an exception handler written in assembly (which thus does not invoke _C_specific_handler) before the _C_specific_handler based logic that actually invokes the PatchGuard check routine is executed. Specifically, the pre-filtering exception handler, whose code is given below, alters the exception code to take on a random value which overlaps with many valid kernel mode exceptions. For example, some status codes that are applicable to the file system space are used, such as STATUS_INSUFFICIENT_RESOURCES, STATUS_DISK_FULL, STATUS_CANT_WAIT. Additionally, the exception address is altered as well (in some cases even set to be pointing into the middle of an instruction), and the dereferenced address (the second exception parameter for access violations) is also set to a randomized value. After these alterations are made, the assembly-language exception handler passes control on to the _C_specific_handler based exception handler, which invokes PatchGuard. Annotated disassembly for one of the assembly-language pre-filter exception handlers is provided below:
; ; EXCEPTION_DISPOSITION ; KiCustomAccessHandler8 ( ; /* rcx */ IN PEXCEPTION_RECORD ExceptionRecord, ; /* rdx */ IN ULONG64 EstablisherFrame, ; /* r8 */ IN OUT PCONTEXT ContextRecord, ; /* r9 */ IN OUT struct _DISPATCHER_CONTEXT* DispatcherContext ; ); KiCustomAccessHandler8 proc near test [rcx+_EXCEPTION_RECORD.ExceptionFlags], 66h loc_14009B4C7: jnz short retpoint rdtsc ; Randomize ExceptionInformation[ 1 ] ; ( This is the "referenced address" for ; an access violation exception.) ; ; ( Note that rax is not set to any ; specific defined value in this ; context. It depends upon the value ; that RtlpExecututeHandlerForException ; and by extension RtlDispatchException ; last set rax to. ) mov [rcx+(_EXCEPTION_RECORD.ExceptionInformation+8)], rax xor [rcx+(_EXCEPTION_RECORD.ExceptionInformation+8)], rdx shr eax, 5 and eax, 70h sub [r8+98h], rax and edx, 7Fh or edx, 0C0000000h ; Set ExceptionCode to a random value. The code ; always has 0xC0000000 set, and the lowest byte ; is always masked with 7F. This often results ; in an exception code that appears like a ; legitimate exception code.) mov [rcx+_EXCEPTION_RECORD.ExceptionCode], edx lea rax, loc_14009B4C7+1 ; Set ExceptionAddress to a bogus value. In this case, ; it is set to in the middle of an instruction. This ; may interfere with attempts to unwind successfully from the exception. mov [rcx+_EXCEPTION_RECORD.ExceptionAddress], rax ; Set Context->Rip to the same ; bogus exception address value. mov [r8+0F8h], rax and qword ptr [r8+88h], 0 retpoint: mov eax, 1 retn KiCustomAccessHandler8 endp
As a direct result of scrubbing the exception and context records by the assembly-language exception routine, it is no longer possible to use the old mechanism of looking for an access violation referencing 0xFFFFFFFFFFFFFFFF in order to differentiate a PatchGuard exception from the many legitimate kernel mode exceptions. In other words, PatchGuard attempts to hide in plain sight amongst the normal background noise of kernel mode exceptions, the vast majority of which exist inside filesystem-related code.