如何用C++模板实现Detours钩子的宏序列逻辑?
用C++模板替代Detours钩子宏实现方案
现有宏实现代码
钩子函数定义
SETUP_ORIG_FUNC(string_copy_n); char* __cdecl HOOK(string_copy_n)(char* dst, const char* src, size_t size) { if (src && dst && size && !convert_ulong_lock && Config::Setting::enable_translation) { auto tstr = Dictionary::GetSingleton()->Get(src); if (tstr) { return ORIGINAL(string_copy_n)(dst, tstr.value().c_str(), tstr.value().size()); } } return ORIGINAL(string_copy_n)(dst, src, size); }
宏定义与类型别名
#define SETUP_ORIG_FUNC(fn_name) fn_name fn_name##_orig = (fn_name)(module_handle + Config::Offset::fn_name); #define HOOK(fn_name) fn_name##_hook #define ORIGINAL(fn_name) fn_name##_orig typedef char*(__cdecl* string_copy_n)(char* dst, const char* src, size_t size);
模板化解决方案
核心模板类实现
这个模板类封装了原始函数指针的初始化与调用,兼顾类型安全与效率:
#include <utility> // 用于std::forward template<typename FuncType, uintptr_t Offset> struct OriginalFunction { // 静态存储原始函数指针,仅初始化一次 static inline FuncType ptr = nullptr; // 初始化函数:从模块基址+偏移量获取函数指针 static void Init(void* module_handle) { if (!ptr) { uintptr_t module_base = reinterpret_cast<uintptr_t>(module_handle); ptr = reinterpret_cast<FuncType>(module_base + Offset); } } // 调用原始函数,完美转发参数 template<typename... Args> static auto Call(Args&&... args) -> decltype(ptr(std::forward<Args>(args)...)) { return ptr(std::forward<Args>(args)...); } };
使用方式
- 定义函数类型(替代原typedef):
using string_copy_n = char*(__cdecl*)(char* dst, const char* src, size_t size);
- 初始化原始函数指针:
在模块加载完成后调用Init:
OriginalFunction<string_copy_n, Config::Offset::string_copy_n>::Init(module_handle);
- 编写钩子函数:
char* __cdecl string_copy_n_hook(char* dst, const char* src, size_t size) { if (src && dst && size && !convert_ulong_lock && Config::Setting::enable_translation) { auto tstr = Dictionary::GetSingleton()->Get(src); if (tstr) { return OriginalFunction<string_copy_n, Config::Offset::string_copy_n>::Call( dst, tstr.value().c_str(), tstr.value().size() ); } } return OriginalFunction<string_copy_n, Config::Offset::string_copy_n>::Call(dst, src, size); }
简化调用的优化
如果觉得OriginalFunction<...>::Call太长,可以用一个简洁的宏辅助(仅用于简化书写,不破坏类型安全):
#define CALL_ORIGINAL(FuncType, Offset) OriginalFunction<FuncType, Offset>::Call
钩子函数里的调用就可以简化为:
return CALL_ORIGINAL(string_copy_n, Config::Offset::string_copy_n)(dst, src, size);
原模板尝试的问题分析
你之前的模板代码错误在于:直接将module_handle + offset转换为返回值类型Ret,而非函数指针类型。函数调用需要的是对应签名的函数指针,而非返回值类型的实例,因此会导致编译错误。
模板实现的优势
- 类型安全:编译时就能检查函数签名是否匹配,避免宏拼接带来的拼写错误或类型不匹配问题
- 可维护性:模板逻辑集中,修改时只需调整模板类,无需逐个修改宏调用
- 效率:静态指针仅初始化一次,调用时无额外开销,和原宏实现效率一致
内容的提问来源于stack exchange,提问作者shevernitskiy
相关产品推荐
相关产品推荐

