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

如何获取实例特定Detour指针?优化函数调用统计方案咨询

基于MS Detours的可扩展高性能函数调用统计实现方案

当前使用__COUNTER__维护全局统计数组的方案在钩子数量增多时,会出现维护成本高、易出错的问题。以下是优化后的实现,兼顾可扩展性、高性能和可维护性:

核心思路

将每个钩子的原函数指针、钩子实现、统计数据绑定为一个独立单元,通过宏简化重复代码,避免手动管理全局数组索引;利用Detours的事务API批量处理钩子,提升性能。

实现代码

1. 定义统计结构与工具宏

#include <atomic>
#include <string_view>
#include <Windows.h>
#include <detours.h>

// 线程安全的函数统计结构
struct FuncStats {
    std::string_view funcName;
    std::atomic<uint64_t> callCount = 0;

    explicit FuncStats(std::string_view name) : funcName(name) {}
};

// 声明钩子相关的原函数指针、统计实例、钩子函数的宏
#define DECLARE_HOOKED_FUNC(ret_type, calling_conv, func_name, ...) \
    using True_##func_name = ret_type (calling_conv *)(__VA_ARGS__); \
    static True_##func_name True_##func_name##_Ptr = nullptr; \
    static FuncStats func_name##_Stats{#func_name}; \
    ret_type calling_conv Hooked_##func_name(__VA_ARGS__)

// 统计调用次数的宏(直接引用当前钩子的静态统计实例)
#define COUNT_CALL() \
    func_name##_Stats.callCount.fetch_add(1, std::memory_order_relaxed)

// 注册钩子到全局列表的宏
#define REGISTER_HOOK(func_name) \
    { reinterpret_cast<void**>(&True_##func_name##_Ptr), reinterpret_cast<void*>(Hooked_##func_name), &func_name##_Stats }

2. 定义钩子函数

新增钩子只需复制以下模板修改函数名和参数即可:

// 示例:Hook GetNativeSystemInfo
DECLARE_HOOKED_FUNC(void, WINAPI, GetNativeSystemInfo, LPSYSTEM_INFO lpSystemInfo)
{
    COUNT_CALL();
    // 调用原函数
    return True_GetNativeSystemInfo_Ptr(lpSystemInfo);
}

// 可在此处添加更多钩子,比如:
// DECLARE_HOOKED_FUNC(BOOL, WINAPI, CreateFileW, LPCWSTR lpFileName, DWORD dwDesiredAccess, ...)
// {
//     COUNT_CALL();
//     return True_CreateFileW_Ptr(lpFileName, dwDesiredAccess, ...);
// }

3. 全局钩子管理与加载/卸载逻辑

// 全局钩子列表:新增钩子只需在此添加一行REGISTER_HOOK
struct HookEntry {
    void** origFuncPtr;
    void* hookedFunc;
    FuncStats* stats;
};

static const HookEntry g_hooks[] = {
    REGISTER_HOOK(GetNativeSystemInfo),
    // REGISTER_HOOK(CreateFileW), // 新增钩子时取消注释即可
};

const size_t g_hookCount = std::size(g_hooks);
HMODULE kern32Lib = nullptr;

void LoadDetours()
{
    DPRINTF(DLOG_INFO, "Detouring functions.\n");

    kern32Lib = LoadLibraryW(L"kernel32.dll");
    if (!kern32Lib)
        return;

    // 批量解析所有原函数地址
    for (const auto& hook : g_hooks) {
        *hook.origFuncPtr = GetProcAddress(kern32Lib, hook.stats->funcName.data());
    }

    // 用Detours事务批量处理钩子,提升性能
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());

    for (const auto& hook : g_hooks) {
        if (*hook.origFuncPtr) {
            DetourAttach(hook.origFuncPtr, hook.hookedFunc);
        }
    }

    DetourTransactionCommit();
}

void UnloadDetours()
{
    DPRINTF(DLOG_INFO, "Restoring functions.\n");

    // 批量恢复钩子
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());

    for (const auto& hook : g_hooks) {
        if (*hook.origFuncPtr) {
            DetourDetach(hook.origFuncPtr, hook.hookedFunc);
        }
    }

    DetourTransactionCommit();

    FreeLibrary(kern32Lib);
}

4. 扩展:获取所有统计数据

void PrintAllFunctionStats()
{
    for (const auto& hook : g_hooks) {
        DPRINTF(DLOG_INFO, "Function: %s | Call Count: %llu\n", 
            hook.stats->funcName.data(), 
            hook.stats->callCount.load(std::memory_order_relaxed));
    }
}

方案优势

  • 可扩展性:新增钩子仅需两步(定义钩子函数+注册到列表),无需手动维护全局数组索引,完全避免索引错位问题。
  • 高性能:
    • 调用计数使用std::memory_order_relaxed,在保证计数准确的前提下最大化性能;
    • 利用Detours事务API批量处理钩子,减少内核态切换开销,尤其适合数百个钩子的场景。
  • 易维护:每个钩子的所有相关逻辑(原函数指针、统计、实现)集中在一起,函数名通过宏自动生成,避免拼写错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:45:54