解析方法体中的 IL 代码
2006-12-01 17:57
387 查看
解析方法体中的 IL 代码
本文由 loyee 翻译,E文很差,某些词句翻译不当,还请多多指正
。译文辛苦,转载请注明出处。
点击此处查看原文
点击下载演示工程 - 8.52 Kb
点击下载源代码 - 21.9 Kb
当你仔细查看代码的时候,可能想知道方法体内使用过的变量和其生命周期以及产生了什么结果。Microsoft 忽视了这个需求,但还是提供了一些东西:IL代码。但这是不够的,实际上是一个字节数组,对于未经专业训练的普通程序员来讲没有任何意义。
我们需要的是一系列的,表现为IL编码形式的实际的操作指令,这正我下面要讲到的。
你一定对我说的“几乎一致”有疑问,因为反射机制不能为你提供最初的源代码。编译器首先移除了所有代码注释,甚至包括从不使用的变量,并且只有有效的和必须的代码被编译到程序中。因而我们不能获得最精确的源代码。
好了,Reflector 是非常不错。但是我们希望在自己的代码中也能获得如 Reflector 这样的效果,我们如何能做到?
首先使用一个典型的例子“Hello world”,由此看看我们希望的结果和利用 .Net Framework 得到的实际的结果:
C#代码:
当我们采用反射方法获取到 SayHello 方法体中的 IL 代码,得到的是一个字节数组,如下:
OK,这样很难读懂。我们所知道的是要将这些IL代码转换,以便我们能处理[阅读或理解,译者注]它。最容易的方法是将其转换为MSIL(Microsoft Intermediate Language)语言。下面是SayHello方法体转换而来的 MSIL 代码(我的程序集应该返回的结果):
MethodInfo mi = null;
// obtain somehow the method info of the method we want to dissasemble
// ussually you open the assembly, get the module, get the type and then the
// method from that type
//
// instanciate a method body reader
SDILReader.MethodBodyReader mr = new MethodBodyReader(mi);
// get the text representation of the msil
string msil = mr.GetBodyCode();
// or parse the list of instructions of the MSIL
for (int i=0; i<mr.instructions.Count;i++)
{
// do something with mr.instructions[i]
}
因此,为了解释这串IL字节,我们必须像这样做:
得到下一个字节,并且查看其操作符;
根据操作符,获取后续1到4个字节中定义的元数据地址;
采用
保存指令对<操作符,操作数>.
重复上述工作,直到解释完这串IL代码。
我们在这里看到的是解析IL的简单循环。其实,它并不简单,其中包括了18个case语句。我没有考虑所有的操作符,仅仅包含了最常用的一部分(实际上有多于240个的操作符)。 操作符在启动应用程序时被载入到两个静态列表中:
public static OpCode[] multiByteOpCodes;
public static OpCode[] singleByteOpCodes; public static void LoadOpCodes()
{
singleByteOpCodes = new OpCode[0x100];
multiByteOpCodes = new OpCode[0x100];
FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
for (int num1 = 0; num1 < infoArray1.Length; num1++)
{
FieldInfo info1 = infoArray1[num1];
if (info1.FieldType == typeof(OpCode))
{
OpCode code1 = (OpCode)info1.GetValue(null);
ushort num2 = (ushort)code1.Value;
if (num2 < 0x100)
{
singleByteOpCodes[(int)num2] = code1;
}
else
{
if ((num2 & 0xff00) != 0xfe00)
{
throw new Exception("Invalid OpCode.");
}
multiByteOpCodes[num2 & 0xff] = code1;
}
}
}
}
构造MethodBodyReader实例后,我们可以利用它任意解析指令清单,获得其字符串表现形式。就是它,有趣的反编译。
netken
本文由 loyee 翻译,E文很差,某些词句翻译不当,还请多多指正
。译文辛苦,转载请注明出处。
点击此处查看原文
点击下载演示工程 - 8.52 Kb
点击下载源代码 - 21.9 Kb
介绍
.NET 的System.Reflection命名空间提供了完整的反射机制,用于查看程序集的结构。可以通过它获取程序集中的所有类型定义,字段,属性,基本上能满足你所有的要求[包括方法定义,译者注]。尽管如此,某些内容仍是看不见的,方法体中的代码就是如此。
当你仔细查看代码的时候,可能想知道方法体内使用过的变量和其生命周期以及产生了什么结果。Microsoft 忽视了这个需求,但还是提供了一些东西:IL代码。但这是不够的,实际上是一个字节数组,对于未经专业训练的普通程序员来讲没有任何意义。
我们需要的是一系列的,表现为IL编码形式的实际的操作指令,这正我下面要讲到的。
背景
任何程序员使用过反射,就一定听说过由Lutz Roeder写的让人生畏的 Reflector 。Reflector 能将任何.NET 程序集反编译成与源代码几乎一致的代码。你一定对我说的“几乎一致”有疑问,因为反射机制不能为你提供最初的源代码。编译器首先移除了所有代码注释,甚至包括从不使用的变量,并且只有有效的和必须的代码被编译到程序中。因而我们不能获得最精确的源代码。
好了,Reflector 是非常不错。但是我们希望在自己的代码中也能获得如 Reflector 这样的效果,我们如何能做到?
首先使用一个典型的例子“Hello world”,由此看看我们希望的结果和利用 .Net Framework 得到的实际的结果:
C#代码:
public void SayHello() { Console.Out.WriteLine("Hello world"); }
当我们采用反射方法获取到 SayHello 方法体中的 IL 代码,得到的是一个字节数组,如下:
0,40,52,0,0,10,114,85,1,0,112,111,53,0,0,10,0,42
OK,这样很难读懂。我们所知道的是要将这些IL代码转换,以便我们能处理[阅读或理解,译者注]它。最容易的方法是将其转换为MSIL(Microsoft Intermediate Language)语言。下面是SayHello方法体转换而来的 MSIL 代码(我的程序集应该返回的结果):
0000 : nop 0001 : call System.IO.TextWriter System.Console::get_Out() 0006 : ldstr "Hello world" 0011 : callvirt instance System.Void System.IO.TextWriter::WriteLine() 0016 : nop 0017 : ret
使用代码
SDILReader 是一个仅仅包含了三个类的动态库文件。为了获得方法体的MSIL代码,需要创建一个MethodBodyReader对象的实例,由它构造你想分解的对象的
MethodInfo实例。
MethodInfo mi = null;
// obtain somehow the method info of the method we want to dissasemble
// ussually you open the assembly, get the module, get the type and then the
// method from that type
//
// instanciate a method body reader
SDILReader.MethodBodyReader mr = new MethodBodyReader(mi);
// get the text representation of the msil
string msil = mr.GetBodyCode();
// or parse the list of instructions of the MSIL
for (int i=0; i<mr.instructions.Count;i++)
{
// do something with mr.instructions[i]
}
如何工作?
很好,这是一个不错的问题。为了开始工作[不知道get started如何翻译,译者注],我们首先需要知道由 .Net 的反射机制获得的IL代码结构。IL 代码结构
IL实际上是执行操作的指令的枚举。一个指令包含两部分<指令代码 [后文中的操作符也表示指令代码,译者注],操作数>。指令代码即System.Reflection.Emit.OpCode的二进制值,而操作数是对其作用的实体的元数据地址(方法、类型、值,等等)。这个地址在.NET Framework中称为元数据标志。
因此,为了解释这串IL字节,我们必须像这样做:
得到下一个字节,并且查看其操作符;
根据操作符,获取后续1到4个字节中定义的元数据地址;
采用
MethodInfo.Module对象获取被定义在该元数据地址的对象;
保存指令对<操作符,操作数>.
重复上述工作,直到解释完这串IL代码。
ILInstruction
ILInstruction类用于存储指令对<操作符,操作数>。同样,我们在类中提供了一个简单的方法,把内部的信息转换成一个易读的字符串。
MethodBodyReader
MethodBodyReader类做所有实际的工作。在构造方法中,调用了私有方法
ConstructInstructions,该方法解析IL代码:
int position = 0; instructions = new List<ILInstruction>(); while (position < il.Length) { ILInstruction instruction = new ILInstruction(); // get the operation code of the current instruction OpCode code = OpCodes.Nop; ushort value = il[position++]; if (value != 0xfe) { code = Globals.singleByteOpCodes[(int)value]; } else { value = il[position++]; code = Globals.multiByteOpCodes[(int)value]; value = (ushort)(value | 0xfe00); } instruction.Code = code; instruction.Offset = position - 1; int metadataToken = 0; // get the operand of the current operation switch (code.OperandType) { case OperandType.InlineBrTarget: metadataToken = ReadInt32(il, ref position); metadataToken += position; instruction.Operand = metadataToken; break; case OperandType.InlineField: metadataToken = ReadInt32(il, ref position); instruction.Operand = module.ResolveField(metadataToken); break; . } instructions.Add(instruction); }
我们在这里看到的是解析IL的简单循环。其实,它并不简单,其中包括了18个case语句。我没有考虑所有的操作符,仅仅包含了最常用的一部分(实际上有多于240个的操作符)。 操作符在启动应用程序时被载入到两个静态列表中:
public static OpCode[] multiByteOpCodes;
public static OpCode[] singleByteOpCodes; public static void LoadOpCodes()
{
singleByteOpCodes = new OpCode[0x100];
multiByteOpCodes = new OpCode[0x100];
FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
for (int num1 = 0; num1 < infoArray1.Length; num1++)
{
FieldInfo info1 = infoArray1[num1];
if (info1.FieldType == typeof(OpCode))
{
OpCode code1 = (OpCode)info1.GetValue(null);
ushort num2 = (ushort)code1.Value;
if (num2 < 0x100)
{
singleByteOpCodes[(int)num2] = code1;
}
else
{
if ((num2 & 0xff00) != 0xfe00)
{
throw new Exception("Invalid OpCode.");
}
multiByteOpCodes[num2 & 0xff] = code1;
}
}
}
}
构造MethodBodyReader实例后,我们可以利用它任意解析指令清单,获得其字符串表现形式。就是它,有趣的反编译。
netken
相关文章推荐
- [译] 解析方法体中的 IL 代码
- 从IL代码来探讨C#中的接口方法、虚方法与抽象方法
- 使用vs命令提示中查看IL代码方法(vs2005以上适用)
- LOG 解析方法(代码来自网络)
- C# 构造函数避免IL(反编译)代码膨胀的方法--C#编译有点狂啊
- 解析使用C++编写无错代码的方法技巧
- Google Test(GTest)使用方法和源码解析——私有属性代码测试技术分析
- 剑指 offer代码解析——面试题39判断平衡二叉树(高效方法)
- 获取数据和解析数据的方法尽量分开写,这样可以降低代码之间的耦合性
- Java8 default methods 默认方法的概念与代码解析
- 撑持链式方法生成/解析XML的Java类库 (依靠dom4j) - 代码共享
- 剑指 offer代码解析——面试题39判断平衡二叉树(高效方法)
- java代码中fastjson生成字符串和解析字符串的方法和javascript文件中字符串和json数组之间的转换方法
- xml解析四种方法的代码实现
- 笔记③:POJ Roadblocks 次短路问题代码解析(优先队列逆序排列两种方法)
- 【dubbo源码解读系列】之二 dubbo代码启动入口解析(自定义main方法)
- [札记]IL经典指令解析之方法调度
- Java线程中yield与join方法的区别代码解析
- 判断回文数的两种方法(代码解析)
- android 网络图片路径解析和本地图片路径解析的方法 ,base64加密报文解析成bitmap【代码示例】