Informative Information for the Uninformed | ||||||||||||||
|
||||||||||||||
Next: Expanded Set of DPC
Up: Notable Protection Mechanisms
Previous: Notable Protection Mechanisms
Contents
Anti-Debug Code During InitializationThat 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
|