Uninformed: Informative Information for the Uninformed

Vol 6» 2007.Jan


Anti-Debug Code During Initialization

That being said, there are still a number of interesting things to examine as far as PatchGuard's protection mechanisms go. Many of these techniques are on their own worthy of discussion, simply from the perspective of their worth as general debug/analysis protection mechanisms. PatchGuard version 2 begins as an appended addition to the nt!SepAdtInitializePrivilegeAuditing routine in the kernel (PatchGuard version 2 continues the tactic of misleading and/or bogus function names that PatchGuard version 1 introduced). This routine is responsible for performing the bulk of PatchGuard's initialization, including setting up the encrypted PatchGuard context data structures. Unlike PatchGuard version 1, the initialization routine is littered with statements that are intended to frustrate debugging, such as the following construct that enters an infinite loop if a debugger is connected (this particular construct is used in many places during PatchGuard initialization):

cli
cmp     cs:KdDebuggerNotPresent, r12b
jnz     short continue_initialization_1
infinite_loop_1:
jmp     short infinite_loop_1
sti

This particular approach is not all that robust as currently implemented in PatchGuard version 2 today. It remains relatively easy to detect these references to nt!KdDebuggerNotPresent ahead of time, and disable them. If Microsoft had elected to corrupt the execution context in a creative way on each occurrence (such as zeroing some registers, or otherwise arranging for a failure to occur much later on if a debugger was attached) before entering the forever loop, then these constructs might have been slightly effective as far as anti-debugging goes.

Other constructs include the highly obfuscated selection of a randomized set of bogus pool tags used to allocate PatchGuard data structures. Like PatchGuard version 1, PatchGuard version 2 uses a randomly chosen bogus pool tag and randomly adjusted allocation sizes in an attempt to frustrate easy detection of the PatchGuard context in-memory by scanning pool allocations. The following is an example of one of the sections of code used by PatchGuard to randomly pick a pool tag and random allocation delta from a list of possible pool tags. The actual allocation size is the random allocation delta plus the minimum size of the PatchGuard context structure, truncated at 2048 bytes. Here, the rdtsc instruction is used for random number generation purposes (readers that have examined the previous [2] PatchGuard paper may recognize this random number generation construct; it is used throughout PatchGuard anywhere a random quantity is required).

;
; Generate a random value, using rdtsc.
;
lea     ebx, [r14+r13+200h]
mov     dword ptr [rsp+0A28h+Timer], ebx
rdtsc
mov     r10, qword ptr [rsp+0A28h+arg_5F8]
shl     rdx, 20h
mov     r11, 7010008004002001h
or      rax, rdx
mov     rcx, r10
xor     rcx, rax
lea     rax, [rsp+0A28h+var_2C8]
xor     rcx, rax
mov     rax, rcx
ror     rax, 3
xor     rcx, rax
mov     rax, r11
mul     rcx
mov     [rsp+0A28h+var_2C8], rax
xor     eax, edx
mov     [rsp+0A28h+arg_1F0], rdx
;
; This is essentially a switch(eax & 7), where eax
; is a random value.  Each case statement selects
; a unique obfuscated pooltag value.  The magical
; 0x432E10h constant below is the offset used to
; jump to the switch case handler selected.
;
lea     rdx, cs:400000h
and     eax, 7
mov     ecx, [rdx+rax*4+432E10h]
add     rcx, rdx
jmp     rcx
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 0D098D0D8h
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
ror     r9d, 6
jmp     DoAllocation
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 0B2AD31A1h
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
rol     r9d, 1
jmp     DoAllocation
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 85B5910Dh
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
ror     r9d, 2
jmp     DoAllocation
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 0A8223938h
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
xor     r9d, 3
ror     r9d, 0Fh
jmp     DoAllocation
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 67076494h
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
rol     r9d, 4
jmp     DoAllocation
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 288C49EDh
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
ror     r9d, 5
jmp     DoAllocation
--------------------------------------------------
mov     dword ptr [rsp+0A28h+var_9D8], 4E574672h
mov     r9d, dword ptr [rsp+0A28h+var_9D8]
xor     r9d, 6
ror     r9d, 18h
jmp     DoAllocation
--------------------------------------------------
DoAllocation:
;
; Get another random value (for the allocation size),
; and deobfuscate the pooltag value that was selected.
;
; Eventually, the value ending up in "r9d" is used as
; the pooltag value.
;
rdtsc
shl     rdx, 20h
mov     rcx, r10
or      rax, rdx
xor     rcx, rax
lea     rax, [rsp+0A28h+var_858]
xor     rcx, rax
mov     rax, rcx
ror     rax, 3
xor     rcx, rax
mov     rax, r11
mul     rcx
mov     [rsp+0A28h+ValueName], rdx
mov     r9, rax
mov     [rsp+0A28h+var_858], rax
xor     r9d, edx
mov     eax, 4EC4EC4Fh
mov     ecx, r9d
mul     r9d
shr     edx, 3
shr     r9d, 5
mov     r8d, r9d
mov     eax, 4EC4EC4Fh
imul    edx, 1Ah
sub     ecx, edx
add     ecx, 61h
shl     ecx, 8
mul     r9d
shr     edx, 3
shr     r9d, 5
mov     eax, 4EC4EC4Fh
imul    edx, 1Ah
sub     r8d, edx
mul     r9d
add     r8d, 41h
mov     eax, 4EC4EC4Fh
or      r8d, ecx
shr     edx, 3
mov     ecx, r9d
shr     r9d, 5
shl     r8d, 8
imul    edx, 1Ah
sub     ecx, edx
add     ecx, 61h
or      ecx, r8d
shl     ecx, 8
mul     r9d
shr     edx, 3
imul    edx, 1Ah
sub     r9d, edx
add     r9d, 41h
or      r9d, ecx
rdtsc
shl     rdx, 20h
mov     rcx, r10
mov     r8d, r9d        ; Tag
or      rax, rdx
xor     rcx, rax
lea     rax, [rsp+0A28h+var_2E8]
xor     rcx, rax
mov     rax, rcx
ror     rax, 3
xor     rcx, rax
mov     rax, r11
mul     rcx
;
; Perform the actual allocation.  We're requesting NonPagedPool,
; with the random pooltag selected by the deobfuscation and
; randomization code above.  The actual size of the block being
; allocated here is given in ebx, with a random "fuzz factor" that
; is added to this minimum allocation size, then truncated to a
; maximum of 2047 bytes.
;
xor     ecx, ecx        ; PoolType
mov     [rsp+0A28h+var_310], rdx
xor     rdx, rax
mov     [rsp+0A28h+var_2E8], rax
and     edx, 7FFh
add     edx, ebx        ; NumberOfBytes
call    ExAllocatePoolWithTag