植物大战僵尸(四)超级攻击

前言

发现帖子被微信公众号推送了,个人还是挺开心的,这样可以有更多的兄弟可以了解编程这个行业。

微信上有人回复说我之前的流程图不合格。我想回答的是,任何的图的最终目的都是为了更好的让人看懂,而不是体现高端标准的存在。当然也可能我的图确实有问题,哈~好多年不画了是真的。我的帖子也是一样,我的概念解释,可能没有体现那么专业,更多的是希望大家能够简单的理解,等有兴趣了,可以自己尝试去深入理解,都是没问题的。(但是我确实是程序员哈,咳咳,还是持证的)

所以在此申明一下,我会尽量呈现我自己经验的东西,大家也千万不要完全以我的概念为标准答案,鼓励大家怀着质疑的心态学习,谁都会错不是么。如果大家有发现有什么地方有问题,也欢迎指出错误。请指出来,共同学习嘛。而不是,哎呀你这个不行这种,谢谢。

讲了很多体外话,来讲下这个系列的计划吧,本来是准备这一篇就直接做修改器了。但是突然觉得,好像CE里面还有一个数据的观察可以讲,可能也对新手朋友有一些思路上的帮助,同时再给AA脚本来一次补课,于是延长一期。下期就是最后一篇了,会讲到通过易语言和AA引擎怎么来制作一个自己的修改器,当然内容好像不少,可能也会有上下集,但是会尽量比较近的时间发出来,谢谢大家。

欢迎文明转载,请注明出处  丸子de爸爸

本文所有技术资料仅供研究,不鼓励任何盗版商用行为

请大家支持正版,支持正版!支持正版!!!

上集传送门

工具

CheatEngine6.7

植物大战僵尸本体

PS:(我使用的是年度版,可以直接解锁所有物品,不然你就自己打到后面去吧)

本次基础概念

内存,一切皆内存,我们操作的全部都是内存。

所有地址相关的概念,都可以理解为坐标,用来给我们做标记的而已。

如果实在搞不明白,你想想指南针为啥指向北边,为啥叫南北,只是定义,只是公认

PS:上面这一段是重复了,但是我觉得还是有必要多重复一下,看多了,大家就印象深刻一点。

AA脚本的保存

每次CE修改完,写了AA脚本之后,可以保存为CT文件,下次运行游戏的时候,直接打开,就可以直接使用。

对象

编程的一种概念,简单理解成对事物的个体化表现。(实在不想说啥抽象啥之类的,很绕口,哈~)

比如说,我们都知道猫,就可以把猫看成一个对象。如果猫是一个对象,那它就可以变得具体化,比如红色的毛呀,多少岁呀,也可以有名字呀,爱吃鱼呀,会跑会跳。

所以对象就会包含两个基本元素:属性和方法。具体区分,可以用动名词分析法,动词就是方法;名词就是属性。如跑,跳,吃就是方法;红色,黑眼睛就是属性。想了解的,可以进一步去查查,动名词分析法或者对象。

实现步骤

植物超级攻击可以分为2种模式:

  1. 修改植物攻击CD
  2. 特殊超级攻击(这个我是不知道怎么形容,这个是因为特殊原因造成的,并不是啥逻辑)

修改植物攻击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引擎制作外挂,敬请期待。

THE END
喜欢就支持以下吧
点赞0
分享
评论 抢沙发
  • 管埋员的头像-小北的自留地

    昵称

  • 取消
    昵称