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).
La rutina puede ser observada a continuación:
La rutina puede ser observada a continuación:
Se obtiene el siguiente resultado (True indica detección exitosa):
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/