Проверка MSIL - PullRequest
       22

Проверка MSIL

3 голосов
/ 13 марта 2010

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

Ответы [ 4 ]

2 голосов
/ 16 марта 2010

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

Он выдаст список операций с кодом операции и параметрами. Код операции - это enum, в зависимости от этого значения параметры могут быть интерпретированы. Параметры в двоичном виде, необходимо использовать 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();
        }
    }
}
1 голос
/ 13 марта 2010

Проверьте эту статью на codeproject http://www.codeproject.com/KB/cs/sdilreader.aspx

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

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

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

1 голос
/ 13 марта 2010

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

0 голосов
/ 23 июля 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;
...