搜索全站

输入至少 2 个字符,查找相关内容。

↑↓ 选择 Enter 打开Esc 关闭

进程与注入检测

进程是内存里最基础也最有价值的工件。它回答三个基本问题:谁在跑、谁启动了谁、运行的这个是不是它自称的那个。

最后更新 2026-10-09版本 v2.0维护 DigiForensics查看历史 1

一、概述

进程是内存里最基础也最有价值的工件。它回答三个基本问题:谁在跑、谁启动了谁、运行的这个是不是它自称的那个。

但"进程"这件事在取证上有一个陷阱:你通过工具看到的进程列表,本身就可能是假的。

Windows 内核用一个双向链表(PsActiveProcessHead)串起所有活动进程。常规工具遍历这个链表来列出进程。

但内核态的代码完全有能力把自己从链表上摘下来,同时让进程照常运行——这就是 DKOM(Direct Kernel Object Manipulation)类技术。

摘链之后,任务管理器、任务栏、tasklist 全都看不见它。但它的代码正在执行、它的内存还在被使用。

这正是 pslist 与 psscan 对比的意义:

方法 原理 能发现摘链进程吗
pslist 遍历内核维护的活动进程链表 ❌ 摘链的进程不在链表上,遍历不到
psscan 扫描物理内存,搜索进程对象特征 ✅ 对象还在内存里,扫描得到

"psscan 有、pslist 无" = 该进程正在运行但被刻意隐藏 = 强恶意指标。 这是内存取证最经典的考点之一,本篇第二节会讲清原理。

除了隐藏,进程层面还要看三件事:名字是不是真的(伪装)、代码是不是自己的(注入/空化/反射加载)、权限是不是异常。本篇逐一展开。

在取证流程中的位置:本篇是内存分析的第一跳,拿到镜像和符号表(windows.info)之后、在任何细查插件之前先跑一遍。它的作用是给后面所有分析提供一份"这台机器上应该有什么"的基线——没有基线,就无法判断哪个进程多余。上游依赖是内存镜像采集与符号包:符号版本和镜像对不上时 pslist 会直接读不出链表头,这是本领域最常见的翻车点,所以符号校验必须在跑插件之前做完。下游是把这份进程清单交给网络连接、凭据、注入三类分析去交叉。

本篇处理的证据形态——不是一个文件,而是一组散落在非分页池里的内核结构:

证据 位置 / 标识 本篇用它判断什么
进程对象 非分页池,池标签 Proc 进程是否真实存在于内存
活动进程链表 由符号 PsActiveProcessHead 解析 能否用遍历法枚举
链上节点 _EPROCESS + ActiveProcessLinks 遍历与扫描结果的差异来源
短进程名 ImageFileName,满 15 字符即截断 名字是否被伪装
完整命令行 PEB 的 ProcessParameters → CommandLine 长路径程序的真面目
父进程号 InheritedFromUniqueProcessId 父子链是否合理
VAD 区权限 vadinfo 的权限位、malfind 找到的 PAGE_EXECUTE_READWRITE 段 是否存在代码注入或空化

这些字段的具体偏移随 Windows 版本变动,不要照抄别处的偏移表,以当前符号解析出的结构定义为准。

采集那一瞬间有哪些进程在跑、父子关系与完整命令行、可执行文件的完整路径与哈希基线、是否存在摘链或代码注入这两类主动隐藏行为,以及哪些进程的权限明显超出其功能需要。

进程退出后不留任何痕迹——psscan 没扫到某个程序,不等于它从未运行,只说明采集区间里没有它的对象了。阴性结果必须写成"在非分页池扫描覆盖范围内未见对应的 Proc 对象",同时列出未覆盖范围:对象已释放的残留、非常规内核版本、页面已换出而未被扫到的情况。时间戳锚定的是采集时刻,证明不了"行为已结束"。命令行是目标进程自己写的字符串,不是执行事实——它可以伪造。摘链结论证明的是"有人在隐藏一个进程",推不出动机,也分不清是攻击者的手法还是安全软件的正常保护行为。同理,父进程号可被任意指定,父子链不构成因果证明。

读者前提:你得能读十六进制偏移、照着结构定义在内存里跳指针(这是手工复核 psscan 结果的前提),得知道非分页池和内核对象大致怎么分配,知道 _EPROCESS 里哪些字段由内核维护、哪些由进程自己填——这个区别直接决定哪些字段能被伪造、哪些不能。只用过任务管理器或进程管理器 GUI 的读者,命令能照抄,但判读会失准。


二、核心原理

2.1 pslist vs psscan:两种完全不同的枚举原理

pslist —— 遍历链表:框架从内核全局变量 PsActiveProcessHead 出发,沿着每个 EPROCESS 里的 ActiveProcessLinks 成员(一个 LIST_ENTRY)逐个走。

这是内核自己维护的"官方名单"。

优点:得到的是内核视角的当前活动进程,包含完整的创建时间、父进程、句柄数等元数据,且顺序反映真实的创建顺序。 缺点:只信链表。任何把 EPROCESS 从链表上摘掉的操作,都会让它从 pslist 的输出里消失。** psscan —— 扫描内存**:框架不信任链表,而是遍历物理内存的每一页,寻找符合 EPROCESS 特征模式的字节序列。

判定依据包括:关键成员位置的值是否合理(如 ExitStatus 为 0 表示活动、Pcb 指针对齐并落在内核范围)、ImageFileName 是否是合法的内核字符串、各链表指针是否自洽。

优点:不看链表也能找到进程对象。摘链、链表指针被篡改、乃至部分池分配被复用,只要对象还在,就扫得出来。 缺点:会把已退出的进程也扫出来。一个刚结束的进程,其 EPROCESS 对象可能尚未被内存分配器回收,此时 psscan 还能看到它——但它已经不是活动进程了。

两者的差集是核心判据:

现象 解释 结论强度
psscan 有,pslist 无 摘链的隐藏进程,正在运行 ★★★★★ 极强
pslist 有,psscan 无 刚退出、对象尚未回收,非隐藏 ★ 无(属正常现象)
两边完全一致 无摘链迹象(但不排除其他隐藏手法) 中性

务必注意第二行:很多人把"pslist 有、psscan 无"也当成异常,这是误判。已终止进程在对象池里滞留是 Windows 的正常行为。这条差异不构成任何结论。

2.2 多视角交叉:psxview

手工对比 pslist 和 psscan 两份输出容易出错。psxview 插件(规范位置 windows.malware.psxview)自动比对四种枚举视角:pslist(遍历活动进程链表)、psscan(扫描内存中的 EPROCESS)、thrdscan(线程枚举反查进程)、csrss(会话/桌面相关的进程视图)。

输出中任一列为 False 就意味着该视角看不到这个进程。 关键是看哪一列为 False:

  • pslist 列为 False,但其他列为 True → 摘链隐藏,最典型的 rootkit 特征
  • psscan 列为 False,其他为 True → 对象刚被回收,正常
  • 多数列为 False → 进程已终止

它还提供 Exit Time 列:有退出时间的进程是已经终止的。Exit Time 有值时,这个进程不是隐藏的,只是不活跃。

2.3 进程名伪装的三种形态

pslist 输出有两个名字列,它们不一致就是线索:ImageFileName 来自 EPROCESS 内的字段(内核记录,15 字符截断,进程运行期间不变),Name 来自 PEB 的进程参数(用户态可写,理论上可被改)。

伪装形态一:模仿系统进程名——真实系统进程都从 C:\Windows\System32\ 加载。pslist 输出不含完整路径,需配合 windows.dlllist(看 DLL 加载路径)或导出进程映像后查 PE 头。

伪装形态二:Unicode 不可见字符——用 svchost.exe (尾部加空格)、svchost.exe.(尾部加点)或插入不可见字符都能骗过粗略的肉眼比对。判据是名称长度:ImageFileName 字段只有 15 字节,尾部空白会被保留但显示时可能被吞掉。必须看字符编码和字节长度,不能只看渲染出来的字符串。

伪装形态三:ImageFileName 与 Name 不一致——这是最可靠的判据,因为两者来自不同位置,要伪造需同时改两处。

2.4 注入检测:malfind 的原理

windows.malware.malfind 检测的是私有可执行内存页(Private Page with Execute/Write 权限),它不依赖任何已知恶意样本。

判定逻辑(三条同时满足即报出):

1. 该 VAD 区域没有对应的文件映射(不是任何 DLL/EXE 的一部分)
2. 该区域的保护属性包含 PAGE_EXECUTE(可执行)
3. 区域内容的头部呈现 PE 特征

典型命中的关键字段:Protection 为 PAGE_EXECUTE_READWRITE(可写+可执行,正常模块不会有写权限)、Private 为 1(非文件 backed)、Hexdump 以 4D 5A 90 00 开头(MZ 头)、Disasm 为入口点指令。

MZ 头 + PAGE_EXECUTE_READWRITE + 私有页 = 注入的强特征。 但不是所有命中都是恶意:JIT 引擎(.NET、Java、浏览器 V8)、防病毒软件、部分游戏的合法代码都符合这个模式。必须结合进程类型判断——notepad.exe 里命中要警惕,chrome.exe 里命中很可能是 V8。

因此:malfind 的输出必须结合 pslist 判断进程是否活动,对已终止进程的命中要谨慎解读。

2.5 其他注入手法与各自特征

手法 内存中的表现 检测方式
经典 DLL 注入 目标进程加载了非预期 DLL;DOBL 列表与内存映射可能不符 windows.dlllist + windows.malware.ldrmodules
反射式 DLL 加载(RDI) 内存中有完整 PE 映像但不在模块列表中,无标准 DllMain 入口 malfind 命中 + ldrmodules 缺失 + 手工分析
进程空化(Process Hollowing) 进程映像名与实际内存内容不符;主线程起始地址不指向自身 EntryPoint windows.malware.hollowprocesses
进程伪装(PEB Masquerade) 进程参数中的 ImagePath/CommandLine 被改 windows.malware.pebmasquerade
线程劫持 线程起始地址落在非预期模块内 windows.malware.suspicious_threads
原子注入(Atom Bombing) 共享内存区出现代码,权限为可读可写而非可执行 malfind 变体思路 + vadinfo 看权限组合
无文件执行(Fileless) 只有内存中的映像,磁盘上无对应文件 内存有完整 PE + 磁盘镜像 MFT/USN 查无此文件(filescan 仅为辅助线索)

2.6 VAD:理解一切内存区域的基础

VAD(Virtual Address Descriptor,虚拟地址描述符) 是 Windows 内核对一段虚拟内存的记账结构,是理解 malfind、vadinfo、vadwalk 的前提。内核用红黑树维护所有 VAD,按虚拟地址排序。windows.vadinfo 显示某进程的 VAD 树(起始地址、大小、保护属性、类型),windows.vadwalk 直接遍历 VAD 树原始结构(更底层),windows.vadyarascan / windows.vadregexscan 用规则扫 VAD 区域内容。

保护属性对照:

保护值 含义 是否正常
PAGE_READONLY / PAGE_READWRITE 普通数据 正常
PAGE_EXECUTE_READ 只读可执行(.text 段典型) 正常
PAGE_EXECUTE_READWRITE 可写 + 可执行 可疑,正常模块极少有
PAGE_EXECUTE_WRITECOPY 写时复制可执行 少见

这类可写可执行页也出现在合法的动态插桩场景里。Frida 注入后内存里是什么形态、与常规远程线程注入怎么区分,见Frida 动态 Hook。


三、操作步骤

3.1 第 1 步:建立基线

# 先确认符号可用(否则后面全部不可信)
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.info

# 进程基线:完整列表
python3 vol.py -f DigiForensics-mem.raw -r csv windows.pslist > pslist.csv
wc -l pslist.csv

先看几个必须确认的基线项:

  • System(PID 0)、smss.exe(PID 2)、csrss.exe 是否存在
  • 进程总数是否合理(正常工作站 150–300 个)
  • 有无 Exit Time 有值的进程(有值 = 已终止)

3.2 第 2 步:摘链检测(核心操作)

最可靠的做法:直接用 psxview(它在一个插件里交叉比对多个视角)。输出示例(隐藏进程):

Offset(Virtual)   Name            PID    pslist  psscan  thrdscan  csrss  Exit Time
0x1a3e5000        svc_host.exe    7124   False   True    True      True   N/A
                   ↑ pslist=False:链表里没有,但扫描得到

两处易错:① 默认输出的是 Offset(Virtual),只有加 --physical-offsets 才是 Offset(Physical);② 最后一列是** Exit Time(带空格)**,写成 ExitTime 会匹配不上。

判读表:

pslist psscan thrdscan csrss Exit Time 结论
False True True True N/A 摘链隐藏进程,正在运行
False True True True 有值 已终止,摘链可能是"清理痕迹"
True False True True N/A 刚退出,对象未回收(正常)
True True True True N/A 无异常

注意 pslist 与 psscan 的方向性差异:pslist 有、psscan 无,通常不是隐藏,而是进程刚退出、EPROCESS 对象尚未被回收。只有 psscan 有、pslist 无才是摘链的经典信号。

若要用原始输出手工比对(注意 CSV 有前置的 TreeDepth 列):

# 列序:TreeDepth,PID,PPID,ImageFileName,Offset,... → PID 是第 2 列,不是第 3 列
# comm -13 集合A 集合B 输出"只在 B 中" → 即 psscan 有、pslist 无
comm -13 \
  <(tail -n +2 pslist.csv  | cut -d, -f2 | tr -d '"' | sort -u) \
  <(tail -n +2 psscan.csv | cut -d, -f2 | tr -d '"' | sort -u)

⚠️ 两个易错点:① CSV 第一列是 TreeDepth,所以 PID 在 $2、$3 是 PPID——取错列会得到一堆父进程号,完全无意义;② comm 要求输入已排序,务必加 sort -u。

3.3 第 3 步:命令行全量提取

windows.cmdline 是取证价值最高、最容易被忽略的插件——它能还原每个进程"当初是怎么被启动的"。

# 快速筛可疑命令行
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.cmdline \
  | grep -iE 'powershell|cmd\.exe|wscript|cscript|mshta|rundll32|regsvr32|certutil|bitsadmin|curl|wget|invoke-|iex|bypass|-enc'

# 用 --filters 只匹配 cmdline 列(比 grep 管道精确;注意是子串匹配,不是通配符)
python3 vol.py -f DigiForensics-mem.raw windows.cmdline --filters cmdline,cmdline,powershell
# 需要正则时在模式末尾加 !,例如:--filters cmdline,cmdline,-e[c|n]!
# 排除某些行用前缀 -: --filters -cmdline,conhost

3.4 第 4 步:父子关系异常

python3 vol.py -f DigiForensics-mem.raw -r pretty windows.pstree > pstree.txt
# 典型可疑组合(详见第 08 篇):
#   winword.exe / excel.exe    →  powershell.exe / cmd.exe   (Office 钓鱼)
#   w3wp.exe / wmiprvse.exe    →  powershell.exe             (Web/WMI 触发)
#   svchost.exe(-k netsvcs) →  未知进程                    (服务被滥用)
#   System (PID 0)            →  任何非系统进程              (几乎必为伪造)
# 人工审查时:System→X、Office→shell、svchost→未知,均需逐一解释

3.5 第 5 步:注入区域排查

# 扫描注入(先看表头确认列名)
python3 vol.py -f DigiForensics-mem.raw -r csv windows.malware.malfind > malfind.csv
grep -i 'PAGE_EXECUTE_READWRITE' malfind.csv

# 导出命中区域做进一步分析(MZ = 4D 5A)
mkdir -p /work/malfind-dump
python3 vol.py -f DigiForensics-mem.raw -o /work/malfind-dump windows.malware.malfind --dump
for f in /work/malfind-dump/*.dmp; do printf "%s  " "$(basename $f)"; xxd -l 4 -p "$f"; done

# 对命中的进程做 VAD 层确认
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.vadinfo --pid 7124
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.vadwalk --pid 7124 \
  | grep -iE 'EXECUTE|PRIVATE'

3.6 第 6 步:模块与进程映像一致性

# 某进程加载的所有 DLL(非系统目录的 DLL 是强信号)
python3 vol.py -f DigiForensics-mem.raw -r csv windows.dlllist --pid 7124 \
  | grep -viE 'C:\\Windows|C:\\Program Files'

# 模块链表 vs 内存映射(发现未登记的模块)
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.malware.ldrmodules --pid 7124

# 进程空化 / PEB 伪装检测
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.malware.hollowprocesses
python3 vol.py -f DigiForensics-mem.raw -r pretty windows.malware.pebmasquerade

四、常见陷阱

进程层分析的每一个结论都建立在"我看到的就是全部"这个假设上,而摘链、伪装、空化都在专门破坏这个假设。

本章按视角交叉、字段判读、注入归因、结论表述四个环节展开。陷阱全部落在 `