• Aucun résultat trouvé

Image Modifi cation

Dans le document Michael A. Davis (Page 169-173)

Popularity: 9

Simplicity: 9

Impact: 8

Risk Rating: 9

Image modification involves editing the binary executables of programs themselves, whether on disk or in memory. While the two representations are similar, a binary on disk greatly differs from its in-memory representation. However, the major sections of an image (text, code, relocatable, and so on) are the same. We will only consider in-memory image modification, as it is the most pertinent to kernel-mode rootkits.

The concept of image modification is common in user-mode rootkits, because Import Address Table (IAT) hooking techniques are portable across all PE-formatted executables.

Here, however, we’ll focus on two stealthier methods used by kernel-mode rootkits:

detours and inline hooks.

Detours/Patches and Inline Hooks All three of these terms refer to the same basic idea.

Microsoft first called it a detourback in 1999 (http://research.microsoft.com/pubs/68568/

huntusenixnt99.pdf), so we’ll use that term from here on out. The goal of all three is the same: to overwrite blocks of code in the binary image to redirect program execution flow.

Detours and patches typically refer to patching the first few bytes of a function inside the binary (known as the function prologue). Such a patch essentially hooks the entire function.

A prologue consists of assembly code that sets up the stack and CPU registers for the function to execute properly. The epilogue does the reverse; it pops the items off the stack and returns. These two constructs are related to calling conventions utilized in the programmer’s code and implemented by the compiler.

An inline patch does the same thing, but instead of overwriting the prologue, it overwrites bytes somewhere else in the function body. This is a much more difficult feat, both to develop and to detect, because of byte alignment issues, disassembling instructions, and maintaining the overall integrity and functionality of the original function (which would need to be restored after patching to remain stealthy).

A detour typically overwrites the prologue with a variation of JMP or CALL instruction, but the exact instruction and parameters depend on the architecture involved and in what memory access mode the processor is running (x86 supports protected mode, real mode, or virtual mode). This is what makes this technique difficult and impacts portability. Instruction sizes also differ from instruction set to instruction set, and CPU manufacturers have various differences when it comes to opcode (shorthand for “operation code”) values for x86 instructions. All of these subtleties make a difference when developing a detour. If the detour is not implemented correctly, the resulting control flow could immediately impact system stability.

A detour targets a function for patching and overwrites the function’s prologue to jump to the detour’s own function. At this point, the detour can perform preprocessing

tasks, such as alter parameters that were meant for the original function. The detour’s function then calls what is known as a trampoline function, which calls the original function without detouring it (passing in any modified parameters). The original function then does what it was designed to do and returns to the detoured function, which can perform some post-processing tasks (such as modify the results of the original function, which for file hiding would be to remove certain entries).

Crafting a detour is a very tedious task. The detour must be customized for whatever function will be patched (the target). If the target changes after an operating system patch or an update, the detour will need to be redone.

Thus, the first step in developing a detour is to examine the target function. We’ll quickly look at how Greg Hoglund’s Migbot rootkit patches the SeAccessCheck() function to disable Windows security tokens effectively. To examine the SeAccessCheck function, we can use WinDbg using the unassemble command (u). Migbot relies on the prologue of SeAccessCheck looking like:

In the output, the digits at the beginning of the line are binary opcodes that encode the instructions that are shown in disassembled form just to the right of the opcodes. Migbot uses the opcodes from this output to create a binary signature of SeAccessCheck. The first thing Migbot does is validate that these opcodes are present in SeAccessCheck. If they aren’t, it doesn’t attempt to patch the function. The function that does this signature check is shown here:

If the function succeeds, then Migbot attempts to patch SeAccessCheck. Now, it has to have some function to call when it does the patch. The function named my_function_

detour_seaccesscheck will be the target of the detour patched into SeAccessCheck:

Let’s look at what this function does. It is composed completely of inline assembly and is declared as a naked function (no prologue or stack operations), so as to minimize the overhead of restoring CPU registers, flags, and other stack information. The first block of instructions from push ebp to cmp [ebp+24,bl should look familiar—they are the exact same instructions from SeAccessCheck that are being overwritten. This is essentially the “trampoline” portion of the detour; it sets up the stack for SeAccess-Check. The final block of assembly instructions are emit instructions that force the C compiler to generate a far jump (opcode 0xEA) to the address 0x08:0xAAAAAAAA.

This address is just garbage that acts as a placeholder for the real target address to be written in at runtime (because we don’t know what this will be ahead of time). This critical step is performed by the function in Migbot that actually carries out the patching operation, called DetourFunctionSeAccessCheck():

VOID DetourFunctionSeAccessCheck() {

//save a pointer to the real SeAccessCheck char *actual_function = (char *)SeAccessCheck;

char *non_paged_memory;

unsigned long detour_address;

unsigned long reentry_address;

int i = 0;

//these opcodes are what we will patch into SeAccessCheck //notice the 0x11223344 address, which we will need to replace //dynamically with the real address of our detour function

char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90, 0x90 };

//after jumping into our detour function, we will need //some way to get back to SeAccessCheck - since we know we //overwrote 9 bytes, we will set our return address to be //9 bytes after the start of SeAccessCheck

reentry_address = ((unsigned long)SeAccessCheck) + 9;

non_paged_memory = ExAllocatePool(NonPagedPool, 256);

//this loop copies our detour function into nonpaged kernel memory for(i=0;i<256;i++) //placeholder address of 0x11223344 with the real address //of our detour function we just copied into memory detour_address = (unsigned long)non_paged_memory;

//now paste that address into our opcodes

*( (unsigned long *)(&newcode[1]) ) = detour_address;

//now loop over our detour function code and replace //the other placeholder address 0xAAAAAAAA with our //re-entry address so we can jump back to SeAccessCheck

Please see the comments in the code for detailed step-by-step explanations. After executing this function, SeAccessCheck will be patched.

As a final note, it’s worth pointing out that the code for SeAccessCheck has changed since Migbot was released. The first code block, shown in the following WinDbg output,

looks much different than the previous one. Thus, the detour in Migbot would not work on this version of SeAccessCheck.

kd> u 805e5858

nt!SeAccessCheck+0x10:

805e5858 a900000002 test eax,2000000h

805e585d 740b je nt!SeAccessCheck+0x22 (805e586a) 805e585f 8b4d20 mov ecx,dword ptr [ebp+20h]

805e5862 25fffffffd and eax,0FDFFFFFFh

805e5867 0b410c or eax,dword ptr [ecx+0Ch]

805e586a 0b4518 or eax,dword ptr [ebp+18h]

805e586d 8b4d28 mov ecx,dword ptr [ebp+28h]

805e5870 8901 mov dword ptr [ecx],eax

Microsoft Research still maintains the detours program (the free and open source version is named Detours Express 2.1) at http://research.microsoft.com/en-us/projects/

detours/. This program can be used as a stable detour/patching library for your own uses.

Dans le document Michael A. Davis (Page 169-173)

Documents relatifs