一段时间以来一直在尝试解决这个问题,并且永远感谢任何在校验和算法方面有更多经验的人伸出援手。
很长一段时间以来,我一直在尝试更改旧的 Playstation 2 游戏的保存文件。但是,这种保存受到三重双向校验和的保护。
如果您好奇,这款游戏是Fire Pro Wrestling Returns。
幸运的是,我已经绘制了大量的保存数据,其中大部分是玩家可以很容易地影响自己的东西(因为其中大部分是用户创建的角色),这有很大帮助。
我已有的数据如下:
没有校验和的保存数据本身正好是 888 KB,其中最后 400 字节似乎是空填充以完善文件大小。
在这 888 kb 之后,共有 4580 字节的校验和,分为三个块。
通过游戏更改保存数据并重新保存的实验,通过比较保存文件,表明校验和的工作方式如下:
第一个块是 3552 字节,由 888 个 4 字节校验和组成,每个校验和用于 888 KB 的保存数据。这已经通过更改单个字节进行了测试和验证。它们似乎也没有相互解决 - 最后有几个块,我能够使两个块彼此相同,并且它们具有相同的校验和。所以希望这意味着每个块都是在真空中计算的,而不是依赖于以前的结果。
第二个块是1024字节,似乎是主保存数据的交叉校验和(所以这个校验和的第一个字节是888个“1kb块”的保存数据的每个第一个字节的校验和)。我已经通过在各个地方更改单个字节来测试并似乎验证了这个理论,并且能够完全预测哪些校验和字节会因此而改变。
第三个块是另一个 4 字节校验和。由于第一块校验和是将 1024 字节数据转换为 4 字节校验和的校验和,而整个第二块校验和是 1024 字节,我在这里猜测这是通过对第二个校验和块,使用与块 1 相同的算法。
所以,基本上似乎有两种校验和算法在工作,其中一种我可以完全控制输入数据,第二种我的输入有限,因为并非所有的保存文件都可以轻松操作,这取自它的所有部分。
以下是块 1 中不同数据块生成的 4 字节校验和的一些示例:
BB 6B 82 F3 = all zeroes
68 8D C8 0C = 01 followed by all zeroes
B4 D9 DC B4 = 02 followed by all zeroes
DF 5F 6E 0D = all zeroes with 01 at 0x60
78 DF C3 08 = all zeroes ending with 01
我的问题是,在以二进制、十六进制或十进制查看它时,我没有发现任何可辨别的模式。我已经开始尝试对它的汇编程序进行逆向工程,看看我是否能找到这样的算法,但考虑到软件的大小(以及可能或可能的 PS2 多核架构),这并不是一项简单的任务不参与)。
我在映射第二个校验和方面所做的工作较少,但是我尝试通过添加或减去一个来更改内部的单个字节,并注意到在大多数情况下,校验和仅改变了一步(虽然在不同的方向),但现在我专注于第一个算法,因为那个感觉像是更大的头疼。
如果提供更多的实验数据有帮助,我很乐意这样做。=)
虽然最终结果绝对是我开始研究这个的原因,但我的兴趣主要不是解决方案,而是了解如何达到它的方法。
更新:
感谢一些非常有用的评论,我得到了一些调试设置,虽然我不确定我找到了正确的代码......我在加载保存游戏时记录了所有内容(因为它需要为加载时进行比较)并查找带有子程序的重复代码块。
找到要引用的正确程序集确实是我在所有这些帮助之后遇到的最大麻烦,而我正在努力学习如何解决这个问题......
例如,这个块在我的日志中重复了 104 次(这是部分加载,不是整个事情,因为我今天早上上班前没有太多时间),尽管我需要将它翻译成伪代码,看看它是否是相关的:
0012f5d4 0c04c77c: JAL , 00131df0, 001c0b04 (ra),
0012f5d8 24a5ffff: ADDIU , 00000000_00000004 (a1), 00000004 (a1), ffff (65535),
00131df0 30820008: ANDI , 00000000_00002000 (v0), 00000000_0000211c (a0), 0008 (8),
00131df4 10400008: BEQ , 00000000_00000008 (v0), 00000000_00000000 (r0), 00131e18,
00131df8 30820010: ANDI , 00000000_00000008 (v0), 00000000_0000211c (a0), 0010 (16),
00131dfc 10400004: BEQ , 00000000_00000010 (v0), 00000000_00000000 (r0), 00131e10,
00131e00 00051040: SLL , 00000000_00000010 (v0), 00000003 (a1), 01 (1),
00131e04 00451021: ADDU , 00000000_00000006 (v0), 00000006 (v0), 00000003 (a1),
00131e08 03e00008: JR , 0012f5dc (ra),
再次编辑添加:
当我有时间坐下来时,希望今晚,一个计划是获取一个我知道有效的保存文件,然后在第一个块的第三个校验和中引入错误,然后使用调试器运行加载序列再次查看失败的地方,看看是否可以缩小第一个校验和块和所涉及的例程。