植物大战僵尸变态辅助开发系列教程(E语言实现和VC6实现)(下)
- 36、全屏秒杀
- 37、秒杀实现
- 37、PVZ聚怪
- 38、种植CALL的查找与调用
- 39、OpenProcess错误
- 40、错误:constchar[19]”转换为“LPCWSTR”
- 附录:
36、全屏秒杀
首先先把僵尸的血量给找到:
咱们之前找过了,这个血量是这只僵尸的临时基址+C8:
手动加入地址:
然后咱们看这块内存的变化,看最大到哪里:
然后咱们等僵尸死掉,看一下变化的位置:
复制记录一下这个最大变化范围的地址:
计算出来这个15C该怎么用呢?
刚才那个僵尸死了的话,记录的地址肯定就没有用了(15C还有用),我们再换一只僵尸,找它的地址:
有了15C这个偏移,我们就不用再去浏览当前僵尸临时基址的内存,再去观察变化最大范围的情况了。
回到游戏,种植一个豌豆去打僵尸,在僵尸没有死的时候,去CE中走索未变动的数值:
回到游戏,再打一下僵尸,再回到CE继续搜索未变动的数值(重复几次):
僵尸死掉消失了,咱们再搜索变动的数值:
特别大的数值,还有这个数值6,都不是我们要的数据,因为记录僵尸死亡状态的要么是0、要么是1,
(上面两张图地址数值不一样,是因为前面不小心把僵尸临时基址删除了,所以又重新找另一只僵尸的临时基址,并重新找僵尸死亡之后变动的数值;步骤都是一样的)
我们一个一个将这些地址复制下来,计算出与基址的偏移:
这只僵尸找完了,咱们还得换另外一只僵尸,用来测试看这6个偏移到底哪个是僵尸死亡状态,再重复上面的两个步骤:
上图中的34、74、158这几个肯定就不是了。
咱们看一下当这个僵尸死亡了以后,这3个地址的数值有什么样的变化:
我们发现掉脑袋了,70这个偏移的数值变为1。
这几个都是,那怎么就试一下,找到僵尸的基址,加上偏移后写入1看看会有什么样的反应。
给28这个偏移写入1后我们发现僵尸直接消失了。
咱们再找一个帽子僵尸的:
应该就是350这个地址是这只帽子僵尸的临时基址:
我们把偏移70这个上图选中地址的数值改为1,发现没有效果,所以说偏移70肯定是不对的,把它删除。
咱们再改一下偏移EC看看,再找一只僵尸:
我们发现偏移EC的值改成1的话僵尸直接就没了,所以偏移28是僵尸死亡,偏移EC是僵尸消失。
咱们再继续找一只帽子僵尸:
因为是发射了两颗豌豆,所以是减40:
有点问题,咱们换最下面那只普通的僵尸看看:
咱们等把这只僵尸打死后看看产生的汇编指令:
咱们记录下僵尸死亡的指令地址,到OD中看一下该地址处的相关数据:
咱们找到僵尸死亡的关键指令之后,就该实现秒杀这个功能了。
37、秒杀实现
找到了僵尸死亡状态的指令地址后,上面选中的部分就都可以删除了。
咱们还是用僵尸移动,搜索僵尸的坐标那个方法,来实现秒杀功能。
从00400000那里往下找一片空白区域:
咱们先把上图选中的记录一下之后,在修改该位置指令为JMP 00400E2C:
要在跳转到的空白位置写入死亡状态:
上面写入的指令是从CE那里复制过来的,这里我们要根据实际情况来修改,所以要把edi修改为esi才对:
僵尸的基址是放在esi这个寄存器里面的,所以跳转到空白处后就不能用edi了:
多了一个nop咱们不能要,咱们给它恢复。
我们看到jnz跳转的话就有两种情况了,跳走了咱们就不用搭理它(因为就这一个功能),没跳走的话就跳回原位置(程序之前从哪里过来的,还得跳回到那里):
回到游戏看效果,僵尸只要一冒头就会消失,但是这样直接消失的话是没有奖励的,因为死亡状态有不同的种类(1消失、2燃烧、3掉头),所以咱们应该写入数值3让僵尸掉头:
要学会不断地去尝试,这样就实现了全屏秒杀,只要僵尸一移动就掉头死掉了;当然为了观赏效果,可以在空白位置处写入条件判断指令,判断僵尸移动到某个坐标位置时再掉头,这样观赏效果好。
37、PVZ聚怪
植物大战僵尸它有一个功能,就是大蒜可以让当前靠近的僵尸改到别的地址(路线)上,所以咱们就通过这个方法来实现把所有僵尸只要一移动,咱们就给它们聚到一行上面,可以达到聚怪的功能。
先通过僵尸的血量找到这只僵尸的临时基址,在之前全屏秒杀那节课讲过一个最大偏移0x15C,加上这个临时基址应该就是僵尸那个数组结束的地址:
我们看到僵尸遇到大蒜后移动的话,只能是在方格里进行移动,所以这是一个整数型的这么一个数据结构;
有这么一种可能,这个范围如下图所示,坐标有可能从0开始到4结束(一共5格),也有可能从1开始到5结束:
所以我们搜索的范围肯定是要比10要小的一个数值:
咱们给它变动了一行,我们再搜索变动的数值:
一看就是第一个地址的数值2是对的,咱们试一下给它改成1:
它就跑到最上面一行来了(因为第二行有个大蒜,导致它又往上跑了一格),我看到找到的这个地址数值变为0了,所以它这个数据结构的类型就是:
如果给它改成4的话,它就跑到最下面一行了:
放一个大蒜,在僵尸碰到这个大蒜的时候产生一条汇编指令:
esi的值是3,从上往下数位置的当前状态,也就是当前的y坐标,edi是这只僵尸的临时基址,所以坐标的偏移就是1C;
咱们记录一下该指令的地址:
到OD中转到该关键指令地址:
如果你想把僵尸聚到中间这行的话,坐标就是2:
咱们还是用最老套的方法,就是僵尸移动就给它聚到一块:
咱们还是要找一片空白区域:
把之前僵尸死亡的代码修改为这里的聚怪代码;
到时候写程序的时候,用哪个功能就会往这里写相应的代码,取消功能的话还能恢复原样。
我们看一下效果:
这样的话僵尸都到这一行了;
只要僵尸一出现的话就会到中间这一行了。
那个坐标2也可以根据你要移动的位置进行相应修改(0、1、2、3、4都可以)。
x坐标涉及到浮点数,操作上比较麻烦,所以这里就先讲y轴的聚怪。
38、种植CALL的查找与调用
我们知道,种植植物的话,植物数量肯定会增加,所以思路就是找到当前植物的数量,再找到这个数量增加的关键指令,下个断点,看一下都调用了哪些函数,分析一下就可以了。
程序崩溃,重新来过:
用铲子铲除一个植物,然后搜数值3:
这样反复多次增加、铲除,得到植物数量的地址:
这个地址就是植物数量增加的关键地址,剥离掉CE,打开OD转到该地址,下个断点,回到游戏再种植一个冰豌豆:
断下来以后按工具栏上的K按钮,打开堆栈调用窗口,分析这些CALL:
第一层是个无参的CALL,内部没有返回值,肯定不是咱们种植的CALL,因为咱们如果要种植的话,肯定会有它们的一个坐标,它种在哪,植物的类型等等这些东西。
我们看到第2层CALL肯定是有参数的,进入它的内部:
这个0x10的话就是4个参数,这个CALL就可能是。
我们看到第3层CALL,进入该CALL内部,我们观察到函数的开头很熟悉,这是重叠建造的CALL:
之前我们找过这个CALL,这个不是,但是这个CALL其实咱们也是可以调用的(可以当做作业供你去测试分析)。
咱们来测试一下,看看第2层CALL的效果,试一下就知道它到底是什么功能了;
把断点都删除了,把相关汇编指令记录一下:
在第2层CALL位置下断点,然后咱们种植一个植物(冰豌豆):
断下来了以后看右下角堆栈窗口里的数据:
把这个冰豌豆铲除,换个地方种植冰豌豆:
这个位置的坐标有可能是(y=3, x=1),也有可能是(y=4, x=2),种下去后OD断下:
我们看到call之前给eax传入了一个数值,所以我们在这个指令上下断点,看一下eax是多少:
回到游戏铲除那个位置的冰豌豆,在原位置上再种冰豌豆,断下:
这样这个CALL的原型就出来了,咱们测试一下,用OD中的代码注入器:
把所有植物取消掉,然后注入写好的种植CALL到游戏进程中:
没有注入成功,修改注入的代码,push的地址必须是16进制的:
我们看到植物种在这上图红色方框那里,种到草地外面去了,修改注入代码eax的值为1:
我们把第3个push的值改为5看看:
咱们把第2个参数改为10,我们发现是荷叶,所以这个参数是植物的种类:
但是第4个参数是一个地址值,这个地址值是会变化的,所以我们还得搜索这个地址的来源:
这个ebp其实是一个特殊的寄存器,我们得找这个ebp里面的值的来源。
咱们到CE中搜索这个地址值:
这样就找到了ebp的值0C2B6008的基址为006A9EC0,偏移为0x768:
找到这个变动的地址的基址后,就可以修改注入代码了:
这种取地址内容的方括号,里面的地址前面加不加0x前缀都可以。
39、OpenProcess错误
我们独立运行编写好的程序,会出现无法成功执行OpenProcess,无法获取进程的权限,而导致读写失败。
也就是说,不在VS编程环境的调试下运行这个程序,而是把生成的exe程序拷贝出来,直接独立运行它的时候就不能正常修改阳光值等功能,所以就需要在运行之前需要提权,获取到打开进程的权限。
我们需要在每个函数执行之后进行判断,看看到底是哪步出的问题:
这样的话就会有一个提示,没有取到这个窗口的句柄,通过这种方法来判断究竟是哪里出现的问题。
在xp系统下可能就会碰到这种情况,在VS调试的时候没问题,但是独立运行程序的时候,无法打开进程(OpenProcess执行失败),所以就需要提权:
把这个提权函数加入到项目代码中,在打开进程之前要先执行这个函数获取权限:
40、错误:constchar[19]”转换为“LPCWSTR”
如果你勾选了上图中的使用 Unicode库
的话,会出现一个问题。
直接编译会出现一个错误:
怎么解决这个错误呢,就是在项目属性里修改字符集为使用多字节字符集
,这样编译就可以正常的通过了。
///