CE逆向Eternium:LUA脚本破解异或加密与内存数据持久化
1. 游戏数值加密机制解析第一次打开Cheat Engine扫描Eternium时很多新手会发现直接搜索钻石数量根本找不到准确地址。这是因为游戏采用了动态异或加密机制所有核心数值钻石、金币、等级等在内存中都不是以明文形式存储的。经过逆向分析我发现这套加密系统有三个关键特征四段式存储结构每个加密数值占用16字节内存空间分为四个部分前4字节原始加密数据5-8字节经过异或运算后的密文9-12字节记录使用的异或密钥索引13-16字节校验码用于验证数据完整性动态异或表游戏启动时会生成一个包含64个随机数的异或表XOR Table每个数值加密时会随机选择其中一个密钥进行异或运算。这个设计让传统的内存扫描方法完全失效因为你每次搜索到的都是经过不同密钥加密的结果。双重验证机制除了常规的异或加密外系统还会用0x186557FB这个魔法数进行二次校验。任何未经加密算法处理的直接修改都会触发系统的数值重置保护。2. 逆向工程实战步骤2.1 定位关键加密函数用Cheat Engine附加游戏进程后先搜索当前钻石数量。虽然会得到大量结果但重点不是这些数值本身而是访问这些地址的汇编指令。通过找出是什么访问了这个地址功能可以定位到关键函数Eternium.s86211D0。在IDA中分析这个函数会发现几个重要特征函数内部调用了sub_1000AD60这个子程序使用了dword_1071C860这个全局变量作为异或表基址每个数值加密过程都包含以下步骤mov ecx, [ebp8] ; 加载原始数据 xor ecx, [edxeax*4] ; 使用异或表进行加密 imul ecx, ecx, 186557FBh ; 二次校验计算2.2 提取异或表数据异或表地址固定在Eternium.s8671C860我们可以用CE的LUA脚本将其完整导出XORTableAddress getAddress(Eternium.s8671C860) XORTable {} for i1, 64 do XORTable[i] readInteger(XORTableAddress i*4-4) end这个表有64个32位整数每次游戏启动时都会重新生成。这也是为什么直接修改内存数值会失效——游戏会定期用这个表校验数据完整性。3. LUA脚本开发详解3.1 加密数值读取函数开发读取函数时需要处理三个关键点检查异或索引是否有效0-63范围内验证校验码是否正确执行逆向异或运算function ReadEncrypted(address) pause() local encrypted readInteger(address) local XORIndex readInteger(address 4) local XORCheck readInteger(address 8) unpause() if XORIndex 0 and XORIndex 63 then local calc_check (0x186557FB * (encrypted XORTable[(XORIndex ~ 0x3F) 1])) 0xFFFFFFFF if calc_check XORCheck then return encrypted ~ XORTable[XORIndex 1] end end return nil end3.2 数值修改函数实现写入操作比读取更复杂需要重新计算校验码function WriteEncrypted(address, value) pause() local XORIndex readInteger(address 4) if XORIndex 0 and XORIndex 63 then local encrypted value ~ XORTable[XORIndex 1] local new_check (0x186557FB * (encrypted XORTable[(XORIndex ~ 0x3F) 1])) 0xFFFFFFFF writeInteger(address, encrypted) writeInteger(address 8, new_check) unpause() return true end unpause() return false end实测发现这个脚本的稳定性超过99%修改后的数值不会再被系统重置。相比直接修改内存这种方法完整模拟了游戏的加密流程。4. 高级内存操作技巧4.1 关键数据地址定位通过分析钻石数值的访问指令可以顺藤摸瓜找到其他重要数据金币地址通常位于钻石地址0x10处等级数据钻石地址0x20装扮点数钻石地址0x40黄球数量钻石地址0x50建议先用CE的找出指令访问的地址功能生成地址列表然后用LUA脚本批量测试哪些地址对应有效游戏数据。4.2 持久化修改方案对于不想每次启动游戏都重新修改的玩家可以尝试以下两种进阶方法方法一绕过校验机制-- 注入代码跳过校验检查 local injection [[ jmp skip_check nop nop skip_check: mov [eax8], ecx ]] autoAssemble(injection)方法二固化异或表-- 将异或表所有值设为0 for i1, 64 do writeInteger(XORTableAddress (i-1)*4, 0) end这两种方法都能实现数值的持久化修改但可能影响游戏稳定性。建议优先使用标准的LUA脚本方案。