我有一些字节格式的MSIL(反射的结果是GetMethodBody()),我想稍微分析一下。我想在MSIL中找到使用new运算符创建的所有类。关于如何以编程方式进行的任何想法?
答案 0 :(得分: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 :(得分:1)
答案 2 :(得分:1)
查看有关codeproject的这篇文章 http://www.codeproject.com/KB/cs/sdilreader.aspx
使用能够将IL字节[]转换为指令列表的源代码。如果您正在处理Generic,您可能想要滚动浏览消息并查看我在该文章中添加的帖子(Bug修复泛型)修复了与使用Generic相关的一些错误(仅当您想要将IL转换为显示文字)。
一旦获得了所有IL指令,只要指令的操作码(instruction.code)与OpCodes.Newobj或Newarr匹配,您只需循环遍历它们并递增计数。
如果您想更多地了解MSIL的内部,我强烈推荐John Gough撰写的“编译.NET CLR”一书。
答案 3 :(得分:0)
我还发现Frank发现的代码非常有用,但它有一个问题,交换机操作码未正确处理。
从MSDN开始,操作码后跟一个int32,其中包含跳转表中的项目数,然后是跳转到的位置。因此,具有3个项目的交换机实际上具有16个数据字节而不是4个。
我正在使用Ziad Elmalki的second post代码中的代码,其中包含一个GetData方法来识别方法调用的目标。
我通过更改GetData中的处理来更正了switch操作码的处理,看起来更像是这样:
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;