dotNet MSIL中的一些不常見IL指令
在做HVM僞代碼覆寫率檢測時,在構造的一些dotNet程式樣本中總是缺少一些MSIL指令
,沒有覆寫所有的MSIL指令。
如C#中的裝箱和取消裝箱的IL指令,可能很多人和我最初的想法一樣,裝箱是 box指令
,取消裝箱是unbox指令,實際上這隻對了一半。
裝箱使用的box指令沒有錯誤,但是取消裝箱不是用的unbox指令,而是 unbox.any 指
令,在C#編譯的程式中就從來沒有發現過 unbox 指令。
另外還有一批沒有發現的指令有 calli , cpobj, ldobj, stobj, refanyval,
mkrefany,arglist, refanytype, initblk, cpblk
initobj指令也比較少見,不過在C#的泛型中遇到了:
如 T obj = default(T); 編譯後就會出現 initobj 指令。
這個指令就是給變量賦初值。
calli 在C#裡面沒有,在C++/CLI構造出來了,實際上它是調用函數指針的用法。
refanyval, mkrefany,arglist, refanytype 看了一篇介紹 undocumented IL 的文章
後構造出來了,具體可參考轉的文章。
其它的指令構造不出來就隻能蠻幹了,把exe用ilasm整成的 il 檔案,然後直接修改il
,再編譯測試,經過反複實驗,構造出了可以正常運作的程式。
先看裝箱和取消裝箱,
int i = 123;
object obj = i;
i = (int)obj;
對應的il代碼是:
第一句:
ldc.i4.s 123
stloc.0
第二句:
ldloc.0
box [mscorlib]System.Int32
stloc.1
第三句:
ldloc.1
unbox.any [mscorlib]System.Int32
首先我就是直接把
改為
unbox [mscorlib]System.Int32
然後測試,發現這是 i 的值不等于 123了,而是一個不确定的數字。看IL文檔得知
unbox生成的隻是一個指針,也就是位址。
這時想到了另外一個指令 ldobj ,這個指令直接從位址讀取資料放到堆棧上。
如是把
ldobj [mscorlib]System.Int32
再測試,結果正确了。看起來 unbox + ldobj 的效果和 unbox.any一樣。
stobj 指令,從名稱就能看出它和 ldobj隻對應的。将立即數存入位址中。
功能類似如c++中的 *p = value;
i = 3;
正常方式應該是
ldc.i4.s 3
stloc.s.0
改為另一種模式
ldloca.s 0 //載入 i 的位址
ldc.i4.s 3 //載入立即數
stobj [mscorlib]System.Int32 //将立即數存入位址
cpobj 指令,直接從位址複制對象類似c++中的 *p1 = *p2;
int i, j;
j = i;
正常方式是
stloc.s 0
ldloc.s 0
stloc.s 1
改為使用cpobj的模式
ldloca.s 1 //dest
ldloca.s 0 //src
cpobj [mscorlib]System.Int32 //copy
initblk 類似如C中的 memset 函數
cpblk 類似如C中的 memcpy函數
DNGuard HVM核心是通過将IL代碼替換為HVM僞指令然後送出給Jit去編譯的,是以需要
檢測所有的HVM僞指令是否都能夠被正确編譯執行。測試遇到沒有覆寫的指令隻好用蠻
幹的方法構造樣本了。
目前以及完成了 .net framework 2.0, .net framework 3.0環境下的指令覆寫檢測,
所有HVM僞指令都編譯通過了。
接下來将對 .net framework 1.1 和 .net framework 3.5的環境進行指令覆寫檢測。