我第一次用 $MFT 构建 Windows 时间线时,每条记录输出八行,按时间戳排序,在 Excel 里打开,感觉自己重建了现实。其实并没有。我只是得到了一份排过序的 NTFS 元数据变更日志。那是有用的东西。但它本身并不是一次调查。
这篇文章是关于 MFT 时间线,我希望在交付第一份报告之前有人能告诉我的内容。
MFT 时间线到底是什么
每条在用和已删除的 MFT 记录都携带八个可可靠提取的时间戳:四个在 $STANDARD_INFORMATION(SI),四个在 $FILE_NAME(FN)。created、modified、accessed、MFT-modified,两个属性各一份。遍历表,对每个非空时间戳输出一行,按时间排序,你就得到了一份 NTFS 何时写入了具体记录的具体字节的时间线。这是下限。
上限是一条 super timeline,把 MFT 与 USN journal、$LogFile、Prefetch、Sysmon、Shimcache、Amcache、注册表配置单元、浏览器历史 与 SRUM 融合在一起。MFT 是脊柱,因为它最稠密,攻击者最难完整伪造。其他一切都对齐到它上面。
我实际使用的版式
不要把 mactime 当作目标格式。把它当作中间格式。在法庭上能立住的形式是:每个事件一行,并附上可辩护的出处:
timestamp_utc | source | attribute | mft_record | seq | path | event
2026-05-15T10:23:01.123Z | MFT | SI | 12345 | 3 | /Users/alice/notes.txt | created
2026-05-15T10:23:01.123Z | MFT | FN | 12345 | 3 | /Users/alice/notes.txt | name_created
2026-05-15T10:24:18.456Z | MFT | SI | 12345 | 3 | /Users/alice/notes.txt | modified
2026-05-15T10:24:18.501Z | USN | - | 12345 | 3 | /Users/alice/notes.txt | DATA_OVERWRITE | CLOSE
source 和记录号/序列号的组合,是分析师常常省略而事后又悔不当初的列。序列号告诉你共享同一个记录号的两个事件,究竟指的是同一个文件的同一个实体,还是指的是一个已删除前任,而它的槽位被重新使用了。没有它,时间线会悄无声息地把两者混在一起。
MFTECmd 的 CSV 输出是最贴近这种版式的现成方案。把它的行通过一个薄薄的脚本,加上 source=MFT 并按时间戳每个一行输出,你就有了可工作的语料。
按"撒谎程度"排序的时间戳
SI 几乎在 Windows 对文件做的每一个操作上都会动。读操作会更新 accessed,如果 NTFS 想(Windows 7+ 出于性能考虑默认不更新 accessed,除非 fsutil behavior set disablelastaccess 0 被设置)。写操作更新 modified 和 MFT-modified。重命名更新 MFT-modified。用 SetFileTime 做的 timestomping 会更新攻击者指向的那一个。
FN 在重命名、创建硬链接以及文件首次创建时更新。此后,FN 基本上不再动。Windows 对 FN 更新的粒度也更粗;很多文件合法地以 .0000000 结尾,因为 FN 是在创建时设置的,之后再没被触碰。
实际上:
- FN created 是 MFT 单独所能提供的"该文件第一次出现在该目录"的最可靠信号。
- SI created 是最容易伪造的。把它当作线索,而非事实。
- SI modified 是主力。它与对应的 USN
DATA_OVERWRITE结合,告诉你文件的字节在那一刻确实发生了变化。 - SI accessed 在 Windows 7+ 上默认关闭。如果你看到它在变,要么是管理员重新打开了它,要么是 Server SKU 卷,要么是某些东西在挂载卷并遍历文件(备份软件、AV 扫描、EDR 自省)。
亚秒级粒度是关键。NTFS 用 100 纳秒粒度存储时间戳。原生 Windows 操作会在低位留下噪声。SetMACE 与著名的 timestomp.exe 之类的 timestomping 工具会四舍五入到秒。一整列整齐排列的 .0000000 后缀就是一种指纹。
与 USN journal 合并
MFT 给你看的是现在的状态以及一份冻结的元数据历史。USN journal 给你看动词。把两者结合,时间线里的一行就变成了一个完整的句子。
我的合并方法是:单独解析 $UsnJrnl:$J,每条记录输出一行带 source=USN,然后把合并后的流按时间戳排序。USN 的 reason 码(FILE_CREATE、DATA_OVERWRITE、RENAME_OLD_NAME、RENAME_NEW_NAME、FILE_DELETE、CLOSE)告诉你操作;MFT 提供结果状态。一个紧跟在 RENAME_NEW_NAME 之前、同一个 USN 上的 RENAME_OLD_NAME 揭示了重命名。没有 journal,两份快照之间的 MFT diff 也许能告诉你文件移动了;但无法告诉你顺序。
一个陷阱:同一事件的 USN 时间戳与 MFT SI 时间戳会有毫秒级漂移。不要过度依赖严格对齐。按秒排序,用 USN reason 码作为决胜。
什么会毁掉 MFT 时间线
乱序采集。 如果你在在线系统上间隔十分钟分别采集 MFT 与 USN,journal 在这段时间内一直在写。请把它们配对在同一时间点采集,最理想是从你自己触发的 VSS 快照里。
忘了 fixup 数组。 任何值得使用的解析器都会处理,但如果你自己写(除非学习目的,请别这么做),按原始 1,024 字节块读取会让每条记录在偏移 510 和 1022 处出现垃圾。先应用 fixup。
跨序列边界混淆记录号。 记录 12345 序列 3 与记录 12345 序列 5 不是同一个文件。槽位被重用了。如果你的时间线只按记录号分组,你会把已删除文件和接管槽位的新文件混在一起。
信任系统二进制文件的 SI。 Windows Update 会更新被打补丁文件的 SI。C:\Windows\System32\svchost.exe 上一个修改过的 SI 几乎总是一次 CBS 安装,而非入侵。在下结论之前,与 setupapi.dev.log 和 CBS.log 交叉比对。
把时间线当作结论。 它是分析的输入。你仍然需要在主机、用户与案件的上下文中理解每个事件意味着什么。
一个能立得住的工作流程
- 采集
$MFT、$UsnJrnl:$J、$LogFile,以及任何可用的 VSS 快照中的同一组文件。立即对所有文件做哈希(SHA-256)。 - 用 MFTECmd 或
mft_dump加上一个 USN 解析器解析 MFT 与 USN。确认解析器无警告运行。 - 输出规范化的行:MFT 每个时间戳一行,USN 每条记录一行,并显式带有
source、mft_record、seq与path。 - 按时间戳排序合成一条流。先不要去重;近似重复中携带着信号。
- 聚焦到关注的时段。拉出可疑事件前后各两小时的窗口。
- 把其余东西分层叠加:Prefetch、Amcache、Sysmon 1/11/15、LNK 与 跳转列表 工件、回收站
$I记录、用于网络与进程资源使用的 SRUM。 - 找不可能的顺序。FN created 之前的 SI created。MFT 中没有对应 SI modified 变化的
DATA_OVERWRITEUSN reason。同一序列下同一记录号上两次FILE_CREATE(解析器有问题)。
最后这一步是 MFT 时间线最有价值之处。它暴露出不可能性。每一个不可能性要么是解析器 bug,要么是攻击者的工件,二者都值得追下去。
MFT 无法告诉你的事情
它无法告诉你是哪个进程触碰了文件。它无法告诉你是哪个用户。它无法告诉你被删除并部分覆盖的、超过几百字节的文件里有什么。要回答这些,你需要周边工件:带命令行审计的 4688(或 Sysmon 1)、安全日志中的 logon ID 链,运气好的话还有一份还抓到该进程驻留的 内存转储。
MFT 是脊柱。其他工件是肌肉。两者缺一不可。
延伸阅读
- Eric Zimmerman,MFTECmd。规范的 CSV 版式,以及大多数 IR 团队据以标准化的解析器。
- SANS,Windows Forensic Analysis Poster。把 MFT 事件正确地折叠到更宽的时间线中。
- Harlan Carvey,Investigating Windows Systems。关于时间线构建的章节至今仍是实用参考。