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

基础篡改保护失效求助:篡改licenseCheck后仍校验通过

问题分析与修复方案

看起来你遇到的核心问题是哈希校验的范围根本没覆盖到你想保护的代码,而且编译器的优化让你的BEGIN和END函数失去了作为代码边界的作用,导致篡改后哈希值毫无变化。下面一步步拆解问题并给出修复方案:

1. 核心问题:BEGIN/END无法正确标记代码范围

你的代码里BEGIN()和END()是空函数,在Visual Studio的默认编译设置下(尤其是Release模式),编译器会直接优化掉这两个空函数——要么把它们的地址合并成同一个,要么直接从二进制中移除。这就导致hash((UINT*)BEGIN, (UINT*)END)实际上是在计算一个空范围的哈希,结果永远不会变,自然检测不到篡改。

另外,即使编译器保留了这两个函数,C++标准也不保证函数在二进制中的布局和源代码顺序一致,licenseCheck和hash完全可能被放在BEGIN和END之外的内存区域,哈希根本碰不到它们。

2. 次要问题:无效的指纹代码

你添加的汇编片段完全没有作用:

lea ebx, d
mov ebx, 0x050b072b

这段代码只是把d的地址加载到ebx,然后立刻把ebx覆盖成一个立即数,完全没修改d的值。这段代码对程序逻辑没有任何影响,编译器甚至可能直接优化掉它,自然也起不到指纹的作用。

修复方案

方案一:手动指定要保护的函数范围

如果你只想保护licenseCheck函数,直接用它的起始地址和一个手动标记的结束位置:

首先,修改licenseCheck并添加结束标记函数:

__declspec(noinline) void licenseCheckEndMarker() {}

bool licenseCheck() {
    std::cout << "Enter license: ";
    std::string license;
    std::cin >> license;
    volatile DWORD d;
    // 修复指纹代码:实际修改d的值
    __asm {
        lea ebx, d
        mov [ebx], 0x050b072b  // 把立即数写入d的内存地址
    }
    return license.compare(correctlicense) == 0;
}

然后修改main中的哈希计算,直接覆盖licenseCheck到licenseCheckEndMarker的范围:

UINT uHash = hash((UINT*)licenseCheck, (UINT*)licenseCheckEndMarker);

给licenseCheck和licenseCheckEndMarker加上__declspec(noinline),防止编译器把它们内联或者重排位置。

方案二:使用自定义节来标记代码范围

更可靠的方式是把要保护的代码放到一个自定义的节(Section)里,然后通过PE头获取这个节的起始和结束地址:

  1. 用#pragma section定义一个自定义节:
#pragma section("PROTECTED", read, execute)
  1. 把要保护的函数放到这个节里:
__declspec(allocate("PROTECTED")) __declspec(noinline) bool licenseCheck() {
    // 函数内容不变(记得修复指纹代码)
}

__declspec(allocate("PROTECTED")) __declspec(noinline) UINT hash(UINT *beginAddress, UINT *endAddress) {
    // 哈希函数内容不变
}
  1. 添加获取节地址的代码(需要包含Windows.h):
void GetSectionInfo(const char* sectionName, UINT** outStart, UINT** outEnd) {
    HMODULE hModule = GetModuleHandle(NULL);
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew);
    
    for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) {
        PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((BYTE*)&pNtHeaders->OptionalHeader + pNtHeaders->FileHeader.SizeOfOptionalHeader + i * sizeof(IMAGE_SECTION_HEADER));
        if (strcmp((char*)pSection->Name, sectionName) == 0) {
            *outStart = (UINT*)((BYTE*)hModule + pSection->VirtualAddress);
            *outEnd = (UINT*)((BYTE*)hModule + pSection->VirtualAddress + pSection->Misc.VirtualSize);
            return;
        }
    }
    *outStart = *outEnd = nullptr;
}
  1. 在main中使用这个函数获取哈希范围:
UINT* protectStart = nullptr;
UINT* protectEnd = nullptr;
GetSectionInfo("PROTECTED", &protectStart, &protectEnd);

if (!protectStart || !protectEnd) {
    std::cout << "Failed to find protected section!" << std::endl;
    system("PAUSE");
    return 0;
}

UINT uHash = hash(protectStart, protectEnd);

额外建议:增强哈希算法

你当前用的异或哈希碰撞概率极高,很容易被攻击者绕过。建议换成更可靠的算法,比如CRC32:

UINT crc32(UINT* beginAddress, UINT* endAddress) {
    UINT crc = 0xFFFFFFFF;
    BYTE* ptr = (BYTE*)beginAddress;
    BYTE* endPtr = (BYTE*)endAddress;
    while (ptr < endPtr) {
        crc ^= *ptr++;
        for (int i = 0; i < 8; i++) {
            crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
        }
    }
    return ~crc;
}

编译注意事项

  • 关闭编译器的函数重排优化(在Visual Studio的项目属性 -> C/C++ -> 优化 -> 禁用"函数级链接"和"优化代码",或者用/OPT:NOICF和/OPT:NOREF链接器选项),防止编译器合并相同函数或移除未引用的代码。
  • 尽量用Release模式编译(关闭调试信息),避免Debug模式下插入的额外调试指令干扰哈希计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:49:37