这篇博客主要是关于 Denuvo 的分析,Denuvo 是一种用于保护数字媒体(如视频游戏)免受盗版和逆向工程的反篡改和数字版权管理系统(DRM)。
- 总体理念:用户首次启动程序时,Denuvo 会收集当前系统的硬件识别信息并发送到服务器,服务器应用可逆数学函数处理后将混合信息(许可证文件)返回给程序,程序创建本地副本以减少后续在线请求。游戏会通过运行时收集的硬件信息与许可证文件中的信息进行对比来执行用户完整性检查,若不一致则游戏可能崩溃。
技术解释:
- 许可证文件:游戏中的某些函数被选为“受保护”,其本身在虚拟机中执行,部分指令被从二进制文件中删除,这些删除的字节与用户硬件识别信息通过可逆数学函数组合成许可证文件。
- 许可证 DWORDs:在将执行权交给原始入口点(OEP)之前,Denuvo 会将许可证文件的部分内容写入 DWORDs,散布在.vm 节中,每个 DWORD 都是从二进制文件中删除的单个指令与客户硬件识别信息的组合。
- 加密常量/删除指令示例:以一个简单的函数为例,Denuvo 会删除二进制文件中的常量(如 -4)并存储在服务器上,程序需要从许可证文件中获取加密的常量并解密后才能执行原始指令,若运行时收集的硬件信息与服务器上加密常量时使用的信息不一致,将导致未定义行为。
- 用户完整性检查:Denuvo 使用多种向量来验证执行受保护二进制文件的系统的完整性,包括预 OEP 检查、KUSER_SHARED_DATA、CPUID、SYSCALL、NTDLL 检查、Process Environment Block(PEB)、XGETBV、GetWindowsDirectoryW、GetVolumeInformationW、GetComputerNameW、GetUsernameW 等。
代码完整性检查:
- 循环冗余检查(CRC):Denuvo 会对重要处理程序(如 CPUID、SYSCALL 等)进行扫描,以确保没有钩子或篡改行为。
- 看似随机的.VM 检查:Denuvo 会从.VM 部分读取看似随机的字节来构造常量,若该常量发生变化则计算将失败。
其他方面:
- 虚拟机(VM):Denuvo 的虚拟机存储值的方式独特,将寄存器值的字节/位分散存储,增加了分析的难度。
- 随机性:Denuvo 利用 native 寄存器的值作为随机性的来源,通过模块化算术生成随机数。
- 混合布尔算术(MBA):Denuvo 利用 MBA 方法将 x86 指令重写为难以理解和分析的表达式,进一步增加了代码的复杂性。
- 实时解密和重新加密的 CPUID:Denuvo 会在虚拟机中解密和重新加密 CPUID,为防止线程同时执行相同的加密 CPUID,使用了自旋锁,但自旋锁也被加密,增加了破解的难度。
- 反基于异常的挂钩:早期 Denuvo 易受基于异常的挂钩攻击,但后来 Denuvo 实施了补丁,破坏了这种攻击方式。
破解方法:
- 修补硬件 ID 检查:手动修补每个硬件识别检查极其困难,不仅有复杂的 CRC,还有随机性。
- 修补常量解密:目标是常量解密例程,返回正确的常量,但在大量 x86 指令中找到单个常量解密并不容易。
- 完全恢复二进制文件:需要修复/反混淆数千个指令,虽然有实例成功恢复了受保护的二进制文件,但非常困难。
- 使用虚拟机:利用虚拟机来欺骗所有必要的硬件信息,但实现起来并不容易,尤其是要避免破坏其他应用程序。
- 总结:Denuvo 在保护游戏方面表现出色,短期内不太可能消失,尽管存在破解方法,但都具有很大的难度。
博客作者感谢了 Sp、Ma、Mk、Az等人为其提供的帮助。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。