使用 WMI 查询安全软件信息
在这篇文章中,我们将详细介绍如何使用 Windows Management Instrumentation (WMI) API 来查询当前计算机上安装的安全软件的基本信息。我们将分析代码的各个部分,并解释每个步骤所涉及的技术和原理。
一、什么是 WMI?
WMI 是 Windows Management Instrumentation 的缩写,是微软提供的用于管理 Windows 系统的一种技术。它提供了一种标准的接口,允许开发者通过编程方式获取和操作 Windows 操作系统的各种信息,包括硬件、软件、网络配置等。WMI 是基于 CIM(Common Information Model)标准的实现,它使用了面向对象的方式来组织和表示系统的信息。
二、相关函数解释
以下是代码中使用的一些函数以及它们的参数的解释,摘抄了 MSDN 文档的相关内容:
CoInitializeEx
HRESULT CoInitializeEx(
LPVOID pvReserved,
DWORD dwCoInit
);
pvReserved
:保留参数,必须为NULL
。dwCoInit
:初始化标志,指定了线程的并发模型。
详情请参阅:CoInitializeEx function (combaseapi.h)
CoCreateInstance
HRESULT CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID *ppv
);
rclsid
:类标识符(CLSID)。pUnkOuter
:用于控制对象的外部创建的指针。通常为NULL
。dwClsContext
:指定服务器的执行环境。riid
:接口标识符(IID)。ppv
:接收指向所请求接口的指针的地址。
详情请参阅:CoCreateInstance function (combaseapi.h)
CoSetProxyBlanket
HRESULT CoSetProxyBlanket(
IUnknown *pProxy,
DWORD dwAuthnSvc,
DWORD dwAuthzSvc,
OLECHAR *pServerPrincName,
DWORD dwAuthnLevel,
DWORD dwImpLevel,
RPC_AUTH_IDENTITY_HANDLE pAuthInfo,
DWORD dwCapabilities
);
pProxy
:指向要设置代理参数的接口指针的指针。dwAuthnSvc
:身份验证服务。dwAuthzSvc
:授权服务。pServerPrincName
:服务器的主体名称。dwAuthnLevel
:身份验证级别。dwImpLevel
:实现级别。pAuthInfo
:身份验证信息。dwCapabilities
:代理的功能标志。
详情请参阅:CoSetProxyBlanket function (combaseapi.h)
IWbemServices::ExecQuery
HRESULT ExecQuery(
[in] const BSTR strQueryLanguage,
[in] const BSTR strQuery,
[in] long lFlags,
[in] IWbemContext *pCtx,
[out] IEnumWbemClassObject **ppEnum
);
strQueryLanguage
:查询语言,例如 "WQL"。strQuery
:查询字符串。lFlags
:标志,指定查询的行为。pCtx
:指向 WMI 上下文对象的指针。ppEnum
:接收指向查询结果集的指针的地址。
详情请参阅:IWbemServices::ExecQuery method (wbemcli.h)
IEnumWbemClassObject::Next
HRESULT Next(
[in] LONG lTimeout,
[in] ULONG uCount,
[out] IWbemClassObject **apObj,
[out] ULONG *puReturned
);
lTimeout
:超时值,以毫秒为单位,或者可以指定为WBEM_INFINITE
以表示无限超时。uCount
:要检索的对象数。apObj
:接收对象的数组。puReturned
:实际返回的对象数。
详情请参阅:IEnumWbemClassObject::Next method (wbemcli.h)
这些函数和参数提供了在代码中使用 WMI API 时的基本操作和配置。通过了解这些函数的作用和参数的含义,可以更好地理解代码的功能和实现原理。
三、代码解析
让我们逐步分析代码并解释其中的各个部分。
3.1 初始化 COM
HRESULT hres = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
这一行代码初始化了 COM(Component Object Model)库,COM 是一种微软提供的用于组件化软件的技术,它允许不同的组件通过接口进行通信和交互。
3.2 创建 WMI Locator
IWbemLocator* pLoc = NULL;
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
这段代码创建了一个 WMI Locator 对象,它是用于定位和连接 WMI 服务的接口。 CLSID_WbemLocator 是 WMI Locator 的类标识符,IID_IWbemLocator 是 WMI Locator 的接口标识符。
3.3 连接 WMI 服务
IWbemServices* pSvc = NULL;
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\SecurityCenter2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
这段代码连接到了 WMI 服务,并指定了要连接的命名空间为 ROOT\SecurityCenter2。在这个命名空间中,我们可以查询关于安全中心的信息,包括安装的安全软件信息。
3.4 设置代理参数
hres = CoSetProxyBlanket(
pSvc,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
这段代码设置了用于 WMI 服务的代理参数,包括身份验证方式、权限级别等。
3.5 执行查询
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM AntiVirusProduct"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator
);
这段代码执行了一个 WMI 查询,查询的语句是 “SELECT * FROM AntiVirusProduct”,表示查询 AntiVirusProduct 类的所有实例。
3.6 处理查询结果
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
// 获取属性值并输出
// ...
}
这段代码处理了查询结果,逐个获取实例的属性值并输出。
四、完整代码和测试
完整代码如下所示:
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")
using namespace std;
int main(int argc, char** argv)
{
setlocale(LC_ALL, ".utf8");
// Initialize COM. ------------------------------------------
HRESULT hres = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hres))
{
wcout << "CoInitializeEx() failure:" << hex << (unsigned long)hres;
return 0;
}
// Obtain the initial locator to Windows Management
// on a particular host computer.
IWbemLocator* pLoc = NULL;
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
if (FAILED(hres))
{
CoUninitialize();
wcout << "CreateInstance failure:" << hex << (unsigned long)hres;
return 0;
}
// Connect to WMI through the IWbemLocator::ConnectServer method
// Connect to the local ROOT\CIMV2 namespace
// and obtain pointer pSvc to make IWbemServices calls.
IWbemServices* pSvc = NULL;
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\SecurityCenter2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
if (FAILED(hres))
{
pLoc->Release();
CoUninitialize();
wcout << "ConnectServer() failure:" << hex << (unsigned long)hres;
return 0;
}
cout << "Connected to ROOT//SecurityCenter2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
cin.get();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM AntiVirusProduct"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
cin.get();
return 1; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject* pclsObj;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp{};
// Get the value of the Name property
hr = pclsObj->Get(L"displayName", 0, &vtProp, 0, 0);
wcout << "AntiVirus Product displayName : " << vtProp.bstrVal << endl;
hr = pclsObj->Get(L"companyName", 0, &vtProp, 0, 0);
wcout << "AntiVirus Product companyName : " << vtProp.bstrVal << endl;
hr = pclsObj->Get(L"pathToSignedProductExe", 0, &vtProp, 0, 0);
wcout << "AntiVirus Product pathToSignedProductExe : " << vtProp.bstrVal << endl;
hr = pclsObj->Get(L"versionNumber", 0, &vtProp, 0, 0);
wcout << "AntiVirus Product versionNumber : " << vtProp.bstrVal << endl;
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
//pclsObj->Release();
CoUninitialize();
cin.get();
return 0; // Program successfully completed.
}
测试过程的截图如下所示:
总结
通过使用 WMI API,我们可以方便地查询和管理 Windows 系统的各种信息。在本文中,我们介绍了如何使用 WMI 查询当前计算机上安装的安全软件的基本信息,包括初始化 COM、连接 WMI 服务、执行查询以及处理查询结果等步骤。通过深入了解 WMI API 的使用,我们可以更好地理解和利用 Windows 系统的管理功能。
发布于:2024.02.07,更新于:2024.02.07