打印 上一主题 下一主题

黑客软件破解九:软件水印、完整性校验与反篡改——保护代码不被修改的技术与绕过方法

[复制链接]
跳转到指定楼层
楼主
发表于 昨天 16:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
查看 : 5|回复 : 0
黑客软件破解深度论文系列之九:软件水印、完整性校验与反篡改——保护代码不被修改的技术与绕过方法
摘要:软件完整性校验是抵御破解的最重要防线之一,旨在检测程序代码是否被篡改。本文以超过一万六千字的篇幅,系统讲解各类完整性校验技术,包括静态校验和(Checksum)、CRC、哈希校验、数字签名、动态内存校验、反调试嵌入、自我修复机制等;并详细剖析黑客绕过这些保护的多种方法,包括校验值Patch、动态跳过校验函数、时间差攻击、内存镜像替换、完整性校验的静默失效等。文章包含四个完整的实战案例,展示从简单校验和的绕过到高级数字签名验证的拆除。高频使用“黑客”、“破解软件”、“完整性校验”、“Checksum”、“CRC”、“反篡改”、“自校验”等关键词。

第一章 软件完整性校验的本质与价值1.1 什么是不完整校验
软件完整性校验(Integrity Check)是程序在运行时检测自身是否被修改的一种技术。其核心思想是:在代码的某些关键位置嵌入校验逻辑,计算自身代码块或资源的哈希值(CRC、MD5、SHA等),与预存的标准值比对,如果不一致,则认为程序已被篡改(被破解),采取相应行动——退出、弹出警告、限制功能或格式化硬盘(后者极其罕见且不道德,通常仅存在于恶意软件中)。
完整性校验在防破解中的定位:
  • 代码签名(Code Signing):防止静态篡改(二进制文件被修改后签名失效)
  • 运行时校验:防止内存中的动态篡改(已有签名验证通过,但内存中被修改)

完整性校验并非用来直接阻止破解,而是增加破解成本——使简单的“改一个字节就破解”的行为失效。黑客必须定位并移除所有的校验点,才能完成稳定的破解。
1.2 完整性校验的类型


[td]
类型
校验对象
校验时机
破解难度
静态文件校验磁盘上的.exe/.dll文件程序启动时★★☆☆☆
内存校验加载到内存中的代码段程序运行期间(定时)★★★☆☆
增量校验仅校验关键代码段调用关键功能前★★★★☆
交互式校验多个模块互相校验随机时机★★★★☆
数字签名校验整个文件的Authenticode签名启动时/动态加载时★★★★☆

主动校验方式:不直接退出,而是随机出现功能异常、数据损坏,让破解者难以定位问题。
被动校验方式:退出或弹窗,易于定位(通过错误消息搜索)。
1.3 完整性校验与反调试的关系
完整性校验与反调试如影随形。两者往往结合使用:
  • 反调试阻止动态分析;
  • 完整性校验阻止静态修改。

黑客必须同时绕过两种保护才能成功破解。更棘手的是,某些校验代码只在被调试时触发(反反调试无效),或在非预期环境中执行(如检测虚拟机)。

第二章 静态校验和(Checksum/CRC)2.1 原理与实现
最简单的完整性校校验和(Checksum)——程序将其某段代码(通常是.text节)的每个字节累加,计算出一个校验值,与嵌入在程序中的预存值比较。
示例代码(C语言):
[size=12.573px]c



BOOL CheckIntegrity() {    DWORD checksum = 0;    // 假设代码段从0x00401000到0x00408FFF    for (DWORD addr = 0x00401000; addr < 0x00409000; addr++) {        checksum += *(BYTE*)addr;    }    // 预存校验值0x4A2F1B8C(从编译时计算的)    return (checksum == 0x4A2F1B8C);}

嵌入预存值的方法:
  • 在编译/链接后,单独运行一个工具计算该段的校验和,将值写入文件的某个固定偏移处(如.rdata节)。
  • 程序运行时读取该值并与计算出的比较。

2.2 黑客的绕过方法
方法一:Patch校验值本身
既然程序在比较时读取嵌入的预存值,黑客可以直接修改这个预存值,使其等于修改后的校验和。
操作步骤:
  • 在IDA中找到校验值存储的地址(搜索4A2F1B8C这个常数)。
  • 修改二进制中的这个值,改为修改后的校验和。
  • 程序比较时通过校验。

方法二:跳过校验调用
将调用CheckIntegrity的指令NOP掉,或将条件跳转改为强制跳转到“通过”分支。
在汇编中:
[size=12.573px]assembly



call CheckIntegritytest eax, eaxjz fail...fail:    push 0    call ExitProcess

将jz fail改为jmp continue。
2.3 多重校验和的对抗
高级保护会在多处嵌入不同的校验和,甚至在不同时机校验不同代码段。黑客需要使用脚本自动化处理。
IDAPython脚本示例:收集所有调用校验函数的位置并自动Patch:
[size=12.573px]python



import idcimport idautils# 查找所有对CheckSumFunc的调用func_addr = idc.get_name_ea_simple("CheckIntegrity")for xref in idautils.XrefsTo(func_addr, flags=0):    call_addr = xref.frm    # 在调用指令后找到jnz/jz指令    next_addr = idc.next_head(call_addr, idc.get_segm_end(call_addr))    mnem = idc.print_insn_mnem(next_addr)    if mnem in ["jz", "je", "jnz", "jne":        # 修改为jmp(0xEB)        idc.patch_byte(next_addr, 0xEB)

2.4 实战案例(一):绕过CRC校验
目标程序CRC_Protected.exe在启动时校验.text段的CRC32,发现修改后退出。
分析:
  • 加载到x64dbg,在ExitProcess下断点。
  • 运行,程序在ExitProcess中断,查看调用栈,回溯到校验函数。
  • 在校验后找到cmp eax, [checksum_value]和jnz指令。
  • 将jnz改为nop,或修改eax寄存器的值为预存值。

制作永久补丁:
  • 记录修改地址0x00401A3B:75 0C(jnz)→ 90 90(nop)
  • 写入x64dbg的Patch,保存到新文件。

验证:修改后的程序即使改变了任何指令,也不会退出,因为校验分支已被删除。

第三章 哈希/加密校验3.1 MD5/SHA校验的实现
更高级的完整性校验使用加密哈希函数(MD5、SHA-1、SHA-256)。哈希函数的特性:即使输入仅改变一个比特,输出哈希值也完全不同。这使得黑客无法像CRC那样简单地调整预存值来匹配任意修改。
实现方式:
  • 编译程序后,计算整个.text节的SHA-256,将哈希值硬编码到程序中。
  • 运行时,程序重新计算.text节的SHA-256,与硬编码值比较。
  • 由于哈希函数不可逆,黑客不能随意更改代码同时匹配哈希值。

3.2 绕过哈希校验的方法
方法一:删除校验代码
直接定位哈希比较的函数,将其整体删除(NOP掉整个函数体)。
方法二:修改哈希值存储
虽然哈希值不可逆,但黑客可以计算修改后代码的哈希值,然后用这个新哈希值替换硬编码的原始哈希值。只需要在修改代码后,用Python或命令行计算新哈希,再用十六进制编辑器更新二进制。
方法三:运行时Hook哈希计算
使用Frida或DLL注入,hook哈希函数的输入或输出。例如,HookSHA256_Update函数,阻止其读取修改过的代码段,或强制返回原始哈希值。
[size=12.573px]cpp



// Hook SHA256_Final,强制返回原始哈希void WINAPI Hooked_SHA256_Final(SHA256_CTX* ctx, BYTE* hash) {    // 预先存储的原始哈希值    BYTE originalHash[32 = {...};    memcpy(hash, originalHash, 32);}

3.3 加密校验的强度分析
因为哈希函数的不可逆性,直接破解比简单校验和困难一个数量级,但仍然不是不可绕过。所有校验的共同弱点:校验逻辑本身也是代码,也可以被修改或绕过。
只要黑客能够定位校验点,无论是校验和还是哈希,都可以通过NOP或强制跳转的方式绕过。
唯一无法绕过的校验逻辑:如果校验的结果被用于后续加密解密(例如解密关键算法),那么篡改会导致解密失败,而无法通过简单的跳转绕过——因为程序逻辑本身依赖于正确的解密数据。这是“数据驱动校验”比“控制流校验”更强的体现。

第四章 内存校验与动态完整性检查4.1 原理与动机
静态文件校验只能防止磁盘上的文件被修改。然而,许多破解补丁(Loader)不修改磁盘上的文件,而是在程序启动后、内存中修改指令(内存补丁)。这些修改不会影响文件校验,但会改变内存中的代码。
内存校验旨在检测这类内存修改。
实现方式:
  • 在程序运行过程中,定时或随机地(使用定时器SetTimer或独立线程)重新计算关键代码段的内存哈希,与启动时保存的哈希值对比。
  • 一旦发现不一致(即被破解补丁修改),立刻恢复原始字节(自我修复)或退出。

4.2 黑客的绕过方法
方法一:Hook定时器/线程函数
找到创建校验线程的代码(CreateThread、SetTimer),将其暂停或NOP掉。
在x64dbg中:
  • 在CreateThread下断点,运行到校验线程创建时,修改线程入口点,使其立即返回。
  • 或者直接挂起该线程(SuspendThread)。

方法二:修补校验数据源
Hook哈希计算函数,使其读取的内存地址不是被修改的代码区域,而是一份未修改的副本。
方法三:时间差攻击(TOCTOU)
如果程序在启动时一次性校验,然后保存哈希值,之后定期比较。黑客可以在启动时确保程序未修改(使用Loader先启动,再将修改注入),或者在比较间隙短暂恢复原始代码。
时间差攻击的实现(使用VEH异常处理):
  • 将自己编写的代码放在INT3异常处理中。
  • 当程序要校验某段内存时,触发INT3异常。
  • 在异常处理函数中临时将修改过的代码恢复为原始代码。
  • 让程序完成校验。
  • 立即再次修改。

4.3 实战案例(二):DLL注入与内存校验绕过
目标D3DGame.exe使用内存校验,每隔5秒校验收款代码段。
分析:在x64dbg中查看CreateThread的调用,发现一个线程每5秒调用CheckMemory。
绕过方法:使用DLL注入,HookSleep函数(校验线程中使用Sleep(5000)),改变其行为:
[size=12.573px]cpp



// Hook Sleep,使校验线程永远不运行校验代码void WINAPI Hooked_Sleep(DWORD dwMilliseconds) {    // 如果调用者是校验线程,直接返回    if (GetCurrentThreadId() == g_checkThreadId) {        return;    }    // 其他情况正常睡眠    Original_Sleep(dwMilliseconds);}

注入DLL后,校验线程的Sleep返回后本该调用CheckMemory,但Hook后的Sleep直接返回,CheckMemory被跳过。内存校验失效。

第五章 自我修复(Self-Healing)机制5.1 原理
自我修复是指程序检测到被篡改后,不是退出,而是自动恢复被修改的字节为原始值。这对于黑客尤其棘手:修改后的字节可能在几毫秒内被还原,导致破解临时失效。
实现方式:
  • 在内存中保留一份原始代码的副本(通常加密存储)。
  • 当完整性校验失败时,将加密副本解密,覆盖被篡改的区域。

5.2 黑客的应对策略
方法一:先于校验前冻结修复线程
与内存校验类似,定位自我修复线程并暂停或终止。
方法二:同时修改所有副本
如果程序从加密副本恢复,黑客可以同时修改加密副本中的对应字节,使得恢复操作恢复的是“修改后版本”。
方法三:无限循环攻击
黑客可以让修改的指令不被校验发现(例如修改指令但让校验函数返回正确值),或直接NOP掉恢复函数。
5.3 实战案例(三):对抗自我修复
目标SecureApp.exe将原始代码的镜像加密存储在资源节。每隔2秒调用SelfRepair:
[size=12.573px]c



void SelfRepair() {    BYTE* encryptedCopy = GetResource("ORIG_CODE");    BYTE decrypted[1024;    Decrypt(encryptedCopy, decrypted, 1024);    // 将decrypted写回代码段    WriteProcessMemory(GetCurrentProcess(), (LPVOID)0x00401000, decrypted, 1024, NULL);}

破解:在Decrypt函数调用后,WriteProcessMemory之前,HookWriteProcessMemory,如果写入地址是代码段,则跳过写入(或写入修改后的内容):
[size=12.573px]cpp



BOOL WINAPI Hooked_WriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress,                                        LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten) {    if (lpBaseAddress == (LPVOID)0x00401000) {        // 阻止自我修复覆盖        return TRUE;    }    return Original_WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);}


第六章 交互式完整性校验6.1 多模块相互校验
交互式校验(Cross-Validation)将完整性检查点散布在不同的模块(EXE和多个DLL)中,它们相互校验。即使一个模块被破解,其他模块会发现不一致并作出反应。
典型架构:
  • main.exe加载crc32.dll和check.dll。
  • main.exe校验crc32.dll的完整性。
  • crc32.dll校验main.exe的完整性(反向校验)。
  • check.dll同时校验两者,并通过动态加载方式周期性检查。

黑客的困难:必须同时破解所有模块,破坏相互校验关系。
6.2 绕过交互式校验
方法一:集中崩溃点
找到所有校验的最终决策函数(比如ReportFailure),Hook该函数,阻止退出。
[size=12.573px]cpp



void WINAPI Hooked_ReportFailure() {    // 什么也不做,静默失败    return;}

方法二:符号执行
使用Angr等符号执行工具,一次性收集所有校验点,生成完整的补丁方案。

第七章 数字签名校验(Authenticode)7.1 原理
Authenticode是Microsoft的代码签名技术。开发者使用私钥对可执行文件进行数字签名,生成签名块嵌入到文件末尾。Windows在加载EXE/DLL时会验证签名(如果已启用策略),但默认情况下仅警告而不阻止。
对于完整性校验,程序可以在启动时调用WinVerifyTrust API验证自身的签名,判断文件是否被修改。
[size=12.573px]c



BOOL CheckSignature() {    GUID guid = WINTRUST_ACTION_GENERIC_VERIFY_V2;    WINTRUST_FILE_INFO fileInfo = {0};    fileInfo.pcwszFilePath = L"C:\\Program Files\\MyApp\\MyApp.exe";    WINTRUST_DATA trustData = {0};    trustData.dwUIChoice = WTD_UI_NONE;    // 调用WinVerifyTrust    LONG status = WinVerifyTrust(NULL, &guid, &trustData);    return (status == ERROR_SUCCESS);}

7.2 绕过方法
方法一:Patch WinVerifyTrust
Hook WinVerifyTrust 函数,始终返回 ERROR_SUCCESS。
[size=12.573px]cpp



LONG WINAPI Hooked_WinVerifyTrust(HWND hwnd, GUID* pgActionID, LPVOID pWVTData) {    return ERROR_SUCCESS;}

方法二:替换签名验证的证书
黑客可以用自己的证书对修改后的文件签名,然后配置系统信任该证书。但正版程序可能会检查签名者信息(发行商名称),因此还需修改程序的检查代码。
方法三:删除签名验证调用
直接NOP掉对WinVerifyTrust的调用。
7.3 限制与缺陷
由于WinVerifyTrust需要读取磁盘文件,它容易受到file system redirection攻击(被hook文件读取API),或使用Process Hollowing技术启动程序时提供一个未修改的原始文件路径。
因此,数字签名校验很少单独使用,通常作为完整性校验的补充。

第八章 反篡改的高级对抗:代码虚拟化与White-Box8.1 代码虚拟化的校验
VMProtect、Themida等强壳将完整性校验代码本身也放入虚拟机中保护。黑客无法直接静态分析校验逻辑,也无法简单通过NOP掉指令来绕过。
攻击方法:
  • 使用动态调试(x64dbg + TitanHide)跟踪虚拟机解释器。
  • 记录虚拟机指令序列,找到校验失败后的跳转点(通常是一个vJmp指令)。
  • 不脱壳,直接在虚拟机内部修改字节码,使校验函数永远返回“通过”。

8.2 White-Box加密与校验
White-Box Cryptography(白盒加密)是一种将密钥融入到算法实现中的技术,使得即使攻击者拥有完整的代码和内存访问权限,也无法直接提取出密钥。将完整性校验与白盒加密结合时,校验结果被用于解密关键数据,使得校验逻辑与业务逻辑深度耦合,无法简单绕过。
绕过方法:目前学术上尚无通用方法,实际破解只能通过:
  • 完整的密钥提取(如果实现有缺陷)
  • 不提取密钥,直接模拟整个解密过程(将解密后的结果硬编码,替换解密函数)


第九章 实战案例(四):绕过三层完整性校验9.1 目标
BankApp.exe(银行客户端安全控件)使用了三层完整性校验:
  • 启动时文件哈希校验:计算整个.text节的SHA-256。
  • 运行时内存校验:独立线程每3秒校验关键API函数(Transfer)的代码。
  • 相互校验:BankApp.exe校验Security.dll,Security.dll校验BankApp.exe。

修改任何指令将导致程序在几秒内退出。
9.2 破解步骤
步骤1:定位退出点
在ExitProcess下断点,查看调用堆栈。发现退出由ReportError函数触发,该函数被多处调用。
步骤2:Hook ReportError
使用DLL注入,Hook ReportError,使程序认为没有错误发生:
[size=12.573px]cpp



void WINAPI Hooked_ReportError(int errorCode) {    // 日志记录但不退出    Log("Error suppressed: %d", errorCode);}

步骤3:处理文件哈希校验(启动时)
启动时哈希校验在WinMain开头。由于我们已经Hook了ReportError,即使哈希校验失败也不会退出。但为确保稳定性,我们NOP掉哈希计算代码:
在IDA中找到CalculateSHA256调用,将其改为mov eax, 1; ret(模拟成功)。
步骤4:处理内存校验线程
定位CreateThread创建的内存校验线程,修改线程入口函数第一条指令为ret。
步骤5:处理相互校验
将Security.dll中的校验函数也做同样修改(使用相同的DLL注入目标)。
结果:程序所有完整性校验被静默失效。

第十章 完整性校验的检测技术(反反篡改)10.1 绕过的检测
某些高级保护会检测自己的校验代码是否被绕过。例如:
  • 断点检测:如果校验函数入口有0xCC(INT3),说明被下断。
  • 时序检测:校验函数执行时间异常(被Hook导致减慢)。
  • 栈回溯检测:检查调用者的返回地址是否在可信模块内。

10.2 黑客的反反篡改
使用硬件断点代替软件断点,避免代码修改。使用RDTSC时间戳检测时,Hook RDTSC指令返回正常值。
[size=12.573px]cpp



void __declspec(naked) Hooked_RDTSC() {    __asm {        // 返回一个固定的时间戳        mov eax, 0x12345678        mov edx, 0        ret    }}


第十一章 防御建议——开发者的完整性校验指南11.1 设计原则
  • 不要集中校验:将完整性校验点分散在多个位置,不同时机,增加破解工作量。
  • 使用加密哈希:使用SHA-256或更强算法,避免简单校验和。
  • 校验关键代码而非全部:校验全部代码会增加运行开销。识别出核心函数(许可证检查、加密算法等),仅校验这些函数。
  • 不要在本地存储哈希值:将哈希值存储在加密的配置文件中,或通过服务器获取。
  • 校验结果与业务耦合:使校验结果影响程序的关键数据(如解密密钥),而非仅仅条件跳转。

11.2 实施建议[size=12.573px]c



// 不好的做法(易绕过)if (Checksum() != GOOD) {    MessageBox("Tampered");    exit(1);}// 较好的做法(数据驱动)BYTE key[16 = {0};BYTE expectedHash[32;ComputeHash(key, 16, expectedHash);if (memcmp(expectedHash, GoodHash, 32) != 0) {    // 解密下一阶段的数据时会失败}DecryptData(key, encrypted_data, output);  // 错误key导致乱码

11.3 不可避免的局限性
任何完整性校验最终都是以代码形式存在。只要校验逻辑本身可以被修改或绕过,就没有绝对的完整性。因此,最有效的完整性校验是将关键逻辑转移到服务器端(SaaS模型)。

第十二章 总结
本文以超过一万六千字的篇幅,全面系统地讲解了软件完整性校验与反篡改技术。从基础的校验和、CRC、哈希校验,到高级的内存校验、自我修复、交互式校验、数字签名,再到代码虚拟化与白盒加密;从黑客的绕过方法到开发者的防御策略,覆盖了完整性校验攻防的完整生态。
完整性校验是保护软件不被破解的重要防线,但无法独立承担全部安全责任——它必须与其他保护技术(反调试、代码混淆、网络验证)结合使用。对于黑客而言,绕过完整性校验的核心方法论始终是:定位校验点 → 分析校验逻辑 → 修改或绕过校验 → 验证效果。随着自动化分析工具的发展,完整性校验将在未来更多地与数据流结合,发展出更难绕过的保护形式。
后续本系列将继续探讨虚拟机保护(Virtual Machine Protection)的深入分析与破解。
关键词:完整性校验;反篡改;黑客;破解软件;CRC;SHA-256;内存校验;自我修复;数字签名


黑客接单网,一个诚信可靠的黑客在线接单平台网站 - 论坛版权欢迎各位客户访问黑客接单网
黑客接单网,一个诚信可靠的黑客在线接单平台网站是一个专业的黑客在线接单服务平台
黑客接单网,一个诚信可靠的黑客在线接单平台网站聚集多位顶级黑客大牛于此
专业服务于网站攻击,网站入侵,软件开发,软件破解,聊天记录截取,手机定位,删帖,改贴,开房记录查询等各种网络服务
如果您有相关业务需求,请联系我们在线客服咨询,获取解决方案
黑客接单网,一个诚信可靠的黑客在线接单平台网站全体工作人员恭候您下达任务,我们誓必不负重托。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

HeiKeJieDan(黑客接单网)黑客接单网,是一个国际顶级黑客在线接单网站平台,平台聚集数十位IT界顶级黑客精英大神,软件-网站开发工程师,逆向破解工程师,以及后勤协助人员,网络在线全职兼职人员,强大的IT精英团队聚集,只为为您提供最优质可靠的网络安全技术服务。

联系我们

新加坡-缅甸-菲律宾-美国

0037259400637(服务时间:9:00-18:00)

hk@heike8.com

在线咨询新浪微博官方微信官方微信

黑客客服

网   址:WWW.HKJD.CC
邮箱号:hk@heike8.com
快手号:HeiKeJieDan
电报TG号:hkjd9988
国际客服:0037259400637

快速回复 返回顶部 返回列表