windowshookwmi

Hooking IWbemServices::ExecMethod to get WMI Parameters


I'm trying to hook IWbemServices::ExecMethod so that I can capture WMI method parameters. Tools like WMIMon from GitHub don't capture parameters, and neither does Event Viewer.

I've searched and found that people say this function is implemented in FastProx.dll. Using IDA, I did find code related to it.

CWbemSvcWrapper::XWbemServices::ExecMethod in IDA.

But the problem is, there is nothing related to ExecMethod in FastProx.dll's symbol exports. GetProcAddressreturns that there is no such symbol. So I have no clue about how to hook it.

I've also tried setting breakpoints in IDA on ExecMethod and ExecMethodAsync, but the breakpoints don't fire.

How can I hook this function correctly? Should I get the COM instance and use the instance pointer to get the vtable entry pointer, then directly hook the vtable function?

Update

I've tried by written a WMI Demo to get the vtable address, but when i'm using IDA or CE to check if the address really the function, or the virtual function address, Visual Studio always shows only 3 IUnknown interface's function on pSvc, By using IDA i've successfully found the one that seems like a virtual function address.

enter image description here enter image description here

But i dont know if it really the IWbemServices::ExecMethod.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <Windows.h>
#include <comutil.h>
#include <wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "comsuppw.lib")

int main()
{
    CoInitializeEx(0, COINIT_MULTITHREADED);
    CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

    IWbemLocator* pLoc = NULL;
    CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);

    IWbemServices* pSvc = NULL;
    pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);

    CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL, EOAC_NONE);

    BSTR className = SysAllocString(L"Win32_Process");
    BSTR methodName = SysAllocString(L"Create");

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

    IWbemClassObject* pInParamsClass = NULL;
    pClass->GetMethod(methodName, 0, &pInParamsClass, NULL);

    IWbemClassObject* pInParams = NULL;
    pInParamsClass->SpawnInstance(0, &pInParams);

    VARIANT varCommand;
    VariantInit(&varCommand);
    varCommand.vt = VT_BSTR;
    varCommand.bstrVal = SysAllocString(L"calc.exe");
    pInParams->Put(L"CommandLine", 0, &varCommand, 0);
    VariantClear(&varCommand);

    IWbemClassObject* pOutParams = NULL;
    pSvc->ExecMethod(className, methodName, 0, NULL, pInParams, &pOutParams, NULL);

    std::cout << "pSvc (object address): 0x" << std::hex << pSvc << std::endl;
    void** vtable = *reinterpret_cast<void***>(pSvc);
    std::cout << "vtable address: 0x" << vtable << std::endl;

    using ExecMethodType = HRESULT(IWbemServices::*)(
        const BSTR, const BSTR, long, IWbemContext*,
        IWbemClassObject*, IWbemClassObject**, IWbemCallResult**);

    ExecMethodType execMethodPtr = &IWbemServices::ExecMethod;

    std::cout << "\npSvc->Exec:" << std::endl;
    for (int i = 0; i <= 2; ++i) {
        std::cout << (uintptr_t)((char*)vtable + 0xC0) << std::endl;
    }

    return 0;
}

enter image description here

Last Update

#include "pch.h"

#include <windows.h>
#include <comdef.h>
#include <wbemcli.h>
#include "Memory.h"
#include "MinHook.h"
#include "Utils.h"
#include "Helper.h"

#pragma comment(lib, "wbemuuid.lib")

typedef HRESULT (STDMETHODCALLTYPE* ExecMethod_Type)(
    __RPC__in IWbemServices* This,
    /* [in] */ __RPC__in const BSTR strObjectPath,
    /* [in] */ __RPC__in const BSTR strMethodName,
    /* [in] */ long lFlags,
    /* [in] */ __RPC__in_opt IWbemContext* pCtx,
    /* [in] */ __RPC__in_opt IWbemClassObject* pInParams,
    /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemClassObject** ppOutParams,
    /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemCallResult** ppCallResult);

ExecMethod_Type OriginalExecMethod = nullptr;

HRESULT STDMETHODCALLTYPE Hooked_ExecMethod(
    __RPC__in IWbemServices* This,
    /* [in] */ __RPC__in const BSTR strObjectPath,
    /* [in] */ __RPC__in const BSTR strMethodName,
    /* [in] */ long lFlags,
    /* [in] */ __RPC__in_opt IWbemContext* pCtx,
    /* [in] */ __RPC__in_opt IWbemClassObject* pInParams,
    /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemClassObject** ppOutParams,
    /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemCallResult** ppCallResult)
{
    /*HRESULT hr = This->ExecMethod(strObjectPath, strMethodName, lFlags, pCtx, pInParams, ppOutParams, ppCallResult);*/

    HRESULT hr = OriginalExecMethod(This, strObjectPath, strMethodName, lFlags, pCtx, pInParams, ppOutParams, ppCallResult);

    DebugOutputFormat("ExecMethod<%p>(%ws, %ws, %x)=%x\n", This, strObjectPath, strMethodName, lFlags, hr);
    return hr;
}

bool PreInit()
{
    DebugOutputFormat("PreInit started");

    if (MH_Initialize() != MH_OK)
    {
        DebugOutputFormat("MinHook init failed");
        return false;
    }

    HMODULE hFastProx = GetModuleHandleA("fastprox.dll");
    if (!hFastProx) {
        DebugOutputFormat("fastprox.dll not loaded");
        return false;
    }

    uintptr_t vtablePointer = ScanSignature(GetCurrentProcess(), "FastProx.dll", "50 45 1E F4 F8 7F 00 00 30 3B 1E F4 F8 7F 00 00 A0 ?? ?? ?? ?? 7F 00 00 B0 ?? ?? ?? ?? 7F 00 00 40 ?? 1E F4 F8");
    if (!vtablePointer) {
        DebugOutputFormat("VTable not found");
        return false;
    }

    uintptr_t* vtableEntry = (uintptr_t*)vtablePointer;
    uintptr_t execMethod = vtableEntry[24];
    uintptr_t execMethodAsync = vtableEntry[25];
    DebugOutputFormat("pSvc->ExecMethod 0x%p", execMethod);
    DebugOutputFormat("pSvc->ExecMethodAsync 0x%p", execMethodAsync);

    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);

    if (0 <= hr)
    {
        IWbemLocator* pLoc;

        if (0 <= (hr = CoCreateInstance(__uuidof(WbemLocator), 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pLoc))))
        {
            IWbemServices* pSvc = NULL;
            hr = pLoc->ConnectServer(
                const_cast<PWSTR>(L"ROOT\\CIMV2"),
                NULL,
                NULL,
                0,
                NULL,
                0,
                0,
                &pSvc
            );
            pLoc->Release();

            if (0 <= hr)
            {
                bool flag = IsPointerValid(pSvc, execMethod, execMethodAsync);
                assert(flag);

                pSvc->Release();
            }
        }
        CoUninitialize();
    }

    if (MH_CreateHook((LPVOID)execMethod, &Hooked_ExecMethod, reinterpret_cast<LPVOID*>(OriginalExecMethod)) != MH_OK)
    {
        OutputMessage("Hook create failed");
        return false;
    }

    if (MH_EnableHook((LPVOID)execMethod) != MH_OK) {
        OutputMessage("Hook enable failed");
        return false;
    }

    DebugOutputFormat("Hook installed");
    return true;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            PreInit();
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

enter image description here enter image description here

But nothing in logs

PreInit started
pSvc->ExecMethod 0x00007FF8F41B5FB0
pSvc->ExecMethodAsync 0x00007FF8F41B61B0
Hook installed
PreInit started
pSvc->ExecMethod 0x00007FF8F41B5FB0
pSvc->ExecMethodAsync 0x00007FF8F41B61B0
Hook installed
PreInit started
pSvc->ExecMethod 0x00007FF8F41B5FB0
pSvc->ExecMethodAsync 0x00007FF8F41B61B0
Hook installed

Solution

  • we can hook function in next way:

    first use separate file, if we use c++ but not c, for include wbemcli.h with CINTERFACE defined.

    #ifdef __cplusplus
    #define CINTERFACE
    #endif
    
    #include <wbemcli.h >
    
    
    LONG HookExecMethod(IWbemServices* pSvc, _Inout_ PVOID * ppPointer, _In_ PVOID pDetour)
    {
        *ppPointer = pSvc->lpVtbl->ExecMethod;
    
        HMODULE hmod;
        if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PCWSTR)*ppPointer, &hmod))
        {
            return DetourAttach(ppPointer, pDetour);
        }
    
        return GetLastError();
    }
    

    then in another file, where we include wbemcli.h without CINTERFACE

    namespace WBEMSERVICES {
        HRESULT ( STDMETHODCALLTYPE *ExecMethod )( 
            __RPC__in IWbemServices * This,
            /* [in] */ __RPC__in const BSTR strObjectPath,
            /* [in] */ __RPC__in const BSTR strMethodName,
            /* [in] */ long lFlags,
            /* [in] */ __RPC__in_opt IWbemContext *pCtx,
            /* [in] */ __RPC__in_opt IWbemClassObject *pInParams,
            /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemClassObject **ppOutParams,
            /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemCallResult **ppCallResult);
    };
    
    
    HRESULT STDMETHODCALLTYPE hook_ExecMethod( 
        __RPC__in IWbemServices * This,
        /* [in] */ __RPC__in const BSTR strObjectPath,
        /* [in] */ __RPC__in const BSTR strMethodName,
        /* [in] */ long lFlags,
        /* [in] */ __RPC__in_opt IWbemContext *pCtx,
        /* [in] */ __RPC__in_opt IWbemClassObject *pInParams,
        /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemClassObject **ppOutParams,
        /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemCallResult **ppCallResult)
    {
        HRESULT hr = WBEMSERVICES::ExecMethod(This, strObjectPath, strMethodName, lFlags, pCtx, pInParams, ppOutParams, ppCallResult);
    
        DbgPrint("ExecMethod<%p>(%ws, %ws, %x)=%x\n", This, strObjectPath, strMethodName, lFlags, hr);
        return hr;
    }
    
    
    LONG HookExecMethod(IWbemServices* pSvc, _Inout_ PVOID * ppPointer, _In_ PVOID pDetour);
    
    HRESULT DoHook()
    {
        HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); 
    
        if (0 <= hr)
        {
            IWbemLocator *pLoc;
    
            if (0 <= (hr = CoCreateInstance(__uuidof(WbemLocator), 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pLoc))))
            {
                IWbemServices *pSvc = NULL;
                hr = pLoc->ConnectServer(
                    const_cast<PWSTR>(L"ROOT\\CIMV2"), 
                    NULL,
                    NULL, 
                    0, 
                    NULL, 
                    0, 
                    0, 
                    &pSvc
                    );
                pLoc->Release();
    
                if (0 <= hr)
                {           
                    hr = HookExecMethod(pSvc, (void**)&WBEMSERVICES::ExecMethod, hook_ExecMethod);
    
                    pSvc->Release();
                }
            }
            CoUninitialize();
        }
    
        return hr;
    }
    

    the &IWbemServices::ExecMethod really create stub function to vcall, not want you need. when pointer in vatable really point to

      HRESULT WINAPI CWbemSvcWrapper::XWbemServices::ExecMethod(
      /* [in] */ __RPC__in const BSTR strObjectPath,
        /* [in] */ __RPC__in const BSTR strMethodName,
        /* [in] */ long lFlags,
        /* [in] */ __RPC__in_opt IWbemContext *pCtx,
        /* [in] */ __RPC__in_opt IWbemClassObject *pInParams,
        /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemClassObject **ppOutParams,
        /* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemCallResult **ppCallResult)