对疯狂的萝卜游戏的逆向分析(part1)
导语:这是此系列文章的第1部分,在该系列中我逆向了一个名为Looney Tunes:Carrot Crazy的Game Boy Color游戏。
0x01 游戏介绍
这是此系列文章的第1部分,在该系列中我逆向了一个名为Looney Tunes:Carrot Crazy的Game Boy Color游戏。这是一个相当晦涩的游戏,但是当我还是孩子的时候我很喜欢它。对于本系列的每个部分,我将设定一个目标并记录实现该目标所采取的步骤。一个目标示例可能是使“ Bugs Bunny”跳高变成原来的两倍。在我的许多工作中,我都使用BGB Game Boy模拟器,因为它具有强大的调试器和内存查看器。
Carrot Crazy是一个简单的平台游戏。玩家控制Bugs Bunny和Lola Bunny收集胡萝卜和其他东西。每个级别中的前两个阶段是相似的,但是第三个阶段是BOSS在追逐玩家的游戏。
GitHub链接:https : //github.com/huderlem/carrotcrazy
0x02 逆向方法
可以查找所有级别跳过key,然后创建自己的自定义key。
疯狂的胡萝卜游戏没有使用电池的RAM,所以当游戏断电它不保存任何数据。但是,在游戏中的某些时候会为玩家提供密码,从而使玩家可以从不同的级别开始。我们可以在不玩游戏的情况下拿到所有的密码!
密码输入画面
为了找到游戏中所有密码的列表,我们需要找到将输入的密码与一些有效密码列表进行比较的例程。首先,我们将找到密码输入的位置,这很有可能是3个相邻字节。找到密码输入的RAM地址后,可以在BGB中设置一个内存访问断点,以在读取该RAM地址时暂停执行。希望这在播放器提交密码时触发,因为某些代码需要将密码输入与一组公认的密码进行比较。
0x03 查找密码输入的RAM位置
现在还对游戏内存布局一无所知,但是我对Game Boy游戏的运作方式非常了解。RAM位于地址范围内$c000- $dfff。我希望在该地址范围内的某个地方找到这三个密码字节。可以用来找到它们的一种策略是打开BGB调试器并查看内存查看器的RAM地址。
BGB 调试器
有很多数据,如何知道要寻找什么?BGB的内存查看器是实时更新的,因此应该能够通过玩游戏来修改密码输入,并在内存查看器中看到相应的字节更改,这通常是在RAM中定位数据的非常有效的方法。Game Boy RAM并不大,所以向下滚动直到发现一个字节,该字节根据我在玩游戏时更改的密码输入而更改。幸运的是,在密码输入屏幕上,游戏中发生的变化并不多,因此大部分RAM是静态的。
当我向下滚动时,我注意到整个RAM bank 0($c000- $cfff)是静态的,并且包含游戏中使用的许多ASCII文本。我会把这些信息藏起来供以后使用。然后,我注意到开始$db00快速变化的RAM块。使用相同的技巧,我得出结论,这里是声音引擎的一部分。这是因为我可以看到某些字节正在与当前背景音乐同步变化。
最后,我看到地址处的大量RAM $df00似乎与游戏中闪烁的密码角色的头部变化的频率相同。基于空间局部性的原则,三个密码字节在附近。果然,我看到位于$dee8,$dee9和$deea三个字节。通过密码输入,这些值如下:
0 = Marvin the Martian 1 = Daffy Duck 2 = Yosemite Sam 3 = Tasmanian Devil 4 = Elmer Fudd
现在知道了密码输入存储在RAM中的位置,应该能够在ROM中找到负责将密码输入与一组已知密码进行比较的例程。
0x04 查找密码比较例程
密码输入字节必须由游戏代码中的至少两个逻辑使用。
1.检查输入的密码是否有效。
2.显示字符表示当前输入的密码。
如前所述,我将在密码输入RAM位置上添加一个内存访问断点。将在读取断点处添加到$dee8,因为这是第一个密码字符。
打一个内存访问断点
现在已经打下了断点,观察到在每一帧中会两次读取RAM地址,这使使用断点变得更加困难。访问该字节的两个例程负责清理密码字节并加载字符。这些都不是要查找的内容,因为在寻找提交密码后执行的代码。BGB在调试器中内置了一个handle,提交密码后,ROM地址上的新例程0:0736将触发断点。
在BGB调试器中,看到了断点周围例程的代码:
ld a, 5 ld [MBC5RomBank], a ld hl, $6e8c ld a, [hli] ld d, a .checkPassword ld e, 3 ld bc, wPasswordCharacters ; $dee8 .loop ld a, [bc] ; <--This is where my breakpoint triggered. inc c cp [hl] jr nz, .noMatch inc hl dec e jr nz, .loop ld a, [hli] cp a, $ff ... .noMatch inc e inc e ld c, e ld b, 0 add hl, bc dec d jr nz, .checkPassword
这是一个非常简单的例程,它遍历一个密码表,然后将玩家输入的密码与它们进行比较。首先,此例程加载密码表。该表位于ROM bank 5的地址中6e8c,如果不是就写为5:6e8c。我们可以看到该寄存器d保存了表中的密码总数。它是密码表中位于5:6e8c的第一个字节。根据之后的.noMatch代码,还可以看到密码表中的每个条目都是5个字节长。密码字符是三个字节,另外两个是一些元数据,描述了密码的级别。
0x05 记录密码表
在十六进制编辑器中打开ROM文件,然后到ROM地址5:6e8c查看密码表的内容。
密码表
如下所示:
Passwords: ; $16e8c db 6 ; number of passwords Password0: db $0, $1, $4 ; characters db $7, $0 ; stage metadata Password1: db $1, $4, $3 db $15, $0 Password2: db $3, $2, $0 db $15, $ff Password3: db $2, $4, $1 db $30, $0 Password4: db $0, $2, $3 db $30, $ff Password5: db $3, $4, $1 db $ff, $ff
根据此密码表,游戏有六个唯一密码。问题是我们不知道元数据确切代表什么,从游戏经验中,我碰巧知道在简单和困难模式下,关卡有不同的密码。因此,可以推断出阶段元数据的第二个字节$0可能适用于简易模式和$ff硬模式。这意味着第一个字节很可能是级别ID。通过更改值并查看游戏过程中发生的情况来进行测试。
使用十六进制编辑器,我将第一个密码修改为以下内容:
Password0: db $0, $0, $0 ; All Marvin the Martian db $2, $0 ; stage metadata
在尝试了第一个字节之后,游戏开始运行,其中不同的“名字”具有全局ID。以下是一些ID的列表:
0: Infogrames Copyright Screen 1: Warner Bros. Copyright Screen 2: Language Selection Screen 3: Title Screen 4: Options Screen 5: Bugs & Lola Intro Scene 6: Studio Hallway (Hub World) 7: Treasure Island Scene 1 Intro 8: Treasure Island Scene 1 - Area 1
可以肯定的是,表中的第一个密码具有$7其ID的值,它对应于上面列表中的“金银岛场景1”。如果测试密码$0, $1, $4(Marvin,Daffy Duck和Elmer Fudd),则应立即将玩家带到金银岛场景1。
金银岛密码
剩下的唯一事情就是最后一个密码的行为。它具有$ff, $ff元数据的值,并且$ff似乎与ID不对应。从外部文档中,我碰巧知道开发人员留下了调试密码,该密码允许玩家在玩游戏时按START + SELECT跳过当前阶段。确实是这个密码。
总而言之,我们发现游戏可以识别所有的密码:
Marvin the Martian, Daffy Duck, Elmer Fudd Treasure Island Scene 1 Easy Mode Daffy Duck, Elmer Fudd, Tasmanian Devil Crazy Town Scene 1 Easy Mode Tasmanian Devil, Yosemite Sam, Marvin the Martian Crazy Town Scene 1 Hard Mode Yosemite Sam, Elmer Fudd, Daffy Duck The Space Station Scene 1 Easy Mode Marvin the Martian, Yosemite Sam, Tasmanian Devil The Space Station Scene 1 Hard Mode Tasmanian Devil, Elmer Fudd, Daffy Duck Ability to skip levels by pressing START + SELECT during gameplay
0x06 创建自己的密码
现在,已经对密码系统进行了完全逆向,应该能够创建自己的密码而不会破坏任何现有密码。我想要一个密码,直接到游戏的最后一步Elmer Fudd的森林。
首先,由于我要附加密码表,因此需要将密码表移至ROM中的其他位置。无法在其当前ROM位置附加它,因为我不想覆盖紧随其后的现有数据。在十六进制编辑器中,可以看到ROM bank 5的末尾有足够的可用空间,因此将现有的密码表移至该位置。
重新定位密码表
首先,必须将第一个字节更改为7,因为表中现在有七个密码。然后,将5字节的密码结构附加到表末尾。
Password6: db $4, $4, $4 ; All Elmer Fudd db $3d, $00 ; Elmer Fudd's Forest Scene 1, Easy Mode
现在,在ROM中有了新的密码表,必须更新加载密码表的例程,因为它仍在使用旧地址。将密码表移至address $17dd6,因此将例程的开头修改为以下内容:
ld a, 5 ; The table is still located in ROM bank 5 ld [MBC5RomBank], a ld hl, $7dd6 ; New password table address
测试一下我们的密码是否有效。
自定义Elmer Fudd密码
0x07 学习总结
这是本系列逆向游戏的第1部分,我们能够对密码系统进行逆向,甚至可以将自己的自定义密码注入游戏中。在以后的文章中,我们将更深入地探讨自定义关卡等问题。
发表评论