/*! * @file wmi_interface.h * @brief Declarations for functions that deal directly with WMI * via the COM interfaces (hence the .cpp extension). */ extern "C" { #include "extapi.h" #include <inttypes.h> #include "wmi_interface.h" } #include <WbemCli.h> #include <comutil.h> #pragma comment(lib, "wbemuuid.lib") #pragma comment(lib, "comsuppw.lib") #define PATH_SIZE 512 #define FIELD_SIZE 512 #define ENUM_TIMEOUT 5000 /*! @brief The GUID of the Directory Search COM object. */ //static const IID IID_IDirectorySearch = { 0x109BA8EC, 0x92F0, 0x11D0, { 0xA7, 0x90, 0x00, 0xC0, 0x4F, 0xD8, 0xD5, 0xA8 } }; /*! * @brief Perform a WMI query. * @param lpwDomain Name of the domain that is to be queried. * @param lpwQuery The filter to use when reading objects (LDAP style). * @param response The response \c Packet to add the results to. */ DWORD wmi_query(LPCWSTR lpwDomain, LPWSTR lpwQuery, Packet* response) { HRESULT hResult; WCHAR cbPath[PATH_SIZE]; swprintf_s(cbPath, PATH_SIZE - 1, L"root\\%s", lpwDomain); if ((hResult = CoInitializeEx(NULL, COINIT_MULTITHREADED)) == S_OK) { IWbemLocator* pLocator = NULL; IWbemServices* pServices = NULL; IEnumWbemClassObject* pEnumerator = NULL; IWbemClassObject* pObj = NULL; Tlv* valueTlvs = NULL; char* values = NULL; VARIANT** fields = NULL; do { if (FAILED(hResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0))) { dprintf("[WMI] Failed to initialize security: %x", hResult); break; } if (FAILED(hResult = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pLocator)))) { dprintf("[WMI] Failed to create WbemLocator: %x", hResult); break; } if (FAILED(hResult = pLocator->ConnectServer(cbPath, NULL, NULL, NULL, WBEM_FLAG_CONNECT_USE_MAX_WAIT, NULL, NULL, &pServices))) { dprintf("[WMI] Failed to create WbemServices at %S: %x", cbPath, hResult); break; } if (FAILED(hResult = pServices->ExecQuery(L"WQL", lpwQuery, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator))) { dprintf("[WMI] Failed to create Enumerator for query %S: %x", lpwQuery, hResult); break; } ULONG numFound; if (FAILED(hResult = pEnumerator->Next(ENUM_TIMEOUT, 1, &pObj, &numFound))) { dprintf("[WMI] Failed to get the first query element: %x", lpwQuery, hResult); break; } // get the names of the fields out of the first object before doing anything else. LPSAFEARRAY pFieldArray = NULL; if (FAILED(hResult = pObj->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &pFieldArray))) { dprintf("[WMI] Failed to get field names: %x", hResult); break; } // lock the array if (FAILED(hResult = SafeArrayLock(pFieldArray))) { dprintf("[WMI] Failed to get array dimension: %x", hResult); break; } do { dprintf("[WMI] Array dimensions: %u", SafeArrayGetDim(pFieldArray); // this array is just one dimension, let's get the bounds of the first dimension LONG lBound, uBound; if (FAILED(hResult = SafeArrayGetLBound(pFieldArray, 1, &lBound)) || FAILED(hResult = SafeArrayGetLBound(pFieldArray, 1, &uBound))) { dprintf("[WMI] Failed to get array dimensions: %x", hResult); break; } LONG fieldCount = uBound - lBound + 1; dprintf("[WMI] Query results in %u fields", fieldCount); fields = (VARIANT**)malloc(fieldCount * sizeof(VARIANT**)); valueTlvs = (Tlv*)malloc(fieldCount * sizeof(Tlv)); values = (char*)malloc(fieldCount * FIELD_SIZE); memset(fields, 0, fieldCount * sizeof(VARIANT**)); memset(valueTlvs, 0, fieldCount * sizeof(Tlv)); memset(values, 0, fieldCount * FIELD_SIZE); for (LONG i = 0; i < fieldCount; ++i) { char* fieldName = values + (i * FIELD_SIZE); LONG indices[2] = { 0, i }; SafeArrayPtrOfIndex(pFieldArray, indices, (void**)&fields[i]); _bstr_t bstr(fields[i]->bstrVal); strncpy_s(fieldName, FIELD_SIZE, (const char*)bstr, FIELD_SIZE - 1); valueTlvs[i].header.type = TLV_TYPE_EXT_WMI_FIELD; valueTlvs[i].header.length = (UINT)strlen(fieldName) + 1; valueTlvs[i].buffer = (PUCHAR)fieldName; dprintf("[WMI] Added header field: %s", fieldName); } // add the field names to the packet packet_add_tlv_group(response, TLV_TYPE_EXT_WMI_FIELDS, valueTlvs, fieldCount); // with that horrible pain out of the way, let's actually grab the data do { if (FAILED(hResult)) { break; } memset(valueTlvs, 0, fieldCount * sizeof(Tlv)); memset(values, 0, fieldCount * FIELD_SIZE); for (LONG i = 0; i < fieldCount; ++i) { char* value = values + (i * FIELD_SIZE); valueTlvs[i].header.type = TLV_TYPE_EXT_WMI_VALUE; valueTlvs[i].buffer = (PUCHAR)value; VARIANT varValue; VariantInit(&varValue); _bstr_t field(fields[i]->bstrVal); if (SUCCEEDED(pObj->Get(field, 0, &varValue, NULL, NULL))) { _variant_t v(varValue); switch (v.vt) { case VT_BOOL: strncpy_s(value, FIELD_SIZE, v.boolVal == VARIANT_TRUE ? "true" : "false", FIELD_SIZE - 1); break; case VT_INT: _snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRId32, (INT)v); break; case VT_INT_PTR: _snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRId64, (INT_PTR)v); break; case VT_UINT: _snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRIu32, (UINT)v); break; case VT_UINT_PTR: _snprintf_s(value, FIELD_SIZE, FIELD_SIZE - 1, "%"PRIu64, (UINT_PTR)v); break; case VT_BSTR: case VT_LPSTR: case VT_LPWSTR: // not sure if this is correct strncpy_s(value, FIELD_SIZE, (char*)(_bstr_t)v.bstrVal, FIELD_SIZE - 1); break; // TODO more types, such as floats, dates, etc. default: // ignore the value for other types break; } } valueTlvs[i].header.length = (UINT)strlen(value) + 1; dprintf("[WMI] Added value for %s: %s", (char*)_bstr_t(fields[i]->bstrVal), value); } // add the field values to the packet packet_add_tlv_group(response, TLV_TYPE_EXT_WMI_VALUES, valueTlvs, fieldCount); pObj->Release(); pObj = NULL; } while ((hResult = pEnumerator->Next(ENUM_TIMEOUT, 1, &pObj, &numFound)) != WBEM_S_FALSE); } while (0); SafeArrayUnlock(pFieldArray); if (SUCCEEDED(hResult)) { hResult = S_OK; } } while (0); if (fields) { free(fields); } if (values) { free(values); } if (valueTlvs) { free(valueTlvs); } if (pObj) { pObj->Release(); } if (pEnumerator) { pEnumerator->Release(); } if (pServices) { pServices->Release(); } if (pLocator) { pLocator->Release(); } CoUninitialize(); dprintf("[WMI] Things appeard to go well!"); } else { dprintf("[WMI] Failed to initialize COM"); } return (DWORD)hResult; }