IL是微軟平臺(tái)上的一門(mén)中間語(yǔ)言帘睦,我們常寫(xiě)的C#代碼在編譯器中都會(huì)自動(dòng)轉(zhuǎn)換成IL竣付,然后在由即時(shí)編譯器(JIT Compiler)轉(zhuǎn)化機(jī)器碼古胆,最后被CPU執(zhí)行逸绎。ildasm.exe反編譯工具將IL匯編成可跨平臺(tái)可執(zhí)行的(pe)文件桶良≡煞可供我們了解別人代碼和修改。有了他我們看待問(wèn)題可以不用停留在編輯器層面疲牵,可深入中間層承二。
- 第一步 安裝
首先安裝完VS2017之后,在我們的系統(tǒng)中找到我們的ildasm.exe程序的地址纲爸,一般在系統(tǒng)中的地址為:
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\ildasm.exe
- 第二步 添加
在VS2017頂部操作欄目的工具里面亥鸠,我們找到外部工具點(diǎn)擊打開(kāi)
我們?cè)诿畹牟糠州斎胛覀冋f(shuō)的ildasm.exe的地址,其他按照我們圖中所示的填寫(xiě)识啦,點(diǎn)擊確定负蚊,保存為自定義外部工具颓哮。
-
第三步 使用
圖片.png
我們返回外面的工具中家妆,可以看到出現(xiàn)了我們剛才添加的外部工具。
我們點(diǎn)擊查看IL
我們已經(jīng)可以查看自己程序的IL了
-
第四步 讀懂
我們嘗試讀懂IL冕茅,當(dāng)我們遇到一些在編輯層無(wú)法看透的問(wèn)題時(shí)候伤极,我們可以嘗試通過(guò)IL里面來(lái)查看程序的問(wèn)題。下面我提供一份IL類(lèi)型圖:
圖片.png
如下面這段IL
MANIFEST:是一個(gè)附加信息列表姨伤,主要包含程序集的一些屬性哨坪,如程序集名稱(chēng)、版本號(hào)乍楚、哈希算法等当编;
Democode:項(xiàng)目名稱(chēng)
Democodeing.Common:命名空間
Democodeing.ICar:接口
Democodeing.Program:類(lèi),主要查看存類(lèi)下面的內(nèi)容炊豪。
如這段代碼中
.class private auto ansi beforefieldinit DemoCoding.Program
extends [mscorlib]System.Object
{
} // end of class DemoCoding.Program
1).class凌箕,表示Program是一個(gè)類(lèi)。并且它繼承自程序集—mscorlib的System.Object類(lèi)词渤;
2)private牵舱,表示訪(fǎng)問(wèn)權(quán)限;
3)auto缺虐,表示程序的內(nèi)存加載全部由CLR來(lái)控制芜壁;
4)ansi,是為了在沒(méi)有托管代碼與托管代碼之間實(shí)現(xiàn)無(wú)縫轉(zhuǎn)換高氮。這里主要指C慧妄、C++代碼等;
5)beforefieldinit剪芍,是用來(lái)標(biāo)記運(yùn)行庫(kù)(CLR)可以在靜態(tài)字段方法生成后的任意時(shí)刻塞淹,來(lái)加載構(gòu)造器(構(gòu)造函數(shù));
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代碼大小 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
1)cil managed:表示其中為IL代碼,指示編譯器編譯為托管代碼罪裹;
2).maxstack:表示調(diào)用構(gòu)造函數(shù).otor期間的評(píng)估堆棧(Evaluation Stack) ;
3) IL_0000:標(biāo)記代碼行開(kāi)頭饱普;
4)ldarg.0:表示轉(zhuǎn)載第一個(gè)成員參數(shù)运挫,在實(shí)例方法中指的是當(dāng)前實(shí)例的引用;
5)call:call一般用于調(diào)用靜態(tài)方法套耕,因?yàn)殪o態(tài)方法是在編譯期就確定的谁帕。而這里的構(gòu)造函數(shù).otor()也是在編譯期就制定的。而另一指令callvirt則表示調(diào)用實(shí)例方法冯袍, 它是在運(yùn)行時(shí)確定的匈挖,因?yàn)槿缜笆觯?dāng)調(diào)用方法的繼承關(guān)系時(shí)康愤,就要比較基類(lèi)與派生類(lèi)的同名函數(shù)的實(shí)現(xiàn)方法(virtual和new)儡循,以確定調(diào)用的函數(shù)所屬的Method Table;
6)ret:表示執(zhí)行完畢翘瓮,返回贮折;
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代碼大小 19 (0x13)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello World"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: call string [mscorlib]System.Console::ReadLine()
IL_0011: pop
IL_0012: ret
} // end of method Program::Main
1) hidebysig:表示當(dāng)把此類(lèi)作為基類(lèi),存在派生類(lèi)時(shí)资盅,此方法不被繼承调榄,同上構(gòu)造函數(shù);
2).entrypoint:指令表示CLR加載程序時(shí)呵扛,是首先從.entrypoint開(kāi)始的每庆,即從Main方法作為程序的入口函數(shù);
3)nop:為空該指令今穿,主要給外部設(shè)備或者指令間隙準(zhǔn)備時(shí)間缤灵;
4)ldstr:創(chuàng)建String對(duì)象變量"Hello World." ;
5)pop:取出棧頂?shù)闹道渡埂.?dāng)我們不需要把值存入變量時(shí)使用腮出;
- 第五步 修改
我們先寫(xiě)一個(gè)最簡(jiǎn)單的
這一段比較簡(jiǎn)單也比較容易理解,編譯之后IL為
我們利用工具雙擊打開(kāi)Main方法
我們修改代碼
文件-->轉(zhuǎn)儲(chǔ)芝薇。確定后選擇另存路徑,會(huì)生成二個(gè)文件:*.il 和 *.res
打開(kāi).il文件
找到我們剛才寫(xiě)的輸出hello word的代碼塊
修改輸出
- 第六步 編譯
我們先打開(kāi)VS 2017的開(kāi)發(fā)人員命令提示符
然后輸入
ilasm /exe /output=C:\Demo.exe /Resource=D:\5\1.res D:\5\1.il
我們看到完成信息之后
在C盤(pán)找到了我們編譯的EXE文件胚嘲,執(zhí)行
-
第七步 從IL理解裝箱拆箱
我們先寫(xiě)一段最簡(jiǎn)單的裝箱拆箱代碼如下
圖片.png
用我之前教你的方法查看程序的IL
打開(kāi)我們的main方法
我們看到IL里面表示裝箱拆箱兩個(gè)命令是用box和unbox,非常好理解吧
END