關(guān)于PVZ1的撐桿土豆偏移bug的淺析
特別聲明:我是用漢化一進行分析的,英原、漢化一等同理,年度版依舊可用
對于這個bug,具體的描述是:
在一行有四只落地的撐桿的情況下,本行的小鬼僵尸可以啃食到土豆雷而土豆雷不爆炸,并且啃不死
這個bug首先由碳酸(貼吧名見下文)于2021.7.30發(fā)現(xiàn)(同樣原理的4桿引雷最晚發(fā)現(xiàn)于2020.7.11,所以這里的發(fā)現(xiàn)是指這個bug的發(fā)現(xiàn)日期,而不是指這個bug的原理產(chǎn)生的任意bug的發(fā)現(xiàn)日期),由零度、惡魂(貼吧名見下文)研究,本專欄為該bug具體的分析,涉及到一定的匯編知識。

正文
原版土豆地雷的函數(shù)為
0045FE20 > Plant_UpdatePotatoMine
如果從這個函數(shù)向下看,找到對于狀態(tài)0x10的處理(0x10為土豆冒出后),可以發(fā)現(xiàn)這樣的代碼:
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的內(nèi)部內(nèi)容,會發(fā)現(xiàn)一個循環(huán)結(jié)構(gòu),而在循環(huán)結(jié)構(gòu)中,有這樣一段代碼:
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就是土豆雷,首先檢測是否為跳跳,撐桿顯然不是,跳過,而后面檢測撐桿是不是撐著桿的狀態(tài)或跳起的狀態(tài),都不是則檢測是不是已落地的撐桿,是則將esp+28加40,esp+30減40,問題就出在這里。
esp+28和esp+30控制著偏移量,但是esp+28和esp+30在每次循環(huán)的開始不會清空,所以會導致下一個僵尸依舊擁有這個偏移。
當偏移量大到一定量的時候,小鬼雖然在范圍內(nèi),但是偏移的太遠了,所以不會導致爆炸。
需要注意的是,這個bug和僵尸的序號(棧位)有關(guān)。
遍歷的順序是從[[[6a9ec0]+768]+90]開始,到[[[6a9ec0]+768]+90]+n*15C(顏色為方便指針的閱讀),如果小鬼的序號在撐桿之前,還沒有偏移就已經(jīng)搜索到小鬼了,依舊會導致爆炸(所以不是一旦有4個就炸不到,還得看情況)。
對于,為何要求是同行:
004676C0中ebx為植物僵尸行數(shù)差,土豆沒有什么特殊的判斷(其實有一個如果僵尸是瀕死的則跳過),所以按照正常的運行。
00467614? ?mov ebx,[esi+1C]
00467617? ?sub ebx,[ebp+0C]
……
004676C0? ?test ebx,ebx
004676C2? ?jne 00467884
所以如果不在同行會直接跳過,不影響偏移。
至于啃不死,就更好解釋了,涉及到這個函數(shù):
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
是非預備狀態(tài)的土豆雷則直接跳走,不執(zhí)行之后的扣血,所以才會無敵
另外,這個具體的偏移量是40px
其實,大嘴花也有類似的現(xiàn)象,但是是對于右行礦工而言的,具體代碼:
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
右行礦工會讓本行的大嘴花產(chǎn)生20像素的偏移

改版如何修復這個bug
很簡單,添加一個初始化。
可以在00467609跳出,然后加入初始化解決

實戰(zhàn)運用
由于本人更多的是一個改版作者,EL、IZE是偶爾玩玩啥的,所以這里就不提出什么運用了(多半是無盡養(yǎng)僵尸,IZ自制關(guān)之類的)
這就不是我擅長的領(lǐng)域了(笑)

結(jié)語
簡單的bug的分析而已,沒什么其他的。
但是我很期待利用這個bug做出的一些優(yōu)秀作品

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

參考資料與文獻
PVZ指針表
Hope_20121221_(貼吧名)等人于2011年、zjfaok(貼吧名)等人于2014年研究發(fā)布
崇明人家123(貼吧名)于2017、2018年收集整合
Dr丶小黑、康師傅豆腐、4573去、Ghastasaucey、六三enjoy(均為貼吧名)先后于2019到2021增添修補
PVZ函數(shù)表
失控的指令(貼吧名)于2020、2021年收集整理

文章作者:Ghastasaucey