如何在PowerShell自定义对象中替换指定字段值及脚本问题排查
Windows安全事件ID 5152字段替换问题
基础代码及输出
以下是获取安全事件ID 5152的基础代码:
foreach ($event in Get-WinEvent -FilterHashtable @{LogName='Security';ID=5152}) { $xml = [xml]$event.toxml(); $xml.event.eventdata.data | foreach { $hash = @{} } { $hash[$_.name] = $_.'#text' } { [pscustomobject]$hash } | Where FilterOrigin -notmatch 'stealth|unknown|Query User Default' }
输出截图:
需求
需要对以下字段进行替换:
- 将
FilterOrigin替换为防火墙显示名 - 将
Direction替换为「入站」或「出站」(%%14592对应入站,%%14593对应出站) - 将
Protocol替换为对应名称(如6对应TCP,17对应UDP)
只需掌握一两个字段的替换方法,即可自行完成其余字段处理。
初次尝试及问题
尝试替换FilterOrigin的代码如下:
foreach ($event in Get-WinEvent -FilterHashtable @{LogName='Security';ID=5152}) { $xml = [xml]$event.toxml(); $xml.event.eventdata.data | foreach { $hash = @{} } { $hash[$_.name] = $_.'#text' } { [pscustomobject]$hash } | Where FilterOrigin -notmatch 'stealth|unknown|Query User Default' | ForEach-Object { if ($_.filterorigin -match ($pattern = '{.+?}')) { $_.filterorigin -replace $pattern, (Get-NetFirewallRule -Name $Matches[0]).DisplayName } } }
问题:输出仅显示防火墙displaynames,说明脚本能识别FilterOrigin中的防火墙ID,但未在对象内完成替换。
更新后的脚本及问题
更新后的脚本:
foreach ($event in Get-WinEvent -FilterHashtable @{LogName='Security';ID=5152}) { $xml = [xml]$event.toxml(); $xml.event.eventdata.data | foreach { $hash = @{} } { $hash[$_.name] = $_.'#text' } { [pscustomobject]$hash } | Where FilterOrigin -notmatch 'Stealth|Unknown|Query User Default|WSH Default' | ForEach-Object { $pattern = '\{.+?\}' $_.FilterOrigin = $_.FilterOrigin -replace $pattern, (Get-NetFirewallRule -Name $Matches[0]).DisplayName $protocolName = @{ 6 = 'TCP'; 17 = 'UDP' }[$_.Protocol] $_.Protocol = if (-not $protocolName) { $_.Protocol } else { $protocolName } # Conceptually clearer PowerShell (Core) 7+ alternative: $_.Direction = $_.Direction -eq '%%14592' ? 'Outbound' : 'Inbound' # $_.Direction = ('Outbound', 'Inbound')[$_.Direction -eq '%%14592'] $_ } }
存在的问题:
- Protocol未被替换,仍显示数字
- FilterOrigin显示为空
- 每次输出结果后都会出现错误
问题截图:
截图中仅Direction被正确替换,脚本中显示的阻止连接的过滤器应有名称,但未正常显示。
解决方案
问题分析及修复步骤
1. FilterOrigin为空的问题
原代码未先执行匹配获取$Matches,且传递给Get-NetFirewallRule的名称带大括号,导致无法找到对应规则。修复代码:
$pattern = '\{.+?\}' if ($_.FilterOrigin -match $pattern) { # 去掉规则名称的大括号 $ruleName = $Matches[0].Trim('{}') # 静默忽略找不到规则的错误 $firewallRule = Get-NetFirewallRule -Name $ruleName -ErrorAction SilentlyContinue if ($firewallRule) { $_.FilterOrigin = $firewallRule.DisplayName } }
2. Protocol未替换的问题
$_.Protocol是字符串类型,哈希表的键是数字,类型不匹配导致匹配失败。修复代码:
$protocolMap = @{ 6 = 'TCP'; 17 = 'UDP' } # 先判断是否为纯数字,再转为整数 if ($_.Protocol -match '^\d+$') { $protocolNum = [int]$_.Protocol $_.Protocol = $protocolMap.ContainsKey($protocolNum) ? $protocolMap[$protocolNum] : $_.Protocol }
3. Direction替换的兼容性问题
Windows PowerShell 5.x不支持三元运算符,改用if-else实现,同时替换为中文表述:
if ($_.Direction -eq '%%14592') { $_.Direction = '入站' } else { $_.Direction = '出站' }
完整修复后的脚本
# 提前缓存防火墙规则,提升大数量事件处理效率 $firewallRules = @{} Get-NetFirewallRule | ForEach-Object { $firewallRules[$_.Name] = $_.DisplayName } foreach ($event in Get-WinEvent -FilterHashtable @{LogName='Security';ID=5152}) { $xml = [xml]$event.toxml() $xml.event.eventdata.data | ForEach-Object -Begin { $hash = @{} } -Process { $hash[$_.name] = $_.'#text' } -End { [pscustomobject]$hash } | Where-Object FilterOrigin -notmatch 'Stealth|Unknown|Query User Default|WSH Default' | ForEach-Object { # 替换FilterOrigin $pattern = '\{.+?\}' if ($_.FilterOrigin -match $pattern) { $ruleName = $Matches[0].Trim('{}') if ($firewallRules.ContainsKey($ruleName)) { $_.FilterOrigin = $firewallRules[$ruleName] } } # 替换Protocol $protocolMap = @{ 6 = 'TCP'; 17 = 'UDP' } if ($_.Protocol -match '^\d+$') { $protocolNum = [int]$_.Protocol $_.Protocol = $protocolMap.ContainsKey($protocolNum) ? $protocolMap[$protocolNum] : $_.Protocol } # 替换Direction if ($_.Direction -eq '%%14592') { $_.Direction = '入站' } else { $_.Direction = '出站' } # 输出处理后的对象 $_ } }
内容的提问来源于stack exchange,提问作者user20682592
相关产品推荐
相关产品推荐

