一、概述
内存证据在法律程序上面临一个结构性的不对称:它和磁盘证据的所有保障机制都是为磁盘设计的。
| 特征 |
磁盘证据 |
内存证据 |
| 可重复采集 |
✅ 今天不采明天还能采 |
❌ 只有一次机会 |
| 事后可验证 |
✅ 可对同一设备重采并比对 |
❌ 原始状态已消失 |
| 时间点 |
文件时间戳是固定的 |
内存是一个采集区间的混合体 |
| 完整性锚点 |
哈希 + 多次采集比对 |
哈希是唯一锚点(只有一个样本可比) |
| 采集过程的证明 |
通常不必要(设备可复现状态) |
必须证明"谁、何时、如何采的" |
| 敏感度 |
取决于访问权限 |
可能含全部用户的明文数据 |
问题也随之而来。
你怎么证明这份镜像不是被人伪造的、不是上次采集的、不是从别的设备来的?答案有三层:
- 镜像哈希——锚定"这份文件自采集后未变"
- 采集记录——锚定"什么时候、谁、用什么工具采的"
- 目标机自身的日志——锚定"确实有人在目标机上做过内存采集"
第三层是很多办案人员不知道的:Windows 事件日志里能证明"有人从这台机器做了内存采集"。
这就是本篇的精华,也是内存证据链最容易被忽略的一环。
在取证流程中的位置:本篇不属于"从镜像里挖信息"的环节,它同时横跨采集的收尾与报告的成文两端——采集时它决定要留下哪些记录(Sysmon 必须事先部署,时间记录必须精确到秒),分析后它决定结论怎么写、哪些结论必须降级表述。所以它必须在采集前就被读完,采完再补是补不上的:Sysmon 没部署就没有事件,采集起止时间没记就永远说不清。这与本板块的 Volatility3 框架与插件选择(采集工具的取舍)和 进程与注入检测(分析结论怎么写)配套:采集方法决定镜像里有什么,可采性决定镜像能不能用,判读决定结论能写到什么程度。
本篇处理的证据形态,是三组证据加两组时间:
| 证据 |
具体位置与字段 |
作用 |
| 镜像哈希 |
采集后、分析前、分析后各算一次,三条必须完全一致 |
锚定"文件自采集后未变";这是唯一可信性锚点,因为只有一个样本可比 |
| 采集记录 |
采集开始/结束时间(精确到秒、含时区)、目标机系统时间(从 windows.info 的 SystemTime 读)、操作人与见证人 |
锚定"何时、谁、用什么工具采的" |
| Sysmon 采集痕迹 |
事件 9 RawAccessRead(Device 字段出现 \\.\PhysicalDrive0,这是全量物理内存采集的直接特征)、事件 11(TargetFilename 只能是普通文件路径,如 E:\evidence\DigiForensics-mem.raw)、事件 10(进程内存访问,更常出现在单进程转储场景)、事件 1、事件 6 与 System.evtx 7045 |
独立证明"有人在这台机器上做过内存采集",这是镜像哈希做不到的 |
| 时钟基准 |
目标机 SystemTime 必须落在采集起止区间内 |
目标机时钟不准时,所有时间线分析都带偏差,必须在报告里记录这个偏差 |
| 可复现性记录 |
每条命令、参数、工具版本、执行人、时间 |
让复核人能重走一遍;报告里"我们找到了 X"需要的是这个过程,不是结论本身 |
Sysmon 事件 9 单独不足以定性:读 \\.\PhysicalDrive0 也可能是磁盘取证工具或恶意软件在读磁盘。判据必须是组合证据——读到物理设备 + 产生了与目标机物理内存大小相当的文件。反过来,事件 10 也不是全量采集的主证据,把它当成主要证据是最常见的误判。
这份内存镜像在什么时间区间采集、由谁操作、采集后是否被改动过、目标机上是否真的发生过一次物理内存读取、每条分析结论能否被别人按记录复现、哪些结论有交叉验证支撑。
镜像里有 X 只能证明"采集期间 X 存在于内存中"——它不能证明用户当时在屏幕上看到 X(内容可能在一个后台进程的缓冲区里,或者早已被换出到 swap);它不能证明采集那几分钟里系统状态没有变;它不能证明 X 不是在采集开始前几秒才被创建、或在采集后被覆盖。内存里找不到某个进程或某个密钥,只能表述为"在该镜像覆盖范围内未发现",未覆盖范围包括已被换出并释放的页、被覆盖的缓冲区、以及采集工具本身的局限——阴性结论在这里几乎没有证明力。Sysmon 的组合证据只证明"发生过采集",证明不了采到的内容有没有被替换;采集起止时间缺失时,任何指向具体行为的结论都无法归属到具体时刻。隐私上也要注意边界:内存里可能含全部用户的明文数据,这既是它的价值也是它的处置风险,报告只写与案件相关的那部分。
读者前提:需要先掌握 Sysmon 与日志聚合(部署必须发生在事发前,本篇的所有日志证据都建立在那篇的配置上,没部署就是没有);需要理解 .evtx 的解析与 EventData 字段的准确位置——事件 9 的 Device 与事件 11 的 TargetFilename 字段集不同,混用会导致检索为空;需要接受"可复现性记录"这个交付要求,并知道它要求你在动手前把命令和参数写下来而不是事后回忆;还需要理解隐私最小化原则在取证报告里的适用方式——写出哈希存在于哪个文件的哪个偏移、属于哪个 RID 账户,而不是把口令或密钥原文直接写进正文。
二、核心原理
2.1 内存证据的四个特有问题
问题一:易失性 → 采集时间必须精确到秒
内存证据的价值几乎全部来自"它记录的是某个特定时刻的状态"。三类时间缺一不可:
| 时间 |
说明 |
为什么必要 |
| 采集开始时间 |
精确到秒,含时区 |
内存证据代表的区间起点 |
| 采集结束时间 |
精确到秒,含时区 |
区间终点;两者之差即采集耗时 |
| 目标机系统时间 |
从 windows.info 的 SystemTime 读出 |
验证目标机时钟与采集机时钟是否一致 |
目标机时钟偏差是真实的坑。目标机时钟不准(手动改过、未同步、时区设错)时,"采集时间"与"内存中记录的时间"就无法对齐。必须交叉比对:SystemTime 落在采集起止区间内 → 时钟基本可信;偏离很大 → 必须在报告中记录这个偏差,因为它影响所有时间线分析。
问题二:无法重复采集
磁盘取证可以「再采一遍看看是否一样」来验证,内存做不到。
所以:采集过程必须一次做对(中途失败只能重来,而重来的那次已经不是「原始状态」);不能「先试试能不能采到」;必须双人确认(一人操作、一人见证)。
问题三:哈希是唯一可信性锚点
既然只有一份样本,「完整性」只能靠哈希保证,且采集后、分析前、分析后三条哈希必须完全一致。任何一条不一致,镜像的可用性就要打问号。命令见 3.5。
问题四:「当时内存里有什么」无法事后重建
你可以确定的:镜像里有 X(因为镜像里有 X)
你不能确定的:采集那一刻用户是否"看得到" X
采集那 5 分钟里系统是否一直在变
X 是否在采集前几秒才被创建
X 是否在采集后被覆盖(而你在镜像里看到的是"更早"的副本)
一个具体例子:你在内存里找到一个敏感文件的内容。这证明"该文件的内容在采集期间存在于内存中"。但不能证明"用户当时在屏幕上看到它"——内容可能在一个后台进程里,或者在采集开始前就已经被换出到 swap 了。
2.2 采集痕迹:目标机日志能独立证明「有人采过内存」
这是内存证据链最关键、也最容易被忽略的一环。
为什么需要它:如果某方对内存镜像提出质疑(「你怎么证明这不是编的?」),镜像自身的哈希只能证明「文件没被改」,不能证明「它确实来自那台设备」。
目标机自身的日志能提供这个证明——而且它独立于分析过程,不可由分析人员构造。
Windows 上做内存采集必然产生痕迹:读取物理设备(Sysmon 事件 9)、创建大文件(事件 11)、访问进程内存(事件 10)、采集工具进程创建(事件 1)、驱动加载(事件 6 / System.evtx 7045)。
Sysmon 需要提前部署。没有 Sysmon 的机器上不会有这些事件——日志侧证据的存在与否,取决于采集之前有没有做日志部署准备。详见 2.4。
2.3 四个关键事件(以及为什么不能只看 10)
| 事件 |
含义 |
与内存采集的对应 |
| ID 9 RawAccessRead |
通过 \\.\ 句柄直接读卷 |
全量物理内存采集的直接特征(绕过文件系统) |
| ID 10 ProcessAccess |
打开另一个进程,带 GrantedAccess 掩码 |
单进程转储(如只转储 LSASS)的特征 |
| ID 11 FileCreate |
创建/覆盖文件 |
镜像落盘路径的直接证据 |
| ID 1 ProcessCreate |
进程创建 |
采集工具自身被启动,含完整命令行 |
| ID 6 DriverLoad |
驱动加载 |
采集用的驱动 |
⚠️ 最常见的误判:拿事件 10 当内存采集的主要证据。事件 10 反映的是进程内存访问,而全量物理内存读取走的是磁盘设备路径(事件 9)。事件 10 更常出现在单进程转储场景。两者都要查,且事件 9 才是全量采集的主证据。
事件 9 本身也不足以定性:读 \\.\PhysicalDrive0 也可能是磁盘取证工具或恶意软件在读磁盘。判定要靠组合证据:读到物理设备 + 产生了与内存大小相当的文件 = 内存采集。
字段位置不能混:\\.\PhysicalDrive0 出现在事件 9 的 Device 字段;事件 11 的 TargetFilename 只能是普通文件路径(如 E:\evidence\DigiForensics-mem.raw),不可能是物理设备路径。
GrantedAccess 常见掩码(事件 10):0x1010 = PROCESS_VM_READ + PROCESS_QUERY_LIMITED_INFORMATION(凭据转储的典型)。
0x1410 含 PROCESS_VM_OPERATION(改内存)。
0x1f0fff 含 PROCESS_VM_WRITE(注入)。
0x1fffff 全部权限。
这一串掩码值要背下来。事件 10 一旦筛出来,第一眼就该看 GrantedAccess——它直接告诉你对方想读还是要写。
2.4 部署 Sysmon(必须在事发前完成)
这是本篇最重要的实务建议:如果目标环境没有部署 Sysmon,事后无法补做。
一个极端情况值得先想清楚:如果目标机从装机起就没开过 Sysmon,你手上一份采集完的镜像,日志侧那一层证据永远是空的。方案设计阶段决定的事,报告阶段补不回来。
<!-- 最小化配置:只启用内存采集相关事件,降低噪声 -->
<Sysmon version="4">
<EventFiltering>
<ProcessAccess onmatch="include">
<GrantedAccess>0x1010</GrantedAccess> <!-- VM_READ -->
<TargetImage>C:\Windows\System32\lsass.exe</TargetImage>
</ProcessAccess>
<RawAccessRead onmatch="include" />
</EventFiltering>
</Sysmon>
# 安装(需管理员) / 卸载
Sysmon64.exe -accepteula -i config.xml
Sysmon64.exe -u
部署 Sysmon 本身会改变系统状态,应在取证方案中提前设计。若因合规要求不能安装 Sysmon,应在报告中明确写出"该机未部署 Sysmon,采集痕迹无法从日志侧验证"作为证据链局限。
2.5 隐私与最小化原则
内存镜像可能包含所有登录用户的明文口令、Token、会话票据、聊天记录、邮件正文、未保存文档,以及与案件无关的第三方个人信息——敏感度远高于磁盘镜像。
四个层次的最小化做法:
| 层次 |
做法 |
| 采集时 |
只在授权范围内采集;能不采全量就不采全量(单进程转储 > 全量镜像) |
| 分析时 |
只提取与案件相关的工件;不"顺便"翻看无关内容 |
| 报告时 |
敏感内容脱敏或不展示;凭据只说"存在"不贴明文(见第 07 篇) |
| 归档时 |
镜像按最高密级保管;分析副本用完销毁;记录销毁动作 |
一个实用的判断标准:如果一个证据细节与案件无关,它就不该出现在报告里。 可以分析,但不必记录。
举例:分析中看到某员工与案件无关的私人聊天记录——它不构成证据,不应写入报告。
2.6 结论必须交叉验证
| 内存发现 |
交叉验证来源 |
| 外部 IP 连接 |
防火墙日志、代理日志、NetFlow、威胁情报 |
| 域名 |
浏览器历史、DNS 缓存日志、Sysmon 事件 22(DNS Query) |
| 文件路径 |
$MFT、$UsnJrnl、$LogFile、Prefetch、ShimCache |
| 程序执行 |
Prefetch、ShimCache、UserAssist、Amcache、事件日志 4688 |
| 时间点 |
事件日志时间戳(比内存精度高得多) |
| 进程父子关系 |
事件日志 4688、Sysmon 事件 1 |
| 凭据 |
认证日志 4624/4625、域控日志 |
内存的时序精度低于磁盘。这一点反直觉,但很重要。
磁盘上的 FILETIME 精确到 100 纳秒且是固定值;内存里的时间受采集耗时影响,可能偏差数分钟。
所以"谁先执行、谁后执行"这类因果排序,应该以事件日志为准,内存用于确认"确实发生过"。
这条结论的上游是易失性顺序与采集时机窗口,采集前就该据此判断值不值得冒险采,回看见第 01 篇。
三、操作步骤
3.1 第 1 步:采集记录模板(现场填写)
【内存采集记录】
1. 目标机识别:设备序列号/资产编号、主机名、OS 与 Build、
物理内存大小、磁盘序列号、MAC 地址
2. 采集信息
采集人: 见证人: 采集地点:
采集开始(含时区): 采集结束(含时区): 采集耗时:
采集方式(现场/远程/网络推送):
采集期间保持开机:是/否 采集期间有用户活动:是/否
3. 工具信息
工具名称与版本: 工具文件 SHA-256: 工具来源:
是否为目标机原装:是/否
4. 状态变更记录
是否加载驱动:(驱动名与路径) 是否加载内核模块:
是否修改 SELinux:(变更前后状态) 其他状态变更:
5. 产物
镜像文件名:DigiForensics-mem.raw
镜像大小(字节): 镜像 SHA-256: 哈希计算时间: 存储介质编号:
这份记录本身就是证据。它比事后任何描述都可靠,因为它是在现场、即时填写的。空白项也要保留并注明"不适用",不能事后补写。
3.2 第 2 步:Sysmon 事件查询
# 前提:已从目标机提取 Sysmon 日志
# 路径:C:\Windows\System32\winevt\Logs\Microsoft-Windows-Sysmon\Operational.evtx
evtx_dump.py /evidence/logs/Sysmon.evtx | grep -A 20 'EventID.*9' \
| grep -iE 'UtcTime|Image|Device' # 事件 9:Device = \\.\PhysicalDrive0
evtx_dump.py /evidence/logs/Sysmon.evtx | grep -A 20 'EventID.*11' \
| grep -iE 'UtcTime|Image|TargetFilename' # 事件 11:TargetFilename = 普通文件路径
evtx_dump.py /evidence/logs/Sysmon.evtx | grep -A 25 'EventID.*1' \
| grep -iE 'UtcTime|Image|CommandLine' # 事件 1:采集工具进程与命令行
evtx_dump.py /evidence/logs/Sysmon.evtx | grep -A 25 'EventID.*10' \
| grep -iE 'UtcTime|SourceImage|TargetImage|GrantedAccess' # 事件 10:单进程内存访问
# 把事件时间与镜像文件创建时间对齐(BSD 与 GNU stat 均可)
stat -c '%w' DigiForensics-mem.raw 2>/dev/null \
|| stat -f '%SB' -t '%Y-%m-%d %H:%M:%S' DigiForensics-mem.raw
# → 2026-03-14 10:23:45 +0800,与事件 11 的 02:23:45 UTC 一致 → 互相印证
3.3 第 3 步:无 Sysmon 时的替代路径