Informative Information for the Uninformed | ||||||||||||||
|
||||||||||||||
Next: Obfuscation of System Integrity
Up: Notable Protection Mechanisms
Previous: Expanded Set of DPC
Contents
Self-Decrypting and Mutating System Integrity Check RoutinePatchGuard version 2 also inherits the capability to encrypt its datastructures and executable code in-memory from version 1. This is a defensive mechanism that intends to make it difficult for an attacker to perform a classic egghunt style search, wherein the attacker has devised an identifiable signature for PatchGuard data structures that can be used to locate it in an exhaustive non-paged-pool memory scan. From this perspective, the obfuscation and encryption of PatchGuard code and data structures that are dynamically allocated is still a reasonably strong defensive mechanism. Unfortunately for Microsoft, though, some of the data structures linking to PatchGuard are internal system structures (such as a KDPC and associated KTIMER used to kick off PatchGuard execution). This presents a weakness that could be potentially used to identify PatchGuard structures in memory (which will be explored in more detail later). The encryption of PatchGuard's internal context structures was covered by Uninformed's original paper [2] on the subject. However, the mechanism by which PatchGuard obfuscates its system integrity checking and validation routines was not discussed. This mechanism is novel enough to warrant some explanation. The technique used to obfuscate PatchGuard's executable code in-memory involves two layers of decryption/deobfuscation functions, each of which decrypts the next layer. After both layers have run their course, PatchGuard's validation routines are plaintext in memory and are then directly executed. The first decryption layer is the code block that is called from the repurposed DPC routine selected by PatchGuard at boot time. Its job is to decrypt itself (in 8 byte chunks, starting with the second instruction in the function). After the decryption of the this code block is complete, the decryption stub continues on to decrypt a second code block (the actual PatchGuard validation routine). When this second decryption/deobfuscation cycle is completed, the decryption stub then executes the actual PatchGuard system integrity check routine. As noted above, the first task for the decryption stub is to decrypt itself. Except for the first instruction of the stub, the entire routine is encrypted when entered. The first instruction encrypts itself and decrypts the next instruction. The following instruction decrypts the next two instructions, and soforth. This is accomplished by a series of four byte long instructions that xor an eight byte quantity with a decryption key (initially starting at the current instruction pointer - here, rcx and rip always have the same value. An example of how this process works is illustrated below:
; ; rcx: Address of the decryption stub (same as rip) ; rdx: Decryption key ; Breakpoint 5 hit nt!ExpTimeRefreshDpcRoutine+0x20a: fffff800`0112c98b ff5538 call qword ptr [rbp+38h] 0: kd> u poi(rbp+38) ; ; Note that beyond the first instruction, the decryption stub is initially seemingly ; garbage data (though it has an apparent pattern to it, since it is merely obfuscated ; by xor). ; fffffadf`f6e6d55d f0483111 lock xor qword ptr [rcx],rdx fffffadf`f6e6d561 88644d68 mov byte ptr [rbp+rcx*2+68h],ah fffffadf`f6e6d565 62 ??? fffffadf`f6e6d566 d257df rcl byte ptr [rdi-21h],cl fffffadf`f6e6d569 88644d78 mov byte ptr [rbp+rcx*2+78h],ah fffffadf`f6e6d56d 62 ??? fffffadf`f6e6d56e d257ef rcl byte ptr [rdi-11h],cl fffffadf`f6e6d571 88644d48 mov byte ptr [rbp+rcx*2+48h],ah 0: kd> t fffffadf`f6e6d55d f0483111 lock xor qword ptr [rcx],rdx 0: kd> r ; ; Note the initial input arguments. rcx points to the decryption stub's first ; instruction (same as rip), and rdx is the decryption key. ; rax=fffffadff6e6d55d rbx=fffff8000116d894 rcx=fffffadff6e6d55d rdx=601c55c0cf06e32a rsi=fffff800003c7ad0 rdi=0000000000000003 rip=fffffadff6e6d55d rsp=fffff800003c51f8 rbp=fffff800003c7ad0 r8=0000000000000000 r9=0000000000000000 r10=0000000001c7111e r11=fffff800003c54c0 r12=fffff8000116d858 r13=fffff800003c5370 r14=fffff80001000000 r15=fffff800003c60a0 iopl=0 nv up ei pl zr na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00000246 fffffadf`f6e6d55d f0483111 lock xor qword ptr [rcx],rdx ds:002b:fffffadf`f6e6d55d=684d6488113148f0 ; ; After allowing the decryption of the stub to progress, we see the stub in its executable ; form. The first instruction is initially re-encrypted after executed, but a later ; instruction in the decryption stub returns the initial instruction to its executable, ; plaintext form. ; 0: kd> u FFFFFADFF6E6D55D ; ; The `lock' prefix is used to create a four byte instruction when there ; is no immediate offset specified (a MASM limitation, as the assembler ; will convert a zero offset into the shorter form with no immediate ; offset operand). ; fffffadf`f6e6d55d f0483111 lock xor qword ptr [rcx],rdx fffffadf`f6e6d561 48315108 xor qword ptr [rcx+8],rdx fffffadf`f6e6d565 48315110 xor qword ptr [rcx+10h],rdx fffffadf`f6e6d569 48315118 xor qword ptr [rcx+18h],rdx fffffadf`f6e6d56d 48315120 xor qword ptr [rcx+20h],rdx fffffadf`f6e6d571 48315128 xor qword ptr [rcx+28h],rdx fffffadf`f6e6d575 48315130 xor qword ptr [rcx+30h],rdx fffffadf`f6e6d579 48315138 xor qword ptr [rcx+38h],rdx 0: kd> u fffffadf`f6e6d57d 48315140 xor qword ptr [rcx+40h],rdx fffffadf`f6e6d581 48315148 xor qword ptr [rcx+48h],rdx ; ; Because the initial instruction was re-encrypted after it was executed, ; we need to decrypt it again. ; fffffadf`f6e6d585 3111 xor dword ptr [rcx],edx fffffadf`f6e6d587 488bc2 mov rax,rdx fffffadf`f6e6d58a 488bd1 mov rdx,rcx fffffadf`f6e6d58d 8b4a4c mov ecx,dword ptr [rdx+4Ch] ; ; The following is the second stage decryption loop. It's purpose is to ; decrypt a code block following the current decryption stub in memory. ; ; This code block is then executed (it is responsible for performing the ; actual PatchGuard system verification checks). ; fffffadf`f6e6d590 483144ca48 xor qword ptr [rdx+rcx*8+48h],rax fffffadf`f6e6d595 48d3c8 ror rax,cl 0: kd> u fffffadf`f6e6d598 e2f6 loop fffffadf`f6e6d590 ; ; After decryption of the second block is completed, we'll execute it ; by jumping to it. Doing so kicks off the system verification routine ; that verifies system integrity, arranging for a bug check if not, ; otherwise arranging for itself to be executed again several minutes ; later. ; fffffadf`f6e6d59a 8b8288010000 mov eax,dword ptr [rdx+188h] fffffadf`f6e6d5a0 4803c2 add rax,rdx fffffadf`f6e6d5a3 ffe0 jmp rax Prior to returning control, the verification routine re-encrypts itself so that it does not remain in plaintext after the first invocation. In addition, PatchGuard also re-randomizes the key used to encrypt and decrypt the PatchGuard validation routine on each execution, such that a would-be attacker has a frequently mutating target. Due to this behavior, the PatchGuard validation routine changes appearance (in encrypted form) in-memory every few minutes, which is the period of PatchGuard's validation checks. While this is perhaps an admirable effort on Microsoft's part as far as interesting obfuscation techniques go, it turns out that there are much easier avenues of attack that can be used to disable PatchGuard without having to involve oneself in the search of a target that alters its appearance in-memory every few minutes.
|