关于PVZ1的撑杆土豆偏移bug的浅析
特别声明:我是用汉化一进行分析的,英原、汉化一等同理,年度版依旧可用
对于这个bug,具体的描述是:
在一行有四只落地的撑杆的情况下,本行的小鬼僵尸可以啃食到土豆雷而土豆雷不爆炸,并且啃不死
这个bug首先由碳酸(贴吧名见下文)于2021.7.30发现(同样原理的4杆引雷最晚发现于2020.7.11,所以这里的发现是指这个bug的发现日期,而不是指这个bug的原理产生的任意bug的发现日期),由零度、恶魂(贴吧名见下文)研究,本专栏为该bug具体的分析,涉及到一定的汇编知识。

正文
原版土豆地雷的函数为
0045FE20 > Plant_UpdatePotatoMine
如果从这个函数向下看,找到对于状态0x10的处理(0x10为土豆冒出后),可以发现这样的代码:
0045FFFE cmp eax,10 { 16 }
00460001 jne 00460053
00460003 mov eax,[edi+1C]
00460006 push eax
00460007 push edi
00460008 xor ecx,ecx
0046000A call 004675C0
0046000F test eax,eax
00460011 je 0046001F
00460013 push edi
00460014 call 004666A0
00460019 pop edi
0046001A pop esi
0046001B pop ebp
0046001C pop ebx
0046001D pop ecx
0046001E ret
这段代码比较让人注意的两个call,004666A0控制着爆炸,而这里的重点在于
004675C0 > Plant_SearchZombie
我们看一下这个call的内部内容,会发现一个循环结构,而在循环结构中,有这样一段代码:
00467731 cmp edx,04
00467734 jne 00467794
00467736 mov ecx,[esi+24]
00467739 cmp ecx,12
0046773C jne 0046774B
0046773E cmp byte ptr [esi+000000BC],00
00467745 jne 00467884
0046774B mov eax,[esi+28]
0046774E cmp eax,0C
00467751 je 00467884
00467757 cmp eax,0B
0046775A je 00467884
00467760 cmp ecx,03
00467763 jne 00467772
00467765 mov eax,00000028
0046776A add [esp+28],eax
0046776E sub [esp+30],eax
00467772 cmp ecx,14
00467775 jne 00467786
00467777 mov eax,[esi+00000080]
0046777D cmp eax,[edi+28]
00467780 jne 00467884
00467786 cmp byte ptr [esi+51],00
0046778A je 00467794
0046778C mov [esp+14],0000001E
这里,edx是植物类型,0x4就是土豆雷,首先检测是否为跳跳,撑杆显然不是,跳过,而后面检测撑杆是不是撑着杆的状态或跳起的状态,都不是则检测是不是已落地的撑杆,是则将esp+28加40,esp+30减40,问题就出在这里。
esp+28和esp+30控制着偏移量,但是esp+28和esp+30在每次循环的开始不会清空,所以会导致下一个僵尸依旧拥有这个偏移。
当偏移量大到一定量的时候,小鬼虽然在范围内,但是偏移的太远了,所以不会导致爆炸。
需要注意的是,这个bug和僵尸的序号(栈位)有关。
遍历的顺序是从[[[6a9ec0]+768]+90]开始,到[[[6a9ec0]+768]+90]+n*15C(颜色为方便指针的阅读),如果小鬼的序号在撑杆之前,还没有偏移就已经搜索到小鬼了,依旧会导致爆炸(所以不是一旦有4个就炸不到,还得看情况)。
对于,为何要求是同行:
004676C0中ebx为植物僵尸行数差,土豆没有什么特殊的判断(其实有一个如果僵尸是濒死的则跳过),所以按照正常的运行。
00467614 mov ebx,[esi+1C]
00467617 sub ebx,[ebp+0C]
……
004676C0 test ebx,ebx
004676C2 jne 00467884
所以如果不在同行会直接跳过,不影响偏移。
至于啃不死,就更好解释了,涉及到这个函数:
0052FB40->Zombie_EatPlant
扣血的地址在:
0052FCF0 add dword ptr [esi+40],-04
但是,有这样的代码:
0052FC31 cmp ecx,04
0052FC34 jne 0052FC40
0052FC36 cmp dword ptr [esi+3C],00
0052FC3A jne 0052FDEE
0052FC40 xor al,al
是非预备状态的土豆雷则直接跳走,不执行之后的扣血,所以才会无敌
另外,这个具体的偏移量是40px
其实,大嘴花也有类似的现象,但是是对于右行矿工而言的,具体代码:
004676DD cmp edx,06
004676E0 mov [esp+14],00000000
004676E8 jne 00467731
004676EA mov eax,[esi+28]
004676ED cmp eax,25
004676F0 jne 004676FF
004676F2 mov ecx,00000014
004676F7 add [esp+28],ecx
004676FB sub [esp+30],ecx
右行矿工会让本行的大嘴花产生20像素的偏移

改版如何修复这个bug
很简单,添加一个初始化。
可以在00467609跳出,然后加入初始化解决

实战运用
由于本人更多的是一个改版作者,EL、IZE是偶尔玩玩啥的,所以这里就不提出什么运用了(多半是无尽养僵尸,IZ自制关之类的)
这就不是我擅长的领域了(笑)

结语
简单的bug的分析而已,没什么其他的。
但是我很期待利用这个bug做出的一些优秀作品

本文涉及到的人名对于的贴吧名
碳酸->111111和1
零度->失控的指令
恶魂->Ghastasaucey

参考资料与文献
PVZ指针表
Hope_20121221_(贴吧名)等人于2011年、zjfaok(贴吧名)等人于2014年研究发布
崇明人家123(贴吧名)于2017、2018年收集整合
Dr丶小黑、康师傅豆腐、4573去、Ghastasaucey、六三enjoy(均为贴吧名)先后于2019到2021增添修补
PVZ函数表
失控的指令(贴吧名)于2020、2021年收集整理

文章作者:Ghastasaucey

