用 Get-WinEvent 实务排查事件日志 ── 筛选速度决定调查时间

· · PowerShell, Windows, 事件日志, 故障排查, 运维改善, 信息系统, 审计, 故障排除

「上周五半夜,服务器好像自己重启了」「只有某台机器,业务应用每个月都会崩溃几次」── 这类调查的出发点,几乎无一例外都是 Windows 的事件日志。但如果用事件查看器的 GUI 去滚动浏览几十万条日志,光是这个动作就能耗掉一上午。

用 PowerShell 的 Get-WinEvent,这项工作几十秒就能完成。不过有一个条件:必须在正确的位置进行筛选。如果写成 Get-WinEvent | Where-Object { ... },就会先把所有事件都读进来再丢弃,甚至可能比 GUI 还慢。

本文将整理正确使用 Get-WinEvent 筛选的方法、现场常见调查(意外重启、登录、应用异常终止、服务停止)的实践方法,以及从多台计算机收集日志的方式。

前提环境

项目 内容
目标操作系统 Windows 10/11、Windows Server。Get-WinEvent 读取的是Windows Vista 及以后的事件日志基础架构,经典日志和 ETW 日志都能处理1
PowerShell 版本 Windows PowerShell 5.1 与 PowerShell 7 均可使用(Get-WinEvent 是仅限 Windows 的 Cmdlet,Linux/macOS 上的 PowerShell 7 无法使用)。本文的代码采用了 5.1 与 7 都能运行的写法1
权限 System、Application 等多数日志普通用户也能读取,但读取 Security 日志需要管理员权限或与之等效的权限授予。有些日志如果不以管理员身份运行就无法获取信息(第 4 章 (2) 有权限授予的步骤)12
远程获取 第 5 章的方法 1(-ComputerName)不依赖 PowerShell 远程处理(WinRM)。但需要在目标机器的防火墙中预先允许对事件日志服务的远程访问。方法 2(Invoke-Command)则以 WinRM 为前提1

1. 先说结论

  • 使用 Get-WinEvent 而不是 Get-EventLog后者仅限 Windows PowerShell 使用,且只能处理经典日志,在 PowerShell 7 中无法使用。1
  • 筛选用 -FilterHashtable 完成。由于是在事件日志一侧进行过滤,比用 Where-Object 在后段筛选要快出一个数量级。3
  • -FilterHashtable 可用的键是固定的。LogNameProviderNameIDLevelStartTimeEndTimeKeywordsPathUserID 等。3
  • 复杂条件或按事件数据筛选要用 XPath(-FilterXPath)。可以从事件查看器的「自定义视图」中复制出 XPath。1
  • Level 是数值。1=严重、2=错误、3=警告、4=信息、5=详细。4
  • 读取安全日志需要特殊权限。以管理员身份运行最省事,但对调查人员来说,通过「Event Log Readers」组或通道 ACL 只授予读取权限更符合最小权限原则。12
  • 看的是事件数据,而不是消息字符串。ToXml() 获取结构化数据后,脚本就不再依赖操作系统的语言设置。1
  • .evtx 文件也能解析。指定 -Path 后,就能在本地分析在现场采集到的日志。1
  • 长期持续的汇总要用 Windows 事件转发(WEF)。如果只是一次性调查,用 Invoke-Command 并行执行就足够了。5

2. 两种日志与 Cmdlet 的选择

Windows 的事件日志大致分为两大类。

种类 示例 可读取的 Cmdlet
经典日志 System / Application / Security Get-EventLog(仅 5.1)、Get-WinEvent
应用程序和服务日志 Microsoft-Windows-TaskScheduler/Operational Get-WinEvent

后者才真正包含对调查有用的信息。任务计划程序的执行历史、PowerShell 的脚本块日志、Windows Update 的应用历史等,很多能成为查明原因决定性证据的日志都在这一侧。因此,今后编写的脚本统一使用 Get-WinEvent 才是正确做法。1

首先来确认都有哪些日志。

# 日志列表(按记录数从多到少排序)。RecordCount 为 0 的日志尚未记录任何内容
Get-WinEvent -ListLog * -ErrorAction SilentlyContinue |
    Where-Object RecordCount -gt 0 |
    Sort-Object RecordCount -Descending |
    Select-Object LogName, RecordCount, MaximumSizeInBytes, IsEnabled -First 20

# 查找特定产品的日志
Get-WinEvent -ListLog *TaskScheduler* | Format-Table LogName, IsEnabled, RecordCount
Get-WinEvent -ListProvider *PowerShell* | Select-Object Name

3. 筛选”在哪里进行”才是关键

比较三种能得到相同结果的写法。

# 【最差】读入全部记录后再在 PowerShell 一侧丢弃
Get-WinEvent -LogName System | Where-Object { $_.Id -eq 41 }

# 【推荐】在事件日志一侧筛选(FilterHashtable)
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ID = 41 }

# 【复杂条件】用 XPath 筛选
Get-WinEvent -LogName System -FilterXPath "*[System[EventID=41]]"

第一种写法会把几十万条事件全部对象化之后再丢弃,大部分等待时间都被浪费掉了。第二种、第三种写法是在事件日志的 API 一侧完成筛选,只会返回需要的内容。3

-FilterHashtable 可用的键是固定的。3

指定内容 示例
LogName 日志名称 'System', 'Microsoft-Windows-TaskScheduler/Operational'
ProviderName 事件的来源 'Application Error', 'Service Control Manager'
ID 事件 ID(可以是数组) 41, @(1000, 1001)
Level 严重程度(数值) 2(错误), @(1,2)
StartTime / EndTime 时间范围 (Get-Date).AddDays(-7)
Keywords 关键字(如审计成功/失败) 9007199254740992(审计成功)
Path .evtx 文件 'D:\collect\srv01_System.evtx'
UserID 用户 SID 'S-1-5-21-...'

Level 的数值如下所示。4

值(Level) 日语环境的显示 英语环境的显示
1 重大 Critical
2 エラー Error
3 警告 Warning
4 情報 Information
5 詳細 Verbose

右边两列是事件查看器的「级别」列,以及 Get-WinEvent 结果中 LevelDisplayName 属性所显示的字符串。LevelDisplayName 会随操作系统的显示语言而变化。用肉眼核对输出时可以参照这张对照表,但用脚本筛选时务必指定数值型的 Level(如果按显示名称判断,脚本在英语环境下就无法运行)。

落实成实用的形式,就是下面这样。

# 汇总最近 7 天内的错误・严重事件,按来源分组(掌握现状的第一步)
$filter = @{
    LogName   = 'System', 'Application'
    Level     = 1, 2
    StartTime = (Get-Date).AddDays(-7)
}
try {
    Get-WinEvent -FilterHashtable $filter -ErrorAction Stop |
        Group-Object ProviderName, Id |
        Sort-Object Count -Descending |
        Select-Object Count, Name -First 15
}
catch {
    # 只默默放行"无匹配事件"的情况。不依赖语言区域,
    # 用 FullyQualifiedErrorId 判断(消息字符串会随语言环境变化,不可靠)
    if ($_.FullyQualifiedErrorId -notlike 'NoMatchingEventsFound*') { throw }
}

结果怎么看。Group-Object 的输出是 CountName 两列。由于是按 ProviderName, Id 这样的多个属性分组,Name 中会以逗号分隔列出「来源, 事件 ID」(例如 Service Control Manager, 7034)。结果按件数从多到少排列,第一步要看的是排名靠前的来源里有没有陌生的名字是否在故障发生的时间段出现件数的高峰。先在这里锁定嫌疑对象,再用下一章的方法深入排查具体事件。

如果一条匹配的事件都没有,Get-WinEvent 会报错。此时请不要轻易加上 -ErrorAction SilentlyContinue“没有匹配”、”没有读取该日志的权限”、”无法到达目标”,这些情况都会变成同样的”空结果”。在调查场景下,这是最让人头疼的失效方式。

正确的做法是像上面那样用 -ErrorAction Stop 接住错误,只在 FullyQualifiedErrorIdNoMatchingEventsFound 时才把它吞掉。按消息字符串判断的话,在非英语环境下不会匹配,也就不起作用(错误处理的思路参见「PowerShell 的错误处理与重试设计」)。

4. 实务中常见的调查方法

(1) 意外重启与关机

# 41: 未经正常关机就被重启了(Kernel-Power)
# 6008: 意外关机(EventLog)
# 1074: 进程/用户发出的关机请求(是谁、是什么导致了关机)
# 6005/6006: 事件日志服务的启动/停止(= 启动/停止的标志)
Get-WinEvent -FilterHashtable @{
    LogName   = 'System'
    ID        = 41, 1074, 6005, 6006, 6008
    StartTime = (Get-Date).AddDays(-30)
} | Select-Object TimeCreated, Id, ProviderName,
      @{ n = 'Message'; e = { ($_.Message -split "`r?`n")[0] } } |
    Sort-Object TimeCreated -Descending | Format-Table -AutoSize

结果怎么看。输出是 TimeCreated / Id / ProviderName / Message(内容较长,只取第一行)这 4 列,按时间从新到旧排列。要看的不是每一行的内容本身,而是每次重启时,哪些 ID 按什么顺序出现。如果是有计划的重启,通常会成组出现 1074(有人发出请求)→ 6006(事件日志服务停止=正常关机)→ 6005(启动=开机完成)。

1074 是能看出”是谁请求重启”的关键事件。如果这里出现 WindowsUpdate 或某个特定的进程名,原因就基本可以确定了。如果只有 41 和 6008 出现而没有 1074,就要怀疑是断电或死机导致的异常停止。

(2) 登录与注销追踪(安全日志)

# 4624: 登录成功 / 4625: 登录失败 / 4634: 注销
# 需要有安全日志的读取权限(以管理员身份运行,
# 或者预先将执行账户加入 Event Log Readers 组)
Get-WinEvent -FilterHashtable @{
    LogName   = 'Security'
    ID        = 4624, 4625, 4634
    StartTime = (Get-Date).AddDays(-1)
} | ForEach-Object {
    $xml = [xml]$_.ToXml()
    $d   = @{}
    foreach ($n in $xml.Event.EventData.Data) { $d[$n.Name] = $n.'#text' }
    [pscustomobject]@{
        时刻       = $_.TimeCreated
        类型       = switch ($_.Id) { 4624 { '登录成功' } 4625 { '登录失败' } 4634 { '注销' } }
        用户       = $d['TargetUserName']
        登录类型   = $d['LogonType']    # 2=交互 3=网络 10=RDP
        来源地址   = $d['IpAddress']
    }
} | Where-Object 用户 -notlike '*$' | Format-Table -AutoSize

这正是使用事件数据的典型示例。用正则表达式切分 Message 会依赖操作系统的语言设置,但 ToXml()EventData 可以按名称引用,因此同一份脚本在日语环境和英语环境下都能运行。6

结果怎么看。输出是 时刻 / 类型 / 用户 / 登录类型 / 来源地址 这 5 列(因为是用 [pscustomobject] 自己拼装的,列名和顺序都可以自己决定)。末尾加上 Where-Object 用户 -notlike '*$' 来剔除计算机账户(名称以 $ 结尾),剩下的就都是人的账户。要看的是登录类型 为 3(网络)或 10(RDP)的行中,来源地址 有没有陌生的 IP 地址,以及同一个用户的 4625(失败)是否在短时间内连续出现

还要注意,如果没有启用审计,事件根本不会被记录。「一条都没有」并不一定代表「什么都没发生」,也可能只是「没有被记录」。

只给调查人员”读取”权限。为了让他们能读取安全日志就分发管理员权限,是应该避免的做法。把账户加入内置的 Event Log Readers 组(SID 为 S-1-5-32-573),无需管理员权限也能读取事件日志。2

# 在目标计算机上执行(需要管理员权限)。
# 内置组的显示名称可能会随操作系统语言而变化,因此用 SID 指定更可靠
Add-LocalGroupMember -SID 'S-1-5-32-573' -Member 'EXAMPLE\审计专员'

# 确认是否添加成功
Get-LocalGroupMember -SID 'S-1-5-32-573'

如果要分发到多台计算机,可以用组策略中的「计算机配置 > 策略 > Windows 设置 > 安全设置 > 受限组」,或组策略首选项中的「计算机配置 > 首选项 > 控制面板设置 > 本地用户和组」,将域组加入到各服务器本地的 Event Log Readers 组这一设置进行分发。

如果想按通道进一步细化权限,可以用 wevtutil 查看和修改各日志的访问权限(SDDL)。2

wevtutil gl Security                    # 显示当前设置(channelAccess 就是 SDDL)
# wevtutil sl Security /ca:<SDDL>       # 修改设置。/ca 是"替换"现有的 SDDL

/ca 不是追加,而是替换。修改之前请务必先保存好 wevtutil gl 的输出。

(3) 应用程序异常终止

# 1000: Application Error(应用程序崩溃)
# 1026: .NET Runtime(因托管异常导致终止)
# 1001: Windows Error Reporting(故障存储桶信息)
Get-WinEvent -FilterHashtable @{
    LogName   = 'Application'
    ID        = 1000, 1001, 1026
    StartTime = (Get-Date).AddDays(-14)
} | Select-Object TimeCreated, Id, ProviderName, Message |
    Sort-Object TimeCreated -Descending | Format-List

1000 中会包含崩溃的模块名和偏移量,1026 中会包含 .NET 的异常堆栈。先在这里锁定嫌疑对象,再进入转储分析会更高效(「Windows 崩溃转储采集入门」「用 WinDbg + SOS 读取崩溃转储」)。

(4) 服务停止与重启

# 7034: 服务意外终止 / 7031: 终止后触发恢复操作 / 7045: 安装了新服务
Get-WinEvent -FilterHashtable @{
    LogName     = 'System'
    ProviderName = 'Service Control Manager'
    ID          = 7031, 7034, 7045
    StartTime   = (Get-Date).AddDays(-30)
} | Select-Object TimeCreated, Id, Message | Format-List

7045(安装新服务)在检测非预期的软件安装这一用途上也很有用。关于 Windows 服务的运维设计,请参见「Windows 服务的创建方式与运维」。

(5) 任务计划程序的执行记录

本节会用到 XPath,先来掌握它的基本骨架。事件日志的 XPath 实际上只需要记住两种写法。1

写法 指向的内容 示例
*[System[ ... ]] 所有事件共通的项目(事件 ID、时间、级别、提供程序) *[System[EventID=41]]
*[EventData[Data[@Name='项目名']='值']] 该事件特有的项目(带名称的事件数据) *[EventData[Data[@Name='TaskName']='\夜间汇总']]

只需要用 and / or 把这两种写法连接起来。由于值的比较要用单引号,在 PowerShell 一侧放进 Here-String(@" ~ "@)中,就不用为引号的嵌套发愁(下面的代码就是这种形式)。

与其自己动手拼写,不如让事件查看器帮你写好再复制过来,这样更快也更可靠。在「事件查看器 > (右键点击目标日志) > 筛选当前日志」中通过 GUI 指定条件,切换到对话框的「XML」标签页,就能看到相同条件以 XML 形式显示的查询。夹在 <Select Path="..."></Select> 之间的部分,可以原样传给 -FilterXPath1

# 【错误做法】取出全部记录后,再用显示用的消息(依赖语言)筛选
Get-WinEvent -FilterHashtable @{
    LogName   = 'Microsoft-Windows-TaskScheduler/Operational'
    StartTime = (Get-Date).AddDays(-3)
} -ErrorAction SilentlyContinue |
    Where-Object { $_.Message -like '*夜间汇总*' }

# 【正确做法】用任务名(事件数据)在服务器一侧筛选。速度快,也不依赖语言
$xpath = @"
*[System[TimeCreated[timediff(@SystemTime) <= 259200000]]]
and
*[EventData[Data[@Name='TaskName']='\夜间汇总']]
"@
Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' `
             -FilterXPath $xpath -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List

timediff 是以毫秒为单位指定”距当前的经过时间”的 XPath 函数,259200000 就是 3 天。任务名要用注册时的完整路径指定(如果在根目录下,就是 \任务名)。用于显示的 Message 既依赖语言设置,又只能在客户端一侧筛选,因此请按本文的方针改用事件数据来筛选。

该日志有时默认处于禁用状态。启用方法以及任务不执行时的排查思路,整理在「任务计划程序的任务不执行、以 0x1 结束」中。

5. 查询多台/其他计算机的日志

方法 1:直接远程读取

Get-WinEvent -ComputerName 'srv01' -FilterHashtable @{ LogName='System'; ID=41 } -MaxEvents 10

方法 2:用 Invoke-Command 并行查询(台数较多时选这种)

$servers = 'srv01', 'srv02', 'srv03'
Invoke-Command -ComputerName $servers -ScriptBlock {
    Get-WinEvent -FilterHashtable @{
        LogName = 'System'; Level = 1, 2; StartTime = (Get-Date).AddDays(-1)
    } -ErrorAction SilentlyContinue |
        Select-Object TimeCreated, Id, ProviderName, LevelDisplayName
} | Sort-Object PSComputerName, TimeCreated

Invoke-Command 会对多台计算机并行执行(「PowerShell Remoting(WinRM)入门」「PowerShell 的并行处理」)。

方法 3:采集 .evtx 后在本地解析

可以直接读取现场导出的文件。比多次通过网络查询更快,还能作为证据留存下来。

# 在现场执行: wevtutil epl System D:\collect\srv01_System.evtx
Get-WinEvent -Path 'D:\collect\srv01_System.evtx' -FilterXPath "*[System[(Level=1 or Level=2)]]" |
    Select-Object TimeCreated, Id, ProviderName, Message

方法 4:Windows 事件转发(WEF) ── 如果要长期持续汇总,就选这个。这是把各终端的事件转发到收集服务器的标准功能,无需额外部署代理。5

6. 日志大小与保留期限

“想去查的时候,那个时间段的日志已经被覆盖了”,这是非常常见的失败案例。默认的最大容量偏小,在事件较多的环境中几天就会循环一遍。

# 确认当前的大小设置和保留状况
Get-WinEvent -ListLog 'System', 'Application', 'Security' |
    Select-Object LogName, IsEnabled, LogMode,
        @{ n='MaxMB'; e={ [math]::Round($_.MaximumSizeInBytes / 1MB, 1) } },
        RecordCount, OldestRecordNumber

# 修改最大容量(需要管理员权限。例: 将 System 日志改为 256MB)
wevtutil sl System /ms:268435456

对于以调查为前提的服务器,或正在发生故障的终端,标准做法是先扩大日志容量,再等待问题重现。关于日志的世代管理与自动归档,也请参见「PowerShell 脚本进阶 ── 安全地实现日志排查、归档与报表自动化」。

7. 实务定式(判断表)

情况 选择 补充说明
今后编写的脚本 Get-WinEvent Get-EventLog 仅限 5.1、只能处理经典日志1
按日志名・ID・时间范围筛选 -FilterHashtable 最快、也最易读3
按事件数据内容筛选 -FilterXPath / -FilterXml 可从事件查看器的自定义视图复制得到1
记录数多、耗时长 缩小时间范围・使用 -MaxEvents 不要在 PowerShell 一侧做筛选
想从消息中提取值 ToXml() 的 EventData 脚本不再依赖语言设置1
多台计算机做一次性调查 Invoke-Command 会并行执行5
长期持续汇总 Windows 事件转发(WEF) 无需额外代理的标准功能5
过去的日志已经消失 扩大日志容量 在等待问题重现之前先做好

8. 总结

  • 统一使用 Get-WinEventGet-EventLog 在 PowerShell 7 中无法使用,能处理的日志范围也有限。
  • 筛选用 -FilterHashtable-FilterXPath 完成。用 Where-Object 在后段筛选,会让调查耗时恶化一个数量级。
  • 排查重启问题时,除了 41、6008 之外,还要看1074(是谁发出的请求)。应用异常终止则以 1000、1026、1001 作为切入点。
  • 使用 ToXml() 的事件数据而不是消息字符串,脚本就不再依赖语言设置。
  • 多台计算机的一次性调查用 Invoke-Command,长期持续汇总用 Windows 事件转发,需要留证时就采集 .evtx 并在本地解析。
  • 如果想调查的时间段的日志已经不存在,就什么都做不了。重新评估日志容量,是故障应对准备工作中最优先的一项。

示例代码下载

本文中出现的代码,已整理成可以直接运行的形式提供下载,其中包含重启历史、登录历史、任务失败、多台计算机收集等内容。

下载示例代码(zip)

本文的示例因依赖 Windows 环境和租户配置,未做实际运行验证。所有文件都已完成语法解析和基于 PSScriptAnalyzer 的静态分析,但请务必在自己的验证机上确认实际运行效果。

# 语法解析 + 静态分析(在非 Windows 系统上也能运行)
./Invoke-SampleTests.ps1

如果没有可以练习的环境,也不必在业务终端上拿生产日志练手。用 Windows Sandbox,几分钟就能启动一个干净的 Windows,关闭后即会消失(「用 Windows Sandbox 加快应用验证的方法」)。不过沙盒是刚启动的环境,几乎没有日志积累,如果要试验重启历史、登录历史这类”随时间推移而积累的日志”,用评估用的虚拟机会更合适。先在自己的终端上运行第 3 章的命令,亲身体会筛选速度的差异,这样就足够了。

配置值(路径、服务器名、租户 ID 等)均为示例。请勿直接在生产环境中运行,请根据自己公司的环境进行相应替换。

相关文章

相关咨询领域

合同会社小村软件承接 Windows 环境的故障排查、间歇性发生的重启与应用异常终止的原因分析,以及日志收集与监控体系搭建等业务。

参考链接

  1. Microsoft Learn,Get-WinEvent。关于可以获取经典日志和 Windows Vista 及以后的事件日志两者、通过 -ListLog / -ListProvider 获取列表、用 -FilterHashtable / -FilterXPath / -FilterXml 进行筛选、通过 -Path 读取已归档的日志(.evtx)、通过 -ComputerName 进行远程获取、用 -MaxEvents 限制返回数量、读取安全日志需要管理员权限,以及每个事件都可以用 ToXml() 方法获取其 XML 表示。  2 3 4 5 6 7 8 9 10 11 12 13 14 15

  2. Microsoft Learn,Active Directory security groups ─ Event Log Readers。关于内置的「Event Log Readers」组的成员可以读取本地计算机的事件日志(且不涉及授予管理员权限)。关于修改单个通道访问权限的方法,另请参见 wevtutil 的 sl /ca(通道访问权限)。  2 3 4

  3. Microsoft Learn,Creating Get-WinEvent queries with FilterHashtable。关于 -FilterHashtable 可以指定的键(LogName、ProviderName、Path、Keywords、ID、Level、StartTime、EndTime、UserID、Data 等)、因在服务器一侧筛选而比 Where-Object 更高效,以及关键字与严重程度取值的指定方法。  2 3 4 5

  4. Microsoft Learn,Event Levels。关于事件严重程度级别的标准取值(1=Critical、2=Error、3=Warning、4=Informational、5=Verbose)。  2

  5. Microsoft Learn,Windows Event Forwarding。关于无需额外部署代理即可将多台 Windows 的事件转发到收集服务器,以及通过订阅指定收集对象。另请参见 Invoke-Command 对多台计算机的并行执行。  2 3 4

  6. Microsoft Learn,4624(S): An account was successfully logged on。关于登录成功事件的事件数据中包含的项目(TargetUserName、LogonType、IpAddress 等)及登录类型取值的含义,以及记录与否会因审计策略的设置而不同。 

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

本文与以下服务页面相关联,欢迎从最接近的入口查看。

常见问题

汇总了咨询这一主题时常见的问题。

Get-EventLog 和 Get-WinEvent 应该用哪一个?
应该用 Get-WinEvent。Get-EventLog 只能在 Windows PowerShell 5.1 中使用,而且只能处理 System、Application、Security 这类传统(经典)日志。Windows Vista 以后新增的「应用程序和服务日志」下的日志,例如 Microsoft-Windows-TaskScheduler/Operational 这类详细日志,只有 Get-WinEvent 才能读取。由于 PowerShell 7 中根本无法使用 Get-EventLog,今后编写的脚本请统一使用 Get-WinEvent。
Get-WinEvent 太慢了,没法用来做调查。
很可能是在管道后段用 Where-Object 做筛选。这种写法会先把日志中的全部事件对象化并读入 PowerShell,然后再丢弃不需要的部分——在有几十万条记录的日志上是不现实的。使用 -FilterHashtable 或 -FilterXPath 时,筛选会在事件日志一侧完成,只返回需要的事件,速度会有数量级的提升。请养成先用 FilterHashtable 指定日志名、时间范围、事件 ID 的习惯。
尝试读取安全日志时被拒绝访问。
读取安全日志默认需要特殊权限。如果只是在自己机器上确认,「以管理员身份运行」PowerShell 即可解决;但如果不想把管理员权限交给调查人员,可以将其加入内置的「Event Log Readers(事件日志读取者)」组,或者通过通道的访问权限(ACL)只授予读取权限。从最小权限的角度出发,推荐后者。另外,也存在审计日志本身根本没有被记录的情况。登录审计等功能依赖于审计策略的设置,如果一条事件都找不到,也请确认策略本身是否已启用。
想从事件的消息中只提取特定的值(用户名或进程名)。
与其用正则表达式从 Message 属性中切分,不如使用 Properties 或事件数据更可靠。每个事件都可以通过 ToXml() 方法获取其 XML 表示,其中 EventData 下包含带名称的项目。用于显示的消息字符串会随操作系统的显示语言而变化,但事件数据的结构不会改变,因此可以写出在日语环境和英语环境下都能运行的脚本。
想统一查询多台服务器的事件日志。
如果台数不多,用 Get-WinEvent 的 -ComputerName 参数,或通过 Invoke-Command 远程执行就足够了。Invoke-Command 会对指定的多台计算机并行执行,几十台规模仍然实用。如果想长期持续汇总,请考虑用 Windows 事件转发(WEF)把事件收集到汇总服务器。如果只是一次性调查,也可以在各服务器上导出 evtx 文件,再在本地用 Get-WinEvent -Path 解析,这也是一种有效的方法。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表