解析方法体中的 IL 代码
来源:互联网 发布:戴尔笔记本连不上网络 编辑:程序博客网 时间:2024/06/18 15:20
![](http://www.cnblogs.com/CuteSoft_Client/CuteEditor/images/emsmile.gif)
介绍
.NET 的 System.Reflection
命名空间提供了完整的反射机制,用于查看程序集的结构。可以通过它获取程序集中的所有类型定义,字段,属性,基本上能满足你所有的要求[包括方法定义,译者注]。尽管如此,某些内容仍是看不见的,方法体中的代码就是如此。
当你仔细查看代码的时候,可能想知道方法体内使用过的变量和其生命周期以及产生了什么结果。Microsoft 忽视了这个需求,但还是提供了一些东西:IL代码。但这是不够的,实际上是一个字节数组,对于未经专业训练的普通程序员来讲没有任何意义。
我们需要的是一系列的,表现为IL编码形式的实际的操作指令,这正我下面要讲到的。
背景
任何程序员使用过反射,就一定听说过由Lutz Roeder写的让人生畏的 Reflector 。Reflector 能将任何.NET 程序集反编译成与源代码几乎一致的代码。
你一定对我说的“几乎一致”有疑问,因为反射机制不能为你提供最初的源代码。编译器首先移除了所有代码注释,甚至包括从不使用的变量,并且只有有效的和必须的代码被编译到程序中。因而我们不能获得最精确的源代码。
好了,Reflector 是非常不错。但是我们希望在自己的代码中也能获得如 Reflector 这样的效果,我们如何能做到?
首先使用一个典型的例子“Hello world”,由此看看我们希望的结果和利用 .Net Framework 得到的实际的结果:
C#代码:
public void SayHello()
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
Console.Out.WriteLine("Hello world");
}
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
当我们采用反射方法获取到 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 : nop0001 : call System.IO.TextWriter System.Console::get_Out()0006 : ldstr "Hello world"0011 : callvirt instance System.Void System.IO.TextWriter::WriteLine()0016 : nop0017 : ret
使用代码
SDILReader 是一个仅仅包含了三个类的动态库文件。为了获得方法体的MSIL代码,需要创建一个MethodBodyReader对象的实例,由它构造你想分解的对象的
MethodInfo
实例。
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
如何工作?
很好,这是一个不错的问题。为了开始工作[不知道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)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
ILInstruction instruction = new ILInstruction();
// get the operation code of the current instruction
OpCode code = OpCodes.Nop;
ushort value = il[position++];
if (value != 0xfe)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
code = Globals.singleByteOpCodes[(int)value];
}
else
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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个的操作符)。 操作符在启动应用程序时被载入到两个静态列表中:
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
构造MethodBodyReader实例后,我们可以利用它任意解析指令清单,获得其字符串表现形式。就是它,有趣的反编译。
netken
- 解析方法体中的 IL 代码
- IL代码底层运行机制
- IL代码底层运行机制
- IL代码底层运行机制
- IL代码底层运行机制
- 使用il查看代码
- 看懂IL代码
- 了解IL代码
- 读懂IL代码
- IL代码的分析
- 详解.NET IL代码
- 详解.NET IL代码
- 详解.NET IL代码
- IL语言(一)-HelloWorld解析
- IL代码底层运行机制之
- 详解.NET IL代码(补充)
- 查看C#的IL代码
- C# 构造函数避免IL(反编译)代码膨胀的方法--C#编译有点狂啊
- HTML内容排版标记
- Analysis包中的源码详解
- HTML文件标记
- 在asp.net中使用xml文件的两种类型及用法
- HTML标记一览
- 解析方法体中的 IL 代码
- 如何拓扑发现路由器,交换机和打印机
- 几个 C++ 的数据结构类(下载)
- library linking on LINUX
- .Net“破解”新招——如何使用不带私钥的程序集?
- 如有DXperience源代碼,如何編譯
- Effective C++之15
- web3.0
- 用js控制表格的隐藏与显示