You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行时本地函数Inline Hook实现报错求助

本地函数Inline Hook实现问题分析与修正

核心问题梳理

你的代码存在几个关键错误,导致Hook失败:

  • 函数地址错误偏移:本地函数不存在DLL导出的Thunk层,不需要读取开头字节计算偏移,直接使用testFunction的原始地址即可。
  • 调用约定不匹配:testFunction默认是__cdecl调用约定,但Hook函数和函数指针误用__stdcall,会造成栈不平衡引发崩溃。
  • 跳转地址计算错误:因原函数地址被错误偏移,导致Hook跳转和跳板跳转的地址计算全部失效。
  • 指令截断风险:硬复制5字节到跳板前,未确保原函数开头指令的完整性(简单场景下本地函数开头通常是完整的短指令,可直接处理)。

修正后的完整代码

#include <Windows.h>
#include <stdio.h>

// 统一使用__cdecl调用约定,匹配testFunction的默认约定
typedef int(__cdecl* tdOrigFunction)(int name);
tdOrigFunction testFunctionATrampoline;

int __cdecl HookedTestFunction(int name)
{
    printf("Hooked testFunction\n");
    // 调用跳板执行原函数逻辑
    int ret = testFunctionATrampoline(name);
    printf("原函数返回值:%d\n", ret);
    return 2;
}

int Error(const char* msg) {
    printf("%s (%u)\n", msg, GetLastError());
    return 1;
}

int __cdecl testFunction(int name) {
    printf("testFunction\n");
    printf("testFunction\n");
    printf("testFunction\n");
    printf("testFunction\n");
    printf("testFunction\n");
    printf("testFunction%d\n", name);
    return 1;
}

int main()
{
    BYTE* origFunctionAddress = (BYTE*)testFunction;
    BYTE* trampolineAddress = NULL;

    // 分配可执行的跳板内存
    trampolineAddress = (BYTE*)VirtualAlloc(NULL, 32, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (trampolineAddress == NULL) {
        return Error("Failed to allocate memory for trampoline");
    }

    // 1. 复制原函数开头5字节到跳板(确保原函数前5字节是完整指令,这里testFunction开头满足)
    memcpy(trampolineAddress, origFunctionAddress, 5);

    // 2. 在跳板末尾添加跳回原函数剩余部分的指令
    // 计算跳转偏移:原函数5字节后的地址 - 跳板地址 - 跳转指令长度(5)
    uintptr_t jumpBackOffset = (uintptr_t)(origFunctionAddress + 5) - (uintptr_t)(trampolineAddress + 5) - 5;
    *(BYTE*)(trampolineAddress + 5) = 0xE9; // JMP指令
    *(uintptr_t*)(trampolineAddress + 6) = jumpBackOffset;

    // 3. 修改原函数开头的内存保护属性,允许写入
    DWORD oldProtectVal;
    if (!VirtualProtect(origFunctionAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtectVal)) {
        return Error("Failed to change memory protection");
    }

    // 4. 在原函数开头写入跳转到Hook函数的指令
    uintptr_t jumpToHookOffset = (uintptr_t)HookedTestFunction - (uintptr_t)(origFunctionAddress + 5) - 5;
    *(BYTE*)origFunctionAddress = 0xE9; // JMP指令
    *(uintptr_t*)(origFunctionAddress + 1) = jumpToHookOffset;

    // 5. 恢复原函数内存保护属性
    VirtualProtect(origFunctionAddress, 5, oldProtectVal, &oldProtectVal);

    // 绑定跳板到函数指针
    testFunctionATrampoline = (tdOrigFunction)trampolineAddress;

    // 测试Hook效果
    int ret = testFunction(1);
    printf("Hook函数返回值:%d\n", ret);

    // 释放跳板内存(可选,程序结束前释放即可)
    VirtualFree(trampolineAddress, 0, MEM_RELEASE);

    return 0;
}

关键修改说明

  1. 统一调用约定:将函数指针和Hook函数都改为__cdecl,和本地函数的默认约定保持一致。
  2. 移除错误的地址偏移:直接使用testFunction的原始地址,不再处理多余的相对跳转计算。
  3. 正确计算跳转偏移:无论是跳板跳回原函数,还是原函数跳转到Hook函数,都基于正确的地址计算相对偏移。
  4. 内存操作优化:调整VirtualAlloc的大小更合理,确保跳板有足够空间存放指令。

内容的提问来源于stack exchange,提问作者houxinlin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:17:00