Проверка MSIL

У меня есть MSIL в байтовом формате (результат отражения GetMethodBody ()), который я хотел бы немного проанализировать. Я хотел бы найти все классы, созданные с помощью оператора new в MSIL. Любые идеи о том, как это сделать программно?


person Frank Schwieterman    schedule 12.03.2010    source источник
comment
У вас есть доступ к сборке? Если вы это сделаете, вы можете использовать инструмент Reflector.   -  person Andrew Bezzub    schedule 13.03.2010
comment
Я хочу сделать это программно, так как мне нужно пройти много кода.   -  person Frank Schwieterman    schedule 13.03.2010
comment
В разделе «Примечания» указано, что вам нужно: msdn.microsoft .com / ru-ru / library /   -  person Hans Passant    schedule 13.03.2010


Ответы (4)


В итоге я использовал парсер MSIL здесь: http://blogs.msdn.com/zelmalki/archive/2008/12/11/msil-parser.aspx, с немного измененным источником для работы с ConstructorInfo, а также с MethodInfo (результаты возвращаются отражателем).

Он предоставит список операций с кодом операции и параметрами. Код операции - это перечисление, на основе которого можно интерпретировать параметры. Параметры представлены в двоичной форме, необходимо использовать MethodInfo.Module.Resolve * () для получения фактических значений параметров.

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace AspnetReflection
{
    public class MsilReader
    {
        static readonly Dictionary<short, OpCode> _instructionLookup;
        static readonly object _syncObject = new object();
        readonly BinaryReader _methodReader;
        MsilInstruction _current;
        Module _module; // Need to resolve method, type tokens etc


        static MsilReader()
        {
            if (_instructionLookup == null)
            {
                lock (_syncObject)
                {
                    if (_instructionLookup == null)
                    {
                        _instructionLookup = GetLookupTable();
                    }
                }
            }
        }

        public MsilReader(MethodInfo method)
        {
            if (method == null)
            {
                throw new ArgumentException("method");
            }

            _module = method.Module;
            _methodReader = new BinaryReader(new MemoryStream(method.GetMethodBody().GetILAsByteArray()));
        }

        public MsilReader(ConstructorInfo contructor)
        {
            if (contructor == null)
            {
                throw new ArgumentException("contructor");
            }

            _module = contructor.Module;
            _methodReader = new BinaryReader(new MemoryStream(contructor.GetMethodBody().GetILAsByteArray()));
        }

        public MsilInstruction Current
        {
            get { return _current; }
        }


        public bool Read()
        {
            if (_methodReader.BaseStream.Length == _methodReader.BaseStream.Position)
            {
                return false;
            }

            int instructionValue;

            if (_methodReader.BaseStream.Length - 1 == _methodReader.BaseStream.Position)
            {
                instructionValue = _methodReader.ReadByte();
            }
            else
            {
                instructionValue = _methodReader.ReadUInt16();

                if ((instructionValue & OpCodes.Prefix1.Value) != OpCodes.Prefix1.Value)
                {
                    instructionValue &= 0xff;
                    _methodReader.BaseStream.Position--;
                }
                else
                {
                    instructionValue = ((0xFF00 & instructionValue) >> 8) |
                                       ((0xFF & instructionValue) << 8);
                }
            }

            OpCode code;

            if (!_instructionLookup.TryGetValue((short) instructionValue, out code))
            {
                throw new InvalidProgramException();
            }

            int dataSize = GetSize(code.OperandType);

            var data = new byte[dataSize];

            _methodReader.Read(data, 0, dataSize);

            _current = new MsilInstruction(code, data);

            return true;
        }

        static int GetSize(OperandType opType)
        {
            int size = 0;

            switch (opType)
            {
                case OperandType.InlineNone:
                    return 0;
                case OperandType.ShortInlineBrTarget:
                case OperandType.ShortInlineI:
                case OperandType.ShortInlineVar:
                    return 1;

                case OperandType.InlineVar:
                    return 2;

                case OperandType.InlineBrTarget:
                case OperandType.InlineField:
                case OperandType.InlineI:
                case OperandType.InlineMethod:
                case OperandType.InlineSig:
                case OperandType.InlineString:
                case OperandType.InlineSwitch:
                case OperandType.InlineTok:
                case OperandType.InlineType:
                case OperandType.ShortInlineR:
                    return 4;
                case OperandType.InlineI8:

                case OperandType.InlineR:


                    return 8;

                default:

                    return 0;
            }
        }


        static Dictionary<short, OpCode> GetLookupTable()
        {
            var lookupTable = new Dictionary<short, OpCode>();

            FieldInfo[] fields = typeof (OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public);

            foreach (FieldInfo field in fields)
            {
                var code = (OpCode) field.GetValue(null);

                lookupTable.Add(code.Value, code);
            }

            return lookupTable;
        }
    }


    public struct MsilInstruction
    {
        public readonly byte[] Data;
        public readonly OpCode Instruction;

        public MsilInstruction(OpCode code, byte[] data)
        {
            Instruction = code;

            Data = data;
        }


        public override string ToString()
        {
            var builder = new StringBuilder();

            builder.Append(Instruction.Name + " ");

            if (Data != null && Data.Length > 0)
            {
                builder.Append("0x");

                foreach (byte b in Data)
                {
                    builder.Append(b.ToString("x2"));
                }
            }

            return builder.ToString();
        }
    }
}
person Frank Schwieterman    schedule 16.03.2010

Вы можете взглянуть на движок таких инструментов, как FxCop. Он называется CCI. Или посмотрите на одно из Mono, названное Cecil, на котором основан Жандарм. Они созданы для этих (и других) задач.

person Lars Truijens    schedule 12.03.2010

Ознакомьтесь с этой статьей о codeproject http://www.codeproject.com/KB/cs/sdilreader.aspx

Используйте исходный код, который даст вам возможность включить байт IL [] в список инструкций. Если вы имеете дело с Generic, вы можете просмотреть сообщения и проверить сообщение, которое я поместил в этой статье (Исправление ошибок для Generic), в котором исправлены некоторые ошибки, связанные с использованием Generic (только если вы хотите превратить IL в отображать текст).

Когда у вас есть все инструкции IL, все, что вам нужно, - это перебрать их и увеличивать счетчик всякий раз, когда код операции инструкции (struction.code) совпадает с OpCodes.Newobj или Newarr.

Если вы хотите лучше понять внутреннюю структуру MSIL, я настоятельно рекомендую книгу Джона Гофа «Компиляция для .NET CLR».

person Fadrian Sudaman    schedule 13.03.2010
comment
Вероятно, это сработало бы так же хорошо, как и другой код, который я нашел, спасибо. - person Frank Schwieterman; 17.03.2010

Я также нашел код, который Фрэнк счел очень полезным, но у него есть одна проблема: код операции переключения обрабатывается неправильно.

Из MSDN, за кодом операции следует int32, содержащий количество элементов в таблице переходов, а затем позиции для перехода. Таким образом, коммутатор с 3 элементами фактически имеет 16 байтов данных, а не 4.

Я использую код Зиада Эльмалки второй пост по теме, который включает метод GetData для определения таких вещей, как цель вызова метода.

Я исправил обработку кодов операций переключателя, изменив их обработку в GetData, чтобы они выглядели примерно так:

    case OperandType.InlineSwitch:
        {
            int numberOfCases = BitConverter.ToInt32(rawData, 0);
            int[] caseAddresses = new int[numberOfCases];
            byte[] caseData = new byte[4];
            for (int i = 0; i < numberOfCases; i++)
            {
                _methodReader.Read(caseData, 0, caseData.Length);
                caseAddresses[i] = BitConverter.ToInt32(caseData, 0);
            }
            data = caseAddresses;
        }
        break;
person Andrew Kennan    schedule 23.07.2010