1. 程式人生 > >吞食魚2(FeedingFrenzyTwo) 修改器

吞食魚2(FeedingFrenzyTwo) 修改器

# 吞食魚2(FeedingFrenzyTwo) 修改器 童年回憶系列。小時候特別喜歡玩這類遊戲,軟體不大,很慢的網速也不會下載太久,然後對配置要求不高,很破的電腦也可以玩得很開心。不過也有糟心的時候啊,大魚太多,無數次死於挑戰咬梭子魚的尾巴……今年最後一天,就休閒一小會吧。 ![image-20201230224005827](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012245image-20201230224005827.png) ![image-20201231012316488](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012428image-20201231012316488.png) 小時候看不懂是啥意思,現在知道了,我來翻譯下第一段。 > **警告!** > > 發現梭子魚在珊瑚礁附近遊蕩。留意警告標誌,遠離它張開的大嘴!要是你有迷之自信,可以試著咬它的尾巴。咬 4 次就會有驚喜……如果到那時候你還活著…… 現在再玩已經沒有當年的感覺了——滑鼠換了……其實我老早就想,為什麼到了下一關,我的魚就變小了??太不爽了。遊戲裡各種對玩家不利的設定:小魚不能吃大魚、被大魚追、被貝殼吃掉、被烏賊噴、被水母電、被大嘴鳥吞、被水雷炸、被河豚刺、吸水還要 CD、跳出水面翻滾的話進水眩暈……所以只有修改記憶體資料才是稱霸海洋的祕訣! 工具:Cheat Engine 6.4 (以前搞的漢化版,其實翻譯不完全,建議用英文版) ## 大,大,大 這個遊戲的規則是吃小魚,躲大魚,等長大了以後就可以吃遍全圖。成長值進度條在左上角,前面的關卡都是 3 個成長階段,到了後面還有更多的級別。 ![image-20201230224112420](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012252image-20201230224112420.png) 好的,按照程式設計師的思路,姑且猜測 growth 資料用整型儲存,每種魚的加成不一樣,小魚少些,大魚多些,初始值為 0,開啟 CE ,開始搜尋,每吃一條魚就搜一次“增加的數值”。 ![image-20201230225023229](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012258image-20201230225023229.png) 好的,沒搜幾次,輕鬆搜到 2 個值。嘗試修改第一個,發現魚沒反應;再嘗試第二個,魚長大了!然後自然是一頓操作:找出改寫 -> 顯示反彙編,然後看到了如下程式碼: ![image-20201230225514423](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012303image-20201230225514423.png) 這也太舒服了,直接找到靜態地址`005AC624`,省的找基址了,直接手動新增地址,growth 就有了。基址`005AC624`偏移`+3C, +40`(跟基址寫成 `"FeedingFrenzyTwo.exe"+001AC624` 是一樣的) ![image-20201230225654162](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012310image-20201230225654162.png) ## 四兩吃千斤 修改 growth 的數值,就可以瞬間變大,通關,但是這一點也不爽啊,我一定要把被吃的仇報了才行! ![image-20201230224112420](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012252image-20201230224112420.png) 還是剛才的進度條,用通常思路來猜,成長階段數值分別是 0, 1, 2 ,姑且先試一下。藉助修改 growth 值快速長大,搜尋這幾個值就很快了。 ![image-20201230231355759](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012314image-20201230231355759.png) 先嚐試修改第一個,圖中我修改 `0245AE24` 的值為 0 以後,我 2 階的大魚被比我小的魚吃掉了,那應該就是它了。觀察了一會發現,這個值就是用來確定會不會被吃的,但是查詢訪問它的程式碼並沒找到什麼關鍵判定,程式碼太多了,我也懶得看,反正只要不被吃復位,這個值就不會變,直接把它改大點就行了。 在魚變大的時候查詢改寫它的地址,可以找到偏移量 `EC` `00496B47 - 89 99 EC000000 - mov [ecx+000000EC],ebx` 再稍微除錯跟蹤下,找到基址。基址`005AC624`偏移`+40, +344, +0, +EC`,型別我選了 byte ,不過應該沒啥影響。 ![image-20201230232503913](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012327image-20201230232503913.png) ![image-20201230232204382](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012319image-20201230232204382.png) 然而事實是,我不會被大魚吃掉了,但是在大魚旁邊的時候也不會觸發吃魚的動作了……是太難吃了嗎?哈哈……不過,離成功不遠了。經過一番探索,我用“增大的數值”搜尋前邊的成長階段,找到了另一個數值。簡單點來說,這兩個值一個是玩家魚在電腦魚面前的大小,一個是電腦魚在玩家魚面前的小大。<- 我說小大,因為第二個值數值越大判定電腦魚越小。 基址`005AC624`偏移`+40, +344, +0, +F8`,就在上一個值旁邊,很狡猾啊,這個值是從 1 到 3 的,之前搜精確數值的時候沒找到,早知道就先去看看資料結構了。 ![image-20201230232932352](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012335image-20201230232932352.png) 把這兩個值同時改成 5 ,終於報了當年的血海深仇,啊哈哈哈哈哈哈哈…… ![image-20201230233834408](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012338image-20201230233834408.png) ## 速度和位置 吸取剛才的教訓,現在來看看資料結構。 ![image-20201230234630832](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012344image-20201230234630832.png) 嘿嘿,果然有了意外收穫。仔細觀察魚的狀態和數值,可以發現上面的 4 組浮點數分別代表魚的位置和速度,修改這些值可以讓魚瞬移到地圖任何地方。 然後我就有了一個大膽的想法——能不能把地圖裡的其他魚瞬移到我嘴邊呢?省的亂跑了。事實是——YES! 首先要找到存放地圖上所有魚的地方。 拿玩家魚的位置來說,地址是 基址`005AC624`偏移`+40, +344, +0, +98`,按照程式的物件模型來想,`+344`指標應該是玩家指標,裡面存放了很多和玩家相關的資料,剛才做四兩吃千斤的時候,資料也在這個物件下,那麼看下`+0`指標應該就是玩家的魚的指標了,我這次遊戲的指標是 `09397280` 。好的現在假設有這麼個全域性魚陣列,那麼這個 `09397280` 也一定在裡面,直接搜尋這個指標: ![image-20201230235851446](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012348image-20201230235851446.png) 嘿嘿,我為什麼單獨標出來這個 `07EDC488` 呢?因為看資料結構,在之前的 `+344` 指標前面,在 `+324` 指標的地方,指向的資料不就是 `07EDC488` 嘛,這應該是個陣列首地址,展開一看,果然全都是魚! ![image-20201231000302729](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012354image-20201231000302729.png) ![image-20201231000449674](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012358image-20201231000449674.png) 魚是有了,可是數量不知道……這個陣列沒有結束標識,貌似是像`vector`那樣管理的,有固定大小,靠整數標記結束的位置,而遊戲本身可能不記錄實時的魚數量,所以我找了一遍,一直沒找到資料,也可能是我找的方法不對。魚在不同狀態的時候地圖上的魚數量會變,但是因為不知道具體值,非常難找,我猜測是一些常數,規定了不同關卡魚的數量上限。 我還強行試了下移動所有魚直到空指標,會訪問銷燬過的魚物件而導致訪問越界崩潰,問題應該出在這裡 `mov ebx,[eax+edi]//4*n` ,`edi` 的值增大以後,`eax+edi` 就不一定是有效地址了。然而令人驚奇的是,只要不點錯誤視窗上的確定鍵,就還可以繼續遊戲!真神奇……指令碼如下: ```x86asm [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat //0051A002 alloc(newm,512) label(nextfish) label(exit) newm: pushad mov eax,["FeedingFrenzyTwo.exe"+001AC624] mov eax,[eax+40] //GetPlayersFish mov esi,[eax+344] mov esi,[esi]//ThisIsThePlayersFishClass //PlayerPosition mov ecx,[esi+98] mov edx,[esi+9C] //GetYou(heiheihei) mov eax,[eax+324] mov edi,0 nextfish: mov ebx,[eax+edi]//4*n cmp ebx,0//NoNextFish je exit add edi,4 cmp ebx,esi//IsPlayer? je nextfish mov [ebx+98],ecx mov [ebx+9C],edx jmp nextfish exit: popad ret createthread(newm) LdrInitializeThunk: DB 8B FF 55 8B EC [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newm) ``` ![image-20201231010422689](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012417image-20201231010422689.png) 可以不理它,別點確定,直接返回遊戲: ![image-20201231010500391](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231012422image-20201231010500391.png) 如果想穩妥一點,就去掉 `jmp nextfish` 這句,這樣就每次只移動 1 只魚,因為地圖上一直會有一堆魚,所以低頻呼叫移動 1 只魚的指令碼是沒事的,也可以輕鬆疊滿 FRENZY 因為已經可以四兩吃千斤了,所以繼續搞這個功能也沒啥必要了,就到此一遊吧,不找魚總數了。 ## 變小魔法 ![image-20201231154633471](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231074731image-20201231154633471.png) 遊戲裡有很多有意思的道具,比如蘑菇,可以讓周圍的大魚變小;還有個紅色的瘋狂魚,可以時間停止然後自動吃掉螢幕裡的魚,如果能找到對應的 call 就爽了。下面就來試一試。 首先我大概想了個思路,因為之前得到了儲存魚 growth 數值的地方,電腦魚與玩家魚用的是同一個類,所以儲存的偏移應該也是一樣的,所以檢視資料結構裡全域性魚陣列,隨便找條大魚,查詢改寫 growth 數值的地方,然後去吃個蘑菇。之後程式碼斷在了 `0041D744` ,這裡沒什麼有用資訊,返回到上層函式,發現了有意思的東西。 `0042863F - 68 08B35500 - push 0055B308 : ["shrinkBurstFx"]` 這個單詞 "shrink" 就是縮小的意思,再繼續向上返回查詢,又發現了一個位置: `0049AA93 - 68 B8E65400 - push 0054E6B8 : ["fishShrink"]` 然後我再繼續返回,發現到了外層大迴圈,而函式內部的斷點是在魔法光球打在魚身上的時候才會中斷,我們需要的函式卻是吃蘑菇的事件函式。好的,現在先暫停一下,縷縷思路: 1. 玩家吃蘑菇 2. 觸發吃蘑菇事件函式,發出光球,目標是電腦大魚 3. 電腦大魚被光球打中,觸發縮小事件,縮小 我們剛才找的 "shrink" 相關的函式應該是步驟 3 ,需要找的是步驟 2 ,而步驟 2 到步驟 3 應該不在同一個函式中,它們之間應該只是訊息傳遞的過程,所以這個線索就斷了。 不過,嘿嘿,程式碼註釋裡已經給我們提供了新的線索——"shrink" 字串。每次觸發事件的時候,就會引用和 "shrink" 相關的字串,所以新的思路有了——搜尋 "shrink" 字串,然後查詢什麼訪問了字串,看看吃蘑菇的時候會斷在哪裡。首先找到 `078BBCF8` 的位置是我們剛才找到的 "fishShrink" ,而就在它下面不遠的位置,發現了 "shrinkPickup" ,地址是 `078BBE10`!!!哇,運氣好到爆炸! ![image-20201231125320859](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231073035image-20201231125320859.png) 查詢什麼訪問了 `078BBE10` ,然後檢視堆疊,在這個字串附近的函式一個一個進去看,終於在 `004747CD` 的地方找到了線索。 ![image-20201231114603233](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231073017image-20201231114603233.png) 在`004747CD` 下斷點,然後吃東西就會中斷,再向上返回,發現返回的位置和吃的東西有關。 吃蘑菇返回到這裡 ![image-20201231114743831](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231073024image-20201231114743831.png) 吃魚返回到這裡 ![image-20201231115001960](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231073029image-20201231115001960.png) 試了幾次,吃什麼就會返回到什麼地方,所以附近應該就有吃的函數了,傳入的指標就是吃的東西,用多型的思想,吃什麼就執行什麼的事件函式,所以離勝利不遠了…… 經過一番除錯,終於找到了吃東西的函式——`call [eax+90]` ,就在 `0042A98C` 的地方,`esi` 是被吃的物件,`+90` 大概是虛擬函式表裡執行被吃事件的函式指標,引數是 `edi` ,儲存發起吃東西事件物件的指標,還有個暫存器引數 `ecx` ,儲存的是被吃物件指標。一個以吃東西為遊戲內容的遊戲,把吃東西的函式找到了,**遊戲結束!** ![image-20201231152036835](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231073040image-20201231152036835.png) 後面的工作就輕而易舉了,下斷點在每次吃掉蘑菇的時候步入,就到了 `004A249D` ,這個 `call 00493BD0` 就是我們苦苦尋找的吃蘑菇的事件函數了,這個函式只有一個暫存器引數,就是 `esi` 儲存發起事件的物件指標,這裡我們把玩家填進去就好。還記得玩家指標在哪嗎?沒錯,就是之前找四兩吃千斤的時候找到的物件:基址`005AC624`偏移`+40, +344, +0` ![image-20201231152131801](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231073044image-20201231152131801.png) 然後寫出指令碼,只要執行這個指令碼,就相當於吃了蘑菇,變小魔法就會觸發了,大功告成。 ```x86asm [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat //004A249D alloc(newm,512) label(exit) newm: pushad mov eax,["FeedingFrenzyTwo.exe"+001AC624] mov eax,[eax+40] //GetPlayersFish mov esi,[eax+344] mov esi,[esi]//ThisIsThePlayersFishClass call 00493BD0 exit: popad ret createthread(newm) LdrInitializeThunk: DB 8B FF 55 8B EC [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newm) ``` ## 狂吃 ![image-20201231160844486](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231085858image-20201231160844486.png) 還是按照上面的思路,在`0042A98C` 的吃東西函式 `call [eax+90]` 下斷點,然後吃個 "FEEDING FURY" 道具,找到對應的程式碼。很輕鬆就找到了,然後寫出下面的指令碼。 ```x86asm [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat //0053D05D alloc(newm,512) label(exit) newm: pushad mov eax,["FeedingFrenzyTwo.exe"+001AC624] mov eax,[eax+40] //GetPlayersFish mov esi,[eax+344] mov esi,[esi]//ThisIsThePlayersFishClass //feeding fury 00502399 add esi,00000154 mov eax,[esi] mov ecx,esi call [eax+20] test eax,eax je exit mov eax,[esi] mov ecx,esi call [eax+20] mov edx,[eax] mov ecx,eax call [edx+70] exit: popad ret createthread(newm) LdrInitializeThunk: DB 8B FF 55 8B EC [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newm) ``` ## 防雷 直接找出是什麼訪問了玩家魚指標,然後去撞水雷。查詢過程非常卡,所以到了水雷旁邊再開始查詢。撞到水雷時,會出現一些新的程式碼,一個一個找。運氣很不錯,找第一個就發現了關鍵跳轉。 `0050B773 - 75 30 - jne 0050B7A5` 有魚死掉時的關鍵跳轉 再向下找,就在下面找到了魚撞雷死亡呼叫的函式 `0050B798 - FF 90 DC000000 - call dword ptr [eax+000000DC]` 當玩家單位觸發時步入,裡面只有 3 行彙編。 ![image-20201231183108794](https://images.cnblogs.com/cnblogs_com/adjwang/1909227/o_201231103507image-20201231183108794.png) 只要在玩家觸發水雷的時候跳過這個函式就可以了 ```x86asm [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) label(returnhere) label(originalcode) label(exit) newmem: //this is allocated memory, you have read,write,execute access //place your code here push eax mov eax,["FeedingFrenzyTwo.exe"+001AC624] mov eax,[eax+40] //GetPlayersFish mov eax,[eax+344] mov eax,[eax]//ThisIsThePlayersFishClass //if player hit a mine //then jump cmp eax,ecx pop eax je exit originalcode: mov eax,[ecx] call dword ptr [eax+000000EC] exit: jmp returnhere "FeedingFrenzyTwo.exe"+102BE3: jmp newmem nop nop nop returnhere: [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "FeedingFrenzyTwo.exe"+102BE3: mov eax,[ecx] call dword ptr [eax+000000EC] //Alt: db 8B 01 FF 90 EC 00 00 00 ``` 因為這裡的函式在其他事件觸發時也有被呼叫到,所以不知道這麼改有什麼副作用,暫且先這樣,等以後出了問題再回來看……(懶) 另外還發現了銷燬玩家物件的函式,不知道以後用不用得上。 `00438EBD - FF 50 1C - call dword ptr [eax+1C]` 玩家魚物件銷燬 ## 其他 * 吸水能量條 基址`"FeedingFrenzyTwo.exe"+001AC624` 偏移 `+40, +344, +0, +20C` 型別是 `float` 從 0 到 1 * 吸水持久度 基址 `005A7314` 浮點數 `float`,數值越小持續時間越長 * 吸水恢復速度 基址 `005A7318` 浮點數 `float`,數值越大恢復速度越快 * 直接過關 基址 `005AC624` 偏移 `+40, +8C` 位元組 `byte` ,改成 1 即可。大部分時候好用,個別關不行,不知道為啥 從空中翻滾後落到水裡會暈一會,這個指令碼可以防止眩暈。其實功能非常簡單,就是跳過 `mov byte ptr [ebx+0000019D],01` 這一句。 ```x86asm [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat alloc(newmem,2048) label(returnhere) label(originalcode) label(exit) newmem: //this is allocated memory, you have read,write,execute access //place your code here jmp exit originalcode: mov byte ptr [ebx+0000019D],01 exit: jmp returnhere "FeedingFrenzyTwo.exe"+13FC74: jmp newmem nop nop returnhere: [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(newmem) "FeedingFrenzyTwo.exe"+13FC74: mov byte ptr [ebx+0000019D],01 //Alt: db C6 83 9D 01 00 00 01 ``` ## 結語 權當是休閒娛樂,沒做太多複雜的東西,想做成品修改器的小夥伴也可以寫一個。 [CT檔案](https://files.cnblogs.com/files/adjwang/FeedingFrenzyTwo.zip) 今年最後一天了,祝大家玩得