超越仿真:对N64源代码进行逆向工程的巨大努力

2020-05-07 20:03:25

本周早些时候,在没有任何警告的情况下,互联网上出现了一个Windows可执行文件,其中包含一个完全可以玩的超级马里奥64(Super Mario 64)PC端口。这个独立的程序远不只是一个普通的模拟ROM,它支持自动缩放到任何屏幕分辨率等功能,玩家已经在试验添加简单的显卡级别的重着色器,包括光线跟踪。

PC端口--发布时几乎没有升级--并不像其他一些现已停产的Super Mario 64移植项目那样,是在现代游戏引擎中从头开始构建的。它的发布与最近任天堂内部文件的泄露无关,这些文件可以追溯到Gamecube时代。

相反,这个移植似乎是一年来努力将Super Mario64ROM反编译成可解析的C代码的直接结果。这种从原始二进制代码到易读代码的逆向工程并不是一个简单的过程,但越来越多的业余反编译器社区正在努力解开他们最喜欢的一些游戏背后的秘密。

两年的反编译超级马里奥64的努力并不是从一开始就考虑到Windows可执行文件的。肯尼克斯目前正在帮助领导从这项努力中剥离出来的塞尔达逆向工程团队(Zelda Reverse Engineering Team,简称ZRET),他说,相反,它的主要动机是那些想要更好地理解游戏代码的速跑者,以便帮助发现那些[速跑]漏洞。(Kenix目前正在帮助领导从这项努力中剥离出来的塞尔达逆向工程团队(Zelda Reverse Engineering Team,简称ZRET)。

对N64ROM进行反向工程的第一步是最基本的,就像首先找出使用了哪个特定版本的Silicon Graphics IDO编译器来创建ROM一样。这需要进行大量的试验和错误测试,在某些情况下,可以通过仔细解析泄漏的调试版本和隐藏在ROM文件中的源代码片段来帮助测试。Kenix告诉ARS,即使运行这样一个过时的编译器进行测试,也意味着模仿最初的N64开发人员使用的SGI工作站系统调用。

下一步只是简单地找出ROM是如何组织的。";您如何知道函数在哪里?“你怎么知道多边形或纹理数据在哪里?”肯尼克斯说。您必须分析ROM的内部结构,然后生成一种将其拆分成各种文件的方法。

幸运的是,N64游戏将它们的文件安排在16字节的块中,这样可以更容易地看到标记文件末尾的空填充。还有一些游戏,比如Ocarina of Time,使用了一个易于解析的直接内存访问表,该表定义了原始ROM中的大多数文件边界(如今,像N64Split这样的工具可以自动执行这个过程)。游戏的调试版本还可以帮助反向工程师记录其结构,这要归功于存在未压缩的游戏文件和C宏,如__FILE__和__LINE__,它们揭示了任天堂使用的内部文件名。

由于ROM的编译器和基本结构已知,简单的反编译技术可以生成一个庞大的原始汇编语言指令列表,这些指令将馈送到N64硬件。但是,将这些指令转换成人类可解析和容易编辑的C代码绝非一个简单的过程(而且将汇编代码转换为C的自动化工具通常会引入逻辑错误或使代码混淆得太严重)。

因此,真正对N64ROM进行反向工程意味着逐个函数地检查这些汇编代码文件,并手动将它们转换成可用的C代码。与仿真不同的是,有时足够接近就足够了,但这里的精确度很重要。我们的目标是[在运行编译器之后]逐字节匹配游戏中所有函数的原始汇编代码,";Kenix说。

即使以这种方式转换少数汇编指令的一个小函数也可能是一个复杂的过程。但是,单个N64功能可能会运行数千条指令,而一款N64游戏可能会有数千条这样的功能(例如,在Ocarina of Time中,超过15,700个)。

难度也可能因游戏而异。对于超级马里奥64,任天堂在编译源代码时没有任何花哨的编译器选项,这意味着反编译后的汇编语言更容易转换回C代码。然而,对于像“时间的奥卡莉娜”这样的游戏,任天堂使用优化标志来生成更快的代码,使得生成的ROM更难解开源代码。

Kenix说,当有优化标志时,对于';vs';While';[Statement]等,很难将循环与';匹配。您必须尝试所有等效的代码模式,直到找到匹配的模式。

虽然ZRET领导层明白PC端口将是他们努力的自然结果,但Kenix表示,反向工程师认为这超出了我们的工作范围。我们只需反编译游戏即可。其他人不可避免地会拿起它并写入PC端口。";

但ZRET成员Rozlette指出,即使手头有反编译的C代码,制作PC端口也不像只是为Windows编译那么容易,ZRET成员罗兹莱特(Rozlette)指出。";有很多代码处理与N64硬件的对话。例如,N64渲染管道与现代OpenGL有很大不同。