2010-03-12 3 views
3

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

+0

У вас есть доступ к сборке? Если вы это сделаете, вы можете использовать инструмент Reflector. –

+0

Я хочу сделать это программно, так как у меня много кода для прохождения. –

+2

В разделе «Примечания» указано, что вам нужно: http://msdn.microsoft.com/en-us/library/system.reflection.methodbody.getilasbytearray.aspx –

ответ

2

В итоге я использовал анализатор 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(); 
     } 
    } 
} 
1

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

1

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

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

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

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

+0

Возможно, это сработало бы так же, как и другой код, который я нашел, Благодарю. –

0

Я также обнаружил, что код Frank оказался очень полезным, но он имеет одну проблему, код операции переключения обрабатывается неправильно.

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

Я использую код от от Ziad Elmalki по этому вопросу, который включает метод 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;