本人不是很有自信能写好这个教程,加上本人时间紧张没法保证能稳定更新,如果有疏漏或者疑问请在评论区指出,非常感谢! 首先非常感谢指导我学习 NDS 游戏破解的 Enler 大佬,如果没有他我的童年回忆的汉化和这个破解教程就不能实现了,非常感谢!
本教程的破解源代码(流星洛克人一代汉化的汉化破解源代码)将会在汉化完成后整理(为了尊重汉化组劳动成果,仅包含我之前乱翻的测试文本)并传到 Github 上,届时大家可以自行查看学习。
如果你是初次接触老游戏机游戏的破解,我个人先建议你去尝试一下简单的游戏破解教程: https://www.bilibili.com/read/cv18346849/
上一部分我们简单尝试了一次调试和逆向工程,但是即便是变成了伪代码,想要解读的话难度还是比较大。那么这次我们来通过导入符号表到 No$GBA 和 IDA Pro 来帮助我们更好地理解伪代码吧!
了解编译原理的小伙伴应该知道,这是一个编译中间产物,记录了所有子程序的入口点地址还有其它关键变量常量的内存地址,我们可以通过符号表配合提供的静态链接库中已编译的代码,来在产物程序/游戏程序里搜索到可能存在的子程序。具体的介绍可以自行在网上搜索,这里只是说个不太准确的大概定义。
在上一部分我提到过,NitroSDK 是老任为游戏开发商提供的游戏开发套件,提供了方方面面的操作支持(图形操作,文件/卡带读取,音频加载播放等等)。考虑到我们后面会编写一个从文件读取字库的程序,我们需要知道我们的游戏使用了哪些 NitroSDK 的函数(尤其是重要的文件读取函数)。
这次我们需要使用 CrystalTile2 来搜索并导出符号表。CrystalTile2 不仅仅是一个图块数据搜索修改工具,在那时候还集成了汇编程序和 NDS 相关的功能,甚至还能搜索导出 NitroSDK 符号表(天使汉化组NB!)。
那么现在我们打开 CrystalTile2 并打开我们的游戏 ROM:
然后点击 工具 菜单,选择 NDS 文件系统信息(你也可以点击菜单栏上的 NDS 图标或者按下 Ctrl + N 打开)
在弹出的 NDS 文件系统信息窗口中点击 编辑 菜单,选择 NEF 符号表编辑器:
可以看到编辑器的窗口下面已经有了几种符号表给我们使用,还有符号过滤功能,不过我们为了之后方便可以直接全选,然后符号表我们选择 NitroSDK3 和 NitroSDK4(我们并不清楚游戏使用了哪个版本的 SDK 所以直接全部要),如果需要还可以勾选 对重定义符号进行编号(搜索过程中可能会存在多个相同的符号,勾选之后可以避免冲突和误读):
接下来点击 搜索 按钮,等待一两分钟后列表就显示了搜索出来的各种地址和符号了:
接下来点击 生成 按钮,生成 NEF 符号表文件和 TXT 文本格式的符号表文件:
检查我们的游戏文件所在目录,可见我们的符号表已经生成:
那么到这里我们就完成了符号表导出,接下来打开 No$GBA 模拟器并加载我们的游戏,再尝试点击主窗口暂停游戏,此时你会发现左上角的汇编窗口显示出了方便解读的子程序名称:
No$GBA 我们算是导入完成了,那么我们还要更进一步,让 IDA 也给 NitroSDK 的符号做逆向分析吧!
回到 CrystalTile2 的 NEF 符号表编辑器,这次我们取消勾选 过滤器 中的 OTHER 选项和 对重定义符号进行编号 选项,然后重新搜索并生成一份符号表:
接下来我们打开生成出来的 TXT 符号表文件,把超出我们主内存区域(0x02000000 - 0x02400000)的符号删除(CrystalTile2 已经帮我们排好顺序了,只要看看开头结尾就行)(因为我们 IDA 里导入的内存范围就只有这么大)(这边推荐用 VSCode 打开):
然后保存文件,注意使用 UTF-8 编码保存(CrystalTile2 默认保存为 UTF-16 LE 格式),接下来我们要编写一个 Python 脚本,让 IDA 读取我们的 TXT 符号表并在每个符号表开头做解析:
# 根据 SYM 符号信息,在 IDA 内解析相应的函数并重命名
import os, sys, idc
with open("""把这部分换成 TXT 符号表文件的路径""", 'r', encoding='utf8') as r:
for line in r.read().splitlines():
if len(line.strip()) > 0:
[addr, name] = line.strip().split('\t')
idc.create_insn(int(addr, 16))
idc.set_name(int(addr, 16), name, idc.SN_CHECK)
接下来打开 IDA,打开我们上一部分做的逆向项目,点击左上角的 File 文件菜单,选择 Script file… 加载脚本文件:
选择好文件后,我们可能会收到很多这样的提示,勾选 Don’t display this message again (for this session only)(对此次会话不再显示)后确定即可。(因为可能有部分符号已经被 IDA 自动命名了所以会冲突,我们无需太过在意)
执行完成后,可以看到主窗口上方的数据条的蓝色部分变多了:
然后左侧的 Functions 函数视图里的大部分函数也被正确命名了:
当然因为本人其实不大会用 IDA,这个脚本并不能完美导入全部的符号,还请大佬来优化一下脚本了。
如果遇到没有被正确命名的函数也不用担心,以这个 FS_ReadFile 为例子:
02097FB4 FS_ReadFile
我们来到这个地址所在的内存看看程序:
我们右键伪代码的函数名,选择 Rename global item…(重命名全局命名)(你也可以对准光标然后按下 N 键)
在弹出的窗口把名称重新命名然后确认即可:
到此这一部分的教程就告一段落了,希望可以帮助到大家更好的逆向分析程序哦!