首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
编程技术
发新帖
0
0
[原创]WMI 永久订阅的 Filter 与 Consumer 绑定解析
发表于: 2026-7-19 21:44
882
[原创]WMI 永久订阅的 Filter 与 Consumer 绑定解析
WangWei_CM.
2026-7-19 21:44
882
# WMI 永久订阅的 Filter 与 Consumer 绑定解析 相关内容实现项目:KswordARK 地址<mark class="encrypted">03aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2f1%4N6G2M7X3c8p5c8g2k6Q4x3V1k6w2f1%4N6G2M7X3b7`.</mark>  文章中内容已经在Ksword中实现,仓库地址:<mark class="encrypted">443K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2f1%4N6G2M7X3c8p5c8g2k6Q4x3V1k6w2f1%4N6G2M7X3b7`.</mark>  本篇要完成的是:读取 `ROOT\subscription` 中的永久订阅,列出每个 Filter 的触发条件,再通过 Binding 的对象路径还原它连接的 Filter 和 Consumer。 要完成这项读取,分为四步: 1. 初始化 COM 与调用安全状态。 2. 连接 `ROOT\subscription`,取得该命名空间的服务接口。 3. 查询 `__EventFilter`,保存名称、WQL、查询语言和事件命名空间。 4. 查询 `__FilterToConsumerBinding`,解析其中的 Filter 与 Consumer 对象路径。 这四步按顺序提供读取条件、目标命名空间、触发条件和关联关系。最后得到的结果说明某个 Filter 在读取时连接到哪个 Consumer,同时保留无法解析的原始引用和失败状态。 ## 一、对象关系和完整流程 永久订阅的运行路径由 Filter、Binding 和 Consumer 组成。Filter 负责匹配事件。Binding 保存对象引用。Consumer 负责事件匹配后的处理方式。三者都位于 WMI 的 `ROOT\subscription` 命名空间。 ```text 系统状态或事件来源变化 -> __EventFilter 匹配事件 WQL -> __FilterToConsumerBinding 提供对象引用 -> Consumer 执行命令、处理脚本或写日志 ``` ```text 初始化 COM -> 连接 ROOT\subscription -> 查询 Filter -> 查询 Binding -> 解析两端对象路径 -> 输出关联关系与读取状态 ``` 读取过程需要分别查询 Filter 与 Binding。它以 Binding 的两个对象路径建立关系,不按名称文本猜测归属。Consumer 的类名和名称只在对象路径可解析时读取。 ### WMI 是按命名空间组织的管理对象系统 Windows Management Instrumentation(WMI)为系统、驱动、服务和应用提供统一的管理对象访问方式。WMI 服务接收查询请求。WMI 提供程序把操作系统中的状态映射成类和实例。客户端通过 COM 接口读取这些对象。 WMI 的基础对象各有职责。命名空间类似逻辑容器,例如 `ROOT\CIMV2` 包含大量系统管理类。类描述一组属性和方法,例如 `Win32_Process`。实例表示某一时刻的具体对象,例如某个进程实例。对象路径是能够唯一定位实例的字符串。客户端指定命名空间和查询后,就能通过 `IWbemServices` 获取实例。 ```text WMI 客户端 COM 调用 / WQL 查询 WMI 服务(winmgmt) 命名空间 ROOT\CIMV2:提供程序与系统管理对象 命名空间 ROOT\subscription:永久订阅对象 ``` 命名空间之间相互独立。某个类在 `ROOT\CIMV2` 存在,不表示相同类名能在 `ROOT\subscription` 查询到。连接成功也只代表拿到所选命名空间的服务接口。 ### WQL 描述对象筛选条件,事件 WQL 描述“什么时候” 普通查询与事件查询需要分开理解。WQL(WMI Query Language)是一种面向 WMI 对象的查询语言,语法接近 SQL。普通 WQL 例如 `SELECT Name, ProcessId FROM Win32_Process` 读取当前实例。事件 WQL 使用 `__InstanceCreationEvent`、`__InstanceDeletionEvent`、`__InstanceModificationEvent` 等事件类,描述对象状态变化时应关注的条件。 一个典型的内在事件查询可能是: ```sql SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' ``` `WITHIN 5` 表示 WMI 按一定间隔检查这类变化,单位是秒。`TargetInstance` 是事件携带的变化对象。`ISA 'Win32_Process'` 限制关注的对象类别。查询只表达“匹配什么事件”,并不包含执行命令、写日志或调用脚本的处理细节。 事件还可以由专用提供程序直接报告。无论事件来自轮询还是提供程序,Filter 都需要通过 `EventNamespace` 指明查询在哪个命名空间解释。相同的 WQL 文本放在不同事件命名空间中,可能指向完全不同的类集合。 ### 临时订阅存在于连接期间,永久订阅保存为 WMI 对象 临时订阅与永久订阅的存续范围不同。临时事件订阅由客户端调用事件查询接口建立,客户端断开后该订阅通常不再保留。永久事件订阅把 Filter、Consumer 和 Binding 实例保存到 `ROOT\subscription`。WMI 服务可以在随后满足条件时,按保存的关联处理事件。 永久订阅由三个可独立存在的对象组成。`__EventFilter` 保存事件 WQL、查询语言和事件命名空间。事件处理对象实例保存处理方式。`__FilterToConsumerBinding` 的 `Filter` 与 `Consumer` 属性保存到两端的对象路径引用。Binding 才是条件与处理端之间的连接边。 ```text __EventFilter Name / Query / QueryLanguage / EventNamespace __FilterToConsumerBinding Filter / Consumer / DeliveryQoS / MaintainSecurityContext 事件处理对象实例 __CLASS / Name / 该处理对象类型的特有属性 ``` 这三个对象可以形成一对多或多对一关系。一个 Filter 能由多个 Binding 指向不同 Consumer,一个 Consumer 也能被多个 Binding 引用。因此读取过程应为每个对象保留稳定对象路径,并以 Binding 的两个引用建立关联,不能仅按 `Name` 文本猜测归属。 ### Consumer 类型决定处理属性的含义 处理对象的属性要先按 WMI 类区分。`NTEventLogEventConsumer` 用事件日志相关字段描述处理方式。`LogFileEventConsumer` 用日志文件相关字段描述处理方式。`CommandLineEventConsumer` 才会出现命令行模板。`ActiveScriptEventConsumer` 则会涉及脚本引擎和脚本文本等属性。 对象路径类似 `CommandLineEventConsumer.Name="Example"`,它是 WMI 实例定位符,不是可执行文件路径。正确过程是用 `GetObject` 打开该路径,读取 `__CLASS`,再按这一类处理对象的文档读取适用字段。未知类别应保留类名、对象路径和原始属性状态,不能把每个 Consumer 都转换为命令文本。 ### WMI 接口、BSTR 和 VARIANT 有各自的资源责任 WMI 的 COM 自动化对象有明确的所有权。`IWbemLocator`、`IWbemServices`、`IEnumWbemClassObject` 和 `IWbemClassObject` 都是 COM 接口指针。成功得到接口后,持有者在使用结束时调用 `Release`。`BSTR` 是自动化字符串,`SysAllocString` 成功后由调用方 `SysFreeString`。不能用 `delete[]` 或 `free` 释放。 `VARIANT` 是可保存字符串、整数、布尔值、接口或数组的联合类型。WMI 属性读取会把真实类型写入 `VARIANT`。调用方先检查 `vt`,再读取相应成员,然后调用 `VariantClear` 释放其中可能存在的 BSTR、接口或数组。把任何属性都强制转换为字符串会破坏数值和对象路径的边界。 ### 永久订阅配置与一次事件处理结果属于不同层次 静态对象与事件执行记录要分开。完整读取 Filter、Binding 和 Consumer,只说明在观察时刻这些 WMI 实例与对象路径可以关联。事件是否发生、WQL 是否匹配、处理对象是否成功处理以及处理产生什么结果,需要从事件日志、WMI 活动记录或独立运行时证据确认。 输出可明确分为三层:配置层记录三类对象和引用关系。解析层记录类名和特有属性。运行时层记录事件时间、匹配结果和处理状态。任何层缺失都保留原始失败 HRESULT,不用另一层的成功替代。 每次读取还应记录命名空间路径、读取时间和调用账户上下文,避免不同用户或不同远程连接得到的结果混在同一批记录中。 ## 二、第一步:初始化 COM 与调用安全状态 这一步让当前线程能够使用 COM 接口,并为后续 WMI 服务调用准备默认安全状态。COM(Component Object Model,组件对象模型)是 Windows 中一套对象调用规则。WMI 客户端、定位器和服务接口都通过这套规则协作。 初始化失败时不能继续读取。默认安全初始化可能返回 `RPC_E_TOO_LATE`,表示进程中的其它组件已经先设置了 COM 安全参数。此时保持已有设置,继续连接目标命名空间。 完成第一步后,当前线程已经具备调用 WMI COM 接口的前提,但还没有指向任何命名空间。接下来需要连接 `ROOT\subscription`,取得保存永久订阅对象的服务接口。 ## 三、第二步:连接 ROOT\subscription ### Filter 和 Binding 位于永久订阅命名空间 连接 WMI locator 的方法返回命名空间服务接口。`ROOT\subscription` 保存永久事件订阅,`ROOT\CIMV2` 通常保存触发事件涉及的系统类。两个命名空间的用途不同。 ```cpp // 意义:连接指定 WMI 命名空间。 // 返回:S_OK 成功。ppNamespace 接收 IWbemServices,调用方 Release。 HRESULT IWbemLocator::ConnectServer( BSTR strNetworkResource, BSTR strUser, BSTR strPassword, BSTR strLocale, LONG lSecurityFlags, BSTR strAuthority, IWbemContext* pCtx, IWbemServices** ppNamespace ); // 意义:设置连接后 WMI 服务代理的认证和模拟级别。 // 返回:S_OK 成功。失败 HRESULT 表示代理安全状态不可用。 HRESULT CoSetProxyBlanket( IUnknown* pProxy, DWORD dwAuthnSvc, DWORD dwAuthzSvc, OLECHAR* pServerPrincName, DWORD dwAuthnLevel, DWORD dwImpLevel, RPC_AUTH_IDENTITY_HANDLE pAuthInfo, DWORD dwCapabilities ); // 意义:从宽字符文本创建 OLE 自动化分配器管理的 BSTR。 // 返回:成功时为新 BSTR,内存不足时为 nullptr。需要下方释放函数。 BSTR SysAllocString(const OLECHAR* psz); // 意义:释放 SysAllocString 或 COM getter 返回的 BSTR。 // 返回:无。传入 nullptr 安全。 void SysFreeString(BSTR bstrString); ``` 正确做法:以当前令牌连接 `ROOT\subscription`,再设置 `RPC_C_IMP_LEVEL_IMPERSONATE`。连接返回服务接口后,代理安全设置仍是独立步骤。 ```cpp BSTR path = SysAllocString(L"ROOT\\subscription"); IWbemServices* services = nullptr; if (SUCCEEDED(locator->ConnectServer(path, nullptr, nullptr, nullptr, WBEM_FLAG_CONNECT_USE_MAX_WAIT, nullptr, nullptr, &services))) { CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); } SysFreeString(path); ``` 错误示例:从 `ROOT\CIMV2` 查询 `__FilterToConsumerBinding`。该类位于永久订阅命名空间,错误命名空间会得到“类不存在”或空结果。 完成第二步后,已经获得只对应 `ROOT\subscription` 的 `IWbemServices` 服务接口。这个接口能查询永久订阅对象,但关联的起点仍然未知。接下来先读取 Filter,保留触发条件和稳定对象路径。 ## 四、第三步:查询 __EventFilter ### Filter 保存触发条件本身 执行 WQL 的方法返回枚举器。`__EventFilter` 的 `Name` 是对象名称,`Query` 是触发 WQL,`QueryLanguage` 和 `EventNamespace` 说明查询解释方式与事件来源。 ```cpp // 意义:执行只读 WQL 查询并返回对象枚举器。 // 返回:S_OK 成功。ppEnum 接收 IEnumWbemClassObject,调用方 Release。 HRESULT IWbemServices::ExecQuery( const BSTR strQueryLanguage, const BSTR strQuery, LONG lFlags, IWbemContext* pCtx, IEnumWbemClassObject** ppEnum ); ``` 正确做法:保留名称、相对路径、WQL、查询语言和事件命名空间。相对路径如 `__EventFilter.Name="SCM Event Log Filter"` 可与 Binding 的 `Filter` 引用直接对照。 ```cpp const wchar_t* query = L"SELECT Name, Query, QueryLanguage, EventNamespace, __RELPATH " L"FROM __EventFilter"; // 对每个结果分别保存 Name、Query、QueryLanguage、EventNamespace、__RELPATH。 ``` 错误示例:只保存 WQL。相同 WQL 在不同事件命名空间中的含义可能不同。缺少 `EventNamespace` 时,无法确定查询的事件类来源。 完成第三步后,每个 Filter 都有名称、触发 WQL、事件命名空间和相对路径。此时只知道“什么事件会匹配”,还不知道匹配后连接到哪个处理对象。接下来读取 Binding,并逐条保留它的两个引用。 ## 五、第四步:查询 Binding 并解析两端对象路径 ### Binding 引用连接 Filter 与 Consumer Binding 查询要保存两个原始引用。`Filter` 指向一个 `__EventFilter` 对象。`Consumer` 指向 `CommandLineEventConsumer`、`ActiveScriptEventConsumer`、日志处理对象或其它处理对象。`DeliveryQoS` 与 `MaintainSecurityContext` 是 Binding 自身属性。 ```cpp const wchar_t* query = L"SELECT Filter, Consumer, DeliveryQoS, MaintainSecurityContext " L"FROM __FilterToConsumerBinding"; // 对每个 Binding 保留 Filter 与 Consumer 的原始对象路径,不用名称文本替代引用。 ``` 正确做法:通过服务接口读取引用对象。`GetObject` 接受 WMI 对象路径。返回对象后,读取 `__CLASS` 与 `Name`。解析失败时保留原始引用。引用缺失本身也是读取结果。 ```cpp // 意义:按 WMI 对象路径取得一个对象实例。 // 返回:S_OK 成功。ppObject 接收 IWbemClassObject,调用方 Release。 HRESULT IWbemServices::GetObject( const BSTR strObjectPath, LONG lFlags, IWbemContext* pCtx, IWbemClassObject** ppObject, IWbemCallResult** ppCallResult ); IWbemClassObject* endpoint = nullptr; if (SUCCEEDED(services->GetObject(bindingConsumerPath, 0, nullptr, &endpoint, nullptr))) { // 读取 endpoint 的 __CLASS 与 Name,随后 Release。 endpoint->Release(); } ``` 错误示例:把 `Consumer` 引用当成可执行文件路径。它通常是 `NTEventLogEventConsumer.Name="..."` 这类 WMI 对象路径,需要先解析类和对象名。是否存在命令文本取决于处理对象类型。 完成第四步后,Filter、Binding 和 Consumer 已能通过对象路径对应起来。读取结果还需要处理超时、属性类型和资源释放,才能区分空结果、等待超时和属性不可读。 ## 六、处理等待与属性资源 ### 枚举器需要有超时边界 读取枚举器的方法每次最多等待指定毫秒数。`WBEM_S_TIMEDOUT` 且没有对象时表示本次等待结束,不等于类没有实例。连续超时达到上限后记录状态并继续其它类。 ```cpp // 意义:从 WMI 枚举器读取对象。 // 返回:WBEM_S_NO_ERROR 成功。WBEM_S_TIMEDOUT 等待超时。 HRESULT IEnumWbemClassObject::Next( LONG lTimeout, ULONG uCount, IWbemClassObject** apObjects, ULONG* puReturned ); HRESULT status = enumerator->Next(5000, 1, &object, &returned); if (status == WBEM_S_TIMEDOUT && returned == 0) { // 增加超时次数。达到上限后结束这一类查询。 } ``` 属性读取后要清理 `VARIANT`。Filter 的 Query、Binding 的引用通常是 BSTR,数值和布尔字段使用其它联合成员。读取后需要使用下方清理 API,不能把所有属性强制转换成字符串指针。 ```cpp // 意义:把 VARIANT 初始化为空值,以便 WMI 属性方法写入。 // 返回:无。每个初始化后的 VARIANT 都需要清理内部资源。 void VariantInit(VARIANTARG* pvarg); // 意义:释放 VARIANT 内部的 BSTR、接口、数组等资源。 // 返回:S_OK 成功。 HRESULT VariantClear(VARIANTARG* pvarg); ``` 完成等待和属性处理后,输出中的每个字段都有明确来源,也能保留读取失败的边界。最后要保持三类对象各自的身份,避免把配置关系误读为一次实际执行结果。 ## 七、保持三类对象的独立身份 ### Filter、Binding 与 Consumer 不能合成一条记录 对象建模要分别保存 Filter、Binding 和 Consumer。Filter 表示条件,Consumer 表示动作,Binding 表示关联。一个 Filter 可以连接多个 Consumer,一个 Consumer 也可被多个 Binding 引用。 配置与实际触发需要分别判断。读取到完整 Binding,说明三类对象在读取时可以关联。实际事件是否发生、WQL 是否匹配和处理对象是否成功处理,需要事件日志或其它独立证据确认。 完整可运行程序在附件<mark class="encrypted">2c8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4j5h3&6Y4N6$3g2A6j5$3#2Q4x3X3g2D9j5h3&6*7L8%4g2#2i4K6u0W2j5$3!0E0i4K6u0r3K9e0g2H3k6#2R3K6P5r3k6T1P5Y4S2Y4</mark> 
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
上传的附件:
008-WMI永久订阅的Filter与Consumer绑定解析.zip
(156.22kb,2次下载)
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
WangWei_CM.
30
发帖
1
回帖
40
RANK
关注
私信
他的文章
[原创] 进程模块枚举、映像身份与线程启动地址关联
215
[原创]Windows 进程 CPU、内存、I/O 与调度指标采样
981
[原创]跨位数进程的映像、命令行与环境块读取
535
[原创]进程实例身份、父子关系与会话归属
293
[原创]Windows 进程枚举NtQuerySystemInformation(SystemProcessInformation
635
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
谁下载
×
Fzzz
mb_lthgjpwj
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部