Interception of DPC Exception Registration
Presently, all execution paths leading to the execution of PatchGuard DPC
routines involve an exception/unwind handler. This is another single point of
failure weakness that can be exploited by third parties attempting to
disable PatchGuard. An approach involving the detection of all of the
PatchGuard DPC routines, followed by interception of the exception handler
registrations for each DPC is proposed as another means of defeating
PatchGuard.
Though this technique is not as clean or clear-cut as the technique
proposed in 4.1, this approach is considered by the author as
a viable bypass mechanism for PatchGuard version 2. This technique
essentially involves patching the exception registrations for each
possible DPC routine that could be used by PatchGuard, such that
each exception registration points to a routine that employs a
virtual unwind to safely exit out of the PatchGuard DPC without
invoking the system integrity check. Any such approach faces
several obstacles, however.
The first major difficulty for this technique is locating each PatchGuard DPC.
Since none of the PatchGuard DPC routines are exported, a little bit more
creative thinking is involved in finding the locations to patch. The author
feels that a combination of pattern matching and code fingerprinting would
best serve this goal; there are a number of commonalities between the
different PatchGuard DPC routines that could be used to locate them with a
relatively high degree of confidence in PatchGuard version 2. Specifically,
the author feels that the following criteria are acceptable for use in
detecting the PatchGuard DPC routines:
- Each DPC routine has one exception/unwind-marked registration with _C_specific_handler.
- Each DPC routine has exactly four _C_specific_handler scopes.
- Each DPC routine is referenced in raw address form (64-bit pointer) in the executable code sections comprising ntoskrnl at
least twice.
- Each DPC routine has at least two _C_specific_handler scopes with an associated unwind/exception handler.
- Each DPC routine has exactly one _C_specific_hanlder scope with a call to a common subfunction that references RtlUnwindEx
(an exported routine).
- Each DPC routine has several sets of distinctive, normally rare instructions (ror/rol instructions).
Given several (or even all) of these criteria, it should be possible
to accurately locate all ten DPC routines via scanning non-pagable
code in the kernel. It is possible to locate the exception
registration information for the DPC routines through processing of
the exception directory for the kernel (and indeed, most of the
criteria require doing this as a prerequisite). Locating the kernel
image base is fairly trivial as well; the address of an exported
routine can be taken, and truncated to a 64K region. From there,
one need only perform downward searches in 64K increments for the
DOS header signature (followed by a check for a PE32+ header).
Another hurdle that must be solved for this approach is the placement of the
replacement exception handler routines. These routines are required to be
within 4GB of the kernel image base (there is only a 32-bit RVA in the unwind
metadata), meaning that in general, it is not practical to simply store them
in a driver binary or pool allocation (by default, these addresses are usually
far more than 4GB away from the kernel image base). There are no documented
and exported routines to allocate kernel mode virtual memory at a specific
virtual address to the author's knowledge. However, other, less savory
approaches could theoretically be taken (such as allocating physical memory
and altering paging structures directly to create a valid memory region within
4GB of the kernel image base).
After one has solved these difficulties, the rest of this approach is fairly
trivial (and similar to portions of the technique described in 4.1.).
Specifically, the replaced exception handlers need to invoke RtlVirtualUnwind
to unwind back to the kernel DPC dispatcher, and then request that execution
be resumed at the unwound context.
This mechanism is not nearly as robust as the first in the author's point of
view, though both approaches could be disabled by abandoning SEH entirely as a
critical path in the execution of the PatchGuard system integrity check
routine. Specifically, Microsoft could change the characteristics of the DPC
routines in an attempt to frustrate fingerprinting and detection of them at
runtime. Pre-validation of unwind metadata (or additional checks in the
exception dispatcher itself to ensure that all SEH routines registered as part
of an image are within the confines of the image in-memory) could also be used
to defeat this technique. There are other security benefits to validating
that SEH routines on x64 that are registered as part of an image really exist
within an image, as will be discussed below. As such, the author would expect
this to appear in a future Windows version.
|