跳转到帖子

逆向木马远控实战:从 Windows 加载器、进程注入到 C2 留痕的完整排查

精选回复

发布于

背景与分析边界

本文记录一次授权内网攻防演练中的 Windows 远控样本分析过程。告警主机出现异常计划任务,随后由办公软件子进程发起外联。样本链路为“加载器—内存解密—进程注入—远控通信”,具备持久化、主机信息收集和命令执行能力。

分析目标不是复现木马功能,而是确认加载方式、权限边界、注入目标、C2 协议特征以及可用于批量排查的日志和检测规则。所有动态分析均应在隔离虚拟机内完成,关闭共享目录、剪贴板和宿主机网络桥接。

不要直接在生产终端双击可疑样本。应先保全文件、内存、计划任务及日志;清理动作会破坏时间线和内存中的配置数据。

一、现场保全与问题定位

首先记录样本路径、文件时间、父子进程、登录用户和主机网络连接。文件时间可能被篡改,不能单独作为入侵时间依据,应与 Sysmon、任务计划程序、Prefetch、Amcache 和代理日志交叉验证。

$Case = 'C:/IR/case-2025-01'
New-Item -ItemType Directory -Force $Case | Out-Null

Get-Date -Format o | Out-File "$Case/collection-time.txt"
Get-Process | Sort-Object Id | Format-Table Id,ProcessName,Path -Auto | Out-File "$Case/process.txt"
Get-NetTCPConnection | Sort-Object OwningProcess | Format-Table -Auto | Out-File "$Case/tcp.txt"
Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine | Export-Csv "$Case/process-tree.csv" -NoTypeInformation

Get-FileHash 'C:/Users/Public/update.exe' -Algorithm SHA256 | Format-List | Out-File "$Case/hash.txt"
Get-AuthenticodeSignature 'C:/Users/Public/update.exe' | Format-List * | Out-File "$Case/signature.txt"
Copy-Item 'C:/Users/Public/update.exe' "$Case/sample.bin"

保全时建议同时记录文件系统 ACL。部分加载器会利用普通用户可写目录与高权限计划任务之间的边界错误完成提权或持久化。

icacls 'C:/Users/Public/update.exe'
icacls 'C:/ProgramData/Updater'
whoami /all
whoami /priv

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

对 PE 文件先检查架构、节区、签名、导入表和熵值。典型加载器常见特征包括节区名称异常、入口点位于高熵节、导入函数极少、只保留 LoadLibrary/GetProcAddress,或者包含较大的资源段和覆盖数据。

  • 使用 Detect It Easy、PE-bear 或 pefile 检查 PE 头、节区权限和 Overlay。
  • 使用 FLOSS 提取栈字符串、解码字符串和静态字符串,避免只执行 strings 后误判“无内容”。
  • 使用 capa 判断是否包含进程注入、反调试、计划任务、服务控制或网络通信能力。
  • 若为 .NET 样本,先确认 CLR Header,再使用 ILSpy 或 dnSpyEx 检查入口类、资源解密函数和反射加载逻辑。
diec sample.bin
floss sample.bin > floss.txt
capa -vv sample.bin > capa.txt
objdump -x sample.bin > pe-header.txt

本次样本的导入表只有少量基础 API,但在解密循环后动态解析 VirtualAlloc、VirtualProtect、CreateProcessW 和 WriteProcessMemory。资源段中存在一块高熵数据,解密后以 MZ 开头,说明第一阶段主要承担内存加载职责。

逆向时应重点定位三类函数:配置解密函数、API 解析函数、最终控制流转移位置。对简单 XOR 或滚动异或,可从“循环读取密文—与短密钥运算—写入新缓冲区”的数据流入手;不要仅根据反编译器自动生成的变量名判断语义。

三、动态分析:在 API 边界观察明文

样本存在字符串加密时,最有效的办法通常不是先完整还原算法,而是在数据即将交给系统 API 时观察参数。可在 x64dbg 中对以下函数设置断点:

  • kernelbase!VirtualAlloc、VirtualProtect:确认新内存区域大小及权限变化。
  • kernelbase!CreateProcessW:记录被创建的宿主进程及创建标志。
  • kernelbase!WriteProcessMemory、ntdll!NtWriteVirtualMemory:确认写入目标 PID、地址和长度。
  • kernelbase!CreateRemoteThread、ntdll!NtCreateThreadEx:确认远程线程入口地址。
  • winhttp!WinHttpConnect、WinHttpSendRequest:提取目标主机、URI 和发送缓冲区。
  • ws2_32!connect、send、recv:分析自定义 TCP 协议。
  • bcrypt!BCryptDecrypt、crypt32!CryptUnprotectData:在加解密边界获取配置或通信明文。

Windows x64 调用约定中,前四个整数或指针参数通常位于 RCX、RDX、R8、R9,其余参数位于栈上。例如命中 WinHttpSendRequest 时,R9 指向可选请求体,长度参数位于栈参数区。记录缓冲区前应先确认指针可读和长度合理,避免把句柄或无效地址误认为数据。

如果样本通过 GetProcAddress 动态解析 API,可先在该函数返回前记录请求的函数名,再对解析出的地址下执行断点。若样本直接使用系统调用,则结合调用前寄存器、内存映射和 Sysmon 事件分析,不应只依赖用户态 API 断点。

四、确认进程注入链路

动态行为显示加载器以挂起方式创建合法系统进程,向其写入解密后的 PE 映像,随后修改线程上下文并恢复执行。此类行为与普通远程线程注入不同,仅检索 CreateRemoteThread 可能漏报。

应将以下证据串联起来:

  • 源进程创建目标进程时包含挂起标志。
  • 目标进程出现私有、可执行内存,且内存内容具有 PE 头或代码特征。
  • 线程入口或指令指针位于非映像内存区域。
  • 磁盘映像与进程内存映像不一致。
  • 目标进程随后产生与其正常业务不符的网络连接。

可使用 Process Explorer 检查线程起始地址和签名,使用 PE-sieve 或 Moneta 检查可执行私有内存。工具结果需要结合进程用途判断,浏览器、EDR 和 JIT 运行时也可能存在可执行私有页面。

pe-sieve64.exe /pid 4321 /threads /iat 2 /data 2 /dir C:/IR/dump
moneta64.exe -p 4321 -m ioc

五、持久化与权限边界排查

本次样本通过当前用户计划任务启动,因此初始执行不需要管理员权限。但任务动作指向用户可写目录,如果同类任务由高权限账户创建,就可能形成明显的权限边界问题。

schtasks /query /fo LIST /v > C:/IR/scheduled-tasks.txt
reg query HKCU/Software/Microsoft/Windows/CurrentVersion/Run /s
reg query HKLM/Software/Microsoft/Windows/CurrentVersion/Run /s
sc query type= service state= all

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

排查计划任务时不要只看任务名称。需要核对创建时间、作者、执行账户、最高权限选项、触发器、动作路径及工作目录。重点关注动作位于 AppData、Temp、Public、ProgramData 子目录,或执行 powershell、wscript、mshta、rundll32 后加载用户可写文件的任务。

六、C2 通信逆向与网络留痕

样本通过 HTTPS 外联时,抓包通常只能看到域名、证书、IP、连接周期和流量长度。此时可在加密前后的 API 边界提取明文,而不是在生产网络中贸然安装自签根证书。

本次流量具有固定心跳周期,请求体解密后由长度字段、消息类型、主机标识和数据组成。研判时建议记录协议结构而非仅记录域名,因为攻击者更换域名后,消息长度分布、URI、请求头顺序和心跳周期仍可能保持稳定。

  • DNS:首次解析时间、TTL、历史解析和同主机查询记录。
  • TLS:SNI、证书指纹、JA3/JA4,仅作为组合特征,不宜单独封禁。
  • HTTP:方法、URI、User-Agent、请求体长度和固定头字段。
  • 时序:心跳间隔、失败重连退避、上下行字节比例。
  • 进程:连接所属 PID、父进程和进程签名。
tshark -r traffic.pcapng -Y 'dns or tls.handshake.type == 1' -T fields -e frame.time -e ip.dst -e dns.qry.name -e tls.handshake.extensions_server_name
tshark -r traffic.pcapng -Y 'tcp.stream eq 3' -x

七、使用 Sysmon 还原行为时间线

如果终端已部署 Sysmon,可重点关联以下事件:1 为进程创建,3 为网络连接,7 为映像加载,8 为远程线程创建,10 为进程访问,11 为文件创建,12/13/14 为注册表操作,22 为 DNS 查询。事件 8 和 10 噪声较大,应结合源进程、目标进程、访问权限和网络行为筛选。

$Start = (Get-Date).AddDays(-3)
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; StartTime=$Start; Id=1,3,8,10,11,12,13,14,22} |
  Select-Object TimeCreated,Id,MachineName,Message |
  Export-Csv 'C:/IR/sysmon-timeline.csv' -NoTypeInformation -Encoding UTF8

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational'; StartTime=$Start} |
  Select-Object TimeCreated,Id,Message |
  Export-Csv 'C:/IR/task-timeline.csv' -NoTypeInformation -Encoding UTF8

时间线中应先找到最早的异常落地文件,再向前追溯下载、邮件附件、共享目录或远程管理入口,向后关联持久化、注入和外联。只删除最终远控进程,无法确认初始入口是否仍然存在。

八、样本检测规则

YARA 规则应优先组合样本家族中相对稳定的结构和字符串,避免只使用文件哈希,也不要仅凭常见注入 API 就判定为木马。下面的规则用于已确认样本族的初筛,命中后仍需人工验证:

rule Win_RAT_Loader_Family_Triage
{
    meta:
        description = "Authorized incident triage rule for analyzed RAT loader family"
        scope = "file"
    strings:
        $api1 = "NtWriteVirtualMemory" ascii wide
        $api2 = "NtCreateThreadEx" ascii wide
        $net1 = "WinHttpSendRequest" ascii wide
        $cfg1 = "machine_id" ascii wide
        $cfg2 = "sleep_interval" ascii wide
        $mutex = "Global\\UpdateSession_" ascii wide
    condition:
        uint16(0) == 0x5A4D and
        filesize < 8MB and
        2 of ($api*) and
        1 of ($net*) and
        2 of ($cfg*, $mutex)
}

部署前应在干净软件库、运维工具和 EDR 组件上回放测试。若字符串由运行时解密产生,文件扫描可能无法命中,应补充内存扫描、行为检测和网络侧特征。

九、风险处置与修复建议

  • 先隔离主机网络并保留内存、日志和样本,再终止恶意进程。不要在证据未保全时直接重装。
  • 删除持久化项前导出计划任务 XML、注册表项、服务配置和 WMI 订阅,保留原始时间信息。
  • 若远控进程处于高完整性级别或 SYSTEM 权限,按凭据已暴露处理,轮换登录过该主机的域账户、服务账户和本地管理员密码。
  • 检查目标进程令牌、SeDebugPrivilege、凭据访问日志及横向登录记录,确认是否突破普通用户权限边界。
  • 封禁域名和 IP 只能作为临时措施,还应基于进程链、计划任务动作、内存特征和协议行为建立检测。
  • 限制普通用户对高权限任务动作目录、服务二进制路径和 DLL 搜索目录的写权限。
  • 启用 PowerShell 脚本块日志、任务计划程序日志、Sysmon 和集中式日志留存,并统一终端时间源。
  • 若无法证明样本未获取凭据或未安装其他组件,优先可信重装,而不是仅删除已知文件。

总结

远控木马分析的关键不是给样本贴一个家族名称,而是还原可验证的执行链:文件如何落地、加载器如何解密、代码进入哪个进程、以什么权限运行、如何持久化、通信明文在何处出现,以及这些行为在主机和网络侧留下什么证据。

实际排查中,静态逆向负责定位关键函数,动态断点负责获取运行时配置,内存检查负责确认注入,Sysmon 与任务日志负责还原时间线。最终检测应由文件、行为、内存和网络多类特征共同组成,避免样本更换哈希或 C2 地址后完全失效。

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

这条排查链里,建议把重点放在“注入后的进程如何与 C2 通信对应起来”,否则很容易只定位到加载器,却把真正承载通信的宿主进程漏掉。

如果现场有 Sysmon,可以先拉取事发时间段的进程、网络和注入相关事件,再按 ProcessGuid 串联,别只依赖 PID(会复用)。例如:

$filter = @{
    LogName  = 'Microsoft-Windows-Sysmon/Operational'
    Id       = 1,3,8,10,22,25
    StartTime = (Get-Date).AddHours(-2)
}
Get-WinEvent -FilterHashtable $filter | ForEach-Object {
    $event = $_
    $xml = [xml]$event.ToXml()
    $d = @{}
    foreach ($item in $xml.Event.EventData.Data) {
        $d[$item.Name] = $item.'#text'
    }
    [pscustomobject]@{
        Time       = $event.TimeCreated
        EventId    = $event.Id
        Guid       = $d['ProcessGuid']
        Image      = $d['Image']
        SourceGuid = $d['SourceProcessGUID']
        TargetGuid = $d['TargetProcessGUID']
        Source     = $d['SourceImage']
        Target     = $d['TargetImage']
        RemoteIP   = $d['DestinationIp']
        RemotePort = $d['DestinationPort']
        DNS        = $d['QueryName']
    }
} | Sort-Object Time | Export-Csv .\sysmon-timeline.csv -NoTypeInformation -Encoding UTF8

这里有几个容易误判的点:

  • 事件 8 不是注入的必要条件。它主要覆盖远程线程创建,APC、线程上下文劫持等路径可能不出现;事件 10 的进程访问也只能算线索,需要结合访问权限、调用栈和目标进程内存判断。
  • 网络连接要追到目标进程。若加载器访问了某个宿主进程,后续应沿 TargetProcessGUID → ProcessGuid 查网络与 DNS 事件,而不是只筛加载器文件名。DNS 还可能发生在注入前,或由其他组件完成。
  • 内存里的可执行私有页不等于木马。要结合线程起始地址、页内容、模块映射关系,以及是否对应到异常外联判断;JIT 也会产生类似内存特征。仅筛 RWX 页还会漏掉先写入、后改成 RX 的载荷。

以上事件是否存在取决于 Sysmon 版本和采集规则,尤其网络事件 3 需要确认已启用;没日志不能反推“没发生”。发现宿主进程异常后,条件允许的话先隔离网络、保留进程或整机内存,再终止进程。最后把样本中的 C2 配置、主机侧实际连接、DNS/代理侧记录分开标注:配置里出现的地址不一定真正连接过,共享 IP 也不宜直接当作封禁依据。

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

最近浏览 0

  • 没有会员查看此页面。