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

如何实现函数地址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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:33:14