如何使用Powershell通过WMI和CIM实现复杂操作?

摘要:目录WMI 和 CIM了解 WMI 和 CIMCIM 和 WMI 结构CIM 和 WMI 中的存储库命名空间类实例查找文档实际案例查询配置信息列出本地存储库命名空间和类类名前缀的意义查询语言查询实例获取实例筛选实例(Filter)WQL(W
目录WMI 和 CIM了解 WMI 和 CIMCIM 和 WMI 结构CIM 和 WMI 中的存储库命名空间类实例查找文档实际案例查询配置信息列出本地存储库命名空间和类类名前缀的意义查询语言查询实例获取实例筛选实例(Filter)WQL(WMI Query Language)连接到远程计算机WMI 连接CIM 连接查询存储库类创建会话对象使用会话配置会话选项移除会话查询和操作存储库对象发现存储库对象的方法调用存储库对象的方法直接调用使用Invoke-WmiMethod使用Invoke-CimMethod WMI 和 CIM 了解 WMI 和 CIM CIM 和 WMI 结构 PowerShell | v CIM Cmdlets (Get-CimInstance) | v WMI Service (CIMOM, winmgmt) | v Provider (Win32_Service, Win32_Process, etc.) | v 操作系统内核 / 硬件 什么是 WMI 和 CIM? WMI (Windows Management Instrumentation) 微软的实现,用来管理和监控 Windows 系统资源(进程、服务、网络、硬件…)。 底层基于 CIM (Common Information Model) 标准。 CIM (Common Information Model) 由 DMTF(分布式管理任务组)制定的一个 国际标准。 它规定了“如何用统一的对象模型描述系统资源”。 换句话说,CIM 是“标准规范”,而 WMI 是“微软的实现”。 可以把 CIM/WMI 架构想象成一个三层模型: +---------------------+ | 管理应用 (PowerShell, MMC, 脚本) | +---------------------+ | WMI / CIMOM (管理基础层) | +---------------------+ | Providers (提供者) | +---------------------+ | 底层操作系统 / 硬件 / 服务 | 顶层:管理应用 这是你接触到的层,例如: PowerShell (Get-CimInstance, Get-WmiObject) 图形化工具(任务管理器、事件查看器) 第三方监控软件(如 SCCM, Nagios 插件) 应用不会直接访问硬件,而是通过 WMI/CIM API。 中间层:CIMOM / WMI 服务 CIMOM (CIM Object Manager) 是核心管理引擎。 在 Windows 中就是 WMI 服务 (winmgmt)。 功能: 接收应用请求(查询、修改) 调用正确的 Provider 返回对象化的数据 协议: WMI 早期 → 基于 DCOM(RPC) CIM 新架构 → 支持 WS-Man (Web Services Management) 底层:Providers(提供程序) Provider = “翻译官”,把底层资源包装成 CIM/WMI 对象。 每种资源有不同 Provider,例如: Win32_Process → 管理进程 Win32_Service → 管理服务 Win32_OperatingSystem → 获取操作系统信息 当你运行如下命令: Get-WmiObject Win32_Service 实际流程是: PowerShell 请求 CIMOM CIMOM 找到负责“服务”的 Provider Provider 向 Windows Service Control Manager (SCM) 取数据 数据包装成对象返回给你 CIM 与 WMI 的关系 特性 WMI (旧) CIM (新) 标准性 微软私有实现 DMTF 标准 通信 DCOM/RPC WS-Man (跨平台) Cmdlet Get-WmiObject (已过时) Get-CimInstance 跨平台 仅限 Windows 可用于 Linux/VMware/网络设备 从 PowerShell 3.0 开始,微软推荐 用 CIM cmdlet (Get-CimInstance) 替代 WMI,因为它基于开放协议 WS-Man,能支持远程和跨平台。 从 PowerShell 3.0 开始,微软推荐 用 CIM cmdlet (Get-CimInstance) 替代 WMI,因为它基于开放协议 WS-Man,能支持远程和跨平台。 示例: # 获取操作系统信息 Get-CimInstance -ClassName Win32_OperatingSystem # 获取所有服务 Get-CimInstance -ClassName Win32_Service # 获取进程 Get-CimInstance -ClassName Win32_Process | Select-Object Name, ProcessId 你看到的 Win32 类*,其实就是 Provider 暴露出来的 WMI 类,遵循 CIM 模型。 CIM 和 WMI 中的存储库 命名空间 查看命令 Get-CimInstance -Namespace root -ClassName __Namespace 返回的某些命名空间可能包括: subscription DEFAULT CIMV2 msdtc Cli Intel_ME SECURITY HyperVCluster SecurityCenter2 RSOP Intel PEH StandardCimv2 WMI directory Policy virtualization Interop Hardware ServiceModel SecurityCenter Microsoft Appv dcim 类 实例 = 类的实际对象。 如果类是“模板”,实例就是“真实存在的那个”。 Win32_Process 类 → 定义进程的属性(名称、PID、路径…) 实例 → 每一个正在运行的进程 (notepad.exe, explorer.exe) 这些类是 Provider 暴露出来的 实例 实例 = 类的实际对象。 如果类是“模板”,实例就是“真实存在的那个”。 Win32_Process 类 → 定义进程的属性(名称、PID、路径…) 实例 → 每一个正在运行的进程 (notepad.exe, explorer.exe) Get-CimInstance -ClassName Win32_Process | Select Name, ProcessId 输出: Name ProcessId ---- --------- explorer.exe 1234 powershell.exe 5678 这里 Win32_Process 是类,而每一行就是它的实例。 查找文档 列出所有可用的类(探索命名空间) WMI 信息按"命名空间"组织,最常用的是root\cimv2 # 列出 root\cimv2 命名空间下的所有类(会非常多!) Get-CimClass -Namespace root\cimv2 # 更实用的方法:按关键词过滤类名 Get-CimClass -ClassName "*Process*" -Namespace root\cimv2 Get-CimClass -ClassName "*Network*" -Namespace root\cimv2 Get-CimClass -ClassName "*User*" -Namespace root\cimv2 查看类的详细定义(自我文档化) 找到感兴趣的类后,用Get-CimClass查看其详细定义: # 查看 Win32_Process 类的完整定义 $processClass = Get-CimClass -ClassName Win32_Process # 查看这个类有哪些属性(Properties) $processClass.CimClassProperties | Select-Object Name, Type, Flags # 查看这个类有哪些方法(Methods)- 这非常重要! $processClass.CimClassMethods 获取类的实例并查看实际数据 这是最常用的操作——获取实际数据 # 获取所有进程实例 Get-CimInstance -ClassName Win32_Process # 获取特定进程(如 notepad) Get-CimInstance -ClassName Win32_Process -Filter "Name='notepad.exe'" # 只选择需要的属性(类似 SQL SELECT) Get-CimInstance -ClassName Win32_Process | Select-Object Name, ProcessId, CommandLine, WorkingSetSize 调用类的方法执行操作 WMI 类的真正威力在于它们的方法,这让你能远程执行管理操作。 示例:终止远程进程 # 找到要终止的进程 $notepadProcess = Get-CimInstance -ClassName Win32_Process -Filter "Name='notepad.exe'" # 调用 Terminate 方法 Invoke-CimMethod -InputObject $notepadProcess -MethodName Terminate 示例:创建新进程(远程执行命令) # 准备调用 Create 方法的参数 $methodArgs = @{ CommandLine = "calc.exe" # 要启动的命令或程序 } # 在本地或远程计算机上启动计算器 Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments $methodArgs # 更复杂的例子:在远程计算机上执行 $remoteCimSession = New-CimSession -ComputerName "Server01" Invoke-CimMethod -CimSession $remoteCimSession -ClassName Win32_Process -MethodName Create -Arguments $methodArgs 实际案例 案例 1:快速系统信息收集(用于安全基线核查) # 收集关键系统信息 $systemInfo = @{ OS = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object Caption, Version, InstallDate Computer = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object Name, Domain, Manufacturer, Model BIOS = Get-CimInstance -ClassName Win32_BIOS | Select-Object SerialNumber, Version Hotfixes = Get-CimInstance -ClassName Win32_QuickFixEngineering | Select-Object HotFixID, InstalledOn } $systemInfo | Format-List 案例 2:监控网络共享(检测异常共享) # 获取所有网络共享 $shares = Get-CimInstance -ClassName Win32_Share # 筛选出可疑的共享(如 ADMIN$ 被非管理员账户访问,或异常命名的共享) $suspiciousShares = $shares | Where-Object { $_.Name -like "*temp*" -or $_.Path -notlike "C:*" -or $_.Description -like "*admin*" } if ($suspiciousShares) { Write-Warning "发现可疑网络共享:" $suspiciousShares | Format-Table Name, Path, Description } 案例 3:用户和登录会话审计 # 获取当前登录的用户会话 $logonSessions = Get-CimInstance -ClassName Win32_LogonSession foreach ($session in $logonSessions) { $logonType = switch ($session.LogonType) { 2 { "交互式" } 3 { "网络" } 10 { "远程交互" } default { "其他: $($session.LogonType)" } } Write-Host "登录ID: $($session.LogonId), 类型: $logonType, 开始时间: $($session.StartTime)" } 微软官方文档: Win32 类:在搜索引擎中搜索 "Win32_Process class Microsoft" 直接访问:https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-provider 使用Get-CimClass的-Property和-Method参数: # 专门查看方法的详细信息 (Get-CimClass -ClassName Win32_Process).CimClassMethods # 查看特定方法的参数 $processClass = Get-CimClass -ClassName Win32_Process $processClass.CimClassMethods["Create"].Parameters 第三方 WMI 浏览器工具: 如WMI Explorer等图形化工具,可以更直观地浏览整个 WMI 架构。 查询配置信息 列出本地存储库命名空间和类 为什么要列出命名空间? 因为 WMI/CIM 的所有类都是按照命名空间分组的(就像文件夹)。要想知道有哪些类能用,先得知道有哪些命名空间。 命令方式: 用 WMI(支持递归): Get-WmiObject -Namespace root -List -Recurse | Select -Unique __NAMESPACE 这里 -Recurse 能让它把 root 下的所有子命名空间都展开。 用 CIM(更现代,但不能递归): Get-CimInstance -Namespace root -ClassName __Namespace 然后你可以逐层查看,靠 Tab 自动补全 来探索下一级命名空间。 补充说明:有些命名空间需要管理员权限,所以要 提升 PowerShell权限,否则会报错。 为什么要列出类? 当你要完成某个管理任务时,你需要知道有没有合适的 WMI/CIM 类能用。 举例:想管理进程 → 找类 → 发现有 Win32_Process。 命令方式: WMI: Get-WmiObject -Namespace root\CIMv2 -List CIM Get-CimClass -Namespace root\CIMv2 排序,方便查找: Get-CimClass -Namespace root\CIMv2 | Sort CimClassName 类名前缀的意义 在 root\CIMv2 命名空间里你会注意到两种前缀: CIM_ → 抽象类,是基础定义,通常不会直接拿来用。 Win32_ → 具体实现类,带有 Windows 特定的信息。 举例: CIM_OperatingSystem(抽象概念:操作系统) Win32_OperatingSystem(Windows 系统的具体实现,能拿到属性,比如版本号、安装日期等) 系统类: 你可能看到一些类名前缀是 __(双下划线),比如 __Namespace。 这些是 系统类,主要被 WMI/CIM 内部使用,比如管理存储库本身,不是直接管理资源。 猜测类名: 存储库本身没有“搜索引擎”,所以找类名有点像猜谜。 可以用 关键字搜索类名: Get-CimClass *network* | Sort CimClassName 这会返回类名中带有 “network” 的所有类。 但注意,它不能搜类的“描述信息”,因为存储库里只存储类定义,没有说明文档。 查询语言查询实例 什么是实例? 类:是“定义”。比如 Win32_LogicalDisk 定义了磁盘的属性(盘符、大小、类型…)。 实例:是“实际对象”。比如你电脑上的 C盘、D盘,它们就是 Win32_LogicalDisk 类的两个实例。 所以,查询实例就是:从类里拿出具体的对象及其属性。 获取实例 WMI: Get-WmiObject -Class Win32_LogicalDisk CIM(推荐的现代方法): Get-CimInstance -ClassName Win32_LogicalDisk 注意: 输出格式不同,但信息相同。 CIM 支持 Tab 补全类名,更方便。 筛选实例(Filter) 默认会返回该类的 所有实例,比如所有磁盘。 但我们通常只想要部分 → 可以加 -Filter。 语法规则: 这里的筛选器 不是 PowerShell 运算符,而是 WMI/CIM 专用运算符(更像 SQL 语法) 比较操作 WMI/CIM 运算符 PowerShell 运算符 等于 = -eq 不等于 <> -ne 大于 > -gt 小于 < -lt 小于或等于 <= -le 大于或等于 >= -ge 通配符匹配 LIKE(% 是通配符) -like(* 是通配符) 且 AND -and 或 OR -or 例子:只要本地磁盘(DriveType=3) Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" 或者 Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" 解释: DriveType 是一个数字枚举: 3 = 本地固定磁盘(硬盘/SSD) 5 = 光驱(DVD) 2 = 可移动磁盘(U盘) 所以必须查类文档来知道数字含义。 WQL(WMI Query Language) 直接用 SQL 风格的语句,更直观: Get-CimInstance -Query "SELECT * FROM Win32_LogicalDisk WHERE DriveType = 3" 或者 Get-WmiObject -Query "SELECT * FROM Win32_LogicalDisk WHERE DriveType = 3" 解释: SELECT * FROM 类名 → 获取所有实例 WHERE 条件 → 加过滤条件 语法和 SQL 类似,但功能比 SQL 简化(因为是 SQL 的子集) 连接到远程计算机 WMI 连接 使用 -ComputerName 参数指定远程计算机的名称或 IP 地址 可以指定多个计算机名称,以便在单个语句中在多台计算机上运行该命令 可以将计算机名称作为逗号分隔列表、包含多个计算机名称的数组或者将生成计算机名称集合的括号命令作为字符串对象提供 使用 -Credential 参数指定备用用户名 如果仅指定用户名,系统会提示输入密码 如果使用 Get-Credential cmdlet 将用户名和密码存储在变量中,则可以引用该变量来消除密码提示 Get-WmiObject -ComputerName LON-DC1 -Credential ADATUM\Administrator -Class Win32_BIOS 指定多个时,如果连接到一台计算机失败,该命令将生成一条错误消息,并继续尝试剩余的计算机。 CIM 连接 使用 -ComputerName 参数为与远程计算机的临时连接提供支持 但是CIM没有 -Credential 参数来指定备用凭据,如果要使用备用凭据,则需要创建 CIM 会话 Get-CimInstance -ComputerName LON-DC1 -Classname Win32_BIOS 查询存储库类 创建会话对象 创建会话时,应将其存储在变量中供以后参考。 创建会话并将其存储在变量中的基本语法是: $s = New-CimSession -ComputerName LON-DC1 可以同时创建多个会话: $sessions = New-CimSession -ComputerName LON-CL1,LON-DC1 创建会话时,PowerShell 不会立即建立连接。 当 cmdlet 使用 CIM 会话时,PowerShell 将连接到指定的计算机,然后在 cmdlet 完成时,PowerShell 会终止连接。 使用会话 将会话存储到变量中后,可使用 -CimSession 参数来通过 CIM cmdlet 引用它。 下面的示例使用一个包含多个会话的变量: Get-CimInstance -CimSession $sessions -ClassName Win32_OperatingSystem 配置会话选项 利用会话选项对象,可以为会话指定多个设置。 创建新会话时,可指定会话选项对象来配置会话。 下面的示例使用 DCOM 而不是 WS-MAN 来创建会话: $opt = New-CimSessionOption -Protocol Dcom $DcomSession = New-CimSession -ComputerName LON-DC1 -SessionOption $opt Get-CimInstance -ClassName Win32_BIOS -CimSession $DcomSession 上述代码中的第一行创建一个会话选项对象,它指定应使用 DCOM 协议进行连接 第二行使用该会话选项对象创建新的会话,并将其存储在变量中 最后一行使用会话来查询会话中定义的远程计算机,并返回所请求的信息 移除会话 创建会话后,它会在 PowerShell 实例关闭之前一直保存在内存中并可供使用。 可使用 Remove-CimSession cmdlet 手动移除会话。 下面的示例移除变量中包含的一个或多个会话: # 若你设置sessions的时候,这个变量控制的是多个主机的话,那你这一下子就可以移除全部了 $sessions | Remove-CimSession 若要移除特定远程计算机的会话,可查询该计算机的会话,然后将其移除,如以下示例所示: Get-CimSession -ComputerName LON-DC1 | Remove-CimSession 若要移除所有会话,请运行以下命令: Get-CimSession | Remove-CimSession 查询和操作存储库对象 发现存储库对象的方法 查询类的实例时,可以使用 Get-Member cmdlet 发现可用于该类型对象的方法 以下示例介绍如何使用 Get-Member 查看 Win32_Service 实例的属性和方法: Get-WmiObject -Class Win32_Service | Get-Member -MemberType Method 或者 Get-CimInstance -ClassName Win32_Service | Get-Member -MemberType Method 还可以使用 Get-CimClass 查看可用于特定类的方法: Get-CimClass -Class Win32_Service | Select-Object -ExpandProperty CimClassMethods 调用存储库对象的方法 若要在对象上使用方法,可以调用该方法 或者可以使用 Invoke-WmiMethod 或 Invoke-CimMethod cmdle 如果使用 cmdlet,则使用的 cmdlet 需要与正在使用的对象类型匹配 直接调用 如果已将 Windows Management Instrumentation (WMI) 对象加载到变量中,通常会使用直接调用 可以通过指定变量名称、点 (.) 和该方法来调用可用于该对象类型的方法 此方法类似于显示变量中包含的对象属性值的方式。 以下示例将后台处理程序服务作为 WMI 对象进行查询,然后调用 StopService 方法。 $WmiSpoolerService = Get-WmiObject -Class Win32_Service -Filter "Name='Spooler'" $WmiSpoolerService.StopService() 上一个示例中的 StopService 方法不需要为其传递任何参数,因此括号内没有值。 如果调用需要参数的方法,则参数的值将放在括号中。 以下示例将启动模式参数的值设置为 Manual: $WmiSpoolerService.ChangeStartMode("Manual") 若要确定方法所需的参数,则应查看该方法的类的相关文档。 请注意,WMI 方法的参数需要按特定顺序传递。 若要标识顺序,可以使用 GetMethodParameters() 方法。 以下示例查询 Change 方法的参数: $WmiSpoolerService.GetMethodParameters("Change") 如果方法需要多个参数,而你不想更改其中的部分参数,则可以为不想更改的参数传递一个$null值。 此外,无需指定位于要更改的参数之后的参数。 在以下示例中,Change 方法具有 11 个参数,但只配置了第二个参数(显示名称)。 $WimSpoolerService.Change($null,"Printer Service") 使用Invoke-WmiMethod 可以单独使用 Invoke-WmiMethod cmdlet,也可以使用管道向其发送 WMI 对象。 下面是工作原理相同的两个示例: Get-WmiObject -Class Win32_OperatingSystem | Invoke-WmiMethod -Name Win32Shutdown -Argument 0 或者 Invoke-WmiMethod -Class Win32_OperatingSystem -Name Win32Shutdown -Argument 0 使用Invoke-CimMethod Invoke-CimMethod cmdlet 提供与 Invoke-WmiMethod cmdlet 类似的功能,但 Invoke-CimMethod 的参数列表是一个字典对象 此类对象由一个或多个键值对组成 每个对的键是参数名称,每个对的值是相应的参数值 在以下示例中,Path 是参数的名称,Notepad.exe 是值: Invoke-CimMethod -ComputerName LON-DC1 -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='Notepad.exe'} 若要在字典中包含多个参数,请使用分号来分隔每个键值对 若要针对特定的对象实例调用方法,请先使用 Get-CimInstance cmdlet 检索对象 可以将对象直接通过管道传递给 Invoke-CimMethod cmdlet,或者先将其存储在变量中 如果对象提供了所有必要的信息,则无需指定任何参数 以下示例检索 Notepad.exe 的所有正在运行的实例,然后将其终止: Get-CimInstance -ClassName Win32_Process -Filter "Name='notepad.exe'" | Invoke-CimMethod -MethodName Terminate 如果将 -ComputerName 或 -CIMSession 参数与 Get-CimInstance 一起使用,并将生成的对象通过管道传递给 Invoke-CimMethod,则在对象来自的所有计算机或会话上调用该方法 例如,若要终止远程计算机上的进程,可以运行以下命令: Get-CimInstance -ClassName Win32_Process -Filter "Name='notepad.exe'" -Computername LON-DC1 | Invoke-CimMethod -MethodName Terminate