惡意代碼編寫者常用反虛擬機技術逃避分析,這種技術能夠檢測本身是否運行在虛擬機中。若是惡意代碼探測到本身在虛擬機中運行,它會執行與其自己行爲不一樣的行爲,其中最簡單的行爲是中止自身運行。ios
近年來,隨着虛擬化技術的使用不斷增長,採用反虛擬機技術的惡意代碼數量逐漸降低。惡意代碼編寫者已經開始意識到,目標主機是虛擬機,也並不意味着它就沒有攻擊價值。git
隨着虛擬化技術的不斷髮展和普通應用,反虛擬機技術可能變得更加少見。這裏研究最多見的反虛擬機技術(包括VMware、virtualbox和virtualpc,重點是最經常使用的VMware),而且介紹一些如何防護它們的辦法。github
1.根據MAC地址windows
一般,MAC地址的前三個字節標識一個提供商。以00:05:6九、00:0c:29和00:50:56開始的MAC地址與VMware相對應;以00:03:ff開始的MAC地址與virtualpc對應;以08:00:27開始的MAC地址與virtualbox對應。數組
BOOL CheckVMWare()安全
{網絡
string mac;數據結構
get_3part_mac(mac);架構
if (mac=="00-05-69" || mac=="00-0c-29" || mac=="00-50-56")app
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualPC()
{
string mac;
get_3part_mac(mac);
if (mac=="00-03-ff")
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualBox()
{
string mac;
get_3part_mac(mac);
if (mac=="08-00-27")
{
return TRUE;
}
else
{
return FALSE;
}
}
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff[30];
} ASTAT, *PASTAT;
void get_3part_mac(string &mac)
{
NCB Ncb;
ASTAT Adapter;
UCHAR uRetCode;
LANA_ENUM lenum;
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBENUM;
Ncb.ncb_buffer = (UCHAR *)&lenum;
Ncb.ncb_length = sizeof(lenum);
uRetCode = Netbios(&Ncb);
for (int i = 0; i < lenum.length; i++)
{
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBRESET;
Ncb.ncb_lana_num = lenum.lana[i];
uRetCode = Netbios(&Ncb);
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBASTAT;
Ncb.ncb_lana_num = lenum.lana[i];
strcpy((char *)Ncb.ncb_callname, "*");
Ncb.ncb_buffer = (unsigned char *)&Adapter;
Ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios(&Ncb);
if (uRetCode == 0)
{
char tmp[128];
sprintf(tmp, "%02x-%02x-%02x",
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2]
);
mac = tmp;
}
}
}
2. 基於主板序列號、主機型號、系統盤所在磁盤名稱等其餘硬件信息
//經過WMI獲取主機信息 BOOL ManageWMIInfo(string &result, string table, wstring wcol)
{
HRESULT hres;
char bord[1024];
//初始化COM hres = CoInitialize(0);
//得到WMI鏈接COM接口 IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< "Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return false;
}
//經過鏈接接口鏈接WMI的內核對象名ROOT//CIMV2 IWbemServices *pSvc = NULL;
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\CIMV2"), // Object path of WMI namespace NULL, // User name. NULL = current user NULL, // User password. NULL = current 0, // Locale. NULL indicates current NULL, // Security flags. 0, // Authority (e.g. Kerberos) 0, // Context object &pSvc // pointer to IWbemServices proxy );
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return false;
}
//設置請求代理的安全級別 hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx NULL, // client identity EOAC_NONE // proxy capabilities );
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return false;
}
//經過請求代理來向WMI發送請求 IEnumWbemClassObject* pEnumerator = NULL;
string select = "SELECT * FROM "+ table;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t(select.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
cout << "Query for Network Adapter Configuration failed."
<< " Error code = 0x」"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return false;
}
//循環枚舉全部的結果對象 ULONG uReturn = 0;
IWbemClassObject *pclsObj;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
VARIANT vtProp;
VariantInit(&vtProp);
hr = pclsObj->Get(wcol.c_str(), 0, &vtProp, 0, 0);
if(!FAILED(hr))
{
CW2A tmpstr1(vtProp.bstrVal);
strcpy_s(bord,200,tmpstr1);
result = bord;
}
VariantClear(&vtProp);
}
//釋放資源 pSvc->Release();
pLoc->Release();
pEnumerator->Release();
pclsObj->Release();
CoUninitialize();
return true;
}
BOOL CheckVMWare()
{
string table = "Win32_BaseBoard";
wstring wcol = L"SerialNumber";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret == "None")
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVMWare()
{
string table = "Win32_DiskDrive";
wstring wcol = L"Caption";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("VMware") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVMWare()
{
string table = "Win32_computersystem";
wstring wcol = L"Model";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("VMware") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualBox()
{
string table = "Win32_computersystem";
wstring wcol = L"Model";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("VirtualBox") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualBox()
{
string table = "Win32_DiskDrive";
wstring wcol = L"Caption";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("VBOX") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualPC()
{
string table = "Win32_DiskDrive";
wstring wcol = L"Caption";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("Virtual HD") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualPC()
{
string table = "Win32_computersystem";
wstring wcol = L"Model";
string ret;
ManageWMIInfo(ret, table, wcol);
if (ret.find("Virtual Machine") != string::npos)
{
return TRUE;
}
else
{
return FALSE;
}
}
3.根據當前進程信息
經過進程快照讀取當前進程信息,查找是否存在虛擬機中特有的進程,如VMware中的vmware.exe和VirtualBox中的VBoxService.exe。
BOOL CheckVMWare()
{
DWORD ret = 0;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bMore = Process32First(hProcessSnap, &pe32);
while(bMore)
{
if (strcmp(pe32.szExeFile, "vmware.exe")==0)
{
return TRUE;
}
bMore = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return FALSE;
}
BOOL CheckVirtualBox()
{
DWORD ret = 0;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bMore = Process32First(hProcessSnap, &pe32);
while(bMore)
{
if (strcmp(pe32.szExeFile, "VBoxService.exe")==0)
{
return TRUE;
}
bMore = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return FALSE;
}
4.根據特定的文件夾或文件信息
經過查找磁盤中是否存在特定的文件夾或文件,判斷當前是否在虛擬機中。VMware虛擬機中一般會有路徑C:Program
FilesVMwareVMware Tools;VirtualBox 虛擬機中一般會有路徑 C:Program
FilesOracleVirtualBox Guest Additions。
BOOL CheckVMware()
{
if (PathIsDirectory("C:\Program Files\VMware\VMware Tools\") == 0)
{
return FALSE;
}
else
{
return TRUE;
}
}
BOOL CheckVirtualBox()
{
if (PathIsDirectory("C:\Program Files\Oracle\VirtualBox Guest Additions\") == 0)
{
return FALSE;
}
else
{
return TRUE;
}
}
5.根據特定註冊表信息
經過讀取主機具備虛擬機特性的註冊表位置來判斷是否處於虛擬機環境中。針對VMware能夠判斷註冊表項HKEY_CLASSES_ROOT\Applications\VMwareHostOpen.exe;針對VirtualBox能夠判斷註冊表項HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\VirtualBox
Guest Additions。固然,註冊表中能被檢測出的位置不少,這裏只是舉個例子。
BOOL CheckVMWare()
{
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT, "\Applications\VMwareHostOpen.exe", &hkey) == ERROR_SUCCESS)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CheckVirtualBox()
{
HKEY hkey;
if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Oracle\VirtualBox Guest Additions", &hkey) == ERROR_SUCCESS)
{
return TRUE;
}
else
{
return FALSE;
}
}
6.根據特定服務名
經過獲取主機當前具備VMware特性的服務信息,判斷當前主機是否爲虛擬機。在VMware中一般會存在VMware物理磁盤助手服務和VMware Tools服務等;在VirtualBox中一般會存在VirtualBox Guest Additions Service服務等。
BOOL CheckVMWare()
{
int menu = 0;
//打開系統服務控制器 SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if(SCMan == NULL)
{
cout << GetLastError() << endl;
printf("OpenSCManager Eorror/n");
return -1;
}
//保存系統服務的結構 LPENUM_SERVICE_STATUSA service_status;
DWORD cbBytesNeeded = NULL;
DWORD ServicesReturned = NULL;
DWORD ResumeHandle = NULL;
service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
//獲取系統服務的簡單信息 bool ESS = EnumServicesStatusA(SCMan, //系統服務句柄 SERVICE_WIN32, //服務的類型 SERVICE_STATE_ALL, //服務的狀態 (LPENUM_SERVICE_STATUSA)service_status, //輸出參數,系統服務的結構 1024 * 64, //結構的大小 &cbBytesNeeded, //輸出參數,接收返回所需的服務 &ServicesReturned, //輸出參數,接收返回服務的數量 &ResumeHandle); //輸入輸出參數,第一次調用必須爲0,返回爲0表明成功 if(ESS == NULL)
{
printf("EnumServicesStatus Eorror/n");
return -1;
}
for(int i = 0; i < ServicesReturned; i++)
{
if (strstr(service_status[i].lpDisplayName, "VMware Tools")!=NULL || strstr(service_status[i].lpDisplayName, "VMware 物理磁盤助手服務")!=NULL)
{
return TRUE;
}
}
//關閉服務管理器的句柄 CloseServiceHandle(SCMan);
return FALSE;
}
BOOL CheckVirtualPC()
{
int menu = 0;
//打開系統服務控制器 SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if(SCMan == NULL)
{
cout << GetLastError() << endl;
printf("OpenSCManager Eorror/n");
return -1;
}
//保存系統服務的結構 LPENUM_SERVICE_STATUSA service_status;
DWORD cbBytesNeeded = NULL;
DWORD ServicesReturned = NULL;
DWORD ResumeHandle = NULL;
service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
//獲取系統服務的簡單信息 bool ESS = EnumServicesStatusA(SCMan, //系統服務句柄 SERVICE_WIN32, //服務的類型 SERVICE_STATE_ALL, //服務的狀態 (LPENUM_SERVICE_STATUSA)service_status, //輸出參數,系統服務的結構 1024 * 64, //結構的大小 &cbBytesNeeded, //輸出參數,接收返回所需的服務 &ServicesReturned, //輸出參數,接收返回服務的數量 &ResumeHandle); //輸入輸出參數,第一次調用必須爲0,返回爲0表明成功 if(ESS == NULL)
{
printf("EnumServicesStatus Eorror/n");
return -1;
}
for(int i = 0; i < ServicesReturned; i++)
{
if (strstr(service_status[i].lpDisplayName, "Virtual Machine")!=NULL)
{
return TRUE;
}
}
//關閉服務管理器的句柄 CloseServiceHandle(SCMan);
return FALSE;
}
BOOL CheckVirtualBox()
{
int menu = 0;
//打開系統服務控制器 SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if(SCMan == NULL)
{
cout << GetLastError() << endl;
printf("OpenSCManager Eorror/n");
return -1;
}
//保存系統服務的結構 LPENUM_SERVICE_STATUSA service_status;
DWORD cbBytesNeeded = NULL;
DWORD ServicesReturned = NULL;
DWORD ResumeHandle = NULL;
service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
//獲取系統服務的簡單信息 bool ESS = EnumServicesStatusA(SCMan, //系統服務句柄 SERVICE_WIN32, //服務的類型 SERVICE_STATE_ALL, //服務的狀態 (LPENUM_SERVICE_STATUSA)service_status, //輸出參數,系統服務的結構 1024 * 64, //結構的大小 &cbBytesNeeded, //輸出參數,接收返回所需的服務 &ServicesReturned, //輸出參數,接收返回服務的數量 &ResumeHandle); //輸入輸出參數,第一次調用必須爲0,返回爲0表明成功 if(ESS == NULL)
{
printf("EnumServicesStatus Eorror/n");
return -1;
}
for(int i = 0; i < ServicesReturned; i++)
{
if (strstr(service_status[i].lpDisplayName, "VirtualBox Guest")!=NULL)
{
return TRUE;
}
}
//關閉服務管理器的句柄 CloseServiceHandle(SCMan);
return FALSE;
}
7.根據時間差
因爲在虛擬機中,代碼的運行速度一般不如真實主機。因此惡意代碼經過運行一段特定的代碼來比較這段代碼在虛擬機和真實主機之中的相對運行時間,以此來判斷是否處於虛擬機之中。
BOOL CheckVMWare()
{
__asm
{
rdtsc
xchg ebx,eax
rdtsc
sub eax,ebx
cmp eax,0xFF
jg detected
}
return FALSE;
detected:
return TRUE;
}
BOOL CheckVirtualPC()
{
__asm
{
rdtsc
xchg ebx,eax
rdtsc
sub eax,ebx
cmp eax,0xFF
jg detected
}
return FALSE;
detected:
return TRUE;
}
BOOL CheckVirtualBox()
{
__asm
{
rdtsc
xchg ebx,eax
rdtsc
sub eax,ebx
cmp eax,0xFF
jg detected
}
return FALSE;
detected:
return TRUE;
}
虛擬機監視器監視虛擬機的運行,它運行在宿主操做系統,併爲客戶機操做系統提供一個完整的虛擬平臺。與此同時,虛擬機監視器也存在一些能夠被惡意代碼探測到虛擬化的安全缺陷。
在內核模式下,VMware使用二進制翻譯技術進行指令的模擬。運行於內核態的某些特權指令被解釋和模擬,因此它們不在物理處理器上運行。
相反,在用戶模式下,代碼直接在處理器上運行,幾乎全部與硬件交互的指令,要麼是特權指令,要麼會產生內核態陷阱指令或中斷指令。
VMware截獲全部中斷並處理它們,以便虛擬機仍然認爲這是一個正常機器。然而在x86體系結構中,一些指令在獲取硬件相關的信息時並不產生異常,如sidt、sgdt、sldt、cpuid等等。爲了正確虛擬這些指令,VMware須要在全部指令上進行二進制翻譯,所以形成巨大的性能損失。
爲了不執行全指令模擬形成的巨大性能損失,VMware容許一些特定指令在沒有正確虛擬化的前提下運行。最終,這意味着某些指令序列在VMware虛擬機而不是在物理機中運行時返回不一樣的結果。處理器使用某些關鍵的結構與表,它們會被加載與真實系統不一樣的偏移量,而這正是未進行全虛擬化的反作用。
中斷描述表(IDT)是CPU內部的一個數據結構,操做系統使用它來確保正確響應中斷和異常。在x86體系結構下,全部的內存獲取,或是經過全局描述表(GDT)得到,或是經過本地描述表(LDT)得到。這些表中包含段描述符,它們提供每個段的詳細存取信息,其中包含段基地址類型、長度,以及存取權限等等。
IDT、GDT和LDT是CPU內部的寄存器,它們分別存放着各自表的基地址和大小。有三條敏感指令(sidt、sgdt和sldt)能夠讀取這些表的位置,而且將相應的寄存器存入內存地址。由於這些指令能夠隨時被用戶態代碼調用,且不會產生陷阱,也未被VMware正確虛擬化,因此這些異常均可能被用來探測VMware的存在。
1.使用Red Pill反虛擬機技術
RedPill經過運行sidt指令獲取IDTR寄存器的值。虛擬機監視器必須從新定位Guest系統的IDTR,來避免與Host系統的IDTR衝突。由於在虛擬機中運行sidt指令時,虛擬機監視器不會獲得通知,因此會返回虛擬機的IDTR。RedPill經過測試這種差別來探測Vmware的使用。這種方法存在一個缺陷,因爲IDT的值只針對處於正在運行的處理器而言,在單CPU中它是個常量,但當它處於多CPU時就可能會受到影響了,由於每一個CPU都有其本身的IDT,這樣問題就天然而然的產生了。
針對此問題,Offensive Computing組織成員提出了兩種應對方法:
其中一種方法就是利用Red Pill反覆地在系統上循環執行任務,以此構造出一張當前系統的IDT值變化統計圖,但這會增長CPU負擔;
另外一種方法就是windows API函數SetThreadAffinityMask()將線程限制在單處理器上執行,當執行此測試時只能準確地將線程執行環境限制在本地處理器,而對於將線程限制在VM處理器上就可能行不通了,由於VM是計劃在各處理器上運行的,VM線程在不一樣的處理器上執行時,IDT值將會發生變化,所以此方法也不多被使用。
2.使用No Pill反虛擬機技術
sgdt和sldt指令探測VMware的技術一般被稱爲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技術的探測。
3.查詢I/O端口
VMware使用虛擬化的I/O端口完成宿主系統與虛擬機之間的通訊,以便支持諸如複製和粘貼功能。這個端口能夠被查詢,而後與一個magic數比較,以肯定VMware的使用。
這種技術成功的關鍵在於x86體系結構中的in指令,它從一個源操做數指定的端口複製數據到目的操做數指定的內存地址。VMware會監視in指令的執行,並捕獲目的通訊端口爲0x5668(VX)的I/O。
VMware會檢查第二個操做數是不是VX,在這種狀況發生時,EAX寄存器載入的值是0x564D5868(VMXh),ECX寄存器必須被載入你但願在端口上執行相應操做的值,值0xA表示 get VMware version type,0x14表明 get the memory size。
它們均可以被用來探測VMware,但0xA更受歡迎,由於它能肯定VMware的版本。如代碼所示setz指令在magic數與VMXh匹配時設置返回值rc爲1,若是在真實的機器上運行會觸發EXCEPTION_EXECUTE_HANDLER 異常,在異常處理中設置返回值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;
}
對付這種反虛擬化技術的最簡單方法是使用NOP指令替換in指令,或修補條件跳轉,使得它不論比較結果如何,都執行到未探測到虛擬機的程序分支。
4.使用str指令
在保護模式下運行的全部程序在切換任務時,對於當前任務中指向TSS的段選擇器將會被存儲在任務寄存器中,TSS中包含有當前任務的可執行環境狀態,包括通用寄存器狀態、段寄存器狀態、標誌寄存器狀態、EIP寄存器狀態等等,當此項任務再次被執行時,處理器就會其原先保存的任務狀態。
每項任務均有其本身的TSS,而咱們能夠經過STR指令來獲取指向當前任務中TSS的段選擇器。這裏STR指令是用於將任務寄存器(TR)中的段選擇器存儲到目標操做數,目標操做數能夠是通用寄存器或內存位置,使用此指令存儲的段選擇器指向當前正在運行的任務的任務狀態段(TSS)。
在虛擬機和真實主機之中,經過STR讀取的地址是不一樣的,當地址等於0x0040xxxx時,說明處於虛擬機中,不然爲真實主機。
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 " % i)
要在IDA PRO中運行腳本,選擇File->Script File,能夠看到下面的輸出。
這個輸出代表腳本檢測到了三條漏洞指令類型。滾動到IDA PRO的反彙編窗口,咱們看到三條紅色高亮顯示的指令sidt、str和sldt。
5.使用無效的操做碼
每臺機器都有一組定義的指令,一般稱爲指令集架構(Instruction Set Architecture)。
當遇到無效指令(不存在於ISA中)時,機器引起無效操做碼異常。軟件能夠處理異常(使用一般的try/catch機制),也可讓操做系統處理異常,或者在最壞的狀況下崩潰機器。
VirtualPC使用一堆無效指令來容許虛擬機和VirtualPC之間鏈接。當VirtualPC的虛擬機想要與VirtualPC通訊時,程序設置異常處理程序(try/catch塊),在調用VM軟件以前設置所需的參數,發出特殊的無效操做碼指令。
VM軟件將識別此無效操做碼並相應地操做,若是VirtualPC存在則不引發異常,而且若是VirtualPC不存在則產生異常。
最後,程序的catch塊將處理異常並檢查返回的VM軟件的參數。總之,VirtualPC使用無效的操做碼機制做爲後門。
DWORD IslnsideVPC_exceptionFilter(LPEXCEPTION_POINTERS ep)
{
PCONTEXT ctx=ep->ContextRecord;
ctx->Ebx = -1; //未運行在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;
}
1.檢測電腦中經常使用軟件的使用狀況
名爲 Intelligent Software Solutions Inc.doc 的惡意軟件樣本文件使用了下面的反虛擬機技巧,SHA256值爲048fc07fb94a74990d2d2b8e92c099f3f986af185c32d74c857b07f7fcce7f8e。
RecentFiles對象表示系統最近打開過的歷史文檔。
一般,安裝了word程序的用戶可能會打開超過2個或更多數量的文檔。然而,當該惡意軟件植入到新建立的虛擬機和word環境中後,老是情況不斷,不能正常運行。每次測試時手動打開一兩次,老是出現程序異常。即便是保存了虛擬機鏡像狀態,重啓調試分析後,惡意程序仍然不能正常執行。
從DKTxHE函數功能能夠看出,惡意軟件以RecentFiles數量來判斷是否身處VM環境中,若是在VM環境中,它將不會執行任何惡意行爲。以後,隨意建立了3個不一樣名稱的word文檔,逐一打開並關閉,讓歷史文檔數量爲3,最終成功運行並檢測到了惡意軟件。
2. 探測殺毒軟件公司相關的IP地址
一樣是上面的惡意軟件,它在另外一個子程序中使用了下面的反虛擬機技巧。
首先,它經過向遠程地址 https://www.maxmind.com/geoip/v2.1/city/me 發出某種認證請求,以後設置請求信息中的HTTP
Refer屬性和User-Agent值,訪問連接 https://www.maxmind.com/en/locate-my-ip-address 以此獲取宿主系統的地址信息。
獲取信息封裝於JSON格式文件中,包含國家、城市、或者與IP相關的組織機構等信息。IP信息的organization字段顯示爲美國Comcast寬帶網絡供應商。
惡意軟件發出訪問請求後,獲取到宿主系統的相關信息將存儲於某個數組中。若是獲取到的組織機構名稱與JSON文件中的任何機構字符串匹配,惡意軟件將發生異常並中止運行。固然,列表中的機構名稱在代碼中是通過混淆的。
VMware等軟件中或多或少都存在一些安全漏洞,能夠利用這些漏洞使宿主操做系統崩潰或者是在宿主操做系統中運行代碼。當主機系統被感染後,一些公開可用的工具能夠用來對VMware等軟件進行攻擊。
當遇到的惡意代碼彷佛不能運行時,在使用調試或反彙編惡意代碼搜索其反虛擬機探測代碼以前,應該考慮使用一個卸載了VMware
Tools的虛擬機。VMware中有一些未文檔化的功能能夠幫助減輕反虛擬機技術的探測。將下面的代碼放到VMware的.vmx文件中,以減輕虛擬機被探測的可能。
isolation.tools.getPtrLocation.disable = "TRUE"
isolation.tools.setPtrLocation.disable = "TRUE"
isolation.tools.setVersion.disable = "TRUE"
isolation.tools.getVersion.disable = "TRUE"
monitor_control.disable_directexec = "TRUE"
monitor_control.disable_chksimd = "TRUE"
monitor_control.disable_ntreloc = "TRUE"
monitor_control.disable_selfmod = "TRUE"
monitor_control.disable_reloc = "TRUE"
monitor_control.disable_btinout = "TRUE"
monitor_control.disable_btmemspace = "TRUE"
monitor_control.disable_btpriv = "TRUE"
monitor_control.disable_btseg = "TRUE"
參數directexec可使用戶模式下的代碼被模擬執行而不是直接在硬件上運行,所以它能夠挫敗一些反虛擬機技術。
前四條設置被VMware後門命令使用,它們的做用是使得運行在Guest系統中的VMware
Tools不能獲取宿主系統的信息。這些設置會禁用VMware
Tools的一些有用功能,並可能對虛擬機性能有嚴重負面影響。因此,僅當其餘技術無效時再添加這些選項。固然,也能夠將惡意代碼在其餘虛擬環境或者物理主機上運行。
同反調試技術同樣,要想發現惡意代碼中的反虛擬機技術須要在長期調試過程當中積累更多經驗。例如,看到一個代碼在一個條件跳轉處過早終止,這可能就是反虛擬機技術形成的結果。一如既往地警戒這種類型的問題,而後查看其以前的代碼,來肯定它到底執行了什麼操做。
和反調試技術同樣,經過修改條件跳轉指令或者使用NOP指令覆蓋來繞過相關探測。
最後讓咱們總結一下提到的內容:
騰訊2016遊戲安全技術競賽有一道題,大概意思就是給一個exe,要求編寫一個Tencent2016C.dll,並導出多個接口函數 CheckVirtualPCX、CheckVMWareX、CheckVirtualBoxX。X爲1-100之間的數字。函數功能是檢測本身是否處於相應的虛擬機中,是返回TRUE,不然返回FALSE。函數的原型都是 typedef
BOOL (WINAPI*
Type_CheckXXXXXX)();。
編譯好dll以後,放在Tencent2016C.exe的同目錄,運行Tencent2016C.exe,點擊檢測按鈕,在物理機中運行時函數接口輸出爲0,在VMware虛擬機、VirtualBox虛擬機和VirtualPC虛擬機中運行時,相關的接口輸出1。咱們把提到的知識綜合一下完成這道題目。
解題的參考代碼和題目相關信息:https://github.com/houjingyi233/test-virtual-machine/
參考資料
1.《惡意代碼分析實戰》第17章反虛擬機技術(本文的主體框架)
2.這個惡意軟件「奇葩」的反虛擬機技巧
3.天樞戰隊官方博客(本文大部分代碼的來源)
4.虛擬機檢測技術剖析
5.Detect if your program is running inside a Virtual Machine
更多精彩乾貨,請關注看雪學院公衆號 ikanxue!
本文由看雪論壇 houjingyi 原創 轉載請註明來自看雪社區