../

Hook Injection

2022-11-15

Hooking is a common technique in game cheat development: by placing a small snippet of machine code (usually a jmp instruction) into the memory location where a normal function resides, one can alter the behavior of that function. This technique is useful not only in game hacking but also in software testing, where it can be used to mock non-virtual functions. Implementing a hook is also quite simple.

Consider the following code:

#include <stdio.h>

int func()
{
    printf("hello, beautiful world");
    return 0;
}

int main()
{
    return func();
}

Although modifying the func function is trivial here, let’s assume func actually resides in a dynamic shared library or a static library for which we do not have the source code. In such cases, we can use hooking to modify func at runtime.

Here, we will take x64 Linux as an example. Generally, code resides in the .text segment, where memory is read-only. Therefore, we use the mprotect function to modify the attributes of this memory area to allow writing:

#include <unistd.h>
#include <sys/mman.h>

// Assume the hooked function is orig_func
int page_size = getpagesize();
void *aligned_addr = (void *)(((uintptr_t)orig_func) & (~(page_size - 1)));
if (mprotect(aligned_addr,
             page_size * 2,
             PROT_EXEC | PROT_WRITE | PROT_READ ) != 0) {
    err_log("failed to modify mem props");
    exit(EXIT_FAILURE);
}

Next, we write the jump machine code. The assembly pseudo-code is as follows:

mov rax, &hook_func
jmp rax

Since the rax register is used to store return values in the C/C++ ABI and can be modified freely (it is caller-saved), we do not need to worry that modifying the rax register here will alter the program’s intended behavior.

The concrete implementation looks like this:

uint8_t hook_mcode[] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0};
*(uintptr_t*)(hook_code + 2) = (uintptr_t)hook_func;
memcpy(orig_func, hook_mcode, 12);

Finally, we have the complete code:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <unistd.h>
#include <sys/mman.h>

int func()
{
    printf("hello, beautiful world\n");
    return 0;
}

int hook_func()
{
    printf("farewell, cruel world\n");
    return 0;
}

void hook(void *orig_func, void *hook_func, uint8_t *mcode_backup)
{
    int page_size;
    void *aligned_addr;
    uint8_t hook_mcode[12] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00,
                              0x00, 0x00, 0x00, 0xFF, 0xE0};

    page_size = getpagesize();
    aligned_addr = (void *)(((uintptr_t)orig_func) & (~(page_size - 1)));
    if (mprotect(aligned_addr,
                 page_size * 2,
                 PROT_EXEC | PROT_WRITE | PROT_READ ) != 0) {
        fprintf(stderr, "failed to modify mem props\n");
        exit(EXIT_FAILURE);
    }
    *(uintptr_t*)(hook_mcode + 2) = (uintptr_t)hook_func;
    memcpy(mcode_backup, orig_func, 12);
    memcpy(orig_func, hook_mcode, 12);
}

void restore(void *func, uint8_t *mcode)
{
    memcpy(func, mcode, 12);
}

int main()
{
    uint8_t mcode_backup[12] = {0};
    func();
    hook(func, hook_func, mcode_backup);
    func();
    restore(func, mcode_backup);
    func();
    return 0;
}

The execution results are as follows:

hello, beautiful world
farewell, cruel world
hello, beautiful world

Here, the first line represents the output of the original function, the second line shows the output after being hooked, and the final line displays the output after restoration.


Mistivia - https://mistivia.com