硬件

描述

虚拟环境在模拟硬件设备时会留下一下痕迹或特征,可以通过查找这些内容来判断是否处于虚拟环境中。

1.检查硬盘是否有特定名称

以SetupDiGetClassDevs函数为例:
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#pragma comment(lib, "Setupapi.lib")

// 定义要检查的硬盘名称
#define TARGET_NAME L"VBOX"

// 检查硬盘是否有特定名称
BOOL CheckDiskName()
{
    // 初始化一个WinAPI应用程序,并返回一个包含所有磁盘驱动器设备信息的设备信息集句柄
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
        printf("SetupDiGetClassDevs failed: %d\n", GetLastError());
        return FALSE;
    }

    // 遍历设备信息集中的每个设备信息元素
    SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
    DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    DWORD dwIndex = 0;
    while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_DISK, dwIndex, &DeviceInterfaceData))
    {
        // 获取设备接口详细数据,并分配足够的内存空间
        DWORD dwSize = 0;
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &DeviceInterfaceData, NULL, 0, &dwSize, NULL);
        PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
        DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

        // 获取设备接口详细数据,并打印设备路径
        if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DeviceInterfaceData, DeviceInterfaceDetailData, dwSize, NULL, NULL))
        {
            printf("Device path: %ws\n", DeviceInterfaceDetailData->DevicePath);
            // 打开设备文件,并获取存储属性
            HANDLE hDevice = CreateFile(DeviceInterfaceDetailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
            if (hDevice != INVALID_HANDLE_VALUE)
            {
                STORAGE_PROPERTY_QUERY StoragePropertyQuery;
                StoragePropertyQuery.PropertyId = StorageDeviceProperty;
                StoragePropertyQuery.QueryType = PropertyStandardQuery;

                STORAGE_DEVICE_DESCRIPTOR StorageDeviceDescriptor;
                DWORD dwBytesReturned = 0;

                // 发送IO控制码,并获取存储属性
                if (DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
                    &StoragePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY),
                    &StorageDeviceDescriptor, sizeof(STORAGE_DEVICE_DESCRIPTOR),
                    &dwBytesReturned, NULL))
                {
                    // 获取硬盘名称,并打印
                    WCHAR szDiskName[256];
                    ZeroMemory(szDiskName, sizeof(szDiskName));
                    memcpy(szDiskName,
                        (BYTE*)&StorageDeviceDescriptor + StorageDeviceDescriptor.VendorIdOffset,
                        StorageDeviceDescriptor.ProductIdOffset - StorageDeviceDescriptor.VendorIdOffset);

                    printf("Disk name: %ws\n", szDiskName);

                    // 比较硬盘名称和目标名称,如果相同,则返回真
                    if (wcscmp(szDiskName, TARGET_NAME) == 0)
                    {
                        return TRUE;
                    }
                }

                CloseHandle(hDevice);
            }
        }

        HeapFree(GetProcessHeap(), 0, DeviceInterfaceDetailData);
        dwIndex++;
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    // 如果没有找到匹配的硬盘名称,则返回假
    return FALSE;
}

int main()
{
    // 检查硬盘是否有特定名称
    BOOL bFound = CheckDiskName();
    if (bFound)
    {
        printf("Disk name is matched, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }

    printf("Disk name is not matched, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
以SetupDiEnumDeviceInfo函数为例:
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#pragma comment(lib, "Setupapi.lib")

// 定义要检查的硬盘名称
#define TARGET_NAME L"VMware"
// 定义设备实例ID最大长度
#define MAX_DEVICE_ID_LEN 200

// 检查硬盘是否有特定名称
BOOL CheckDiskName()
{
    // 初始化一个WinAPI应用程序,并返回一个包含所有磁盘驱动器设备信息的设备信息集句柄
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
        printf("SetupDiGetClassDevs failed: %d\n", GetLastError());
        return FALSE;
    }

    // 遍历设备信息集中的每个设备信息元素
    SP_DEVINFO_DATA DeviceInfoData;
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    DWORD dwIndex = 0;
    while (SetupDiEnumDeviceInfo(hDevInfo, dwIndex, &DeviceInfoData))
    {
        // 获取设备实例ID,并打印
        WCHAR szDeviceInstanceId[MAX_DEVICE_ID_LEN];
        if (SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, szDeviceInstanceId, MAX_DEVICE_ID_LEN, NULL))
        {
            printf("Device instance ID: %ws\n", szDeviceInstanceId);

            // 获取设备描述,并打印
            WCHAR szDeviceDescription[256];
            if (SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, NULL, (PBYTE)szDeviceDescription, sizeof(szDeviceDescription), NULL))
            {
                printf("Device description: %ws\n", szDeviceDescription);

                // 比较设备描述和目标名称,如果相同,则返回真
                if (wcscmp(szDeviceDescription, TARGET_NAME) == 0)
                {
                    return TRUE;
                }
            }
        }

        dwIndex++;
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    // 如果没有找到匹配的硬盘名称,则返回假
    return FALSE;
}

int main()
{
    // 检查硬盘是否有特定名称
    BOOL bFound = CheckDiskName();
    if (bFound)
    {
        printf("Disk name is matched, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }

    printf("Disk name is not matched, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
以SetupDiGetDeviceRegistryProperty函数为例:
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#pragma comment(lib, "Setupapi.lib")

// 定义要检查的硬盘名称
#define TARGET_NAME L"VIRTUAL HD"

// 检查硬盘是否有特定名称
BOOL CheckDiskName()
{
    // 初始化一个WinAPI应用程序,并返回一个包含所有磁盘驱动器设备信息的设备信息集句柄
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
        printf("SetupDiGetClassDevs failed: %d\n", GetLastError());
        return FALSE;
    }

    // 遍历设备信息集中的每个设备信息元素
    SP_DEVINFO_DATA DeviceInfoData;
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    DWORD dwIndex = 0;

    while (SetupDiEnumDeviceInfo(hDevInfo, dwIndex, &DeviceInfoData))
    {
        // 获取设备描述,并打印
        WCHAR szDeviceDescription[256];
        if (SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, NULL, (PBYTE)szDeviceDescription, sizeof(szDeviceDescription), NULL))
        {
            printf("Device description: %ws\n", szDeviceDescription);
            // 比较设备描述和目标名称,如果相同,则返回真
            if (wcscmp(szDeviceDescription, TARGET_NAME) == 0)
            {
                return TRUE;
            }
        }
        dwIndex++;
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    // 如果没有找到匹配的硬盘名称,则返回假
    return FALSE;
}
int main()
{
    // 检查硬盘是否有特定名称
    BOOL bFound = CheckDiskName();
    if (bFound)
    {
        printf("Disk name is matched, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }
    printf("Disk name is not matched, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}

2.检查HDD Vendor ID是否有特定值

以DeviceIoControl函数为例:
#include <windows.h>
#include <stdio.h>

// 定义要检查的HDD Vendor ID
#define TARGET_ID "WDC"

// 定义启用GUID_DEVINTERFACE_DISK的宏
#define _NTDDI_WIN2K 0x05000000

// 包含设备和驱动程序安装的头文件
#include <setupapi.h>

// 链接到设备和驱动程序安装的库文件
#pragma comment(lib, "Setupapi.lib")

// 定义设备实例ID最大长度
#define MAX_DEVICE_ID_LEN 200

// 检查HDD Vendor ID是否有特定值
BOOL CheckHDDVendorID()
{
    // 初始化一个WinAPI应用程序,并返回一个包含所有磁盘驱动器设备信息的设备信息集句柄
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
        printf("SetupDiGetClassDevs failed: %d\n", GetLastError());
        return FALSE;
    }

    // 遍历设备信息集中的每个设备信息元素
    SP_DEVINFO_DATA DeviceInfoData;
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    DWORD dwIndex = 0;
    while (SetupDiEnumDeviceInfo(hDevInfo, dwIndex, &DeviceInfoData))
    {
        // 获取设备实例ID,并打印
        WCHAR szDeviceInstanceId[MAX_DEVICE_ID_LEN];
        if (SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, szDeviceInstanceId, MAX_DEVICE_ID_LEN, NULL))
        {
            printf("Device instance ID: %ws\n", szDeviceInstanceId);

            // 打开设备句柄
            HANDLE hDevice = CreateFile(szDeviceInstanceId, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
            if (hDevice != INVALID_HANDLE_VALUE)
            {
                // 定义存储查询结果的结构体
                typedef struct _STORAGE_PROPERTY_QUERY {
                    STORAGE_PROPERTY_ID PropertyId; // 使用枚举类型
                    STORAGE_QUERY_TYPE QueryType;
                    UCHAR AdditionalParameters[1];
                } STORAGE_PROPERTY_QUERY;
                typedef struct _STORAGE_DEVICE_DESCRIPTOR {
                    ULONG Version;
                    ULONG Size;
                    UCHAR DeviceType;
                    UCHAR DeviceTypeModifier;
                    BOOLEAN RemovableMedia;
                    BOOLEAN CommandQueueing;
                    ULONG VendorIdOffset;
                    ULONG ProductIdOffset;
                    ULONG ProductRevisionOffset;
                    ULONG SerialNumberOffset;
                    STORAGE_BUS_TYPE BusType;
                    ULONG RawPropertiesLength;
                    UCHAR RawDeviceProperties[1];
                } STORAGE_DEVICE_DESCRIPTOR;

                // 构造查询结构体
                STORAGE_PROPERTY_QUERY query = { 0 };
                query.PropertyId = StorageDeviceProperty; // 使用枚举常量 // 修改这里
                query.QueryType = PropertyStandardQuery; // 标准查询

                // 发送控制代码到设备驱动程序
                DWORD dwBytesReturned = 0;
                BYTE buffer[1024] = { 0 }; // 存储返回数据的缓冲区
                BOOL bResult = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
                    &query, sizeof(STORAGE_PROPERTY_QUERY),
                    buffer, sizeof(buffer),
                    &dwBytesReturned, NULL);
                if (bResult)
                {
                    // 获取返回数据中的设备描述结构体
                    STORAGE_DEVICE_DESCRIPTOR* deviceDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)buffer;

                    // 获取HDD Vendor ID并打印
                    char* vendorId = (char*)buffer + deviceDescriptor->VendorIdOffset;
                    printf("HDD Vendor ID: %s\n", vendorId);

                    // 比较HDD Vendor ID和目标值,如果相同,则返回真
                    if (strcmp(vendorId, TARGET_ID) == 0)
                    {
                        return TRUE;
                    }
                }
            }
        }

        dwIndex++;
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    // 如果没有找到匹配的HDD Vendor ID,则返回假
    return FALSE;
}

int main()
{
    // 检查HDD Vendor ID是否有特定值
    BOOL bFound = CheckHDDVendorID();
    if (bFound)
    {
        printf("HDD Vendor ID is matched, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }

    printf("HDD Vendor ID is not matched, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}

3.检查音频设备是否存在

一般虚拟环境可能不会存在音频设备,那么恶意软件即可通过检查音频设备来达到规避的目的。
以CoCreateInstance函数为例:
#include <windows.h>
#include <stdio.h>

// 定义启用IMMDevice接口的宏
#define _WIN32_WINNT 0x0600

// 包含MMDevice API的头文件
#include <mmdeviceapi.h>

// 检查音频设备是否存在
BOOL CheckAudioDevice()
{
    // 初始化COM库
    CoInitialize(NULL);

    // 创建一个IMMDeviceEnumerator接口对象
    IMMDeviceEnumerator* pEnumerator = NULL;
    HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
    if (FAILED(hr))
    {
        printf("CoCreateInstance failed: %x\n", hr);
        return FALSE;
    }

    // 获取默认的音频输出设备
    IMMDevice* pDevice = NULL;
    hr = pEnumerator->lpVtbl->GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
    if (FAILED(hr))
    {
        printf("GetDefaultAudioEndpoint failed: %x\n", hr);
        return FALSE;
    }

    // 释放COM对象
    pDevice->lpVtbl->Release(pDevice);
    pEnumerator->lpVtbl->Release(pEnumerator);

    // 返回真表示有音频设备
    return TRUE;
}

int main()
{
    // 检查音频设备是否存在
    BOOL bFound = CheckAudioDevice();
    if (!bFound)
    {
        printf("No audio device found, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }

    printf("Audio device found, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}

4.检查CPU温度信息是否可用

一般虚拟环境不会支持该功能,恶意软件可以尝试读取CPU温度信息来判断是否在虚拟环境中。
wmic /namespace:\\root\WMI path MSAcpi_ThermalZoneTemperature get CurrentTemperature

5.检查物理显示适配器的 IDirect3D9 接口

恶意软件可以获取或控制系统中的Direct3D设备,并检查物理显示适配器的IDirect3D9接口的Description输出是否是特定值,如果是则不运行,从而规避检测。
以Direct3DCreate9函数为例:
#include <windows.h>
#include <d3d9.h>
#include <stdio.h>

// 定义要检查的Description输出
#define TARGET_DESC "VMware SVGA 3D"

// 检查物理显示适配器的Description输出是否是特定值
BOOL CheckDisplayAdapterDesc()
{
    // 创建一个IDirect3D9对象
    IDirect3D9* pD3D = NULL; // 声明为C类型
    pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (pD3D == NULL)
    {
        printf("Direct3DCreate9 failed\n");
        return FALSE;
    }
    // 获取物理显示适配器的数量
    UINT AdapterCount = pD3D->GetAdapterCount();
    // 遍历每个物理显示适配器
    for (UINT i = 0; i < AdapterCount; i++)
    {
        // 获取物理显示适配器的标识符
        D3DADAPTER_IDENTIFIER9 Identifier;



        HRESULT hr = pD3D->GetAdapterIdentifier(i, 0, &Identifier);
        if (FAILED(hr))
        {
            printf("GetAdapterIdentifier failed: %x\n", hr);
            continue;
        }

        // 获取物理显示适配器的Description输出并打印
        char* Desc = Identifier.Description;
        printf("Display adapter %d Description: %s\n", i, Desc);
        // 比较Description输出和目标值,如果相同,则返回真
        if (strcmp(Desc, TARGET_DESC) == 0)
        {
            return TRUE;
        }
    }
    // 释放IDirect3D9对象
    pD3D->Release();
    // 如果没有找到匹配的Description输出,则返回假
    return FALSE;
}

int main()
{
    // 检查物理显示适配器的Description输出是否是特定值
    BOOL bFound = CheckDisplayAdapterDesc();
    if (bFound)
    {
        printf("Display adapter Description is matched, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }

    printf("Display adapter Description is not matched, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}
以GetAdapterIdentifier函数为例:
#include <windows.h>
#include <d3d9.h>
#include <stdio.h>

// 定义要检查的Description输出
#define TARGET_DESC "VMware SVGA 3D"

// 检查物理显示适配器的Description输出是否是特定值
BOOL CheckDisplayAdapterDesc()
{
    // 创建一个IDirect3D9对象
    IDirect3D9* pD3D = NULL;
    pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (pD3D == NULL)
    {
        printf("Direct3DCreate9 failed\n");
        return FALSE;
    }

    // 获取物理显示适配器的数量
    UINT AdapterCount = pD3D->GetAdapterCount();

    // 遍历每个物理显示适配器
    for (UINT i = 0; i < AdapterCount; i++)
    {
        // 获取物理显示适配器的标识符
        D3DADAPTER_IDENTIFIER9 Identifier;
        HRESULT hr = pD3D->GetAdapterIdentifier(i, 0, &Identifier);
        if (FAILED(hr))
        {
            printf("GetAdapterIdentifier failed: %x\n", hr);
            continue;
        }

        // 获取物理显示适配器的Description输出并打印
        char* Desc = Identifier.Description;
        printf("Display adapter %d Description: %s\n", i, Desc);

        // 比较Description输出和目标值,如果相同,则返回真
        if (strcmp(Desc, TARGET_DESC) == 0)
        {
            return TRUE;
        }
    }
    // 释放IDirect3D9对象
    pD3D->Release();
    // 如果没有找到匹配的Description输出,则返回假
    return FALSE;
}
int main()
{
    // 检查物理显示适配器的Description输出是否是特定值
    BOOL bFound = CheckDisplayAdapterDesc();
    if (bFound)
    {
        printf("Display adapter Description is matched, exit\n");
        // 这里可以退出程序或执行其他代码
        return 0;
    }
    printf("Display adapter Description is not matched, continue\n");
    // 这里插入恶意代码,比如显示一个弹窗消息
    MessageBoxA(NULL, "You have been infected by a malware!", "Malware Alert", MB_OK | MB_ICONWARNING);
}