NTFS研究

摘要:点击标题阅读全文…

Run List

Run List概述

想象一下,你的硬盘是一本非常非常厚的书,而你想要保存的文件是一篇很长的文章。

  • 如果文章很短,比如只有一两句话,你可以直接把这句话写在书的目录页上。在 NTFS 中,这叫做“驻留文件”(Resident File),文件数据直接存储在主文件表(MFT)的记录里。
  • 但如果文章很长,目录页上写不下,你就得把文章写在书的正文页里。你可能会从第100页开始写,写满3页。但可能第103页已经被别的文章占了,所以你只能跳到第250页,再继续写5页。
    现在,你需要在目录里记录下这篇文章到底被写在了哪些页码上,否则就找不到了。这个记录信息,在 NTFS 文件系统中,就叫做“运行列表”(Run List)。

Run List本质上是一组指令,它告诉操作系统如何完整地找到一个大文件的每一个部分,相当于一个指针集,该集包含了若干个指向文件各个碎片的“指针”

什么是运行 (Run)?

一次“运行”(Run)指的是硬盘上一段连续的数据块(簇),这些数据块都属于同一个文件。在我们书本的比喻里,从第100页开始连续写了3页,这“连续的3页”就是一次“运行”。

每一条“运行”指令都包含两个核心信息:

  1. 运行长度 (Run Length): 这部分数据有多长?(例如:连续的3个数据块)
  2. 运行偏移 (Run Offset): 从哪里开始找这部分数据?(例如:从上一个数据块结束的位置向前跳20个数据块)

实例

这里用的是24年美亚个人赛的介质取证:

[[2024美亚个人赛#参考David_USB_8GB.e01,已删除的文件的运行列表(Run List)的运行偏移量(Run Offset)数量是多少?]]

删除的文件是bitlocker_key.jpg,其用MFT Browser

可以看到,bitlocker_key.jpg的MFT的Record部分分为了三个部分,即Header、Attributes、Record Slack三个部分

1. 记录头 (Header / FILE Record Header)

记录头是每个 MFT 记录的起始部分,大小固定(通常为 48 或 56 字节),包含了关于这个记录本身的基础管理信息。 它就像是文件的“身份证”和“状态卡”。

主要记录的数据包括:

  • 签名 (Signature): 通常是 “FILE” 字符串,用来标识这是一个有效的 MFT 文件记录。如果记录损坏,这里可能会被标记为 “BAAD”。

  • 更新序列 (Update Sequence): 一种用于校验 MFT 记录在写入磁盘时是否发生错误的机制。

  • 记录状态标志 (Flags): 用来表明该记录的状态,例如,这个记录是否正在被使用(文件存在),还是已经被删除(可供重用),以及这个记录描述的是一个文件还是一个目录。

  • 第一个属性的偏移量: 指明了紧随其后的“属性”区域从哪个字节开始。

  • 记录的实际使用大小和分配大小: 显示该 MFT 记录当前已经使用了多少字节,以及总共分配了多少字节(通常是1024字节)。

  • 文件引用 (File Reference): 指向基础文件记录的指针。对于非基础记录(扩展记录)来说,这个字段会指向它的主记录。

2. 属性 (Attributes)

这是 MFT 记录的核心和最主要的部分。在 NTFS 中,“一切皆为属性”。文件的名称、大小、时间戳,甚至文件的数据本身,都被视为文件的属性进行存储。

这些属性一个接一个地排列在记录头之后,直到记录的末尾或者遇到一个特殊的结束标记。每个属性也都有自己的头部,用来定义属性的类型、大小、名称等信息。

一些最常见和重要的属性包括:

  • $STANDARD_INFORMATION (标准信息): 存储了文件的基本元数据,如创建时间、修改时间、访问时间、文件所有者以及 DOS 文件属性(如只读、隐藏、系统文件等)。

  • $FILE_NAME (文件名): 存储了文件的名称(支持长文件名和 8.3 格式的短文件名)、父目录的引用以及文件的大小。一个文件可以有多个文件名属性(例如,硬链接)。

  • $DATA (数据): 这是最重要的属性之一,它包含了文件实际的数据内容。

    • 对于非常小的文件,数据可以直接存储在 MFT 记录的这个属性区域内,这被称为“驻留数据”(Resident Data)。

    • 对于较大的文件,这个属性区域内则会存储一个“运行列表”(Run List),指向文件数据在硬盘其他位置的存储块。这被称为“非驻留数据”(Non-resident Data)。

  • $ATTRIBUTE_LIST (属性列表): 当一个文件的所有属性信息多到无法在一个 MFT 记录中存下时,系统会分配额外的 MFT 记录(称为扩展记录)。这个属性会列出所有属性的位置,包括那些存储在其他扩展记录中的属性。[

  • $SECURITY_DESCRIPTOR (安全描述符): 存储了与文件访问控制相关的信息,比如谁可以读取、写入或执行这个文件(访问控制列表 ACLs)。

  • 其他属性: 还包括 $INDEX_ROOT 和 $INDEX_ALLOCATION (用于目录文件组织其下的文件列表)、$BITMAP (用于索引)、$OBJECT_ID (文件的唯一标识符) 等等。

3. MFT 记录闲置空间 (Record Slack / MFT Slack Space)

MFT 记录通常有固定的大小(如 1024 字节)。然而,一个文件的记录头和所有属性加起来,往往用不满这 1024 字节。从最后一个属性的末尾到整个记录结束的这部分未使用的空间,就称为 MFT 记录闲置空间

对于一段Data Run

下面我们来详细解析您提供的这个 DataRun:22 B5 0D 3F 4C 00 00 00

在 NTFS 中,DataRun 是一连串的指令,而 0x00 字节是明确的结束标记。分析时,我们从左到右读取,直到遇到 0x00 为止。

让我们来分解 22 B5 0D 3F 4C 这一部分:

1. 头部字节 (Header Byte): 0x22

这个字节是第一条(也是唯一一条)“运行”指令的头部。我们需要把它拆成高4位和低4位来看:

  • 低4位 (Low Nibble): 2

    • 这表示描述“运行长度 (Run Length)”的字段占 2个字节
  • 高4位 (High Nibble): 2

    • 这表示描述“运行偏移 (Run Offset)”的字段占 2个字节

2. 运行长度 (Run Length): B5 0D

根据头部字节的指示,我们向后读取 2个字节 作为运行长度。NTFS 使用小端序(Little-Endian),所以我们需要倒过来看:

  • B5 0D 应该被解释为 0x0DB5。

  • 将十六进制 0DB5 转换为十进制,得到 3509

  • 含义: 这个文件的数据部分占用了 3509 个连续的簇 (Cluster)

3. 运行偏移 (Run Offset): 3F 4C

接着,我们继续向后读取 2个字节 作为运行偏移。同样使用小端序:

  • 3F 4C 应该被解释为 0x4C3F。

  • 将十六进制 4C3F 转换为十进制,得到 19519

  • 含义: 由于这是文件数据的第一个(也是唯一一个)“运行”,这个偏移量是绝对的,它指的是文件数据从该分区的第 19519 个簇开始存储。

4. 结束标记 (Terminator): 0x00

在 4C 之后,我们读到了 0x00。

  • 含义: 这标志着运行列表 (Run List) 到此结束。后面没有更多的“运行”指令了。

结论

综合以上分析,这个 DataRun 告诉我们:

这个文件的数据存储在从磁盘的第 19519 个簇开始,连续占用了 3509 个簇。由于只有这一条指令,所以文件是完整、连续存储的,没有产生任何碎片。

对比一个分段文件的例子

如果一个文件是分段的,它的 DataRun 可能会是这样的:

21 64 F8 03 11 C8 00

  • 第一段: 21 64 F8 03

    • 21 -> 长度占1字节,偏移占2字节。

    • 64 -> 长度为 0x64 = 100个簇。

    • F8 03 -> 0x03F8 = 从第 1016 个簇开始。

  • 第二段: 11 C8

    • 11 -> 长度占1字节,偏移占1字节。

    • C8 -> 长度为 0xC8 = 200个簇。

    • 00 -> 偏移为 0x00 = 紧接着上一段。 (这是一个简化的例子,实际偏移会更复杂)