You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确挂钩IWbemServices::ExecMethod以捕获WMI方法参数

如何正确挂钩IWbemServices::ExecMethod以捕获WMI方法参数

要搞定挂钩IWbemServices::ExecMethod来捕获WMI方法参数这件事,咱们得先理清为啥常规方法行不通,再一步步走正确的虚函数表(VTable)挂钩路子。

为啥常规挂钩方法不管用?

首先得明确,IWbemServices是个COM接口,它的方法都是通过虚函数表调用的,FastProx.dll虽然是WMI的核心实现库,但根本没导出ExecMethod这个符号——这就是你用GetProcAddress找不到它的原因。至于IDA断点没触发,大概率是因为WMI调用走了COM代理/存根,你没命中到真正执行逻辑的模块,或者断点设错了位置。

正确思路:虚函数表(VTable)挂钩

COM对象的所有接口方法都存在vtable里,所以咱们的核心玩法是:拿到IWbemServices的实例指针,找到它的vtable中ExecMethod对应的条目,直接对这个条目动手挂钩。

一步步实现详解

1. 先找准ExecMethod在vtable里的偏移

COM接口的vtable是严格按接口声明顺序排列的,IWbemServices继承自IUnknown,所以前3个条目固定是QueryInterface、AddRef、Release。要找到ExecMethod的偏移,有两个靠谱办法:

  • 翻wbemcli.h里的IWbemServices声明,数清楚从IUnknown之后到ExecMethod有多少个虚函数,按每个函数占8字节(64位)或4字节(32位)计算偏移;
  • 直接用IDA打开FastProx.dll,找到CWbemSvcWrapper::XWbemServices的vtable,一眼就能定位ExecMethod的位置——64位系统下它的偏移通常是0xC0,对应vtable数组的第36个元素(0xC0 / 8 = 36)。

2. 用MinHook实现挂钩(完整可参考代码)

下面结合你之前的Demo,整理出能跑通的挂钩代码,核心是先拿到vtable里的原始函数地址,再用MinHook替换成咱们的钩子函数:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <comutil.h>
#include <wbemcli.h>
#include "MinHook.h"

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

// 定义ExecMethod的函数原型,严格遵循COM调用约定
typedef HRESULT (STDMETHODCALLTYPE* ExecMethod_Type)(
    __RPC__in IWbemServices* This,
    __RPC__in const BSTR strObjectPath,
    __RPC__in const BSTR strMethodName,
    __RPC__in long lFlags,
    __RPC__in_opt IWbemContext* pCtx,
    __RPC__in_opt IWbemClassObject* pInParams,
    __RPC__deref_opt_inout_opt IWbemClassObject** ppOutParams,
    __RPC__deref_opt_inout_opt IWbemCallResult** ppCallResult
);

ExecMethod_Type OriginalExecMethod = nullptr;

// 咱们的钩子函数,在这里捕获参数
HRESULT STDMETHODCALLTYPE Hooked_ExecMethod(
    __RPC__in IWbemServices* This,
    __RPC__in const BSTR strObjectPath,
    __RPC__in const BSTR strMethodName,
    __RPC__in long lFlags,
    __RPC__in_opt IWbemContext* pCtx,
    __RPC__in_opt IWbemClassObject* pInParams,
    __RPC__deref_opt_inout_opt IWbemClassObject** ppOutParams,
    __RPC__deref_opt_inout_opt IWbemCallResult** ppCallResult
) {
    // 1. 先捕获并打印调用的类名和方法名
    wprintf(L"[捕获到WMI调用] 类路径: %ws | 方法名: %ws\n", strObjectPath, strMethodName);

    // 2. 解析输入参数(比如Win32_Process::Create的CommandLine参数)
    if (pInParams != nullptr) {
        VARIANT varParam;
        VariantInit(&varParam);
        if (SUCCEEDED(pInParams->Get(L"CommandLine", 0, &varParam, nullptr, nullptr))) {
            if (varParam.vt == VT_BSTR) {
                wprintf(L"[参数] CommandLine: %ws\n", varParam.bstrVal);
            }
            VariantClear(&varParam);
        }
    }

    // 3. 调用原始函数,保证WMI调用正常执行
    HRESULT hr = OriginalExecMethod(This, strObjectPath, strMethodName, lFlags, pCtx, pInParams, ppOutParams, ppCallResult);

    // 4. 也可以在这里处理返回结果
    if (SUCCEEDED(hr) && ppOutParams != nullptr && *ppOutParams != nullptr) {
        VARIANT varReturn;
        VariantInit(&varReturn);
        if (SUCCEEDED((*ppOutParams)->Get(L"ReturnValue", 0, &varReturn, nullptr, nullptr))) {
            wprintf(L"[返回值] ReturnValue: %d\n", varReturn.lVal);
            VariantClear(&varReturn);
        }
    }

    return hr;
}

int main() {
    // 初始化COM环境
    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hr)) {
        printf("CoInitializeEx失败: 0x%X\n", hr);
        return 1;
    }

    CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

    // 获取IWbemLocator实例
    IWbemLocator* pLoc = NULL;
    hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
    if (FAILED(hr)) {
        printf("CoCreateInstance失败: 0x%X\n", hr);
        CoUninitialize();
        return 1;
    }

    // 连接到WMI命名空间
    IWbemServices* pSvc = NULL;
    hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
    if (FAILED(hr)) {
        printf("ConnectServer失败: 0x%X\n", hr);
        pLoc->Release();
        CoUninitialize();
        return 1;
    }

    // 设置COM安全 blanket
    CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);

    // 获取IWbemServices的vtable指针
    void** vtable = *reinterpret_cast<void***>(pSvc);
    // 64位系统下ExecMethod的偏移是0xC0,对应vtable[36](0xC0 / 8 = 36)
    ExecMethod_Type targetFunc = reinterpret_cast<ExecMethod_Type>(vtable[36]);

    // 初始化MinHook并挂钩
    if (MH_Initialize() != MH_OK) {
        printf("MH_Initialize失败\n");
        goto Cleanup;
    }

    if (MH_CreateHook(reinterpret_cast<void*>(targetFunc), reinterpret_cast<void*>(Hooked_ExecMethod), reinterpret_cast<void**>(&OriginalExecMethod)) != MH_OK) {
        printf("MH_CreateHook失败\n");
        goto Cleanup;
    }

    if (MH_EnableHook(reinterpret_cast<void*>(targetFunc)) != MH_OK) {
        printf("MH_EnableHook失败\n");
        goto Cleanup;
    }

    printf("挂钩成功,触发WMI调用测试...\n");

    // 触发一次WMI调用,测试钩子是否生效
    BSTR className = SysAllocString(L"Win32_Process");
    BSTR methodName = SysAllocString(L"Create");
    IWbemClassObject* pClass = NULL;
    hr = pSvc->GetObject(className, 0, NULL, &pClass, NULL);
    if (SUCCEEDED(hr)) {
        IWbemClassObject* pInParamsClass = NULL;
        hr = pClass->GetMethod(methodName, 0, &pInParamsClass, NULL);
        if (SUCCEEDED(hr)) {
            IWbemClassObject* pInParams = NULL;
            hr = pInParamsClass->SpawnInstance(0, &pInParams);
            if (SUCCEEDED(hr)) {
                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);

                if (pOutParams) pOutParams->Release();
                pInParams->Release();
            }
            pInParamsClass->Release();
        }
        pClass->Release();
    }
    SysFreeString(className);
    SysFreeString(methodName);

Cleanup:
    // 清理资源
    if (pSvc) pSvc->Release();
    if (pLoc) pLoc->Release();
    MH_DisableHook(reinterpret_cast<void*>(targetFunc));
    MH_Uninitialize();
    CoUninitialize();

    system("pause");
    return 0;
}

几个关键注意点

  • 32/64位适配:vtable的偏移在32位系统下是0x60(对应vtable[24],0x60/4=24),要根据编译架构调整;
  • 跨架构COM调用:如果你的进程是32位,而WMI服务是64位(反之亦然),COM会用代理模块wbemprox.dll,这时候你需要把挂钩代码注入到目标架构的进程里;
  • 参数解析:pInParams是IWbemClassObject类型,要用它的Get方法提取参数,注意正确处理VARIANT类型的内存释放。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 03:10:28