论坛首页 安全编程开发区 阅读主题

[原创]游戏逆向中常用的Hook技术

436 浏览 2 回复
#1 楼主 2026-06-01 21:08:48
依旧是复习过去学过的知识。本文的Hook技术对应内容请看侧边目录。一、内联Hook(InlineHook)(1)inline hook 是什么当我们想要拦截现有运行中的进程内某个现有的汇编函数体,最常用的办法就是 inline hook。它可以在权限允许内,通过修改程序运行中的内存代码段汇编,以达到拦截任何函数的目的,包括系统api(只限非内核态的函数体,要hook内核函数需要进内核态),以及程序内部现有的任何函数体。比如想拦截系统APICreateFileW的调用,修改原调用参数并继续执行CreateFileW原函数逻辑,获得返回值,或者直接拦截返回NULL失败,或者拦截程序本身代码汇编的函数体,用 inline hook都可以做到。具体步骤如下:备份原始指令:在目标函数入口处,保存前几个字节(通常是 5 到 12 字节)。写入跳转指令:将目标函数的开头替换为一条跳转指令(通常是 JMP)。执行自己的逻辑:程序运行到目标函数时,会直接跳到你写的“钩子函数”里。跳回原函数:如果你还想让原程序继续运行,就在执行完你的逻辑后,先执行备份的原始指令,再跳回目标函数的后续位置。(2)示例代码解析
x86示例代码如下,需要注意的是编译器如果发现 OriginalHelloWorldFunction 内容很短,且在同一个文件里,它在编译 main 函数时,将不会去执行 CALL 指令,而是直接把那句 printf 的内容复制到了 main 里面。所以需要在函数定义前加上 __declspec(noinline),通知编译器不要内联这个函数。#include <windows.h>
#include <stdio.h>

// --- 被 HOOK 的目标函数 ---

__declspec(noinline) void OriginalHelloWorldFunction() {
    printf("[执行] 原始的 Hello World 函数\n");

// --- 我们自定义的钩子函数 (Hook 函数) ---
void MyCustomHelloWorldFunction()
    printf("[拦截] 成功进入了我们的钩子函数!\n");

// --- 核心:执行 Inline Hook 的逻辑 ---
void SetupInlineHookJmp32()
    // 1. 定义跳转机器码结构 (E9 + 4字节偏移量)
    // 机器码格式:E9 XX XX XX XX

    // 2. 计算跳转偏移量 (公式:目标地址 - 原地址 - 指令长度)
    // 注意:跳转是相对于当前指令下一条地址开始计算的
    DWORD RelativeOffset = (DWORD)MyCustomHelloWorldFunction - (DWORD)OriginalHelloWorldFunction - 5;

    // 将计算好的 4 字节偏移填充到机器码中
    *(DWORD*)(JumpInstruction + 1) = RelativeOffset;

    // 3. 修改目标内存属性为“可读写执行”,否则修改代码会引发崩溃 (Access Violation)
    DWORD OldMemoryProtection;
    VirtualProtect(OriginalHelloWorldFunction, 5, PAGE_EXECUTE_READWRITE, &OldMemoryProtection);

    // 4. 正式写入机器码 (覆盖原函数开头的 5 个字节)
    memcpy(OriginalHelloWorldFunction, JumpInstruction, 5);

    // 5. 恢复内存原始保护属性 (养成良好的安全习惯)
    VirtualProtect(OriginalHelloWorldFunction, 5, OldMemoryProtection, &OldMemoryProtection);

    printf("[系统] Inline Hook 已部署完毕。\n");

int main()
    printf("====== 32位 Inline Hook 测试开始 ======\n\n");

    // 第一步:测试 Hook 之前的函数行为
    printf("1. 尝试直接调用函数(此时未 Hook):\n");
    // 注意:这里如果先调用,可能会被编译器内联,测试建议直接开始 Hook
    OriginalHelloWorldFunction();
    // 第二步:部署 Hook
    SetupInlineHookJmp32();

    // 第三步:再次调用原函数名,观察输出
    printf("\n2. 再次尝试调用原函数:\n");
    OriginalHelloWorldFunction();

    printf("\n====== 测试结束 ======\n");
    getchar(); // 暂停程序查看结果
    return 0;
}x64示例代码:#include <windows.h>
#include <stdio.h>

// 告诉编译器不要内联这些函数
__declspec(noinline) void OriginalHelloWorldFunction()
    printf("[执行] 原始的 x64 Hello World 函数\n");

void MyCustomHelloWorldFunction()
    printf("[拦截] 成功进入了 x64 钩子函数!\n");

void SetupInlineHookX64()
    // 1. 定义 12 字节的绝对跳转指令
    // 48 B8 [8字节地址] FF E0 (jmp rax)
    BYTE JumpInstruction[12] = { 
        0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, <address>
        0xFF, 0xE0                         // jmp rax

    // 2. 将 64 位绝对地址写入机器码
    void* TargetAddress = &MyCustomHelloWorldFunction;
    memcpy(&JumpInstruction[2], &TargetAddress, 8);

    // 3. 修改内存属性(注意:这次需要 12 字节空间)
    DWORD OldMemoryProtect

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290783.htm
#2 2026-06-01 21:08:48
tql
#3 2026-06-01 21:08:48
jiayou

请登录后参与讨论

立即登录 注册账号