1. 游戏数值加密机制解析
第一次打开Cheat Engine扫描Eternium时,很多新手会发现直接搜索钻石数量根本找不到准确地址。这是因为游戏采用了动态异或加密机制,所有核心数值(钻石、金币、等级等)在内存中都不是以明文形式存储的。经过逆向分析,我发现这套加密系统有三个关键特征:
四段式存储结构:每个加密数值占用16字节内存空间,分为四个部分:
- 前4字节:原始加密数据
- 5-8字节:经过异或运算后的密文
- 9-12字节:记录使用的异或密钥索引
- 13-16字节:校验码(用于验证数据完整性)
动态异或表:游戏启动时会生成一个包含64个随机数的异或表(XOR Table),每个数值加密时会随机选择其中一个密钥进行异或运算。这个设计让传统的内存扫描方法完全失效,因为你每次搜索到的都是经过不同密钥加密的结果。
双重验证机制:除了常规的异或加密外,系统还会用0x186557FB这个魔法数进行二次校验。任何未经加密算法处理的直接修改都会触发系统的数值重置保护。
2. 逆向工程实战步骤
2.1 定位关键加密函数
用Cheat Engine附加游戏进程后,先搜索当前钻石数量。虽然会得到大量结果,但重点不是这些数值本身,而是访问这些地址的汇编指令。通过"找出是什么访问了这个地址"功能,可以定位到关键函数Eternium.s86+211D0。
在IDA中分析这个函数会发现几个重要特征:
- 函数内部调用了sub_1000AD60这个子程序
- 使用了dword_1071C860这个全局变量作为异或表基址
- 每个数值加密过程都包含以下步骤:
mov ecx, [ebp+8] ; 加载原始数据 xor ecx, [edx+eax*4] ; 使用异或表进行加密 imul ecx, ecx, 186557FBh ; 二次校验计算
2.2 提取异或表数据
异或表地址固定在Eternium.s86+71C860,我们可以用CE的LUA脚本将其完整导出:
XORTableAddress = getAddress("Eternium.s86+71C860") XORTable = {} for i=1, 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 [eax+8], ecx ]] autoAssemble(injection)方法二:固化异或表
-- 将异或表所有值设为0 for i=1, 64 do writeInteger(XORTableAddress + (i-1)*4, 0) end这两种方法都能实现数值的持久化修改,但可能影响游戏稳定性。建议优先使用标准的LUA脚本方案。