Hardware H FileSystem Registry Generic OS Queries Global OS object UI artifacts OS Features Processes Network CPU Hardware Firmware tables Hooks Timing WMI Human-like behavior macOS
Virtual environments emulate hardware devices and leave specific traces in their descriptions - which may be queried and the conclusion about non-host OS made.
Functions used:
Code sample
hDevs = SetupDiGetClassDevs(
&guid, // GUID_DEVCLASS(DEVINTERFACE)_DISKDRIVE
NULL,
NULL,
DIGCF_PRESENT);
SetupDiEnumDeviceInfo(
hDevsInfo,
0,
&devinfo); // PSP_DEVINFO_DATA
SetupDiGetDeviceRegistryProperty(
hDevs,
&devinfo,
SPDRP_FRIENDLYNAME,
&dword_1,
szFriendlyName, // HDD name will be here
dFriendlyNameSize,
&dword_2);
Detections table
Check if hard disk drive has one of the following names: | |
Detect | Name |
---|---|
QEMU | QEMU |
VirtualBox | VBOX |
VirtualPC | VIRTUAL HD |
VMWare | VMWare |
The following function is used:
Code sample
bool GetHDDVendorId(std::string& outVendorId) {
HANDLE hDevice = CreateFileA(_T("\\\\.\\PhysicalDrive0"),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
if (hDevice == INVALID_HANDLE_VALUE)
return false;
STORAGE_PROPERTY_QUERY storage_property_query = {};
storage_property_query.PropertyId = StorageDeviceProperty;
storage_property_query.QueryType = PropertyStandardQuery;
STORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {};
DWORD BytesReturned = 0;
if (!DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&storage_property_query, sizeof(storage_property_query),
&storage_descriptor_header, sizeof(storage_descriptor_header),
&BytesReturned, )) {
printf("DeviceIoControl() for size query failed\n");
CloseHandle(hDevice);
return false;
}
if (!BytesReturned) {
CloseHandle(hDevice);
return false;
}
std::vector<char> buff(storage_descriptor_header.Size); //_STORAGE_DEVICE_DESCRIPTOR
if (!DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
&storage_property_query, sizeof(storage_property_query),
buff.data(), buff.size(), 0)) {
CloseHandle(hDevice);
return false;
}
CloseHandle(hDevice);
if (BytesReturned) {
STORAGE_DEVICE_DESCRIPTOR* device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buff.data();
if (device_descriptor->VendorIdOffset)
outVendorId = &buff[device_descriptor->VendorIdOffset];
return true;
}
return false;
}
Detections table
Check if HDD Vendor ID is one of the following: | |
Detect | Name |
---|---|
VirtualBox | VBOX |
VMWare | vmware |
This technique was extracted from TeslaCrypt malware sample and was described in this Joe Security blog post.
Code sample
void AudioEvasion() {
PCWSTR wszfilterName = L"audio_device_random_name";
if (FAILED(CoInitialize(NULL)))
return;
IGraphBuilder *pGraph = nullptr;
if (FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph)))
return;
if (E_POINTER != pGraph->AddFilter(NULL, wszfilterName))
ExitProcess(-1);
IBaseFilter *pBaseFilter = nullptr;
CoCreateInstance(CLSID_AudioRender, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pBaseFilter);
pGraph->AddFilter(pBaseFilter, wszfilterName);
IBaseFilter *pBaseFilter2 = nullptr;
pGraph->FindFilterByName(wszfilterName, &pBaseFilter2);
if (nullptr == pBaseFilter2)
ExitProcess(1);
FILTER_INFO info = { 0 };
pBaseFilter2->QueryFilterInfo(&info);
if (0 != wcscmp(info.achName, wszfilterName))
return;
IReferenceClock *pClock = nullptr;
if (0 != pBaseFilter2->GetSyncSource(&pClock))
return;
if (0 != pClock)
return;
CLSID clsID = { 0 };
pBaseFilter2->GetClassID(&clsID);
if (clsID.Data1 == 0)
ExitProcess(1);
if (nullptr == pBaseFilter2)
ExitProcess(-1);
IEnumPins *pEnum = nullptr;
if (0 != pBaseFilter2->EnumPins(&pEnum))
ExitProcess(-1);
if (0 == pBaseFilter2->AddRef())
ExitProcess(-1);
}
This technique was extracted from GravityRAT malware and is described by this link.
Code sample (Windows cmd command)
wmic /namespace:\\root\WMI path MSAcpi_ThermalZoneTemperature get CurrentTemperature
This method checks physical display adapters present in the system when the IDirect3D9 interface was instantiated. It works on all Windows versions starting from Windows XP.
Functions used:
Code sample
#include <d3d9.h>
// https://github.com/qt/qtbase/blob/dev/src/plugins/platforms/windows/qwindowsopengltester.cpp#L124
void detect() {
typedef IDirect3D9* (WINAPI* PtrDirect3DCreate9)(UINT);
HMODULE d3d9lib = ::LoadLibraryA("d3d9");
if (!d3d9lib)
return;
PtrDirect3DCreate9 direct3DCreate9 = (PtrDirect3DCreate9)GetProcAddress(d3d9lib, "Direct3DCreate9");
if (!direct3DCreate9)
return;
IDirect3D9* direct3D9 = direct3DCreate9(D3D_SDK_VERSION);
if (!direct3D9)
return;
D3DADAPTER_IDENTIFIER9 adapterIdentifier;
const HRESULT hr = direct3D9->GetAdapterIdentifier(0, 0, &adapterIdentifier);
direct3D9->Release();
if (SUCCEEDED(hr)) {
printf("VendorId: 0x%x\n", adapterIdentifier.VendorId);
printf("DeviceId: 0x%x\n", adapterIdentifier.DeviceId);
printf("Driver: %s\n", adapterIdentifier.Driver);
printf("Description: %s\n", adapterIdentifier.Description);
}
}
Credits for this code sample go to elsamuko who pointed it out.
Example of output on a usual host machine is provided below:
VendorId: 0x10de
DeviceId: 0x103c
Driver: nvldumdx.dll
Description: NVIDIA Quadro K5200
And here is an example of output on a virtual machine (VMware):
VendorId: 0x15ad
DeviceId: 0x405
Driver: vm3dum64_loader.dll
Description: VMware SVGA 3D
Examined fields are named after the corresponding fields of D3DADAPTER_IDENTIFIER9 structure. Malware can compare values in these fields to the ones which are known to be present inside the virtual machine and if match is found, then it draws the conclusion that it’s run under virtual machine.
Detections table
Check if the following values are present in the fields of D3DADAPTER_IDENTIFIER9 structure: | |||
Detect | Structure field | Value | Comment |
---|---|---|---|
VMware | VendorId | 0x15AD | |
DeviceId | 0x405 | Only when used in combination with VendorId related to VMware (0x15AD) | |
Driver | vm3dum.dll | ||
Driver | vm3dum64_loader.dll | ||
Description | VMware SVGA 3D |
Signature recommendations are general for each technique: hook the function used and track if it is called. It’s pretty hard to tell why application wants to get HDD name, for example. It doesn’t necessarily mean applying evasion technique. So the best what can be done in this situation is intercepting target functions and tracking their calls.