Rutinas para la detección del uso de máquinas virtuales

A continuación puede encontrar tres rutinas para la detección de ambientes virtualizados que hacen uso del software VMware, Oracle VM VirtualBox, Windows Virtual PC o QEMU, y cuyo enfoque es para el sistema operativo Windows. La detección es realizada buscando valores por defecto en la configuración de las tarjetas de red (dirección MAC), valores localizados en el registro, y a través de Windows Management Instrumentation (Que finalmente es traducido también en valores encontrados en el registro).

Detección a través de la dirección MAC

Se obtiene la dirección MAC de todas las tarjetas de red y se compara con prefijos predeterminados por cada software:

Fabricante
Prefijo
VMware
00:05:69:xx:xx:xx
VMware
00:0C:29:xx:xx:xx
VMware
00:1C:14:xx:xx:xx
Oracle VM VirtualBox
08:00:27:xx:xx:xx
Windows Virtual PC
00:03:FF:xx:xx:xx
QEMU
52:54:00:xx:xx:xx

La rutina puede ser observada a continuación:

BOOL mac_test()
{
unsigned char MACData[8];
WKSTA_TRANSPORT_INFO_0 *pwkti;
DWORD dwEntriesRead;
DWORD dwTotalEntries;
BYTE *pbBuffer;
NET_API_STATUS dwStatus = NetWkstaTransportEnum(NULL, 0, &pbBuffer, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, NULL);
pwkti = (WKSTA_TRANSPORT_INFO_0 *)pbBuffer;
for (DWORD i = 1; i < dwEntriesRead; i++) {
swscanf((wchar_t *)pwkti[i].wkti0_transport_address, L"%2hx%2hx%2hx%2hx%2hx%2hx", &MACData[0], &MACData[1], &MACData[2], &MACData[3], &MACData[4], &MACData[5]);
if ((MACData[0] == 0 && MACData[1] == 5 && MACData[2] == 105) || // VMware
(MACData[0] == 0 && MACData[1] == 12 && MACData[2] == 41) || // VMware
(MACData[0] == 0 && MACData[1] == 28 && MACData[2] == 20) || // VMware
(MACData[0] == 0 && MACData[1] == 80 && MACData[2] == 86) || // VMware
(MACData[0] == 8 && MACData[1] == 0 && MACData[2] == 39) || // Oracle VM VirtualBox
(MACData[0] == 0 && MACData[1] == 3 && MACData[2] == 255) || // Windows Virtual PC
(MACData[0] == 82 && MACData[1] == 84 && MACData[2] == 0)) { // QEMU
dwStatus = NetApiBufferFree(pbBuffer);
return TRUE;
}
}
dwStatus = NetApiBufferFree(pbBuffer);
return FALSE;
}

Detección a través del registro de Windows

Se obtiene el valor de la cadena "0" en la clave "HKLM\SYSTEM\CurrentControlSet\Services\Disk\Enum", y se busca valores predeterminados por cada software:
La rutina puede ser observada a continuación:
Fabricante
Prefijo
Oracle VM VirtualBox
VBOX
Windows Virtual PC
DiskVirtual
Windows Virtual PC
VIRTUAL
QEMU
QEMU
BOOL reg_test()
{
HKEY hKey;
CHAR szBuffer[1024];
ULONG hSize = sizeof(szBuffer);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueEx(hKey, "0", NULL, NULL, (unsigned char *)szBuffer, &hSize) == ERROR_SUCCESS) {
if (strstr(szBuffer, "VBOX") != NULL ||
strstr(szBuffer, "VMware") != NULL ||
strstr(szBuffer, "DiskVirtual") != NULL ||
strstr(szBuffer, "VIRTUAL") != NULL ||
strstr(szBuffer, "QEMU") != NULL) {
RegCloseKey(hKey);
return TRUE;
}
}
RegCloseKey(hKey);
}
return FALSE;
}

Detección a través de Windows Management Instrumentation 

El procedimiento realizado es similar a los dos anteriores, se busca valores predeterminados a través del uso de WMI. Dichos valores son obtenidos a través de WQL, usando las siguientes consultas y valores predeterminados por cada software:

SELECT Version FROM Win32_BIOS

Fabricante
Prefijo
Oracle VM VirtualBox
VBOX
Windows Virtual PC
A M I - 8000914
QEMU
BOCHS

SELECT Model FROM Win32_ComputerSystem

Fabricante
Prefijo
VMware
VMware
Oracle VM VirtualBox
VirtualBox
Windows Virtual PC
Virtual Machine
QEMU
BOCHS

SELECT DeviceID FROM Win32_CDROMDrive

Fabricante
Prefijo
QEMU
QEMU
Oracle VM VirtualBox
VBOX

SELECT PNPDeviceID FROM Win32_DiskDrive

Fabricante
Prefijo
VMware
VMware
Oracle VM VirtualBox
VBOX
Windows Virtual PC
DISKVIRTUAL
QEMU
QEMU

SELECT Description FROM CIM_LogicalDevice

Fabricante
Prefijo
VMware
VMware
Oracle VM VirtualBox
VirtualBox
La rutina aunque un poco más compleja pero no menos importante puede ser observada a continuación:



BOOL wmi_test()
{
CHAR buffer[64];
CHAR value[256];
PSTR objects[5][6] = {{"Win32_BIOS", "Version", "VBOX", "BOCHS", "A M I - 8000914", NULL},
{"Win32_ComputerSystem", "Model", "VirtualBox", "Bochs", "VMware", "Virtual Machine"},
{"Win32_CDROMDrive", "DeviceID", "VBOX", "QEMU", NULL, NULL},
{"Win32_DiskDrive", "PNPDeviceID", "VBOX", "QEMU", "VMware", "DISKVIRTUAL"},
{"CIM_LogicalDevice", "Description", "VirtualBox", "VMware", NULL, NULL}};
HRESULT hres;
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
return FALSE;
hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
if (FAILED(hres)) {
CoUninitialize();
return FALSE;
}
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);

if (FAILED(hres)) {
CoUninitialize();
return FALSE;
}
IWbemServices *pSvc = NULL;
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
if (FAILED(hres)) {
pLoc->Release();
CoUninitialize();
return FALSE;
}
hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(hres)) {
pSvc->Release();
pLoc->Release();
CoUninitialize();
return FALSE;
}
for (int i = 0; i < 5; i++) {
IEnumWbemClassObject* pEnumerator = NULL;

memset(buffer, 0, 64);
strcpy(buffer, "SELECT * FROM ");
strcat(buffer, objects[i][0]);
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t(buffer), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
if (FAILED(hres)) {
pSvc->Release();
pLoc->Release();
CoUninitialize();
return FALSE;
}

IWbemClassObject *pclsObj;
ULONG uReturn = 0;
while (pEnumerator) {
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn)
break;
VARIANT vtProp;
wchar_t wcstring[256];
mbstowcs(wcstring, objects[i][1], 256);
hr = pclsObj->Get(wcstring, 0, &vtProp, 0, 0);

memset(value, 0, 256);
strcpy(value, _bstr_t(vtProp.bstrVal).operator char *());

for (int j = 2; j < 6; j++) {
if (objects[i][j] == NULL)
continue;
if (strstr(value, objects[i][j]) != NULL) {
VariantClear(&vtProp);
pclsObj->Release();
pEnumerator->Release();
pSvc->Release();
pLoc->Release();
CoUninitialize();
return TRUE;
}
}

VariantClear(&vtProp);
pclsObj->Release();
}
pEnumerator->Release();
}
pSvc->Release();
pLoc->Release();
CoUninitialize();
return FALSE;
}

Pruebas Finales

Haciendo uso del anterior código y ejecutando las tres rutinas de forma consecutiva:



int main(int argc, char* argv[])
{
printf("MAC Address Test: %d\n", mac_test());
printf("Registry Test: %d\n", reg_test());
printf("Wmi Test: %d\n", wmi_test());
return 0;
}

Se obtiene el siguiente resultado (True indica detección exitosa):
Fabricante
MAC Test
REG Test
WMI Test
VMware
True
True
True
Oracle VM VirtualBox
True
True
True
Windows Virtual PC
True
True
True
QEMU
True
True
True
Windows 7 Sin virtualizar
True
False
True

Código fuente

El archivo comprimido contiene cuatro archivos:
vmtest.c: Recopilación de las rutinas.
StdAfx.cpp, StdAfx.h: Código necesario para la compilación.
vmtest.exe: Ejecutable de prueba

Fuente
http://www.sinfocol.org/