【发布时间】:2015-12-29 23:07:55
【问题描述】:
我一直在搞乱驻留在 dll 中的游戏的保存代码,并发现了一些有趣的东西。
我正在使用带有 Reflexil 的 ILSpy 来读取和更改 dll 的代码。
好的,我有以下 sn-p 代码(如 ILSpy 所示):
stream.Write(this.V.Count); //this is signed 4-byte integer, it equals 1 in this case (10 00 00 00)
using (List<StatModifier>.Enumerator enumerator = this.V.GetEnumerator())
{
while (enumerator.MoveNext())
{
StatModifier current = enumerator.Current;
current.Write(stream);
}
}
在此之前有一些代码写入同一个 BinaryWriter,但我稍后会谈到,因为它与这个问题无关。
current.Write(stream); 是对该方法的调用:
public virtual void Write(BinaryWriter stream)
{
}
这是一个空方法(我使用 Reflexil 删除了它的内容)。只有其中的 IL 代码是这个单一命令(如 ILSpy 中所示):
Offset OpCode Operand
>0 0 ret
接下来运行的代码(在方法调用之后)被 ret 命令缩短,因此它不会向二进制文件写入任何其他内容。然后游戏代码会正确关闭文件 (stream.Close();)。这与问题无关,但我知道你会问。如果有人要求,我可以添加之前和之后的代码,但我没有看到这样做的意义。
我测试的方式:
- 我用干净的游戏代码创建了一个保存文件,以便加载它。
- 我将它复制到另一个文件夹。
- 我修改了代码来测试它。
- 我把原来的复制回来了。
- 我已进入和退出游戏(执行此代码 - 将游戏保存到文件的代码)。
- 我用十六进制编辑器检查了文件。
- 重复 3-6。
这会加载保存文件,进入游戏,然后在退出时使用更改后的代码覆盖它。我知道这部分代码保存了游戏方面的哪一部分,并且我知道在我短暂的进入和退出会话期间它保持不变。这样,除了保存代码之外,一切都保持不变。
当我用这段代码启动游戏并让它运行时,它会创建这个二进制文件。该代码已被更改,因此大多数通常保存的数据都已删除,因此更容易查找更改:
74 74 70 00 18 05 00 00 00 9A 99 19 3F 00 00 00
00 00 00 00 00 00 00 80 3F 00 00 80 3F 00 00 80
3F 01 01 00 00 00 9A 99 19 3F 9A 99 19 3F
二进制文件的第一部分是由我在开头省略的代码编写的,因为它跨越了几个类和方法调用,但我认为这里没有任何意义。这是重要的部分:
01 00 00 00 9A 99 19 3F 9A 99 19 3F
这是最后 12 个字节。前四个表示从第一个代码 sn-p (this.V.Count) 开始的有符号 4 字节整数。
最后八个是神秘的。我不知道他们是从哪里来的。于是又玩了一些。我把第一个 sn-p 改成这样:
stream.Write(this.V.Count); //this is signed 4-byte integer, it equals 1 in this case (10 00 00 00)
using (List<StatModifier>.Enumerator enumerator = this.V.GetEnumerator())
{
while (enumerator.MoveNext())
{
StatModifier current = enumerator.Current;
}
}
如您所见,current.Write(stream) 不再被调用。
当我再次运行游戏时,我得到了这个输出:
7C 74 70 00 18 05 00 00 00 9A 99 19 3F 00 00 00
00 00 00 00 00 00 00 80 3F 00 00 80 3F 00 00 80
3F 01 01 00 00 00
你有没有发现有什么遗漏?第一次运行的最后 8 个字节消失了。唯一的变化是删除了空方法调用。
当我查看完整的保存文件时,我第一次注意到这一点,试图在源代码中跟踪它的创建。我只是找不到那些 8 字节的来源,所以我最终修改了代码,删除了这个和那个,看看我错过了什么。事实证明我并没有错过任何东西。所以这也发生在完全干净的代码中。
如果有人真的感兴趣,我可以告诉他们这是关于哪款游戏,并将相关文件发送给他们,以便他们自己尝试。
那么,有人知道这些字节的来源吗?这怎么可能?
编辑由于不清楚而投票关闭的人: 我所问的究竟是在哪个宇宙中不清楚?
【问题讨论】:
-
函数是虚函数,实际运行的实现是在从StatModifier派生的子类中,你看错了类。
-
如何找到该课程?所有代码都一样吗?如何阅读?
-
哦,我想我明白了。有一个从代码中读取的枚举(当查看加载代码时),其中包含 StatModifierOT、StatModifierMax 和一些类似的名称,这些名称都是具有覆盖(whatsitcall)字的写入方法的类。我敢打赌,如果我从正确的课程中阅读,一切都会再次变得有意义。将您的评论作为答案,它将被接受。
标签: c# byte binaryfiles cil binarywriter