文章总结: 本文作为WMI内部机制系列首篇,系统阐述了WMI基础架构,重点解析了WMI服务与WmiPrvse.exe的关系。通过PowerShell实战演示,展示了如何追踪WMI类至特定ProviderDLL,并利用Win32_Process创建隐藏进程。文章揭示了WMI活动的底层执行逻辑,为后续研究WMI与COM交互奠定基础,对理解Windows底层机制极具参考价值。 综合评分: 88 文章分类: 实战经验,红队,内网渗透,终端安全
WMI 内部机制详解 (第一部分) – 基础架构与 Provider 追踪
Jonathan Johnson Jonathan Johnson
securitainment
2026年3月6日 17:46 中国香港
| 原文链接 | 作者 | | — | — | | https://jonny-johnson.medium.com/wmi-internals-part-1-41bb97e7f5eb | Jonathan Johnson |
理解基础知识
最近我对 WMI 内部机制产生了浓厚兴趣,因此决定撰写一系列博客来分享我的研究成果。本篇作为系列的第一篇,将介绍 WMI 的基本概念,并展示如何将 WMI 活动追溯到 WMI Provider Host 进程 (WmiPrvse.exe)——这是负责执行 WMI 操作的可执行文件。本文旨在为系列第 2 部分奠定基础,后续将深入探讨 WMI 与 COM 之间的关系。需要说明的是,本文不会涵盖 WMI 的所有内容,例如永久性 WMI 事件订阅等主题不在讨论范围内。
免责声明:本文中的许多内容并非原创,因此我想在开头就向相关作者致谢,并建议大家参阅下方的Resources部分。那些文章和讨论极大地加深了我对这项技术的理解。
WMI 术语
Microsoft 希望拥有一项能够跨企业收集信息和管理资产的技术。为此,他们实现了自己版本的 Web-Based Enterprise Management,并将其命名为 Windows Management Instrumentation (WMI)。WMI 允许用户和管理员获取各类对象的信息,进而了解环境、计算机、进程等方面的情况。WMI 还允许管理员创建对象,例如创建进程、服务等。为实现这些功能,WMI 采用了 Common Information Model (CIM)标准来表示上述各种对象。这些对象被称为 “managed objects” (托管对象)。
WMI 有 4 个主要组件:
-
WMI Providers:
负责监控 managed objects 的 COM 服务器。这些 providers 由一个 DLL (COM 服务器) 和一个 Managed Object Format (MOF) 文件组成,其中 MOF 文件用于定义 WMI 类。这些 providers 通常以 DLL 形式存在,可以在
C:\Windows\System32\wbem\*路径下找到 -
Managed Objects:
表示进程、服务、操作系统等对象的 WMI 类。
-
WMI Infrastructure:
即 WMI 服务 (Winmgmt)。该服务包含两个组件:
- CIM Object Manager (CIMOM)。负责处理管理应用程序与 providers 之间的连接,被视为 WMI 的核心组件。
- 磁盘上的数据库 “存储” 被称为 WMI/CIMOM Object Repository。该仓库按 WMI 命名空间进行组织,命名空间的格式类似于 root\cim2,每个命名空间包含一组 providers。仓库位于:C:\Windows\System32\wbem\Repository\
-
Management Application (也称为 WMI Consumer):
与 WMI 基础设施交互的客户端应用程序,可以是普通的可执行文件 (EXE)、VBScript 脚本、PowerShell 脚本等。我们将在后面的演练中看到具体的示例。
在继续之前,我想回过头来介绍一下 WMI 服务 (Winmgmt) 的实现方式及其任务执行机制。
WMI 服务 (WinMgmt) 封装在 wmisvc.dll 中,该 DLL 被加载到 svchost.exe 进程中运行。通过查看注册表中 WinMgmt 的配置可以验证这一点:
也可以通过 Process Explorer 进行确认:
你可能还注意到磁盘上另一个 WMI 相关的二进制文件——WmiPrvSe (WMI Provider Host)。该程序负责加载正确的 COM 服务器 (WMI providers) 来执行指定的任务。它通过 C:\Windows\system32\wbem\wmiprvse.exe -secured -Embedding启动,其父进程是命令行参数为 C:\Windows\system32\svchost.exe -k DcomLaunch -p的 svchost 进程。而这个 svchost 本身运行在 services.exe 下。
以下是 WMI 调用的高层流程示例:
- WMI 服务 (wmisvc.dll) 通过 (C:\Windows\system32\svchost.exe -k netsvcs -p -s Winmgmt) 在 SVCHOST 进程中启动
- 管理应用程序 (powershell.exe) 执行 WMI 方法
- WmiPrvSe 通过 C:\Windows\system32\wbem\wmiprvse.exe -Secured -Embedding在 DCOMLaunch svchost 进程下启动
- WMI 服务将相应的 WMI provider 加载到 WmiPrvSe 中
- WmiPrvSe 执行该方法对应的函数
WMI 底层还涉及大量 COM/RPC 相关的机制。如需了解更多信息,请参阅 Windows Internals 一书第 2 部分,特别是第 10 章。
WMI 实战演练
就我个人经验来说,通过实际操作 Windows 提供的各种 cmdlets,WMI 变得更加容易理解。让我们来实践一下。
首先,我们需要确定要与之交互的 WMI 类和方法。好在 PowerShell 提供了两种不同类型的 WMI cmdlet:WMI cmdlets 和 CIM cmdlets。CIM cmdlets 是较新且更推荐的 WMI 交互方式,但 WMI cmdlets 仍然有其用武之地,我们稍后会看到。
我想看看是否有某个 WMI 类可以用来创建进程。为此,我需要查找是否有类暴露了名称中包含 Create 的方法,运行以下命令:
PS > Get-CimClass -MethodName *Create*
NameSpace: ROOT/cimv2
CimClassName CimClassMethods CimClassProperties
------------ --------------- ------------------
Win32_Process {Create, Terminat... {Caption, Description, InstallDate, Name...}
Win32_BaseService {StartService, St... {Caption, Description, InstallDate, Name...}
Win32_Service {StartService, St... {Caption, Description, InstallDate, Name...}
…
可以看到,有一个名为 Win32_Process的 WMI 类,它包含一个名为 Create的方法。该类的 provider 位于 ROOT/cimv2命名空间中。不过,我们目前还不知道具体的 WMI provider 是什么,接下来让我们找出答案。
如前所述,WMI providers 本质上就是 COM 服务器,这意味着它们在注册表中通过类标识符 (CLSID) 进行注册。通过获取一个 provider 实例 并按我们感兴趣的 WMI 类进行过滤,即可提取出对应的 CLSID。
PS > (Get-CimInstance __Provider -Filter "Name = '$(([WmiClass] 'Win32_Process').Qualifiers['provider'].Value)'").CLSID
{d63a5850-8f16-11cf-9f47-00aa00bf345c}
然后可以在注册表的 HCKR hive 中搜索该 CLSID:
PS > Get-ItemPropertyValue -Path "Registry::HKEY_CLASSES_ROOT\CLSID\{d63a5850-8f16-11cf-9f47-00aa00bf345c}\InprocServer32\" -Name '(default)'
C:\WINDOWS\system32\wbem\cimwin32.dll
很好,现在我们已经获得了关于目标 WMI 类和方法的关键信息:
WMI Class: Win32Process
Method: Create
Provider:cimwin32.dll
Namespace: ROOT/cimv2
最后,我需要了解成功创建进程所需传递的参数。实现这一目标有多种方式,让我们先尝试通过 WMI cmdlets 来获取这些信息。
PS > (Get-CimClass -ClassName Win32_Process).CimClassMethods['Create'].Parameters
Name CimType Qualifiers ReferenceClassName
---- ------- ---------- ------------------
CommandLine String {ID, In, MappingStrings}
CurrentDirectory String {ID, In, MappingStrings}
ProcessStartupInformation Instance {EmbeddedInstance, ID, In, MappingStrings}
ProcessId UInt32 {ID, MappingStrings, Out}
很好,可以看到有 3 个 “In” 参数 (CommandLine、CurrentDirectory、ProcessStartupInformation) 和 1 个 “Out” 参数 (ProcessId)。为了获取关于方法参数的更多信息,我通常会打开 provider 的 MOF 文件。在本例中是 C:\Windows\System32\wbem\cimwin32.mof。
找到 Win32_Process 类的定义位置后,可以看到大量有价值的信息:
[Dynamic,Provider("CIMWin32") : ToInstance,SupportsCreate,CreateBy("Create"),SupportsDelete,DeleteBy("DeleteInstance"),Locale(1033) : ToInstance,UUID("{8502C4DC-5FBB-11D2-AAC1-006008C78BC7}") : ToInstance]
class Win32_Process : CIM_Process
{
[Read : ToSubclass,Privileges{"SeDebugPrivilege"} : ToSubclass,MappingStrings{"Win32API|Tool Help Structures|MODULEENTRY32|szExePath"} : ToSubclass] string ExecutablePath;
[Read : ToSubclass,Privileges{"SeDebugPrivilege"} : ToSubclass,MappingStrings{"Win32|WINNT.H|QUOTA_LIMITS|MaximumWorkingSetSize"} : ToSubclass] uint32 MaximumWorkingSetSize;
[Read : ToSubclass,Privileges{"SeDebugPrivilege"} : ToSubclass,MappingStrings{"Win32|WINNT.H|QUOTA_LIMITS|MinimumWorkingSetSize"} : ToSubclass] uint32 MinimumWorkingSetSize;
...
首先,这里有大量的 read 指令,这说明我们很可能可以通过实例化 Win32_Process 类来获取进程对象。这一点我们稍后会演示,但目前我们关注的是 Create 方法。可以看到这里有一个 “Constructor” 限定符,表示存在一个可以创建该类实例的调用。查看 Constructor 方法的详细信息,发现它指向的是 Create。
[Constructor,Static,Implemented,Privileges{"SeAssignPrimaryTokenPrivilege", "SeIncreaseQuotaPrivilege", "SeRestorePrivilege"} : ToSubclass,ValueMap{"0", "2", "3", "8", "9", "21", ".."} : ToSubclass,MappingStrings{"Win32API|Process and Thread Functions|CreateProcess"} : ToSubclass] uint32 Create([In : ToSubclass,MappingStrings{"Win32API|Process and Thread Functions|lpCommandLine "} : ToSubclass] string CommandLine,[In : ToSubclass,MappingStrings{"Win32API|Process and Thread Functions|CreateProcess|lpCurrentDirectory "} : ToSubclass] string CurrentDirectory,[In : ToSubclass,MappingStrings{"WMI|Win32_ProcessStartup"} : ToSubclass] Win32_ProcessStartup ProcessStartupInformation,[Out : ToSubclass,MappingStrings{"Win32API|Process and Thread Functions|CreateProcess|lpProcessInformation|dwProcessId"} : ToSubclass] uint32 ProcessId);
该定义包含的信息与 WMI cmdlet 提供的信息一致,区别在于它指明了参数 ProcessStartupInformation需要通过 Win32_ProcessStartup实例传入。查看这个类的定义,可以发现我们能够创建该类的自定义实例,并指定各种 ProcessStartup选项。其中特别引人注目的是 ShowWindow参数。
[Abstract,Locale(1033) : ToInstance,UUID("{8502C4DB-5FBB-11D2-AAC1-006008C78BC7}") : ToInstance]
class Win32_ProcessStartup : Win32_MethodParameterClass
{
[Write : ToSubclass,MappingStrings{"Win32API|Process and Thread Structures|STARTUPINFO|wShowWindow"} : ToSubclass] uint16 ShowWindow;
...
class Win32_ProcessStartup : Win32_MethodParameterClass
{
[Description("The ShowWindow property specifies how the window is to be displayed to the user.") : Amended ToSubclass,Values{"SW_HIDE", "SW_NORMAL", "SW_SHOWMINIMIZED", "SW_SHOWMAXIMIZED", "SW_SHOWNOACTIVATE", "SW_SHOW", "SW_MINIMIZE", "SW_SHOWMINNOACTIVE", "SW_SHOWNA", "SW_RESTORE", "SW_SHOWDEFAULT", "SW_FORCEMINIMIZE"} : Amended ToSubclass] uint16 ShowWindow;
可以看到,有一个选项可以指定 Sw_Hidden(即值为 0)。我们来试试这个选项——毕竟,启动一个隐藏的 notepad 进程是每个正经黑客都会做的事情。
首先,创建一个设置了 hidden 参数的 Win32_ProcessStartup 实例:
PS> $Win32_ProcessStartupClass = Get-CimClass -ClassName Win32_ProcessStartup
PS > $ProcessStartupInformation = New-CimInstance -CimClass $Win32_ProcessStartupClass -Property @{'ShowWindow' = 0} -ClientOnly #0 = SW_HIDDEN
最后,调用 Create 方法:
PS > Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='notepad.exe'; CurrentDirectory='C:\'; ProcessStartupInformation=$ProcessStartupInformation}
ProcessId ReturnValue PSComputerName
— — — — — — — — — — — — — — — — — —
2432 0
执行调用后,可以看到该进程在 WmiPrvse.exe 下被创建:
查看 WmiPrvse.exe 二进制文件,可以确认 Win32_Process 的 provider DLL — cimwin32.dll 已经被加载:
在结束之前,还记得前面提到我们可以通过 Win32_Process 获取进程的 WMI 实例吗?让我们用它来查询新创建的 notepad 进程的信息:
PS > Get-CimInstance -ClassName Win32_Process -Filter "ProcessId = 15444"
ProcessId Name HandleCount WorkingSetSize VirtualSize
— — — — — — — — — — — — — — — — — — — — — — — — — -
15444 notepad.exe 190 13496320 2203470827520
同样也可以通过 Get-WMIObject来实现:
PS > Get-WmiObject -Class Win32_Process -Filter "ProcessId = 15444"
总结
本文的目标是为 “WMI Internals” 系列的后续文章奠定知识基础。我认为这一步很重要,这样所有读者都能拥有相同的术语体系和对基本原理的理解。今天展示的内容并非新鲜事物,但在后续文章中将以更进阶的示例加以呈现。本文有意省略了一些 WMI 相关的主题,但我强烈建议大家前往资源部分,参阅一些优秀研究人员的成果。感谢阅读,第 2 部分将深入探讨 WMI 与 COM 之间的关系。
参考资源
- Matt Graeber 的 BlackHat 演讲 — Abusing Windows Management Instrumentation (WMI)
- Microsoft 文档:
- About WMI
- WMI Architecture
- WMI Infrastructure
- Windows Internals Part 2 Chapter 10
- 与 Matt Graeber 和 Alex Ionescu 的交流。
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment Jonathan Johnson Jonathan Johnson《WMI 内部机制详解 (第一部分) – 基础架构与 Provider 追踪》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论