翻译《文件系统取证分析》第13章

第13章 NTFS 数据结构

这是关于NTFS的第三章亦是最后一章,这里我们将分析它的数据结构。前两章写了它的基本概念和怎么去解析它。对许多人来说,目前为止所涉及的知识已经足够了,但我们中的其他人想知道更加多的知识。本章的组织方式是我们先了解数据结构的基础元素,与其他的文件系统章节不同, 本章的编写是为了在章节11“NTFS 概念”和章节12“NTFS 解析”之后阅读。章节的第一部分可以同时于章节11阅读,但是之后的部分必须在完成章节12并明白了各种的属性。在我们开始之前,记得这是非官方发布的NTFS说明书。这里面介绍的数据结构来自 Linux NTFS 组,正如我们将看到,它们与磁盘上存在的内容相匹配。我们可能无论如何不知道其中的一些附加的标记值或者细节。

基本概念

在这一节中,我们研究NTFS数据结构的基本概念。在第一小节,我们将研究使大型数据结构更可靠的设计特性。接下来,我们讨论MFT entry和属性头的数据结构。

修正值

在我们查看任何指定的NTFS数据结构之前,我们需要去讨论一种用于提高可靠性的存储技术。NTFS将修正值合并到长度超过一个扇区的数据结构中。使用修正值,当数据结构写入磁盘时,大型数据结构中每个扇区的最后两个字节将替换为签名值。稍后将通过验证所有相关扇区是否包含一致的签名值来验证数据的完整性。注意这里修正值仅仅用在数据结构并不用在包含文件内容的扇区。

图13.1. 一个多扇区数据结构,具有原数值然后将修正值应用到每个扇区的最后两个字节.

使用修正的数据结构具有标识当前16位签名值的头字段和一个包含原始数值的数组。当数据结构写到磁盘的时候,签名值会加1,每个扇区的最后两个字节将复制到数组,而签名值会写到每个扇区的最后两个字节。读数据结构的时候,操作系统应验证每个扇区的最后两个字节是否等于签名值,并将原始数值从数组中取出来替换到数据。图13.1展示了一个数据结构及其实际值,然后是写入磁盘的版本。在第二个数据结构, 每个扇区的最后两个字节已替换为 0x0001。
修正程序用与检测扇区和数据结构是否损坏,如果多个扇区数据结构中仅仅一个扇区被写入了,这个修正值将不同于签名值,操作系统将知道这个数据坏了。我们剖析样本文件系统时,首先需要替换签名值。

MFT Entries(File Records)

正如在第11章和第12章已经讨论过的,主文件表(MFT)是NTFS的核心,对于每一个文件和目录都有一个entry。MFT entries是一个固定大小并包含一些小字段。至今为止,这些entries的大小为1024字节, 但是这个大小其实是在启动扇区定义的。每一个MFT entry使用修正值,所以在磁盘上版本数据结构每个扇区最后两个字节被替换为修正值。参考前一节一段修正值的说明。MFT entry的数据结构字段如下表 13.1所示。

    表13.1。MFT entry的基本数据结构    
    Byte Range    Description                 Essential
    0–3         Signature ("FILE")             No
    4–5         Offset to fixup array             Yes
    6–7         Number of entries in fixup array     Yes
    8–15         $LogFile Sequence Number (LSN)        No
    16–17         Sequence value                 No
    18–19         Link count                 No
    20–21         Offset to first attribute         Yes
    22–23         Flags (in-use and directory)         Yes
    24–27         Used size of MFT entry             Yes
    28–31         Allocated size of MFT entry         Yes
    32–39         File reference to base record         No
    40–41         Next attribute id             No
    42–1023     Attributes and fixup values         Yes

标准的签名值是"FILE",但如果chkdsk在其中发现错误的话一些entries也可能是"BAAD"。接下来两个字段是修正值,其数组在42个字节之后,偏移值相对entry的开始。
LSN用于文件系统日志,这在第12章“应用程序”分类那部分讨论过。文件系统进行元数据更新时的日志记录,可以更快地修复损坏的文件系统。
无论entry分配与否,序列值都会增加,这是操作系统决定的。变量“link count”显示这个MFT entry包含多少个目录。如果文件创建了硬链接,每一个链接都会令这个数字加1。
我们使用相对于entry开头的偏移值找到文件的第一个属性。所有其他属性都跟随着第一个属性。我们通过属性头中的size字段前进来找到它们。文件末尾标记0xfffffff存在于最后一个属性之后。如果一个文件需要多于一个MFT entry,则其他条目将在MFT entry中举有基 entry的 文件引用(不通顺)。
字段flags仅有两个值。当这个entry可用的时候,0x01位被设置,如果这个entry是目录则0x02位被设置。
让我们继续来看原始MFT entry。要查看这个表,我们将使用 The Sleuth Kit(TSK)的icat来看$MFT文件的$DATA属性,该条目是0。记住,我们可以通过在MFT entry地址后面添加属性类型ID来指定TSK的任何属性。在本例中,$DATA属性的类型为128。

# icat –f ntfs ntfs1.dd 0-128 | xxd
0000000: 4649 4c45 3000 0300 4ba7 6401 0000 0000 FILE0...K.d.....
0000016: 0100 0100 3800 0100 b801 0000 0004 0000 ....8...........
0000032: 0000 0000 0000 0000 0600 0000 0000 0000 ................
0000048: 5800 0000 0000 0000 1000 0000 6000 0000 X...........`...
[REMOVED]
0000496: 3101 b43a 0500 0000 ffff ffff 0000 5800 1..:..........X.
0000512: 0000 0000 0000 0000 0000 0000 0000 0000 ................
[REMOVED]
0001008: 0000 0000 0000 0000 0000 0000 0000 5800 ..............X.

这个输出是小端序的,所以我们需要将它的数字顺序反过来。我们看到"FILE"签名标志,然后第4第5展示修正数组在MFT entry的第48(0x0030)个字节。第6到7个字节展示这个数组举有三个值。第16到17个字节展示MFT entry序号为1,意思是这个entry是第一次使用。第18到19字节展示"link count"是1,于是我们明白它仅仅包含一个名字。第20到21个字节展示第一个属性在偏移56(0x0038)个字节处。
标志值在第22到23个字节处,这个entry在使用中(0x0001)。基础entry值在第32到39处为0,告诉我们这是基entry,并且第40到41字节展示下一个属性ID是6。因此,我们应该期待属性ID是1到5。
修正数组从第48个字节开始。前两个字节是签名值,即0x0058。下两个字节是原始值,用来替换签名值。我们看每个扇区最后两个字节,第510到511和1022到1023个字节处,屏弃恶看到每一个都有0x0058。去处理entry,我们从修正数组中取出0x0000来替换这些值。跟着修正数组的是在字节56处的第一个属性。这个文件属性结尾在第504字节,结尾标记为0xffffffff。此时后面已经没有任何属性了。
如果你想用TSK去查看任何MFT entry,你可以使用属于 icat 的 dd 命令去跳过开头而到正确的定位。你可以设置设置块大小为1024,即每个MFT entry的大小。举个例子,去查看entry 1234 你可以使用如下命令
# icat -f ntfs ntfs1.dd 0 | dd bs=1024 skip=1234 count=1 | xxd

Attribute Header

一个MFT entry用属性来填充,并且每个属性含有相同的数据结构体头,我们现在来研究它。图13.2展示了一个经典的文件数据报和头定位。常驻和非常驻属性有轻微的不同因为非常驻属性需要保存run信息。
    图 13.2. 有不同头位置的典型文件。

前16字节和同类型的属性都一样,包含字段如表 13.2
    表 13.2 属性的前16字节数据结构
    Byte Range     Description             Essential
    0–3         Attribute type identifier     Yes
    4–7         Length of attribute         Yes
    8–8         Non-resident flag         Yes
    9–9         Length of name             Yes
    10–11         Offset to name             Yes
    12–13         Flags                 Yes
    14–15         Attribute identifier         Yes

这些值提供了属性的基本信息,包括类型,大小和名称定位。大小用来找MFT entry下一个属性,如果它是最后一个,那么它的下一个将是0xffffffff。当属性为非常驻,那么non-resident标记就会被设置为1。压缩为(0x0001),加密为(0x4000),散为(0x8000)。属性ID是在当前MFT entry中是唯一的值。偏移相对属性的头。常驻属性的字段如下表13.3
    表 13.3 常驻属性数据结构
    Byte Range    Description             Essential
    0–15         General header (see Table 13.2)    Yes
    16–19         Size of content         Yes
    20–21         Offset to content         Yes

属性内容这些值简单地告诉我们大小和位置(相对于属性的开头),亦可以叫它流。让我们看一个例子。当我们预先切开MFT entry, 我们将看到属性开始于第56个字节。我将在那里获得属性,并重置输出端的偏移,这样就可以更加容易确定属性头偏移。
0000000: 1000 0000 6000 0000 0000 1800 0000 0000 ....`...........
0000016: 4800 0000 1800 0000 305a 7a1f f63b c301 H.......0Zz..;..

$STANDARD_INFORMAITION输出显示属性类型在前4个字节,为16(0x10)。第4到7个字节显示大小为96个字节(0x60)。第8个字节显示这是一个常驻属性(0x00),第9个字节显示它没有名称(0x00)。标志和ID值是0,在第12到第13字节和第14到第15字节。第16到19字节表明属性72(0x48)字节大小。第20到第21字节表明它的位置是在属性头开始的24字节(0x18)。属性中的报告长度为24字节偏移和72字节的属性长度等于总共96字节。
非常驻属性含有不同的数据结构因为它需要描述一个簇run中的任意数值。属性字段在如下的表 13.4。
    表 13.4 非常驻属性数据结构
    Byte Range    Description                         Essential
    0–15         General header (see Table 13.2)             Yes
    16–23         Starting Virtual Cluster Number (VCN) of the runlist     Yes
    24–31         Ending VCN of the runlist                 Yes
    32–33         Offset to the runlist                     Yes
    34–35         Compression unit size                     Yes
    36–39         Unused                             No
    40–47         Allocated size of attribute content             No
    48–55         Actual size of attribute content             Yes
    56–63         Initialized size of attribute content             No

回顾第8章“文件系统分析”,我们给逻辑文件地址定义了一个不同的名称叫VCN。当需要多个MFT entries来描述单个属性时,将使用VCN的开始和结束编号。例如一个$DATA属性非常零碎,并且runs不能放进单一个MFT entry,它将分配第二个MFT entry。第二块entry将包含一个$DATA属性,在第一个条目的结束VCN之后,开始VCN等于。我们将在$ATTRIBUTE_LIST部分看到一个例子。压缩单元大小描述在第11章有描述,仅用于压缩属性。
    图 13.3 第一字节在run显示长度字段为1个字节,偏移字段为两个字节.

数据runlist中的偏移是相对属性的开头。runlist的格式非常有效单有点令人困惑。它的长度可变,但至少为一个字节。数据结构的第一个字节被组织成高4位和低4位(也称为半字节)。4个最低有效位包含runlist长度字段中的字节数,该字段位于头字节之后。4个高位有效位包含run偏移字段的字节数,该字段位于长度字段之后。我们可以看图13.3的一个例子。第一个字节显示run长度字段是1字节和run偏移字段是两个字节。
这些值以簇大小为单位,并且偏移字段相对前一个偏移是一个有符号值。例如,属性中第一个run的偏移是相对文件系统的开始,第二个run的偏移则是相对上一个偏移。负数它的最高有效位是1,如果要将该值插入计算器以转换该值,则必须根据需要添加尽可能多的1。将把它组成一个完整的32或者64位数。例如,如果这个值是0xf1,则需要在转换器中输入0xfffffff1。
要查看非常驻属性,我们返回之前分析的entry,并进一步查看$DATA属性。属性内容如下所示,偏移值相对属性的开始。
0000000: 8000 0000 6000 0000 0100 4000 0000 0100 ....`.....@.....
0000016: 0000 0000 0000 0000 ef20 0000 0000 0000 ......... ......
0000032: 4000 0000 0000 0000 00c0 8300 0000 0000 @...............
0000048: 00c0 8300 0000 0000 00c0 8300 0000 0000 ................
0000064: 32c0 1eb5 3a05 2170 1b1f 2290 015f 7e31 2...:.!p..".._~1
0000080: 2076 ed00 2110 8700 00b0 6e82 4844 7e82 v..!.....n.HD~.

开始的4字节显示属性类型是128(0x80),第二组4字节标识其总大小是96字节(0x60)。第8个字节是1,说明这个是非常驻属性,第9个字节是0,显示名称属性长度是0,因此这是个默认的$DATA属性,并非一个ADS。第12到13字节是标志,值为0,意思是这个属性没有加密或压缩。
非常驻信息从第16字节开始,第16到23字节显示这个run的起始VCN是0。run的结束VCN在第24到31字节,值为8431(0x20ef)。字节32到33显示runlist的偏移是相对开始的64字节(0x0040)。第40到47字节,48到55,56到63,分别为“分配的”,“实际的”,和“初始化的”在空间中的总大小,并且它们的值都相等,都是8634368字节(0x0083c000)。
在第64字节,我们最终获得runlist。我将再一次复制相关的输出:
0000064: 32c0 1eb5 3a05 2170 1b1f
回顾第一个字节中的高和低4位,表示了其他字段的大小。第64字节的低4位表示run的长度字段有两个字节,高4位表示偏移字段占用3个字节。为了确定run的长度,我们检查第65到66字节,给我们7872簇(0x1ec0)。接下来的3个字节,字节67到69,用于表示偏移,簇342709(0x053ab5)。因此,第一个run开始于簇342709,连续7872簇。
下一个run位于于上一个run之后,即第70字节开始。我们可以看到长度字段是1字节,偏移字段是2字节。长度值在字节71,为112(0x70)。偏移值在字节72到73,为7963(0x1f1b)。这个偏移是一个有符号值,相对上一个偏移,所以我们把7963和342709相加,得到350672。因此第二个run位于簇350672和连续112簇。我会把剩下的runlist留给你来解析。

Standard File Attributes

前一节概述了如何处理一个MFT entry和属性头。每个属性头指向可以找到内容的一个常驻或非常驻位置。本节说明如何处理每个不同的属性内容类型。

$STANDARD_INFORMAITION 属性

$STANDARD_INFORMAITION 属性的标识符是16,总是常驻并包含文件或者目录的基本元数据。它存在于每一个文件和目录,通常是第一个属性,因为它有最低的类型标识符。它的字段(有必要)在表13.5。
    表13.5 $STANDARD_INFORMATION属性的数据结构
    Byte Range    Description                     Essential
    0–7                Creation time                     No
    8–15              File altered time                 No
    16–23         MFT altered time                 No
    24–31         File accessed time                 No
    32–35         Flags (see Table 13.6)                 No
    36–39         Maximum number of versions             No
    40–43         Version number                     No
    44–47         Class ID                     No
    48–51         Owner ID (version 3.0+)             No
    52–55         Security ID (version 3.0+)             No
    56–63         Quota Charged (version 3.0+)             No
    64–71         Update Sequence Number (USN) (version 3.0+)    No

四个时间值以100纳秒为单位自1601年1月1日的UTC时间差保存。同样的时间字段也存在于$FILE_NAME属性,但这是Windows在查看属性时候显示,这些是更新的。ID值用于应用程序级的功能或安全。安全ID值是$Secure文件的索引,不同于Windows SID值。标志值在表13.6中给出。
    Table 13.6. $STANDARD_INFORMATION属性的标志位值
    Flag Value    Description                         Essential
    0x0001         Read Only                         No
    0x0002         Hidden                             No
    0x0004         System                             No
    0x0020         Archive                         No
    0x0040         Device                             No
    0x0080         #Normal                         No
    0x0100         Temporary                         No
    0x0200         Sparse file                         No
    0x0400         Reparse point                         No
    0x0800         Compressed                         No
    0x1000         Offline                         No
    0x2000         Content is not being indexed for faster searches    No
    0x4000         Encrypted                         No

很多标志和FAT文件系统中的一样,在那里可以找到对它们的描述。加密和稀疏标志在属性头中给出。因此我认为它们在这个定位中并非必须。这是可以商榷的,因为其他人可能会声称此标志是必须的,而MFT entry头值则不是必需的。
让我们看看$STANDARD_INFORMATION属性。我们可以查看属性并通过使用 icat 指定属性类型。这将删除标准头自动提供给我们内容部分。
# icat -f ntfs ntfs1.dd 0-16 | xxd
0000000: 305a 7a1f f63b c301 305a 7a1f f63b c301 0Zz..;..0Zz..;..
0000016: 305a 7a1f f63b c301 305a 7a1f f63b c301 0Zz..;..0Zz..;..
0000032: 0600 0000 0000 0000 0000 0000 0000 0000 ................
0000048: 0000 0000 0001 0000 0000 0000 0000 0000 ................
0000064: 0000 0000 0000 0000 ........

属性的内容部分在$MFT文件的前8个字节,显示给我们创建时间,它的值和其他4个时间字段一样。字节32到35是标志值,0x00000060,包括了隐藏和系统属性位。字节36到39和40到43显示这个文件版本字段还未被使用,44到47字节展示了类ID为0。它的自身ID在字节48到51处,也是0,安全ID在字节52到55处为1。剩余值都是0,这在$MFT之中并不奇怪,因为它通常不应用任何用户的配额,并且大多数系统并没有更新日志记录的功能,所以不会分配USN。

$FILE_NAME Attribute

$FILE_NAME 属性的标识符是48,它有两个作用。它放在一个MFT entry内,存储了文件名和父目录信息,它在目录索引中使用。在MFT entry中使用的时候,它不包含任何必需信息,在一个目录索引中使用的时候除外。
一个标准文件或者目录,它将是第二个属性并且总是常驻的。如果一个文件需要多个MFT entries,$ATTRIBUTE_LIST属性将在$STANDARD_INFORMATION属性和$FILE_NAME属性之间出现。$FILE_NAME属性的字段如下表13.7所示。
    表13.7 $FILE_NAME属性数据结构
    Byte Range    Description                     Essential
    0–7         File reference of parent directory        No
    8–15         File creation time                 No
    16–23         File modification time                 No
    24–31         MFT modification time                 No
    32–39         File access time                 No
    40–47         Allocated size of file                 No
    48–55         Real size of file                 No
    56–59         Flags (see Table 13.6)                 No
    60–63         Reparse value                     No
    64–64         Length of name                     Yes / No
    65–65         Namespace (see Table 13.8)             Yes / No
    66 +         Name                         Yes / No

当$FILE_NAME属性用于目录索引中的时候,最后三个名称字段是必需的,但在文件的MFT entry中使用此属性时则不是必需的。标志字段和$STANDARD_INFORMATION里面的值一样,它们都在之前列出来了。
命名空间字节标识接下来的name字段遵循的规则。其数值见表13.8
    表13.8 $FILE_NAME命名空间字段值
    Name space value    Description
    0             POSIX: 名称区分大小写,并允许除'/'和NULL字符之外的所有的Unicode字符
    1             Win32: 名称不区分大小写,允许绝大多数的Unicode字符,除了特殊值,如'/', '\', ':', '>', '<', and '?'。
    2             DOS: 名称不区分大小写,全大写,并且无特殊字符。长度必须小于等于8个字符,扩展名必须小于等于3个字符。
    3             Win32 & DOS: 当一个原始名已符合DOS命名空间并且不需要两个名称的时候使用。

要查看$FILE_NAME属性,我们将再次看$MFT和指定的属性类型48。
# icat -f ntfs ntfs1.dd 0-48 | xxd
0000000: 0500 0000 0000 0500 305a 7a1f f63b c301 ........0Zz..;..
0000016: 305a 7a1f f63b c301 305a 7a1f f63b c301 0Zz..;..0Zz..;..
0000032: 305a 7a1f f63b c301 0040 0000 0000 0000 0Zz..;...@......
0000048: 0040 0000 0000 0000 0600 0000 0000 0000 .@..............
0000064: 0403 2400 4d00 4600 5400 ..$.M.F.T.

前8个字节是文件参考号,其中高两个字节是顺序号和低6字节是MFT entry。因此父目录是MFT entry 5,并且顺序是5,这是根目录的入口。接下来的8个字节是创建时间,它和属性里面其它3个时间值一样。
字节40到47和48到55分别是文件占用大小和实际大小。它们的值都是16,384字节(0x4000)。实际上,这个文件的$DATA属性是8,634,360字节,所以这显然不准确。许多文件将这些大小设置为0,但是它用于目录索引的时候它是准确的。
标志值在56到57字节,设置为0x0006, 这是隐藏和系统标志。它们和我们看到$STANDARD_INFORMATION中的一样。字节64显示这个名称有4字母长,字节65显示它的命名空间为3,这是符合Win32&DOSde 。我们可以看到名称在字节66处开始,使用UTF-16编码的Unicode表示。名字是“$MFT”。
最后一个例子,考虑一个具有两个$FILE_NAME属性的文件,因为Windows必须有一个DOS名存在。这个文件有$FILE_NAME属性,同时有一个DOS命名空间和一个Win32命名空间。我们将不会详细分析它们的细节,但输出如下:
# icat -f ntfs ntfs1.dd 5009-48-2 | xxd
0000000: 3920 0000 0000 0300 00b6 89a9 086a c401 9 ...........j..
0000016: 00b6 89a9 086a c401 00b6 89a9 086a c401 .....j.......j..
0000032: 00b6 89a9 086a c401 0000 0000 0000 0000 .....j..........
0000048: 0000 0000 0000 0000 2020 0000 0000 0000 ........ ......
0000064: 0b01 3500 3700 3300 3900 3800 3400 3000 ..5.7.3.9.8.4.0.
0000080: 3800 6400 3000 3100 8.d.0.1.

需要注意的是字节65显示这个命名空间是1,即Win32。这个entry中的名字是"57398408d01."。现在我们将查看下一个$FILE_NAME属性,但是它的属性标识是3:
# icat -f ntfs ntfs1.dd 5009-48-3 | xxd
0000000: 3920 0000 0000 0300 00b6 89a9 086a c401 9 ...........j..
0000016: 00b6 89a9 086a c401 00b6 89a9 086a c401 .....j.......j..
0000032: 00b6 89a9 086a c401 0000 0000 0000 0000 .....j..........
0000048: 0000 0000 0000 0000 2020 0000 0000 0000 ........ ......
0000064: 0802 3500 3700 3300 3900 3800 3400 7e00 ..5.7.3.9.8.4.~.
0000080: 3100 1.

这个属性有个命名空间在65字节处是2,即DOS。它在这个entry中的名字是"573984~1."。

$DATA Attribute

$DATA属性最容易理解因为它没有本地结构。在头之后,只有与文件内容相对应的原始内容。它的标识类型是128并且无最大最小大小。如果内容超过700字节,它可能是一个非常驻属性。对于大多数文件,这是MFT entry中的最后一个属性。注意,除了索引属性外,目录可以包含$DATA属性

$ATTRIBUTE_LIST Attribute

$ATTRIBUTE_LIST 属性存在于MFT entry中用来显示其他属性可以位于何处。一个文件如果属性头放不进一个MFT entry的时候,它包含了一个列表是每一个文件或者目录属性的入口。它的属性类型标识是32,并且每一个列表入口有下面表13.9的字段。
    表13.9 $ATTRIBUTE_LIST属性的列表入口数据结构。
    Byte Range     Description                         Essential
    0–3         Attribute type                         Yes
    4–5         Length of this entry                     Yes
    6–6         Length of name                         Yes
    7–7         Offset to name (relative to start of this entry)     Yes
    8–15         Starting VCN in attribute                 Yes
    16–23         File reference where attribute is located         Yes
    24–24         Attribute ID                         Yes

当需要多个MFT entries来描述单个属性时,使用起始VCN值。出现这个情况的时候,附加entries将有非0的起始VCN值。属性头将显示它有一个非0的起始VCN。
让我们查看一个含有一个$ATTRIBUTE_LIST属性的文件。
# icat -f ntfs ntfs1.dd 5009-32 | xxd
0000000: 1000 0000 2000 001a 0000 0000 0000 0000 .... ...........
0000016: 9113 0000 0000 0800 0000 0000 0000 0000 ................
0000032: 3000 0000 2000 001a 0000 0000 0000 0000 0... ...........
0000048: 9113 0000 0000 0800 0300 0000 0006 0000 ................
0000064: 3000 0000 2000 001a 0000 0000 0000 0000 0... ...........
0000080: 9113 0000 0000 0800 0200 0200 502d 40bc ............P-@.
0000096: 8000 0000 2000 001a 0000 0000 0000 0000 .... ...........
0000112: 3713 0000 0000 1200 0000 0000 1000 0000 7...............
0000128: 8000 0000 2000 001a 2014 0000 0000 0000 .... ... .......
0000144: ad13 0000 0000 0800 0000 0000 0000 0000 ................

前4个字节显示第一个entry的类型,即16(0x10),因此是$STANDARD_INFORMATION属性。字节4到5显示这个列表entry长度是32字节(0x0020)和字节16到31显示这个属性是位于MFT entry 5,009(0x1391)中,这是我们目前正在查看的。
接下来的两个entries在字节32和64开始,用于$FILE_NAME属性,它的标识是48(0x30)。这些属性也位于当前MFT entry。
字节96是第一个$DATA属性的第一个entry的开始。字节104到111显示这个$DATA属性用于该属性的VCN 0,字节112到117显示属性位于MFT entry的4,919(0x1337)。
$DATA属性的第二个entry在字节128处开始。我们可以看出它们是同一个$DATA属性的一部分,因为它们的ID值都是0。字节136到143告诉我们第二个entry开始于VCN 5,152(0x1420)。换句话说,$DATA属性在第一个entry已经有足够的空间在MFT entry中描述前面的5,152簇了。剩下的簇 run 存储在MFT entry 5,037(0x13ad)中的$DATA属性中,我们可以第144到149字节处看到。
图13.4显示这个文件的概要。它有一个$STANDARD_INFORMATION和两个$FILE_NAME属性位于基础MFT entry 5,009 并且$DATA属性的头定位在 4,919和5,037。
    图13.4 样本映像中属性列表的入口布局。

 

回顾第12章,非基础MFT entries将不具有标准的$FILE_NAME和$STANDARD_INFORMATION属性。我们可以通过查看本例中的一个entry来验证这一点。运行istat输出一个非基础entry 4919 输出如下:
# istat –f ntfs ntfs1.dd 4919
MFT Entry Header Values:
Entry: 4919 Sequence: 18
Base File Record: 5009
$LogFile Sequence Number: 66117460
Allocated File
Links: 0
[REMOVED]
Attributes:
Type: $DATA (128-0) Name: $Data Non-Resident size: 5787792
929409 929410 929411 929412 929413 929414 929415 929416
[REMOVED]

这个MFT entry仅有一个$DATA属性,并且我们可以看到这个头显示基础记录在 5,009入口处。"link count"是0,因为没有名称指向它。

$OBJECT_ID Attribute

$OBJECT_ID属性有一个类型标识为64,并且储存一个文件的128位全局对象标识可以使用的地址到文件代替它的名称。它允许一个文件去找到它的名字被修改的事件。"\$Extend\$ObjId"索引根据文件的对象ID排序并包含文件的引用地址每一个文件都可以找到。这个属性仅包含4个字段,并且常规仅仅第一个是被定义的,表13.10 是给出来的字段。
    表 13.10 $OBJECT_ID属性数据结构
    Byte Range     Description             Essential
    0–15         Object ID             Yes
    16–31         Birth volume ID         No
    32–47         Birth object ID         No
    48–63         Birth domain ID         No

许多分配了对象ID的文件只有第一个值,并且属性大小是16字节,$VOLUME文件包含一个$OBJECT_ID属性,它展示如下:
# icat -f ntfs img.dd 3-64 | xxd
0000000: fe24 b024 e292 fe47 95ac e507 4bf5 6782 .$.$...G....K.g.

$REPARSE_POINT Attribute

$REPARSE_POINT属性有一个属性标识是192,它是文件的重新解析点。重解析点用于卷的符号链接,连接和装载点。微软定义一些$REPARSE_POINT属性内容,但也可以开发特定于应用程序的属性。连接和装载点的内容结构在表13.11中。
    表 13.11 $REPARSE_POINT属性连接和装载点的数据结构。
    Byte Range     Description                         Essential
    0–3         Reparse type flags                     Yes
    4–5         Size of reparse data                     Yes
    6–7         Unused                             No
    8–9         Offset to target name (relative to byte 16)         Yes
    10–11         Length of target name                     Yes
    12–13         Offset to print name of target (relative to byte 16)     Yes
    14–15         Length of print name                     Yes

连接和装载点的类型标记将设置 0xa0000000 标记。这里我们看到一个重解析点链接到"C:\windows":
# icat -f ntfs ntfs2.dd 167-192 | xxd
0000000: 0300 00a0 2800 0000 0000 1c00 1e00 0000 ....(...........
0000016: 5c00 3f00 3f00 5c00 6300 3a00 5c00 7700 \.?.?.\.c.:.\.w.
0000032: 6900 6e00 6400 6f00 7700 7300 0000 1200 i.n.d.o.w.s.....

字节8到9显示到目标名称的偏移是0字节,所以它开始在字节16。它的长度在字节10到11处,我们可以看到它是28字节(0x1c)。使用Unicode字符,我们看到目标名称是"\??\C:\windows."

索引属性和数据结构

前面的章节涵盖了适用于所有文件的属性和概念。本节重点关注索引特有的数据结构和属性。回想一下,索引背后基本概念是排序树中的数据结构。该树有一个或者多个结点,并且每个结点含有一个或者多个索引入口。树根位于$INDEX_ROOT属性中,其他结点索引记录位于$INDEX_ALLOCATION属性。$BITMAP属性用来管理索引记录的申请状态。
在本节,我们从外部开始学习。我们将从属性开始,然后描述它们共同的数据结构。

$INDEX_ROOT Attribute

$INDEX_ROOT属性总是常驻的,它的类型标识符为144。它总是索引树的根并且只能存储一个索引入口的小列表。$INDEX_ROOT属性有一个16字节的头,跟着是结点头和索引链表入口。关于这个可以在图13.5中看到。结点头将在"索引结点头数据结构"部分中描述,索引入口将在"索引入口数据结构"部分中描述。在这里,我们将关注在$INDEX_ROOT头。

图 13.5. $INDEX_ROOT属性中的头和结点头和索引入口的内部布局。

表 13.12 $INDEX_ROOT属性头的数据结构
    Byte Range    Description                                 Essential
    0–3         Type of attribute in index (0 if entry does not use an attribute)    Yes
    4–7         Collation sorting rule                             Yes
    8–11         Size of each index record in bytes                     Yes
    12–12         Size of each index record in clusters                     Yes
    13–15         Unused                                     No
    16+ Node header (see Table 13.14)                             Yes

索引入口包含数据结构标识属性类型,它们是如何排序的,和$INDEX_ALLOCATION属性中每一个index record的大小。字节8到11是大小的值,单位是字节,字节12是簇数或大小的对数。"$Boot File"部分更加详细地描述这种编码方式。注意这个index record大小也在引导扇区种给出。
要查看$INDEX_ROOT属性的内容,我们使用icat并提供144类型:
# icat -f ntfs ntfs1.dd 7774-144 | xxd
0000000: 3000 0000 0100 0000 0010 0000 0400 0000 0...............
0000016: 1000 0000 a000 0000 a000 0000 0100 0000 ................
[REMOVED]

字节0到3表示索引中的属性是属性类型48(0x30),即$FILE_NAME属性,字节8到11显示每个index record将是4096个字节。

$INDEX_ALLOCATION Attribute

大目录不能把它的所有索引放在常驻$INDEX_ROOT属性里面,所以它们需要一个非常驻$INDEX_ALLOCATION属性。$INDEX_ALLOCATION属性有index record填充。一个index record有一个静态的大小并在排序树种包含一个结点。index record的大小在$INDEX_ROOT属性头种定义和在引导扇区中定义,但通常大小是4096字节。$INDEX_ALLOCATION属性有类型标识符为160并且和$INDEX_ROOT属性同时存在。
每个index record开始用特定的数据结构头,跟着是一个结点头和一个index入口链表。在$INDEX_ROOT属性中结点头和index入口的数据结构都是一样的。第一个index record在属性的0字节开始。我们可以在图13.6中看到这个,$INDEX_ALLOCATION属性有两个index record。
    图13.6 $INDEX_ALLOCATION属性和它的记录头,结点头和索引入口在内部的布局。

index record头的值在下面的表13.13
    表13.13 index record头的数据结构
    Byte Range    Description                         Essential
    0–3         Signature value ("INDX")                 No
    4–5         Offset to fixup array                     Yes
    6–7         Number of entries in fixup array             Yes
    8–15         $LogFile Sequence Number (LSN)                 No
    16–23         The VCN of this record in the full index stream        Yes
    24+         Node header (see Table 13.14)                 Yes

前4个字段几乎和MFT entry中的字段相同,但签名值不同。参考本章开始讨论的修正数组。

VCN值在字节16到23确定在树中的标识。$INDEX_ALLOCATION属性由多个index record填充,按照顺序。VCN值在头部标识这个index record放到大缓冲中。当一个index entry指向它的孩子结点,它在结点的index record头中使用VCN地址。
让我们看$INDEX_ALLOCATION属性的内容从同一个目录。
# icat –f ntfs ntfs1.dd 7774-160 | xxd
0000000: 494e 4458 2800 0900 4760 2103 0000 0000 INDX(...G`!.....
0000016: 0000 0000 0000 0000 2800 0000 f808 0000 ........(.......
[REMOVED]

我们可以在第一行看到签名值"INDX", 字节4到5和6到7显示修复记录值。字节16到23显示这个index record是VCN 0 缓冲。结点头开始在字节24处,$INDEX_ALLLOCATION属性是8192字节大小,所以有空间给其他的index record。它在字节4,096处开始:

[REMOVED]
0004096: 494e 4458 2800 0900 ed5d 2103 0000 0000 INDX(....]!.....
0004112: 0400 0000 0000 0000 2800 0000 6807 0000 ........(...h...
0004128: e80f 0000 0000 0000 3b00 0500 6900 c401 ........;...i...
[REMOVED]

我们看到签名值"INDX"并且字节4,112到4,119展示给我们这个VCN 4(在文件系统中每一簇1,024字节)。我们将在讨论结点头和index 入口之后回到这个例子。

$BITMAP Attribute

在之前的部分,我们看一个$INDEX_ALLOCATION属性有两个4,096字节的index record。它可能有一些index record还未被使用。$BITMAP属性用来跟踪在$INDEX_ALLOCATION属性中的每一个index record分配。通常Windows仅仅分配我们需要的index record,但在删除多个文件后,或者由于每个簇大于一个index record,一个目录可能有不需要的记录。注意这个属性也用来跟踪$MFT分配了哪些MFT entry。
$BITMAP属性的标识符为176并用字节组成,并且每一位对应一个index record。我们可以用icat来查看前一个目录的$BITMAP属性。

# icat -f ntfs ntfs1.dd 7774-176 | xxd
0000000: 0300 0000 0000 0000 ........

我们可以在0字节处看到值0x03,二进制的0000 0011。因此,index record0和1已经分配了。

Index Node Header Data Structure(索引结点头数据结构)

目前为止,我们已经看了$INDEX_ROOT和$INDEX_ALLOCATION属性,并且它们有相同的头数据即结点头和索引入口列表。在这一节,我们描述结点头数据结构。这个头出现在$INDEX_ROOT和每一个index record数据结构和用于显示这个索引入口列表的开始和结束。这个头的字段在下面的表13.14。

表 13.14 索引结点头的数据结构。

    Byte Range     Description                                         Essential
    0–3         Offset to start of index entry list (relative to start of the node header) Yes
    4–7         Offset to end of used portion of index entry list (relative to start of the node header)    Yes
    8–11         Offset to end of allocated index entry list buffer (relative to start of the node header)    Yes
    12–15         Flags                                                 No

一个$INDEX_ROOT结点中的索引入口在紧接着结点头之后,但是因为修正值,索引入口在一个索引缓冲中可能不存在。当我们看已经删除的index入口剩余数据,我们将检查缓冲中已用部分结束和分配缓冲结束之间的数据。
flags字段只有一个标志,当列表中的entry指向子结点时将设置0x01标志。每个index entry都存在这样的标志。
让我们仔细查看上前面的属性。$INDEX_ROOT属性有下面的数据:

# icat -f ntfs ntfs1.dd 7774-144 | xxd
0000000: 3000 0000 0100 0000 0010 0000 0400 0000 0...............
0000016: 1000 0000 a000 0000 a000 0000 0100 0000 ................
[REMOVED]

结点头从字节16开始,字节16到19显示列表从16字节(0x10)开始,即32字节,到160(0xa0)字节结束,字节176。在这种情况下,分配空间和使用空间一样,因为它们来自同一个$INDEX_ROOT属性,它是常驻并且尽可能小。字节28处有0x01标志,所以这是这个节点的子结点(定位到$INDEX_ALLOCATION)。
现在让我们再次查看$INDEX_ALLOCATION属性:

# icat –f ntfs ntfs1.dd 7774-160 | xxd
0000000: 494e 4458 2800 0900 4760 2103 0000 0000 INDX(...G`!.....
0000016: 0000 0000 0000 0000 2800 0000 f808 0000 ........(.......
0000032: e80f 0000 0000 0000 2100 0000 0600 0000 ........!.......
[REMOVED]

前24字节是index record头,结点头在此之后开始。字节24到27显示这个index entry列表开始在字节偏移40(0x28)处。注意这是相对结点头开始的,所以我们需要加上24,得到64。这个案列index entry列表不是立即跟着index entry列表头因为修正记录数据在index entry列表头和实际列表之间。回顾index record头的字节4到5表明这个修正数组定位在偏移40(0x28)。
字节28到31表示到列表最后一个entry偏移量为2,296(0x8f8)字节,字节32到35表明分配列表缓冲偏移结束在4,072(0x0fe8)字节。因此,缓冲中有1,776字节没被使用可能包含来自存储在这些结点中的名称的文件数据,字节36到39表明标志是0,所以这个结点没有子结点。

Generica Index Entry Data Structure(通用索引项数据结构)

目前为止,我们已经讨论了NTFS索引的一般概念,唯一缺少的是对索引项的讨论。从这点,数据结构将特定于索引类型,但是这是所有index entry的通用结构,我将在本节描述。
index entry标准字段在表 13.15所示。

表 13.15 通用index entry数据结构。

Byte Range     Description
    0–7         Undefined
    8–9         Length of this entry
    10–11         Length of content
    12–15         Flags (see Table 13.16)
    16+         Content

entry的最后8字节,开始在$INDEX_ALLOCATION(8字段边界仅当标志被设置)子结点的一个VCN。

开始的8字节被用来存储特定的index entry数据。字节8到9定义这个index entry多大,字节10到11是index entry内容的长度,开始于字节16。内容可以是任何数据。flags字段的值在如下的表13.16。

表13.16 index entry标志字段的标记值

    Value         Description
    0x01         Child node exists
    0x02         Last entry in list

当一个index entry有子结点,那么标志位为0x01,那么子结点的VCN地址在index entry的最后8字节处。回顾每个index entry都有一个VCN。当标志位为0x02的时候,这个就是列表中最后一个entry。

Directory Index Entry Data Structure(目录Index Entry数据结构)

用于文件名的目录索引有一个特定index entry数据结构。它使用上一节中概述的基本模板。并包括一个文件引用地址和一个$FILE_NAME属性。每一个index entry的字段在表 13.17里面所示。

    表13.17, 目录index entry数据结构。

    Byte Range     Description                     Essential
    0–7         MFT file reference for file name         Yes
    8–9         Length of this entry                 Yes
    10–11         Length of $FILE_NAME attribute             No
    12–15         Flags (see Table 13.16)             Yes
    16+         $FILE_NAME Attribute (if length is > 0)     Yes

    Last 8 bytes of entry, starting on an 8-byte boundary VCN of child node in $INDEX_ALLOCATION(field exists only if flag is set)

文件引用值指向index entry相对的MFT entry。两个标志值应用于这个entry,0x01表示它是子结点,0x02表示这是列表中的最后一个结点。
现在让我们查看$INDEX_ROOT和$INDEX_ALLOCATION属性的剩余部分,我们已经对这些属性进行了部分剖析。$INDEX_ROOT属性的内容如下:

# icat -f ntfs ntfs1.dd 7774-144 | xxd
0000000: 3000 0000 0100 0000 0010 0000 0400 0000 0...............
0000016: 1000 0000 a000 0000 a000 0000 0100 0000 ................
0000032: c51e 0000 0000 0500 7800 5a00 0100 0000 ........x.Z.....
0000048: 5e1e 0000 0000 0300 e03d ca37 5029 c401 ^........=.7P)..
0000064: 004c c506 0202 c401 e09a 2a36 5029 c401 .L........*6P)..
0000080: d0e4 22b5 096a c401 0004 0000 0000 0000 .."..j..........
0000096: 7003 0000 0000 0000 2120 0000 0000 0000 p.......! ......
0000112: 0c02 4d00 4100 5300 5400 4500 5200 7e00 ..M.A.S.T.E.R.~.
0000128: 3100 2e00 5400 5800 5400 0000 0000 0300 1...T.X.T.......
0000144: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000160: 1800 0000 0300 0000 0400 0000 0000 0000 ................
[REMOVED]

我们已经处理了开始的32字节,因为开始16字节是$INDEX_ROOT头,第二个16字节是结点头。字节32到37表示这个entry是MFT entry 7,877(0x1ec5)。字节40到45表示index entry的大小是120(0x78)字节,所以它将结束于我们输出的152字节。字节26到27表示这个属性大小是90字节(0x5a)。标志位在字节28处表示entry最后8字节是子结点的地址, 这是我们所期望的,因为结点头中标志显示有一个子结点。

$FILE_NAME属性位于包含子结点VCN的index entry的最后,字节48到137和字节144到151,是0簇。我们可以看到文件名为"MASTER~1.txt"。字节152到175包含一个空index entry和标志位在字节164是3,表示这是列表中的结尾并且它包含子结点。字节168到175包含子结点的地址,VCN是4。我们看到$INDEX_ALLOCATION属性中的两个index record。图13.7表示一个这个属性的图形布局。

    图 13.7 我们的$INDEX_ROOT示例目录布局

$INDEX_ALLOCATION属性里面的每一个index record都可以同样的方式重复处理。相反,我们将在结点头之后看到一些有趣的数据。我们之前已经看到index entries在index record 0 以偏移2,296结束,但是在结点中还有分配的 1,776 字节。因此,可能会删除掉文件名信息 。我们可以在未使用的区中寻找合法的index enties。字节偏移2,400处具有以下内容:

0002400: be1e 0000 0000 0600 6800 5400 0000 0000 ........h.T.....
0002416: 5e1e 0000 0000 0300 908b bf37 5029 c401 ^..........7P)..
0002432: 004c c506 0202 c401 e09a 2a36 5029 c401 .L........*6P)..
0002448: 30a7 6410 9c4a c401 003c 0000 0000 0000 0.d..J...<......
0002464: 003a 0000 0000 0000 2120 0000 0000 0000 .:......! ......
0002480: 0903 7000 7300 6100 7000 6900 2e00 6400 ..p.s.a.p.i...d.
0002496: 6c00 6c00 6500 0000 2513 0000 0000 0b00 l.l.e...%.......

这是文件中的一个index entry在MFT entry 7,870(0x1ebe),并且这个文件的名称是psapi.dll。由于索引重排的性质,我们无法判断这个文件是被删除了还是因为添加或者删除了其他文件而被移动到一个不同的entry位置。我们只能在查看所有其他索引项后才能判断。TSK显示所有entries,并将其留给用户来确定未分配的原因。不过,Autopsy将过滤TSK输出,只显示唯一的名称。

图13.8 显示$INDEX_ROOT的index entries和$INDEX_ALLOCATION的index records关系。我们可以看到root结点的索引有两个entries。任何文件的名称比"MASTER~1.TXT小的index record在VCN 0,而名称大的index record在VCN 4。VCN 0中的index record尾部包含未分配的空间,并且我们可以找到文件"psapi.dll"的数据。注意这个名称大于"MASTER~1.TXT",所以当它保存到这个结点,这像这个根结点中的一个不同的入口。
    图 13.8 解剖样本中的索引entries布局。

File System Metadata Files(文件系统元数据文件)

现在我们已经研究了文件和索引可以具有各种属性,接下来我们将研究文件系统元数据文件。它们中的大多数使用普通的文件属性,但是一部分也会有它们自己的属性。这些属性在以下各章节里面描述。

$MFT File

文件$MFT位于主文件表入口0并且在本章开始的时候已经讨论过在每一个文件中非常重要。在第12章"istat"输出一个$MFT文件在我们的例子影像。它在$MFT中有标准属性和$DATA属性。
$MFT的一个唯一属性是$BITMAP属性,用于管理MFT entries的分配状态。它是按字节组织的,当一位设置为1表示entry被分配。否则当此位设置为0表示entry未被分配。我们可以用icat并指定属性类型176来查看$BITMAP属性。
# icat -f ntfs ntfs1.dd 0-176 | xxd
0000000: ffff 00ff ffff ffff ffff ffff ffff ffff ................
0000016: ffff ffff ffff ffff ffff ffff ffff ffff ................
[REMOVED]

我们看到这里除了第3个字节很多位都被设置为1。这个字节对应MFT entries的16到23。

$Boot File

$Boot文件位于MFT entry 7,并且它的$DATA属性包含引导扇区和引导代码。这个属性总是在0扇区开始,启动扇区数据结构位于这里。其它扇区被用于启动代码。启动扇区的字段在表 13.18 中显示。

    表 13.18. 引导扇区数据结构
Byte Range    Description                         Essential
0–2         Assembly instruction to jump to boot code         No (unless it is the bootable filesystem)
3–10         OEM Name                        No
11–12         Bytes per sector                     Yes
13–13         Sectors per cluster                     Yes
14–15        Reserved sectors (Microsoft says it mustbe 0)        No
16–20         Unused (Microsoft says it must be 0)            No
21–21        Media descriptor                    No
22–23        Unused (Microsoft says it must be 0)            No
24–31        Unused (Microsoft says it is not checked)        No
32–35        Unused (Microsoft says it must be 0)            No
36–39        Unused (Microsoft says it is not checked)        No
40–47        Total sectors in file system                Yes
48–55        Starting cluster address of MFT                Yes
56–63        Starting cluster address of MFT Mirror $DATA attribute    No
64–64        Size of file record (MFT entry)                Yes
65–67        Unused                            No
68–68        Size of index record                    Yes
69–71        Unused                            No
72–79        Serial number                        No
80–83        Unused                            No
84–509        Boot code                        No
510–511    Signature (0xaa55)                    No

这些字段不用来对应FAT文件系统中的引导扇区BIOS Parameter Block(BPB)字段。微软文档标识它们其中的部分必须是0为文件系统挂载的时候用,但它们依然考虑非必须的数值,因为它们在文件系统功能中不需要,并且微软可能决定不检查这些数值。我验证如果这些数值非0,则Windows XP并不挂载磁盘。

引导扇区最重要的数值是每一个扇区和簇的大小。如果这些,我们将不能标识任何数据的位置。其次重要的数值是MFT的开始位置和每一个MFT entry的大小。目前为止,MFT entries 总是1024字节,但是这个字段存在,以便将来可以轻易改变大小。还需要注意的是,$DATA属性和$MFTMirr地址已经给出。它允许恢复工具确定$MFT entry的备份副本在哪里,以便检测定位MFT。
字段显示MFT entry和index record大小有指定的格式。如果数值大于0, 它表示簇数量用到每一个数据结构。如果数值小于0,表示它是2的对数字节在每一个数据结构。要计算字节数,取负数的绝对值(也就是正数)并作为2的次幂。举个例子,如果值是-10,那么数据结构的大小就是2的10次幂即1024字节。当簇大小大于单个MFT entry或者index record的时候出现这种情况。
让我们仔细分析引导扇区。在第12章我们看到"istat"输出$Boot文件,并且现在我们可以使用"icat"取查看$DATA属性(类型128)。

# icat –f ntfs ntfs1.dd 7-128 | xxd
0000000: eb52 904e 5446 5320 2020 2000 0202 0000 .R.NTFS .....
0000016: 0000 0000 00f8 0000 3f00 ff00 3f00 0000 ........?...?...
0000032: 0000 0000 8000 8000 4060 1f00 0000 0000 ........@`......
0000048: b53a 0500 0000 0000 10d8 0700 0000 0000 .:..............
0000064: 0100 0000 0400 0000 947c 2250 8422 5004 .........|"P."P.
0000080: 0000 0000 fa33 c08e d0bc 007c fbb8 c007 .....3.....|....
0000096: 8ed8 e816 00b8 000d 8ec0 33db c606 0e00 ..........3.....
[REMOVED]
0000448: 6d70 7265 7373 6564 000d 0a50 7265 7373 mpressed...Press
0000464: 2043 7472 6c2b 416c 742b 4465 6c20 746f Ctrl+Alt+Del to
0000480: 2072 6573 7461 7274 0d0a 0000 0000 0000 restart........
0000496: 0000 0000 0000 0000 83a0 b3c9 0000 55aa ..............U.
[REMOVED]

在第一行我们看到OEM名称是"NTFS",跟着是一些ASCII空格(0x20)。这是Windows访问的标准名称。字节11到12显示每一个扇区的字节数是512(0x200)。字节13显示每个扇区两簇,所以每簇是1024字节。字节40到47显示文件系统中的总数值是2,056,256(0x001f6040),即表示文件系统有1GB这么大。字节48到55显示MFT的起始簇是342,709(0x00053ab5)和字节56到64显示MFT mirror的$DATA属性的起始簇是514,064(0x0007d810)。
字节64显示每个MFT entry的大小。回想这个数值依赖它是正数还是负数。在这个案例,它是1,所以代表MFT entry中的簇数是1024字节。字节68是用于目录的index record的大小,此值为4,即说明每4簇一个index record。
字节72到79是文件系统的序列号,0x04502284 50227C94。剩下的字节包含引导代码并且字节510到511是0xAA55签名,如同我们看FAT文件系统时一样。

$AttrDef File

$AttrDef文件系统元文件的MFT entry号码为4,它定义文件系统属性名和标识。此文件的$DATA属性包含一个entry列表,它的字段如表 13.19所示。
    表 13.19. $AttrDef entries数据结构。
    Byte Range        Description            Essential
    0–127            Name of attribute         Yes
    128–131        Type identifier            Yes
    132–135         Display rule             No
    136–139         Collation rule            No
    140–143        Flags (see Table 13.20)        Yes
    144–151        Minimum size            No
    152–159        Maximum size            No

    Table 13.20. $AttrDef entry标志字段标志数值。
    Value            Description
    0x02            Attribute can be used in an index
    0x04            Attribute is always resident
    0x08            Attribute can be non-resident

如果属性没有任何大小限制,最小大小将为0,并且最大大小将为0xffffffffffffffff。标志字段可以设置图13.20所示的值。
属性在索引中时使用排序规则。决定它该如何排序。示例图像中$AttrDef文件的$DATA属性包含以下内容:

# icat -f ntfs ntfs1.dd 4-128 | xxd
0000000: 2400 5300 5400 4100 4e00 4400 4100 5200 $.S.T.A.N.D.A.R.
0000016: 4400 5f00 4900 4e00 4600 4f00 5200 4d00 D._.I.N.F.O.R.M.
0000032: 4100 5400 4900 4f00 4e00 0000 0000 0000 A.T.I.O.N.......
0000048: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000064: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000096: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000112: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000128: 1000 0000 0000 0000 0000 0000 4000 0000 ............@...
0000144: 3000 0000 0000 0000 4800 0000 0000 0000 0.......H.......
[REMOVED]

我们可以看到第一个属性定义为$STANDARD_INFORMATION属性。在字节128到131处我们可以看到这个属性类型为16(0x10)。在字节140到143处是标志位,显示这个entry总是常驻的。字节144到151显示这个属性的最小的大小是48字节(0x30),并且最大大小为72字节(0x48)。

$Bitmap File

在MFT entry 6号处是$Bitmap文件,有一个$DATA属性用来管理簇的分配状态。位图数据由1字节数值组成,每个字节的最低有效位对应于前一个字节的最高有效位对应的簇之后的簇。

举例来说,考虑两个字节的二进制数值为00000001和00000011。第一个字节最低有效位是1对应0簇。字节中的下7位(向后从右到左)全是0,所以我们知道簇1到7都还没有被分配。第二个字节有两个最低有效位都是1,对应簇8和9。像你可以看到的一样,你读这个通过查找最低有效位,从右到左向后移动,然后转到右边的下一个字节来读取此值。
要确定给定簇的分配状态,需要确定它位于bitmap的哪个字节。这是通过将簇地址除以8并忽略其余部分来完成。举个例子,簇5将在位图的 0字节,簇18将在位图的第二个字节。去找字节中位对应的簇,我们检验余数。例如,当我们用5除以8,余数是5,18除以8,余数是2。图 13.9  是计算簇5和簇74在位图中的情况。

    图 13.9 在位图中寻找第5和第74个簇的分配状态例子。

带着位图中位组织的知识,让我们回到示例文件系统。我们可以用"icat"检测$DATA属性的内容:
# icat –f ntfs ntfs1.dd 6-128 | xxd
0000000: ffff ffff ffff ffff ffff ff3f 0000 0000 ...........?....
0000016: 0000 f0ff ffff ffff ffff ffff ffff ffff ................
0000032: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000048: ffff ffff ffff ffff 0300 0000 ffff ffff ................
0000064: ffff ffff ffff ffff ffff ffff ffff ffff ................
0000080: ffff ffff ffff ffff ffff ffff ffff ffff ................
[REMOVED]

我们看到这里初始位都是1,这是有意义的,因为引导扇区位于文件系统的前8KB中,所以我们希望分配这些字节。在输出的字节11中,我们看到值0x3f,二进制是00111111。这个字节相对文件系统中的簇88到95,并且6个最低有效位都是1,这表示都是已经分配了。第7和第8位是0,对应簇94和簇95.我们也可以看到下6个字节都是0,所以这些位的所有簇都未被分配。在字节18我们看到值0xf0,二进制是 1111 0000。

$Volume File

$Volume文件在MFT entry 3处,并且它有两个唯一属性。本章节将对它们进行描述。

$VOLUME_NAME Attribute

$VOLUME_NAME属性的类型标识是96 应该只分配给$Volume文件。它仅仅包含volume的名称,用UTF16的Unicode编码。它的内容从我们的示例映像中显示如下:
# icat -f ntfs ntfs1.dd 3-96 | xxd
0000000: 4e00 5400 4600 5300 2000 4400 6900 7300 N.T.F.S. .D.i.s.
0000016: 6b00 2000 3200 k. .2.

我们看到这个文件系统的卷标名称是"NTFS Disk 2."

$VOLUME_INFORMATION Attribute

第二对于$Volume文件的个唯一属性是$VOLUME_INFORMATION属性,类型标识是112。这个属性包含文件系统的版本。它的字段如表 13.21所示。

    表 13.21. $VOLUME_INFORMATION属性数据结构。
    Byte     Range Description         Essential
    0–7     Unused                 No
    8–8     Major version             Yes
    9–9     Minor version             Yes
    10–11 Flags (see Table 13.22)         No

Windows NT系统的文件系统版本是 1.2。Windows 2000 的文件系统版本是 3. 0。Windows XP 的文件系统是 3.1。标志显示表 13.22 应用这个数据结构。

    表 13.22. $VOLUME_INFORMATION标志字段标志数值。
    Flag     Description
    0x0001     Dirty
    0x0002     Resize $LogFile (file system journal)
    0x0004     Upgrade volume next time
    0x0008     Mounted in NT
    0x0010     Deleting change journal
    0x0020     Repair object IDs
    0x8000     Modified by chkdsk

$VOLUME_INFORMATION属性在我们的示例文件系统有如下的内容:
# icat -f ntfs ntfs1.dd 3-112 | xxd
0000000: 0000 0000 0000 0000 0301 0000 ............

第8和第9字节告诉我们这个文件系统版本是3.1,即XP。这个标志被设置为0。

$ObjId File

正如我们在第12章中看到,可以使用文件的对象ID而不是名称来寻址。这允许文件改名后依然可以找到该文件。文件\$Extend\$ObjId有一个名为$O的索引,将文件的对象ID与它的MFT entry关联起来。文件$ObjId通常不位于保留的MFT entry。
索引有典型的$INDEX_ROOT和$INDEX_ALLOCATION属性,并且它的index entries的字段如下表 13.23。
    表 13.23. $ObjId index entries 数据结构。
    Byte Range     Description            Essential
    0–1         Offset to file information     Yes
    2–3         Size of file information     Yes
    4–7         Unused                 No
    8–9         Size of index entry         Yes
    10–11         Size of object ID (16-bytes)     Yes
    12–15         Flags (see Table 13.16)     Yes
    16–31         Object ID             Yes
    32–39         File reference             Yes
    40–55         Birth volume ID         No
    56–71         Birth object ID         No
    72–87         Birth domain ID         No

标志字段有标准的值,当子结点存在的时候为 0x01,当它是最后一个entry的时候数值是0x02。这里一些$INDEX_ROOT 属性index entriies, 当结点头被删除:
0000000: 2000 3800 0000 0000 5800 1000 0000 0000 .8.....X........
0000016: fe24 b024 e292 fe47 95ac e507 4bf5 6782 .$.$...G....K.g.
0000032: 0300 0000 0000 0300 0000 0000 0000 0000 ................
0000048: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000064: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000080: 0000 0000 0000 0000 2000 3800 0000 0000 ........ .8.....
0000096: 5800 1000 0000 0000 a162 3d5e cdda d811 X........b=^....
0000112: 883c 00b0 d01d e93f a400 0000 0000 0100 .<.....?........
0000128: fe24 b024 e292 fe47 95ac e507 4bf5 6782 .$.$...G....K.g.
0000144: a162 3d5e cdda d811 883c 00b0 d01d e93f .b=^.....<.....?
0000160: 0000 0000 0000 0000 0000 0000 0000 0000 ................

我们从第8个字节看到entry是88字节(0x58)大小,并且字节16到31显示了16位的对象ID。字节32到37显示这个对象ID的MFT entry是3。这是$OBJECT_ID属性的index entry剖析"$OBJECT_ID Attribute"段。这个entry的其余ID字段是0,下一个entry从字节88开始。

$Quota File

文件\$Extend\$Quota给用户未来配额。它并不位于保留的MFT entry。它包含两个索引同时使用标准$INDEX_ROOT和$INDEX_ALLOCATION属性来保存这些index entries。$O索引关联将SID与所有者ID关联,$O索引关联所有者ID到配额信息。$O索引的index entry字段如下表 13.24。
    表 13.24. $Quota的$O index entries数据结构。
    Byte Range     Description             Essential
    0–1         Offset to owner ID (OFF)     Yes
    2–3         Length of owner ID         Yes
    4–7         Unused                 No
    8–9         Size of index entry         Yes
    10–11         Size of SID (L)         Yes
    12–15         Flags (see Table 13.16)     Yes
    16–(16+L-1)     SID                 Yes
    OFF+         Owner ID             Yes

这个index entry的标志值像我们看到的文件名的一样。当它是一个子结点的时候设置0x01,当它是最后一个entry的时候设置0x02。如果一个孩子存在,最后8字节将被用来作为这个孩子的VCN。
下面是$O索引中的第一个index entry:
0000000: 1c00 0400 0000 0000 2000 0c00 0000 0000 ........ .......
0000016: 0101 0000 0000 0005 1200 0000 0401 0000 ................
0000032: 1c00 0400 0000 0000 2000 0c00 0000 0000 ........ .......
0000048: 0101 0000 0000 0005 1300 0000 0301 0000 ................
[REMOVED]

字节0到1显示所有者ID位于entry的开始偏移28(0x1c)处,并且字节2到3显示所有者ID是4字节长。字节8到9显示index entry是32字节(0x20),字节10到11显示SID是12字节(0x0c)。字节16到27包含SID,字节28到31包含所有者ID,是260(0x0104)。第二个entry在列表开始的32字节处,并且它的所有者ID在字节60到63,是259(0x0103)。
$Q索引映射所有者ID到所有者配额信息。它的index entry值如下表 13.25所示。
    表 13.25. $Quota中的$Q索引数据结构。
    Byte Range     Description                 Essential
    0–1         Offset to quota information         Yes
    2–3         Size of quota information         Yes
    4–7         Unused                     No
    8–9         Size of index entry             Yes
    10–11         Size of owner ID (4 bytes)         Yes
    12–15         Flags (see Table 13.16)         Yes
    16–19         Owner ID                 Yes
    20–23         Version                 No
    24–27         Quota flags (see Table 13.26)         Yes
    28–35         Bytes charged to user             Yes
    36–43         Time of last charge             No
    44–51         Threshold value (a soft limit)         Yes
    52–59         Hard limit value             Yes
    60–67         Exceeded time                 Yes
    68–79         SID                     Yes

如果index entry是一个子结点那么标志位设为0x01,如果是最后一个entry就设置位0x02。配额标志的值在表 13.26处。
    表 13.26. $Q index entry的标志位数值字段。
    Flag         Description
    0x00000001     Default limits being used
    0x00000002     Limit reached
    0x00000004     ID deleted
    0x00000010     Tracking data usage
    0x00000020     Enforcing data usage
    0x00000040     Usage tracking requested
    0x00000080     Create log when threshold is met
    0x00000100     Create log when limit is met
    0x00000200     Out of date
    0x00000400     Corrupt
    0x00000800     Pending deletes

下面是一个来自我们用于$O索引的同一个映像中的index entry。出自于$INDEX_ALLOCATION属性,头部的数据被移除了。这实际来自entry列表里面并对应所有者ID在之前的例子中显示。
0000000: 1400 3c00 0000 0000 5000 0400 0000 0000 ..<.....P.......
0000016: 0301 0000 0200 0000 0100 0000 0028 0500 .............(..
0000032: 0000 0000 401b 7c3c 7751 c401 ffff ffff ....@.|<wQ......
0000048: ffff ffff ffff ffff ffff ffff 0000 0000 ................
0000064: 0000 0000 0101 0000 0000 0005 1300 0000 ................
0000080: 1400 3c00 0000 0000 5000 0400 0000 0000 ..<.....P.......
0000096: 0401 0000 0200 0000 0100 0000 0094 6602 ..............f.
0000112: 0000 0000 90fe 8bdf d769 c401 ffff ffff .........i......
0000128: ffff ffff ffff ffff ffff ffff 0000 0000 ................
0000144: 0000 0000 0101 0000 0000 0005 1200 0000 ................

字节0到1显示配额信息的偏移是20字节(0x14),字节2到3显示配额信息占用60字节。字节16到19显示所有者ID 259(0x0103),即我们看到的$O 索引中第二个entry。字节24到27有配额标志,我们看到这个用户有默认限制。字节28到35显示用户帐号仅仅有337920字节(0x052800)。如果你继续解析下一个entry,它依然包含在内。

$LogFile File

文件$LogFile在MFT的第二个entry,它用来作为NTFS日志。它有标准文件属性并保存日志数据在$DATA属性中。很不幸地,额外数据细节我们并不知道。不过我们将取出一部分内容,以大致了解其内容。
日志被组织成4096字节的页面。前两个用于个重启动区,它们每一页都有"RSTR"签名:
# icat -f ntfs ntfs1.dd 2 | xxd | grep RSTR
0000000: 5253 5452 1e00 0900 0000 0000 0000 0000 RSTR............
0004096: 5253 5452 1e00 0900 0000 0000 0000 0000 RSTR............

这个数据结构中的其他值很多都为0, 并且仅仅字符串"NTFS"用Unicode。在第二个重启动数据结构之后偏移8192处就是记录,它们的每一个开始都带着签名"RCRD"
# icat –f ntfs ntfs1.dd 2 | xxd | grep RCRD
0008192: 5243 5244 2800 0900 0050 2500 0000 0000 RCRD(....P%.....
0012288: 5243 5244 2800 0900 0050 2500 0000 0000 RCRD(....P%.....
[REMOVED]

显示保留数据属性可以从日志文件中找到,我用ASCII字符串"My new file, can you see it?"建立一个叫"C:\log-test.txt"的文件,在日志文件中我们可以看到如下所示:
2215936: 5243 5244 2800 0900 f93b 9403 0000 0000 RCRD(....;......
[REMOVED]
2217312: 3801 1800 0000 0000 ec12 0000 0000 0000 8...............
2217328: a14d 0500 0000 0000 4d79 206e 6577 2066 .M......My new f
2217344: 696c 652c 2063 616e 2079 6f75 2073 6565 ile, can you see
2217360: 2069 743f 0000 0000 0000 0000 0000 0000 it?............
2217376: 0000 0000 0000 0000 0000 0000 0000 0000 ................
[REMOVED]
2217808: 0003 0000 0094 7c22 5010 0000 004e 5446 ......|"P....NTF
2217824: 5320 4469 736b 2032 0043 3a5c 6c6f 672d S Disk 2.C:\log-
2217840: 7465 7374 2e74 7874 0000 1500 2e00 2e00 test.txt........
2217856: 5c00 2e00 2e00 5c00 2e00 2e00 5c00 6c00 \.....\.....\.l.
2217872: 6f00 6700 2d00 7400 6500 7300 7400 2e00 o.g.-.t.e.s.t...
2217888: 7400 7800 7400 0300 4300 3a00 5c00 6000 t.x.t...C.:.\.`.

我们在记录内部可以看到文件中的文本。当我们保存文件之后,记录中我们一样可以看到文件和磁盘名"NTFS Disk 2.",我修改字符串一样包含"This is my update.."这个数据晚点将在日志文件:
2248704: 5243 5244 2800 0900 f24b 9403 0000 0000 RCRD(....K......
[REMOVED]
2250800: ec12 0000 0000 0000 a14d 0500 0000 0000 .........M......
2250816: 2020 5468 6973 2069 7320 6d79 2075 7064 This is my upd
2250832: 6174 652e 0000 0000 0000 0000 0000 0000 ate.............
2250848: 0000 0000 0000 0000 0000 0000 0000 0000 ................

这个记录仅仅包含新的内容,不包含正在更新的文件的路径名。此记录中可能有一个引用指向标识文件名的另一个记录。

$UsrJrnl File

更新日志属于应用程序类别,并在对文件进行更改时记录。这些更改记录在文件\$Extend\$UsrJrnl名叫$J的$DATA属性中,并不在保留的MFT entry。$J的$DATA属性是稀疏的,并且它包含一个不同大小的数据结构列表,称为更改日志条目。还有一个名为$Max的$DATA属性,包含关于用户日志的最大设置的信息。
像我们在第12章中讨论,对于存储和检索数据的文件系统目标而言,这些数据并不是必须的。因此,此表中的"Essential"列指数据对于提供文件更改日志的目标是否必不可少。$J中entries的数据结构具有表13.27中给出的字段。
    表 13.27. $UsrJrnl 中的 $J 属性条目的数据结构。
    Byte Range    Description                                 Essential
    0–3         Size of this journal entry                         Yes
    4–5         Major version                                 Yes
    6–7         Minor version                                 Yes
    8–15         File reference of file that caused this entry                 Yes
    16–23         Parent directory file reference for file that caused this entry        No
    24–31         USN for entry                                 Yes
    32–39         Timestamp                                 Yes
    40–43         Flags for type of change (see Table 13.28)                 Yes
    44–47         Source information                             No
    48–51         Security ID (SID)                             No
    52–55         File attributes                             No
    56–57         Size of file name                             Yes
    58+         File name                                 Yes

字节40到43包含改变日志条目的理由。这个字段设置标志位,并可以一个或者多个理由。表13.28中是这些数值的定义。
    表 13.28. $J 条目中更改类型字段数值。
    Value         Description
    0x00000001    The default $DATA attribute was overwritten
    0x00000002    The default $DATA attribute was extended
    0x00000004    The default $DATA attribute was truncated
    0x00000010    A named $DATA attribute was overwritten
    0x00000020    A named $DATA attribute was extended
    0x00000040    A named $DATA attribute was truncated
    0x00000100    The file or directory was created
    0x00000200    The file or directory was deleted
    0x00000400    The extended attributes of the file were changed
    0x00000800    The security descriptor was changed
    0x00001000    The name changed—change journal entry has old name
    0x00002000    The name changed—change journal entry has new name
    0x00004000    Content indexed status changed
    0x00008000    Changed basic file or directory attributes
    0x00010000    A hard link was created or deleted
    0x00020000    Compression status changed
    0x00040000    Encryption status changed
    0x00080000    Object ID changed
    0x00100000    Reparse point value changed
    0x00200000    A named $DATA attribute was created, deleted, or changed
    0x80000000    The file or directory was closed

源数值在字节44到47通常是0,但如果操作系统造成而不是用户则可以位非0。让我们查看一个小型的更改日志。为了找到日志的MFT entry,我们检查文件系统\$Extend元数据内容目录,即MFT entry 11。
# fls -f ntfs ntfs3.dd 11
r/r 25-144-2: $ObjId:$O
r/r 24-144-3: $Quota:$O
r/r 24-144-2: $Quota:$Q
r/r 26-144-2: $Reparse:$R
r/r 27-128-3: $UsnJrnl:$J
r/r 27-128-4: $UsnJrnl:$Max

我们看到它是MFT entry 27。我们用"icat"来显示$J 属性内容:
# icat –f ntfs ntfs3.dd 27-128-3 | xxd
0000000: 5000 0000 0200 0000 1c00 0000 0000 0100 P...............
0000016: 0500 0000 0000 0500 0000 0000 0000 0000 ................
0000032: 3000 e2b9 eb69 c401 0001 0000 0000 0000 0....i..........
0000048: 0201 0000 2000 0000 1400 3c00 6600 6900 .... .....<.f.i.
0000064: 6c00 6500 2d00 3000 2e00 7400 7800 7400 l.e.-.0...t.x.t.

前4个字节告诉我们这个更改日志entry大小80字节,字节8到13告诉我们改entry是针对MFT entry 28(0x1c)的。字节24到31告诉我们这个entry的USN为0N,字节40到43显示原因标志是0x00000001,即用来覆盖默认$DATA属性。最后我们看到这个entry是针对文件"file-0.txt"的。$Max属性包含常规更改日志管理信息。它的字段在下面的表13.29给出。
    表 13.29 $UsrJrnl中的$Max属性的数据结构。
    Byte Range     Description         Essential
    0–7         Maximum size         Yes
    8–15         Allocation size     Yes
    16–23         USN ID             Yes
    24–31         Lowest USN         Yes

这个信息像对研究没有帮助,但为了完整起见,$Max属性的内容如下:
# icat –f ntfs ntfs3.dd 27-128-4 | xxd
0000000: 0000 8000 0000 0000 0000 1000 0000 0000 ................
0000016: 4057 7491 eb69 c401 0000 0000 0000 0000 @Wt..i..........

总结

NTFS中有很多数据结构和指针,让手动分析困难起来。在本章中,我们研究了已知的常见数据结构。应该强调的是它们并非来自官方规范,但已经证明它们是可靠的。可能有一些尚未看过的数值或者标志选项。

参考书目

Linux NTFS Project. NTFS Documentation, 1996-2004. http://linuxntfs.sourceforge.net/ntfs/index.html.
Solomon, David and Mark Russinovich. Inside Windows 2000. 3rd ed. Redmond: Microsoft
Press, 2000.
See also the Bibliography section of Chapter 11.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值