植物大战僵尸分析及游戏辅助Python实现
起因
网上已经有很多帖子分析植物大战僵尸冷却的。有的看不出是个什么逻辑,一会搜索1,一会搜索0的,脑子笨想不出来是什么门道。
没办法只能自己操刀按自己的想法分析看看了,就当学习。
更多资源请访问 大神论坛
准备工具
x32dbg
植物大战僵尸
Cheat Engine 7.1
过程
先把阳光数量变成可控的,阳光基址就不说怎么找了。其实不找也行只要把阳光变的多点就行,目的就是能随时用阳光。
按照网上的思路反复搜索1,搜索0的确是能做到无CD,但是看不出门道。换一个思路,冷却时间是最为直观的。就是种上植物之后,卡片会变成冷却状态,有一个冷却进度,如下图的豌豆射手。
文章图片
所以目标就是先搜索出来记录这个冷却时间的地址。
这个冷却时间有两种可能,
种上植物后,程序设置一个冷却时间,之后慢慢的往下减,减到0冷却就没了。
种上植物后,程序将变量置0,开始计时,慢慢的增加,增加到某个阈值,就是无冷却了。(其实是这个)
搜索方法为了统一,不管它是增加计时,还是减数计时,搜索方法就一个。
搜索记录冷却时间的地址
搜索方法
搜索未知的初始值
冷却阴影变动,就再搜索变动了的数值
未发现第一格冷却计时地址,接着重复第2步
先用CE附加植物大战僵尸进程。
接着种上植物,CE搜索未知的初始值。
首次扫描
文章图片
冷却阴影变动后按下图操作
再次扫描
文章图片
一直重复再次扫描的步骤,慢慢的筛选,当一个冷却结束后,可以再种一个植物(必须是同一格)。
其实中间可以穿插一些未变动的数值搜索,这样可以更快的检索出结果来。
搜索结果
文章图片
看动图可以种植的时候是0,有冷却时间的时候随时间增长。
很明显就是第一格的冷却时间,把他拉下来,右击后选择「找出是什么改写了这个地址」
文章图片
记录下改写指令的汇编地址004B2FEA
文章图片
实现无冷却
CE先使用一阶段,关掉CE,打开X32dbg附加植物大战僵尸
文章图片
按下Alt+A附加上植物大战僵尸进程
文章图片
Ctrl + g到跳转窗口,跳转到刚刚记录下来的004B2FEA地址。
文章图片
跳转到地址004B2FEA指令处的分析。
文章图片
// 伪代码大概就是这个意思
// …
coolDownTime++;
if ( coolDownTime > 当前植物的冷却需要的总时长) {
// 取消冷却
cancelCooling(当前植物);
}
// …
实现无冷却有一下两种方法。
方法一
nop掉004B2FF3跳转指令
// 改成这个样子
// …
coolDownTime++;
// 取消冷却
cancelCooling(当前植物);
// …
文章图片
方法二
把比较换成与0比较
// 改成这个样子
// …
coolDownTime++;
if ( coolDownTime > 0) {
// 取消冷却
cancelCooling(当前植物);
}
// …
文章图片
这样改过之后打上补丁玩个游戏就没意思了,可以用CE代码注入,实现动态修改。
这里只演示第二种方法,第二种会了第一种方法就是大同小异。
x32dbg剥离植物大战僵尸进程
文章图片
换CE附加上植物大战僵尸进程,点击查看内存。
文章图片
ctrl + g跳转到刚记录的004B2FEA指令地址(就是+1指令的地址)
文章图片
实现的是第二种方法,所以我们选中下面这条指令
PlantsVsZombies.exe+B2FF0 - 3B 47 28 - cmp eax,[edi+28]
文章图片
安装下面图片一步一步来
文章图片
文章图片
文章图片
下面是生成出来的代码,没有任何改动
文章图片
修改后的代码
文章图片
[ENABLE]
//code from here to ‘[DISABLE]’ will be used to enable the cheat
alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)
【逆向分析|大神论坛 史上最全植物大战僵尸分析及游戏辅助Python实现】newmem: //this is allocated memory, you have read,write,execute access
//place your code here
originalcode:
// cmp eax,[edi+28]
cmp eax, 0
jle PlantsVsZombies.exe+B3007
exit:
jmp returnhere
“PlantsVsZombies.exe”+B2FF0:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
“PlantsVsZombies.exe”+B2FF0:
cmp eax,[edi+28]
jle PlantsVsZombies.exe+B3007
//Alt: db 3B 47 28 7E 12
接着就是分配到当前的CT表,保存完这个页面就可以关了。
文章图片
在主界面就能看到刚刚保存的脚本
文章图片
这样可以动态修改指令。勾上就是无冷却,取消就是正常代码。
第一种方法和第二种方法都可以实现,第一种方法留下自己实现吧。
看看效果
文章图片
游戏,索然无味。
到这里还没有结束,不是为了实现冷却,而是尽量的多分析一下看看。
继续深入一点分析-挖掘基址
把刚刚汇编的地方还原,跳出当前函数看看上一层的逻辑。
断在004B2FEA之处后,先点击运行到返回 => 在单步步过一下 ( 或者双击右下角堆栈窗口的0019F9EC地址数据,也可以跳转到上一层逻辑)
文章图片
上一层函数的逻辑
文章图片
形成伪代码就是
// …
cardList = [豌豆射手, 向日葵, 樱桃炸弹 …]
cardCount = [eax + 24] // 当前可使用的植物卡片数量
count = 0 // xor ebx, ebx
if (cardCount > 0) {
do {
card = cardList[count];
handlerCard(card);
// 最开始分析的call, 里面执行+1 重置冷却时间
count++;
} while(count < cardCount);
}
// …
逻辑大致是清晰了,现在找基址一下基址。现在关注一些上下文信息
文章图片
汇编语句 00428E40 | 8B87 5C010000 | mov eax,dword ptr ds:[edi+15C] |
00428E46(断点处EIP) | 33F6 | xor esi,esi | esi = 0
00428E48 | 3958 24 | cmp dword ptr ds:[eax+24],ebx | [eax + 24] 是格子数量 最上面(xor ebx, ebx)
00428E4B | 7E 1B | jle plantsvszombies.428E68 | if
00428E4D | 8D49 00 | lea ecx,dword ptr ds:[ecx] |
00428E50 | 8D4403 28 | lea eax,dword ptr ds:[ebx+eax+28] | 这是数组
00428E54 | E8 57A10800 | call plantsvszombies.4B2FB0 | 刚刚分析的就是这个call
00428E59 | 8B87 5C010000 | mov eax,dword ptr ds:[edi+15C] |
00428E5F | 46 | inc esi | i++
00428E60 | 83C3 50 | add ebx,50 |
00428E63 | 3B70 24 | cmp esi,dword ptr ds:[eax+24] | esi < [eax + 24]
00428E66 | 7C E8 | jl plantsvszombies.428E50 |
寄存器信息 EAX : 19B9EC90
EBX : 00000000
ECX : 0000019E
EDX : 00000163
EBP : 0019FA5C
ESP : 0019F9F0
ESI : 1FF12C18
EDI : 200DB408
EIP : 00428E46
call plantsvszombies.4B2FB0需要传入植物卡片的地址([ebx+eax+28] ),eax是上下文提供的,ebx是偏移相当于高级语言中i++的作用。
现在eax的值不确定,有两种方法,
一个是向上看eax是谁给的,慢慢向上一层一层的找
使用CE搜索eax寄存器的值19B9EC90。(eax = [edi+15C], 搜索edi(200DB408)就行)
我选择第二种,因为第一种我试过了,不好使不方便。
第二种方式:x32dbg剥离植物大战僵尸进程。
文章图片
换成CE加载,搜索edi寄存器的值200DB408(注意先勾上十六进制)
文章图片
拉下来右键=>『找出是什么访问了这个地址』。
文章图片
文章图片
搜索ESI值0295B120
文章图片
基址都记录下来吧,以后可能都会用到。(其实随便选一个就行)
PlantsVsZombies.exe+3794F8
PlantsVsZombies.exe+37959C
PlantsVsZombies.exe+379670
PlantsVsZombies.exe+379618 组合分析使用基址 // 猜测 CARDS { // .... int count;
// +0x24 Card list[X];
// +0x28 每个Card大小为0x50 // 后面分析这个数组好像是固定长度 控制 [CARDS + 0x24] 可以扩容和减少格数 // 设置超过数组的 size 就会程序异常 // ..... } //植物卡片对象 地址
CARDS = [[[PlantsVsZombies.exe+379618] + 0x868] + 0x15C]
//植物卡片个数 可以实现扩容和减少格数
[CARDS + 0x24]
// 第N格植物卡片地址 N > 0
[0x50 * N + CARDS + 0x28]
// 第N格植物卡片地址 N > 0 冷却记录
[0x50 * N + CARDS + 0x28 + 0x24]
// 第N格植物卡片地址 N > 0 冷却时间
[0x50 * N + CARDS + 0x28 + 0x28]
// 模块起始地址
00400000 - PlantsVsZombies.exe
有了这些分析就可以写代码了。
Python 实现辅助
下面放一部分,具体的全部代码可以看附录里面的代码,大多读写内存的操作都是封装的Win32Api(还不如用C++写)
省略 … pid = get_pid(“PlantsVsZombies.exe”)
print("PlantsVsZombies.exe 进程id: " + str(pid))
processHandle = open_process(pid)
def showCard(processHandle):
count = plant_cards_count(processHandle)
os.system(“cls”)
x = PrettyTable([“格子”, “当前冷却时间”, “总时间”])
for n in range(count):
plant_info = get_plant_n(processHandle, n)
x.add_row([n+1, plant_info[“cooling”], plant_info[“cool_time”]])
title = “阳光:{}”.format(getSunCount(processHandle))
print(x.get_string(title=title))
def drawTable():
while True:
showCard(processHandle)
time.sleep(1)
滚动滚轮 def on_scroll(x, y, dx, dy):
# dy == -1 下滑
# dy == 1 上滑
sunCount = getSunCount(processHandle)
setSunCount(processHandle, sunCount + (5 * dy))
省略 … 效果:向上滚动滚轮增加阳光,向下滚动滚轮减少阳光(注意动态中阳光的变化)。可以看到有几个格子,冷却时间,总时间。
修改的功能就只有一个阳光,其他的就不写了,写多了也没意义,现在不会还有人玩这游戏的吧哈哈。有兴趣的可以下载附件中的CT(里面包含无冷却,格子数量的控制功能)。写这个Python辅助脚本的目的就是验证上面分析出来的基址和信息对不对,别无目的。
总结
整个过程的分析是完成了,就像开篇说的搜索1/0的方法还是没有探索出来是怎么影响的,知道『原理』的可以撩一撩,是从什么角度分析的,学习一下。
文中提到的方式都可以实现无冷却,分析过程中也随便扒拉出了其他信息,比如当前格子的冷却,当前植物冷却时间的总时间,还有格子数量[CARDS + 0x24]。分析该到一段落了,大多分析结果都是通过少量已知的上下文条件,和我的经验推断出来的(不一定准确),如有异议可以回帖交流啦。
推荐下载相同版本的植物大战僵尸,不同版本CT文件可能不通用,软件还有全部的游戏辅助源码都在下方链接文章中,欢迎下载并交流沟通
https://www.dslt.tech/article-206-1.html
版权声明:本文由 syncking 原创,欢迎分享本文,转载请保留出处
推荐阅读
- Kotlin 编程语言成为其 Android 应用程序开发人员的首选语言
- Python集合模块(collections用法示例)
- Python变量使用详解
- Python while循环语句用法
- Python教程介绍
- Python元组介绍及其操作函数详解
- Python字符串用法及其操作函数
- Python pass语句用法示例
- Python运算符完全解读