Code Virtualizer虛擬機保護初探

Code Virtualizer簡介
Code Virtualizer是由Oreans開發的一款代碼虛擬機保護軟件,用於保護軟件不被逆向工程,同傳統的加密/壓縮殼不一樣,該虛擬機保護軟件並無對目標程序的代碼和數據進行壓縮和加密處理,而是將源程序的指令代碼進行混淆與亂序處理並轉換成語義等價的虛擬機僞指令,而後由虛擬機調度執行。因爲是對原有指令代碼的虛擬化處理,所以可能會下降原有代碼的執行效率,Code Virtualizer支持宏包裹的方式對原程序的部分代碼片斷進行保護處理,從而使得開發人員能夠只對部分關鍵的代碼片斷進行虛擬化處理,而不是必須對全部的代碼進行虛擬化。
虛擬機分析
虛擬機保護會把源程序的X86指令變成自定義的僞指令,等到執行的時候,他會模擬CPU「取指-譯碼-執行」這樣的步驟,Code Virtualizer內置在保護程序中的VM就會啓動,讀取僞指令,而後解析執行,執行完畢後,當前系統的寄存器/EFLAGS以及堆棧狀態同未虛擬化時的執行結果一致。
Code Virtualizer是一個堆棧虛擬機,它的一切操做都是基於堆棧傳遞的。有的虛擬機保護是本身建立的堆棧,而Code Virtualizer則直接使用的是當前程序自身的堆棧。在Code Virtualizer中,僞指令就是一個個的handler,VM中有一個核心的Dispatch部分,本質就是一個大的循環+SWITCH語句,它經過讀取程序的僞指令,而後在DispatchTable裏面定位到不一樣的handler中執行。絕大多數狀況下,在一個handler中執行完成後,程序將回到Dispatch起始,取得並解析下一條指令,而後到next handler中執行。
該虛擬機是基於語義的,對彙編指令轉化爲等價語義的代碼,而後虛擬執行。
假設虛擬機執行前的虛擬機環境爲:php

esp    init_esp
 ebp    init_ebp
 eax    init_eax
 ebx    init_ebx
 ecx    init_ecx
 edx    init_edx
 esi    init_esi
 edi    init_edi
 zf 0

... ...segmentfault

在該虛擬機執行的語句爲xor eax,eax則執行完後的虛擬機環境應該爲:數據結構

esp    init_esp
 ebp    init_ebp
 eax    0
 ebx    init_ebx
 ecx    init_ecx
 edx    init_edx
 esi    init_esi
 edi    init_edi
 zf 1

通過Code Virtualizer加密的X86指令,一條簡單的指令被分解成數條僞指令,它按照本身的僞指令排列去實現原指令的功能,再加上垃圾指令以及指令亂序,使得逆向工程人員將沒法看到源程序的指令。
測試代碼
咱們用匯編語言寫了一段簡單的代碼用來測試Code Virtualizer的處理狀況,由於代碼比較簡單,所以更能使咱們將分析集中在虛擬機自己。
.data
MsgBoxCaption db "Hello",0
MsgBoxText db "Hello Code Virtualizer!",0測試

.code
start:加密

VIRTUALIZER_START
mov eax, 0deadbeefh
VIRTUALIZER_END
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess,0
end start
上面代碼中加粗的部分是用Code Virtualizer SDK的宏來包裝要虛擬化的代碼,也就是隻有VIRTUALIZER_START和VIRTUALIZER_END包圍的代碼纔會被虛擬化。
虛擬機初始化
進入程序入口點後,程序首先將虛擬機僞指令地址壓入棧中,而後跳轉到VM_INIT部分去執行。
.v_lizer:00407548 000 push offset VM_CODE
.v_lizer:0040754D 004 jmp VM_INIT
進入VM_INIT後, 就是先把EFLAGS和通用寄存器壓入堆棧,而後對當前代碼進行重定位處理,這是由於若是Code Virtualizer須要對DLL/SYS進行處理的話,須要將自身的Dispatcher Handler進行重定位才行。
.v_lizer:004034EC 004 pusha
.v_lizer:004034ED 024 pushf
.v_lizer:004034EE 028 cld
.v_lizer:004034EF 028 call $+5
.v_lizer:004034F4
DELTA:
.v_lizer:004034F4 02C pop edi
.v_lizer:004034F5 028 sub edi, offset DELTA
.v_lizer:004034FB 028 mov eax, edi
.v_lizer:004034FD 028 add edi, offset VM_CONTEXT
.v_lizer:00403503 028 cmp eax, [edi+VM_CONTEXT.delta_offset]
...
.v_lizer:0040350A 028 mov [edi+VM_CONTEXT.delta_offset], eax
上面代碼中的VM_CONTEXT是Code Virtualizer的核心數據結構,用於保存虛擬機的執行環境與Dispatch Handler表,對於其中的重要字段咱們在下面的分析中會進行解析。接着就對VM_CONTEXT中的調度表中的Handler進行重定位處理:
.v_lizer:0040350A mov [edi+_VM_CONTEXT.delta_offset], eax
.v_lizer:0040350D mov ecx, 0A8h ; Handler數量
.v_lizer:00403512 jmp short loc_403521
.v_lizer:00403514 jmp short loc_40351C
.v_lizer:00403516 add [edi+ecx*4+58h], eax
.v_lizer:0040351A jmp short loc_403520
.v_lizer:0040351C add [edi+ecx*4+48h], eax
.v_lizer:00403520 dec ecx
.v_lizer:00403521 or ecx, ecx
.v_lizer:00403523 jnz short loc_403514
VM_INIT初始化執行完畢後,對應的幾個關鍵寄存器說明以下:
ESI 指向僞指令的起始,每次執行Handler,ESI根據指令長度和指令的內部處理不斷增長。
EDI 指向VM_CONTEXT結構。
EAX就是從虛擬機指令碼未解碼以前的數據,經過運算獲得Handler Index。
EBX是參與指令解碼的,和虛擬機指令進行運算獲得對應的Handler Index,值得注意的是某些Handler內部也會改變EBX的值。
EDX是一個臨時寄存器,在虛擬機的堆棧操做中,常用EDX來做爲臨時變量存放數據。
指令解碼
完成後基本的環境初始化後,就開始對虛擬機僞指令進行解碼,解碼的代碼以下:
.v_lizer:00403556 lodsb
===========> 取出一字節(取出僞指令)
.v_lizer:00403557 add al, 0E5h
.v_lizer:00403559 push ebx
.v_lizer:0040355A mov bl, 0E4h
.v_lizer:0040355C inc bl
.v_lizer:0040355E sub bl, 0FBh
.v_lizer:00403561 sub bl, 8Eh
.v_lizer:00403564 sub al, bl
.v_lizer:00403566 pop ebx
.v_lizer:00403567 sub al, bl
.v_lizer:00403569 add al, 5Ch
.v_lizer:0040356B sub al, 0E5h
===========>等價於sub al, bl
.v_lizer:00403B22 push ebx
.v_lizer:004053B3 mov bl, 33h
.v_lizer:004053B5 push ebx
.v_lizer:004053B6 mov bh, 0AEh
.v_lizer:004053B8 shl bh, 7
.v_lizer:004053BB sub bh, 0ABh
.v_lizer:004053BE add bh, 0A7h
.v_lizer:004053C1 sub al, bh
.v_lizer:004053C3 pop ebx
.v_lizer:004053C4 sub al, bl
.v_lizer:004053C6 add al, 0FCh
.v_lizer:004053C8 pop ebx
===========>等價於sub al, 33h
.v_lizer:004053C9 push ebx
.v_lizer:004053CA mov bl, 0E9h
.v_lizer:004053CC push dx
.v_lizer:004053CE mov dl, 1
.v_lizer:00404E63 add bl, dl
.v_lizer:00404E65 pop dx
.v_lizer:00405588 and bl, 97h
.v_lizer:0040558B or bl, 7
.v_lizer:004056AE xor bl, 0D1h
.v_lizer:004056B1 sub al, bl
.v_lizer:004056B3 pop ebx
===========>等價於sub al,56h
.v_lizer:004056B4 sub bl, 0ADh
.v_lizer:004056B7 add bl, alspa

.v_lizer:004056B9 push 75ECh
.v_lizer:004056BE mov [esp], edx
==========>等價於push edx調試

.v_lizer:00403AC3 mov dl, 0EBh
.v_lizer:00403AC5 dec dl
.v_lizer:00403AC7 neg dl
.v_lizer:00403AC9 shl dl, 4
.v_lizer:0040672F add dl, 4Dh
.v_lizer:00406732 add bl, dl
==========>等價於add bl,0ADhcode

.v_lizer:00403D2D pop edx
.v_lizer:00403D2E movzx eax, al
.v_lizer:00403D31 jmp dword ptr [edi+eax*4]
==========>這裏就跳轉到對應的Handler去執行索引

上面的代碼對代碼進行的混淆處理,但實際要作的事情很簡單,就是把VM CODE轉換成對應的Dispatch Handler的索引,而後跳轉到對應的指令處理例程去執行相應的邏輯。
分析上述代碼後得知,索引的轉換規則以下:
Handler Index = al - bl - 0x33 - 0x56
bl += al
所以,能夠看到Code Virtualizer的虛擬機僞指令是通過簡單的變換處理,目的是防止很容易的就分析出虛擬機僞指令的功能,所以咱們能夠經過編寫調試器腳本將原始指令(索引值)轉換爲明文的僞指令。須要注意的是,與某些虛擬機不一樣,Code Virtualizer的虛擬機的指令並不是等長,所以咱們在處理的時候須要知道當前指令的全長是多少,從而跳過operand部分。若是Handler中修改了bl的值,那麼也應該注意。
代碼混淆與亂序
在Code Virtualizer中大量使用了垃圾指令和代碼亂序來達到干擾逆向工程人員分析的目的。
什麼是代碼亂序?
代碼亂序是將一系列的代碼序列分散打亂分佈在PE映像中,中間穿插跳轉指令以及不改變環境的垃圾代碼,從而擾亂正常分析流程。通常的來說,鏈接指令以無條件的跳轉jmp、變形的短跳轉call、對稱的條件跳轉指令([jz、jnz];[jc、jnc]…)等實現,前提是不改變其餘環境以避免破壞代碼正常功能。
Code Virtualizer的代碼亂序是經過將指令序列分散打亂分佈,而後使用大量的絕對跳轉指令JMP來實現代碼片斷的鏈接。如圖1所示:
代碼亂序
而對於Code Virtualizer中的代碼混淆,主要就是把簡單的運算膨脹成複雜的、但有些計算是相互抵消的指令,增長大量的冗餘代碼。 打個簡單的比方,就是把原本的1+2=3改寫成11+11(0.5+0.5)+2(1/2) = 3這樣的形式。所以咱們在分析的時候,就須要特別注意代碼最後哪些關鍵寄存器的值發生了改變,以及堆棧的變化,圍繞這兩點進行分析就不容易被混淆的垃圾代碼所迷惑。
好比某個Handler的部分代碼以下所示:
.v_lizer:00403999 000 sub esi, 15F6572Eh *
.v_lizer:0040583F 000 sub esi, 28315364h *
.v_lizer:00405E16 000 add esi, eax
.v_lizer:00405E18 000 add esi, 28315364h *
.v_lizer:00405E1E 000 push ecx *
.v_lizer:00405E1F 004 mov ecx, 15F6572Eh *
.v_lizer:004046C9 004 add esi, ecx *
.v_lizer:004046CB 004 pop ecx *內存

通過分析能夠得知,上面一大段的代碼僅僅等價於add esi, eax,其中加*的代碼都是冗餘的垃圾指令。
Dispatch Handler分析
根據對VM_CONTEXT的分析,Code Virtualizer一共包含了160多個Dispatch Handler。所以咱們若是要完整的分析Code Virtualizer,那麼就須要對這160多個Dispatch Handler進行分析。本文嘗試分析當前程序使用到的Handler,對這些Handler有了必定了解後,分析剩下的Handler就基本是體力勞動了。
對於咱們用來測試的例子來講,虛擬化後的僞指令如圖2:
虛擬機僞指令

因爲作了簡單的數值轉換,所以即便是相同的僞指令在這個指令代碼片斷中也顯示的是不一樣的值。而後咱們用調試器一步一步的跟蹤,分析出相關的僞指令的含義。
固然咱們只是分析了一小部分,所有的僞指令一共有150多個,經過分析少數相關的虛擬機僞指令,咱們就能夠窺豹一斑,大體瞭解虛擬機僞指令的工做過程。下面以其中一條指令爲例:
Handler Index = 0x2D

.v_lizer:00403608 lodsd
========>讀取四字節
.v_lizer:00403609 add eax, 2317011Eh
.v_lizer:0040360E sub eax, 367E736Ch
.v_lizer:00403613 sub eax, ebx
.v_lizer:00403615 add eax, 367E736Ch
.v_lizer:0040361A push esi
.v_lizer:0040361B mov esi, 2317011Eh
.v_lizer:00403620 sub eax, esi
.v_lizer:00403622 pop esi
========>等價於sub eax, ebx
.v_lizer:00403623 push esi
.v_lizer:00403624 mov esi, 1C9811AFh
.v_lizer:00403629 add eax, 1A36C00h
.v_lizer:004062FD sub eax, esi
.v_lizer:004062FF sub eax, 1A36C00h
.v_lizer:00406304 mov esi, [esp]
.v_lizer:00406307 add esp, 4
========>等價於sub eax, 1C9811AFh
.v_lizer:0040630D push 0Ah
.v_lizer:00406312 mov [esp], ebx
.v_lizer:00406315 mov ebx, 21307C1Eh
.v_lizer:0040631A add ebx, 321175AFh
.v_lizer:00406320 xor ebx, 3769313Ah
.v_lizer:00406326 sub eax, 0C57480Eh
.v_lizer:00404562 add eax, ebx
.v_lizer:00404564 add eax, 0C57480Eh
.v_lizer:00404569 pop ebx
========>等價於add eax, 6428C0F7h
.v_lizer:0040456A xor ebx, eax
.v_lizer:0040456C push 64C5h
.v_lizer:00404571 mov [esp], eax
========>等價於
xor ebx, eax
push eax
綜上分析,得出該段代碼實際功能爲
lodsd
sub eax, ebx
sub eax, 1c9811afh
add eax, 6428c0f7h
xor ebx, eax
push eax
在本文測試的樣本文件中,lodsd讀取的內容是0xB8AFC4D7,ebx爲0x40741B,通過上述計算後變爲4,實際上也就是把指令字後面的4字節作以下變換後壓入堆棧:
real IMM32= IMM32 - ebx - 1C9811AFh + 6428C0F7h
代碼片斷的功能至關於PUSH IMM32。
示例代碼手工還原
push OFFSET vm.offset0x1C
pop edx
pop [edx]
push OFFSET vm.offset0x0
pop edx
pop [edx]
push OFFSET vm.offset0x18
pop edx
pop [edx]
push OFFSET vm.offset0x10
pop edx
pop [edx]
push OFFSET vm.offset0x0C
pop edx
pop [edx]
push OFFSET vm.offset0x0C
pop edx
pop [edx]
push OFFSET vm.offset0x04
pop edx
pop [edx]
push OFFSET vm.offset0x08
pop edx
pop [edx]
push OFFSET vm.offset0x14
pop edx
pop [edx]

由於在進入虛擬機調度以前,程序調用瞭如下指令
pushad
pushfd

所以執行後棧的內容以下
EDI
ESI
EBP
ESP
EBX
EDX
ECX
EAX
EFLAGS

咱們能夠知道
push OFFSET vm.field
pop edx
pop [edx]
這三句代碼實際上等價於
Lea edx, OFFSET [vm.field]
Pop [edx]
也就是把棧中的DWORD彈出到edx指向的內存中。所以咱們能夠推測出vm中的相關的未知字段是什麼了。

struct VM_CONTEXT
{
DWORD vm_edi; +0
DWORD vm_edx; +4
DWORD vm_ecx; +8
DWORD vm_ebx; +0c
DWORD vm_ebp; +10
DWORD vm_eax; +14
DWORD vm_esi; +18
DWORD vm_eflag; +1c
DWORD VM_off_20;
DWORD VM_off_24;
DWORD VM_off_28;
DWORD delta_offset;
DWORD VM_lock;
DWORD VM_off_34;
DWORD VM_off_38;
DWORD VM_off_3c;
DWORD VM_off_40;
DWORD VM_off_44;
DWORD VM_off_48;
DWORD VM_dispatch_xxx1;
DWORD VM_dispatch_xxx2;
DWORD VM_dispatch_xxx3;
...
}
通過對不一樣的樣本進行不一樣的虛擬化處理,能夠看到寄存器對應關係並非固定的,但同同樣本的寄存器位置是固定的。根據上面分析的Handler對應的語義,咱們手工將僞指令轉換成對應的X86代碼:
mov edx, esp
push edx
push 4
pop eax
add dword ptr [esp], 4
mov esp, dword ptr [esp]
push 0xDEADBEEF
把咱們賦值的數據壓入堆棧
push offset vm.eax
將VM的EAX寄存器也壓入堆棧
pop edx
push edx
push edx
push 4
pop eax
add dword ptr [esp], 4
pop edx
push 0x44a6
push 0xdfe8
pop edx
pop edx
pop [edx]
push 0x40100f
add dword ptr [esp], vm.delta_offset
其中0x40100f就是虛擬機執行完後要繼續執行的原始代碼的位置,加上vm.delta_offset是爲了重定位。看到上面一大堆代碼,實際上也是Code Virtualizer加入的混淆指令,經分析精簡後實際上完成的功能就是
mov eax, 0xDEADBEEF
push 0x40100F
add dword ptr [esp], vm.delta_offset
繼續分析其他的代碼,
lodsd
add esi, eax
mov ebx, 0
push offset vm.eflags
pop edx
push dword ptr [edx]

push offset vm.eax
pop edx
push dword ptr [edx]

push offset vm.ecx
pop edx
push dword ptr [edx]

push offset vm.edx
pop edx
push dword ptr [edx]

push offset vm.ebx
pop edx
push dword ptr [edx]

push offset vm.ebx
pop edx
push dword ptr [edx]

push offset vm.ebp
pop edx
push dword ptr [edx]

push offset vm.esi
pop edx
push dword ptr [edx]

push offset vm.edi
pop edx
push dword ptr [edx]
這一大段就是把原來PUSHAD與PUSHFD指令保存的標誌位和寄存器數據再原樣壓入堆棧。
恢復好環境後,就進入了尾聲,跳轉到VM_EXIT代碼段執行,VM_EXIT也包含了一些混淆的代碼,但其最關鍵的也就是最後三條代碼:
.v_lizer:004042D2 popa
.v_lizer:004042D3 popf
.v_lizer:004042D4 retn
所以執行完上面三條語句就退出了虛擬機保護。虛擬機執行完後程序的寄存器環境以下圖所示:

可見EAX被設爲了0xDEADBEEF,並且堆棧也恢復了平衡。通過上面的分析咱們能夠知道,Code Virtualizer並非直接對要保護的X86指令作VM處理,而是先對加入必要的保存和恢復環境的代碼,而後對原始的代碼進行混淆,最後纔會這部分代碼作VM處理。
參考文獻
Code Virtualizer 1.3.8.0版虛擬機分析 nEINEI
Ryosuke的分析
http://bbs.pediy.com/showthread.php?t=62447&highlight=Virtualizer

相關文章
相關標籤/搜索