使用 Windows Management Instrumentation 进行诊断

Windows Communication Foundation (WCF) 通过 WCF Windows Management Instrumentation (WMI) 提供程序在运行时公开服务的检查数据。

启用 WMI

WMI 是Microsoft Web-Based 企业管理(WBEM)标准的实现。 有关 WMI SDK 的详细信息,请参阅 Windows Management Instrumentation。 WBEM 是一个行业标准,用于定义应用程序如何将管理工具接口公开给外部管理工具。

WMI 提供程序是在运行时通过与 WBEM 兼容的接口公开检测的组件。 它由一组具有属性/值对的 WMI 对象组成。 对可以是许多简单的类型。 管理工具可以在运行时通过接口连接到服务。 WCF 公开服务的属性,例如地址、绑定、行为和侦听器。

可以在应用程序的配置文件中激活内置的 WMI 提供程序。 这是通过 wmiProviderEnabledsystem.serviceModel> 部分中的诊断<>属性<完成的,如以下示例配置所示。

<system.serviceModel>  
    …  
    <diagnostics wmiProviderEnabled="true" />  
    …  
</system.serviceModel>  

此配置条目公开 WMI 接口。 管理应用程序现在可以通过此接口进行连接,并访问应用程序的管理工具。

访问 WMI 数据

可以通过许多不同的方式访问 WMI 数据。 Microsoft为脚本、Visual Basic 应用程序、C++应用程序和 .NET Framework 提供 WMI API。 有关详细信息,请参阅 使用 WMI

谨慎

如果使用 .NET Framework 提供的方法以编程方式访问 WMI 数据,则应注意,在建立连接时,此类方法可能会引发异常。 连接不是在实例构建 ManagementObject 期间建立的,而是在涉及实际数据交换的第一个请求时建立的。 因此,您应该使用 try..catch 块来捕获可能的异常。

可以更改 WMI 中跟踪 System.ServiceModel 源的跟踪和消息日志记录级别以及消息日志记录选项。 这可以通过访问 AppDomainInfo 实例来完成,该实例公开了以下布尔属性:LogMessagesAtServiceLevelLogMessagesAtTransportLevelLogMalformedMessages、 、 和 TraceLevel. 因此,如果您为消息日志记录配置了跟踪侦听器,但在配置中将这些选项设置为 false,则可以在应用程序运行时将其更改为 true。 这将有效地在运行时启用消息日志记录。 同样,如果在配置文件中启用消息日志记录,则可以使用 WMI 在运行时禁用它。

您应该知道,如果在配置文件中未指定用于消息日志记录的消息日志记录跟踪侦听器,或者未 System.ServiceModel 指定用于跟踪的跟踪侦听器,则即使 WMI 接受这些更改,您的任何更改也不会生效。 有关正确设置相应侦听器的更多信息,请参阅 配置消息日志记录配置跟踪。 配置指定的所有其他跟踪源的跟踪级别在应用程序启动时有效,并且无法更改。

WCF 公开了 GetOperationCounterInstanceName 一种脚本编写方法。 如果您为性能计数器实例提供作名称,则此方法将返回该实例名称。 但是,它不会验证您的输入。 因此,如果提供的作名称不正确,则会返回不正确的计数器名称。

OutgoingChannel如果未在方法中创建Service到目标服务的 WCF 客户端,则实例的属性Service不会对服务打开的连接到其他服务的通道进行计数。

谨慎 WMI 仅支持 TimeSpan 最多 3 个小数点的值。 例如,如果您的服务将其其中一个属性设置为 MaxValue,则在通过 WMI 查看时,其值将在 3 个小数点后截断。

安全

由于 WCF WMI 提供程序允许在环境中发现服务,因此在授予对它的访问权限时应格外小心。 如果您放宽默认的仅限管理员访问权限,则可以允许不太受信任的方访问您环境中的敏感数据。 具体而言,如果放宽对远程 WMI 访问的权限,则可能会发生泛洪攻击。 如果进程被过多的 WMI 请求淹没,则其性能可能会降低。

此外,如果放宽对 MOF 文件的访问权限,则不太受信任的参与方可以纵 WMI 的行为并更改在 WMI 架构中加载的对象。 例如,可以删除字段,以便对管理员隐藏关键数据,或者将未填充或导致异常的字段添加到文件中。

默认情况下,WCF WMI 提供程序为管理员授予“执行方法”、“提供程序写入”和“启用帐户”权限,并为 ASP.NET、本地服务和网络服务授予“启用帐户”权限。 具体而言,在非 Windows Vista 平台上,ASP.NET 帐户具有对 WMI ServiceModel 命名空间的读取访问权限。 如果不想将这些权限授予特定用户组,则应停用 WMI 提供程序(默认情况下处于禁用状态),或禁用特定用户组的访问。

此外,当您尝试通过配置启用 WMI 时,由于用户权限不足,WMI 可能无法启用。 但是,不会将任何事件写入事件日志来记录此失败。

要修改用户权限级别,请使用以下步骤。

  1. 单击“开始”,然后单击“运行”并键入 compmgmt.msc

  2. 右键单击 Services and Application/WMI Controls 以选择 Properties ( 属性)。

  3. 选择 Security 选项卡,然后导航到 Root/ServiceModel 命名空间。 单击"安全设置"按钮。

  4. 选择要控制访问权限的特定组或用户,然后使用 AllowDeny 复选框配置权限。

向其他用户授予 WCF WMI 注册权限

WCF 向 WMI 公开管理数据。 它通过托管进程内 WMI 提供程序(有时称为“分离的提供程序”)来实现此目的。 对于要公开的管理数据,注册此提供程序的帐户必须具有相应的权限。 在 Windows 中,默认情况下,只有一小部分特权帐户可以注册分离的提供程序。 这是一个问题,因为用户通常希望从不在默认集中的帐户下运行的 WCF 服务中公开 WMI 数据。

要提供此访问权限,管理员必须按以下顺序向其他账户授予以下权限:

  1. 访问 WCF WMI 命名空间的权限。

  2. 注册 WCF 分离 WMI 提供程序的权限。

授予 WMI 命名空间访问权限

  1. 运行以下 PowerShell 脚本。

    write-host ""  
    write-host "Granting Access to root/servicemodel WMI namespace to built in users group"  
    write-host ""  
    
    # Create the binary representation of the permissions to grant in SDDL  
    $newPermissions = "O:BAG:BAD:P(A;CI;CCDCLCSWRPWPRCWD;;;BA)(A;CI;CC;;;NS)(A;CI;CC;;;LS)(A;CI;CC;;;BU)"  
    $converter = new-object system.management.ManagementClass Win32_SecurityDescriptorHelper  
    $binarySD = $converter.SDDLToBinarySD($newPermissions)  
    $convertedPermissions = ,$binarySD.BinarySD  
    
    # Get the object used to set the permissions  
    $security = gwmi -namespace root/servicemodel -class __SystemSecurity  
    
    # Get and output the current settings  
    $binarySD = @($null)  
    $result = $security.PsBase.InvokeMethod("GetSD",$binarySD)  
    
    $outsddl = $converter.BinarySDToSDDL($binarySD[0])  
    write-host "Previous ACL: "$outsddl.SDDL  
    
    # Change the Access Control List (ACL) using SDDL  
    $result = $security.PsBase.InvokeMethod("SetSD",$convertedPermissions)
    
    # Get and output the current settings  
    $binarySD = @($null)  
    $result = $security.PsBase.InvokeMethod("GetSD",$binarySD)  
    
    $outsddl = $converter.BinarySDToSDDL($binarySD[0])  
    write-host "New ACL:      "$outsddl.SDDL  
    write-host ""  
    

    此 PowerShell 脚本使用安全描述符定义语言 (SDDL) 向 Built-In 用户组授予对“root/servicemodel”WMI 命名空间的访问权限。 它指定以下 ACL:

    • Built-In 管理员 (BA) - 已具有访问权限。

    • 网络服务 (NS) - 已具有访问权限。

    • 本地系统 (LS) - 已具有访问权限。

    • Built-In Users ( 用户) - 要授予访问权限的组。

授予提供程序注册访问权限

  1. 运行以下 PowerShell 脚本。

    write-host ""  
    write-host "Granting WCF provider registration access to built in users group"  
    write-host ""  
    # Set security on ServiceModel provider  
    $provider = get-WmiObject -namespace "root\servicemodel" __Win32Provider  
    
    write-host "Previous ACL: "$provider.SecurityDescriptor  
    $result = $provider.SecurityDescriptor = "O:BUG:BUD:(A;;0x1;;;BA)(A;;0x1;;;NS)(A;;0x1;;;LS)(A;;0x1;;;BU)"  
    
    # Commit the changes and display it to the console  
    $result = $provider.Put()  
    write-host "New ACL:      "$provider.SecurityDescriptor  
    write-host ""  
    

向任意用户或组授予访问权限

本节中的示例向所有本地用户授予 WMI 提供程序注册权限。 如果要向非内置用户或组授予访问权限,则必须获取该用户或组的安全标识符 (SID)。 没有简单的方法可以获取任意用户的 SID。 一种方法是以所需用户身份登录,然后发出以下 shell 命令。

Whoami /user  

有关详细信息,请参阅 已知 SID。

访问远程 WMI 对象实例

如果需要访问远程计算机上的 WCF WMI 实例,则必须在用于访问的工具上启用数据包隐私。 以下部分介绍如何使用 WMI CIM Studio、Windows Management Instrumentation Tester 以及 .NET SDK 2.0 实现这些目标。

WMI CIM 工作室

如果已安装 WMI 管理工具,则可以使用 WMI CIM Studio 访问 WMI 实例。 这些工具位于以下文件夹中:

%windir%\Program Files\WMI Tools\

  1. Connect to namespace: (连接到命名空间: ) 窗口中,键入 root\ServiceModel 并单击 OK (确定)。

  2. WMI CIM Studio 登录 窗口中,单击 选项 >> 按钮以展开窗口。 选择 Packet privacy (数据包隐私 ) 作为 Authentication level (身份验证级别),然后单击 OK (确定)。

Windows Management Instrumentation 测试器

此工具由 Windows 安装。 要运行它,请在 Start/Run (开始/运行) 对话框中键入 cmd.exe 来启动命令控制台,然后单击 OK(确定)。 然后,在命令窗口中键入 wbemtest.exe 。 然后启动 Windows Management Instrumentation Tester 工具。

  1. 点击 Connect 窗口右上角的按钮。

  2. 在新窗口中,为 Namespace 字段输入 root\ServiceModel,然后选择 Packet privacy 作为 Authentication level。 单击连接

使用托管代码

还可以使用命名空间提供的 System.Management 类以编程方式访问远程 WMI 实例。 以下代码示例演示了如何执行此作。

String wcfNamespace = $@"\\{this.serviceMachineName}\Root\ServiceModel");
  
ConnectionOptions connection = new ConnectionOptions();  
connection.Authentication = AuthenticationLevel.PacketPrivacy;  
ManagementScope scope = new ManagementScope(this.wcfNamespace, connection);