服务器突然连接陌生 IP,安全软件只留下了一条告警;可疑进程已经退出,磁盘上也找不到对应文件。这时,真正有价值的证据可能仍留在内存里:攻击者执行过的命令、尚未释放的网络连接、解密后的配置、注入进程的代码,以及已经删除但仍在页缓存中的文件。
Volatility 正是用来提取这些运行时证据的开源内存取证框架。它可以分析进程、命令行、网络连接、DLL、ELF、文件对象、注册表、内核模块和代码注入等信息,让零散告警还原成一条可以验证的攻击链。
本文以 Volatility 3 为准,回答四个实际问题:
- 如何确认一份内存镜像能够正常分析;
- 如何从大量进程中快速锁定异常 PID;
- 如何关联命令行、网络、文件、持久化和内存注入证据;
- 如何分别排查 Windows 与 Linux 用户态、内核态恶意行为。
旧版 Volatility 2 已停止维护,网上常见的 imageinfo、--profile、connscan 和 memdump 等命令不能直接照搬到 Volatility 3。本文选取的是日常应急响应中的主流插件,而不是把所有实验性插件机械堆在一起。
如果已经安装好 Volatility,可以先记住最核心的调查思路:
1 | 识别镜像 → 建立进程基线 → 检查命令和网络 → 锁定可疑 PID → 排查注入或 Rootkit → 导出证据 → 构建时间线 |
一、先跑通 Volatility 3:安装、镜像与输出
1.1 Volatility 2 与 Volatility 3 的主要区别
| 对比项 | Volatility 2 | Volatility 3 |
|---|---|---|
| Python 版本 | 主要依赖 Python 2 | Python 3.8 及以上 |
| 系统识别 | 使用 imageinfo 猜测 Profile |
通过 Automagic 自动构建分析层 |
| Windows 内核信息 | 手动指定 --profile |
使用微软 PDB 符号,无需 Profile |
| 插件格式 | pslist |
windows.pslist |
| 项目状态 | 仓库已归档 | 当前维护版本 |
因此,Volatility 3 的基本命令是:
1 | vol -f <内存镜像> <插件> [插件参数] |
如果使用的是 GitHub 源码目录,也可以将 vol 替换为:
1 | python vol.py -f <内存镜像> <插件> [插件参数] |
1.2 Windows 安装
建议在独立虚拟环境中安装,避免和系统中的其他 Python 工具产生依赖冲突。
1 | py -3 -m venv .venv |
如果 PowerShell 禁止执行激活脚本,可以直接调用虚拟环境中的程序:
1 | .\.venv\Scripts\vol.exe -h |
1.3 Linux 安装
1 | python3 -m venv .venv |
通过下面的命令确认版本以及可用插件:
1 | vol --version |
具体插件的参数不要凭印象填写,可以随时查看帮助:
1 | vol windows.pslist -h |
1.4 获取和校验内存镜像
Volatility 只负责分析,不负责采集内存。Windows 可以使用 WinPmem、FTK Imager 等工具。以 WinPmem 为例,在管理员终端中执行:
1 | winpmem_mini_x64.exe memory.raw |
采集完成后立即记录时间、主机信息、工具版本和文件哈希:
1 | Get-FileHash .\memory.raw -Algorithm SHA256 |
Linux 可以使用 sha256sum:
1 | sha256sum memory.raw |
分析时应复制一份工作副本,原始镜像设为只读并单独保存。内存采集本身会改变少量目标内存,因此它是采集期间的近似快照,而不是完全静止的系统状态。
1.5 输出目录和结果格式
全局参数必须放在插件名称之前。-o 指定导出目录,-r 指定结果渲染格式:
1 | vol -f memory.raw -o output windows.pslist |
建议每次调查建立独立目录:
1 | case-001/ |
二、Windows 调查:从异常进程追到持久化
2.1 识别镜像和系统信息
Volatility 3 不需要先运行 imageinfo,直接使用 windows.info:
1 | vol -f memory.raw windows.info |
重点关注:
Is64Bit:系统是否为 64 位;NtMajorVersion、NtMinorVersion:Windows 内核版本;NtSystemRoot:系统目录;SystemTime:镜像中的系统时间;Kernel Base:内核基址。
如果这里就出现 Unsatisfied requirement,先不要继续运行其他插件,应检查镜像格式、文件完整性和符号解析情况。
2.2 枚举进程
进程调查不能只看一个插件。不同插件使用不同数据来源,交叉比较才能发现隐藏、退出或被破坏的进程对象。
1 | vol -f memory.raw windows.pslist |
| 插件 | 作用 | 调查重点 |
|---|---|---|
windows.pslist |
遍历活动进程链表 | 正常运行的进程 |
windows.psscan |
扫描内存中的进程对象 | 已退出、隐藏或解链进程 |
windows.pstree |
展示父子进程关系 | 异常父进程和启动链 |
windows.psxview |
对比多种进程枚举来源 | 可能隐藏的进程 |
分析进程树时需要结合 Windows 的正常启动关系。例如,普通情况下 services.exe、lsass.exe 应由 wininit.exe 启动,winlogon.exe 不应从用户临时目录加载。下面这些现象值得继续调查:
- 系统进程名称被轻微拼错,如
scvhost.exe; - 系统进程位于用户目录或临时目录;
- Office、浏览器等应用直接启动 PowerShell、脚本解释器或系统管理工具;
- 父进程不存在、PID 关系异常或启动时间明显不合理;
psscan能找到,但pslist中不存在;- 同名系统进程数量、会话或父进程不符合正常规律。
2.3 查看进程上下文
发现可疑 PID 后,不要立即下结论。继续检查命令行、环境变量、账户、权限、句柄和 DLL:
1 | vol -f memory.raw windows.cmdline --pid 1234 |
常用判断方法:
cmdline:寻找编码 PowerShell、远程下载地址、可疑脚本或异常参数;envars:检查临时目录、代理设置和被修改的搜索路径;getsids:确定进程所属用户及安全主体;privileges:关注调试、模拟、加载驱动等高权限;handles:寻找打开的文件、注册表键、互斥体、令牌和命名管道;dlllist:寻找从临时目录、用户目录或网络路径加载的 DLL。
查看控制台缓冲区和历史命令:
1 | vol -f memory.raw windows.consoles |
内存中不一定保留完整命令历史。没有结果只能说明相关结构未找到,不能证明攻击者没有执行命令。
2.4 网络连接
1 | vol -f memory.raw windows.netscan |
netscan 通过扫描网络结构恢复 TCP/UDP 信息,通常是 Windows 调查的首选;netstat 可以作为补充,但对内核符号和数据结构完整性的要求更高。
应记录以下字段:
- 本地和远程 IP、端口;
- TCP 状态;
- 所属 PID 和进程名;
- 创建时间;
- 同一进程是否连接多个异常地址。
单凭陌生 IP 不能定性。应将网络连接与进程路径、命令行、代码注入结果和威胁情报交叉验证。LISTENING 端口也要检查是否与主机角色相符。
2.5 文件对象及内容导出
先扫描内存中的文件对象:
1 | vol -f memory.raw windows.filescan |
结果较多时,可以先保存并搜索关键词:
1 | vol -f memory.raw windows.filescan > filescan.txt |
Linux/macOS 中可使用 grep,Windows 中可使用 Select-String:
1 | Select-String -Path .\filescan.txt -Pattern "\.exe|\.dll|\.ps1|Temp|Downloads" |
获得 _FILE_OBJECT 的虚拟地址后导出文件:
1 | vol -f memory.raw -o output windows.dumpfiles --virtaddr 0x9e8f12345670 |
如果拿到的是物理地址,则使用:
1 | vol -f memory.raw -o output windows.dumpfiles --physaddr 0x12345670 |
也可以按文件名正则表达式筛选:
1 | vol -f memory.raw -o output windows.dumpfiles --filter "(?i)payload.*\.dll$" |
导出结果可能来自数据段、映像段或缓存,文件不一定完整。应先计算哈希,再使用静态分析工具检查,不要直接双击运行:
1 | Get-FileHash .\output\* -Algorithm SHA256 |
2.6 注册表、服务和持久化
列出内存中的注册表配置单元:
1 | vol -f memory.raw windows.registry.hivelist |
读取常见自启动位置:
1 | vol -f memory.raw windows.registry.printkey --key "Software\Microsoft\Windows\CurrentVersion\Run" |
如果存在多个用户或多个同名配置单元,可以使用 hivelist 给出的偏移限定目标 Hive:
1 | vol -f memory.raw windows.registry.printkey --offset 0x9e8f00123000 --key "Software\Microsoft\Windows\CurrentVersion\Run" |
其他常用持久化与程序执行痕迹插件:
1 | vol -f memory.raw windows.svcscan |
这些结果的含义不同:服务和计划任务更接近持久化配置;UserAssist、Amcache 和 Shimcache 主要用于证明程序存在或可能执行过,不能仅凭一条缓存记录证明恶意行为。
2.7 账户与敏感信息
在合法授权的调查中,可以检查内存注册表中的本地账户哈希、域缓存凭据和 LSA Secret:
1 | vol -f memory.raw windows.hashdump |
这些输出属于高敏感证据,保存、传输和展示时应脱敏。文章、工单和公开报告中不要直接粘贴真实账户哈希或密钥。
2.8 检查进程内存区域
查看进程的虚拟地址描述符(Virtual Address Descriptor,VAD):
1 | vol -f memory.raw windows.vadinfo --pid 1234 |
重点关注:
- 同时具有写入和执行权限的私有内存;
- 没有对应磁盘文件的可执行区域;
- 路径与进程身份不匹配的映射文件;
- 区域头部包含
MZ,但没有正常模块记录; - 权限、文件映射和实际内容之间存在矛盾。
2.9 检测注入和进程空洞
1 | vol -f memory.raw windows.malfind --pid 1234 |
malfind 会寻找具有可疑权限和内容的内存区域,但命中不等于恶意。浏览器、Java、.NET、杀毒软件和其他使用 JIT 的程序也可能创建可写、可执行内存。正确做法是继续检查反汇编、PE 头、线程起始地址、网络连接及进程来源。
需要导出可疑区域时,先查看当前版本帮助是否支持 --dump:
1 | vol windows.malfind -h |
2.10 使用 YARA 扫描
安装完整依赖后,可以使用 YARA 规则扫描内核或进程 VAD。下面使用简单规则寻找 PE 文件头,仅用于演示语法:
1 | vol -f memory.raw windows.vadyarascan --pid 1234 --yara-string 'rule find_pe { strings: $mz = { 4D 5A } condition: $mz at 0 }' |
使用规则文件时,先通过帮助确认当前版本接受的 URI 格式:
1 | vol windows.vadyarascan -h |
YARA 结果只是匹配线索。规则过宽会产生大量误报,规则过窄又可能漏掉变种,因此要结合上下文判断。
2.11 导出进程和进程内存
导出进程的 PE 映像:
1 | vol -f memory.raw -o output windows.pslist --pid 1234 --dump |
导出进程可访问的内存映射:
1 | vol -f memory.raw -o output windows.memmap --pid 1234 --dump |
两者用途不同:pslist --dump 更适合恢复进程映像,memmap --dump 更适合后续字符串搜索、配置提取和离线扫描。导出失败并不代表进程正常,相关内存页可能已经换出、损坏或不完整。
2.12 内核模块与 Rootkit 线索
1 | vol -f memory.raw windows.modules |
modules 与 modscan 的差异类似 pslist 与 psscan:前者遍历正常模块链表,后者扫描内存结构。需要关注未签名驱动、异常加载路径、模块链表差异、指向未知地址范围的回调及卸载后仍残留的驱动痕迹。
2.13 文件系统和时间线线索
1 | vol -f memory.raw windows.mftscan |
MFT 扫描可能恢复已删除文件名、时间戳和备用数据流(Alternate Data Stream,ADS)线索。时间线结果应与进程创建时间、网络连接时间、注册表最后写入时间和外部日志统一到同一时区后再排序。
2.14 容易遗漏的实用线索
前面的命令足以完成大多数调查,但下面几组插件经常能补上关键证据。
查看登录会话、桌面与命令历史:
1 | vol -f memory.raw windows.sessions |
cmdscan 偏向恢复命令历史结构,consoles 还会解析控制台缓冲区。两者可能得到重复或残缺内容,应相互补充。
扫描互斥体和线程:
1 | vol -f memory.raw windows.mutantscan |
恶意软件常用命名互斥体避免重复运行;可疑线程插件则重点检查线程起始地址是否位于无文件映射或异常权限的 VAD 中。互斥体名称和线程告警都只能作为关联线索。
检查并导出注册表证书:
1 | vol -f memory.raw windows.registry.certificates |
这有助于发现被植入的根证书、代理证书或攻击工具使用的证书。导出后还需使用证书工具核对颁发者、指纹、有效期和用途。
将外部 strings 结果映射回具体进程:
1 | strings -a -td memory.raw > strings.txt |
windows.strings 不是重新提取字符串,而是读取带十进制物理偏移的 strings 输出,将内容关联到内核或进程地址空间。Windows 路径应写成合法的 file:/// URI。
三、Linux 调查:从用户态进程查到内核 Rootkit
3.1 内存采集与符号表准备
Volatility 3 不负责采集 Linux 内存。官方教程推荐使用 AVML(Acquire Volatile Memory for Linux),执行前应确认工具支持目标内核和处理器架构:
1 | sudo ./avml linux.raw |
先扫描镜像中的 Linux Banner:
1 | vol -f linux.raw banners |
Banner 通常包含内核版本、编译器和发行版信息。它是寻找符号表的重要依据,但镜像中可能同时残留多个 Banner,应以目标系统实际运行的内核为准。
Linux 内核可以自行编译,符号表必须尽量与目标主机的内核版本、配置和调试信息严格匹配。应优先从官方教程推荐的 volatility3-symbols 仓库查找预生成的 JSON.XZ 文件;没有匹配项时,再使用 dwarf2json 转换带 DWARF 调试信息的 vmlinux:
1 | ./dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-6.8.0-31-generic > symbols/linux/ubuntu-6.8.0-31.json |
通过 -s 指定符号目录,然后验证启动时间和进程列表:
1 | vol -s symbols -f linux.raw linux.boottime |
如果 banners 有结果,但 linux.pslist 提示符号不满足,通常说明符号文件缺失或与目标内核构建不匹配。
3.2 进程、权限与命令历史
首先建立 Linux 进程基线:
1 | vol -s symbols -f linux.raw linux.pslist |
| 插件 | 作用 | 调查重点 |
|---|---|---|
linux.pslist |
遍历任务链表 | 活动进程、UID、GID 和创建时间 |
linux.pstree |
展示父子进程 | 异常 Shell、提权链和孤立进程 |
linux.psaux |
展示完整命令行 | 下载器、反弹 Shell 和异常启动参数 |
linux.pidhashtable |
从 PID 哈希表枚举任务 | 与 pslist 交叉发现隐藏进程 |
对可疑 PID 继续检查环境变量、Capabilities、内存映射和跟踪关系:
1 | vol -s symbols -f linux.raw linux.envars --pid 1234 |
重点关注 LD_PRELOAD、LD_LIBRARY_PATH、代理变量、云凭据环境变量、异常工作路径,以及不符合进程身份的 CAP_SYS_ADMIN、CAP_SYS_PTRACE 等高权限 Capability。linux.check_creds 用于发现多个进程异常共享凭据结构的情况,可作为权限劫持或 Rootkit 线索。
恢复 Bash 历史命令:
1 | vol -s symbols -f linux.raw linux.bash |
linux.bash 从进程内存结构恢复历史记录,不等同于读取磁盘上的 .bash_history。没有结果可能是 Shell 类型不同、结构已释放或对应内存页不在镜像中。
3.3 网络接口、连接与防火墙
检查网络接口、地址和链路状态:
1 | vol -s symbols -f linux.raw linux.ip.Addr |
重点关注混杂模式、异常虚拟接口、容器网络命名空间,以及与主机角色不符的地址配置。
枚举活动和残留 Socket:
1 | vol -s symbols -f linux.raw linux.sockstat |
sockstat 从进程文件描述符关联 Socket,适合确认 PID;sockscan 扫描内存中的 Socket 结构,可补充已经关闭或未正常关联的对象。调查时记录协议、本地地址、远程地址、端口、网络命名空间和所属进程。
检查 Netfilter 钩子:
1 | vol -s symbols -f linux.raw linux.netfilter |
异常 Netfilter 回调可能与防火墙篡改、流量隐藏或内核级后门有关,但必须确认回调地址是否属于正常内核模块。
3.4 文件、挂载点与 ELF
列出进程打开的文件:
1 | vol -s symbols -f linux.raw linux.lsof |
查看挂载点和命名空间:
1 | vol -s symbols -f linux.raw linux.mountinfo |
该结果可用于发现临时文件系统、覆盖挂载、容器挂载和异常设备。容器环境中不同进程可能处于不同 Mount Namespace,不能只检查宿主机视角。
枚举页缓存中的文件及 Inode:
1 | vol -s symbols -f linux.raw linux.pagecache.Files |
linux.pagecache.Files 用于寻找缓存文件;linux.pagecache.InodePages 可按 Inode 导出仍驻留页缓存的内容。不同版本的 Inode 选择和导出参数可能变化,应先使用 -h 查看当前版本要求,并通过全局 -o output 设置导出目录。
检查进程映射的 ELF 和共享库:
1 | vol -s symbols -f linux.raw linux.elfs |
需要导出 ELF 时先确认插件帮助:
1 | vol linux.elfs -h |
重点关注 /tmp、/dev/shm、已删除路径、无对应文件的 ELF 映射,以及从用户可写目录加载到高权限进程的共享库。
3.5 内核模块与 Rootkit 检测
先读取内核日志和内核线程,为后续异常判断建立上下文:
1 | vol -s symbols -f linux.raw linux.kmsg |
内核日志可能保留模块加载失败、崩溃、设备和安全策略信息;内核线程名称、父子关系或处理函数地址异常时,需要进一步核对对应内核模块。
查看已加载模块并使用不同来源交叉检查:
1 | vol -s symbols -f linux.raw linux.lsmod |
这些插件分别从模块链表、内存扫描或其他内核结构枚举模块。某个插件单独出现差异不一定是 Rootkit,也可能由模块卸载、内存损坏或内核版本差异造成。
检查常见内核 Hook:
1 | vol -s symbols -f linux.raw linux.check_syscall |
| 插件 | 主要检查对象 |
|---|---|
linux.check_syscall |
系统调用表 Hook |
linux.check_idt |
中断描述符表异常指针 |
linux.check_afinfo |
网络协议操作函数 Hook |
linux.tty_check |
TTY 接收缓冲回调 Hook |
linux.keyboard_notifiers |
键盘通知链,可发现内核键盘记录器线索 |
现代 Linux 还应检查 eBPF 和追踪框架:
1 | vol -s symbols -f linux.raw linux.ebpf |
eBPF、Ftrace 和 Tracepoint 本身都是正常机制。判断恶意性时,需要检查程序名称、加载时间、回调地址、所属模块及其是否符合系统中的监控或安全产品。
3.6 恶意代码和进程内存扫描
寻找匿名可执行区域和可疑内存权限:
1 | vol -s symbols -f linux.raw linux.malfind |
linux.malfind 重点展示匿名可执行映射和异常权限,可用于发现无文件载荷、注入代码或已经解包的程序。JIT、运行时和安全产品也可能产生相似区域,因此仍需结合进程身份、映射内容和网络行为判断。
使用正则表达式或 YARA 扫描进程虚拟内存:
1 | vol -s symbols -f linux.raw linux.vmaregexscan --pid 1234 --pattern "https?://" |
插件参数可能随版本调整,执行前用下面的命令确认:
1 | vol linux.vmaregexscan -h |
3.7 Linux 推荐调查顺序
1 | # 1. 识别内核并验证符号 |
四、把命令输出变成证据链:流程与排错
4.1 推荐执行顺序
面对未知 Windows 内存镜像,可以按下面的顺序调查:
1 | # 1. 确认镜像可解析 |
最后应形成一条能够互相支持的证据链,例如:
1 | 异常父子进程 |
插件命中本身不是最终结论。报告中应分别写明事实、推断和仍需验证的内容。
4.2 常见错误
vol 不是内部或外部命令
虚拟环境没有激活,或者 Python 的 Scripts 目录不在 PATH 中。直接执行虚拟环境内的 vol.exe,或进入源码目录使用 python vol.py。
Unsatisfied requirement plugins...kernel
常见原因包括:
- 镜像不是受支持的 Windows 内存格式;
- 镜像不完整或已经损坏;
- Windows PDB 下载失败;
- Linux 镜像缺少匹配的符号表;
- 将插件用于错误的操作系统镜像。
增加日志级别可以查看更多诊断信息:
1 | vol -vvv -f memory.raw windows.info |
第一次运行非常慢
Windows 符号不存在时,Volatility 会尝试从微软符号服务器获取 PDB 并在本地生成缓存。确认分析机能够访问网络,并保留缓存目录,后续运行通常会更快。
旧教程命令无法运行
常见迁移关系如下:
| Volatility 2 写法 | Volatility 3 对应方式 |
|---|---|
imageinfo |
windows.info |
--profile=Win7SP1x64 |
删除,由符号和 Automagic 处理 |
pslist |
windows.pslist |
connscan、connections |
主要使用 windows.netscan |
procdump |
windows.pslist --pid <PID> --dump |
memdump |
windows.memmap --pid <PID> --dump |
hivelist |
windows.registry.hivelist |
printkey |
windows.registry.printkey |