Home > Software design >  Access violation in official Microsoft WMI example
Access violation in official Microsoft WMI example

Time:01-14

I'm trying to learn how the WMI works, but the default examples given have so far been atrocious.

Here's the example for calling the Create method of the Win32_Process class:

https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--calling-a-provider-method

I have added proper error handling to this, we store the HRESULT of each call in a variable hres and check if the calls failed. As such:

        hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
        if (FAILED(hres))
        {
            wprintf("Failed to get class. Error code = 0x%lx\n", hres);
            return hres;
        }

The code executes correctly right up until here:

    // Create the values for the in parameters
    VARIANT varCommand;
    varCommand.vt = VT_BSTR;
    varCommand.bstrVal = _bstr_t(L"notepad.exe");

    // Store the value for the in parameters
    hres = pClassInstance->Put(L"CommandLine", 0,
        &varCommand, 0);
    wprintf(L"The command is: %s\n", V_BSTR(&varCommand));

Where the pClassInstance->Put throws 'ol c5.

At this point, hres is S_OK for the call to SpawnInstance but these are the pointers we have for the class instances:

 pClass 0x000001c04e73fca0 IWbemClassObject *
-   pClassInstance  0x000001c04e749d60 IWbemClassObject *
-       IUnknown    {...}   IUnknown
-       __vfptr 0x00007ff9f8d0ee98 {fastprox.dll!const CWbemInstance::`vftable'{for `_IWmiObject'}} {0x00007ff9f8c6f450 {fastprox.dll!CWbemObject::QueryInterface(void)}, ...}  void * *
        [0x00000000]    0x00007ff9f8c6f450 {fastprox.dll!CWbemObject::QueryInterface(void)} void *
        [0x00000001]    0x00007ff9f8c907d0 {fastprox.dll!CWbemObject::AddRef(void)} void *
        [0x00000002]    0x00007ff9f8c8ffd0 {fastprox.dll!CWbemObject::Release(void)}    void *
 pInParamsDefinition 0x000001c04e743ca0 IWbemClassObject *

And varCommand:

 varCommand BSTR = 0x000001c04e74ffe8 tagVARIANT

The call stack:

    oleaut32.dll!SysAllocString()
    vfbasics.dll!AVrfpSysAllocString()
    wbemcomn.dll!CVar::SetVariant()
    fastprox.dll!CWbemInstance::Put()
>   Ele.exe!WMIConnection::InvokeMethod()

So it appears that bstrVal isn't being properly set, I think? I tried initializing it first with VariantInit, and I also tried dynamically allocating it on the heap instead. Neither resolved the issue:

        VARIANT varCommand;
        VariantInit(&varCommand);
        varCommand.vt = VT_BSTR;
        varCommand.bstrVal = _bstr_t(L"notepad.exe");

I also tried manually zeroing out the Variant buffer, to no effect. This is what we have for bstrVal in the memory dump when the access violation occurs:

bstrVal 0x000001c04e74ffe8 <Error reading characters of string.>    wchar_t *
               <Unable to read memory>                  wchar_t

CodePudding user response:

I figured it out. There are several forum posts on the internet asking about this example, with no solutions given, so I am very happy to provide this now.

The Microsoft example is using the incorrect classes.

In the Microsoft example, they attempt to call the Put method on a class instance of Win32_Process to set the parameters.

This is incorrect. We need to set the parameters via first getting the class method definition for Win32_Process::Create and then setting its parameters inside a new instance of Win32_Process::Create.

We additionally need to construct an instance of the Win32_ProcessStartup class object as it's a required input parameter for Win32_Process::Create.

In the example below I will populate one field of the Win32_ProcessStartup class instance, you can figure out the rest.

So this code from the Microsoft example:

 IWbemClassObject* pClass = NULL;
    hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);

    IWbemClassObject* pInParamsDefinition = NULL;
    hres = pClass->GetMethod(MethodName, 0, 
        &pInParamsDefinition, NULL);

    IWbemClassObject* pClassInstance = NULL;
    hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);

    // Create the values for the in parameters
    VARIANT varCommand;
    varCommand.vt = VT_BSTR;
    varCommand.bstrVal = _bstr_t(L"notepad.exe");

    // Store the value for the in parameters
    hres = pClassInstance->Put(L"CommandLine", 0,
        &varCommand, 0);
    wprintf(L"The command is: %s\n", V_BSTR(&varCommand));

Becomes (sans error handling for readability):

        // Get the class object
        hres = pClass->GetMethod(lpwMethodName, 0,
            &pInParamsDefinition, NULL);

        hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);

        // Get the Win32_ProcessStartup class object
        IWbemClassObject* pStartupObject = NULL;

        hres = pSvc->GetObject(_bstr_t(L"Win32_ProcessStartup"), 0, NULL, &pStartupObject, NULL);

        // Create an instance of the Win32_ProcessStartup class object
        IWbemClassObject* pStartupInstance = NULL;

        hres = pStartupObject->SpawnInstance(0, &pStartupInstance);

        // Create the value for the ShowWindow variable of the Win32_ProcessStartup class
        VARIANT varParams;
        VariantInit(&varParams);
        varParams.vt = VT_I2;
        varParams.intVal = SW_SHOW;
        
        // And populate it
        hres = pStartupInstance->Put(_bstr_t(L"ShowWindow"), 0, &varParams, 0);

        // Get the method definition for Win32_Process::Create and store it in pInParamsDefinition
        hres = pClass->GetMethod(_bstr_t(lpwMethodName), 0, &pInParamsDefinition, NULL);

        // Spawn an instance of the Create method and store it in pParamsInstance
        IWbemClassObject* pParamsInstance = NULL;

        hres = pInParamsDefinition->SpawnInstance(0, &pParamsInstance);

        // Now we can set the parameters without error
        hres = pParamsInstance->Put(_bstr_t(L"CurrentDirectory"), 0, pvCurrentDirectory, 0);

Please note that all of these hres returns should be checked for failure.

The definition for Win32_ProcessStartup is provided here:

https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processstartup

And the definition for the Create method of Win32_Process:

https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-process

CodePudding user response:

varCommand.bstrVal = _bstr_t(L"notepad.exe");

This line of code creates a temporary _bstr_t object that goes out of scope, destroying the allocated BSTR memory, after varCommand.bstrVal has been assigned to. Thus, varCommand.bstrVal is left dangling, pointing at invalid memory, when varCommand is passed to pClassInstance->Put(). That is undefined behavior.

Use this instead to keep the BSTR alive until you are actually done using it:

_bstr_t str(L"notepad.exe");
varCommand.bstrVal = str;

Otherwise, use _variant_t instead:

// Create the values for the in parameters
_variant_t varCommand(L"notepad.exe");

// Store the value for the in parameters
hres = pClassInstance->Put(L"CommandLine", 0, &varCommand, 0);
wprintf(L"The command is: %s\n", V_BSTR(&varCommand));
  • Related