如何实现函数地址XOR加密存储,规避逆向工程分析?
如何隐藏函数地址的异或加密逻辑避免IDA识别
你的问题根源在于编译期/链接期的符号保留与优化:
- 你使用了
constexpr函数,且传入的salt是编译期字面量,函数地址在链接阶段确定后,编译器会将EncryptFunctionCall((uintptr_t)&HiddenFunctionCall, 0x999)保留为符号化的表达式(原函数地址异或salt),IDA能直接解析这种符号关联,导致加密逻辑完全暴露。 - 你需要让加密后的地址以数值形式存储在内存中,而非保留符号化的运算关系。
方案1:运行时动态计算加密地址(最易实现)
去掉constexpr修饰,将salt的计算改为运行时动态生成,加入无意义运算干扰编译器优化,避免符号关联:
#include <iostream> using namespace std; __forceinline uintptr_t EncryptFunctionCall(uintptr_t PointerToFunction, uintptr_t Salt) { return PointerToFunction ^ Salt; } __declspec(noinline) void HiddenFunctionCall() { cout << "Hello\n"; } // 动态生成salt,加入干扰逻辑避免编译器优化 uintptr_t GetDynamicSalt() { uintptr_t salt = 0x999; // 加入无意义的异或运算,干扰编译器对salt的常量推导 salt ^= reinterpret_cast<uintptr_t>(&GetDynamicSalt); salt ^= 0x123456; salt ^= reinterpret_cast<uintptr_t>(&GetDynamicSalt); return salt; } __declspec(noinline) void MainThread() { uintptr_t encryptedAddr = EncryptFunctionCall((uintptr_t)&HiddenFunctionCall, GetDynamicSalt()); cout << encryptedAddr << "\n"; // 解密并调用函数 using FuncPtr = void(*)(); FuncPtr func = reinterpret_cast<FuncPtr>(encryptedAddr ^ GetDynamicSalt()); func(); }
原理:运行时计算的salt无法被编译器视为常量,加密操作会被编译为普通运行时运算,IDA只能看到内存中的数值,无法关联到原函数地址和salt。
方案2:静态变量预计算加密地址(适合固定函数)
利用C++11的lambda表达式在静态变量构造时计算加密地址,结合编译时间做额外混淆,让每次编译后的加密值都不同:
#include <iostream> using namespace std; __declspec(noinline) void HiddenFunctionCall() { cout << "Hello\n"; } // 静态存储加密后的地址,程序启动时计算 static const uintptr_t kEncryptedFuncAddr = [](){ uintptr_t salt = 0x999; // 加入编译时间作为混淆因子,每次编译salt都会变化 salt += __TIME__[2]; return (uintptr_t)&HiddenFunctionCall ^ salt; }(); __declspec(noinline) void MainThread() { cout << kEncryptedFuncAddr << "\n"; // 解密并调用 uintptr_t salt = 0x999; salt += __TIME__[2]; using FuncPtr = void(*)(); FuncPtr func = reinterpret_cast<FuncPtr>(kEncryptedFuncAddr ^ salt); func(); }
原理:静态变量kEncryptedFuncAddr在程序启动时就被初始化为加密后的数值,内存中存储的是纯数值,IDA不会显示异或运算关系;结合__TIME__宏可以让每次编译的加密结果不同,进一步提升逆向难度。
方案3:内联汇编直接生成加密地址(最彻底)
绕开C++编译器的符号优化,直接用汇编指令计算并存储加密地址,IDA只能解析汇编指令,无法识别原函数和salt的关联:
#include <iostream> using namespace std; __declspec(noinline) void HiddenFunctionCall() { cout << "Hello\n"; } __declspec(noinline) void MainThread() { uintptr_t encryptedAddr; // 用汇编直接计算加密地址 __asm { mov rax, offset HiddenFunctionCall xor rax, 0x999 mov encryptedAddr, rax } cout << encryptedAddr << "\n"; // 解密并调用函数 __asm { mov rax, encryptedAddr xor rax, 0x999 call rax } }
原理:汇编指令直接操作寄存器计算加密地址,编译器不会保留任何符号化的运算关系,IDA只能看到寄存器操作,无法直接关联到原函数地址和salt。
内容的提问来源于stack exchange,提问作者YoAvrageBiTz
相关产品推荐
相关产品推荐

