我可以使用Assembly.TryGetRawMetadata和System.Reflection.Metadata访问方法体(例如IL字节数组)吗?

时间:2018-05-12 17:26:40

标签: c# reflection .net-core metadata

.NET Core 2的FCL包含一个扩展方法Assembly.TryGetRawMetadata,可以使用System.Reflection.Metadata来检查加载的程序集的元数据。

我想使用这些工具来检查方法体(就像我使用常规的反射MethodBody一样),但这似乎不可能。

using System.Reflection;
using System.Reflection.Metadata;

class Program
{
    unsafe static void Main()
    {
        var thisAssembly = Assembly.GetExecutingAssembly();
        if (thisAssembly.TryGetRawMetadata(out byte* rawMetadata, out int rawMetadataLength) == false)
        {
            return;
        }

        var metadata = new MetadataReader(rawMetadata, rawMetadataLength);

        foreach (var methodDefinitionHandle in metadata.MethodDefinitions)
        {
            var methodDefinition = metadata.GetMethodDefinition(methodDefinitionHandle);
            var methodRVA = methodDefinition.RelativeVirtualAddress;
            … // Question: Is there any way to get to the method body from `methodRVA`? 
        }
    }
}

我可以使用方法'RVAs,但由于我无法找到rawMetadata指针对应的RVA,我似乎无法解除引用这些RVA。

AFAIK,Assembly.TryGetRawMetadata仅返回从元数据根(BSJB)开始并包含元数据流和堆的数据blob,但方法体实际上存储在此数据之外。

当然,我可以先检查整个程序集文件(其路径在thisAssembly.Location中给出),但这需要第二次将其重新加载到内存中。

Assembly.TryGetRawMetadata真的不允许我进入方法体,还是忽略了什么?

1 个答案:

答案 0 :(得分:1)

我认为用TryGetRawMetadata完全不可能做到这一点。正如规范所述,"方法体可以存储在PE文件的任何只读部分中,因此仅元数据blob不足以解析该RVA。你必须像这样使用PEReader

static void Main() {
    var thisAssembly = Assembly.GetExecutingAssembly();
    using (var asmFile = File.OpenRead(thisAssembly.Location)) {
        var reader = new PEReader(asmFile);
        var metadata = reader.GetMetadataReader();

        foreach (var methodDefinitionHandle in metadata.MethodDefinitions) {
            var methodDefinition = metadata.GetMethodDefinition(methodDefinitionHandle);
            var methodRVA = methodDefinition.RelativeVirtualAddress;
            // now with PEReader you can resolve your RVA
            var body = reader.GetMethodBody(methodRVA);
            var ilReader = body.GetILReader();
            // ...
        }
    }
}

为了让它按照您的意愿工作 - 应该有一些模拟TryGetRawMetadata,它返回整个PE图像的指针和长度。 PEReader已经有接受这样的指针和长度的构造函数,据我所知,已经提出了这样的模拟方法(TryGetRawImage),但尚未实现。

那就是说,PEReader不需要将整个汇编文件读入内存。它接受stream,并且对构造函数有PEStreamOptions参数。这些选项包括PrefetchImagePrefetchMetadata(以及其他),这表明默认情况下它不会将整个内容读入内存。

作为选项 - 您可以使用TryGetRawMetadata获取RVA,然后单独使用PEReader将其解析为方法正文。 PEReader有可能会读取解决该RVA所需的最小数量,然后直接寻求身体并只读它,尽管我并不完全确定。

如果你足够勇敢 - 你甚至可以尝试从元数据根指针本身找到PE图像的开始,然后将其提供给PEReader构造函数,尽管我显然不会推荐它。