目录
一、找出存放四个玩家健康值的地址
二、找出修改数据的代码
三、找出是谁调用了修改指令
四、分析玩家的内存数据
五、注入修改代码
六、C语言代码注入
相对于前面8个步骤,步骤9稍微要难一点,所以单独写。
步骤9是在步骤7的基础上,继续讲代码注入。但是它涉及到代码的细粒度区分,比如“攻击”时,上面4个玩家用到的都是同一段代码,这就需要通过代码去判断,什么时候是在攻击自己玩家(玩家1,玩家2),什么时候是在攻击敌方玩家。
精确查找,分别找出存放四个玩家健康值数据的地址,并双击添加到地址栏。注意查找时,数值类型要选单浮点,因为健康值为小数。当然,如果不知道数值是什么类型,也可以用"未知的初始值"查找,只是要麻烦一点。
在四个玩家的地址上,右键选择“找出是什么改写了这个地址”后,再点击“攻击”该玩家,修改该玩家健康值的指令就会显示出来。
最后会发现,攻击每个玩家时,调用的都是这条指令:
10002F25D - F3 0F11 43 08 - movss [rbx+08],xmm0
所以,我们不能直接修改这条指令,实现攻击己方玩家时健康值增加,攻击敌方玩家时健康值降低。
当然,我们说的仅仅是在“攻击”时,如果直接修改玩家的健康值,是可以成功的。
我们需要用反汇编的方式,继续往前找,找出是谁调用了修改健康值的指令。
右键选择“找出指令访问的地址”:
当然,如果我们只是找这个四个玩家健康值的地址,前面早就找出来了,根本不用这么麻烦。下面的步骤才是关键,那就是分析数据。
选中四个玩家地址,然后右键选择“打开选中地址的分析数据”:
上面界面选了后,会弹出一个框,点确定即可。
然后给这个数据结构取一个名字:
然后会弹出一个框,点击“yes”:
这个结构的起始大小默认是4096,点击“ok”:
然后这个数据分析的界面就出现了:
上面这个界面,分别显示了4个玩家对应的内存数据,我们最开始就已经知道,Dave和Eric是一队,而HAL和KITT是另一个队的。通过上面的数据会发现,只有蓝色框框出的那行,比较符合这个规则。
那么这个界面的作用是什么?
1.找出区分队伍的数据地址偏移量,即+0014;
2.找出各玩家健康值的偏移量,即+0008。
回到汇编界面,点击“自动汇编”:
先选择“CT表框架代码”:
再选择“代码注入”,并填写地址:
然后会生成一段模板代码,需要稍微改下:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048,"Tutorial-x86_64.exe"+2F25D)
label(returnhere)
label(originalcode)
label(exit)
newmem:
cmp [rbx+14],1//第1队玩家
je team1
cmp [rbx+14],2//第2队玩家
je team2
team1:
movss xmm0,[rbx+08]// [rbx+08]就是原健康值的地址,即攻击第1队玩家时健康值不变
jmp originalcode
team2:
movss xmm0,[value2]// 第2队敌方玩家的健康值为0
jmp originalcode
value2:
dd (float)0
originalcode:
movss [rbx+08],xmm0
exit:
jmp returnhere
"Tutorial-x86_64.exe"+2F25D:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-x86_64.exe"+2F25D:
movss [rbx+08],xmm0
//Alt: db F3 0F 11 43 08
上面代码看起很多,实际只有中间那段是手写的,作用就是:
如果玩家是1队的,健康值不变;如果玩家是2队的,健康值设为0。
保存脚本,然后选择“文件”菜单中的“分配到当前CT表”:
回到CE主界面,激活自动汇编脚本:
然后点击Tutorial-x86_64.exe的步骤9界面,点击“重新启动游戏并自动执行”,然后随便“攻击”一个玩家,就可以激活“下一步”按钮了。
直接修改汇编代码,需要对汇编指令相当熟悉才可以,而且逻辑稍微复杂一点,修改起来会相当繁琐。比较幸运的是,在CE中,居然可以直接使用C语言的代码来编写。
下面我用C语言来实现一个功能,那就是:
攻击己方玩家时,健康值减少10;而攻击敌方玩家时,健康值减少100。
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048,"Tutorial-x86_64.exe"+2F25D)
label(returnhere)
{$c}
// {$c} 表示c语言代码块,可以定义我们需要调用的函数。
void SetHP(void* hpBase)
{
if(*(int*)(hpBase+0x14)!=1){
*(float*)(hpBase+0x8)=*(float*)(hpBase+0x8)-100; //攻击敌方玩家,健康值减少100。
}else{
*(float*)(hpBase+0x8)=*(float*)(hpBase+0x8)-10; //攻击己方玩家,健康值减少10。
}
}
{$asm}//c代码块结束
newmem:
// 注意这是里用的是ccode,hpBase代表参数名,rbx代表参数值
{$ccode hpBase=rbx}
SetHP(hpBase);
{$asm}
jmp returnhere
"Tutorial-x86_64.exe"+2F25D:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-x86_64.exe"+2F25D:
movss [rbx+08],xmm0
上面代码总体来说,比汇编代码要容易读一些,也精简了不少,实现效果如下:
最后再说一点,关于movss指令:
MOVSS,Move scalar single precision floating-point value between XMM registers or between an XMM register and memory.
存放float类型的数据大多是xmm寄存器,而movss指令(SSE指令集之一)则用来移动单精度float类型的数据。
除了可以内嵌C语言的代码外,还可以支持lua代码内嵌,把上面的C语言版本改成lua后,代码如下:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048,"Tutorial-x86_64.exe"+2F25D)
label(returnhere)
{$lua}//我在win7 64位上面,不加下面这段代码,就不能运行luacode
if not getAddressSafe('luaclient-x86_64.dll') then
assert(injectDLL('luaclient-x86_64.dll'))
end
{$asm}
newmem:
//hpBase代表参数名,rbx代表参数值
{$luacode hpBase=rbx}
if readInteger(hpBase+0x14)==1
then
writeFloat(hpBase+0x8,readFloat(hpBase+0x8)-10.0)
else
writeFloat(hpBase+0x8,0)
end
{$asm}//这里必须要顶格写,不然会报错
jmp returnhere
"Tutorial-x86_64.exe"+2F25D:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-x86_64.exe"+2F25D:
movss [rbx+08],xmm0
lua语法更加简洁一些,但是在我的电脑(win7 64位)上,需要手动注入luaclient.dll,不然不能运行。
参考文章:
Cheat Engine
Cheat Engine Tutorial Guide (x64) - Cheat Engine
Cheat Engine