如何获取实例特定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
相关产品推荐
相关产品推荐

