通用的语言基础是.NET运行的基础,当我们对程序运行的结果有异议的时候,如何透过本质看表面,需要我们从底层来入手探索,这时候,IL便是我们必须知道的基础。
一、IL基础概念
1.1 什么是IL?
IL是.NET框架中间语言(Intermediate Language)的缩写。使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直接执行的机器代码,而是一种中间语言IL(Intermediate Language)的代码。
1.2 为何要了解IL?
元数据和IL是CLR的基础,了解必要的IL是深入认识CLR的捷径,我们没有理由放弃一条可以直接通达大门的便捷之路而盲目地以其他的方式追求深入。同时,大量的事例分析都是以IL来揭秘的,因此了解IL是读懂他人代码的必备基础,可以给自己更多的收获。
二、IL分析工具
2.1 ILASM.exe和ILDASM.exe
.NET Framework中自带了一套成熟的编译于反编译利器:ILASM.exe和ILDASM.exe,其中ILASM.exe工具用来执行IL代码并生成可执行程序,而ILDASM.exe则用来反编译可执行程序(反编译为IL代码进行查看)。
2.2 Reflector.exe
Reflector是由微软员工Lutz Roeder编写的免费程序。Reflector的出现使·NET程序员眼前豁然开朗,因为这个免费工具可以将·NET程序集中的IL反编译成C#或者Visual Basic代码。除了能将IL转换为C#或Visual Basic以外,Reflector还能够提供程序集中类及其成员的概要信息、提供查看程序集中IL的能力以及提供对第三方插件的支持。
三、一个Hello World的IL之旅
3.1 准备一个Hello World程序
using System; using System.Data; namespace HelloIL { public class Program { public static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
3.2 利用ILDASM体验IL
(1)对编译后的可执行文件HelloIL.exe,使用ILDasm.exe进行反编译,将会还原HelloIL为IL编码,结构如下:
分为两个部分:MANIFEST和HelloIL程序集。
(2)其中,MANIFEST是附加信息列表,主要包含了程序集的一些属性:程序集名称、版本号、哈希算法、程序集模块等,以及对外部引用程序集的引用项:
// Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly HelloIL { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .hash algorithm 0x00008004 .ver 1:0:0:0 } .module HelloIL.exe // MVID: {C176F7F5-9415-4616-A709-E08967A0C6A0} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x00000000003C0000
① .assembly指令用于定义编译目标或者加载外部库:这里只加载了mscorlib核心库,而System.Data被忽略,有效避免了过度加载引起的代码膨胀;
② .ctor指令表示构造函数,代码里没有任何显示构造函数,因此这里调用基类System.Object的构造函数(System.Object位于mscorlib程序集中);
(3)其次,HelloIL程序集是我们要分析的重点:
① 首先是Program类
② 然后是ctor方法(构造方法)
③ 最后是Main方法
(4)化繁为简,一览天下
这里将上面的IL代码简化一下,去粗取精来展现一下上面示例的IL代码,详细的分析以注释方式描述:
// 加载外部程序集 .assembly extern mscorlib // 指定编译目标程序集 .assembly HelloIL .class Program extends [mscorlib]System.Object { .method public instanct void .ctor() cil managed { .maxstack 8 // 调用基类构造函数 ldarg.0 call instance void [mscorlib]System.Object::.ctor() // 执行完毕,返回 ret } .method static void Main() cil managed { // 表明程序入口点 .entrypoint .maxstack 8 // 装载string对象 ldstr "Hello World!" // 调用静态方法WriteLine call void [mscorlib]System.Console::WriteLine(string) // 执行完毕,返回 ret } }
3.3 IL体验小结
通过一个Hello World示例,我们和IL进行了第一次的亲密接触。认识IL,是个循序渐进的过程,有了本次的小示例作为铺垫,我们可以轻松地认识简单的IL代码了。
参考资料
(1)本文源自王涛(anytao)的《你必须知道的.NET(第二版)》,感谢金馆长熊猫表情。
(2)Zery,《读懂IL就这么简单(一)》