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

C++修复GetGameFunctionAddress实现64位模块基址获取兼容

64位EXE模块基址获取兼容修复方案

原有实现直接依赖GetModuleHandleA获取EXE模块基址,在64位进程下会因为文件名匹配规则、路径格式、参数大小写等问题返回空值,导致后续地址计算错误、Hook崩溃。以下是可直接替换的兼容实现,同时支持32/64位进程场景。

修复后完整代码

#include <windows.h>
#include <tlhelp32.h>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cctype>

// 内部工具:不区分大小写匹配模块名,自动忽略路径前缀
static bool MatchModuleName(const char* loadedModulePath, const char* targetExePath)
{
    if (!loadedModulePath || !targetExePath) return false;

    // 提取路径末尾的纯文件名
    auto extractFileName = [](const char* fullPath) -> const char* {
        const char* split1 = strrchr(fullPath, '\\');
        const char* split2 = strrchr(fullPath, '/');
        const char* splitPos = split1 > split2 ? split1 : split2;
        return splitPos ? splitPos + 1 : fullPath;
    };

    const char* loadedName = extractFileName(loadedModulePath);
    const char* targetName = extractFileName(targetExePath);

    // 逐字符小写比对,适配Windows文件名大小写不敏感规则
    while (*loadedName && *targetName)
    {
        if (tolower((unsigned char)*loadedName) != tolower((unsigned char)*targetName))
            return false;
        loadedName++;
        targetName++;
    }
    return *loadedName == 0 && *targetName == 0;
}

__int64 GetGameFunctionAddress(std::string GameFileExe, std::string Address)
{
    uintptr_t moduleBase = 0;
    // 通过进程模块快照枚举基址,32/64位通用
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, GetCurrentProcessId());
    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32A moduleEntry;
        moduleEntry.dwSize = sizeof(moduleEntry);
        if (Module32FirstA(hSnapshot, &moduleEntry))
        {
            do
            {
                if (MatchModuleName(moduleEntry.szModule, GameFileExe.c_str()) 
                    || MatchModuleName(moduleEntry.szExePath, GameFileExe.c_str()))
                {
                    moduleBase = (uintptr_t)moduleEntry.modBaseAddr;
                    break;
                }
            } while (Module32NextA(hSnapshot, &moduleEntry));
        }
        CloseHandle(hSnapshot);
    }

    // 兜底逻辑:快照枚举失败时重试GetModuleHandleA
    if (moduleBase == 0)
    {
        HMODULE hModule = GetModuleHandleA(GameFileExe.c_str());
        if (!hModule)
        {
            // 传入全路径时自动提取文件名重试
            const char* split1 = strrchr(GameFileExe.c_str(), '\\');
            const char* split2 = strrchr(GameFileExe.c_str(), '/');
            const char* splitPos = split1 > split2 ? split1 : split2;
            if (splitPos) hModule = GetModuleHandleA(splitPos + 1);
        }
        moduleBase = (uintptr_t)hModule;
    }

    // 基址获取失败直接返回空,避免生成野地址
    if (moduleBase == 0) return 0;

    // 用strtoull解析偏移,兼容64位大地址场景,避免strtoul的32位截断问题
    uintptr_t funcRva = (uintptr_t)std::strtoull(Address.c_str(), NULL, 16);
    return (__int64)(moduleBase + funcRva);
}

关键修复说明

  • 替换原有分架构分支的实现,统一用模块快照枚举逻辑获取基址,加TH32CS_SNAPMODULE32标志支持64位进程下的模块枚举场景,完全规避64位下GetModuleHandleA的匹配失败问题
  • 新增文件名自动匹配逻辑:自动剥离传入路径的目录前缀、不区分大小写比对模块名,解决传入全路径、文件名大小写不一致导致的查找失败
  • 偏移解析从strtoul替换为strtoull,解决64位下RVA偏移超过32位长度时的截断问题
  • 增加多层兜底和空值校验,基址获取失败时直接返回0,避免计算出非法内存地址导致Detours Hook时进程崩溃
  • 原有调用逻辑、Detours对接代码不需要做任何修改,直接替换函数实现即可生效

使用注意事项

64位下使用Detours Hook时,需要确认返回的函数地址所在内存页具备可执行权限,且目标函数长度大于Detours要求的最小跳转桩长度(通常大于5字节),否则会出现Hook失败或内存访问异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:57:20