虛擬機檢測技術剖析

做者:riusksk (泉哥)php

主頁:http://riusksk.blogbus.com編程

 

前言windows

在當今信息安全領域,特別是惡意軟件分析中,常常須要利用到虛擬機技術,以提升病毒分析過程的安全性以及硬件資源的節約性,所以它在惡意軟件領域中是應用愈來愈來普遍。這裏咱們所謂的虛擬機(Virtual Machine)是指經過軟件模擬的具備完整硬件系統功能的、運行在一個徹底隔離環境中的完整計算機系統。經過虛擬機軟件(好比VMware,Virtual PC ,VirtualBox),你能夠在一臺物理計算機上模擬出一臺或多臺虛擬的計算機,這些虛擬機徹底就像真正的計算機那樣進行工做,例如你能夠安裝操做系統、安裝應用程序、訪問網絡資源等等。攻擊者爲了提升惡意程序的隱蔽性以及破壞真實主機的成功率,他們都在惡意程序中加入檢測虛擬機的代碼,以判斷程序所處的運行環境。當發現程序處於虛擬機(特別是蜜罐系統)中時,它就會改變操做行爲或者中斷執行,以此提升反病毒人員分析惡意軟件行爲的難度。本文主要針對基於Intel CPU的虛擬環境VMware中的Windows XP SP3系統進行檢測分析,並列舉出當前常見的幾種虛擬機檢測方法。api

 

方法一:經過執行特權指令來檢測虛擬機數組

Vmware爲真主機與虛擬機之間提供了相互溝通的通信機制,它使用「IN」指令來讀取特定端口的數據以進行兩機通信,但因爲IN指令屬於特權指令,在處於保護模式下的真機上執行此指令時,除非權限容許,不然將會觸發類型爲「EXCEPTION_PRIV_INSTRUCTION」的異常,而在虛擬機中並不會發生異常,在指定功能號0A(獲取VMware版本)的狀況下,它會在EBX中返回其版本號「VMXH」;而當功能號爲0x14時,可用於獲取VMware內存大小,當大於0時則說明處於虛擬機中。VMDetect正是利用前一種方法來檢測VMware的存在,其檢測代碼分析以下:安全

bool IsInsideVMWare()
{
  bool rc = true;
 
  __try
  {
    __asm
    {
      push   edx
      push   ecx
      push   ebx
 
      mov    eax, 'VMXh'
      mov    ebx, 0  // ebx設置爲非幻數’VMXH’的其它值
      mov    ecx, 10 // 指定功能號,用於獲取VMWare版本,當它爲0x14時用於獲取VMware內存大小
      mov    edx, 'VX' // 端口號
      in     eax, dx // 從端口dx讀取VMware版本到eax
//若上面指定功能號爲0x14時,可經過判斷eax中的值是否大於0,如果則說明處於虛擬機中
      cmp    ebx, 'VMXh' // 判斷ebx中是否包含VMware版本’VMXh’,如果則在虛擬機中
      setz   [rc] // 設置返回值
 
      pop    ebx
      pop    ecx
      pop    edx
    }
  }
  __except(EXCEPTION_EXECUTE_HANDLER)  //若是未處於VMware中,則觸發此異常
  {
    rc = false;
  }
 
  return rc;
}

測試結果:網絡

                 圖1ide

如圖1所示,VMDetect成功檢測出VMWare的存在。函數

 

方法二:利用IDT基址檢測虛擬機工具

利用IDT基址檢測虛擬機的方法是一種通用方式,對VMware和Virtual PC均適用。中斷描述符表IDT(Interrupt Descriptor Table)用於查找處理中斷時所用的軟件函數,它是一個由256項組成的數據,其中每一中斷對應一項函數。爲了讀取IDT基址,咱們須要經過SIDT指令來讀取IDTR(中斷描述符表寄存器,用於IDT在內存中的基址),SIDT指令是以以下格式來存儲IDTR的內容:

typedef struct

{

    WORD IDTLimit;         // IDT的大小

    WORD LowIDTbase;     // IDT的低位地址

    WORD HiIDTbase; // IDT的高位地址

} IDTINFO;

因爲只存在一個IDTR,但又存在兩個操做系統,即虛擬機系統和真主機系統。爲了防止發生衝突,VMM(虛擬機監控器)必須更改虛擬機中的IDT地址,利用真主機與虛擬機環境中執行sidt指令的差別便可用於檢測虛擬機是否存在。著名的「紅丸」(redpill)正是利用此原理來檢測VMware的。Redpill做者在VMware上發現虛擬機系統上的IDT地址一般位於0xFFXXXXXX,而Virtual PC一般位於0xE8XXXXXX,而在真實主機上正如圖2所示都位於0x80xxxxxx。Redpill僅僅是經過判斷執行SIDT指令後返回的第一字節是否大於0xD0,如果則說明它處於虛擬機,不然處於真實主機中。Redpill的源碼甚是精簡,源碼分析以下:

#include <stdio.h>
int main () {
 
  unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; //至關於SIDT[adrr],其中addr用於保存IDT地址
  *((unsigned*)&rpill[3]) = (unsigned)m;    //sidt[addr]中的addr設爲m的地址
  ((void(*)())&rpill)();  //執行SIDT指令,並將讀取後IDT地址保存在數組m
 
  printf ("idt base: %#x\n", *((unsigned*)&m[2]));   //因爲前2字節爲IDT大小,所以從m[2]開始即爲IDT地址
  if (m[5]>0xd0) printf ("Inside Matrix!\n", m[5]); //IDT基址大於0xd0xxxxxx時則說明程序處於VMware
  else printf ("Not in Matrix.\n");
  return 0;
}
測試結果如圖2所示:
                                     圖2
利用此IDT檢測的方法存在一個缺陷,因爲IDT的值只針對處於正在運行的處理器而言,在單CPU中它是個常量,但當它處於多CPU時就可能會受到影響了,由於每一個CPU都有其本身的IDT,這樣問題就天然而然的產生了。針對此問題,Offensive Computing組織成員提出了兩種應對方法,其中一種方法就是利用Redpill反覆地在系統上循環執行任務,以此構造出一張當前系統的IDT值變化統計圖,但這會增長CPU負擔;另外一種方法就是windows API函數SetThreadAffinityMask()將線程限制在單處理器上執行,當執行此測試時只能準確地將線程執行環境限制在本地處理器,而對於將線程限制在VM處理器上就可能行不通了,由於VM是計劃在各處理器上運行的,VM線程在不一樣的處理器上執行時,IDT值將會發生變化,所以此方法也是不多被使用的。爲此,有人提出了使用LDT的檢測方法,它在具備多個CPU的環境下檢測虛擬機明顯優於IDT檢測方法,該方法具體內容參見下節內容。
 

方法三:利用LDT和GDT的檢測方法

在 《Intel® 64 and IA-32  Architecture Software Developer’s Manual Volume 3A: System Programming Guide》第二章的Vol.3 2-5 一頁(個人Intel開發手冊是2008版的)中對於LDT和GDT的描述以下(如下內容爲我的翻譯):
在保護模式下,全部的內存訪問都要經過全局描述符表(GDT)或者本地描述符表(LDT)才能進行。這些表包含有段描述符的調用入口。各個段描述符都包含有各段的基址,訪問權限,類型和使用信息,並且每一個段描述符都擁有一個與之相匹配的段選擇子,各個段選擇子都爲軟件程序提供一個GDT或LDT索引(與之相關聯的段描述符偏移量),一個全局/本地標誌(決定段選擇子是指向GDT仍是LDT),以及訪問權限信息。
若想訪問段中的某一字節,必須同時提供一個段選擇子和一個偏移量。段選擇子爲段提供可訪問的段描述符地址(在GDT 或者LDT 中)。經過段描述符,處理器從中獲取段在線性地址空間裏的基址,而偏移量用於肯定字節地址相對基址的位置。假定處理器在當前權限級別(CPL)可訪問這個段,那麼經過這種機制就能夠訪問在GDT 或LDT 中的各類有效代碼、數據或者堆棧段,這裏的CPL是指當前可執行代碼段的保護級別。
……
GDT的線性基址被保存在GDT寄存器(GDTR)中,而LDT的線性基址被保存在LDT寄存器(LDTR)中。
 

因爲虛擬機與真實主機中的GDT和LDT並不能相同,這與使用IDT的檢測方法同樣,所以虛擬機必須爲它們提供一個「複製體」。關於GDT和LDT的基址可經過SGDT和SLDT指令獲取。虛擬機檢測工具Scoopy suite的做者Tobias Klein經測試發現,當LDT基址位於0x0000(只有兩字節)時爲真實主機,不然爲虛擬機,而當GDT基址位於0xFFXXXXXX時說明處於虛擬機中,不然爲真實主機。具體實現代碼以下:

#include <stdio.h>
 
void LDTDetect(void)
{
    unsigned short ldt_addr = 0;
    unsigned char ldtr[2];
 
    _asm sldt ldtr
    ldt_addr = *((unsigned short *)&ldtr);
    printf("LDT BaseAddr: 0x%x\n", ldt_addr);
 
    if(ldt_addr == 0x0000)
    {
        printf("Native OS\n");
    }
    else
        printf("Inside VMware\n");
}
 
void GDTDetect(void)
{
    unsigned int gdt_addr = 0;
    unsigned char gdtr[4];
 
    _asm sgdt gdtr
    gdt_addr = *((unsigned int *)&gdtr[2]);
    printf("GDT BaseAddr:0x%x\n", gdt_addr);
 
    if((gdt_addr >> 24) == 0xff)
    {
        printf("Inside VMware\n");
    }
    else
        printf("Native OS\n");
}
 
int main(void)
{
    LDTDetect();
    GDTDetect();
    return 0;
}

 

 

測試結果如圖3所示:

                   圖3

 

方法四:基於STR的檢測方法

在保護模式下運行的全部程序在切換任務時,對於當前任務中指向TSS的段選擇器將會被存儲在任務寄存器中,TSS中包含有當前任務的可執行環境狀態,包括通用寄存器狀態,段寄存器狀態,標誌寄存器狀態,EIP寄存器狀態等等,當此項任務再次被執行時,處理器就會其原先保存的任務狀態。每項任務均有其本身的TSS,而咱們能夠經過STR指令來獲取指向當前任務中TSS的段選擇器。這裏STR(Store task register)指令是用於將任務寄存器 (TR) 中的段選擇器存儲到目標操做數,目標操做數能夠是通用寄存器或內存位置,使用此指令存儲的段選擇器指向當前正在運行的任務的任務狀態段 (TSS)。在虛擬機和真實主機之中,經過STR讀取的地址是不一樣的,當地址等於0x0040xxxx時,說明處於虛擬機中,不然爲真實主機。實現代碼以下:

#include <stdio.h>
int main(void)
{
    unsigned char mem[4] = {0};
    int i;
 
    __asm str mem;
    printf (" STR base: 0x");
    for (i=0; i<4; i++)
    {
        printf("%02x",mem[i]);
    }
 
    if ( (mem[0]==0x00) && (mem[1]==0x40))
        printf("\n INSIDE MATRIX!!\n");
    else
        printf("\n Native OS!!\n");
    return 0;
}

測試結果如圖4所示:

                          圖4

 

方法五:基於註冊表檢測虛擬機

在windows虛擬機中經常安裝有VMware Tools以及其它的虛擬硬件(如網絡適配器、虛擬打印機,USB集線器……),它們都會建立任何程序均可以讀取的windows註冊表項,所以咱們能夠經過檢測註冊表中的一些關鍵字符來判斷程序是否處於虛擬機之中。關於這些註冊表的位置咱們能夠經過在註冊表中搜索關鍵詞「vmware」來獲取,下面是我在VMware下的WinXP中找到的一些註冊表項:

 

項名:HKEY_CLASSES_ROOT\Applications\VMwareHostOpen.exe

項名:HKEY_CLASSES_ROOT\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\ProductName

鍵值「VMware Tools

項名:HKEY_CLASSES_ROOT\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\SourceList\PackageName

鍵值:VMware Tools.msi

項名:HKEY_CURRENT_USER\Printers\DeviceOld

鍵值:_#VMwareVirtualPrinter,winspool,TPVM:

項名:HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier

鍵值:VMware Virtual IDE Hard Drive

項名:HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 1\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier

鍵值:NECVMWar VMware IDE CDR10

項名:HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\ProductName

鍵值:VMware Tools

項名:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\C2A6F2EFE6910124C940B2B12CF170FE\InstallProperties\DisplayName

鍵值:VMware Tools

項名:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Reinstall\0002\DeviceDesc

鍵值:VMware SVGA II

項名:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards\2\Description

鍵值:VMware Accelerated AMD PCNet Adapter

項名:HKEY_LOCAL_MACHINE\SOFTWARE\VMware, Inc.\VMware Tools

項名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000\DriverDesc

鍵值:VMware SVGA II

項名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E968-E325-11CE-BFC1-

08002BE10318}\0000\ProviderName

鍵值:VMware, Inc.

項名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0001\DriverDesc

鍵值:VMware Accelerated AMD PCNet Adapter

項名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E97B-E325-11CE-BFC1-08002BE10318}\0000\DriverDesc

鍵值:VMware SCSI Controller

項名:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Print\Monitors\ThinPrint Print Port Monitor for VMWare

 

除以上這些表項以外,還有不少地方能夠檢測,特別是虛擬機提供的虛擬化軟硬件、服務之類,好比文件共享服務,VMware 物理磁盤助手服務,VMware Ethernet Adapter Driver,VMware SCSI Controller等等的這些信息均可做爲檢測虛擬機的手段。這裏咱們就以其中某表項爲例編程舉例一下,其它表項檢測方法同理,具體代碼以下:

.386
.model flat, stdcall
option casemap:none
 
   include  windows.inc
   include  user32.inc
   include  kernel32.inc
   include  advapi32.inc
 
   includelib  user32.lib
   includelib  kernel32.lib
   includelib  advapi32.lib
 
.data
szCaption     db "VMware Detector ",0
szInside         db "Inside VMware!",0
szOutside              db "Native OS!",0
szSubKey      db "software\VMWare, Inc.\VMware tools",0
hKey              dd    ?
 
.code
start:
  invoke RegOpenKeyEx, HKEY_LOCAL_MACHINE, addr szSubKey, 0,\
                 KEY_WRITE or KEY_READ, addr hKey
  .if eax == ERROR_SUCCESS
  invoke MessageBox, NULL,addr szInside, addr szCaption, MB_OK
  .else
  invoke MessageBox, NULL,addr szOutside, addr szCaption, MB_OK
  .endif
  invoke RegCloseKey,hKey
  invoke ExitProcess,NULL
end start

 

測試結果如圖5所示:

            圖5

 

方法六:基於時間差的檢測方式

本方法經過運行一段特定代碼,而後比較這段代碼在虛擬機和真實主機之中的相對運行時間,以此來判斷是否處於虛擬機之中。這段代碼咱們能夠經過RDTSC指令來實現,RDTSC指令是用於將計算機啓動以來的CPU運行週期數存放到EDX:EAX裏面,其中EDX是高位,而EAX是低位。下面咱們以xchg    ecx,  eax 一句指令的運行時間爲例,這段指令在個人真實主機windows 7系統上的運行時間爲0000001E,如圖6所示:

              圖6

而該指令在虛擬機WinXP下的運行時間爲00000442,如圖7所示:

               圖7

二者之間的運行時間明顯差異不少,在虛擬機中的運行速度遠不如真實主機的,通常狀況下,當它的運行時間大於0xFF時,就能夠肯定它處於虛擬機之中了,所以不難寫出檢測程序,具體實現代碼以下:

.586p
.model flat, stdcall
option casemap:none
 
include                   windows.inc
include                   kernel32.inc
include                   user32.inc
 
includelib                kernel32.lib
includelib                user32.lib
 
                          
.data
szTitle                   db       "VMDetect With RDTSC", 0h
szInsideVM                db       "Inside VMware!", 0h
szOutsideVM               db       "Native OS!", 0h
 
.code
 
start:
         RDTSC
         xchg              ecx, eax
         RDTSC    
         sub              eax, ecx
         cmp              eax, 0FFh
         jg               Detected
         
         invoke   MessageBox, 0, offset szOutsideVM, offset szTitle, 0
         ret
         
Detected:
         invoke   MessageBox, 0, offset szInsideVM, offset szTitle, 0
         ret
end start

測試結果如圖8所示:

              圖8

 

方法七:利用虛擬硬件指紋檢測虛擬機

利用虛擬硬件指紋也可用於檢測虛擬機的存在,好比VMware默認的網卡MAC地址前綴爲「00-05-69,00-0C-29或者00-50-56」,這前3節是由VMware分配的惟一標識符OUI,以供它的虛擬化適配器使用。在個人VMWare WinXP下的MAC地址爲00-0C-29-5B-D7-67,如圖9所示:

                                 圖9

但因爲這些可通過修改配置文件來繞過檢測。另外,還可經過檢測特定的硬件控制器,BIOS,USB控制器,顯卡,網卡等特徵字符串進行檢測,這些在前面使用註冊表檢測方法中已有所涉及。另外以前在看雪論壇上也有朋友提到經過檢測硬盤Model Number是否含有「vmware」或「virtual」等字樣來實現檢測虛擬機的功能,網址見這(附源碼):http://bbs.pediy.com/showthread.php?t=110046。

 

總結

國外SANS安全組織的研究人員總結出當前各類虛擬機檢測手段不外乎如下四類:

●   搜索虛擬環境中的進程,文件系統,註冊表;

●   搜索虛擬環境中的內存

●   搜索虛擬環境中的特定虛擬硬件

●   搜索虛擬環境中的特定處理器指令和功能

由於現代計算系統大可能是由文件系統,內存,處理器及各類硬件組件構成的,上面提到的四種檢測手段均包含了這些因素。縱觀前面各類檢測方法,也均在此四類當中。除此以外,也有人提出經過網絡來檢測虛擬機,好比搜索ICMP和TCP數據通信的時間差別,IP ID數據包差別以及數據包中的異常頭信息等等。隨着技術研究的深刻,相信會有更多的檢測手段出現,與此同時,虛擬機廠商也會不斷進化它們的產品,以增長anti-vmware的難度,這不也正是一場永無休止的無煙戰爭!

相關文章
相關標籤/搜索