
第一次嘗試惡意代碼分析就遇到了虛擬機檢測,于是就想著先學習一下檢測的技術然后再嘗試繞過。學習后最終發現,似乎最好的方法不應該是去patch所有檢測方法,而是直接調試并定位檢測函數再繞過。但既然已經研究了兩天,索性將收集到的資料整理一下,方便后人查找。
一、基于特征的檢測
1.1 文件以及目錄
相關 API :GetFileAttributes、etModuleHandle
通常,各種 hypervisor 會在虛擬機上安裝驅動程序和其他軟件,以使客戶機能夠利用 hypervisor 提供的功能。惡意軟件可以搜索這些文件、目錄或進程的存在。VMware 虛擬機中可能會有如下的文件列表:
C:\Program Files\VMware\ C:\Windows\System32\vm3dc003.dll C:\Windows\System32\vm3ddevapi64-debug.dll C:\Windows\System32\vm3ddevapi64-release.dll C:\Windows\System32\vm3ddevapi64-stats.dll C:\Windows\System32\vm3ddevapi64.dll C:\Windows\System32\vm3dgl64.dll C:\Windows\System32\vm3dglhelper64.dll C:\Windows\System32\vm3dservice.exe C:\Windows\System32\vm3dum64-debug.dll C:\Windows\System32\vm3dum64-stats.dll C:\Windows\System32\vm3dum64.dll C:\Windows\System32\vm3dum64_10-debug.dll C:\Windows\System32\vm3dum64_10-stats.dll C:\Windows\System32\vm3dum64_10.dll C:\Windows\System32\vm3dum64_loader.dll C:\Windows\System32\vmGuestLib.dll C:\Windows\System32\vmGuestLibJava.dll C:\Windows\System32\vmhgfs.dll C:\Windows\System32\VMWSU.DLL C:\Windows\System32\vsocklib.dll C:\Windows\System32\drivers\vm3dmp.sys C:\Windows\System32\drivers\vm3dmp_loader.sys C:\Windows\System32\drivers\vm3dmp-debug.sys C:\Windows\System32\drivers\vm3dmp-stats.sys C:\Windows\System32\drivers\vmnet.sys C:\Windows\System32\drivers\vmmouse.sys C:\Windows\System32\drivers\vmusb.sys C:\Windows\System32\drivers\vmci.sys C:\Windows\System32\drivers\vmhgfs.sys C:\Windows\System32\drivers\vmmemctl.sys C:\Windows\System32\drivers\vmx86.sys C:\Windows\System32\drivers\vmrawdsk.sys C:\Windows\System32\drivers\vmusbmouse.sys C:\Windows\System32\drivers\vmkdb.sys C:\Windows\System32\drivers\vmnetuserif.sys C:\Windows\System32\drivers\vmnetadapter.sys
1.2 Devices
相關API:CreateFile
在Windows虛擬機中,虛擬機通常擁有獨特命名的 “devices”。
◆\\.\HGFS,主機和虛擬機之間共享的文件系統
◆\\.\vmci,虛擬機和主機之間的通信管道
1.3 MAC 地址
相關API:GetAdaptersAddresses、GetAdaptersInfo、Netbios
Hypervisors 通常為虛擬機提供虛擬化的網卡(NICs),具有獨占于hypervisor供應商的MAC地址。
已知的 VMware 虛擬機的 MAC地址有:
◆00:50:56:XX:XX:XX
◆00:1C:14:XX:XX:XX
◆00:0C:29:XX:XX:XX
◆00:05:69:XX:XX:XX
1.4 BIOS 字符串
相關API:RegOpenKeyEx、RegQueryValueEx
虛擬化產品通常會附帶一個基本輸入/輸出系統(BIOS),其中包含虛擬機特有的字符串。可以通過檢查Windows注冊表來查看。
◆HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System中鍵值 “SystemBiosVersion” 包含 “VMware, Inc.”
◆HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS中包含鍵值
- “BIOSVendor” 為 “VMware, Inc.”
- “BIOSVersion” 的起始字符串為 “VM.”
- “SystemManufacturer” 為 “VMware, Inc.”
- “SystemProductName” 包含 “VMware.”
◆HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\SystemInformation中也有類似的信息
1.5 其他 Windows 注冊表項
VMware中,以下注冊表項會帶有 “VMWARE” 或者 “VM” 字符串
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0 HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 1\Scsi Bus 0\Target Id 0\Logical Unit Id 0 HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 2\Scsi Bus 0\Target Id 0\Logical Unit Id 0 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Disk\Enum HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\IDE HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\SCSI
1.6 SMBIOS 字符串
相關API:GetSystemFirmwareTable
System Management BIOS 是由BIOS讀取的一組數據結構。虛擬機會帶有有特殊的字符串。VMware 虛擬機會帶有 “VMware”。
1.7 ACPI 字符串
相關API:GetSystemFirmwareTable
Advanced Configuration and Power Interface (ACPI) 是操作系統通過 BIOS 或 UEFI 與各種硬件組件進行通信的接口。VMware 虛擬機會帶有 “VMware”。
1.8 進程
相關API:CreateToolhelp32Snapshot、Process32First、Process32Next
一些虛擬機利會有特殊的后臺進程。這些進程主要提供一些方便的功能,例如自動配置屏幕分辨率和配置網絡設置等。
VMware虛擬機中可能會有以下進程:
◆Vmtoolsd.exe
◆Vmwaretray.exe
◆Vmwareuser.exe
◆VGAuthService.exe
◆Vmacthlp.exec
有些惡意軟件還會通過進程數來檢測虛擬環境,比如 ”EvilBunny“ 會檢測是否存在至少15個進程。
1.9 硬盤硬件ID以及名稱
相關API:SetupDiGetClassDevs、SetupDiGetDeviceRegistryProperty
Win32/Winwebsec的一些變種使用了一種通過 Setup API來檢查硬盤設備名的方法。類似的也可以檢測硬盤硬件ID。

1.10 通過 WMI 獲取硬件信息
Windows Management Instrumentation(WMI),提供了一種標準化的接口,允許程序獲取系統信息并執行管理任務。WMI是基于COM(Component Object Model)技術構建的,并使用WQL(WMI查詢語言)來查詢數據。
◆可以通過 WMI 獲取 BIOS 序列號并查看是否帶有類似“VMWARE”的字符串。類似的還可以檢測 Win32_ComputerSystem 下的 Model 以及 Manufacturer 字段。
VOID CheckBIOSSerialNumberWMI(){ IWbemServices* pSvc = NULL; IWbemLocator* pLoc = NULL; IEnumWbemClassObject* pEnumerator = NULL; BOOL bFound = FALSE; HRESULT hRes; // Init WMI if (!InitWMI(&pSvc, &pLoc, _T("ROOT\\CIMV2"))) return; // If success, execute the desired query if (!ExecWMIQuery(&pSvc, &pLoc, &pEnumerator, _T("SELECT * FROM Win32_BIOS"))) return; // Get the data from the query IWbemClassObject* pclsObj = NULL; ULONG uReturn = 0; VARIANT vtProp; while (pEnumerator) { hRes = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) break; // Get the value of the Name property VariantInit(&vtProp); hRes = pclsObj->Get(_T("SerialNumber"), 0, &vtProp, 0, 0); if (SUCCEEDED(hRes)) { if (vtProp.vt == VT_BSTR) { if (StrStrI(vtProp.bstrVal, _T("VMWare")) != 0) { bFound = TRUE; VariantClear(&vtProp); pclsObj->Release(); break; } } VariantClear(&vtProp); } // release the current result object pclsObj->Release(); } // Cleanup pSvc->Release(); pLoc->Release(); pEnumerator->Release(); CoUninitialize(); PrintResult(bFound, _T("Checking SerialNumber from BIOS using WMI"));}
◆可以通過返回結果的條目數,檢查是否存在一些物理機應該有的硬件,比如:
SELECT * FROM Win32_Fan // 風扇SELECT * FROM Win32_CacheMemory //SELECT * FROM Win32_VoltageProbe // 電壓探針SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation // 熱區信息SELECT * FROM CIM_Memory // 物理內存SELECT * FROM CIM_Sensor // 傳感器設備SELECT * FROM CIM_NumericSensor // 數字傳感器SELECT * FROM CIM_TemperatureSensor // 溫度傳感器SELECT * FROM CIM_VoltageSensor // 電壓傳感器
二、基于指令的檢測
某些CPU指令在虛擬機中執行時返回值與在物理機機上執行時返回值不同。惡意軟件可以執行這些指令,并嘗試根據返回值推斷虛擬機的存在。雖然這些檢測方法不普遍適用于所有虛擬機,且隨著虛擬機的迭代也已經部分失效,但仍然有必要了解。下面列出已知的用于虛擬機檢測的指令。
2.1 IN
IN指令從指定的I/O端口讀取值到指定的內存地址。I/O端口是操作系統和I/O設備(如磁盤控制器和聲音設備)之間的通信手段。基于VMware的虛擬機具有端口號為 0x5658 的I/O設備,允許hypervisor 和虛擬機之間通信。通常,在非特權用戶模式下運行IN指令將觸發特權指令異常。但在 VMware 中,不會引發此類異常。VMware 公司甚至將此作為官方檢測 VMware hypervisor 的手段。VMware 也提供了一個接口,允許用戶“關閉”此方法。
bool isVmwarePresent()
{
__try {
_asm {
mov eax, 0x564d5868 // 'VMXh'
mov ebx, 0
mov cx, 1
mov dx, 0x5658 // 'VX'
in eax, dx
}
return true;
}
__except (GetExceptionCode() == EXCEPTION_PRIV_INSTRUCTION) {
return false;
}
}
2.2 CPUID
CPUID是一種x86指令,用來獲取CPU的各種信息。該指令通過輸入 EAX 中的值來確定要在EAX,EBX,ECX和EDX寄存器中返回的信息。下面給出三種基于 CPUID 指令的檢測方法。
1.當 EAX=0 時, CPUID 將按EBX,EDX和ECX寄存器的順序返回一個12個字符的制造商ID字符串。Intel CPU返回“GenuineIntel”,而AMD CPU在K5型號之后返回“AuthenticAMD” 。當在虛擬機中運行時,ID將被專屬于某些虛擬機的自定義字符串替換。VMware虛擬機中理論上將返回 “VmwareVmware”。
BOOL CheckCPUIDVendor(){ INT CPUInfo[4] = { -1 }; CHAR szHypervisorVendor[0x40]; __cpuid(CPUInfo, 0x40000000); SecureZeroMemory(szHypervisorVendor, sizeof(szHypervisorVendor)); memcpy(szHypervisorVendor, CPUInfo + 1, 12); if (_strcmpi(szHypervisorVendor, "VMwareVMware") == 0) return TRUE; return FALSE;}
2.當 EAX=1 時,CPUID將返回有關處理器功能的信息,例如處理器支持的指令。返回值 ECX 中的第 31 位確定是否存在 hypervisor。在物理機中此值為 0,在虛擬機中為 1。
BOOL CheckCPUIDVendor(){ INT CPUInfo[4] = { -1 }; __cpuid(CPUInfo, 1); if ((CPUInfo[2] >> 31 ) & 1) return TRUE; return FALSE;}
3.在虛擬機中執行 CPUID 還會觸發 VM-Exit Event,使得進程從 guest 切換到 VMM。可以用 RDTSC 指令來計算這個開銷。
BOOL rdtsc_diff_vmexit(){ ULONGLONG tsc1 = 0; ULONGLONG tsc2 = 0; ULONGLONG avg = 0; INT cpuInfo[4] = {}; // Try this 10 times in case of small fluctuations for (INT i = 0; i < 10; i++) { tsc1 = __rdtsc(); __cpuid(cpuInfo, 0); tsc2 = __rdtsc(); // Get the delta of the two RDTSC avg += (tsc2 - tsc1); } // We repeated the process 10 times so we make sure our check is as much reliable as we can avg = avg / 10; return (avg < 1000 && avg > 0) ? FALSE : TRUE;}
2.3 SIDT(已失效)
SIDT是 Intel x86 指令,用于檢測舊單核處理器上的虛擬機存在。這個指令被 “ScoopyNG” 以及 “Redpill” 使用,來檢測虛擬機的存在。該命令將中斷描述符表寄存器 (IDTR)的內容存放到指定內存地址中。中斷描述符表(IDT)是一種重要的操作系統數據結構,用于處理來自硬件(如鍵盤和鼠標)的中斷命令。hypervisor 會將虛擬化操作系統的 IDT 的地址移動到 hypervisor 可預測的地址。Klein 和 Rutkowska 都發現VMware在當時將 IDT 存儲在地址0xFFXXXXXX上,從而在單核處理器上檢測 VMware 虛擬機。
然而,隨著多核處理器的引入,這種虛擬機檢測技術不再可靠,因為處理器內的每個核心都具有唯一的中斷描述符表,而每個表的地址都不同。此外,在我們對 VMware 的虛擬機測試SIDT命令時,即使只分配了一個處理器核心,IDT 的地址也從未出現在0xFFXXXXXX的地址上。因此,這種虛擬機檢測方法已失效。
2.4 SGDT(已失效)
SGDT是 Intel x86 指令,用于將全局描述符表寄存器(GDTR)的內容存放到指定內存地址中。GDT 是處理器使用的數據結構,用于定義有關進程內存的詳細信息,例如基址、大小和內存權限。這種虛擬機檢測方法曾被“ScoopyNG”使用。與SIDT指令類似,當在基于VMware的虛擬機中運行時,該指令會返回0xFFXXXXXX的值。然而,這個指令也面臨著 SIDT 指令引入多核處理器后遇到的同樣問題。在使用 VMware Workstation 測試這種檢測方法時,返回的值并不是0xFFXXXXXX。因此,這種檢測方法已失效。
2.5 SLDT(已失效)
SLDT 是 Intel x86 指令,用于將本地描述符表寄存器(LDTR)的內容存放到指定內存地址。LDT包含有關進程內存段的信息,例如代碼段、數據段以及堆。當在虛擬機中執行此命令時,將返回非零值,而在非虛擬化環境中返回0。“ScoopyNG”和惡意軟件“Conficker”曾使用過這種虛擬機檢測方法。我們在 VMware Workstation、VirtualBox、XEN、HyperV 和 KVM/QEMU 虛擬機上測試了此命令,發現所有虛擬機都返回0。這種檢測方法已失效。
2.6 STR(已失效)
STR是 Intel x86 指令,它返回當前執行進程的任務狀態寄存器(TSR)內容到指定內存地址。任務狀態段 TSS 用于存放操作系統用于任務切換的數據,其中包括進程 threads 和 registry 信息,供處理器操作系統處理線程調度。Klein 發現在虛擬機內運行此命令時,它返回格式為 0x0040XXXX 的地址。對VMware Workstation、VirtualBox、XEN、HyperV和KVM/QEMU虛擬機上測試此命令,發現這種檢測方法已失效。
2.7 SMSW(已失效)
SMSW是 Intel x86命令之一,用于將機器狀態字返回到指定的內存地址。機器狀態字的信息來自CR0寄存器,與 CPU 用于指示是否啟用某些功能(如內存分頁)的 flags 有關。Quist 發現,在VMware虛擬機內運行此命令,當 EAX=0xCCCCCCCC 時,將返回 0x8001XXXX。經測試在VMware虛擬機內和物理機上測試此命令都會返回值 0x8005XXXX。因此這種檢測方法已失效。
2.8 VM “Synthetic Instructions”(已失效)
Traut在2003年提出了一種使用“合成CPU指令”的虛擬機檢測方法。這些指令僅被虛擬機支持,在物理機上執行時會觸發無效指令異常。由于這些是無效的CPU指令,因此它們未在Intel或AMD的程序員手冊中記錄。這些指令的格式為“0FC7C8XXXX”。其中最“VMCPUID”指令被“Necurs”僵尸網絡用于檢查虛擬機的存在。

在 VMware Workstation、VirtualBox、XEN、HyperV 和 KVM/QEMU 虛擬化平臺上測試 “VMCPUID” 指令的代碼如下:
bool IsVmcpuidSupported()
{
bool supported = true;
void* mempool;
char vmcpuid[] = "\x0F\xC7\xC8\x01\x00"; // VMCPUID OPCODE
mempool = malloc(sizeof(vmcpuid));
memcpy(mempool, &vmcpuid, sizeof(vmcpuid));
DWORD prevAccess;
VirtualProtect(mempool, sizeof(vmcpuid), PAGE_EXECUTE_READ, &prevAccess);
__try {
((void(*)())mempool)();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
supported = false;
}
return supported;
}
結果所有測試的虛擬化平臺都不支持 VMCPUID 命令。實際上,這個命令被錯誤地解釋為兩個命令:
CMPXCHG8B EAX ADD DWORD PTR DS:[EAX],EAX
三、其他檢測方法
3.1 Thermal Zone Temperature
惡意軟件“Win32/gravityRAT”采用了一種通過檢測系統 thermal zone 來檢測虛擬機存在的方法。通過使用下面的powershell腳本,可以輕松檢查機器是否支持“MSAcip_ThermalZoneTemperature”對象。如果此腳本沒有返回結果,則假定該計算機在虛擬機中,因為虛擬機默認不支持熱力監控。由于Thermal zone sensors 可以在系統的BIOS或UEFI中禁用和啟用,在多個物理機上測試結果不一致,所以該方法并不可靠。
function Get-AntiVMwithTemperature {
$t = Get-WmiObject MSAcip_ThermalZoneTemperature -Namespace "root/wmi"
$valorTempKelvin = $t.CurrentTemperature / 10
$valorTempCelsius = $valorTempKelvin - 273.15
$valorTempFashrenheit = (9/5) * $valorTempCelsius + 32
return $valorTempCelsius.ToString() + " C : " + valorTempFashrenheit.ToString() + \
" F : " + $valorTempKelvin.ToString() + "K"
}
Ref:
Anti-VM Technique with MSAcpi_ThermalZoneTemperature
(https://debugactiveprocess.medium.com/anti-vm-techniques-with-msacpi-thermalzonetemperature-32cfeecda802)
3.2 IP Timestamp Patterns
這種虛擬機檢測方法是由Noorafize等人發現的 ,它基于虛擬機無法像物理機一樣保持準確的時間。虛擬機通常通過與主機計算機共享時間來進行時間同步,以嘗試偽裝成物理機,但來自虛擬機的IP報文時間戳存在不一致性。研究人員發現,由于虛擬機需要與 hypervisor 進行交互,導致虛擬機的時間戳信息略有不同。為了使用此檢測方法,攻擊者需要向目標系統發送大量的IP / ICMP 數據包以獲取足夠的信息,以推斷目標機器是否為虛擬機。
Ref:
M. Noorafiza, H. Maeda, T. Kinoshita and R. uda, "Virtual Machines Detection Methods using IP Timestamps pattern Characteristic," vol. 8, 2016
3.3 CPU Detail Anomalies
通過檢查CPU物理核心數、邏輯核心數和cache容量與實際產品配置的不一致性來檢測虛擬機。
Ref:Mettrick D. Virtual machine detection through Central Processing Unit (CPU) detail anomalies[D]. , 2022.
3.4 Physical memory resource maps
在 Windows 中,設備驅動程序需要訪問硬件資源(如內存、I/O 端口、中斷等),這些資源的地址通常是硬件相關的,因此在不同的系統上地址可能會發生變化。為了方便驅動程序的編寫和移植,Windows 內核提供了資源映射機制,將硬件相關的地址轉換為系統相關的地址,從而使驅動程序可以在不同的系統上運行而不需要進行修改。內存翻譯就是其中一種資源映射類型,它將硬件設備的物理內存地址映射為系統中的虛擬內存地址,使驅動程序可以通過虛擬地址來訪問設備內存。
可以通過比對虛擬地址的不同檢測虛擬機環境。測試發現Hyper-V虛擬機具有與 00001000-000a0000 匹配的物理內存映射區域。VirtualBox虛擬機具有與 00001000-009f000 匹配的物理內存映射區域。
Ref:
VM Detection Tricks, Part 1: Physical memory resource maps
(https://labs.nettitude.com/blog/vm-detection-tricks-part-1-physical-memory-resource-maps/)
3.5 Driver Thread Fingerprinting
驅動程序可能創建一定數量的線程,并具有可預測的屬性,因此這些屬性可以用于指紋識別并構建有用的檢測啟發式算法。
Ref:
VM Detection Tricks, Part 2: Driver Thread Fingerprinting
(https://labs.nettitude.com/blog/vm-detection-tricks-part-2-driver-thread-fingerprinting/)
3.6 基于鼠標行為的方法
惡意軟件 Win32/Okrum 使用了基于行為的一些方法來檢測沙箱:
◆GetTickCount函數調用兩次,間隔20秒休眠。如果GetTickCount值沒有改變(即時間已經加速),惡意軟件將終止自身。
◆連續兩次調用GetCursorPos函數。如果鼠標在X軸上的位置發生了變化(即鼠標位置是隨機生成的),惡意軟件將終止自身。
◆調用GetGlobalMemoryStatusEx。如果實際物理內存的數量小于1.5GB,惡意軟件將終止自身
◆只有左鍵(物理)鼠標按鈕按下至少三次后(在無限循環中查詢GetAsyncKeyState),負載才開始。
Ref:
ESET_Okrum_and_Ketrican.pdf
(https://www.welivesecurity.com/wp-content/uploads/2019/07/ESET_Okrum_and_Ketrican.pdf)
參考
1.https://www.cynet.com/attack-techniques-hands-on/malware-anti-vm-techniques/
2.https://www.ptsecurity.com/ww-en/analytics/antisandbox-techniques/
3.https://artemonsecurity.com/vmde.pdf
4.https://github.com/LordNoteworthy/al-khaser
5.https://github.com/a0rtega/pafish
看雪學苑
安全牛
安全圈
看雪學苑
看雪學苑
黑白之道
嘶吼專業版
安全圈
LemonSec
一顆小胡椒
一顆小胡椒
LemonSec