概要

Windows管理规范(Windows Management Instrumentation, WMI)可能是微软提供给管理员使用最优秀的工具之一。WMI可以从计算机中收集大量系统信息。WMI是一个外部技术,PowerShell仅仅与其接口交互而已。

典型的Windows计算机包含数万个管理信息,WMI会将这些信息整理成易于访问的形式。

WMI是一个巨大的、复杂的技术,如果要深入研究,可以阅读书籍 PowerShell and WMI,作者是微软MVP Richard Siddaway(Manning, 2012)。该书提供了大量示例,并讨论了关于PowerShell v3引入关于CIM Cmdlets的新功能。

命名空间

在最顶层,WMI被组织成命名空间(namespaces)。可以把命名空间想象为关联到特定产品或技术的一个文件夹。比如:

  • root\CIMv2”,该命名空间包含了所有Windows操作系统和计算机硬件信息。
  • 而“root\MicrosoftDNS”命名空间包含了所有关于DNS服务器(假设已经安装了该角色)的信息。
  • 在客户端计算机上,“root\SecurityCenter”包含了关于防火墙、杀毒软件等工具的信息。
    • 新版本的Windows使用“root\SecurityCenter2”代替它。

通过微软管理控制台(Microsoft Management Console, MMC)的WMI控制单元在本机上产生的命名空间:
image.png
在命名空间中,WMI被分成一系列的类,每个类都是可用于WMI查询的管理单元。比如:

  • 在“root\SecurityCenter”中的“Antivirus-Product”类被设计用于保存反间谍软件的信息;
  • 在“root\CIMv2”中的“Win32_LogicalDisk”类被设计用于保存逻辑磁盘的信息。

注意:不是所有的计算机都包含相同的WMI命名空间或类。比如,新版本的Windows存在“Root\SecurityCenter2”命名空间,而不是“Root\SecurityCenter”命名空间;而前者在新版本的计算机中包含了所有信息。

实例和类

下面看一下从“root\SecurityCenter2”中查询“AntiSpywareProduct”:

  1. Get-CimInstance -Namespace root\SecurityCenter2 -ClassName AntiSpywareProduct

当有一个或多个可管理组件时,你可以看到在对应的类中有相同数量的实例(instances)。一个实例由类代表了一个现实世界的事件。如果计算机只有一个单一的BIOS,那么在“root\CIMv2”中会有一个关于“Win32_BIOS”的实例。如果计算机安装了100个后台服务,你会看到100个“Win32Service”的实例。
请注意,在“root\CIMv2”中的类型一般以“Wim32
”(即使在64位系统中亦然)或“CIM_”(Common Information Model的缩写,是WMI建立的标准)开头。

不同命名空间下的类型名称重复也存在可能性,虽然这种情况很少,但在WMI中允许存在,因为每个命名空间实际上是一种有边界的容器。当引用一个类时,同时需要引用其命名空间,以便WMI知道从哪里找到对应的类,从而避免因为多个重名,但不属于同一个命名空间的类造成混乱。

所有这些实例、类和其他不可名状的东西统称为WMI仓库(WMI Repository)。
表面看上去,使用WMI十分简单:只需要指出哪个类包含你要的信息,然后从WMI中查询类的实例,最后检查实例的属性得知其管理信息。有时候需要实例执行一个方法,从而启动一个动作(action)或开始一个配置变更(configuration change)。

WMI的不足

微软为WMI制定了一系列的编程标准,但是产品组或多或少把精力放在如何实现类和是否对其文档化。结果就是使得WMI变得混乱。
例如,在“root\CIMv2”命名空间中,只有很少的类提供了让你修改配置设置的方法(methods)。因为属性是只读的,意味着你必须使用方法来修改。如果对应的方法不存在,你就不能使用WMI来修改这些类。

微软从来没规定某个产品必须使用WMI,或者如果这个产品使用了WMI,必须公开WMI的每个可能的部分。微软的DHCP服务可以访问到WMI。正如旧版本的Windows服务器一样,你可以查询网卡的配置,但是不能查到连接速度,因为这些信息不支持通过WMI查询。

但是微软也对此进行了改善,微软正在努力使PowerShell Cmdlets尽可能完成更多的管理任务。比如,过去WMI仅用于某种特定的编程方式重启远程计算机,这个方法由“Win32_OperatingSystem”类实现。而现在,PowerShell提供了名称为“Restart-Computer”的Cmdlet来实现。在某些情况下,Cmdlets内部会通过WMI实现,而无须直接调用WMI。
实际上,在PowerShell v3及后续版本中,你会留意到大量“CIM”命令。在大部分情况下,这些命令都是对WMI的某些部分进行了封装,从而提供了更加以PowerShell为中心的方式与WMI交互。

探索WMI

WMI没有特定的工具。你可以尝试在搜索引擎中搜索WMI explorer并查看结果,也可以尝试访问https://www.ks-soft.net/hostmon.eng/wmi/index.htm

由于每台计算机上的WMI命名空间和类都不尽相同,所以你需要把工具直接在准备查询的机器上运行,以便看到对应机器的WMI仓库。

范例:现在来查询一组计算机,并从中得知它们的图标间距设置。这个任务依赖于Windows桌面,并且是操作系统的核心部分,所以我们从“root\CIMv2”类开始。
image.png
搜索引擎是查询所需类信息的另一种好方法。在icon spacing之前添加wmi前缀作为关键字进行搜索。比如,在搜索引擎中查找“wmi icon spacing”。结果中会有https://stackoverflow.com/questions/202971/formula-or-api-for-calulating-desktop-icon-spacing-on-windows-xp,在该页中可以会找到一些C#代码。

  1. ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2","SELECT * FROM Win32_Desktop");

对此,我们并不知道上述代码的意思,但是“Win32_Desktop”看上去像是个类名称。接下来可以查询该类名,这样的查询通常会帮助我们找到一些存在的文档。

另外一个途径是使用PowerShell本身。比如,我们想知道一些关于磁盘的信息,那么需要从猜测正确的命名空间开始。但是我们已经知道“root\CIMv2”包含了所有OS核心和硬件设备的信息,所以可以使用下面的命令。
image.png
最终找到了“Win32_LogicalDisk”。

注意:这些名称以“**CIM_**”开头的类通常是基类,你通常不能直接使用这些类。“Win32”版本的类是Windows特有的,并且这种前缀仅存在于特定命名空间——其他命名空间不使用以CIM作为前缀这种命名方式。

WMI和CIM

在PowerShell v3及后续版本中,有两种与WMI交互的方式。

  • 所谓的“**WMI Cmdlets**”(别名Gwmi),例如“Get-WmiObject”与“Invoke-WmiMethod”——这些都是遗留命令,但是微软不会对它们进行后续开发投入。它们与远程过程调用(RPC)交互,也就是说,只有在防火墙支持状态审查时才能通过防火墙(实际上很难)。
  • 新版的“**CIM Cmdlets**”,例如“Get-CimInstance”与“Invoke-CimMethod”——它们或多或少等价于旧版本的“WMI Cmdlets”,但它们通过WS-MAN(Windows远程管理服务)交互,替代原有的RPCs。这是微软的主方向,执行“Get-Command –noun CIM*”可以显示很多微软提供的这类命令的功能。

毫无疑问,这些命令的后端同样是WMI,其差异在于如何交互和如何被使用。对于已经装有PowerShell和启用了Windows远程管理服务的新系统,CIM Cmdlets提供最佳体验——微软也会对其进行持续的功能及性能改进。

实际上,在Windows Server 2012 R2以及更新版本中,旧版的WMI默认为禁用状态,因此尽可能使用CIM。除此之外,CIM cmdlet可以使用旧版的RPC(或DCOM)协议通讯。因此即使与老机器进行通讯时,也可以仅使用CIM cmdlet。

使用Get-WmiObject

通过“Get-WmiObject”命令,可以指定一个命名空间、一个类名称甚至远程计算机的名称以及备用凭据名。如果需要,还可以从指定的计算机中查询该类的所有实例。如果需要减少类实例的返回结果,甚至可以提供筛选条件实现。
使用下面的语法,可以获取一个命名空间中的类列表。
image.png
也可以通过指定命名空间和类型查询一个类:
image.png
其中“root\CIMv2”是系统默认的命名空间,所以如果你的类在该命名空间中,可以不显式指定。同时,“-class”是位置参数,也就是说,如果把类名称放到第一个位置,它依旧能正常工作。

  1. PS C:\> gwmi antispywareproduct -Namespace root\securitycenter2
  2. PS C:\> Get-WmiObject win32_desktop -Namespace root\CIMv2
  3. # 可以不显式指定root\CIMv2
  4. PS C:\> Get-WmiObject win32_desktop

查看属性

对于许多WMI类,PowerShell的默认配置已经设定了需要展示的属性。“Win32_OperatingSystem”是一个很好的例子,因为它默认仅在列表中展示了6个属性。可以把WMI对象用管道传输到GmFormat-List *中,以便查看所有可用的属性。
image.png

过滤指定规则

另外,“-filter”参数允许指定规则,来查询特定实例。该参数使用起来有点棘手。
image.png
对于该命令和输出结果,需要注意下面事项:

  • 筛选条件通常被双引号包裹。
  • 筛选比较操作符,并不使用PowerShell的常规操作符“-eq”或“-lt”,而使用更加传统的操作符,比如=、>、<、<=、>=和<>。
    • 可以使用“LIKE”操作符,但在匹配值时必须使用“%”作为字符通配符,如“NAME LIKE '%administrator%'”。
    • 注意,这里不能像PowerShell一样使用*作为通配符。
  • 字符串匹配是以单引号包裹,这也是筛选表达式的最外层的引号是双引号的原因。
  • 避免在WMI中使用反斜杠。当需要使用文本的反斜杠时,必须使用两个反斜杠替代
  • Gwmi的输出结果总会包含大量系统属性,PowerShell的默认显示配置通常会隐藏这些属性。
    • 系统属性名称以双下划线开始,有两个非常有用的属性:
    • __SERVER:包含被查询的实例所在的计算机名称。该属性与“PSComputerName”属性功能一致。
    • __PATH:是实例本身的绝对引用,可以用来查询实例。

      检索远程计算机信息

      该Cmdlet不仅可以从远程计算机中查询信息,也可以从多台计算机中检索。可以使用任何可以生成一个包含计算机名称或IP列表的技术。
      image.png
      计算机名称按顺序连接,如果某一台计算机不可用,该Cmdlet会产生一个错误,并跳过这台计算机,并转向下一台计算机。对于不可用的计算机,Cmdlet通常需要等待直到发生超时,意味着Cmdlet可能会暂停30~45秒之后才决定放弃这台计算机,然后产生错误并继续向后连接。
      一旦查询到一个WMI实例的集合后,就可以把它们用管道连接到任何以“-Object”Cmdlet、“Format-”Cmdlet或“Out-”“Export-”“ConvertTo-”开头Cmdlet中。你可以使用自定义表格显示“Win32_BIOS”类的信息。
      image.png

      格式化输出

      可以创建一个关于表的自定义列,并用列的表达式执行一个全新的WMI查询。 ```powershell PS C:> gwmi -Class win32_bios -ComputerName localhost |

      Format-Table @{n=’ComputerName’; e={$.__SERVER}}, @{l=’BIOSSerial’; exp={$.SerialNumber}}, @{n=’OSBuild’; e={gwmi -class win32opreatingsystem -computer $.__SERVER | select -expand BuildNumber}} -AutoSize

ComputerName BIOSSerial OSBuild


IM20211010 M9NRCX05M144394

  1. 下面是上述示例说明:
  2. - Get-WmiObject”从两台计算机中查询“Win32_BIOS”信息。
  3. - 结果被管道传输到“Format-Table”,并创建了三个自定义列。
  4. - 第一列:名称为ComputerName,使用“Win32_BIOS”实例中的“__SERVER”系统属性得出。
  5. - 第二列:名称为BIOSSerial,使用“Win32_BIOS”实例中的“SerialNumber”属性得出。
  6. - 第三列:名称为OSBuild
  7. - 该列执行一个新的“Get-WmiObject”查询,从“Win32_BIOS”实例的“`__SERVER`”属性中查询“Win32_OperatingSystem”类。
  8. - 然后把结果用管道传输到“Select-Object”中,这些信息来自于“Win32_OperatingSystem”实例的“BuildNumber”属性的内容,并用于填充OSBuild列的值。
  9. <a name="O1JK6"></a>
  10. ## 使用Get-CimInstance
  11. Get-CimInstancePowerShell v3引入的新命令,与“Get-WmiObject”有很多相似的地方,但是也有几个语法上的差异。
  12. - 需要使用“-ClassName”代替“-Class”(可简写为-Class)。
  13. - 没有用于列出命名空间中的所有类的“-List”参数。
  14. - 可以使用“Get-CimClass”并搭配“-Namespace”参数获取类列表。
  15. - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/635565/1654340075713-9e69ad76-729a-4d65-ad16-3ac6cac3277c.png#clientId=u9907fc6f-3a92-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=367&id=ud4e5e01f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=367&originWidth=1267&originalType=binary&ratio=1&rotation=0&showTitle=false&size=463912&status=done&style=none&taskId=u73a2ffc0-541c-4220-9a90-185ccc29d87&title=&width=1267)
  16. - **没有“-Credential”参数**;
  17. - 如果需要使用凭据从远程计算机查询,可以通过“Invoke-Command”发送“`Get-CimInstance`”。
  18. 范例:查看逻辑磁盘信息
  19. ```powershell
  20. PS C:\> Get-CimInstance -ClassName Win32_LogicalDisk
  21. DeviceID DriveType ProviderName VolumeName Size FreeSpace
  22. -------- --------- ------------ ---------- ---- ---------
  23. C: 3 OS 128849350656 10425810944
  24. D: 3 Data 358349795328 30610714624
  25. Z: 4 \\10.1.2.83\d

范例:使用替代凭据查询远程计算机进程

  1. PS C:\> Invoke-Command -ScriptBlock { Get-CimInstance -ClassName Win32_Process } -ComputerName im20211010 -Credential im30\rick