Windows应急响应 - 08 计划任务与服务审计

08-计划任务与服务审计 (Scheduled Tasks & Services Audit)

计划任务和服务是 Windows 上最经典的持久化手段。攻击者通过创建计划任务实现定时回连,通过安装恶意服务获得 SYSTEM 权限开机自启。与 Linux 的 crontab + systemd 体系相比,Windows 的计划任务系统更复杂(XML 定义、多种触发器)、服务体系也更庞大(SCM 机制、Unquoted Path 漏洞、WMI 订阅)。本章是持久化排查的第一步。

前置知识01-系统基础与注册表 | 03-事件日志分析

关联章节09-注册表持久化审计 | 06-进程与网络分析

Linux 对照07-计划任务审计 | 08-服务与启动项审计


1. 计划任务排查

1.1 计划任务基础

与 Linux crontab 的对比

特征 Linux crontab Windows Scheduled Tasks
配置方式 文本文件 XML 文件 + 注册表
位置 /var/spool/cron/, /etc/cron.d/ C:\Windows\System32\Tasks\
管理命令 crontab -l schtasks /query
触发方式 时间调度 时间/事件/登录/启动/空闲 等多种触发器
权限 以用户身份运行 可配置为 SYSTEM 运行
事件日志 syslog/cron.log Event ID 4698/4702

1.2 枚举计划任务

CMD 方式

1
2
3
4
5
6
7
8
9
10
11
:: 列出所有计划任务
schtasks /query /fo LIST /v

:: 以表格格式(更紧凑)
schtasks /query /fo TABLE

:: 以 CSV 格式(便于导入分析)
schtasks /query /fo CSV /v > C:\IR\scheduled_tasks.csv

:: 查看特定任务的详细信息
schtasks /query /tn "TaskName" /fo LIST /v

PowerShell 方式(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 列出所有计划任务,含关键属性
Get-ScheduledTask | ForEach-Object {
$info = Get-ScheduledTaskInfo $_ -ErrorAction SilentlyContinue
$actions = $_.Actions
[PSCustomObject]@{
TaskName = $_.TaskName
TaskPath = $_.TaskPath
State = $_.State
Author = $_.Author
RunAs = $_.Principal.UserId
RunLevel = $_.Principal.RunLevel
ActionType = ($actions | ForEach-Object { $_.GetType().Name }) -join ','
Execute = ($actions | Where-Object { $_ -is [CimInstance] } | ForEach-Object { $_.Execute }) -join ','
Arguments = ($actions | Where-Object { $_ -is [CimInstance] } | ForEach-Object { $_.Arguments }) -join ','
LastRunTime = $info.LastRunTime
NextRunTime = $info.NextRunTime
LastResult = $info.LastTaskResult
}
} | Format-Table -AutoSize

# 导出到 CSV
# ... | Export-Csv "C:\IR\ScheduledTasks.csv" -NoTypeInformation -Encoding UTF8

直接查看 XML 任务定义

1
2
3
4
5
6
7
8
9
10
11
12
# 任务定义存储在文件系统中
Get-ChildItem "C:\Windows\System32\Tasks" -Recurse -File -ErrorAction SilentlyContinue |
Select-Object FullName, CreationTime, LastWriteTime, Length |
Sort-Object CreationTime -Descending | Format-Table -AutoSize

# 查看特定任务的 XML 内容
Get-Content "C:\Windows\System32\Tasks\Microsoft\Windows\UpdateTask"

# 在 XML 中搜索可疑关键字
Get-ChildItem "C:\Windows\System32\Tasks" -Recurse -File -ErrorAction SilentlyContinue |
Select-String -Pattern 'powershell|cmd\.exe|certutil|mshta|regsvr32|rundll32|bitsadmin|encoded|base64|http|ftp' |
Select-Object Path, LineNumber, Line | Format-Table -AutoSize

1.3 可疑计划任务特征

特征 说明 示例
执行 Temp 目录程序 正常任务不会指向 Temp Execute: C:\Users\Admin\AppData\Local\Temp\update.exe
编码命令 PowerShell -enc Execute: powershell.exe Arguments: -enc SQBuAH...
非标准时间调度 深夜、凌晨定时执行 每天凌晨 3:00 执行
高频执行 每 5-15 分钟执行一次 C2 心跳回连
SYSTEM 权限运行 攻击者常配置为最高权限 RunAs: SYSTEM RunLevel: Highest
随机/无意义任务名 正常任务名有明确含义 TaskName: Update2384
下载器行为 使用 certutil/bitsadmin 下载 certutil -urlcache -split -f http://...
无 Author 信息 或 Author 为可疑值 Author: (empty)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 自动检测可疑计划任务
Get-ScheduledTask | ForEach-Object {
$task = $_
$actions = $task.Actions
$suspicious = $false
$reasons = @()

foreach ($action in $actions) {
$exe = $action.Execute
$args = $action.Arguments

# 检查执行路径
if ($exe -like '*\Temp\*' -or $exe -like '*\AppData\*' -or $exe -like '*\Public\*') {
$suspicious = $true; $reasons += "可疑路径: $exe"
}
# 检查编码命令
if ($args -match '-enc|-EncodedCommand') {
$suspicious = $true; $reasons += "编码命令: $args"
}
# 检查下载器
if ($args -match 'http|ftp|urlcache|bitsadmin|WebClient|DownloadFile') {
$suspicious = $true; $reasons += "下载行为: $args"
}
# 检查 LOLBAS
if ($exe -match 'mshta|regsvr32|rundll32|certutil|cmstp|msiexec') {
$suspicious = $true; $reasons += "LOLBAS 工具: $exe"
}
}

# 检查 SYSTEM 运行 + 非 Microsoft 路径
if ($task.Principal.UserId -eq 'SYSTEM' -and $task.TaskPath -notlike '\Microsoft\*') {
$suspicious = $true; $reasons += "SYSTEM 权限非系统任务"
}

if ($suspicious) {
[PSCustomObject]@{
TaskName = $task.TaskName
TaskPath = $task.TaskPath
Reasons = $reasons -join ' | '
Execute = ($actions.Execute) -join ','
Arguments = ($actions.Arguments) -join ','
RunAs = $task.Principal.UserId
State = $task.State
}
}
} | Format-List

1.4 计划任务相关事件日志

Event ID 日志源 说明
4698 Security 计划任务已创建(含完整 XML 定义)
4699 Security 计划任务已删除
4700 Security 计划任务已启用
4701 Security 计划任务已禁用
4702 Security 计划任务已更新
106 TaskScheduler/Operational 任务已注册
140 TaskScheduler/Operational 任务已更新
141 TaskScheduler/Operational 任务已删除
200 TaskScheduler/Operational 任务执行开始
201 TaskScheduler/Operational 任务执行完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查询计划任务创建事件(Security 4698)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4698
} -MaxEvents 50 -ErrorAction SilentlyContinue | ForEach-Object {
$xml = [xml]$_.ToXml()
$taskXml = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TaskContent' }).'#text'
[PSCustomObject]@{
Time = $_.TimeCreated
TaskName = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TaskName' }).'#text'
Creator = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'SubjectUserName' }).'#text'
TaskXML = $taskXml.Substring(0, [Math]::Min(200, $taskXml.Length)) + '...'
}
} | Format-List

# 查询 TaskScheduler Operational 日志
Get-WinEvent -LogName "Microsoft-Windows-TaskScheduler/Operational" -MaxEvents 200 -ErrorAction SilentlyContinue |
Where-Object { $_.Id -in @(106, 140, 141) } |
Select-Object TimeCreated, Id,
@{N='Action'; E={
switch ($_.Id) { 106{'Registered'} 140{'Updated'} 141{'Deleted'} }
}}, Message |
Format-Table -AutoSize

2. Windows 服务排查

2.1 服务基础

与 Linux systemd 的对比

特征 Linux systemd Windows Services (SCM)
管理命令 systemctl sc, Get-Service
配置位置 /etc/systemd/system/ 注册表 HKLM\SYSTEM\CurrentControlSet\Services\
启动类型 enabled/disabled Auto/Manual/Disabled/DelayedAuto
权限 root 或指定用户 SYSTEM / LocalService / NetworkService / 指定用户
二进制类型 任何可执行文件 Win32OwnProcess / Win32ShareProcess / KernelDriver

2.2 服务枚举

CMD 方式

1
2
3
4
5
6
7
8
9
10
11
:: 查询所有服务
sc query type= service state= all

:: 查询特定服务详情
sc qc "ServiceName"

:: 显示服务的二进制路径(关键!)
sc qc "ServiceName" | findstr BINARY_PATH_NAME

:: 查询所有非 Microsoft 服务
wmic service get name,displayname,pathname,startmode,startname | findstr /i /v "microsoft"

PowerShell 方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 列出所有服务及二进制路径
Get-WmiObject Win32_Service | Select-Object Name, DisplayName, State, StartMode,
PathName, StartName, Description |
Sort-Object State | Format-Table -AutoSize

# 或使用 CIM(推荐)
Get-CimInstance Win32_Service | Select-Object Name, State, StartMode,
PathName, StartName | Format-Table -AutoSize

# 查找非标准路径的服务(排除 System32 和 Program Files)
Get-CimInstance Win32_Service | Where-Object {
$_.PathName -and
$_.PathName -notlike '*System32*' -and
$_.PathName -notlike '*SysWOW64*' -and
$_.PathName -notlike '*Program Files*' -and
$_.PathName -notlike '*svchost.exe*'
} | Select-Object Name, State, StartMode, PathName, StartName | Format-Table -AutoSize

# 查找以 SYSTEM 运行但不在系统目录的服务
Get-CimInstance Win32_Service | Where-Object {
$_.StartName -eq 'LocalSystem' -and
$_.PathName -and
$_.PathName -notlike 'C:\Windows\*' -and
$_.PathName -notlike '"C:\Windows\*' -and
$_.PathName -notlike 'C:\Program Files*' -and
$_.PathName -notlike '"C:\Program Files*'
} | Select-Object Name, State, PathName | Format-Table -AutoSize

2.3 Unquoted Service Path 漏洞检测

如果服务的二进制路径包含空格且未加引号,Windows 会按顺序尝试多种路径解释

攻击者可在中间路径放置恶意 EXE 实现提权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 示例:PathName = C:\Program Files\My App\service.exe
# Windows 尝试顺序:
# 1. C:\Program.exe
# 2. C:\Program Files\My.exe
# 3. C:\Program Files\My App\service.exe

# 检测 Unquoted Service Path
Get-CimInstance Win32_Service | Where-Object {
$_.PathName -and
$_.PathName -notlike '"*' -and # 不以引号开头
$_.PathName -like '* *' -and # 包含空格
$_.PathName -notlike 'C:\Windows\System32\*' # 排除 System32
} | Select-Object Name, State, StartMode, PathName, StartName | Format-Table -AutoSize

# 检查是否已被利用(恶意文件是否存在于中间路径)
Get-CimInstance Win32_Service | Where-Object {
$_.PathName -and $_.PathName -notlike '"*' -and $_.PathName -like '* *'
} | ForEach-Object {
$path = $_.PathName
$parts = $path.Split(' ')
for ($i = 0; $i -lt $parts.Count - 1; $i++) {
$testPath = ($parts[0..$i] -join ' ') + '.exe'
if (Test-Path $testPath) {
Write-Host "[!] 发现 Unquoted Path 利用: $testPath" -ForegroundColor Red
Write-Host " 服务: $($_.Name) | 原始路径: $path" -ForegroundColor Yellow
}
}
}

2.4 服务相关事件日志

Event ID 日志源 说明
7045 System 新服务已安装
7040 System 服务启动类型已更改
7034 System 服务意外终止
7036 System 服务进入运行/停止状态
4697 Security 服务安装(需开启审计)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 查询新安装的服务(Event ID 7045)
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ID = 7045
} -MaxEvents 50 -ErrorAction SilentlyContinue | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
ServiceName = $_.Properties[0].Value
ImagePath = $_.Properties[1].Value
ServiceType = $_.Properties[2].Value
StartType = $_.Properties[3].Value
AccountName = $_.Properties[4].Value
}
} | Format-Table -AutoSize

# 查找可疑的新安装服务
# 关注:ImagePath 指向 Temp/AppData,使用 cmd.exe /c 或 powershell.exe 的服务
Get-WinEvent -FilterHashtable @{
LogName = 'System'; ID = 7045
} -MaxEvents 200 -ErrorAction SilentlyContinue | Where-Object {
$imagePath = $_.Properties[1].Value
$imagePath -like '*\Temp\*' -or
$imagePath -like '*\AppData\*' -or
$imagePath -like '*cmd*/c*' -or
$imagePath -like '*powershell*' -or
$imagePath -like '*-enc*'
} | ForEach-Object {
Write-Host "[!] 可疑服务安装:" -ForegroundColor Red
Write-Host " Time: $($_.TimeCreated)"
Write-Host " Name: $($_.Properties[0].Value)"
Write-Host " Path: $($_.Properties[1].Value)"
Write-Host " Account: $($_.Properties[4].Value)"
}

2.5 注册表中的服务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 服务配置存储在注册表
# HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>

# 查看特定服务的注册表配置
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\<ServiceName>"

# 批量检查服务的 ImagePath
Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Services" | ForEach-Object {
$imagePath = (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).ImagePath
$start = (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).Start
if ($imagePath -and $start -le 2) { # Auto(2) 或 Boot(0)/System(1)
[PSCustomObject]@{
ServiceName = $_.PSChildName
ImagePath = $imagePath
StartType = switch($start) { 0{'Boot'} 1{'System'} 2{'Auto'} 3{'Manual'} 4{'Disabled'} }
}
}
} | Where-Object {
$_.ImagePath -notlike '*System32*' -and
$_.ImagePath -notlike '*SysWOW64*' -and
$_.ImagePath -notlike '*Program Files*'
} | Format-Table -AutoSize

3. COM 对象持久化概述

3.1 COM 劫持基本原理

COM (Component Object Model) 对象通过 CLSID(全局唯一标识符)注册

注册位置:

HKLM\SOFTWARE\Classes\CLSID\{GUID}\InprocServer32 — 系统级

HKCU\SOFTWARE\Classes\CLSID\{GUID}\InprocServer32 — 用户级(优先级更高)

COM 劫持:在 HKCU 中创建与系统 COM 对象相同 CLSID 的注册表项,指向恶意 DLL

当系统或应用加载该 COM 对象时,优先加载 HKCU 中的恶意 DLL

3.2 COM 劫持检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 检查 HKCU 中的 COM 注册(用户级 COM 对象很少见)
$hkcuCOM = Get-ChildItem "HKCU:\SOFTWARE\Classes\CLSID" -ErrorAction SilentlyContinue
if ($hkcuCOM) {
Write-Host "[!] 发现 HKCU 中的 COM 注册 ($($hkcuCOM.Count) 个):" -ForegroundColor Yellow
foreach ($item in $hkcuCOM) {
$inproc = Get-ItemProperty "$($item.PSPath)\InprocServer32" -ErrorAction SilentlyContinue
$localSvr = Get-ItemProperty "$($item.PSPath)\LocalServer32" -ErrorAction SilentlyContinue
if ($inproc -or $localSvr) {
[PSCustomObject]@{
CLSID = $item.PSChildName
InprocServer32 = $inproc.'(Default)'
LocalServer32 = $localSvr.'(Default)'
} | Format-List
}
}
} else {
Write-Host "[+] HKCU 中无用户级 COM 注册" -ForegroundColor Green
}

# 常被劫持的 COM 对象
# {B5F8350B-0548-48B1-A6EE-88BD00B4A5E7} - 计划任务 COM handler
# {BCDE0395-E52F-467C-8E3D-C4579291692E} - MMDeviceEnumerator
# {E436EBB8-524F-11CE-9F53-0020AF0BA770} - Filter Mapper

4. WMI 事件订阅持久化

4.1 WMI 订阅基本概念

WMI Event Subscription 是一种无文件持久化技术

三个组件缺一不可:

__EventFilter:触发条件(如定时器、进程启动、用户登录)

__EventConsumer:执行动作(CommandLineEventConsumer 执行命令、ActiveScriptEventConsumer 执行脚本)

__FilterToConsumerBinding:将 Filter 和 Consumer 绑定

Linux 对比:类似于 inotifywait + 自定义脚本,但 WMI 是系统内置且更隐蔽

4.2 WMI 订阅枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 枚举所有 WMI 事件过滤器
Get-WMIObject -Namespace root\Subscription -Class __EventFilter |
Select-Object Name, Query, QueryLanguage | Format-List

# 枚举所有 WMI 事件消费者
Get-WMIObject -Namespace root\Subscription -Class __EventConsumer |
Select-Object Name, __CLASS | Format-List

# 特别检查 CommandLineEventConsumer(执行命令)
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer |
Select-Object Name, CommandLineTemplate, ExecutablePath | Format-List

# 检查 ActiveScriptEventConsumer(执行脚本)
Get-WMIObject -Namespace root\Subscription -Class ActiveScriptEventConsumer |
Select-Object Name, ScriptingEngine, ScriptText, ScriptFileName | Format-List

# 枚举绑定关系
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding |
Select-Object @{N='Filter';E={$_.Filter.Split('"')[1]}},
@{N='Consumer';E={$_.Consumer.Split('"')[1]}} | Format-Table

# CIM 方式(推荐)
Get-CimInstance -Namespace root/Subscription -ClassName __EventFilter
Get-CimInstance -Namespace root/Subscription -ClassName __EventConsumer
Get-CimInstance -Namespace root/Subscription -ClassName __FilterToConsumerBinding

4.3 WMI 订阅清除

1
2
3
4
5
6
7
8
9
10
11
# 删除可疑的 WMI 持久化(确认恶意后再操作!)

# 删除 Filter
Get-WMIObject -Namespace root\Subscription -Class __EventFilter -Filter "Name='MaliciousFilter'" | Remove-WMIObject

# 删除 Consumer
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer -Filter "Name='MaliciousConsumer'" | Remove-WMIObject

# 删除 Binding
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding |
Where-Object { $_.Filter -like '*MaliciousFilter*' } | Remove-WMIObject

5. Startup Items 概述

5.1 常见启动项位置

详细的注册表启动项在 09-注册表持久化审计 中展开

文件系统启动项

1
2
3
4
5
6
7
8
9
# 1. Startup 文件夹(当前用户)
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" -ErrorAction SilentlyContinue

# 2. Startup 文件夹(所有用户)
Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp" -ErrorAction SilentlyContinue

# 3. 列出所有用户的 Startup 文件夹
Get-ChildItem "C:\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\*" -ErrorAction SilentlyContinue |
Select-Object FullName, CreationTime, LastWriteTime | Format-Table -AutoSize

注册表启动项(简要列出,详见第 09 章)

1
2
3
4
5
6
7
# Run 键
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue
Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue

# RunOnce 键
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" -ErrorAction SilentlyContinue
Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" -ErrorAction SilentlyContinue

6. 一键审计脚本

6.1 计划任务与服务综合审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<#
.SYNOPSIS
计划任务与服务一键审计脚本
.DESCRIPTION
枚举计划任务、服务、WMI 订阅、启动项,标记可疑项
#>

$OutputDir = "C:\IR\TaskServiceAudit_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null

Write-Host "========== 计划任务与服务审计 ==========" -ForegroundColor Cyan

# ---- 1. 计划任务 ----
Write-Host "`n[*] 1. 枚举计划任务..." -ForegroundColor Yellow
$tasks = Get-ScheduledTask | ForEach-Object {
$actions = $_.Actions
[PSCustomObject]@{
Name = $_.TaskName
Path = $_.TaskPath
State = $_.State
Author = $_.Author
RunAs = $_.Principal.UserId
Execute = ($actions.Execute) -join '; '
Arguments = ($actions.Arguments) -join '; '
}
}
$tasks | Export-Csv "$OutputDir\ScheduledTasks.csv" -NoTypeInformation -Encoding UTF8

# 标记可疑任务
$suspiciousTasks = $tasks | Where-Object {
$_.Execute -like '*\Temp\*' -or $_.Execute -like '*\AppData\*' -or
$_.Arguments -like '*-enc*' -or $_.Arguments -like '*http*' -or
$_.Arguments -like '*certutil*' -or $_.Arguments -like '*powershell*base64*'
}
if ($suspiciousTasks) {
Write-Host " [!] 发现 $($suspiciousTasks.Count) 个可疑计划任务:" -ForegroundColor Red
$suspiciousTasks | Format-List
} else {
Write-Host " [+] 未发现明显可疑计划任务" -ForegroundColor Green
}

# ---- 2. 近期创建的任务(事件日志) ----
Write-Host "[*] 2. 检查近期创建的计划任务 (Event 4698)..." -ForegroundColor Yellow
try {
Get-WinEvent -FilterHashtable @{
LogName = 'Security'; ID = 4698; StartTime = (Get-Date).AddDays(-30)
} -ErrorAction SilentlyContinue | ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated
Task = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TaskName' }).'#text'
Creator = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'SubjectUserName' }).'#text'
}
} | Format-Table -AutoSize
} catch { Write-Host " [-] 无法读取事件日志" -ForegroundColor Red }

# ---- 3. 服务审计 ----
Write-Host "[*] 3. 枚举服务..." -ForegroundColor Yellow
$services = Get-CimInstance Win32_Service | Select-Object Name, State, StartMode, PathName, StartName
$services | Export-Csv "$OutputDir\Services.csv" -NoTypeInformation -Encoding UTF8

# 非标准路径服务
$suspSvc = $services | Where-Object {
$_.PathName -and
$_.PathName -notlike '*System32*' -and $_.PathName -notlike '*SysWOW64*' -and
$_.PathName -notlike '*Program Files*' -and $_.PathName -notlike '*svchost*'
}
if ($suspSvc) {
Write-Host " [!] 非标准路径服务 ($($suspSvc.Count) 个):" -ForegroundColor Yellow
$suspSvc | Format-Table -AutoSize
}

# ---- 4. Unquoted Service Path ----
Write-Host "[*] 4. 检查 Unquoted Service Path..." -ForegroundColor Yellow
$unquoted = $services | Where-Object {
$_.PathName -and $_.PathName -notlike '"*' -and $_.PathName -like '* *' -and
$_.PathName -notlike 'C:\Windows\System32\*'
}
if ($unquoted) {
Write-Host " [!] 发现 Unquoted Path:" -ForegroundColor Red
$unquoted | Select-Object Name, PathName | Format-Table -AutoSize
}

# ---- 5. 新安装服务(Event 7045) ----
Write-Host "[*] 5. 检查近期安装的服务 (Event 7045)..." -ForegroundColor Yellow
Get-WinEvent -FilterHashtable @{
LogName = 'System'; ID = 7045
} -MaxEvents 30 -ErrorAction SilentlyContinue | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Service = $_.Properties[0].Value
ImagePath = $_.Properties[1].Value
Account = $_.Properties[4].Value
}
} | Format-Table -AutoSize

# ---- 6. WMI 订阅 ----
Write-Host "[*] 6. 检查 WMI 事件订阅..." -ForegroundColor Yellow
$filters = Get-CimInstance -Namespace root/Subscription -ClassName __EventFilter -ErrorAction SilentlyContinue
$consumers = Get-CimInstance -Namespace root/Subscription -ClassName __EventConsumer -ErrorAction SilentlyContinue
$bindings = Get-CimInstance -Namespace root/Subscription -ClassName __FilterToConsumerBinding -ErrorAction SilentlyContinue

if ($filters -or $consumers -or $bindings) {
Write-Host " [!] 发现 WMI 订阅:" -ForegroundColor Yellow
Write-Host " Filters: $($filters.Count) | Consumers: $($consumers.Count) | Bindings: $($bindings.Count)"
$filters | Select-Object Name, Query | Format-List
$consumers | Format-List
} else {
Write-Host " [+] 未发现 WMI 事件订阅" -ForegroundColor Green
}

# ---- 7. Startup 文件夹 ----
Write-Host "[*] 7. 检查 Startup 文件夹..." -ForegroundColor Yellow
$startupItems = Get-ChildItem "C:\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\*" -ErrorAction SilentlyContinue
$startupItems += Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\*" -ErrorAction SilentlyContinue
if ($startupItems) {
$startupItems | Select-Object FullName, CreationTime | Format-Table -AutoSize
} else {
Write-Host " [+] Startup 文件夹为空" -ForegroundColor Green
}

Write-Host "`n========== 审计完成:$OutputDir ==========" -ForegroundColor Cyan

7. 实战练习与对照

7.1 排查流程总结

计划任务排查步骤

  1. Get-ScheduledTask 枚举所有任务

  2. 重点检查 Execute/Arguments 中的可疑内容

  3. 查看 C:\Windows\System32\Tasks\ 中的 XML 定义

  4. 分析 Event ID 4698 追溯任务创建者和时间

  5. 关联进程分析(任务执行时产生的进程)

服务排查步骤

  1. Get-CimInstance Win32_Service 枚举所有服务

  2. 重点检查 PathName 异常的服务

  3. 检查 Unquoted Service Path

  4. 分析 Event ID 7045 追溯服务安装时间

  5. 检查注册表 HKLM\SYSTEM\CurrentControlSet\Services\ 中的配置

7.2 Linux 工程师对照

Linux 操作 Windows 对应
crontab -l Get-ScheduledTask / schtasks /query
ls /etc/cron.d/ dir C:\Windows\System32\Tasks\
systemctl list-units --type=service Get-Service / sc query
systemctl show <svc> sc qc <svc> / Get-CimInstance Win32_Service
cat /etc/systemd/system/<svc>.service reg query HKLM\SYSTEM\...\Services\<svc>
journalctl -u <svc> Event ID 7045/7040 in System log
无直接对应 WMI Event Subscription
无直接对应 COM Object Hijacking


上一章 目录 下一章
07-文件系统取证 Windows应急响应 09-注册表持久化审计