Uninformed: Informative Information for the Uninformed

Vol 8» 2007.Sep


Randomized Call Frames in Repurposed DPC Routine Exception Paths

One of the bypass vectors proposed for PatchGuard 2 was to intercept execution at _C_specific_handler, detect PatchGuard, and resume execution at the return point of the PatchGuard DPC (i.e. inside the timer or DPC dispatcher). This is trivially possible due to the extensive unwind metadata present on Windows x64 combined with the fact that a DPC that has been re-purposed by PatchGuard does no useful work (other than invoking PatchGuard) and has no meaningful effect on any out parameters or return value.

In order to counteract this weakness, PatchGuard 3 introduces a random number of function calls when a re-purposed DPC is called, but before any exception is triggered. The intent with this randomization of the call frame stack is to invalidate the approach of always unwinding one level deep in order to effect a return from the DPC routine in question. Because there are a random number of call frames between the point at which an exception is raised and the start of the PatchGuard DPC routine, and the fact that the PatchGuard DPC routines are not exported, it is more difficult to safely return out of a PatchGuard DPC routine from the anywhere in the SEH dispatching code path.

An example of the call frame randomization code is provided below (in this case, ecx is initialized to small, random number that denotes the number of calls to make). There are a number of routines in the form of KiCustomRecurseRoutineN where N is [0..9], each identical.

KiCustomRecurseRoutine4 proc near
  sub     rsp, 28h
  dec     ecx
  jz      short retpoint
  call    KiCustomRecurseRoutine5
retpoint:
  mov     eax, [rdx]
  add     rsp, 28h
  retn
KiCustomRecurseRoutine4 endp

Although unwinds can still be performed, an attacker would need to be able to locate the actual return address of the PatchGuard DPC routine which might involve differentiating between the bogus KiCustomRecurseRoutine calls and the actual call into the DPC routine itself.