Uninformed: Informative Information for the Uninformed

Vol 6» 2007.Jan

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:

  1. Each DPC routine has one exception/unwind-marked registration with _C_specific_handler.
  2. Each DPC routine has exactly four _C_specific_handler scopes.
  3. Each DPC routine is referenced in raw address form (64-bit pointer) in the executable code sections comprising ntoskrnl at least twice.
  4. Each DPC routine has at least two _C_specific_handler scopes with an associated unwind/exception handler.
  5. Each DPC routine has exactly one _C_specific_hanlder scope with a call to a common subfunction that references RtlUnwindEx (an exported routine).
  6. 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.