4104 记录脚本内容本身
PowerShell 脚本块日志(事件 4104)位于 Microsoft-Windows-PowerShell/Operational 通道,记录实际提交给执行引擎的脚本块内容。它的独特价值在于无文件场景:脚本从内存或命令行拼接执行、磁盘上不留文件时,4104 仍会把代码文本记录下来。这一功能需要显式启用脚本块日志策略,未启用时该通道只有引擎启停等基础事件,看不到脚本内容。
基本信息
| 日志通道 | Microsoft-Windows-PowerShell/Operational |
|---|---|
| 关键事件 | 4104 脚本块内容、4103 模块日志、400/600 引擎启停 |
| 启用方式 | 组策略或注册表启用 ScriptBlockLogging(脚本块日志) |
| 记录内容 | 脚本块文本、脚本块 ID、路径(若有)、严重性 |
| 分片特性 | 较长的脚本会拆成多个 4104 事件,需按脚本块 ID 拼接 |
| 保留限制 | 受通道容量限制,滚动覆盖较快,事件后应尽早导出 |
典型使用场景
- 还原从内存或命令行执行的 PowerShell 代码内容
- 分析编码后的命令(-EncodedCommand)解码后的实际脚本
- 把脚本块与同一时间窗的进程创建事件对齐,确认执行主体
- 识别常见的下载执行、凭据收集、横向工具调用脚本
- 为检测规则提供样本:确认真实出现的代码形态
- 判断脚本是否被分片记录,完整拼接后再分析
检索与拼接
检索与拼接
# 读取最近 30 条脚本块事件
Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' -MaxEvents 200 |
Where-Object Id -eq 4104 |
Select-Object -First 30 TimeCreated, @{n='Text';e={$_.Message}}
# 按脚本块 ID 归组,把分片事件拼成完整脚本
$ev = Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' -MaxEvents 500 | Where-Object Id -eq 4104
$ev | ForEach-Object { $x=[xml]$_.ToXml(); [pscustomobject]@{ t=$_.TimeCreated; id=($x.Event.EventData.Data | Where-Object Name -eq 'ScriptBlockId').'#text'; msg=($x.Event.EventData.Data | Where-Object Name -eq 'ScriptBlockText').'#text' } } |
Group-Object id | Sort-Object Count -Descending | Select-Object -First 5 Name, Count
# 导出原始通道供离线分析
wevtutil epl Microsoft-Windows-PowerShell/Operational C:\evidence\PSOperational.evtx相关事件与判读
| 事件 | 含义 | 判读要点 |
|---|---|---|
| 4104 | 脚本块内容 | 同一脚本块 ID 的多条事件是分片,需拼接 |
| 4103 | 模块与命令调用日志 | 补充模块级调用细节,通常量更大 |
| 400 | 引擎启动 | 常标记一次新的 PowerShell 会话开始 |
| 600 | 引擎停止 | 与会话结束对应,可用来界定会话时长 |
| (无记录) | 策略未启用或已被清理 | 先确认策略状态,再判断是否真的没有执行 |
脚本块记录量受策略与通道容量限制,事件跨度过长时早期记录可能已被覆盖。分析时先确认日志时间范围,再决定是否需要从镜像中提取更早的日志文件。
分片脚本要按脚本块 ID 拼接后再阅读:只看单条 4104 会得到半截代码,容易被误判为无害脚本。