一、准备
<1>、工具
- 逆向工具:IDA Freeware 8.4;
- 逆向对象:植物大战僵尸杂交版v2.0.88;(B站作者:潜艇伟伟迷)
- 逆向辅助工具:文心一言;(在此之前从未接触过汇编,所以利用AI快速了解汇编代码)
<2>、IDA快捷键
SHIFT+F12 打开Strings视图,包含有所有字符串 SHIFT+F3 打开Functions视图 空格 view视图下流程视图的切换 CTRL+F Strings视图/Functions视图下搜索 ALT+T view视图/pseudocode视图下搜索 F5 反汇编为C语言 F2 添加/删除断点 F7
单步步入 F8 单步跳过 F9 运行直到下一断点/结束 F11 全屏 G 地址跳转 X 展示引用当前(光标所在)方法的方法 ESC 返回上一个方法 ; 编辑注释 CTRL+Z 撤回上一步操作
- IDA顶部菜单 Debugger 下的 Debugger windows 下的 Locals 视图可以在动态分析时实时展示当前断点方法中的变量值内容;
- IDA顶部菜单 Edit 下的 Patch Program 下的 Assemble an instrction 可修改汇编代码(其他两个没用过)。同菜单中 Apply patches to input file 可将修改的代码应用到文件中。
<3>、其他
- 植物大战僵尸杂交版 在安装完成后需要运行一次,不然就只有一个启动文件 pvzHE-Launcher.exe(本人就在这个文件上浪费了太多时间)。运行完之后就会生成多个文件,其中就有一个我们的关注对象 PlantsVsZombies.exe。
二、开始
<1>、初次探索
使用IDA打开 PlantsVsZombies.exe 文件(第一次打开文件自行百度需要设置的点)之后不要急于分析代码。先根据对应文件以及自己的目标,大致猜测几个可能得关键词。(例如:Plant(s) 、Zombie(s)、bullet、shell、cannonballd 等)然后SHIFT+F12 打开Strings视图,接着CTRL+F搜索上面猜测的关键词(由于作者开发游戏时对于命名可能采用缩写等方式,所以在初次搜索时尽量不要使用完整的单词,我一般选择截取至少前四位字母的方式,如果搜到的内容较多便可以将搜索搜索词改的更精确些,否则可尝试一下截取前三位字母,再小就不建议了)。
以Plant(s)为例,如图所示:
此时我们就可以找一些我们感兴趣的字符串。如图中的 plants 。然后变可以双击鼠标左键查看,如图所示:
在这里我们就可以看到游戏开发者关于变量的命名结果(单引号中的)。此时你可以试着向上或者向下查找对你可能有帮助的变量名,亦或者继续使用上面最开始的查找方式。
之后我们便可以对变量进行一个全局的搜索(ALT+T)。下面是zombies(plants搜索结果一样)的搜索:
搜索结果:
这里我们可以发现搜索结果中有两个方法 sub_407B50 和 sub_418C70 (该方法对我们没有用,本人猜测可能是初始化,没有深究)。那么目标就放到了 sub_407B50 这里。双击sub_407B50 。如图所示(代码片段):
无奈看不懂,只能F5查看伪代码:
但是这还是不容易看出代码逻辑。于是,我便使用空格键切换到流程视图,在代码开始和结尾处各打了一个断点,想要通过debug看看代码大致执行顺序。但是我发现,当点击关卡时,会被断点拦截,同时,只有结尾处的断点放行后,才会正式进入游戏,于是我简单的向上找(引用该方法的方法)了一下,没有找到where等循环的代码结构,便放弃了继续找下去,将sub_407B50 记录了一下,就跳过了。
<2>、再次探索
1.经过上面的查找,我觉的我在关键词上的选择范围有点大了,于是我又开始试着以植物的名称(如:豌豆),攻击相关的字眼(如:发射),子弹相关(如:豌豆,刺,尖刺,玉米等)等进行查找。
2.在查找shoot时,发现了大量关于(anim_shooting)的结果,根据命名不难猜出这是关于触发射击动画的变量。
对Function进行排序后,发现sub_45EF10对anim_shooting 的使用最多,于是我猜测该方法可能是植物/僵尸攻击的方法。然后我就无脑的对sub_45EF10里面anim_shooting 行代码进行断点。
继续debug。此时令人兴奋的时刻到了。
种植豌豆后,没有任何事发生。但当僵尸刷新出来后,进程被断点拦截住了。
于是我开始F8一点一点的步进,最终发现,知道代码运行到141行返回结果时,子弹依旧没有出现。
于是我继续F8来到了 sub_45F8A0 的 36行:
这是什么鬼,这行哪里调用了sub_45EF10,但还是硬着头皮F8,于是来到了43行,可这任然没有得到我期望的结果,只能试着继续F8,期望能看到子弹的出现。可没多久,我就在向上走了三个方法到sub_4130D0 时(这之间没进过几个方法,几乎一路return),我看到了循环:
这也就意味着,子弹的发射确确实实和 sub_45EF10有关。(原因:sub_45F8A0下的 sub_45EF10 有字符串 anim_shooting 。并且,只有子弹发射前一刻,该方法会拦截到进程)
至于子弹没出现可能是渲染顺序方面的原因吧。为了继续佐证我的猜想,我便在 sub_45EF10的上级sub_45F8A0开始尝试打断点。
从这两个断点中我知道了if外在不断地循环中,当只要进入if内,子弹就会发射。也就是说,这里起到了计时器的作用,那么此时我将 0 调节的更大,这样就缩短了发射子弹的时间。
<3>、尝试修改代码
在第八行打上断点,将视图切换到view视图(有汇编代码的视图),然后F9,等待拦截到。
这里(如果看不懂汇编,可以直接将代码复制给 文心一言 )的代码:
cmp eax, 0 ;和零进行比较 jg short loc_45F91C ;如果eax中的值大于0,就跳转到loc_45F91C对应的代码块,如果小于等于零就跳过loc_45F91C
也就是说我们只要改变 cmp eax,0 中的0值即可。
点击ok(输入100会被自动改为64h,最好还是直接手动输入16进制数吧),取消断点看结果:
可是我发现该处只对冰豌豆有效,但对普通豌豆只加速了发射子弹的动画,至于发射子弹则是普通豌豆发射不了子弹了,对于其他植物也无正向效果。只不是我期望的,于是恢复修改,继续在此基础上分析。既然上面的操作能影响冰豌豆的攻击速度,那么if中一定有攻击方法,并且断点步进只经过sub_45EF10:
那么接下来就在sub_45EF10 中断点分析,由于之前猜想的关于 anim_shoot 的方法都可能是判定动画,和执行动画方法。与攻击方法应属于并列的关系,所以我们可以暂时将注意力集中到不含有anim_ 的方法上。但注意观察游戏界面的变化(这可能有助于缩下分析范围)。
然后我找到了四个方法,其中有两个和疑似动画相关的方法关联性比较高:
明显sub_473310 的可能性比较大,接下来就是验证哪一个是攻击方法。(想办法阻止其中一个方法不执行)但是ida可以很容易修改代码,但很难新增代码,所以我这里就试着将 v9 改为 !v9:
test ebx, ebx ;与自身and运算 jz loc_45F09E ;如果等于0则跳转loc_45F09E---修改jz为jnz ;相等与if(v9) push offset aAnimShooting ; "anim_shooting" ;设置参数 call sub_4732C0 ;调用方法 test al, al ;与自身and运算 jz loc_45F09E ;如果等于0则跳转loc_45F09E ;相等与if(v9) ;--------------------否则执行下面代码----------------- push 14h push ebx call sub_473310
修改后:
没有影响子弹发射,继续:
也没有用,那么就恢复修改,对 sub_466E00 重复上面操作,可能问题来了,我在183行和140行打的断点并没有拦截到,而141行的断点却拦截到了(造成的原因可能是返回值对应的汇编代码是被共用的,所以会被拦截到,但当时我没意识到,所以我就开始好奇,难道sub_466E00真的没被执行吗。)。于是我就在sub_466E00 里面打了一个点,结果真让我瞎猫碰上死耗子了(我承认我有赌的成分
),于是我就长按F8(也可以找到所有return打点F9)想看看是那个方法调用的:
结果来到了sub_464820 ,同时我发现我在sub_466E00打的第一个点:
这里拦截几次就有几个子弹产生,而且不分植物。之后我就将 sub_464820 中所有调用 sub_466E00 的地方打上断点,结果是仙人掌在312行被拦截:
冰豌豆在321行被拦截(321对应的代码块被其他地方调用,通过步进来确定是哪个植物调的哪一行即可)
然后我对较容易修改的321行进行修改 if(result) 改为if(!result),结果是冰豌豆只有攻击动画没有子弹产生。那么到这里,我们就可以猜测出sub_464820是用来判断哪种植物发射哪种子弹(与参数V2中的值直接相关,可以通过 Locals 视图查看)而sub_466E00则是处理子弹的方法。由于我没有找到V2如何设置值的,所以我就尝试修改判断条件让321行的代码尽可能多的执行(中间有点繁杂无味就跳过了),结果如下:
267-270原本的代码是v2中的下标36对应的值如果等于 1 则直接return出该方法,而我则将其改为了不等于1。这样冰豌豆的sub_466E00触发几率就增大了。修改后应用到文件中的效果如下:
<4>、最后
通过上面的步骤我又找到控制僵尸(包括僵尸输出数量,位置等)和毁灭加农炮的相关的方法:
三、结束
本篇目的是仅为了学习交流!
由于初次逆向,对于逆向过程还有很多不足之处,希望道友们多开金口。
关于文中提到的V2值的设置(或者其他好玩有意思的点),有知道的盆友也可以告诉我一下。
最后呢,我也想诚恳的请教大佬们,我有可以根据地址向内存中写入值的代码,但我不知道如何搞到基地址+偏移量,是否与view视图中的地址有关?还请赐教,谢谢。