前言
发现帖子被微信公众号推送了,个人还是挺开心的,这样可以有更多的兄弟可以了解编程这个行业。
微信上有人回复说我之前的流程图不合格。我想回答的是,任何的图的最终目的都是为了更好的让人看懂,而不是体现高端标准的存在。当然也可能我的图确实有问题,哈~好多年不画了是真的。我的帖子也是一样,我的概念解释,可能没有体现那么专业,更多的是希望大家能够简单的理解,等有兴趣了,可以自己尝试去深入理解,都是没问题的。(但是我确实是程序员哈,咳咳,还是持证的)
所以在此申明一下,我会尽量呈现我自己经验的东西,大家也千万不要完全以我的概念为标准答案,鼓励大家怀着质疑的心态学习,谁都会错不是么。如果大家有发现有什么地方有问题,也欢迎指出错误。请指出来,共同学习嘛。而不是,哎呀你这个不行这种,谢谢。
讲了很多体外话,来讲下这个系列的计划吧,本来是准备这一篇就直接做修改器了。但是突然觉得,好像CE里面还有一个数据的观察可以讲,可能也对新手朋友有一些思路上的帮助,同时再给AA脚本来一次补课,于是延长一期。下期就是最后一篇了,会讲到通过易语言和AA引擎怎么来制作一个自己的修改器,当然内容好像不少,可能也会有上下集,但是会尽量比较近的时间发出来,谢谢大家。
欢迎文明转载,请注明出处 丸子de爸爸
本文所有技术资料仅供研究,不鼓励任何盗版商用行为
请大家支持正版,支持正版!支持正版!!!
上集传送门
工具
CheatEngine6.7
植物大战僵尸本体
PS:(我使用的是年度版,可以直接解锁所有物品,不然你就自己打到后面去吧)
本次基础概念
内存,一切皆内存,我们操作的全部都是内存。
所有地址相关的概念,都可以理解为坐标,用来给我们做标记的而已。
如果实在搞不明白,你想想指南针为啥指向北边,为啥叫南北,只是定义,只是公认
PS:上面这一段是重复了,但是我觉得还是有必要多重复一下,看多了,大家就印象深刻一点。
AA脚本的保存
每次CE修改完,写了AA脚本之后,可以保存为CT文件,下次运行游戏的时候,直接打开,就可以直接使用。
对象
编程的一种概念,简单理解成对事物的个体化表现。(实在不想说啥抽象啥之类的,很绕口,哈~)
比如说,我们都知道猫,就可以把猫看成一个对象。如果猫是一个对象,那它就可以变得具体化,比如红色的毛呀,多少岁呀,也可以有名字呀,爱吃鱼呀,会跑会跳。
所以对象就会包含两个基本元素:属性和方法。具体区分,可以用动名词分析法,动词就是方法;名词就是属性。如跑,跳,吃就是方法;红色,黑眼睛就是属性。想了解的,可以进一步去查查,动名词分析法或者对象。
实现步骤
植物超级攻击可以分为2种模式:
- 修改植物攻击CD
- 特殊超级攻击(这个我是不知道怎么形容,这个是因为特殊原因造成的,并不是啥逻辑)
修改植物攻击CD
CE查找CD地址
首先老规矩,打开CE和游戏本体,开始游戏。并用CE附加进程。(选植物的时候可以选个玉米投手,后面有用)
用前两次找的阳光指针或者基址,改个阳光数量。
然后等一个僵尸出现,等僵尸出现,在那一排种下一个单发射手(其他都行,看你自己),然后切换到CE。
这时候我们可以理一下攻击CD的流程。和之前植物种植CD是一样的。
每攻击一下,跑CD,CD结束,再攻击。(CD是先赋值最大值,再减少到0。尝试出来的)
回到CE,扫描类型设置为“未知的初始值”,首次扫描。
在CE中开启变速精灵,将速度降低,点击应用。
这是为了我们可以在一次攻击中,多次搜索CD。防止僵尸被太快的打死。
回到游戏,看植物发射了一颗子弹。切换到CE,搜索“减少的数值”,点击“再次扫描”。
如果植物又发射了一颗新的子弹,需要搜索“增加的数值”。因为攻击CD是一个循环,见流程图。
具体为啥,第二篇CD里面有详解了,不多说。
多次搜索,最终可以精确到一个值,当然其实你经验好,也不用筛选到最后一个,有可疑的可以先测试。
将整个值改到0,发现植物马上又发射了一颗子弹,所以他就是攻击的CD了。
右键选择“找出是什么访问了这个地址”。
因为理论上,循环的减少CD,一直改写。减少完之后,需要判断是否CD完成,这个判断就是访问。
所以我们这里选择访问。
回到游戏,CE立刻多了很多纪录。
但是出现了个问题,可以发现,最后两条纪录虽然是比较的,但是都不是0。
之前我们是修改到0,然后植物重新发出了子弹。所以正常应该是和0比较,但是这里没有。
说明,这个CD,不是很准确,或者是不由这个CD来控制,或者是有特殊的方法判断。
那没办法,我们从第一个,是减少值CD,从修改入手分析逻辑。
从红框往下dec开始,分析每条代码含义(其实我就是前面几条看到了,才决定一直往下分析的)
- [esi+58]就是攻击CD,CD = CD -1
- 将CD存到eax寄存器中
- 将edi存到栈里
- 检查eax是否为0,也就是判断CD是不是为0
- 之前CD>0就跳转,所以下面的代码,就是CD=0的时候执行的逻辑
- 栈里存一个15常量
- 调用一个方法,啥方法先不管
- ecx赋值一个值,[esi+5C]
- ecx = ecx-CD
- eax赋值为一个不是CD的值,[esi+24]
- esp+4,esp栈顶指针,一般简单可以先不管。
- 将ecx赋值到CD。这里就可以发现可疑了。
之前有一个ecx = ecx-CD,然后ecx又赋值给了原本存放CD的位置。
所以判断,ecx,也就是[esi+5C],是CD的最大值。
AA脚本实现
根据上面的分析,只需要直接把最大值改为50,就可以实现增加输出的速度。
(至于为啥是50,应该是和动画有关,改为低一点的,会有子弹打不出来)
选中 mov ecx,[esi+5C]这一行,选“工具”,自动汇编。
先选“CT表框架代码”,再选“代码注入”。PS:选AOB注入可以解决多版本兼容问题。
AA脚本运行逻辑是:
先从具体代码行数开始执行"PlantsVsZombies.exe"+6DC21:
通过jmp到指定标签,默认是newmem,执行完新的代码之后,再向下接着执行。
打开的时候,执行enable里面的逻辑;关闭的时候,执行disable里面的逻辑。
但是我们只需要修改mov ecx,[esi+5C],改为mov ecx ,0x32就行。
PS:注入原理是,在你选择的代码处将原来的代码修改为jmp,跳转到自己开辟的一块新位置,然后按你的逻辑执行汇编。其中会根据你选择的代码字节码的长度和jmp进行对比,对代码进行调整。所以有时候我们会看到originalcode里面就是一行代码,有时候是两行,是因为每次都在原处改为jmp,必须和源代码的长度是符合的。(jmp好像是5长度,这样如果修改注入的位置只有3,就不会够长度,于是就会把下面两行代码都放到新方法里,在原代码处用nop补位。新手有个概念就行,这些都是系统会做的,慢慢就会明白了)
[ENABLE] //激活脚本时的逻辑
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //申明一个新代码空间newmem
label(returnhere) //申明标签returnhere
label(originalcode) //申明标签originalcode
label(exit) //申明标签exit
newmem: //一般自己的代码就写在这里,但是你也可以直接修改下面的代码,但是需要注意保证原始执行逻辑
originalcode: //原本的代码,修改时需要注意。内容由jmp和代码差生的字节码差导致。
mov ecx,0x32 //这里要改为32,就是十进制的50
sub ecx,eax
exit:
jmp returnhere
"PlantsVsZombies.exe"+6DC21:
jmp newmem
returnhere:
[DISABLE] //关闭脚本时的逻辑
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"PlantsVsZombies.exe"+6DC21:
mov ecx,[esi+5C]
sub ecx,eax
//Alt: db 8B 4E 5C 29 C1
修改完成后,点击“分配到当前的CT表”。就可以在CE中搜索结果栏看见脚本。
点击脚本最前面的方框就可以启动脚本,再点一次就是取消。
启动后,发现植物的攻击频率明显提升。(但是缺点是双发子弹数也变成单发一样了)
超级攻击
这个攻击是我当时在尝试修改攻击间隔的时候发现的,也正是因为这个,我觉得有必要新增这一篇帖子。
CE查找超级攻击地址
无论这种语言是面向对象的还是面相过程的,在某种程度上都可以理解为有对象的存在,只是看是不是显性的。(好像有点远,讲多了也麻烦,大家当是我个人的意见吧,可以自己去查查,面向对象,面向过程。)
他们会把一个对象相关的值,放在相近的内存区域里。其实如果仔细一点我们从之前的分析就可以看出来。
见下图,红框内每一句分析,在上面都有,不记得的往上翻翻。
这里的esi分别和什么有关,esi+58是CD,esi+5C是最大值,esi+24暂时不知道(其实这是植物的种类)。
这说明在植物攻击CD倒计时结束进入的这一块逻辑里,esi对应的是当前植物对象在内存中的的首地址。
在游戏里,把其他植物都铲了,防止干扰你观察数据。
在有僵尸的那一栏,种下一个玉米。(其实种啥都行,玉米方便观察)
回到CE,在push 0F那打断点,其实上面打断点也行,但是为了方便后面调试。
(这里进入断点的,就是开炮的时候,只要在jg下面就行,不同版本的也没关系)
回到游戏,等待断点触发,查看到ESI的值为“28CC5618”,每个人不一样,纪录你自己的。
右键左下角内存区域的任何一项,选择“转到地址”,填写我们刚才的ESI值,点确定。
自己调整下内存区域的显示大小,边框拉一下,拉大一点。
ctrl+B,去掉所有的断点。按F9使程序恢复运行,然后在CE中,变速精灵保证是减速到0.25(这都是方便观察)
可以发现,在红框位置的有一个数值,在CD跑完了之后(自己转到地址是可以加偏移的,可以确定哪个值是CD)
这个红框内的值开始进行了一个倒计时,并最终停留在了1上。
然而为1的时候,游戏里的画面是酱紫的。
玉米保持一个投掷的姿势,等玉米恢复到了填弹状态,该数值才重新变为0。
PS:是的,我重开了游戏,之前开变速精灵的时候崩了...
所以我们是不是可以推测,之前我们那个攻击CD之所以没有0,是因为我们认为的攻击CD只是代表填弹的间隔。
填弹完毕,然后再由这个真正的攻击CD来控制,进行一个攻击动画的倒计时。
等到1的时候,丢出弹药,然后再次填充。
所以我们先在这个地址上右键,然后选择“把地址添加到列表”
你么如果懒,不想自己观察,可以告诉你们偏移就是ESI+90的那个位置。
在CE里,右键刚添加的地址,选择“什么访问了地址”。
老原因,因为在攻击的时候需要判断,是否到一个临界值,所以只是访问而不是改写。
又是一堆纪录,不过好像直接找到了我们想要的东西,是不是有一个与01的比较了,就是他了。
和我们的分析也一样,判断到了1,则释放子弹,为啥不是0。因为0的时候是空闲状态。
所以选中cmp 01那一行,显示反汇编。
接下来都不需要断点了,cmp下面一行是jne,不等于才跳转。
也就是,不等于1的时候,就直接跳走,等于1才执行下面的逻辑。
那我们只需要让他一直执行1的逻辑,就可以实现一直攻击了。(是他执行多少次,就攻击多少次)
选中jne那一行,自动汇编,参考上面的AA。只需要把originalcode中的内容去掉就可以了,因为我们不需要执行任何代码。只是要把跳转去掉就行了。
添加到表中之后,打开脚本,攻击开始狂暴了。
这里其实子弹的个数,就是你真正的攻击CD的数量。因为他只在倒计时里执行判断,所以没法让它一直开枪。
当然有人可能说,那我直接给它改一下不行么?还真不行,它的循环逻辑比较复杂,我观察了一段时间,感觉没有啥太好的处理方法。不过也有思路,就是去观察对象的数据结构,也就是我为啥能发现这个为1的时候,开始执行真正攻击的原因。
进一步的分析数据结构,对比双重或者四重和单重的区别,加以分析,应该就可以找到每次的子弹数的位置了。
如果能分析出子弹对象的属性,让豌豆射手打出玉米加农炮都是可能的...
有兴趣的可以去分析分析,然后分享一下,我还是偷偷懒吧。
总结
想要分享给大家的,不是简单的一个功能,而是一种思路。有时候如果发现找到的结果和自己的想法不太一致,尝试去分析一下对象周边的东西,关联联想是非常重要的,我们之前其他几个功能也有用到类似的方法。不过这一次希望引入对象的概念,使大家能够更加清晰,多谢各位,有问题和意见欢迎回复。
PS:可以留个作业,植物只有看到僵尸才会打子弹,能不能让它不看到僵尸也打子弹,其实很简单,试试吧。
下篇文章真的是本系列最后一篇了,将分享使用易语言和AA引擎制作外挂,敬请期待。
下一篇