2010-03-12 4 views
3

J'ai quelques MSIL au format byte (résultat de GetMethodBody() de la réflexion) que je voudrais analyser un peu. Je voudrais trouver toutes les classes créées avec le nouvel opérateur dans le MSIL. Des idées sur la façon de le faire par programme?MSIL inspection

+0

Avez-vous accès à l'assemblée? Si vous le faites, vous pouvez utiliser l'outil Reflector. –

+0

Je veux le faire par programmation, car j'ai beaucoup de code à passer. –

+2

La section Remarques vous indique ce dont vous avez besoin: http://msdn.microsoft.com/en-us/library/system.reflection.methodbody.getilasbytearray.aspx –

Répondre

2

J'ai fini par utiliser l'analyseur MSIL ici: http://blogs.msdn.com/zelmalki/archive/2008/12/11/msil-parser.aspx, avec la source légèrement modifiée pour fonctionner sur ConstructorInfo ainsi que MethodInfo (résultats renvoyés par le réflecteur).

Il donnera une liste des opérations, avec l'opcode et les paramètres. L'opcode est une énumération, sur la base de cette valeur, les paramètres peuvent être interprétés. Les paramètres sont sous forme binaire, besoin d'utiliser MethodInfo.Module.Resolve *() pour obtenir les valeurs des paramètres réels.

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

Vous pourriez jeter un oeil au moteur derrière des outils comme FxCop. Il s'appelle CCI. Ou consultez celui de Mono, nommé Cecil, sur lequel est basé Gendarme. Ils sont construits pour ces (et autres) types de tâches.

1

Vérifiez cet article sur CodeProject http://www.codeproject.com/KB/cs/sdilreader.aspx

Utilisez le code source qui vous donnera la capacité de prendre l'octet IL [] dans une liste d'instructions. Si vous traitez avec Generic, vous pouvez faire défiler les messages et vérifier un article que j'ai mis dans cet article (Bug Fix for Generic) qui a corrigé quelques bugs liés à l'utilisation avec Generic (seulement quand vous voulez transformer l'IL en texte affiché). Une fois que vous avez toutes les instructions IL, il vous suffit de les parcourir et d'incrémenter le compte chaque fois que l'opcode de l'instruction (code d'instruction) correspond à OpCodes.Newobj ou Newarr.

Si vous voulez acquérir plus de compréhension sur l'interne de MSIL, je recommande fortement le livre "Compilation for the .NET CLR" de John Gough.

+0

Cela aurait probablement fonctionné aussi bien que l'autre code que j'ai trouvé, Merci. –

0

J'ai également trouvé que le code trouvé par Frank est très utile mais il a un problème, un opcode switch n'est pas traité correctement.

A partir de MSDN, l'opcode est suivi d'un int32 contenant le nombre d'éléments dans la table de sauts, puis les positions à atteindre. Donc un commutateur avec 3 éléments a réellement 16 octets de données non 4.

J'utilise le code de second post de Ziad Elmalki sur le sujet qui inclut une méthode GetData pour identifier des choses comme la cible d'un appel de méthode.

Je corrige le traitement des opcodes de commutation en changeant la leur manipulation en GetData à ressembler à ceci:

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; 
Questions connexes