Могу ли я получить доступ к телам методов (например, байтовому массиву IL), используя Assembly.TryGetRawMetadata и System.Reflection.Metadata? - PullRequest
0 голосов
/ 12 мая 2018

. FCL .NET Core 2 включает метод расширения Assembly.TryGetRawMetadata, который можно использовать для проверки метаданных загруженной сборки с помощью System.Reflection.Metadata .

Я хотел бы использовать эти средства для проверки тел методов (как я делал бы с обычными Reflection 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`? 
        }
    }
}

Я могу получить доступ к методам RVA, но, поскольку у меня нет способа узнать, какому RVA соответствует указатель rawMetadata, у меня, похоже, нет способа разыменования этих RVA.

AFAIK, Assembly.TryGetRawMetadata возвращает только большой двоичный объект данных, начинающийся с корня метаданных (BSJB) и включающий потоки и кучи метаданных, но тела методов фактически хранятся вне этих данных.

Конечно, я мог бы просто начать с проверки всего файла сборки (путь которого указан в thisAssembly.Location), но для этого потребуется перезагрузить его в память во второй раз.

Неужели Assembly.TryGetRawMetadata действительно не позволяют мне добраться до тел методов или я что-то пропускаю?


1 Ответ

0 голосов
/ 17 мая 2018

Я думаю, что это невозможно сделать так, как вы хотели бы с TryGetRawMetadata. Как указано в спецификации, «тела метода могут храниться в любом доступном только для чтения разделе PE-файла», и поэтому одного блоба метаданных недостаточно для разрешения этого 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 не обязательно будет читать весь файл сборки в память. Он принимает поток и имеет аргумент PEStreamOptions для конструктора. Эти опции включают PrefetchImage, PrefetchMetadata (среди прочих), что предполагает, что по умолчанию он не считывает все это в память.

Как вариант - вы можете использовать TryGetRawMetadata, чтобы получить RVA, а затем отдельно использовать PEReader, чтобы преобразовать его в тело метода. Есть вероятность, что PEReader будет тогда читать минимальное количество, необходимое для разрешения этого RVA, а затем будет искать непосредственно в теле и читать только это, хотя я не совсем уверен в этом.

Если вы достаточно смелы - вы можете даже попытаться найти начало PE-образа из корневого указателя метаданных, а затем передать его в конструктор PEReader, хотя я, очевидно, не рекомендую этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...