简体   繁体   English

如何在 Windows 10 上加速 powershell 以获取防火墙规则?

[英]How can I speed up powershell to get firewall rules on windows 10?

I need to enumerate all firewall rules on windows 10 using PowerShell.我需要使用 PowerShell 枚举 Windows 10 上的所有防火墙规则。 I switched to PowerShell from netsh because some built-in rules were getting funny names like @{microsoft.windows.shellexperiencehost_10.0.17134.1_neutral_neutral_cw5n1h2txyewy?ms-resource://microsoft.windows.shellexperiencehost/resources/pkgdisplayname} which I was unable to manage with netsh .我从netsh切换到 PowerShell,因为一些内置规则的名称很有趣,例如@{microsoft.windows.shellexperiencehost_10.0.17134.1_neutral_neutral_cw5n1h2txyewy?ms-resource://microsoft.windows.shellexperiencehost/resources/pkgdisplayname}而我无法使用使用netsh管理。 Switching to PowerShell shows that the real name was a UID and fixed my problem but the code is really slow to run:切换到 PowerShell 显示真实名称是一个 UID 并解决了我的问题,但代码运行起来真的很慢:

PS C:\Users\vagrant\Desktop> Measure-Command {.\ps-slow.ps1 show}
Seconds           : 48
...

In contrast to Measure-Command {show-netfirewallrule} :Measure-Command {show-netfirewallrule}

...
Milliseconds      : 644

And netsh :netsh

PS C:\Users\vagrant\Desktop> Measure-Command { netsh advfirewall firewall show rule all verbose}
...
TotalSeconds      : 1.0588127

By commenting out the Get-NetFirewall*Filter part of the script it runs at full speed but of course is missing all the data I want.通过注释掉脚本的Get-NetFirewall*Filter部分,它会全速运行,但当然缺少我想要的所有数据。 The idea is to collect detailed info on all firewall rules and then output the lot as JSON.这个想法是收集所有防火墙规则的详细信息,然后将批次输出为 JSON。

Does anyone have an idea how to optimize this?有谁知道如何优化它? I'm a PowerShell noob so I'm hoping I missed something obvious.我是 PowerShell 菜鸟,所以我希望我错过了一些明显的东西。 The complete script is:完整的脚本是:

Show-NetFirewallRule | `
    Where-Object { $_.cimclass.toString() -eq "root/standardcimv2:MSFT_NetFirewallRule" } | `
        ForEach-Object { `
            $af = $_ | Get-NetFirewallAddressFilter | Select-Object -First 1; # Assumes only one filter
            $appf = $_ | Get-NetFirewallApplicationFilter | Select-Object -First 1; # Assumes only one filter
            $pf = $_ | Get-NetFirewallPortFilter | Select-Object -First 1; # Assumes only one filter
            $if = $_ | Get-NetFirewallInterfaceTypeFilter | Select-Object -First 1; # Assumes only one filter

            New-Object -Type PSCustomObject -Property @{
              Name = $_.Name
              DisplayName = $_.DisplayName
              Description = $_.Description
              Enabled = $_.Enabled.toString()
              Action = $_.Action.toString()
              Direction = $_.Direction.toString()
              EdgeTraversalPolicy = $_.EdgeTraversalPolicy.toString()
              Profile = $_.Profile.toString()
              DisplayGroup = $_.DisplayGroup
              # Address Filter
              LocalAddress = $af.LocalAddress
              RemoteAddress = $af.RemoteAddress
              LocalIp = $af.LocalIp
              RemoteIp = $af.RemoteIp
              # Port Filter
              LocalPort = $pf.LocalPort
              RemotePort = $pf.RemotePort
              Protocol = $pf.Protocol
              IcmpType = $pf.IcmpType
              # Application Filter
              Program = $appf.Program
              # Interface Filter
              InterfaceType = $if.InterfaceType.toString()
            }
        } | Convertto-json

Ok thanks heaps for everyone who posted.好的,感谢所有发帖的人。 The original problem was that netsh leaves unresolved names like:最初的问题是netsh留下未解析的名称,例如:

@{Microsoft.Todos_1.41.12842.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Todos/Resources/app_name_ms_todo}
@{Microsoft.Todos_1.41.12842.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Todos/Resources/app_name_ms_todo}

In its output that can only be resolved by PowerShell, using the original script.在只能由 PowerShell 解析的输出中,使用原始脚本。 The problem with this approach is that this is very slow (minutes).这种方法的问题是速度非常慢(几分钟)。

Suggestions on this thread and from colleagues were:关于这个线程和同事的建议是:

  • Use PS batching (made things slower - suggesting WMI bottleneck)使用 PS 批处理(使事情变慢 - 暗示 WMI 瓶颈)
  • Read the registry directly (mostly works, but data is left unresolved in different ways and would require slow registry scans to resolve - eg @FirewallAPI.dll,-25427 to whatever resource that references)直接读取注册表(大多数情况下有效,但数据以不同的方式未解析,并且需要缓慢的注册表扫描才能解析 - 例如@FirewallAPI.dll,-25427到引用的任何资源)
  • Use the COM API New-Object -ComObject HNetCfg.FwPolicy (has the same output problems as netsh )使用 COM API New-Object -ComObject HNetCfg.FwPolicy (与netsh有相同的输出问题)
  • Adjust use of pipelines/object creation (no measurable impact)调整管道/对象创建的使用(没有可衡量的影响)

In summary, the optimization I want is impossible without sacrificing the data that I want.综上所述,如果不牺牲我想要的数据,我想要的优化是不可能的。 I'm going to attempt to use the faster COM API/netsh most of the time but switch to using the powershell API when there is no choice (when unresolved names force us to)我将在大部分时间尝试使用更快的 COM API/netsh,但在别无选择时切换到使用 powershell API(当未解析的名称迫使我们这样做时)

This approach is faster, so, maybe a different approach for you to get the same information.这种方法更快,因此,您可能会采用不同的方法来获取相同的信息。

param
( 
    [switch]$Local, 
    [switch]$GPO 
) 

# If no switches are set the script will default to local firewall rules 
if (!($Local) -and !($Gpo)) 
{ $Local = $true } 

$RegistryKeys = @() 

if ($Local) {$RegistryKeys += 'Registry::HKLM\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules'} 
if ($GPO) {$RegistryKeys += 'Registry::HKLM\Software\Policies\Microsoft\WindowsFirewall\FirewallRules'} 

Foreach ($Key in $RegistryKeys) 
{ 
    if (Test-Path -Path $Key) 
    { 
        (Get-ItemProperty -Path $Key).PSObject.Members | 
        Where-Object {
        (@('PSPath','PSParentPath','PSChildName') -notcontains $_.Name) -and 
        ($_.MemberType -eq 'NoteProperty') -and 
        ($_.TypeNameOfValue -eq 'System.String')} | 
         ForEach-Object { 

            # Prepare hashtable 
            $HashProps = @{ 
                NameOfRule = $_.Name 
                RuleVersion = ($_.Value -split '\|')[0] 
                Action = $null 
                Active = $null 
                Dir = $null 
                Protocol = $null 
                LPort = $null 
                App = $null 
                Name = $null 
                Desc = $null 
                EmbedCtxt = $null 
                Profile = $null 
                RA4 = $null 
                RA6 = $null 
                Svc = $null 
                RPort = $null 
                ICMP6 = $null 
                Edge = $null 
                LA4 = $null 
                LA6 = $null 
                ICMP4 = $null 
                LPort2_10 = $null 
                RPort2_10 = $null 
            } 

            # Determine if this is a local or a group policy rule and display this in the hashtable 
            if ($Key -match 'HKLM\\System\\CurrentControlSet') 
            {  $HashProps.RuleType = 'Local' } 
            else 
            {  $HashProps.RuleType = 'GPO' } 

            # Iterate through the value of the registry key and fill PSObject with the relevant data 
            ForEach ($FireWallRule in ($_.Value -split '\|')) 
            { 
                switch (($FireWallRule -split '=')[0]) 
                { 
                    'Action' {$HashProps.Action = ($FireWallRule -split '=')[1]} 
                    'Active' {$HashProps.Active = ($FireWallRule -split '=')[1]} 
                    'Dir' {$HashProps.Dir = ($FireWallRule -split '=')[1]} 
                    'Protocol' {$HashProps.Protocol = ($FireWallRule -split '=')[1]} 
                    'LPort' {$HashProps.LPort = ($FireWallRule -split '=')[1]} 
                    'App' {$HashProps.App = ($FireWallRule -split '=')[1]} 
                    'Name' {$HashProps.Name = ($FireWallRule -split '=')[1]} 
                    'Desc' {$HashProps.Desc = ($FireWallRule -split '=')[1]} 
                    'EmbedCtxt' {$HashProps.EmbedCtxt = ($FireWallRule -split '=')[1]} 
                    'Profile' {$HashProps.Profile = ($FireWallRule -split '=')[1]} 
                    'RA4' {[array]$HashProps.RA4 += ($FireWallRule -split '=')[1]} 
                    'RA6' {[array]$HashProps.RA6 += ($FireWallRule -split '=')[1]} 
                    'Svc' {$HashProps.Svc = ($FireWallRule -split '=')[1]} 
                    'RPort' {$HashProps.RPort = ($FireWallRule -split '=')[1]} 
                    'ICMP6' {$HashProps.ICMP6 = ($FireWallRule -split '=')[1]} 
                    'Edge' {$HashProps.Edge = ($FireWallRule -split '=')[1]} 
                    'LA4' {[array]$HashProps.LA4 += ($FireWallRule -split '=')[1]} 
                    'LA6' {[array]$HashProps.LA6 += ($FireWallRule -split '=')[1]} 
                    'ICMP4' {$HashProps.ICMP4 = ($FireWallRule -split '=')[1]} 
                    'LPort2_10' {$HashProps.LPort2_10 = ($FireWallRule -split '=')[1]} 
                    'RPort2_10' {$HashProps.RPort2_10 = ($FireWallRule -split '=')[1]} 
                    Default {} 
                } 
            } 

            # Create and output object using the properties defined in the hashtable 
            New-Object -TypeName 'PSCustomObject' -Property $HashProps
        } 
    } 
}

# Partial results

Action      : Allow
LPort2_10   : 
RuleType    : Local
LPort       : 135
Edge        : 
LA6         : 
Dir         : In
Desc        : @icsvc.dll,-710
ICMP4       : 
RA4         : 
Name        : @icsvc.dll,-709
LA4         : 
App         : %SystemRoot%\system32\svchost.exe
ICMP6       : 
Protocol    : 6
RuleVersion : v2.0
NameOfRule  : vm-monitoring-dcom
RPort       : 
Svc         : RpcSs
RA6         : 
Profile     : 
EmbedCtxt   : @icsvc.dll,-700
RPort2_10   : 
Active      : FALSE

Does Get-NetFirewallRule give you what you want? Get-NetFirewallRule满足您的需求吗?

$MyRules = Get-NetFirewallRule
foreach ($rule in $MyRules) {
    [the rest of your code]
}

I don't think this is well understood, but the get-netfirewall*filter commands are meant to do things faster.我不认为这很好理解,但 get-netfirewall*filter 命令旨在更快地完成任务。 Just compare the first pipeline to the second pipeline.只需将第一个管道与第二个管道进行比较。 It's like using the -filter option in other commands like get-childitem or get-wmiobject.这就像在 get-childitem 或 get-wmiobject 等其他命令中使用 -filter 选项一样。 I don't think even the writers of the online help understand this.我认为即使是在线帮助的作者也无法理解这一点。 It's like powershell is only 80% documented.就像 powershell 只有 80% 的文档。

Get-NetFirewallRule | 
Get-NetFirewallPortFilter | 
Where LocalPort -eq 3389 | Get-NetFirewallRule | 
Set-NetFirewallRule -RemoteAddress 192.168.1.1 -WhatIf
Get-NetFirewallPortFilter | 
Where LocalPort -eq 3389 | Get-NetFirewallRule | 
Set-NetFirewallRule -RemoteAddress 192.168.1.1 -WhatIf

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM