二、查找漏洞指令
虛擬機(jī)監(jiān)視器監(jiān)視虛擬機(jī)的運(yùn)行,它運(yùn)行在宿主操作系統(tǒng),并為客戶機(jī)操作系統(tǒng)提供一個完整的虛擬平臺。與此同時,虛擬機(jī)監(jiān)視器也存在一些可以被惡意代碼探測到虛擬化的安全缺陷。
在內(nèi)核模式下,VMware使用二進(jìn)制翻譯技術(shù)進(jìn)行指令的模擬。運(yùn)行于內(nèi)核態(tài)的某些特權(quán)指令被解釋和模擬,所以它們不在物理處理器上運(yùn)行。
相反,在用戶模式下,代碼直接在處理器上運(yùn)行,幾乎所有與硬件交互的指令,要么是特權(quán)指令,要么會產(chǎn)生內(nèi)核態(tài)陷阱指令或中斷指令。
VMware截獲所有中斷并處理它們,以便虛擬機(jī)仍然認(rèn)為這是一個正常機(jī)器。然而在x86體系結(jié)構(gòu)中,一些指令在獲取硬件相關(guān)的信息時并不產(chǎn)生異常,如sidt、sgdt、sldt、cpuid等等。為了正確虛擬這些指令,VMware需要在所有指令上進(jìn)行二進(jìn)制翻譯,因此造成巨大的性能損失。
為了避免執(zhí)行全指令模擬造成的巨大性能損失,VMware允許一些特定指令在沒有正確虛擬化的前提下運(yùn)行。最終,這意味著某些指令序列在VMware虛擬機(jī)而不是在物理機(jī)中運(yùn)行時返回不同的結(jié)果。處理器使用某些關(guān)鍵的結(jié)構(gòu)與表,它們會被加載與真實系統(tǒng)不同的偏移量,而這正是未進(jìn)行全虛擬化的副作用。
中斷描述表(IDT)是CPU內(nèi)部的一個數(shù)據(jù)結(jié)構(gòu),操作系統(tǒng)使用它來確保正確響應(yīng)中斷和異常。在x86體系結(jié)構(gòu)下,所有的內(nèi)存獲取,或是通過全局描述表(GDT)獲得,或是通過本地描述表(LDT)獲得。這些表中包含段描述符,它們提供每一個段的詳細(xì)存取信息,其中包含段基地址類型、長度,以及存取權(quán)限等等。
IDT、GDT和LDT是CPU內(nèi)部的寄存器,它們分別存放著各自表的基地址和大小。有三條敏感指令(sidt、sgdt和sldt)可以讀取這些表的位置,并且將相應(yīng)的寄存器存入內(nèi)存地址。因為這些指令可以隨時被用戶態(tài)代碼調(diào)用,且不會產(chǎn)生陷阱,也未被VMware正確虛擬化,所以這些異常都可能被用來探測VMware的存在。
1.使用Red Pill反虛擬機(jī)技術(shù)
Red Pill通過運(yùn)行sidt指令獲取IDTR寄存器的值。虛擬機(jī)監(jiān)視器必須重新定位Guest系統(tǒng)的IDTR,來避免與Host系統(tǒng)的IDTR沖突。因為在虛擬機(jī)中運(yùn)行sidt指令時,虛擬機(jī)監(jiān)視器不會得到通知,所以會返回虛擬機(jī)的IDTR。
Red Pill通過測試這種差異來探測Vmware的使用。這種方法存在一個缺陷,由于IDT的值只針對處于正在運(yùn)行的處理器而言,在單CPU中它是個常量,但當(dāng)它處于多CPU時就可能會受到影響了,因為每個CPU都有其自己的IDT,這樣問題就自然而然的產(chǎn)生了。
針對此問題,Offensive Computing組織成員提出了兩種應(yīng)對方法:
其中一種方法就是利用Red Pill反復(fù)地在系統(tǒng)上循環(huán)執(zhí)行任務(wù),以此構(gòu)造出一張當(dāng)前系統(tǒng)的IDT值變化統(tǒng)計圖,但這會增加CPU負(fù)擔(dān);
另一種方法就是windows API函數(shù)SetThreadAffinityMask()將線程限制在單處理器上執(zhí)行,當(dāng)執(zhí)行此測試時只能準(zhǔn)確地將線程執(zhí)行環(huán)境限制在本地處理器,而對于將線程限制在VM處理器上就可能行不通了,因為VM是計劃在各處理器上運(yùn)行的,VM線程在不同的處理器上執(zhí)行時,IDT值將會發(fā)生變化,因此此方法也很少被使用。
2.使用No Pill反虛擬機(jī)技術(shù)
sgdt和sldt指令探測VMware的技術(shù)通常被稱為No Pill。
BOOL CheckVMWare()
{
ULONG xdt = 0 ;
ULONG InVM = 0;
__asm
{
push edx
sidt [esp-2]
pop edx
nop
mov xdt , edx
}
if (xdt > 0xd0000000)
{
InVM = 1;
}
else
{
InVM = 0;
}
__asm
{
push edx
sgdt [esp-2]
pop edx
nop
mov xdt , edx
}
if (xdt > 0xd0000000)
{
InVM += 1;
}
if (InVM == 0)
{
return FALSE;
}
else
{
return TRUE;
}
}
通過禁用VMware加速可以防止No Pill技術(shù)的探測。
3.查詢I/O端口
VMware使用虛擬化的I/O端口完成宿主系統(tǒng)與虛擬機(jī)之間的通信,以便支持諸如復(fù)制和粘貼功能。這個端口可以被查詢,然后與一個magic數(shù)比較,以確定VMware的使用。
這種技術(shù)成功的關(guān)鍵在于x86體系結(jié)構(gòu)中的in指令,它從一個源操作數(shù)指定的端口復(fù)制數(shù)據(jù)到目的操作數(shù)指定的內(nèi)存地址。VMware會監(jiān)視in指令的執(zhí)行,并捕獲目的通信端口為0x5668(VX)的I/O。
VMware會檢查第二個操作數(shù)是否是VX,在這種情況發(fā)生時,EAX寄存器載入的值是0x564D5868(VMXh),ECX寄存器必須被載入你希望在端口上執(zhí)行相應(yīng)操作的值,值0xA表示 get VMware version type,0x14代表 get the memory size。
它們都可以被用來探測VMware,但0xA更受歡迎,因為它能確定VMware的版本。如代碼所示setz指令在magic數(shù)與VMXh匹配時設(shè)置返回值rc為1,如果在真實的機(jī)器上運(yùn)行會觸發(fā)EXCEPTION_EXECUTE_HANDLER 異常,在異常處理中設(shè)置返回值rc為0。
BOOL CheckVMWare()
{
bool rc = true;
__try
{
__asm
{
push edx
push ecx
push ebx
mov eax, 'VMXh'
mov ebx, 0
mov ecx, 10
mov edx, 'VX'
in eax, dx
cmp ebx, 'VMXh'
setz [rc]
pop ebx
pop ecx
pop edx
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
rc = false;
}
return rc;
}
對付這種反虛擬化技術(shù)的最簡單方法是使用NOP指令替換in指令,或修補(bǔ)條件跳轉(zhuǎn),使得它不論比較結(jié)果如何,都執(zhí)行到未探測到虛擬機(jī)的程序分支。
4.使用str指令
在保護(hù)模式下運(yùn)行的所有程序在切換任務(wù)時,對于當(dāng)前任務(wù)中指向TSS的段選擇器將會被存儲在任務(wù)寄存器中,TSS中包含有當(dāng)前任務(wù)的可執(zhí)行環(huán)境狀態(tài),包括通用寄存器狀態(tài)、段寄存器狀態(tài)、標(biāo)志寄存器狀態(tài)、EIP寄存器狀態(tài)等等,當(dāng)此項任務(wù)再次被執(zhí)行時,處理器就會其原先保存的任務(wù)狀態(tài)。
每項任務(wù)均有其自己的TSS,而我們可以通過STR指令來獲取指向當(dāng)前任務(wù)中TSS的段選擇器。這里STR指令是用于將任務(wù)寄存器(TR)中的段選擇器存儲到目標(biāo)操作數(shù),目標(biāo)操作數(shù)可以是通用寄存器或內(nèi)存位置,使用此指令存儲的段選擇器指向當(dāng)前正在運(yùn)行的任務(wù)的任務(wù)狀態(tài)段(TSS)。
在虛擬機(jī)和真實主機(jī)之中,通過STR讀取的地址是不同的,當(dāng)?shù)刂返扔?x0040xxxx時,說明處于虛擬機(jī)中,否則為真實主機(jī)。
BOOL CheckVMWare()
{
unsigned char mem[4] = {0};
__asm str mem;
if ((mem[0] == 0x00) && (mem[1] == 0x40))
{
return TRUE;
}
else
{
return FALSE;
}
}
在IDA PRO中,可以使用下面的腳本查找我們前面提到的指令。
from idautils import *
from idc import *
heads = Heads(SegStart(ScreenEA()), SegEnd(ScreenEA()))
antiVM = []
for i in heads:
if (GetMnem(i) == "sidt" or GetMnem(i) == "sgdt" or GetMnem(i) == "sldt" or GetMnem(i) == "smsw" or GetMnem(i) == "str" or GetMnem(i) == "in" or GetMnem(i) == "cpuid"):
antiVM.append(i)
print "Number of potential Anti-VM instructions: %d" % (len(antiVM))
for i in antiVM:
SetColor(i, CIC_ITEM, 0x0000ff)
Message("Anti-VM: %08x\\n" % i)
要在IDA PRO中運(yùn)行腳本,選擇File->Script File,可以看到下面的輸出。
這個輸出表明腳本檢測到了三條漏洞指令類型。滾動到IDA PRO的反匯編窗口,我們看到三條紅色高亮顯示的指令sidt、str和sldt。
5.使用無效的操作碼
每臺機(jī)器都有一組定義的指令,通常稱為指令集架構(gòu)(Instruction Set Architecture)。
當(dāng)遇到無效指令(不存在于ISA中)時,機(jī)器引發(fā)無效操作碼異常。軟件可以處理異常(使用通常的try/catch機(jī)制),也可以讓操作系統(tǒng)處理異常,或者在最壞的情況下崩潰機(jī)器。
VirtualPC使用一堆無效指令來允許虛擬機(jī)和VirtualPC之間連接。當(dāng)VirtualPC的虛擬機(jī)想要與VirtualPC通信時,程序設(shè)置異常處理程序(try/catch塊),在調(diào)用VM軟件之前設(shè)置所需的參數(shù),發(fā)出特殊的無效操作碼指令。
VM軟件將識別此無效操作碼并相應(yīng)地操作,如果VirtualPC存在則不引起異常,并且如果VirtualPC不存在則產(chǎn)生異常。
最后,程序的catch塊將處理異常并檢查返回的VM軟件的參數(shù)。總之,VirtualPC使用無效的操作碼機(jī)制作為后門。
DWORD IslnsideVPC_exceptionFilter(LPEXCEPTION_POINTERS ep)
{
PCONTEXT ctx=ep->ContextRecord;
ctx->Ebx = -1; //未運(yùn)行在VPC中
ctx->Eip += 4; //跳過”call VPC”操作
return EXCEPTION_CONTINUE_EXECUTION;
}
BOOL CheckVirtualPC()
{
bool rc = TRUE;
__try
{
__asm
{
push ebx
mov ebx, 0
mov eax, 1
__emit 0fh
__emit 3fh
__emit 07h
__emit 0bh
test ebx, ebx
setz[rc]
pop ebx
}
}
__except(IslnsideVPC_exceptionFilter(GetExceptionInformation()))
{
rc = FALSE;
}
return rc;
}
-
Mac
+關(guān)注
關(guān)注
0文章
1083瀏覽量
51140 -
惡意代碼
+關(guān)注
關(guān)注
0文章
10瀏覽量
7622 -
虛擬機(jī)
+關(guān)注
關(guān)注
1文章
888瀏覽量
27813
發(fā)布評論請先 登錄
相關(guān)推薦
評論