Go back

Contents

WMI detection methods
Background
1. Generic WMI queries
2. Escape from tracking using WMI
2.1. Start process using WMI
2.2. Start process using Task Scheduler via WMI
3. Check the last boot time
4. Check the network adapter last reset time
Signature recommendations
Countermeasures
Credits


WMI detection methods

Windows Management Interface (WMI) queries are another way to get OS and hardware information. WMI uses COM interfaces and their methods.


Background

Standard COM functions are used to process queries. They are called in the sequence described below and can be split into 6 steps.

1. COM initialization:

  • CoInitialize/CoInitializeEx

2. Create the required interface instance:

  • CoCreateInstance/CoCreateInstanceEx

3. Connect to the particular services via the interface instance with the following function:

  • ConnectServer

4. Get methods of the services and set their arguments with these functions:

  • Method (to get methods)
  • Put (to set arguments)

5. Retrieve information from the services and execute the methods of the services with the functions below. The functions on the left are proxies for the functions on the right - which are called internally:

  • ExecQuery -> IWbemServices_ExecQuery (retrieve information)
  • ExecMethod -> IWbemServices_ExecMethod (execute method)
  • ExecMethodAsync -> IWbemServices_ExecMethodAsync (execute method)

6. Examine the result of the query with the following functions:

  • [enumerator]->Next
  • [object]->Get

To see how the described theory is applied to practice, please check the examples below.


1. Generic WMI queries

As WMI provides another way to collect system information, it can be used to perform evasion techniques described in other articles, for example:

Code sample

/*
Check number of cores using WMI
*/
BOOL number_cores_wmi()
{
  IWbemServices *pSvc = NULL;
  IWbemLocator *pLoc = NULL;
  IEnumWbemClassObject *pEnumerator = NULL;
  BOOL bStatus = FALSE;
  HRESULT hRes;
  BOOL bFound = FALSE;

  // Init WMI
  bStatus = InitWMI(&pSvc, &pLoc);
  if (bStatus)
  {
    // If success, execute the desired query
    bStatus = ExecWMIQuery(&pSvc, &pLoc, &pEnumerator, _T("SELECT * FROM Win32_Processor"));
    if (bStatus)
    {
      // Get the data from the query
      IWbemClassObject *pclsObj = NULL;
      ULONG uReturn = 0;
      VARIANT vtProp;

      // Iterate over our enumator
      while (pEnumerator)
      {
        hRes = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn)
          break;

        // Get the value of the Name property
        hRes = pclsObj->Get(_T("NumberOfCores"), 0, &vtProp, 0, 0);
        if (V_VT(&vtProp) != VT_NULL) {

          // Do our comparaison
          if (vtProp.uintVal < 2) {
            bFound = TRUE; break;
          }

          // release the current result object
          VariantClear(&vtProp);
          pclsObj->Release();
        }
      }

      // Cleanup
      pEnumerator->Release();
      pSvc->Release();
      pLoc->Release();
      CoUninitialize();
    }
  }

  return bFound;
}


/*
Check hard disk size using WMI
*/
BOOL disk_size_wmi()
{
  IWbemServices *pSvc = NULL;
  IWbemLocator *pLoc = NULL;
  IEnumWbemClassObject *pEnumerator = NULL;
  BOOL bStatus = FALSE;
  HRESULT hRes;
  BOOL bFound = FALSE;
  INT64 minHardDiskSize = (80LL * (1024LL * (1024LL * (1024LL))));

  // Init WMI
  bStatus = InitWMI(&pSvc, &pLoc);
  if (bStatus)
  {
    // If success, execute the desired query
    bStatus = ExecWMIQuery(&pSvc, &pLoc, &pEnumerator, _T("SELECT * FROM Win32_LogicalDisk"));
    if (bStatus)
    {
      // Get the data from the query
      IWbemClassObject *pclsObj = NULL;
      ULONG uReturn = 0;
      VARIANT vtProp;

      // Iterate over our enumator
      while (pEnumerator)
      {
        hRes = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn)
          break;

        // Get the value of the Name property
        hRes = pclsObj->Get(_T("Size"), 0, &vtProp, 0, 0);
        if (V_VT(&vtProp) != VT_NULL) {

          // Do our comparaison
          if (vtProp.llVal < minHardDiskSize) { // Less than 80GB
            bFound = TRUE; break;
          }

          // release the current result object
          VariantClear(&vtProp);
          pclsObj->Release();
        }
      }

      // Cleanup
      pEnumerator->Release();
      pSvc->Release();
      pLoc->Release();
      CoUninitialize();
    }
  }

  return bFound;
}

Credits for this code sample: al-khaser project


Code sample (PowerShell)

(Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber).SerialNumber

Signature recommendations

If the following function contains a 3rd argument from the table column "Query":

  • IWbemServices_ExecQuery(..., query, ...)

then it’s an indicator of the application trying to use the evasion technique.


Detections table

The following WMI queries may be used to detect virtual environment:
Query Field Value Detect Comments
SELECT * FROM Win32_Processor NumberOfCores < 2 [general]
ProcessorId [empty]
SELECT * FROM Win32_LogicalDisk Size < 60GB
SELECT * FROM Win32_BaseBoard SerialNumber None
Version None
SELECT * FROM MSAcpi_ThermalZoneTemperature CurrentTemperature "Not supported"
SELECT * FROM Win32_PnPEntity DeviceId PCI\VEN_80EE&DEV_CAFE VirtualBox
IDE\CDROOMVBOX
IDE\DISKVBOX*
VEN_VMWARE VMware
PROD_VMWARE_VIRTUAL
SELECT * FROM Win32_NetworkAdapterConfiguration MACAddress 08:00:27 VirtualBox See "Check if MAC address is specific" section in "Network" chapter
00:1C:42 Parallels
00:05:69 VMware
00:0C:29
00:1C:14
00:50:56
00:16:E3 XEN
SELECT * FROM Win32_Bios Serial Number VMware- VMware
0 VirtualBox
Version INTEL - 6040000 VMware See "SystemBiosVersion" in "Check if particular registry keys contain specified strings" section in "Registry" chapter
BOCHS BOCHS
PARALLELS Parallels
QEMU QEMU
VBOX VirtualBox
SELECT * FROM Win32_ComputerSystem Model VMware VMware
VirtualBox VirtualBox
Manufacturer VMware VMware
innotek GmbH VirtualBox
SELECT * FROM Win32_VideoController AdapterCompatibility VMware VMware
Oracle Corporation VirtualBox
Caption VMware VMware
VirtualBox VirtualBox
Description VMware VMware
VirtualBox VirtualBox
Name VMware VMware
VirtualBox VirtualBox
SELECT * FROM Win32_PointingDevice Description VMware VMware

Queries listed in the table are not the only ones possible, and are presented to give an idea of how they work and what information can be retrieved with these calls.

Countermeasures

Countermeasures depend on the particular checks implemented via the WMI method and they are the same as for the corresponding methods described in the relevant articles. Additionally, you must restart the “winmgmt” service.



2. Escape from tracking using WMI

WMI provides a way to create new processes and to schedule tasks. Sandboxes usually use the CreateProcessInternalW function hooking to track child processes. However, when you create the process using WMI the function CreateProcessInternalW is not called in the parent process. Therefore, the processes created using WMI may not be tracked by a sandbox and their behavior will not be recorded.

2.1. Start process using WMI

You can create a new process with WMI using the “Win32_Process” class with the method “Create”.

Code sample

// Initialize COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);

//  Set general COM security levels
hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
if (FAILED(hres) && hres != RPC_E_TOO_LATE)
    break;

// create an instance of WbemLocator
CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&wbemLocator);
wbemLocator->ConnectServer(CComBSTR("ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &wbemServices);

// get Win32_Process object
wbemServices->GetObject(CComBSTR("Win32_Process"), 0, NULL, &oWin32Process, &callResult);
wbemServices->GetObject(CComBSTR("Win32_ProcessStartup"), 0, NULL, &oWin32ProcessStartup, &callResult);
oWin32Process->GetMethod(CComBSTR("Create"), 0, &oMethCreate, &oMethCreateSignature);
oMethCreate->SpawnInstance(0, &instWin32Process);
oWin32ProcessStartup->SpawnInstance(0, &instWin32ProcessStartup);
// set startup information for process
instWin32ProcessStartup->Put(CComBSTR("CreateFlags"), 0, &varCreateFlags, 0);
instWin32Process->Put(CComBSTR("CommandLine"), 0, &varCmdLine, 0);
instWin32Process->Put(CComBSTR("CurrentDirectory"), 0, &varCurDir, 0);
CComVariant varStartupInfo(instWin32ProcessStartup);
instWin32Process->Put(CComBSTR("ProcessStartupInformation"), 0, &varStartupInfo, 0);
wbemServices->ExecMethod(CComBSTR("Win32_Process"), CComBSTR("Create"), 0, NULL, instWin32Process, &pOutParams, &callResult);

Code sample is taken from InviZzzible tool


Signature recommendations

If one of the following functions is called with the 2nd argument “Win32_Process” and the 3rd argument “Create”:

  • IWbemServices_ExecMethod(..., BSTR("Win32_Process"), BSTR("Create"), ...)
  • IWbemServices_ExecMethodAsync(..., BSTR("Win32_Process"), BSTR("Create"), ...)

then it’s an indicator of the application trying to use the evasion technique.

Countermeasures

If you use a kernel-mode monitor, hook target functions or register callback on the process creation with PsSetCreateProcessNotifyRoutineEx.


2.2. Start process using Task Scheduler via WMI (Windows 7)

The technique is essentially the same as described in the “Deferred execution using Task Scheduler” section in the “Timing” chapter. WMI just provides another way to schedule a task.

You can create a new task with WMI using the “Win32_ScheduledJob” class with the method “Create”.

However, the “Win32_ScheduledJob” WMI class was designed to work with the AT command, which is deprecated since Windows 8.

In Windows 8 and higher, you can only create scheduled jobs with WMI if the registry key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\Configuration” has a value “EnableAt”=”1” of type REG_DWORD. Therefore, this technique is unlikely to be found in the wild.

Code sample (VB)

strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=Impersonate}!\\" & strComputer & "\root\cimv2") 
Set objSWbemDateTime = CreateObject("WbemScripting.SWbemDateTime")
objSWbemDateTime.SetVarDate(DateAdd("n", 1, Now()))
Set objNewJob = objWMIService.Get("Win32_ScheduledJob")
errJobCreate = objNewJob.Create("malware.exe", objSWbemDateTime.Value, False, , , True, "MaliciousJob") 

Signature recommendations

If one of the following functions is called with the 2nd argument “Win32_ScheduledJob” and the 3rd argument “Create”:

  • IWbemServices_ExecMethod(..., BSTR("Win32_ScheduledJob"), BSTR("Create"), ...)
  • IWbemServices_ExecMethodAsync(..., BSTR("Win32_ScheduledJob"), BSTR("Create"), ...)

then it’s an indicator of the application trying to use the evasion technique.

Countermeasures

Use a kernel-mode monitor, and register callback on the process creation with PsSetCreateProcessNotifyRoutineEx.



3. Check the last boot time

If the last boot time is queried immediately after restoring a VM from a snapshot, the WMI database may contain the value saved at the moment the VM snapshot was created. If the snapshot was created a year ago, the calculated system uptime will be a year as well even if a sandbox updates the last boot time.

This fact can be used to detect a virtual machine restored from a snapshot. Also, any anomalies in the last boot time can be used as sandbox indicators:

  • The system uptime is too big (months or even years)
  • The system uptime is to small (less than several minutes)
  • The last boot time obtained using other methods differs from the last boot time obtained using WMI

Code sample (VB)

strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")
 
For Each objOS in colOperatingSystems
    dtmBootup = objOS.LastBootUpTime
    dtmLastBootUpTime = WMIDateStringToDate(dtmBootup)
    dtmSystemUptime = DateDiff("n", dtmLastBootUpTime, Now)
    Wscript.Echo "System uptime minutes: " & dtmSystemUptime 
Next
 
Function WMIDateStringToDate(dtm)
    WMIDateStringToDate =  CDate(Mid(dtm, 5, 2) & "/" & _
        Mid(dtm, 7, 2) & "/" & Left(dtm, 4) & " " & Mid (dtm, 9, 2) & ":" & _
        Mid(dtm, 11, 2) & ":" & Mid(dtm, 13, 2))
End Function

Code sample is taken from Microsoft Docs


Signature recommendations

If the following function is called with the 3rd argument BSTR(“Win32_OperatingSystem”):

  • IWbemServices_ExecQuery(..., BSTR("Win32_OperatingSystem"), ...)

then it’s a possible indicator of the application trying to use the evasion technique.

Countermeasures

  • Adjust the KeBootTime value
  • Reset the WMI repository or restart the "winmgmt" service after you adjust the KeBootTime value


4. Check the network adapters last reset time

We need to check if there are any adapters that were last reset a long time ago. This may indicate the application is running in a virtual machine restored from a snapshot.

Code sample (VB)

strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_NetworkAdapter")
 
For Each objOS in colNetworkAdapters
    dtmLastReset = objOS.TimeOfLastReset
    dtmLastResetTime = WMIDateStringToDate(dtmLastReset)  'WMIDateStringToDate function from the previous example
    dtmAdapterUptime = DateDiff("n", dtmLastResetTime, Now)
    Wscript.Echo "Adapter uptime minutes: " & dtmAdapterUptime 
Next

Signature recommendations

If the following function is called with the 3rd argument BSTR(“Win32_OperatingSystem”):

  • IWbemServices_ExecQuery(..., BSTR("Win32_NetworkAdapter"), ...)

then it’s a possible indicator of the application trying to use the evasion technique.

Countermeasures

  • Ensure an adequate last reset time for the network adapters
  • Reset the WMI repository or restart the "winmgmt" service


Countermeasures

Countermeasures are presented in the appropriate sub-sections above.


Credits


Go back