Uninformed: Informative Information for the Uninformed

Vol 6» 2007.Jan

Disruption of Debug Register-Based Breakpoints

PatchGuard version 2 attempts to protect itself from breakpoints that are set using the hardware debug registers. These breakpoints operate by setting up to four designated memory locations that are of interest. Each memory location can be configured to cause a debug exception when it is read, written, or executed. Because breakpoints of this flavor are not visible to PatchGuard's code integrity checks (unlike conventional breakpoints, these breakpoints do not involve int 3 (0xcc) opcodes being substituted for target instructions), debug register-based breakpoints (sometimes known as ``memory breakpoints'' or ``hardware breakpoints'') pose a threat to PatchGuard. PatchGuard attempts to counter this threat by disabling all such debug register-based breakpoints as a first step after the system integrity checking routine has been decrypted in-memory:

; Here, the second stage decryption sequence is
; set to run to decrypt the system integrity
; check routine.  We step over the second stage
; decryption and examine the integrity check
; routine in its plaintext state...

fffffadf`f6edc043 8b4a4c          mov     ecx,dword ptr [rdx+4Ch]
fffffadf`f6edc046 483144ca48      xor     qword ptr [rdx+rcx*8+48h],rax
fffffadf`f6edc04b 48d3c8          ror     rax,cl
fffffadf`f6edc04e e2f6            loop    fffffadf`f6edc046
fffffadf`f6edc050 8b8288010000    mov     eax,dword ptr [rdx+188h]
fffffadf`f6edc056 4803c2          add     rax,rdx
fffffadf`f6edc059 ffe0            jmp     rax
fffffadf`f6edc05b 90              nop
; We set a breakpoint on the 'jmp rax' instruction
; above.  This instruction is what transfers control
; to the system integrity check routine.
0: kd> ba e1 fffffadf`f6edc059
0: kd> g
Breakpoint 2 hit
fffffadf`f6edc059 ffe0            jmp     rax
; rax now points to the decrypted system
; integrity check routine in-memory.  The
; first call it makes is to a routine whose
; purpose is to disable all debug register-based
; breakpoints by clearing the debug control
; register (dr7).  Doing so effectively turns
; off all of the debug register breakpoints.
0: kd> u @rax
fffffadf`f6edd8de 4883ec78        sub     rsp,78h
fffffadf`f6edd8e2 48895c2470      mov     qword ptr [rsp+70h],rbx
fffffadf`f6edd8e7 48896c2468      mov     qword ptr [rsp+68h],rbp
fffffadf`f6edd8ec 4889742460      mov     qword ptr [rsp+60h],rsi
fffffadf`f6edd8f1 48897c2458      mov     qword ptr [rsp+58h],rdi
fffffadf`f6edd8f6 4c89642450      mov     qword ptr [rsp+50h],r12
fffffadf`f6edd8fb 488bda          mov     rbx,rdx
fffffadf`f6edd8fe 4c896c2448      mov     qword ptr [rsp+48h],r13
0: kd> u
fffffadf`f6edd903 e8863a0000      call    fffffadf`f6ee138e
; The routine simply writes all zeros to dr7.
0: kd> u fffffadf`f6ee138e
fffffadf`f6ee138e 33c0            xor     eax,eax
fffffadf`f6ee1390 0f23f8          mov     dr7,rax
fffffadf`f6ee1393 c3              ret