我正在做的(部分是为了娱乐和学习,部分希望有一天能作为一项严肃的虚拟化工作)是通过 ILMerge 将我的 VM dll 与目标程序集合并。
之后我才用 dnlib 修改新创建的文件,以调用我的 VM 函数来替换所选方法的方法体。我通过base64编码的二进制字符串传递了方法本身现在不存在的所需元数据,显然还有参数和旧方法体(将来我想为此实现我自己的字节码指令集,但到目前为止它只是原始代码 base64 编码)。
由于 .initlocals 根据我的经验总是设置在 .NET 方法中,因此我想做的是将每个本地的类型保存为数据,以便我可以在 Virtualizer 运行时中使用它来初始化我的本地数组。
我目前的方法只是保存 MDTokenwriter.Write(local.Type.ToTypeDefOrRef().GetNonNestedTypeRefScope().MDToken.ToInt32());
我使用 PreserveAll 标志将我的更改写入程序集opts.MetadataOptions.Flags = dnlib.DotNet.Writer.MetadataFlags.PreserveAll;
并在运行时通过以下方式解析 MDToken
for (int i = 0; i < numLocals; i++)
{
int token = ReadInt32(info, ref pos);
Type t = Module.ResolveType(token);
}
现在,这仅适用于修改后的模块本身中定义的类型,值(struct s {...})和引用类型(例如 Form1)以及其他模块中定义的引用类型(如 System.Windows.Forms.Form )
对于所有核心 CLR 类型(对象、int32、uint64 等)和来自模块外部的所有值类型(如 System.Drawing.Point), ResolveType失败并出现 ArgumentOutOfRangeException(找不到令牌),也从我可以看到的所有数组类型来看,无论底层类型是在哪里定义或引用的。
现在,为什么会这样?如果 I.9.2.1 中的规范
但是,元数据令牌不是持久标识符。相反,它的范围仅限于特定的元数据二进制文件。
被解释为元数据令牌在修改二进制文件时变得无效,为什么它对某些类型的工作非常一致?dnlib 不应该用 PreserveAll 标志来解决这个问题吗?又为什么在方法体指令中完全没有出现这个问题呢?许多指令对 InlineType 进行编码,并且 Module.ResolveType 从未失败过。
而且,更重要的是,如何解决?如何以二进制形式为方法的局部变量保存可靠的类型标识符?