Contenido principal

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

Enero 11, 2013

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:

Fabricante Subcadena
VMware VMware
Oracle VM VirtualBox VBOX
Windows Virtual PC DiskVirtual
Windows Virtual PC VIRTUAL
QEMU QEMU

La rutina puede ser observada a continuación:

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 Subcadena
Oracle VM VirtualBox VBOX
Windows Virtual PC A M I - 8000914
QEMU BOCHS

SELECT Model FROM Win32_ComputerSystem

Fabricante Subcadena
VMware VMware
Oracle VM VirtualBox VirtualBox
Windows Virtual PC Virtual Machine
QEMU Bochs

SELECT DeviceID FROM Win32_CDROMDrive

Fabricante Subcadena
Oracle VM VirtualBox VBOX
QEMU QEMU

SELECT PNPDeviceID FROM Win32_DiskDrive

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

SELECT Description FROM CIM_LogicalDevice

Fabricante Subcadena
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:
:arrow: vmtest.c: Recopilación de las rutinas.
:arrow: StdAfx.cpp, StdAfx.h: Código necesario para la compilación.
:arrow: vmtest.exe: Ejecutable de prueba (Para el arriesgado).

Archivado en: Seguridad, Sistemas operativos |

2 comentarios

  1. Kagure Enero 13, 2013 @ 12:53 pm

    Que bueno leerlo en el nuevo año y como siempre con buenos aportes a la seguridad colombiana.
    Éxitos

  2. Ivan Mayo 7, 2013 @ 12:45 pm

    Muy buena explicacion, exelente, muchas gracias por compartir, seguridad colombiana.

Deja un comentario