我一直在搞乱存在于dll中的游戏的保存代码,并发现了一些有趣的东西。
我正在使用带有Reflexil的ILSpy来读取和更改dll的代码。
好的,我有以下代码片段(如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();
)。这与问题无关,但我知道你会问。如果有人要求我可以添加前后代码,但我没有看到这样做的重点。
我测试的方式:
这会加载保存文件,进入游戏,然后在退出时使用更改的代码覆盖它。我知道这部分代码保存了游戏方面的哪一部分,而且我知道在我的短进入和退出会话期间保持不变的事实。这样,除了保存代码之外,一切都保持不变。
当我使用此代码启动游戏并使其运行时,它会创建此二进制文件。该代码已被更改,以便删除大多数通常保存的数据,以便更容易查找更改:
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个字节。前四个表示从第一个代码段(this.V.Count
)开头的带符号的4字节整数。
最后八个是神秘的。我不知道他们来自哪里。所以玩了一些。我将第一个代码段更改为:
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字节来自哪里,所以我最终改变了代码,删除了这个,以便看看我错过了什么。事实证明我并没有真正错过任何东西。所以这也发生在完全干净的代码中。
如果某人真的感兴趣我可以告诉他们这是哪个游戏并向他们发送相关文件,以便他们可以自己尝试。
那么,有没有人知道这些字节的来源?这怎么可能?
编辑因为不清楚而投票结束的人: 在什么宇宙正是我要问的不清楚?
答案 0 :(得分:2)
该函数是虚拟的,实际运行的实现是在从StatModifier派生的子类中,你正在查看错误的类