6.4 植物大战僵尸:寻找阳光掉落CALL
《植物大战僵尸》是一款非常经典的塔防类游戏,由PopCap Games
公司开发并先后在多个平台上推出。主要玩法为种植各种攻击性植物,抵御僵尸攻击,该游戏可以说绝大多数九零后都接触或者玩过,本章将通过逆向分析技术对该游戏进行分析,并实现一些游戏之外的功能,以此让用户理解二进制安全技术的应用范围。
本次实验将接触到Call
调用这个概念,Call相当于你在编程时所编写的函数,而高级语言中的函数最终也是会被编译器转换为汇编格式的Call调用,这些关键Call普遍都会存在各种参数,关于Call的作用,打个比方有些网游外挂可以实现自动寻路,自动吃药,自动打怪,甚至是全屏秒杀,这些功能是通过修改数值也无法做到的,Call就可做到。
其实关键Call就是作者开发过程中写的一个个处理不同事件的独立的处理函数,这些函数包括了各种独立的游戏功能,而我们可以在远程进程中开辟线程,并通过汇编形式动态的调用这些关键Call,从而实现一些变态功能。
我们通过查找阳光的掉落的定时器,并通过定时器变量顺藤摸瓜找到生成阳光的关键Call,通过给此Call传递不同参数实现掉落阳光,钻石,零秒通关等,阳光遍历技巧如下:
- 进入游戏等待阳光出现 > 当阳光出现后 > 马上搜索未知初始数值
- 返回游戏等待时钟发生变化 > 马上切回CE > 搜索减少的数值 > 阳光下落一点搜一点
- 经过上方步骤反复排查 > 最终能找到一个值范围(0-700) > 锁定1即可实现无限掉落
关于阳光掉落基址与偏移的找法,在文章开头就已经分享了查找的技巧,此处直接给出控制阳光掉落公式 [[[006A9F38 + 768] + 5538]]
- 00413B7C - 83 86 38550000 FF - add dword ptr [esi+00005538],-01
- 004524F4 - 8B 86 68070000 - mov eax,[esi+00000768]
- 00599F75 - A1 389F6A00 - mov eax,[PlantsVsZombies.exe+2A9F38]
读者可自行将这段基址与偏移添加到CE的地址栏中,当添加好以后,读者应该能看到如下图所示的提示信息;
我们要找关键Call
只需要等待阳光出现,当阳光出现后,CE会检测到一条数据 add dword ptr [esi+00005538],-01
说明此时定时器开始工作了,我们只需要记下这个内存地址00413B7C
,然后关闭CE,并打开x64dbg
附加到游戏。
此时我们关闭CE修改器,并打开x64dbg
附加到游戏进程,然后按下Ctrl+G
定位到00413B7C
此时我们需要关注add dword ptr ds:[esi+0x5538],0xFFFFFFFF
这条计时器指令下方,就是一个大跳转该跳转跳向了结束,那么我们可以猜测jne 0x413BF1
跳过的内容很有可能就是跳过了阳光的生成过程,也就是可能跳过了阳光生成Call。
玩过此游戏的一定知道,游戏屏幕中不止可以掉落出阳光,还可以掉出其他的东西,比如钻石,金钱,奖牌等,那么我们有理由相信,该游戏中调用的Call应该有很多参数传递,比如掉落属性,掉落坐标,掉落类型等,而我们已经找到了阳光计时器每次递减的汇编代码,故猜测调用Call应该就在附近,向下查找发现有很多Call
调用,但有一个比较特别的call 0x40CB10
,之所以说它特别是因为该Call
在调用之前,通过Push
传递了多了参数,此处很可疑,我们需要具体分析。
此时我们在00413BE4
处下断点,然后回到游戏等待阳光的掉落,当阳光掉落时x64dbg会断下,此时我们将EAX寄存器内的4修改为2,并运行运行程序,观察会出现什么情况;
当读者运行后,回到游戏中会发现本应该出现阳光的地方居然出现了一个金币,而根据测试金币所代表的数字应该就是2了;
由上方的分析结果可知,当我们给EAX传入不同的值则代表不同的物件,如下是本人经过不断尝试后得出的一些物件的具体下标值;
// 第一个push参数传递 |
到此,我们还没有办法完成外部注入,因为据我观察程序中的edi寄存器与ecx寄存器中的数据是动态的,每次游戏重新运行都会发生变化,如果想要在外部调用这个Call函数,我们需要找到这两个寄存器的基址,或者说找到他们的来源。
先来分析第一个地址如何定位,第一个动态地址是EDI寄存器,这个寄存器每次存储一个整数,此处无法直接找基址,我采取的方式是从后向前逆推代码,观察那些指令改写了该寄存器,然后将这些改写寄存器的指令拼凑起来。
这些调用分别是上图中的,标签赋值1
与标签赋值3
,另外加一个call 0x005AF400
,程序中正是通过这种方式计算出动态数据的,我们直接将其提取出来,提取后代码如下:
00413B85 | B8 26020000 | mov eax,226 | 赋值1 |
再来分析第二个也就是图中的call 0x0040CB10
,第二个相对于第一个来说就好找许多了,因为它是一个动态地址,我们再次回到游戏并等待游戏中阳光的掉落,当阳光掉落后则会断在call 0x0040CB10
位置处,我们此时需要记下ECX寄存器
中的值,此处的地址是1359D108
如下图所示;
接着我们在x64dbg
中选择脱离进程(注意不可结束),接着我们打开CE修改器,然后我们来搜索十六进制数据1359D108
搜索后可得到如下图所示的信息,此处可知一级偏移值是0x768
,接着搜索02729F30
内存地址;
- 004524F4 - 8B 86 68070000 - mov eax,[esi+00000768]
当读者搜索02729F30
后会找到0x6A9EC0
这个内存地址,该地址也就是我们所需要找的基地址了,总结起来寻址公式为[6A9EC0+768]
,从这里也可以看出这应该是一个通用对象地址。
- 00467B00 - 8B 0D C09E6A00 - mov ecx,[006A9EC0]
当读者找到了这些地址以后,相信你应该写出一段具有实际功能的注入代码了,笔者实现的代码流程如下所示;
pushad |
至此我们需要测试一下这段反汇编代码的可用性,读者可以找任意一款反汇编代码注入软件,并点击注入按钮测试效果,但为了能够写出自己的辅助,我们还是需要实现一个简单的注入器,并能够通过远程的方式调用阳光生成Call,使用C语言编写如下代码即可实现注入效果,需要注意调用约定。
|
当读者运行这段代码后,则游戏内会生成50
个大阳光,当然读者也可以自定义修改例如掉落钻石或者是其他物件,输出效果图如下所示;