1. 程式人生 > >ARM平臺指令虛擬化初探

ARM平臺指令虛擬化初探

0x00:什麼是程式碼虛擬化?

  虛擬機器保護是這幾年比較流行的軟體保護技術。這個詞源於俄羅斯的著名軟體保護軟體“VmProtect”,以此為開端引起了軟體保護殼領域的革命,各大軟體保護殼都將虛擬機器保護這一新穎的技術加入到自己的產品中。
程式碼虛擬化是將程式程式碼編譯為虛擬機器指令即虛擬程式碼(自己定義的程式碼集),通過虛擬CPU解釋並執行的一種方式,大致流程如下:

我們拋開ARM平臺CPU流水線機制不談,簡單來說,其實CPU就是遵循一個簡單的模式:迴圈讀取、解碼、執行這個過程。

0x01:為什麼要指令虛擬化

  首先我們來回顧下軟體保護殼的發展,大致可分為三個階段。
第一階段:當殼完成解密目的碼時,它將不會再次控制程式,被保護程式的明文將在記憶體中展開。在此之前,殼可以呼叫一切系統手段來防治黑客的除錯與逆向。
第二階段:可以實現分段式的加解密,殼執行完畢後,並不會消失而仍然會在程式執行到某個點時再次啟動。
第三階段:其實最簡單的解釋是,將被保護的指令使用一套自定義的位元組碼(邏輯上等價)來替換掉程式中原有的指令,而位元組碼在執行的時候又由程式中的直譯器來解釋執行,自定義的位元組碼只有自己的直譯器才能識別,也是因為這一點,基於虛擬機器的保護相對其他保護而言要更加難分析。

0x02:一個簡單的虛擬機器實現

瞭解過程式碼虛擬化的原理之後,就是自定義一套位元組碼,然後使用一個直譯器解釋執行位元組碼。所以,我們要實現定義位元組碼與實現直譯器。
位元組碼只是一個標識,可以隨意定義,以下是自定義的位元組碼,只定義了幾個常用的指令,其中每條指令標識都對應於一個位元組碼。

在定義好指令對應的位元組碼之後,就須要一個直譯器來解釋定義的指令位元組碼了。其實這裡的直譯器與物理機CPU很相似。在物理機中的程式執行需要處理器、暫存器、棧、堆等環境才可以執行起來,所以需要虛擬暫存器,棧、堆等,以下是處理器。

有了上面結構之後,就可以來動手寫直譯器了。直譯器的工作其實就是判斷當前解釋的位元組碼是否可以解析,如果可以就把相應引數傳遞給相應的處理函式,讓處理函式來解釋執行這一條指令。以下是直譯器程式碼。

直譯器解釋執行過程:
首先可以從上面看到直譯器vm_CPU執行時pc會指向Vcode,也就是自定義的位元組碼第一個位元組0xa0(對應指令為MOV),之後會判斷pc指向的位元組碼是否為ret指令,ret指令是0xa3,如果pc指向的不是ret,進入exec_Handle函式進行位元組碼解釋。

0xa0就對應著mov指令,所以當直譯器遇到0xa0就會呼叫vm_mov函式來解釋mov指令。

在vm_mov函式中首先把PC + 1處的一個位元組和PC + 2處2個位元組分別儲存在dest和src中,dest是暫存器標識,在後面的switch中判斷dest是哪個暫存器,在這個例子中dest是0x10,也就是r1暫存器,在case 0x10分支中就把*src賦值給r1。前4個位元組就是第一條mov指令,對應著mov r1, xxxx,xxxx就是這4個位元組中的後2個。

上面是一個直譯器在解釋執行位元組碼時的過程,其實很簡單,就是通過一個位元組碼和解釋函式的關係來呼叫相應的函式(Handle),或者通過一個很長的switch來判斷每個位元組碼,並呼叫相應函式(Handle) 。而解釋函式則通過執行相應的操作來模擬出一個指令。最後,把這些指令串聯在一起就可以執行完一個完整的邏輯。

下面是一個簡單的CrackMe完整的Vcode

下面是一個簡單的CrackMe完整的原始碼

0x03:測試與總結

以android平臺上測試如下:

總結:
其實這只是最簡單的實現,僅僅是為了學習和理解,如果想實現一個基於虛擬機器的保護殼還是有些複雜,比如:隨機VCode與Handle的關係對映、Handle混淆與亂序、程式碼變形、重定位等。

bin:

連結: https://pan.baidu.com/s/1nvbmcSp 密碼: wduy