Msil Parser Quick Update
Here is a quick update. The code will now resolve any tokens in the msil so now you can see the methods, strings, types, etc. I added a new method called GetData that takes a module, opcode, and the raw data byte array. As you can see they are very few data types that are inline in the msil. We use the OperandType enumeration to determine what the operand is for the given opcode. To resolve the inline tokens in the msil we use some of the methods on the Module class. These methods are listed below.
https://msdn.microsoft.com/en-us/library/system.reflection.module.resolvefield.aspx
https://msdn.microsoft.com/en-us/library/system.reflection.module.resolvemember.aspx
https://msdn.microsoft.com/en-us/library/system.reflection.module.resolvemethod.aspx
https://msdn.microsoft.com/en-us/library/system.reflection.module.resolvesignature.aspx
https://msdn.microsoft.com/en-us/library/system.reflection.module.resolvestring.aspx
https://msdn.microsoft.com/en-us/library/system.reflection.module.resolvetype.aspx
When I have some free time this week I am going to look at writing the msil back out using reflection.emit and possibly some other cool things.
Update : Fixed small bug with MsilInstruction.ToString() and RawData byte array.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Reflection.Emit;
namespace TestMsilReader
{
class Program
{
static void Main(string[] args)
{
PrintMethod(typeof(Program).GetMethod("TestMethod", Type.EmptyTypes));
PrintMethod(typeof(String).GetMethod("IsInterned", new Type[] { typeof(string) }));
PrintMethod(typeof(String).GetMethod("IsNullOrEmpty", new Type[] { typeof(string) }));
PrintMethod(typeof(String).GetMethod("Copy", new Type[] { typeof(string) }));
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
private static void PrintMethod(MethodInfo methodInfo)
{
Console.WriteLine("Method {0}.{1}", methodInfo.DeclaringType.Name, methodInfo.Name);
Console.WriteLine("-------------------------------");
MsilReader reader = new MsilReader(methodInfo);
while (reader.Read())
{
Console.WriteLine(reader.Current);
}
Console.WriteLine("-------------------------------");
}
public static void TestMethod()
{
int i = 0;
i += 1;
i += 2303;
int x = i / 2;
Console.WriteLine(x);
Console.WriteLine(i);
Console.WriteLine("test");
}
public class MsilReader
{
private static Dictionary<short, OpCode> _instructionLookup;
private static object _syncObject = new object();
private BinaryReader _methodReader;
private MsilInstruction _current;
private Module _module;
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 MsilInstruction Current
{
get
{
return _current;
}
}
public bool Read()
{
if (_methodReader.BaseStream.Length == _methodReader.BaseStream.Position)
{
return false;
}
int index = (int)_methodReader.BaseStream.Position;
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);
byte[] data = new byte[dataSize];
_methodReader.Read(data, 0, dataSize);
object objData = GetData(_module, code, data);
_current = new MsilInstruction(code, data, index, objData);
return true;
}
private static object GetData(Module module, OpCode code, byte[] rawData)
{
object data = null;
switch (code.OperandType)
{
case OperandType.InlineField:
data = module.ResolveField(BitConverter.ToInt32(rawData,0));
break;
case OperandType.InlineBrTarget:
case OperandType.InlineSwitch:
case OperandType.InlineI:
data = BitConverter.ToInt32(rawData, 0);
break;
case OperandType.InlineI8:
data = BitConverter.ToInt64(rawData, 0);
break;
case OperandType.InlineMethod:
data = module.ResolveMethod(BitConverter.ToInt32(rawData, 0));
break;
case OperandType.InlineR:
data = BitConverter.ToDouble(rawData, 0);
break;
case OperandType.InlineSig:
data = module.ResolveSignature(BitConverter.ToInt32(rawData, 0));
break;
case OperandType.InlineString:
data = module.ResolveString(BitConverter.ToInt32(rawData, 0));
break;
case OperandType.InlineTok:
case OperandType.InlineType:
data = module.ResolveType(BitConverter.ToInt32(rawData, 0));
break;
case OperandType.InlineVar:
data = BitConverter.ToInt16(rawData, 0);
break;
case OperandType.ShortInlineVar:
case OperandType.ShortInlineI:
case OperandType.ShortInlineBrTarget:
data = rawData[0];
break;
case OperandType.ShortInlineR:
data = BitConverter.ToSingle(rawData, 0);
break;
}
return data;
}
private 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;
}
}
private static Dictionary<short, OpCode> GetLookupTable()
{
Dictionary<short, OpCode> lookupTable = new Dictionary<short, OpCode>();
FieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo field in fields)
{
OpCode code = (OpCode)field.GetValue(null);
lookupTable.Add(code.Value, code);
}
return lookupTable;
}
}
public struct MsilInstruction
{
internal MsilInstruction(OpCode code, byte[] rawData, int index, object data)
{
Instruction = code;
RawData = rawData;
Index = index;
Data = data;
}
public readonly OpCode Instruction;
public readonly byte[] RawData;
public readonly int Index;
private readonly object Data;
public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.AppendFormat("0x{0:x4} ", Index);
builder.Append(Instruction.Name);
if (RawData != null && RawData.Length > 0)
{
builder.Append(" 0x");
for ( int i = RawData.Length - 1; i >= 0; i-- )
{
builder.Append(RawData[i].ToString("x2"));
}
}
if (Data != null)
{
builder.Append(" " + Data.ToString());
}
return builder.ToString();
}
}
}
}