Go back

Contents

Hardware info detection methods
1. Check if HDD has specific name
2. Check if HDD Vendor ID has specific value
3. Check if audio device is absent
4. Check if CPU temperature information if available
5. Check physical display adapter for IDirect3D9 interface
Signature recommendations
Countermeasures


Hardware info detection methods

Virtual environments emulate hardware devices and leave specific traces in their descriptions - which may be queried and the conclusion about non-host OS made.


1. Check if HDD has specific name

Functions used:

  • SetupDiGetClassDevs
  • SetupDiEnumDeviceInfo
  • SetupDiGetDeviceRegistryProperty

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


2. Check if HDD Vendor ID has specific value

The following function is used:

  • DeviceIoControl(..., IOCTL_STORAGE_QUERY_PROPERTY, ...)

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


3. Check if audio device is absent

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);
}


4. Check if CPU temperature information is available

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


5. Check physical display adapter for IDirect3D9 interface

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:

  • Direct3DCreate9 - called from `d3d9.dll` library
  • GetAdapterIdentifier - called via IDirect3D9 interface

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

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.


Countermeasures

  • versus HDD checks: rename HDD so that it's not detected by specific strings;
  • versus audio device check: add audio device;
  • versus CPU temperature check: add stub to hypervisor to output some meaningful information;
  • versus physical display adapter check: set up hook on a function GetAdapterIdentifier from d3d9.dll, check if the queried adapter is related to DirectX and replace return values.

Go back