$MFT 是 NTFS 用来跟踪卷上每个文件与目录的索引。它也是你能从一台 Windows 机器上拉出的单一信息量最大的工件。卷上所有其他文件,包括这张表自己,都在其中至少占一条记录。取证工具能回答关于 Windows 不再承认存在的文件的诸多难题,正是因为这些记录在删除后依然存活。威胁狩猎人通过它追踪持久化,因为攻击者在 NTFS 上移动文件就不可能不写它。如果你一辈子只学一个 Windows 工件的字节级细节,就学它。
这是我开始时希望拥有的参考。它涵盖这张表是什么、一条记录长什么样、一条记录可以承载哪些属性、开头那些保留系统文件意味着什么、令人闻风丧胆的"Windows cannot recover the master file table"错误到底说明了什么、以及如何亲自读这张表。
Master File Table 是什么
$MFT 是一个文件。一个单一的文件。它位于每个 NTFS 卷起始处附近的已知偏移。分区偏移 0 的引导扇区(BIOS Parameter Block 的 512 字节)中有一个名为 MftStartLcn 的字段,指向 $MFT 的第一个簇。读取那 1,024 字节,你得到的就是记录 0,即这张表的自我描述。
表的每一行恰好 1,024 字节,描述一个文件或一个目录。每条记录包含名称、时间戳、DOS 风格 flags、安全描述符引用,以及要么是数据本身(小文件),要么是数据所在磁盘簇的列表(大文件),全部以一串带类型的属性编码。
NTFS 是 1993 年随 Windows NT 3.1 引入的,自 Windows XP 起就是所有固定磁盘上的默认 Windows 文件系统。它取代了 FAT。FAT 保留一张小的分配表,并把文件名放在目录条目中。NTFS 把关于每个文件的几乎所有元数据放进一张结构化的表,即 $MFT。这一设计有两个值得牢记的后果:
- 所有元数据集中在一处。 一次到
$MFT的寻道就能枚举出卷上所有文件。这正是取证工具、杀毒引擎、索引服务与备份软件都要读它的原因。也正因为如此,$MFT损坏比 FAT 损坏要严重得多。 - 已删除文件把元数据留在原处。 NTFS 删除一个文件时,会清掉记录头中的一个 bit,并把该文件的簇在
$Bitmap中标记为空闲。记录的其余部分(名称、时间戳,常常还有数据)原封不动,直到该记录槽位被复用。见 删除后还剩什么。
缩写 MFT 是 Master File Table 的缩写。在磁盘上写作 $MFT 是因为在 NTFS 中,美元符号是元数据文件名的前缀。
$MFT 在磁盘上的布局
NTFS 格式化卷时会在分区起始附近预留一块称为 MFT 区 的区域。表中前 16 条记录留给 NTFS 元数据文件(下文描述);记录 0 是表自己的条目,指回它自己的簇。
每当 $MFT 需要更多记录,它便扩展到预留区中以增长。如果在该区被耗尽之前卷已满,Windows 会缩小该区为用户数据腾出空间——这是 $MFT 在年久的文件系统上常常严重碎片化的原因。表从不缩小。一旦创建了一个槽,它就留在 $MFT 中;删除只清掉 in-use 标志。这就是为何在轻度使用的卷上,远高于当前高水位的旧已删除记录能存留数年。
前几条记录的备份存在 $MFTMirr 中,位于卷的中间位置。如果 $MFT 本身不可读,NTFS 会用 $MFTMirr 引导恢复。见 $MFTMirr 与 NTFS 何时使用它。
FILE 记录解剖
每条 MFT 记录以四字节 ASCII 签名 FILE(46 49 4C 45)开头。损坏的记录会写成 BAAD,那是 chkdsk 修复失败时留下的墓碑。签名之后是 56 字节的头,然后是 fixup 数组,再然后是以 0xFFFFFFFF 结尾的一串带类型的属性。
头携带你最常用到的字段:
- 签名。 有效记录为
FILE,无法修复时为BAAD。 - Update sequence(fixup)数组。 防 torn write 的小技巧。记录内每个 512 字节块的最后两个字节被替换为 USN;原值被存入此数组。读取时,NTFS 校验 USN 并复原原始字节。
$LogFile序列号。 指向$LogFile,用于崩溃恢复。- 序列号。 每次该记录槽被复用时递增。与记录号一起构成 64 位的 文件引用,唯一标识一个文件的某次具体存在。
- 硬链接计数。 指向该记录的
$FILE_NAME属性数量。 - Flags。 Bit 0 为
IN_USE(清掉表示已删除)。Bit 1 为DIRECTORY。 - Base file record 引用。 在属于表中他处某基础记录的扩展记录上非零。
- Used 与 allocated 大小。 Used 是这条记录实际占用 1,024 字节槽位的大小;allocated 是槽位大小(标准卷上恒为 1,024)。
关于头和属性流的字节级讲解,见 MFT 记录内部。
头之后是属性。每个属性有自己短小的头(类型、长度、常驻/非常驻 flag、可选名称),随后是数据。没有固定顺序,但实践中通常 $STANDARD_INFORMATION 在前,$DATA 在后。如果一条记录的空间不够(碎片太多、ADS 过多、名称异常长),就会长出 $ATTRIBUTE_LIST 属性指向表中他处的一条或多条扩展记录。解析器必须沿链重建完整文件。
$MFT 中保存的文件属性
下面是带 hex 码的 NTFS 属性类型规范列表:
| 类型 | Hex | 作用 |
|------|-----|---------|
| $STANDARD_INFORMATION | 0x10 | 四个时间戳(created、modified、accessed、MFT-modified)、DOS flags、owner ID、安全 ID、USN 指针。 |
| $ATTRIBUTE_LIST | 0x20 | 当一个文件的属性溢出一条记录时,指向扩展记录的指针。 |
| $FILE_NAME | 0x30 | 一个文件名、父目录引用、分配大小与真实大小,以及第二组四个时间戳。一个文件可能有多条(每个硬链接一条,加上启用了 8.3 的卷上的 8.3 短名)。 |
| $OBJECT_ID | 0x40 | Distributed Link Tracking 服务使用的 128 位对象标识符。 |
| $SECURITY_DESCRIPTOR | 0x50 | 旧式按文件 ACL。现代 NTFS 把 ACL 集中存放在 $Secure,并在 $STANDARD_INFORMATION 中按 ID 引用。 |
| $VOLUME_NAME | 0x60 | 仅出现在记录 3($Volume)上。持有卷标。 |
| $VOLUME_INFORMATION | 0x70 | NTFS 版本、dirty flag。 |
| $DATA | 0x80 | 文件内容。极小文件为常驻;否则为非常驻(一份簇 runlist)。一个文件可承载多个 $DATA;无名的是主数据流,命名的是 备用数据流。 |
| $INDEX_ROOT | 0x90 | B+ 树的根。用于目录($I30)、reparse-point 索引及其它索引结构。 |
| $INDEX_ALLOCATION | 0xA0 | 大型索引的非常驻续段。 |
| $BITMAP | 0xB0 | $MFT 自身或大目录的分配位图。 |
| $REPARSE_POINT | 0xC0 | 符号链接、junction、挂载点、OneDrive 占位符、重复数据删除的桩。 |
| $EA_INFORMATION / $EA | 0xD0 / 0xE0 | OS/2 时代的扩展属性。在现代 Windows 上很少见。WSL1 用它存 POSIX 元数据,那是唯一值得在意的场景。 |
| $LOGGED_UTILITY_STREAM | 0x100 | EFS 加密元数据($EFS)、TxF 事务数据。 |
一条记录总是至少携带 $STANDARD_INFORMATION、一条 $FILE_NAME、一条 $DATA。其它都是可选并按需出现。
常驻 vs 非常驻数据
实际卷上大多数 $DATA 属性是 非常驻:属性头携带一份紧凑的 cluster run 列表(起始 LCN 加长度,反复出现),文件字节存在磁盘别处。属性头本身很小。
如果文件足够小(通常考虑其它属性后小于约 700 字节),NTFS 就把字节内联存到记录里。这就是 常驻数据,也是取证工作中最有用的工件之一:几周前被删除的小文本文件的内容,可能仍逐字节存在于一条未分配的 $MFT 记录里。关于大小阈值与要找什么,见 resident data。
$MFT 前十六条记录里的 NTFS 元数据文件
$MFT 的前 16 条记录留给 NTFS 自身的簿记。它们以 $ 开头,免得与用户文件名冲突。值得知道的:
| Rec # | 文件 | 含义 |
|-------|------|------------|
| 0 | $MFT | 表本身。其 $DATA runlist 指向它自己的簇。 |
| 1 | $MFTMirr | $MFT 前几条记录的部分备份。 |
| 2 | $LogFile | 用于在崩溃后撤销或重做未完成操作的事务日志。 |
| 3 | $Volume | 卷标和 dirty flag。 |
| 4 | $AttrDef | 合法属性类型的 schema。 |
| 5 | . | 根目录。 |
| 6 | $Bitmap | 卷上每个簇一个 bit;跟踪分配状态。 |
| 7 | $Boot | 引导扇区的副本。 |
| 8 | $BadClus | 稀疏文件,其 run 指向文件系统标记为坏的所有簇。 |
| 9 | $Secure | 安全描述符的集中存储。 |
| 10 | $UpCase | 用于大小写不敏感名称比较的 Unicode 大写映射表。 |
| 11 | $Extend | 包含较新系统文件的目录:$ObjId、$Quota、$Reparse、$UsnJrnl、$RmMetadata。 |
变更日志 $UsnJrnl(位于 $Extend 下)在取证中尤为有用;它记录卷上每一次元数据变化,并在时间线重建上与 $MFT 互补。见 把 journal 与 file table 配对。
当 $MFT 出问题
错误 "Windows cannot recover master file table. CHKDSK aborted" 出现在 chkdsk 既无法读取 $MFT、也无法回退到 $MFTMirr 时。到这一步,NTFS 已经尝试过其内建自修复并失败了。我见过的根本原因,按实际出现频率排序:
- 物理介质故障。 MFT 区的坏扇区在读取时返回乱码。SMART 数据通常能佐证。用
ddrescue而不是dd给磁盘做镜像,并在镜像上工作。 - 元数据密集操作过程中的突然断电。 通常事务日志会回滚,但损坏的
$LogFile会让回滚失败。 - 驱动或过滤器层的损坏。 行为不端的磁盘加密栈、文件系统过滤器,或有 bug 的存储驱动可能写出不一致的记录。多个互相打架的安全代理的主机上常见。
- 恶意覆写。 擦写器和少数勒索家族(尤其是 Petya 与早期 NotPetya 浪潮)会故意涂写
$MFT让卷无法挂载。见 MFT 中的勒索软件模式。
取证上稳妥的响应:
- 立刻停止对该卷的写入。 每一次后续写入都会降低恢复机会。
- 用 FTK Imager、
dd或ddrescue对磁盘做镜像 到一个可信的目的地。校验哈希。 - 在镜像而不是原盘上工作。试
testdisk、R-Studio,或一次手动解析,通过对原始卷做签名扫描找出FILE记录。即便磁盘上指向$MFT的指针没了,记录本身通常仍可辨认。 - 如果目标是让卷重新上线而不是恢复数据,那时再对镜像运行
chkdsk /f。
可写卷上的 chkdsk /b 可以清掉坏簇标记,但也可能丢掉它读不懂的记录。仅在已有镜像、且确认可用性优先于取证忠实度之后,才对原盘运行它。
如何读 $MFT
你有三个现实可行的选项:
- MFTECmd(Eric Zimmerman)。一个 Windows CLI,输出大多数时间线工具期待的、与 bodyfile 相近的 CSV 布局。事件响应者事实上的标准。
omerbenamram/mft。一个 Rust crate 与 CLI(mft_dump)。本站使用的解析器,当你想脚本化分析或把它嵌入更大的流水线时很有用。- 本站的浏览器解析器。 把
$MFT拖到 首页,它会运行同一个被编译为 WebAssembly 的 Rust 解析器,完全在你的浏览器中。什么都不会被上传。
具体的优劣对比见 MFT 解析工具。在解析后 $MFT 上的实用工作流见 构建时间线、已删除文件 与 提取 $MFT。
常见问题
MFT 是什么的缩写?
MFT 是 Master File Table 的缩写。在磁盘上写作 $MFT,因为 NTFS 用美元符号作为元数据文件名前缀。
Master File Table 用来做什么?
它是 NTFS 用来定位卷上每个文件与目录的索引。每条条目存储文件名、时间戳、安全信息、属性以及其数据在磁盘上的位置。
Master File Table 保存哪些文件属性?
最起码每条记录都携带 $STANDARD_INFORMATION(时间戳、DOS flags)、$FILE_NAME(名称和第二组时间戳)以及 $DATA(文件内容或指向它的指针)。根据文件不同,记录还可能携带 $ATTRIBUTE_LIST、$OBJECT_ID、$SECURITY_DESCRIPTOR、$INDEX_ROOT、$INDEX_ALLOCATION、$BITMAP、$REPARSE_POINT、$EA 与 $LOGGED_UTILITY_STREAM。完整参考见上文属性表。
Master File Table 有多大?
每条记录 1,024 字节。该表默认预留卷的约 12.5%(即 MFT 区),但只占用实际需要的空间。一个含一百万文件的卷大概有约 1 GB 的 $MFT。
$MFT 与 $MFTMirr 一样吗?
不一样。$MFTMirr 是 $MFT 前几条记录的部分备份,放在磁盘别处,以便在主表头损坏时 NTFS 能启动恢复。
怎么修复损坏的 master file table?
先做磁盘镜像。然后要么对镜像运行 chkdsk /f(快,可能丢记录),要么用能扫描 FILE 签名并从原始簇重组表的恢复工具(慢,但保留更多证据)。在做镜像之前,永远不要在原卷上跑 chkdsk。
我能在 Linux 或 macOS 上读 $MFT 吗?
可以。$MFT 只是个文件。任何接受原始 $MFT 转储的解析器在任何操作系统上都能工作:omerbenamram/mft、analyzeMFT、本站的浏览器工具。只有从在线挂载的卷上 抽取 文件时才需要 Windows。
延伸阅读
- Microsoft,Master File Table。官方、精炼的参考。
- Brian Carrier,File System Forensic Analysis。关于 NTFS 布局与恢复的最佳单本书。
- linux-ntfs 项目的 NTFS Documentation。每个属性的字段偏移,凭多年逆向工程艰苦得来。