Informative Information for the Uninformed | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Initializing PatchGuardThe initialization of PatchGuard is multi-faceted, but it all has to start somewhere. In this case, the initialization of PatchGuard starts in a function with a symbol name that has nothing to do with anti-patch protections at all. In fact, it's named KiDivide6432 and the only thing that it does is a division operation as shown in the code below:
ULONG KiDivide6432( IN ULONG64 Dividend, IN ULONG Divisor) { return Dividend / Divisor; } Though this function may look innocuous, it's actually the first time PatchGuard attempts to use misdirection to hide its actual intentions. In this case, the call to nt!KiDivide6432 is passed a dividend value from nt!KiTestDividend. The divisor is hard-coded to be 0xcb5fa3. It appears that this function is intended to masquerade as some type of division test that ensures that the underlying architecture supports division operations. If the call to the function does not return the expected result of 0x5ee0b7e5, nt!KeInitSystem will bug check the operating system with bug check code 0x5d which is UNSUPPORTED_PROCESSOR as shown below:
nt!KeInitSystem+0x158: fffff800`014212c2 488b0d1754d5ff mov rcx,[nt!KiTestDividend] fffff800`014212c9 baa35fcb00 mov edx,0xcb5fa3 fffff800`014212ce e84d000000 call nt!KiDivide6432 fffff800`014212d3 3de5b7e05e cmp eax,0x5ee0b7e5 fffff800`014212d8 0f8519b60100 jne nt!KeInitSystem+0x170 ... nt!KeInitSystem+0x170: fffff800`0143c8f7 b95d000000 mov ecx,0x5d fffff800`0143c8fc e8bf4fc0ff call nt!KeBugCheck When attaching with local kd, the value of nt!KiTestDividend is found to be hardcoded to 0x014b5fa3a053724c such that doing the division operation, 0x014b5fa3a053724c divided by 0xcb5fa3, produces 0x1a11f49ae. That can't be right though, can it? Obviously, the code above indicates that any value other than 0x5ee0b7e5 will lead to a bug check, but it's also equally obvious that the machine does not bug check on boot, so what's going on here? The answer involves a good old fashion case of ingenuity. The result of the the division operation above is a value that is larger than 32 bits. The AMD64 instruction set reference manual indicates that the div instruction will produce a divide error fault when an overflow of the quotient occurs[2]. This means that as long as nt!KiTestDividend is set to the value described above, a divide error fault will be triggered causing a hardware exception that has to be handled by the kernel. This divide error fault is what actually leads to the indirect initialization of the PatchGuard subsystem. Before going down that route, though, it's important to understand one of the interesting aspects of the way Microsoft did this. One of the interesting things about nt!KiTestDividend is that it's actually unioned with an exported symbol that is used to indicate whether or not a debugger is, well, present. This symbol is named nt!KdDebuggerNotPresent and it overlaps with the high-order byte of nt!KiTestDividend as shown below:
lkd> dq nt!KiTestDividend L1 fffff800`011766e0 014b5fa3`a053724c lkd> db nt!KdDebuggerNotPresent L1 fffff800`011766e7 01 The nt!KdDebuggerNotPresent global variable will be set to zero if a debugger is present. If a debugger is not present, the value will be one (default). If the above described division operation is performed while a debugger is attached to the system during boot, which would equate to dividing 0x004b5fa3a053724c by 0xcb5fa3, the resultant quotient will be the expected value of 0x5ee0b7e5. This means that if a debugger is attached to the system prior to the indirect initialization of the PatchGuard protections, then the protections will not be initialized because the divide error fault will not be triggered. This coincides with the documented behavior and is intended to allow driver developers to continue to be able to set breakpoints and perform other actions that may indirectly modify monitored regions of the kernel in a debugging environment. However, this only works if the debugger is attached to the system during boot. If a developer subsequently attaches a debugger after PatchGuard has initialized, then the act of setting breakpoints or performing other actions may lead to a bluescreen as a result of PatchGuard detecting the alterations. Microsoft's choice to initialize PatchGuard in this manner allows it to transparently disable protections when a debugger is attached and also acts as a means of hiding the true initialization vector. With the unioned aspect of nt!KiTestDividend understood, the next step is to understand how the divide error fault actually leads to the initialization of the PatchGuard subsystem. For this aspect it is necessary to start at the places that all divide error faults go: nt!KiDivideErrorFault. The indirect triggering of nt!KiDivideErrorFault leads to a series of function calls that eventually result in nt!KiOp_Div being called to handle the divide error fault for the div instruction. The nt!KiOp_Div routine appears to be responsible for preprocessing the different kinds of divide errors, like divide by zero. Although it may look normal at first glance, nt!KiOp_Div also has a darker side. The stack trace that leads to the calling of nt!KiOp_Div is shown below3.1:
kd> k Child-SP RetAddr Call Site fffffadf`e4a15f90 fffff800`010144d4 nt!KiOp_Div+0x29 fffffadf`e4a15fe0 fffff800`01058d75 nt!KiPreprocessFault+0xc7 fffffadf`e4a16080 fffff800`0104172f nt!KiDispatchException+0x85 fffffadf`e4a16680 fffff800`0103f5b7 nt!KiExceptionExit fffffadf`e4a16800 fffff800`0142132b nt!KiDivideErrorFault+0xb7 fffffadf`e4a16998 fffff800`014212d3 nt!KiDivide6432+0xb fffffadf`e4a169a0 fffff800`0142a226 nt!KeInitSystem+0x169 fffffadf`e4a16a50 fffff800`01243e09 nt!Phase1InitializationDiscard+0x93e fffffadf`e4a16d40 fffff800`012b226e nt!Phase1Initialization+0x9 fffffadf`e4a16d70 fffff800`01044416 nt!PspSystemThreadStartup+0x3e fffffadf`e4a16dd0 00000000`00000000 nt!KxStartSystemThread+0x16 The first thing that nt!KiOp_Div does prior to processing the actual divide fault is to call a function named nt!KiFilterFiberContext. This function seems oddly named not only in the general sense but also in the specific context of a routine that is intended to be dealing with divide faults. By looking at the body of nt!KiFilterFiberContext, its intentions quickly become clear:
nt!KiFilterFiberContext: fffff800`01003ac2 53 push rbx fffff800`01003ac3 4883ec20 sub rsp,0x20 fffff800`01003ac7 488d0552d84100 lea rax,[nt!KiDivide6432] fffff800`01003ace 488bd9 mov rbx,rcx fffff800`01003ad1 4883c00b add rax,0xb fffff800`01003ad5 483981f8000000 cmp [rcx+0xf8],rax fffff800`01003adc 0f855d380c00 jne nt!KiFilterFiberContext+0x1d fffff800`01003ae2 e899fa4100 call nt!KiDivide6432+0x570 It appears that this chunk of code is designed to see if the address that the fault error occurred at is equal to nt!KiDivide6432 + 0xb. If one adds 0xb to nt!KiDivide6432 and disassembles the instruction at that address, the result is:
nt!KiDivide6432+0xb: fffff800`0142132b 41f7f0 div r8d This coincides with what one would expect to occur when the quotient overflow condition occurs. According to the disassembly above, if the fault address is equal to nt!KiDivide6432 + 0xb, then an unnamed symbol is called at nt!KiDivide6432 + 0x570. This unnamed symbol will henceforth be referred to as nt!KiInitializePatchGuard, and it is what drives the set up of the PatchGuard subsystem. The nt!KiInitializePatchGuard routine itself is quite large. It handles the initialization of the contexts that will monitor certain system images, the SSDT, processor GDT/IDT, certain critical MSRs, and certain debugger-related routines. The very first thing that the initialization routine does is to check to see if the machine is being booted in safe mode. If it is being booted in safe mode, the PatchGuard subsystem will not be enabled as shown below:
nt!KiDivide6432+0x570: fffff800`01423580 4881ecd8020000 sub rsp,0x2d8 fffff800`01423587 833d22dfd7ff00 cmp dword ptr [nt!InitSafeBootMode],0x0 fffff800`0142358e 0f8504770000 jne nt!KiDivide6432+0x580 ... nt!KiDivide6432+0x580: fffff800`0142ac98 b001 mov al,0x1 fffff800`0142ac9a 4881c4d8020000 add rsp,0x2d8 fffff800`0142aca1 c3 ret Once the safe mode check has passed, nt!KiInitializePatchGuard begins the PatchGuard initialization by calculating the size of the INITKDBG section in ntoskrnl.exe. It accomplishes this by passing the address of a symbol found within that section, nt!FsRtlUninitializeSmallMcb, to nt!RtlPcToFileHeader. This routine passes back the base address of nt in an output parameter that is subsequently passed to nt!RtlImageNtHeader. This method returns a pointer to the image's IMAGE_NT_HEADERS structure. From there, the virtual address of nt!FsRtlUninitializeSmallMcb is calculated by subtracting the base address of nt from it. The calculated RVA is then passed to nt!RtlSectionTableFromVirtualAddress which returns a pointer to the image section that nt!FsRtlUninitializeSmallMcb resides in. The debugger output below shows what rax points to after obtaining the image section structure:
kd> ? rax Evaluate expression: -8796076244456 = fffff800`01000218 kd> dt nt!_IMAGE_SECTION_HEADER fffff800`01000218 +0x000 Name : [8] "INITKDBG" +0x008 Misc : <unnamed-tag> +0x00c VirtualAddress : 0x165000 +0x010 SizeOfRawData : 0x2600 +0x014 PointerToRawData : 0x163a00 +0x018 PointerToRelocations : 0 +0x01c PointerToLinenumbers : 0 +0x020 NumberOfRelocations : 0 +0x022 NumberOfLinenumbers : 0 +0x024 Characteristics : 0x68000020 The whole reason behind this initial image section lookup has to do with one of the ways in which PatchGuard obfuscates and hides the code that it executes. In this case, code within the INITKDBG section will eventually be copied into an allocated protection context that will be used during the validation phase. The reason that this is necessary will be discussed in more detail later. After collecting information about the INITKDBG image section, the PatchGuard initialization routine performs the first of many pseudo-random number generations. This code can be seen throughout the PatchGuard functions and has a form that is similar to the code shown below:
fffff800`0142362d 0f31 rdtsc fffff800`0142362f 488bac24d8020000 mov rbp,[rsp+0x2d8] fffff800`01423637 48c1e220 shl rdx,0x20 fffff800`0142363b 49bf0120000480001070 mov r15,0x7010008004002001 fffff800`01423645 480bc2 or rax,rdx fffff800`01423648 488bcd mov rcx,rbp fffff800`0142364b 4833c8 xor rcx,rax fffff800`0142364e 488d442478 lea rax,[rsp+0x78] fffff800`01423653 4833c8 xor rcx,rax fffff800`01423656 488bc1 mov rax,rcx fffff800`01423659 48c1c803 ror rax,0x3 fffff800`0142365d 4833c8 xor rcx,rax fffff800`01423660 498bc7 mov rax,r15 fffff800`01423663 48f7e1 mul rcx fffff800`01423666 4889442478 mov [rsp+0x78],rax fffff800`0142366b 488bca mov rcx,rdx fffff800`0142366e 4889942488000000 mov [rsp+0x88],rdx fffff800`01423676 4833c8 xor rcx,rax fffff800`01423679 48b88fe3388ee3388ee3 mov rax,0xe38e38e38e38e38f fffff800`01423683 48f7e1 mul rcx fffff800`01423686 48c1ea03 shr rdx,0x3 fffff800`0142368a 488d04d2 lea rax,[rdx+rdx*8] fffff800`0142368e 482bc8 sub rcx,rax fffff800`01423691 8bc1 mov eax,ecx This pseudo-random number generator uses the rdtsc instruction as a seed and then proceeds to perform various bitwise and multiplication operations until the end result is produced in eax. The result of this first random number generator is used to index an array of pool tags that are used for PatchGuard memory allocations. This is an example of one of the many ways in which PatchGuard attempts to make it harder to find its own internal data structures in memory. In this case, it adopts a random legitimate pool tag in an effort to blend in with other memory allocations. The code block below shows how the pool tag array is indexed and where it can be found in memory:
fffff800`01423693 488d0d66c9bdff lea rcx,[nt] fffff800`0142369a 448b848100044300 mov r8d,[rcx+rax*4+0x430400] In this case, the random number is stored in the rax register which is used to index the array of pool tags found at nt+0x430400. The fact that the array is referenced indirectly might be seen as another attempt at obfuscation in a bid to make what is occurring less obvious at a glance. If the pool tag array address is dumped in the debugger, all of the pool tags that could possibly be used by PatchGuard can be seen:
lkd> db nt+0x430400 41 63 70 53 46 69 6c 65-49 70 46 49 49 72 70 20 AcpSFileIpFIIrp 4d 75 74 61 4e 74 46 73-4e 74 72 66 53 65 6d 61 MutaNtFsNtrfSema 54 43 50 63 00 00 00 00-10 3b 03 01 00 f8 ff ff TCPc.....;...... After the fake pool tag has been selected from the array at random, the PatchGuard initialization routine proceeds by allocating a random amount of storage that is bounded at a minimum by the virtual size of the INITKDBG section plus 0x1b8 and at a maximum by the minimum plus 0x7ff. The magic value 0x1b8 that is expressed in the minimum size is actually the size of the data structure that is used by PatchGuard to store context-specific protection information, as will be shown later. The fake pool tag and the random size are then used to allocate storage from the NonPagedPool as shown in the pseudo-code below:
Context = ExAllocatePoolWithTag( NonPagedPool, (InitKdbgSection->VirtualSize + 0x1b8) + (RandSize & 0x7ff), PoolTagArray[RandomPoolTagIndex]); If the allocation of the context succeeds, the initialization routine zeroes its contents and then starts initializing some of the structure's attributes. The context returned by the allocation will henceforth be referred to as a structure of type PATCHGUARD_CONTEXT. The first 0x48 bytes of the structure are actually composed of code that is copied from the misleading symbol named nt!CmpAppendDllSection. This function is actually used to decrypt the structure at runtime, as will be seen later. After nt!CmpAppendDllSection is copied to the first 0x48 bytes of the data structure, the initialization routine sets up a number of function pointers that are stored within the structure. The routines that it stores the addresses of and the offsets within the PatchGuard context data structure are shown in figure .
The reason that PatchGuard uses function pointers instead of calling the symbols directly is most likely due to the relative addressing mode used in x64. Since the PatchGuard code runs dynamically from unpredictable addresses, it would be impossible to use the relative addressing mode without having to fix up instructions - a task that would no doubt be painful and not really worth the trouble. The authors do not see any particular advantage gained in terms of obfuscation by the use of function pointers stored in the PatchGuard context structure. After all of the function pointers have been set up, the initialization routine proceeds by picking another random pool tag that is used for subsequent allocations and stores it at offset 0x188 within the PatchGuard context structure. After that, two more random numbers are generated, both of which are used later on during the encryption phase of the structure. One is used as a random number of rotate bits, the other is used as an XOR seed. The XOR seed is stored at offset 0x190 and the random rotate bits value is stored at offset 0x18c. The next step taken by the initialization routine is to acquire the number of bits that can be used to represent the virtual address space by querying the processor via through the cpuid ExtendedAddressSize (0x80000008) extended function. The result is stored at offset 0x1b4 within the PatchGuard context structure. Finally, the last major step before initializing the individual protection sub-contexts is the copying of the contents of the INITKDBG section to the allocated PatchGuard context structure. The copy operation looks something like the pseudo code below:
memmove( (PCHAR)PatchGuardContext + sizeof(PATCHGUARD_CONTEXT), NtImageBase + InitKdbgSection->VirtualAddress, InitKdbgSection->VirtualSize); With the primary portions of the PatchGuard context structure initialized, the next logical step is to initialize the sub-contexts that are specific to the things that are actually being protected.
|