Uninformed: Informative Information for the Uninformed

Vol 8» 2007.Sep

Timer DPC Dispatcher and DPC Dispatching

Although PatchGuard 3 eliminates SEH as a single point of failure with respect to executing the system integrity checks, the timer and DPC dispatchers continue to remain attractive targets. One simple bypass mechanism is to locate the call sites in both routines (such as by recording the addresses of both dispatcher routines as described in bypass technique 1, and then performing disassembly to locate and patch all call sites. At each call site, it is possible to detect that PatchGuard is being executed by looking for either a non-canonical DeferredContext parameter value or a DeferredRoutine that resides within the non-paged pool. (In PatchGuard 3, implementing the former check alone proves sufficient, as for the ease of the implementation of PatchGuard 3, both the repurposed DPC routines and the non-SEH-based control path use compatible calling conventions, which stipulate a non-canonical obfuscated pointer value as the DeferredContext parameter.)

The main disadvantage of this approach involves inherent difficulties in performing arbitrary code patching in x64 (specifically, the large size of any code patch and the large number of now relatively common instruction-pointer-relative instructions). However, given that this is a difficulty that impacts any code patching on x64, the author feels that it should not be considered a significant problem for a determined attacker. In fact, Microsoft Research's very own Detours implements a code patching system for x64[5], illustrating that code patching on x64 in general is not a task that should be considered insurmountable by any means.

Because the timer and DPC dispatchers remain relatively unprotected targets that have not been involved in public bypass source code that has been released to date, the author would recommend bolstering the defenses of the timer and DPC dispatcher for the next PatchGuard release, as the two routines continue to represent an attractive single point of failure. Adding a third PatchGuard execution mechanism that does not involve traditional DPCs at all would be an example of one approach to eliminate the DPC dispatcher related logic as a single point of failure. It may also be possible to increase the difficulty of locating all the call sites within the DPC dispatching related code through a combination of differing static call stack differences for each of the three call sites of the timer DPC dispatcher (i.e. adding dummy function calls) combined with call stack randomization on top of static call stack differences between each of the three timer DPC dispatcher calll sites. Randomized call stacks alone would not suffice as by examining the call stacks of many iterations of timer DPC requests, it would become easy to eliminate the randomized entries (which would not be common to all recorded call stacks) with a relatively high degree of accuracy given a large sample size. A disadvantage to taking such an approach is that it would essentially result in adding deliberately-difficult-to-maintain "spaghetti code" into yet another critical area of the operating system (timer DPC dispatcher logic). The author suspects that the maintainer of the timer DPC dispatcher code would likely not appreciate having to deal with such things.