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
|
直接查看 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
Get-Content "C:\Windows\System32\Tasks\Microsoft\Windows\UpdateTask"
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" } if ($exe -match 'mshta|regsvr32|rundll32|certutil|cmstp|msiexec') { $suspicious = $true; $reasons += "LOLBAS 工具: $exe" } } 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
| 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
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
Get-CimInstance Win32_Service | Select-Object Name, State, StartMode, PathName, StartName | Format-Table -AutoSize
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
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
|
Get-CimInstance Win32_Service | Where-Object { $_.PathName -and $_.PathName -notlike '"*' -and $_.PathName -like '* *' -and $_.PathName -notlike 'C:\Windows\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
| 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
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
|
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\<ServiceName>"
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) { [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
|
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| $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 }
|
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
| Get-WMIObject -Namespace root\Subscription -Class __EventFilter | Select-Object Name, Query, QueryLanguage | Format-List
Get-WMIObject -Namespace root\Subscription -Class __EventConsumer | Select-Object Name, __CLASS | Format-List
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer | Select-Object Name, CommandLineTemplate, ExecutablePath | Format-List
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
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
|
Get-WMIObject -Namespace root\Subscription -Class __EventFilter -Filter "Name='MaliciousFilter'" | Remove-WMIObject
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer -Filter "Name='MaliciousConsumer'" | Remove-WMIObject
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
| Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" -ErrorAction SilentlyContinue
Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp" -ErrorAction SilentlyContinue
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
| Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue
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
|
$OutputDir = "C:\IR\TaskServiceAudit_$(Get-Date -Format 'yyyyMMdd_HHmmss')" New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
Write-Host "========== 计划任务与服务审计 ==========" -ForegroundColor Cyan
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 }
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 }
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 }
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 }
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
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 }
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 排查流程总结
计划任务排查步骤:
Get-ScheduledTask 枚举所有任务
重点检查 Execute/Arguments 中的可疑内容
查看 C:\Windows\System32\Tasks\ 中的 XML 定义
分析 Event ID 4698 追溯任务创建者和时间
关联进程分析(任务执行时产生的进程)
服务排查步骤:
Get-CimInstance Win32_Service 枚举所有服务
重点检查 PathName 异常的服务
检查 Unquoted Service Path
分析 Event ID 7045 追溯服务安装时间
检查注册表 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 |