Function Hooking

This is a continuation of my previous post on DLL injection.

Now that we can run code on our process, we might want to change the behavior of some of the software’s functions. To do this, we will do something called function hooking.

Some knowledge of x86 Assembly is required, but I will explain what each instruction does.

What is function hooking?

Function hooking is the process of modifying the behavior of a function in software. We can achieve this in several ways, the one we will use today involves redirecting a function’s execution to our own execution, while preserving the original function if we wish to call it, perhaps with modified arguments.

This method only works for x86, so make sure both your DLL injector and the program you want to modify are x86.

How does it work?

We will overwrite the start of the function we want to hook with a jump instruction to the function we want to replace it with. We can preserve access to the original function by copying the bytes we overwrote somewhere in memory, and that location memory will serve as a trampoline to the original function, by preserving the overwritten bytes and then jumping to the original function.

Finding the address of the function

A detailed explanation is outside the scope of this article, and it’s something that comes with practice, but you can use a debugger such as x64dbg. Something you want to be careful about is that x86 has variable length instruction, and we will need at least 5 bytes for our jump, so if taking five bytes at the beginning of the function ends up splitting an instruction, you will need to copy more bytes to our trampoline function. It’s best to use a function offset relative to the module’s base address. This is important, we can’t rely on absolute addresses due to ASLR.

Jump instruction

The instruction we will be using to redirect the function is JMP and it has the opcode 0xE9. This function can take a 4 byte long address relative to the current EIP. This means, for example if we want a relative address to our trampoline from the start of the function we can do:

functionAddress - trampolineAddress - 5

The 5 is the length of the JMP instructions, we need to subtract due to it being a relative jump, because EIP will be increased by the length of the JMP instruction.

Code

On my example, I hooked into a function that had the following signature:

int ReturnsAValue();

The function always returns 0, let’s make it return something else.

On a new DLL project, let’s first define a function pointer with the same signature as the function we want to hook so we can access the original function, and a function that will replace it. Keep in mind your offset will probably be different.

We will also define a variable with the absolute address of the original function:

 1int(*ReturnAValueOriginal)();
 2
 3int ReturnsAValueHooked() {
 4    return 1;
 5    // return ReturnAValueOriginal();
 6
 7}
 8
 9const int FUNCTION_OFFSET = 0x11730;
10HANDLE BASE_ADDRESS = GetModuleHandle(NULL);
11const int ADDRESS_WITH_OFFSET = (int)BASE_ADDRESS + FUNCTION_OFFSET;

Now let’s move onto the implementation of the hook:

 1void HookFunction() {
 2    void* originalFunction = (int*)ADDRESS_WITH_OFFSET;
 3    void* hookedFunction = (int*)ReturnsAValueHooked;
 4    int length = 9;
 5    LPVOID trampoline = VirtualAlloc(0, length + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
 6    memcpy(trampoline, originalFunction, length);
 7    *(char*)((int)trampoline + length) = 0xE9;
 8    *(int*)((int)trampoline + length + 1) = (int)originalFunction - (int)trampoline - 5;
 9    DWORD memoryProtection;
10    VirtualProtect(originalFunction, length, PAGE_EXECUTE_READWRITE, &memoryProtection);
11    *(char*)((int)originalFunction) = 0xE9;
12    *(int*)((int)originalFunction + 1) = (int)hookedFunction - (int)originalFunction - 5;
13    VirtualProtect(originalFunction, length, memoryProtection, &memoryProtection);
14    ReturnAValueOriginal = (int (*)())trampoline;
15}

Wow, that’s a lot of casts. Let’s go over it step by step:

First, we want to allocate space for our trampoline function, and then copy the bytes we will overwrite from the the original function onto there. In my case, I couldn’t take out just 5 bytes, I would be cutting across an instruction, so I had to take 9.

We want to add a JMP instructions at the end of the trampoline so we can go back to the original function, this JMP goes over the instructions we overwrote, otherwise we’d end up in a loop.

Next, we need to set the JMP to the hooked function on the original function. The first thing we need is to make sure we can write into the memory the original function is in, the the code part of the executable isn’t writeable for security reasons.

Then, we set a JMP to the hooked function, we set the memory protection back to the default one, and we assign the trampoline to our ReturnAValueOriginal function, so we always have access to it.

Conclusion

Congratulations on making it to the end! There are a lot more different types of hooks to explore, here’s some interesting ones:

Virtual method table hooking

Internal IAT hooking

As an aside, if you’re disassembling Windows functions, you will often see they start with this:

1MOV EDI, EDI

Windows actually uses this for hot patching functions during updates.

You can read more about it here Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?

In practice, you will want to use a library such as Detours, but it’s nice to know how things work.

The code is available on my GitHub

Built with Hugo
Theme Stack designed by Jimmy