玩到一款小游戏,一如既往尝试用 CE 修改血量,发现很多不生效的地方。稍微研究一下,这个游戏的血量没有明确的数值,可以通过未知的初始值,根据数值的增加或减少找到数据的地址。
找到两个,最后测试下面那个可以生效,接下来找基址。
我们右键这条地址,选择对这个地址进行指针扫描。把保存的数据随便取名,等待扫描完成(一般设置的信息默认即可)。
结果出来了:
我们先过滤一下数据:点击指针扫描器,点击“重新扫描内存” -> “移除没有指向正确地址的指针”。
切换到要查找的数值,然后切换我们要查找数据的类型,比如我们的血量数据是单精度浮点数,然后输入 93,保存结果。好的,我们可以重启游戏了。此时再次打开游戏,选中进程,我们发现数据都变了,什么数据都可能有,我们此时再筛选一下。
一般游戏到这里就可以了,但是这个游戏并不行,我们直接改写减少体力的那段指令。
右键数据,点击“找出是什么改写了这个地址”:
点击“显示反汇编器”。
稍微看一下这些汇编指令大概意思是:
1CCA9600644 ~ 1CCA9600651:把当前血量、扣减值、上限值转成浮点数。1CCA9600660: call r11:调用限制函数(把计算后的血量限制在 0 ~ 最大血量 之间)。1CCA960066B: movss [rsi+000000A8],xmm5:将最终计算好的真实血量,写入角色内存地址[rsi+A8]。- 紧接着
1CCA960067B: pop rbp; ret:写完后函数直接结束返回。
我们把 1CCA960066B 使用 nop 填充,发现怪物和我们都不掉血了,说明所有的玩家和敌人受击时,调用的都是这同一行指令。因此我们需要找到玩家的 rsi 值,然后写注入脚本。
我们在详细信息中可以找到,玩家当前这局游戏的角色对象地址为 000001CCA153AD80,那么血量地址为 000001CCA153AD80 + A8。
可以写注入脚本了,打开自动汇编窗口:在当前 CE 的内存视图窗口中,确保选中的还是那一行:1CCA960066B: movss [rsi+000000A8],xmm5。按快捷键 Ctrl + A,在弹出的自动汇编窗口中,点击菜单栏的 “模板” -> “代码注入”。
粘进去写好的脚本:
[ENABLE]
alloc(newmem, 2048, 1CCA960066B)
label(returnhere)
label(exit)
newmem:
// 1. 判断受击者是不是玩家
push rax
mov rax, 000001CCA153AD80 // 玩家地址
cmp rsi, rax
pop rax
je exit // 如果是玩家,直接跳过写入
// 2. 如果是怪物,正常执行扣血
movss [rsi+000000A8],xmm5
jmp exit
exit:
jmp returnhere
1CCA960066B:
jmp newmem
nop 3
returnhere:
[DISABLE]
1CCA960066B:
db F3 0F 11 AE A8 00 00 00
dealloc(newmem)
粘贴完后,再次点击窗口菜单栏的 “文件” -> “分配到当前作弊表”。
搞定,正常测试玩家不掉血,怪物掉血。