如何加快Powershell的速度以在Windows 10上获得防火墙规则?

时间:2018-11-05 00:34:12

标签: windows powershell optimization firewall

我需要使用PowerShell枚举Windows 10上的所有防火墙规则。我从netsh切换到PowerShell,因为某些内置规则获得了{em>这样的名字,例如@{microsoft.windows.shellexperiencehost_10.0.17134.1_neutral_neutral_cw5n1h2txyewy?ms-resource://microsoft.windows.shellexperiencehost/resources/pkgdisplayname},而我无法用netsh进行管理。切换到PowerShell显示真实名称是UID,并解决了我的问题,但是代码确实运行缓慢:

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

Measure-Command {show-netfirewallrule}相比:

...
Milliseconds      : 644

还有netsh

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

通过注释掉脚本的Get-NetFirewall*Filter部分,它可以全速运行,但是当然会丢失我想要的所有数据。想法是收集有关所有防火墙规则的详细信息,然后将其输出为JSON。

有人知道如何优化它吗?我是PowerShell的新手,所以我希望我能错过一些显而易见的事情。完整的脚本是:

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

4 个答案:

答案 0 :(得分:0)

这种方法更快,因此,获得相同信息的方法可能会有所不同。

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

答案 1 :(得分:0)

Get-NetFirewallRule会给您想要的东西吗?

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

答案 2 :(得分:0)

好的,谢谢大家发帖。最初的问题是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}

在输出中,只能使用原始脚本由PowerShell解析。这种方法的问题是,这很慢(几分钟)。

对此线程和同事的建议是:

  • 使用PS批处理(速度变慢-提示WMI瓶颈)
  • 直接读取注册表(大多数情况下可以工作,但是无法以不同的方式解析数据,并且需要缓慢的注册表扫描才能解决-例如@FirewallAPI.dll,-25427引用的任何资源)
  • 使用COM API New-Object -ComObject HNetCfg.FwPolicy(与netsh具有相同的输出问题)
  • 调整管道/对象创建的使用(无可衡量的影响)

总而言之,如果不牺牲我想要的数据,我想要的优化是不可能的。我将尝试大多数时候使用速度更快的COM API / netsh ,但在别无选择时(当无法解析的名称迫使我们使用时)切换到使用powershell API。

答案 3 :(得分:0)

我认为这不是一个很好的理解,但是get-netfirewall * filter命令可以更快地执行操作。只需将第一个管道与第二个管道进行比较即可。就像在其他命令(如get-childitem或get-wmiobject)中使用-filter选项一样。我认为甚至连在线帮助的作者都无法理解这一点。就像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