「上周五半夜,服务器好像自己重启了」「只有某台机器,业务应用每个月都会崩溃几次」── 这类调查的出发点,几乎无一例外都是 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可用的键是固定的。有LogName、ProviderName、ID、Level、StartTime、EndTime、Keywords、Path、UserID等。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 的输出是 Count 和 Name 两列。由于是按 ProviderName, Id 这样的多个属性分组,Name 中会以逗号分隔列出「来源, 事件 ID」(例如 Service Control Manager, 7034)。结果按件数从多到少排列,第一步要看的是排名靠前的来源里有没有陌生的名字、是否在故障发生的时间段出现件数的高峰。先在这里锁定嫌疑对象,再用下一章的方法深入排查具体事件。
如果一条匹配的事件都没有,Get-WinEvent 会报错。此时请不要轻易加上 -ErrorAction SilentlyContinue。“没有匹配”、”没有读取该日志的权限”、”无法到达目标”,这些情况都会变成同样的”空结果”。在调查场景下,这是最让人头疼的失效方式。
正确的做法是像上面那样用 -ErrorAction Stop 接住错误,只在 FullyQualifiedErrorId 为 NoMatchingEventsFound 时才把它吞掉。按消息字符串判断的话,在非英语环境下不会匹配,也就不起作用(错误处理的思路参见「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> 之间的部分,可以原样传给 -FilterXPath。1
# 【错误做法】取出全部记录后,再用显示用的消息(依赖语言)筛选
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-WinEvent。Get-EventLog在 PowerShell 7 中无法使用,能处理的日志范围也有限。 - 筛选用
-FilterHashtable或-FilterXPath完成。用Where-Object在后段筛选,会让调查耗时恶化一个数量级。 - 排查重启问题时,除了 41、6008 之外,还要看1074(是谁发出的请求)。应用异常终止则以 1000、1026、1001 作为切入点。
- 使用
ToXml()的事件数据而不是消息字符串,脚本就不再依赖语言设置。 - 多台计算机的一次性调查用
Invoke-Command,长期持续汇总用 Windows 事件转发,需要留证时就采集.evtx并在本地解析。 - 如果想调查的时间段的日志已经不存在,就什么都做不了。重新评估日志容量,是故障应对准备工作中最优先的一项。
示例代码下载
本文中出现的代码,已整理成可以直接运行的形式提供下载,其中包含重启历史、登录历史、任务失败、多台计算机收集等内容。
本文的示例因依赖 Windows 环境和租户配置,未做实际运行验证。所有文件都已完成语法解析和基于 PSScriptAnalyzer 的静态分析,但请务必在自己的验证机上确认实际运行效果。
# 语法解析 + 静态分析(在非 Windows 系统上也能运行)
./Invoke-SampleTests.ps1
如果没有可以练习的环境,也不必在业务终端上拿生产日志练手。用 Windows Sandbox,几分钟就能启动一个干净的 Windows,关闭后即会消失(「用 Windows Sandbox 加快应用验证的方法」)。不过沙盒是刚启动的环境,几乎没有日志积累,如果要试验重启历史、登录历史这类”随时间推移而积累的日志”,用评估用的虚拟机会更合适。先在自己的终端上运行第 3 章的命令,亲身体会筛选速度的差异,这样就足够了。
配置值(路径、服务器名、租户 ID 等)均为示例。请勿直接在生产环境中运行,请根据自己公司的环境进行相应替换。
相关文章
- 任务计划程序的任务不执行、以 0x1 结束 ── 原因排查与安全的运维设计
- Windows 事件日志・ETW 入门 ── 让业务应用程序的日志接入操作系统标准机制
- Windows应用的crash dump收集入门 - 先搞清楚 WER / ProcDump / WinDbg怎么分工
- PowerShell Remoting(WinRM)入门 ── 批量管理多台 Windows
- Windows 服务的创建与运维 ── 从任务计划程序的取舍到 BackgroundService 服务化
- PowerShell 脚本进阶 ── 安全地实现日志排查、归档与报表自动化
相关咨询领域
合同会社小村软件承接 Windows 环境的故障排查、间歇性发生的重启与应用异常终止的原因分析,以及日志收集与监控体系搭建等业务。
参考链接
-
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
-
Microsoft Learn,Active Directory security groups ─ Event Log Readers。关于内置的「Event Log Readers」组的成员可以读取本地计算机的事件日志(且不涉及授予管理员权限)。关于修改单个通道访问权限的方法,另请参见 wevtutil 的 sl /ca(通道访问权限)。 ↩ ↩2 ↩3 ↩4
-
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
-
Microsoft Learn,Event Levels。关于事件严重程度级别的标准取值(1=Critical、2=Error、3=Warning、4=Informational、5=Verbose)。 ↩ ↩2
-
Microsoft Learn,Windows Event Forwarding。关于无需额外部署代理即可将多台 Windows 的事件转发到收集服务器,以及通过订阅指定收集对象。另请参见 Invoke-Command 对多台计算机的并行执行。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn,4624(S): An account was successfully logged on。关于登录成功事件的事件数据中包含的项目(TargetUserName、LogonType、IpAddress 等)及登录类型取值的含义,以及记录与否会因审计策略的设置而不同。 ↩
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
PowerShell 的安全加固 ── 日志・AMSI・语言模式・JEA
本文整理了在不禁止 PowerShell 的前提下安全使用它的实务要点,讲解启用脚本块日志与转录、禁用 AMSI 与旧版本引擎的绕过路径、通过语言模式加以限制,直至用 JEA 进行权限委任。
Windows 安全审核策略与事件日志排查实务 ── 成为看得懂 4625 的信息系统人员
这是一份用于应对「帮忙查一下登录失败日志」需求的实务指南。内容涵盖基本审核策略与高级审核策略的关系、至少应启用的子类别、事件 ID 4624/4625/4688 的解读方法、Security 日志的容量设计,直至用 Get-WinEvent 提取日志。
用 winget + PowerShell 自动化 PC 装机 ── 让操作手册可执行
本文整理了让新员工电脑的初始设置具备可复现性的方法,涵盖通过 winget 进行应用安装与 export/import、WinGet Configuration 的声明式配置、用 PowerShell 补充的设置,直至无人值守执行时的注意事项。
卷影复制服务(VSS)的原理与实务 ── 使用中文件为何能够备份
使用中的文件明明会因共享冲突而无法复制,备份软件为什么却能正常备份?本文将解说卷影复制服务(VSS)中请求者・编写器・提供程序的角色分工、写时复制的原理、vssadmin 的实务操作,以及差异区域的陷阱。
组策略(GPO)实务入门 ── 原理、生效确认与和 Intune 的分工
还没搞清楚「用 GPO 配发」到底是什么意思,就在操作 AD 环境吗?本文从实务角度整理组策略的原理与 LSDOU 应用顺序、用 gpupdate、gpresult 确认生效情况、与 Intune 的分工,以及客户方 GPO 改变应用行为的陷阱。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
常见问题
汇总了咨询这一主题时常见的问题。
- 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 解析,这也是一种有效的方法。