Uninformed: Informative Information for the Uninformed

Vol 6» 2007.Jan

General Detect Bit Interception

One of PatchGuard's anti-debug mechanisms relates to debug registers. Specifically, PatchGuard attempts to clear Dr7 (the debug control register) in an attempt to disable all debug-register-based breakpoints, as one of the first tasks upon entering the system integrity check routine. This presents an inherent weakness within PatchGuard, as there is support built-in to the processor that allows one to detect (and intercept) direct accesses to any of the debug registers. This support is primarily legacy, intended for so-called in-circuit emulators (ICEs), which were special hardware components that acted as a true hardware-based debugger by allowing one to control a processor from outside the context of the system entirely, in essence truly isolating the debugger from the operating system and any programs running under it. This support is embodied in the General Detect bit in Dr7, which when set, causes a debug trap to be generated on any successful access to a debug register. This is significant in that it provides a way for an attacker to trap PatchGuard's access to Dr7 (zeroing it), which in effect provides a means to pinpoint the exact location of PatchGuard's system integrity routine in-memory, in-plaintext. Furthermore, it gives an attacker the possibility of making any alterations desired to the execution context at the very start of the system integrity check, which could be trivially used in order to simply implement an immediate return out of the system integrity check logic without actually verifying the system's integrity (as dr7 is zeroed before any integrity checks are performed). This approach effectively turns another one of PatchGuard's protection mechanisms against it, utilizing the anti-debug-register behavior to detect (and block) PatchGuard.

The general idea behind this approach is similar to that described in technique 4.4. In the same fashion as in technique 4.4, an implementor of this approach is required to gain control of the debug trap handler. For this task, any of the proposed approaches in technique 4.4 may be used. After control of the debug trap handler is established, an attacker must then set the general detect bit in Dr7 and wait for PatchGuard to access the debug registers. It should be noted that during the legitimate course of execution, the kernel itself will often directly access debug registers, such as during context switches or if NtSetContextThread/NtGetContextThread are invoked. Any such implementation of this technique must be able to differentiate between PatchGuard's accesses of the debug registers and legitimate accesses. This could be trivially implemented by checking if the RIP value at the time of the trap was within a valid kernel image or not, as the PatchGuard system integrity check routine resides in dynamically allocated non-paged pool and not within the confines of the kernel images in-memory.

When the debug trap handler is invoked as a result of PatchGuard zeroing Dr7, then the appropriate action (which could be as trivial as simply executing a hard return out of the system integrity check routine) can be taken by the third-party driver wishing to disable PatchGuard.

Like the techniques that capitalize on PatchGuard's use of SEH to obfuscate the call to the system integrity check routine, this approach relies on using one of PatchGuard's defensive mechanisms against it. The most obvious counter would be to thus remove the behavior of zeroing debug registers. However, disabling this behavior may not be very desirable, as it would then be very easy to detect PatchGuard by, say, setting a read breakpoint on kernel code and waiting for PatchGuard to perform a read. Since reads of kernel code (as opposed to execute fetches) are fairly atypical, this would open up another easy mechanism by which PatchGuard could be bypassed.

The best course of action by Microsoft here would be to make it as difficult as possible to differentiate between legitimate accesses to debug registers and PatchGuard's own accesses, although this is likely to not be very doable. Strengthening the debug trap path against interception by placing additional validation checks over that code path might also be useful in countering this technique, although likely to only a limited, easily-bypassable extent.