吞食鱼2(FeedingFrenzyTwo) 修改器[通俗易懂]

吞食鱼2(FeedingFrenzyTwo) 修改器[通俗易懂]童年回忆系列。小时候特别喜欢玩这类游戏,软件不大,很慢的网速也不会下载太久,然后对配置要求不高,很破的电脑也可以玩得很开心。不过也有糟心的时候啊,大鱼太多,无数次死于挑战咬梭子鱼的尾巴……今年最后一天

大家好,又见面了,我是你们的朋友全栈君。

 

童年回忆系列。小时候特别喜欢玩这类游戏,软件不大,很慢的网速也不会下载太久,然后对配置要求不高,很破的电脑也可以玩得很开心。不过也有糟心的时候啊,大鱼太多,无数次死于挑战咬梭子鱼的尾巴……今年最后一天,就休闲一小会吧。

image-20201230224005827

image-20201231012316488

小时候看不懂是啥意思,现在知道了,我来翻译下第一段。

警告!

发现梭子鱼在珊瑚礁附近游荡。留意警告标志,远离它张开的大嘴!要是你有迷之自信,可以试着咬它的尾巴。咬 4 次就会有惊喜……如果到那时候你还活着……

现在再玩已经没有当年的感觉了——鼠标换了……其实我老早就想,为什么到了下一关,我的鱼就变小了??太不爽了。游戏里各种对玩家不利的设定:小鱼不能吃大鱼、被大鱼追、被贝壳吃掉、被乌贼喷、被水母电、被大嘴鸟吞、被水雷炸、被河豚刺、吸水还要 CD、跳出水面翻滚的话进水眩晕……所以只有修改内存数据才是称霸海洋的秘诀!

工具:Cheat Engine 6.4 (以前搞的汉化版,其实翻译不完全,建议用英文版)

大,大,大

这个游戏的规则是吃小鱼,躲大鱼,等长大了以后就可以吃遍全图。成长值进度条在左上角,前面的关卡都是 3 个成长阶段,到了后面还有更多的级别。

image-20201230224112420

好的,按照程序员的思路,姑且猜测 growth 数据用整型存储,每种鱼的加成不一样,小鱼少些,大鱼多些,初始值为 0,打开 CE ,开始搜索,每吃一条鱼就搜一次“增加的数值”。

image-20201230225023229

好的,没搜几次,轻松搜到 2 个值。尝试修改第一个,发现鱼没反应;再尝试第二个,鱼长大了!然后自然是一顿操作:找出改写 -> 显示反汇编,然后看到了如下代码:

image-20201230225514423

这也太舒服了,直接找到静态地址005AC624,省的找基址了,直接手动添加地址,growth 就有了。基址005AC624偏移+3C, +40(跟基址写成 "FeedingFrenzyTwo.exe"+001AC624 是一样的)

image-20201230225654162

四两吃千斤

修改 growth 的数值,就可以瞬间变大,通关,但是这一点也不爽啊,我一定要把被吃的仇报了才行!

image-20201230224112420

还是刚才的进度条,用通常思路来猜,成长阶段数值分别是 0, 1, 2 ,姑且先试一下。借助修改 growth 值快速长大,搜索这几个值就很快了。

image-20201230231355759

先尝试修改第一个,图中我修改 0245AE24 的值为 0 以后,我 2 阶的大鱼被比我小的鱼吃掉了,那应该就是它了。观察了一会发现,这个值就是用来确定会不会被吃的,但是查找访问它的代码并没找到什么关键判定,代码太多了,我也懒得看,反正只要不被吃复位,这个值就不会变,直接把它改大点就行了。

在鱼变大的时候查找改写它的地址,可以找到偏移量 EC

00496B47 - 89 99 EC000000 - mov [ecx+000000EC],ebx

再稍微调试跟踪下,找到基址。基址005AC624偏移+40, +344, +0, +EC,类型我选了 byte ,不过应该没啥影响。

image-20201230232503913

image-20201230232204382

然而事实是,我不会被大鱼吃掉了,但是在大鱼旁边的时候也不会触发吃鱼的动作了……是太难吃了吗?哈哈……不过,离成功不远了。经过一番探索,我用“增大的数值”搜索前边的成长阶段,找到了另一个数值。简单点来说,这两个值一个是玩家鱼在电脑鱼面前的大小,一个是电脑鱼在玩家鱼面前的小大。<- 我说小大,因为第二个值数值越大判定电脑鱼越小。

基址005AC624偏移+40, +344, +0, +F8,就在上一个值旁边,很狡猾啊,这个值是从 1 到 3 的,之前搜精确数值的时候没找到,早知道就先去看看数据结构了。

image-20201230232932352

把这两个值同时改成 5 ,终于报了当年的血海深仇,啊哈哈哈哈哈哈哈……

image-20201230233834408

速度和位置

吸取刚才的教训,现在来看看数据结构。

image-20201230234630832

嘿嘿,果然有了意外收获。仔细观察鱼的状态和数值,可以发现上面的 4 组浮点数分别代表鱼的位置和速度,修改这些值可以让鱼瞬移到地图任何地方。

然后我就有了一个大胆的想法——能不能把地图里的其他鱼瞬移到我嘴边呢?省的乱跑了。事实是——YES! 首先要找到存放地图上所有鱼的地方。

拿玩家鱼的位置来说,地址是 基址005AC624偏移+40, +344, +0, +98,按照程序的对象模型来想,+344指针应该是玩家指针,里面存放了很多和玩家相关的数据,刚才做四两吃千斤的时候,数据也在这个对象下,那么看下+0指针应该就是玩家的鱼的指针了,我这次游戏的指针是 09397280 。好的现在假设有这么个全局鱼数组,那么这个 09397280 也一定在里面,直接搜索这个指针:

image-20201230235851446

嘿嘿,我为什么单独标出来这个 07EDC488 呢?因为看数据结构,在之前的 +344 指针前面,在 +324 指针的地方,指向的数据不就是 07EDC488 嘛,这应该是个数组首地址,展开一看,果然全都是鱼!

image-20201231000302729

image-20201231000449674

鱼是有了,可是数量不知道……这个数组没有结束标识,貌似是像vector那样管理的,有固定大小,靠整数标记结束的位置,而游戏本身可能不记录实时的鱼数量,所以我找了一遍,一直没找到数据,也可能是我找的方法不对。鱼在不同状态的时候地图上的鱼数量会变,但是因为不知道具体值,非常难找,我猜测是一些常数,规定了不同关卡鱼的数量上限。

我还强行试了下移动所有鱼直到空指针,会访问销毁过的鱼对象而导致访问越界崩溃,问题应该出在这里 mov ebx,[eax+edi]//4*nedi 的值增大以后,eax+edi 就不一定是有效地址了。然而令人惊奇的是,只要不点错误窗口上的确定键,就还可以继续游戏!真神奇……脚本如下:

[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

可以不理它,别点确定,直接返回游戏:

image-20201231010500391

如果想稳妥一点,就去掉 jmp nextfish 这句,这样就每次只移动 1 只鱼,因为地图上一直会有一堆鱼,所以低频调用移动 1 只鱼的脚本是没事的,也可以轻松叠满 FRENZY

因为已经可以四两吃千斤了,所以继续搞这个功能也没啥必要了,就到此一游吧,不找鱼总数了。

变小魔法

image-20201231154633471

游戏里有很多有意思的道具,比如蘑菇,可以让周围的大鱼变小;还有个红色的疯狂鱼,可以时间停止然后自动吃掉屏幕里的鱼,如果能找到对应的 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

查找什么访问了 078BBE10 ,然后查看堆栈,在这个字符串附近的函数一个一个进去看,终于在 004747CD 的地方找到了线索。

image-20201231114603233

004747CD 下断点,然后吃东西就会中断,再向上返回,发现返回的位置和吃的东西有关。

吃蘑菇返回到这里

image-20201231114743831

吃鱼返回到这里

image-20201231115001960

试了几次,吃什么就会返回到什么地方,所以附近应该就有吃的函数了,传入的指针就是吃的东西,用多态的思想,吃什么就执行什么的事件函数,所以离胜利不远了……

经过一番调试,终于找到了吃东西的函数——call [eax+90] ,就在 0042A98C 的地方,esi 是被吃的对象,+90 大概是虚函数表里执行被吃事件的函数指针,参数是 edi ,储存发起吃东西事件对象的指针,还有个寄存器参数 ecx ,储存的是被吃对象指针。一个以吃东西为游戏内容的游戏,把吃东西的函数找到了,游戏结束!

image-20201231152036835

后面的工作就轻而易举了,下断点在每次吃掉蘑菇的时候步入,就到了 004A249D ,这个 call 00493BD0 就是我们苦苦寻找的吃蘑菇的事件函数了,这个函数只有一个寄存器参数,就是 esi 储存发起事件的对象指针,这里我们把玩家填进去就好。还记得玩家指针在哪吗?没错,就是之前找四两吃千斤的时候找到的对象:基址005AC624偏移+40, +344, +0

image-20201231152131801

然后写出脚本,只要执行这个脚本,就相当于吃了蘑菇,变小魔法就会触发了,大功告成。

[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

还是按照上面的思路,在0042A98C 的吃东西函数 call [eax+90] 下断点,然后吃个 “FEEDING FURY” 道具,找到对应的代码。很轻松就找到了,然后写出下面的脚本。

[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

只要在玩家触发水雷的时候跳过这个函数就可以了

[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 这一句。

[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 

结语

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/154767.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mysql8 2058_SQLyog连接MySQL8.0报2058错误的解决方案[通俗易懂]

    mysql8 2058_SQLyog连接MySQL8.0报2058错误的解决方案[通俗易懂]引言用SQLyog连接MySQL8.0(社区版:mysql-installer-community-8.0.15.0.msi),出现错误2058(Plugincaching_sha2_passwordcouldnotbeloaded:xxxx),通过查询资料了解了该错误的原因并在本文中提出了该问题的方案。原因该错误提示如下图所示:具体原因:新的MySQL8.0安装,在初始化数据目录时,…

  • navicat premium 15 J激活码_在线激活[通俗易懂]

    (navicat premium 15 J激活码)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html…

  • fastjson object转jsonobject_jsonobject取值

    fastjson object转jsonobject_jsonobject取值众所周知,kafka中存储的数据是经过BASE64加密后的jsonObject,因此从kafka中读取的数据经过base64解码,得到的是json串,利用JSONObect的方法可以对json串进行解析,拿到对应的数据。那么要如何将scala对象或者java对象转换为JsonObject对象或JSONObject对象呢?(注意:JsonObject对象和JSONObject对象不同,调用的API也…

  • linux修改文件权限命令是什么_chown和chmod命令用法

    linux修改文件权限命令是什么_chown和chmod命令用法Linux系统中的每个文件和目录都有访问许可权限,用它来确定谁可以通过何种方式对文件和目录进行访问和操作。文件或目录的访问权限分为只读,只写和可执行三种。以文件为例,只读权限表示只允许读其内容,而禁止对其做任何的更改操作。可执行权限表示允许将该文件作为一个程序执行。文件被创建时,文件所有者自动拥有对该文件的读、写和可执行权限,以便于对文件的阅读和修改。用户也可根据需要把访问权限设置为需要的任何组合。

  • 基于QT播放器的实现(一)Rgb、YUV格式(附带代码)

    基于QT播放器的实现(一)Rgb、YUV格式(附带代码)基于QT播放器的实现(一)Rgb、YUV格式色度空间转换YUV转RGB的公式对本地RGB32视频图像的播放色度空间转换YUV颜色模型其实常用于视频传输和图像压缩。由于人类的眼睛,对亮度的敏感度远超过对色彩的敏感度,所以视频传输过程中,为了减小带宽,通常将色彩分量UV的比例减小,以达到降低带宽的目的。这就出现了YUV4:4:4、YUV4:2:2、YUV4:1:1等格式。RGB32使用32位来…

  • c# break,continue,goto 跳出循环「建议收藏」

    c# break,continue,goto 跳出循环「建议收藏」1.break跳出循环protectedintTest1(){intindex=0;for(inti=0;i<5;i++){if(i==2){//单个循环,跳出整个for循环,//多个循环,跳出最内层for循环break;…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号