跳转到帖子

从 Windows 加载器拆解到 C2 行为取证与清除

精选回复

发布于

背景

本文记录一次授权内网排查中的 Windows 远控样本分析过程。告警起点是一台办公终端周期性访问未知 443 端口,EDR 同时记录到非系统目录程序创建计划任务并操作 dllhost.exe。分析目标不是简单提取域名或删除文件,而是确认样本加载链、持久化位置、进程注入方式、权限边界及可能的横向活动。

样本只能在快照可回滚、禁止直连互联网的隔离环境中运行。建议使用 FakeNet-NG 或 INetSim 接管 DNS、HTTP 和 TLS 流量,不要让样本接触真实业务网络、域凭据、共享目录或宿主机剪贴板。

一、现场现象与证据固定

终端上的可疑文件位于 C:\ProgramData\WinCache\UpdateHelper.exe。首次处置时不要直接双击、删除或使用杀毒软件清理,否则计划任务、内存载荷和时间线可能被破坏。先隔离主机网络,并固定文件、进程、网络连接和事件日志。

$sample = 'C:\ProgramData\WinCache\UpdateHelper.exe'
Get-Item $sample | Format-List FullName,Length,CreationTimeUtc,LastWriteTimeUtc
Get-FileHash $sample -Algorithm SHA256
Get-AuthenticodeSignature $sample | Format-List Status,SignerCertificate

Get-CimInstance Win32_Process |
  Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine |
  Export-Csv C:\IR\processes.csv -NoTypeInformation

Get-NetTCPConnection |
  Select-Object State,LocalAddress,LocalPort,RemoteAddress,RemotePort,OwningProcess |
  Export-Csv C:\IR\connections.csv -NoTypeInformation

schtasks /Query /TN "\Microsoft\Windows\WDI\WDI-Update" /XML > C:\IR\WDI-Update.xml
wevtutil epl Microsoft-Windows-TaskScheduler/Operational C:\IR\TaskScheduler.evtx
wevtutil epl Microsoft-Windows-Sysmon/Operational C:\IR\Sysmon.evtx
wevtutil epl Security C:\IR\Security.evtx

现场观察到的关键链路如下:

  • UpdateHelper.exe 无有效数字签名,父进程为用户下载目录中的安装程序。
  • 样本创建计划任务 \Microsoft\Windows\WDI\WDI-Update,触发方式为用户登录后延迟执行,运行账户为 SYSTEM。
  • UpdateHelper.exe 启动挂起状态的 dllhost.exe,随后出现跨进程内存写入行为。
  • dllhost.exe 每隔约 60 秒发起一次外连,间隔存在约 15 秒抖动,目的端口固定为 443,但流量并非浏览器产生。

二、静态分析:识别加载器而不是只看字符串

样本为 64 位 PE,导入表很小,只保留 LoadLibraryW、GetProcAddress、VirtualAlloc 等少量函数。节区中 .rsrc 熵值明显偏高,说明主体载荷可能被压缩或加密存放。此类加载器直接执行 strings 往往只能得到诱饵版本信息,需要同时检查节区、资源和动态解析 API 的位置。

certutil -hashfile UpdateHelper.exe SHA256
sigcheck64.exe -nobanner -a -h -i UpdateHelper.exe
floss.exe -n 6 UpdateHelper.exe > floss.txt
capa.exe -vv UpdateHelper.exe > capa.txt

python - <<PY
import math
import pefile
from collections import Counter

path = 'UpdateHelper.exe'
pe = pefile.PE(path, fast_load=False)
print('ImageBase:', hex(pe.OPTIONAL_HEADER.ImageBase))
print('EntryPoint:', hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint))

for sec in pe.sections:
    data = sec.get_data()
    counts = Counter(data)
    entropy = 0.0
    for count in counts.values():
        p = count / len(data) if data else 0
        if p:
            entropy -= p * math.log2(p)
    name = sec.Name.rstrip(b'\x00').decode(errors='replace')
    print(name, hex(sec.VirtualAddress), len(data), round(entropy, 3))

for entry in getattr(pe, 'DIRECTORY_ENTRY_IMPORT', []):
    dll = entry.dll.decode(errors='replace')
    names = []
    for imp in entry.imports:
        names.append(imp.name.decode(errors='replace') if imp.name else f'ord:{imp.ordinal}')
    print(dll, names)
PY

本次样本的 .rsrc 熵值为 7.82,资源尾部存在 CFG0 标记。解包后可见一个反射加载 DLL 和一段配置数据。配置使用单字节异或混淆,并不具备真正的密码学强度;字段包括互斥体、休眠时间、启动方式和两个候选 C2。分析记录中应使用脱敏域名,例如 sync-gw.invalid,避免误传播仍然有效的基础设施。

静态导入中出现 VirtualAlloc、WriteProcessMemory、GetThreadContext、SetThreadContext 和 ResumeThread 时,应重点验证进程空洞或相近的注入流程。不过仅凭 API 名称不能定性,因为这些 API 也可能被调试器、安装器使用,最终结论必须和进程关系、内存权限及线程起始地址关联。

三、动态调试:还原解密和注入链

在隔离虚拟机中使用 x64dbg、Procmon 和 Process Monitor Backing File 记录行为。调试时可在 VirtualAlloc、VirtualProtect、WriteProcessMemory、SetThreadContext、QueueUserAPC、NtUnmapViewOfSection 和 ResumeThread 附近设置断点。重点不是逐条单步,而是在内存解密完成后及时转储明文载荷。

bp kernel32!VirtualAlloc
bp kernel32!VirtualProtect
bp kernel32!WriteProcessMemory
bp kernel32!SetThreadContext
bp kernel32!ResumeThread
bp ntdll!NtUnmapViewOfSection
bp ntdll!NtWriteVirtualMemory
bp ntdll!NtResumeThread

实际执行顺序为:

  • 创建互斥体 Global\WDI_{7A4B9C2E},避免同一主机重复运行。
  • 读取自身资源,定位 CFG0 标记,对后续数据进行异或解码并解压。
  • 以 CREATE_SUSPENDED 标志启动 C:\Windows\System32\dllhost.exe。
  • 取消映射原始映像,在目标进程中申请 RW 内存并写入解包后的 PE。
  • 修复重定位和导入表,将载荷区域改为 RX,修改线程上下文后恢复线程。
  • 退出加载器,后续心跳和命令执行均由 dllhost.exe 承担。

判断进程空洞时可以检查三个相互印证的特征:dllhost.exe 的映像路径仍指向系统文件;主线程起始地址落入匿名可执行内存而不是已加载模块;内存中的 PE 头、节区布局与磁盘上的 dllhost.exe 不一致。仅看到父子进程异常不足以确认注入。

四、C2 行为与网络侧验证

FakeNet-NG 捕获到样本先进行 DNS 查询,随后连接 TCP 443。流量首包不是标准 TLS ClientHello,而是固定四字节标记、版本号、主机标识长度和加密负载,因此基于端口判断为 HTTPS 会漏检。心跳包长度集中在 96 至 144 字节,服务器不可达时采用 5、15、30、60 秒递增退避。

Wireshark 可先使用以下过滤条件缩小范围:

dns.qry.name || tcp.dstport == 443
tcp.stream eq 7
tcp.len > 0 && ip.addr == 192.0.2.25
tls.handshake.extensions_server_name

如果 443 流量无法被 TLS 解析、没有 SNI、首包字节稳定且连接周期固定,应按自定义协议继续检查,不能直接归类为“加密流量不可见”。可结合 Zeek conn.log、dns.log 以及代理设备日志统计周期性、目的地址稀有度和单连接字节数。

从内存载荷导出的功能字符串和命令分发表明,该远控包含主机信息收集、文件读写、屏幕截取和命令执行模块。没有发现键盘记录模块,也没有看到浏览器凭据解密代码。报告中应区分“代码具备能力”和“现场已经执行”:只有对应线程、文件、网络回包或日志证据存在时,才能认定某功能被实际调用。

五、持久化与权限边界判断

计划任务 XML 显示运行主体为 SYSTEM,且任务位于受保护目录。普通用户进程无法直接完成该配置,因此需要继续确认加载器是否继承了安装程序的管理员令牌,或者此前已发生提权。建议核对以下日志:

  • Security 4688:进程创建及完整命令行。
  • Security 4624、4672:登录类型、管理员特权分配和账户来源。
  • TaskScheduler Operational 106、140、141:任务注册、更新和删除。
  • Sysmon 1、3、7、8、10、11、13、22、25:进程、网络、模块加载、远程线程、进程访问、文件、注册表、DNS 和进程篡改。
  • System 7045 与 Security 4697:是否额外创建服务。
  • WMI-Activity 5857 至 5861:是否存在 WMI 永久事件订阅或远程执行。

本次事件中,4688 显示加载器由提权后的安装程序启动,随后以高完整性令牌创建 SYSTEM 计划任务。未发现 UAC 绕过痕迹,因此权限边界突破点是用户主动批准的伪造安装程序,而不是操作系统本地提权漏洞。

横向排查不能只搜索样本文件哈希。还应检索同名任务、互斥体关联字符串、异常 dllhost.exe 网络连接,以及 4624 登录类型 3、5140 管理共享访问、7045 远程服务创建等事件。文件哈希在重新打包后容易变化,行为组合通常更稳定。

六、检测规则示例

以下 YARA 规则针对本次加载器的稳定结构编写。任务路径和互斥体属于高置信特征,通用注入 API 只作为组合条件,避免把普通安装程序全部命中。

import "pe"

rule Win64_WDI_RemoteAccess_Loader
{
    meta:
        description = "Detects the analyzed WDI-themed RAT loader"
        scope = "authorized incident response"
    strings:
        $task  = "\\Microsoft\\Windows\\WDI\\WDI-Update" wide ascii
        $mutex = "Global\\WDI_{" wide ascii
        $cfg   = { 43 46 47 30 }
        $api1  = "WriteProcessMemory" ascii
        $api2  = "SetThreadContext" ascii
        $api3  = "ResumeThread" ascii
    condition:
        uint16(0) == 0x5A4D and
        pe.machine == pe.MACHINE_AMD64 and
        $cfg and
        1 of ($task, $mutex) and
        2 of ($api*)
}

主机侧还可以围绕“非系统父进程创建挂起 dllhost.exe,随后发生进程访问并连接外网”建立关联规则。单独对 dllhost.exe 外连告警可能产生噪声,建议同时约束父进程路径、签名、线程起始地址和网络时间窗口。

七、清除与修复

清除顺序应为隔离、取证、阻断、移除持久化、删除载荷、凭据处置和复核。不要只结束 dllhost.exe 或删除 UpdateHelper.exe;计划任务可能再次拉起副本,远控也可能已经获得用户令牌。

# 先保存任务和文件证据,再执行清理
schtasks /End /TN "\Microsoft\Windows\WDI\WDI-Update"
schtasks /Delete /TN "\Microsoft\Windows\WDI\WDI-Update" /F

Stop-Process -Id <suspicious_dllhost_pid> -Force
Remove-Item 'C:\ProgramData\WinCache\UpdateHelper.exe' -Force
Remove-Item 'C:\ProgramData\WinCache\wincache.dat' -Force

# 清理后复查
schtasks /Query /FO LIST /V | findstr /I "WDI-Update WinCache"
Get-CimInstance Win32_Process | Where-Object {
    $_.Name -eq 'dllhost.exe' -and $_.ExecutablePath -ne 'C:\Windows\System32\dllhost.exe'
}
Get-NetTCPConnection | Where-Object State -eq 'Established'
  • 在 DNS、防火墙和代理侧阻断已确认的 C2,但保留首次、末次访问时间和命中主机清单。
  • 重置受影响用户密码,吊销当前登录会话、VPN 会话和应用令牌。若存在 LSASS 访问或浏览器数据读取证据,应扩大凭据轮换范围。
  • 启用 4688 命令行审计、PowerShell 4103/4104、TaskScheduler Operational 和合理裁剪后的 Sysmon 规则。
  • 使用 WDAC 或 AppLocker 限制 ProgramData、用户下载目录和临时目录中的未签名可执行文件。
  • 复核本地管理员成员、计划任务、服务、启动项、WMI 订阅和 Defender 排除项,防止残留第二种持久化方式。
  • 如果无法证明载荷未执行凭据窃取或内核级操作,业务终端优先重装并从可信基线恢复,不建议仅做文件级清理。

总结

远控样本分析的重点不是得到一个域名或文件哈希,而是把“入口文件、解包过程、注入目标、持久化、C2、权限来源和横向痕迹”串成可验证的证据链。本次样本最终确认通过计划任务维持 SYSTEM 权限,将载荷空洞注入 dllhost.exe,并使用伪装为 443 的自定义协议周期回连。静态特征用于快速归类,动态调试确认真实执行路径,主机和网络日志则用于判断影响范围。修复时只有同时处理持久化、凭据风险和同类行为狩猎,才能避免把一次远控入侵误处理成普通恶意文件告警。

逆向分析与样本观察示意图
逆向分析、样本行为与调试流程示意
服务器与云环境安全示意图
服务器、云资产与权限边界梳理示意

这类事件不要先删加载器,建议按“保全现场 → 确认执行链 → 还原 C2 → 清除与轮换凭据”的顺序处理。直接删除样本会丢失内存注入、父子进程和持久化证据。

1. 先隔离网络并保留内存、连接信息

mkdir C:\IR

tasklist /v > C:\IR\tasklist.txt
wmic process get ProcessId,ParentProcessId,ExecutablePath,CommandLine /format:csv > C:\IR\process.csv
netstat -abno > C:\IR\netstat.txt
ipconfig /displaydns > C:\IR\dns-cache.txt
arp -a > C:\IR\arp.txt
wevtutil epl System C:\IR\System.evtx
wevtutil epl Security C:\IR\Security.evtx
wevtutil epl Microsoft-Windows-PowerShell/Operational C:\IR\PowerShell.evtx
wevtutil epl Microsoft-Windows-Windows Defender/Operational C:\IR\Defender.evtx

有条件的话再做完整内存镜像。重点检查加载器进程的父进程、外连 PID,以及同一进程中是否存在私有可执行内存、异常线程起始地址或被替换的映像段。若怀疑进程注入,仅看磁盘样本通常不够。

2. 从加载器行为关联到 C2

把连接记录中的 PID 回查到进程命令行,并核对 DNS 缓存、代理配置和证书信息:

Get-NetTCPConnection |
  Sort-Object OwningProcess |
  Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,State,OwningProcess

Get-CimInstance Win32_Process |
  Select-Object ProcessId,ParentProcessId,ExecutablePath,CommandLine

netsh winhttp show proxy
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' |
  Select-Object ProxyEnable,ProxyServer,AutoConfigURL

取证时不要只记录域名。建议同时保留目标 IP、端口、首次/末次出现时间、DNS 解析结果、HTTP Host、TLS SNI、证书指纹和固定 Beacon 间隔。若流量看似正常 HTTPS,但连接周期高度稳定、每次上下行字节数接近,也应纳入 C2 判断。

3. 排查常见持久化点

schtasks /query /fo LIST /v > C:\IR\schtasks.txt
sc query type= service state= all > C:\IR\services.txt

reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run /s
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run /s
reg query "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" /s
reg query "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Shell
reg query "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Userinit

Get-CimInstance -Namespace root\subscription -ClassName __EventFilter
Get-CimInstance -Namespace root\subscription -ClassName CommandLineEventConsumer
Get-CimInstance -Namespace root\subscription -ClassName __FilterToConsumerBinding

还应核对启动目录、服务二进制路径、计划任务 Action、WMI 永久事件订阅,以及用户目录下新出现的 DLL/EXE。服务路径无引号、指向 AppData/ProgramData/Temp,或计划任务调用 rundll32、regsvr32、mshta、powershell -enc,都需要重点确认。

4. 用时间线验证,不要只依赖文件时间戳

加载器可能回改时间。可交叉比对 Prefetch、Amcache、Shimcache、Defender、PowerShell 和计划任务日志。重点事件包括 Security 4688、PowerShell 4104、任务计划 106/140/141、服务安装 7045,以及 Defender 检测与隔离记录。若系统原先未开启进程创建审计,4688 可能不完整,应以多源证据互相印证。

5. 清除时按 IOC 对象逐项处理

确认对应关系后,再停止恶意进程、禁用持久化项、隔离文件并封禁域名/IP。样本先计算哈希并记录路径:

Get-FileHash 'C:\path\sample.exe' -Algorithm SHA256
Get-AuthenticodeSignature 'C:\path\sample.exe' | Format-List

# 示例:确认任务确属恶意后再执行
schtasks /change /tn "\可疑任务名" /disable
schtasks /delete /tn "\可疑任务名" /f

# 示例:确认服务确属恶意后再执行
sc.exe stop SuspiciousService
sc.exe delete SuspiciousService

如果已经出现 LSASS 访问、浏览器凭据窃取、令牌盗用、横向移动或域管理员登录痕迹,不建议把“文件删除后不再外连”视为清除完成。更稳妥的是重装受影响主机,并在可信设备上轮换本地管理员、域账号、VPN、邮箱、浏览器保存密码及 API 密钥,同时吊销现有会话和令牌。最后用原有 C2 IOC、文件哈希、计划任务名和异常命令行在全网回溯,避免只处理首台告警主机。

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。