应用Ghidra对PSX游戏进行逆向工程

这是我在钻研PlayStation(PSX)逆向工程时总结出的小手册。如果你对逆向工程的基本概念不相熟,请先看什么是反编译?

读取PlayStation光盘

PlayStation光盘是以规范的ISO 9660 CD-ROM格局记录的,能够在PC上失常读取。光盘映像文件(如.ISO、.BIN/CUE)能够用AcetoneISO或PowerISO等软件进行(虚拟光驱)挂载或内容提取。尽管有时会有意外:

1.有些文件可能实际上是暗藏的。大多数PSX游戏所应用的PsyQ API可能基于数值扇区地位(LBN)而不是文件名从磁盘中读取文件,但PC无奈读取。

2.一些PSX光盘成心制作出坏块,作为避免复制的反盗版措施。

3.一些读取光盘镜像的软件在解决多轨光盘时有问题。你可能要做一些奇怪的事件,比方把第一轨的.BIN加载到AcetoneISO中,或者用binmerge合并多个.BIN,或者制作一个只包含第一轨的假.cue文件。

不过,至多有两个要害文件通常是容易读懂的:system.cnf,它将通知你主游戏可执行文件的文件名;以及可执行文件自身,它通常被命名为PSX.EXE或相似SLPM_86.053。这就是软件逆向剖析所须要的文件。

应用Ghidra开始剖析可执行文件

Ghidra是美国国家安全局(NSA)开发的一款逆向剖析软件,于2019年收费颁布。尽管有其余相似的工具,如radare2和IDA Pro。而Ghidra岂但十分弱小,又收费。

Ghidra可能原生地解决PSX的指令集(MIPS R3000, Ghidra将其了解为MIPS默认的32位小端模式)。插件ghidra_psx_ldr可辅助进行PSX专用的剖析。

下载并解压Ghidra。下载ghidra_psx_ldr的最新压缩包,不解压,而将其放入Ghidra的文件夹中。通过在Windows上运行ghidraRun.bat或在Linux上运行./ghidraRun启动Ghidra。从菜单中选择File - Install Extensions并抉择ghidra_psx_ldr。

创立一个新的 non-shared project (File - New Project)。单击列表中的我的项目入口(project entry)并单击绿龙图标以关上代码浏览器(Code Browser)。在这个新窗口中,点击File - Import(或按下I键),并抉择你的PlayStation可执行文件。

软件当初应该曾经提醒您Auto-Analyze(主动剖析)。如果临时不剖析,您能够稍后在Analysis菜单中运行它(或按A键)。Auto-Analyze须要不少工夫。之后,按CTRL-S保留。以备未来的不时之需,请留神保留后会革除撤销记录。

默认关上了很多不须要的窗口。你比拟须要的窗口是Symbol Tree(列出变量和函数名),Listing(MIPS反汇编后果)和Decompile(反编译为C语言)。其余窗口当初不过是占据空间罢了。

了解汇编指令

您当初有成千上万行MIPS汇编代码,可能只有很少变量和函数领有有意义的名字。如果您不相熟MIPS汇编,这将使人望而却步。

首先,咱们宏现的了解汇编代码。以下是一个片段:

                     **************************************************************                     *                          FUNCTION                          *                     **************************************************************                     undefined myrand()                       assume gp = 0x800f0d90     undefined         v0:1           <RETURN>                     myrand800425d0 0f 80 03 3c     lui        v1,0x800f     assume gp = <UNKNOWN>800425d4 f0 01 63 8c     lw         v1,offset rng_800f01f0(v1)                       = ??800425d8 0f 80 01 3c     lui        at,0x800f800425dc 77 03 62 24     addiu      v0,v1,0x377800425e0 08 00 e0 03     jr         ra800425e4 f0 01 22 ac     _sw        v0,offset rng_800f01f0(at)                       = ??

这里的 "FUNCTION "是一个正文,代表Ghidra认为这是一个函数的起始地位,兴许是因为主动剖析发现有跳转指令指向这个地址。

Undefined myrand()是给出的函数名。因为可执行文件没有保留其编译前的函数名(元数据,metadata),所以简直总是应用FUN_800425d0这样机械化的名称。当你最终钻研出一个函数或变量的性能时,你能够点击它的名字并点击键盘上的“L”来输出新名字,其扭转将在整个程序中失效。

如果一个函数开始被事后标识,请感激ghidra_psx_ldr将其辨认为对PSX devkit中的一个PsyQ规范库函数的援用。这可能是了解函数性能的突破口。例如,跟踪规范rand函数能够应用了解游戏解决随机性的机制;print函数可用于输入调戏信息;write函数在PSX游戏中总是用于写入记忆卡(Memory Card),这是找寻游戏要害变量的突破口,它们用于存储游戏至始至终用到的信息。

每一行汇编代码的最右边都有一个8位16进制数(例如800425d0)。这是这个指令(机器码)在内存中的地址。PSX软件的内存地址通常从0x80000000开始存,而不是0。但两者都指向雷同的物理内存,例如0x800425d0和0x000425d0指向雷同的内存(请浏览Nocash PSX Specifications网页的Memory Map条目以获取详细信息)。

之后是四组两位十六进制数。这是最右边的地址指向的32位空间中的四字节内容。内容或者是指令(机器码),或者是四字节变量,或是其它。

再之后是机器码代表的汇编指令。少数状况下,浏览Decompile窗口的C语言可能会更有用。

最初是分号键(;)以插入正文。反汇编窗口和反编译窗口都可插入正文。

辨别代码和数据

Ghidra很善于辨认代码,但并不完满。因为可执行文件中所有内容都是原始字节码,所以它有时可能不能正确的辨认一段代码,或混同数据类型。

如果您猜想一大段无奈辨认的数据可能是代码,请抉择第一个字节并按D键以进行反汇编。有时,这实际上是一个函数,但没有从任何中央间接调用,则请点击F将其转换为函数。如果产生谬误(例如,产生的“代码”是不正确的),只需按Ctrl-Z撤销。您还能够按C键将已辨认的内容倒转回未辨认的字节码。

您还能够右键单击一行代码,点击Data,并抉择要标识的数据类型。这对于辨认字符串很不便。特地是日本游戏常常应用Shift-JIS编码,而Ghidra不会自动识别。右键单击任意字符串,到数据-设置和设置字符集Shift-JIS。更好的是,数据-默认设置将为所有字符串设置此值。更好的方法是, 在Data - Default Settings中设置,将会对所有字符串失效。

Ghidra甚至容许您输出字符串的翻译。您能够参考机器翻译网站 WWWJDIC。

跟踪代码流

如果遇到函数或变量,能够双击它,或将键盘光标移到那里并按Enter键来拜访它。您能够通过按工具栏上的“左”键或键盘上的“Alt+左”键返回到之前的地位。

函数的右上角兴许有一个XREF列表,这是调用它的其余函数的列表。您能够抉择一个变量或函数并按Ctrl-Shift-F来查看调用他的代码。一种分析方法是从曾经确定性能的货色(字符串或函数)做为突破口,跟踪哪些函数应用它,以钻研这些函数是如何工作的。

您做的每一个标记都好像是一块拼图,以帮忙你了解其它代码。

用调试器实时监督内存

只管ghidra_psx_ldr有一个调试器,在我写这篇文章时,它的性能还很无限。仿真器Mednafen有一个更弱小的调试器,它能够让您实时察看和批改内存。

设置Mednafen并应用像Mednaffe这样的GUI运行它。启动一个游戏(它能够加载BIN/CUE文件,即使在PC上不能正确读取)。如果你在游戏控制器上遇到问题,请遵循以下阐明(倡议装个手柄,这样你就能够在调试器屏幕关上的状况下用键盘、鼠标输出信息):

  1. 启动游戏
  2. 按下所有控制键和方向键,把摇杆转到最大,按下所有L/R键
  3. 按F3键从新抉择控制器(翻译可能谬误,不影响了解)。
  4. 应用Alt-Shift-1启动控制器配置,按批示按下按钮。

控制器只须要配置一次。

当初,按Atl-D加载调试器。 Mednafen debugger documentation提供应用调试器的全面信息,但最重要的是,Alt-1显示CPU信息,S键终止或单步步过,R键持续,Alt-3显示内存信息。这些视图都实时更新。

在内存信息窗口,按G键并输出地址以跟踪。Mednafen显示的地址与Ghidra中的地址统一,只是它们以0而不是8结尾。您能够实时察看已知变量的变动。您还能够按D键将内存转储到文件中,以便当前剖析。从0转储到200000(十六进制),大小为2MB。

其余可执行文件(动静加载技术)

PSX只有2MB的RAM和1MB的VRAM,程序最大只能是2MB。与CPU能够间接寻址ROM的卡带游戏机不同,基于CD的游戏机必须先将程序加载到RAM中,因而须要加载工夫。对于许多PlayStation游戏来说,这曾经足够了,但对于简单的游戏来说,可能还不够。

解决方案是PsyQ动静加载技术,其中多个程序块保留在独自的文件中,主可执行文件能够依据须要加载和卸载这些文件。例如,Tokimeki Memorial有大概4.9MB的可动静加载代码。

每个程序块都将被加载到内存中的固定地位。失去这个地位很麻烦,能够参考我的方法:

  1. 以十六进制关上程序块,找到大概前十六个字节。
  2. 在游戏加载这段程序块时,转储RAM。
  3. 在RAM中搜寻这些字节,以找到它们在内存中的加载地位。

失去程序块在内存中的加载地位,就能够按如下形式将其加载到Ghidra中:

  1. 在code browser中,抉择 File - Add to Program并抉择相干二进制文件。
  2. 点击“Options”。勾选“Overlay”,输出块名称,并输出相干的基址(请记住,对于PSX,它以8而不是0开始)。
  3. Select Tools - Memory Map (or hit the icon on the toolbar). Select your overlay and ensure the following are ticked: R, X, Overlay, and Initialized. “X” is important as it determines executable.
    抉择Tools - Memory Map(或点击工具栏上内存形态的图标)。抉择加载的程序块并确保选中以下项:R, X, Overlay, and Initialized。“X”很重要,它代表可执行文件。
  4. 双击起始地址。这应该会在代码浏览器中显示。
  5. Analysis - Auto Analysis 以反编译。

解决动静加载的另一个难点是,程序块有时在CD-ROM文件列表中未列出,因为只有主EXE能力被文件系统拜访。您必须查看主可执行代码,以查看加载到何处以及多少字节。或者,您能够从内存转储中提取它。