文件记录的结构
MFT以文件记录来实现对文件的管理,每个文件记录都对应着不同的文件,大小固定为1KB,也就是占用两个扇区,而不管簇的大小是多少。如果一个文件有很多属性或是分散成很多碎片,就很可能需要多个文件记录。这时,存放其文件记录位置的第一个记录就称作“基本文件记录”。文件记录在MFT中物理上是连续的,从0开始依次按顺序编号。
文件记录由两部分构成,一部分是文件记录头,另一部分是属性列表,其结构见表4-31。
表4-31 文件记录的结构
结构 | 说明 |
文件记录头 | |
属性1 | |
属性2 | |
…… | |
结束标志 | “FFFFFFFFH” |
通过WinHex查看一下$MFT文件的文件记录,结构如图4-404所示。
图4-404 $MFT文件的文件记录的结构
文件记录头的结构
在同一系统中,文件记录头的长度和具体偏移位置的数据含义是不变的,而属性列表是可变的,其不同的属性有着不同的含义。后文将对属性进行具体分析,先来看看文件记录头的信息,图4-405中阴影部分为一个文件记录的记录头信息。
图4-405 NTFS的文件记录头
NTFS文件记录头信息的含义见表4-32。
表4-32 NTFS文件记录头信息
字节偏移 | 字段长度(字节) | 字段名和含义 |
0x00 | 4 | MFT标志,一定为字符串“FILE” |
0x04 | 2 | 更新序列号(Update Sequence Number)的偏移 |
0x06 | 2 | 更新序列号的大小与数组,包括第一个字节 |
0x08 | 8 | 日志文件序列号($LogFile Sequence Number,LSN) |
0x10 | 2 | 序列号(Sequence Number) |
0x12 | 2 | 硬连接数(Hard Link Count),即有多少目录指向该文件 |
0x14 | 2 | 第一个属性的偏移地址 |
0x16 | 2 | 标志(Flag),00H表示文件被删除,01H表示文件正在使用,02H表示目录被删除,03H表示目录正在使用 |
0x18 | 4 | 文件记录的实际长度 |
0x1C | 4 | 文件记录的分配长度 |
0x20 | 8 | 基本文件记录中的文件索引号 |
0x28 | 2 | 下一属性ID,当增加新的属性时,将该值分配给新属性,然后该值增加,如果MFT记录重新使用,则将它置0,第一个实例总是0 |
0x2A | 2 | 边界,Windows XP中为偏移0x30处 |
0x2C | 4 | 文件记录参考号,Windows XP中使用,Windows 2000中无此参数 |
0x30 | 2 | 更新序列号 |
0x32 | 4 | 更新数组 |
具体说明如下:
偏移00H~03H为MFT的标志字符串,它总为“FILE”。
每次记录被修改都将导致偏移08H~09H处的日志文件序列号$LogFile Sequence Number(LSN)发生变化。
偏移10H~11H处序列号Sequence Number(SN)用于记录主文件表记录被重复使用的次数。
偏移12H~13H处为硬连接数记录硬连接的数目,只出现在基本文件记录中。
偏移18H~1BH处文件记录的实际长度也即文件记录在磁盘上实际占用的字节空间。
偏移1CH~1FH为系统分配给文件记录的长度,一般为“00 04 00 00”,也就是1KB的长度。
偏移20H~27H处为基本文件记录中的文件索引号,基本文件记录在此的值总为0。如果不为0,则是一个主文件表的文件索引号,指向所属的基本文件记录中的文件记录号。在基本文件记录中包含有扩展文件记录的信息,存储在“属性列表ATTRIBUTE_LIST”属性中。
偏移2CH~2FH处为文件记录编号,从0开始编号。
偏移30H~31H处为更新序列号,这两个字节同时会出现在该文件记录第一个扇区最后两个字节处及该文件记录第二个扇区最后两个字节处,如图4-406和4-407所示。
图4-406 文件记录第一个扇区
图4-407 文件记录第二个扇区
偏移32H~35H处为更新数组,这4个字节很重要,它的作用如下:
因为文件记录中“更新序列号”的两个字节同时会出现在该文件记录第一个扇区最后两个字节处及该文件记录第二个扇区最后两个字节处,也就是事先占用了文件记录的4个字节,当文件记录中的信息需要往这4个字节处写入时,就跳转到了32H~35H处来写。具体对应关系是:文件记录第一个扇区最后两个字节对应32H~33H处,文件记录第二个扇区最后两个字节对应34H~35H处,如图4-408所示。
图4-408 更新数组的对应关系
当信息还没有写入该文件记录第一个扇区最后两个字节处及该文件记录第二个扇区最后两个字节处时,更新数组处的4个字节就为0。
文件记录头的参数也可以使用WinHex中的模板来查看,WinHex的模板管理器中提供文件记录的模板。打开WinHex的模板管理器,选择NTFS文件记录的模板,如图4-409所示。
图4-409 模板管理器
图4-409中模板管理器的“①、②、③”三个模板都是NTFS文件记录的模板,其中“①”是WinHex自带的,但对参数的解释不是太全面;“②”是笔者在“①”的基础之上做了些完善;“③”是笔者将“②”做了汉化,翻译成中文了,方便使用。现在就打开“③”来查看NTFS的文件记录头,如图4-410所示。
图4-410 NTFS的文件记录头模板
文件记录中属性的结构
属性的类型
前面说过,文件记录由两部分构成,一部分是文件记录头,另一部分是属性列表。在NTFS系统中所有与文件相关的数据均被认为是属性,包括文件的内容。文件记录是一个与文件相对应的文件属性数据库,它记录了文件数据的所有属性。
每个文件记录中都有多个属性,它们相对独立,有各自的类型和名称。一个属性的偏移00H~03H处的4个字节,为该属性的类型标志,不同的属性其结构和含义各不相同。表4-33为属性类型及其含义。
表4-33 属性类型及其含义
属性类型(Little-Endian) | 属性类型名 | 属性描述 |
10 00 00 00 | $STANDARD_INFORMATION | 标准信息:包括一些基本文件属性,如只读、系统、存档;时间属性,如文件的创建时间和最后修改时间;有多少目录指向该文件(即其硬连接数(hard link count)) |
20 00 00 00 | $ATTRIBUTE_LIST | 属性列表:当一个文件需要多个文件记录时,用来描述文件的属性列表 |
30 00 00 00 | $FILE_NAME | 文件名:用Unicode字符表示的文件名,由于MS-DOS不能识别长文件名,所以NTFS系统会自动生成一个8.3文件名 |
40 00 00 00 | $VOLUME_VERSION | 在早期的NTFS v1.2中为卷版本 |
40 00 00 00 | $OBJECT_ID | 对象ID:一个具有64字节的标识符,其中最低的16字节对卷来说是唯一的(链接跟踪服务为外壳快捷方式,即OLE链接源文件赋予对象ID;NTFS提供的API是直接通过这些对象的ID而不是文件名来打开文件的) |
50 00 00 00 | $SECURITY_DESCRIPTOR | 安全描述符:这是为向后兼容而保留的,主要用于保护文件以防止没有授权的访问,但Windows 2000/XP中已将安全描述符存放在$Secure元数据中,以便于共享(早期的NTFS将其与文件目录一起存放,不便于共享) |
60 00 00 00 | $VOLUME_NAME | 卷名(卷标识):该属性仅存在于$Volume元文件中 |
70 00 00 00 | $VOLUME_INFORMATION | 卷信息:该属性仅存在于$Volume元文件中 |
80 00 00 00 | $DATA | 文件数据:该属性为文件的数据内容 |
90 00 00 00 | $INDEX_ROOT | 索引根 |
A0 00 00 00 | $INDEX_ALLOCATION | 索引分配 |
B0 00 00 00 | $BITMAP | 位图 |
C0 00 00 00 | $SYMBOLIC_LINK | 在早期的NTFS v1.2中为符号链接 |
C0 00 00 00 | $REPARSE_POINT | 重解析点 |
D0 00 00 00 | $EA_INFORMATION | 扩充属性信息 |
E0 00 00 00 | $EA | 扩充属性 |
F0 00 00 00 | $PROPERTY_SET | 早期的NTFS v1.2中才有 |
00 10 00 00 | $LOGGED_UTILITY_STREAM | EFS加密属性:该属性主要用于存储实现EFS加密的有关加密信息,如合法用户列表、解码密钥等 |
每一个属性都可以分为两个部分:属性头和属性体。这里以$MFT文件自身的文件记录中的10H属性为例,其结构如图4-411所示。
图4-411 属性的属性头和属性体
另外属性还有常驻与非常驻之分。当一个文件很小时,其所有属性体都可存放在MFT的文件记录中,该属性就称为常驻属性(Resident Attribute)。有些属性总是常驻的,这样NTFS才可以确定其他非常驻属性,例如,标准信息属性和根索引就总是常驻属性。
如果属性体能直接存放在MFT中,那么NTFS对它的访问时间就将大大缩短,系统只需访问磁盘一次,就可立即获得数据,而不必像FAT文件系统那样,先在FAT表中查找文件位置,再读出连续分配的簇,最后找到文件数据。
如果一个属性的属性体太大而不能存放在只有1KB大小的MFT文件记录中,那么,系统将从MFT之外为之分配区域。这些区域通常称为Data Run(数据流),它们可用来存储属性体。如果属性体是不连续的,NTFS将会分配多个Data Run,以便用来管理不连续的数据。这种属性体存储在Data Run中而不是在MFT文件记录中的属性称为非常驻属性(Nonresident Attribute)。
属性的属性头
每个属性都有一个属性头,这个属性头包含了一些该属性的重要信息,如属性类型、属性大小、名字(并非都有)及是否为常驻属性等。
一个属性根据其是否常驻和是否有属性名,可以排列组合成四种不同的情况,分别为:常驻没有属性名、常驻有属性名、非常驻没有属性名、非常驻有属性名,下面分别分析它们的属性头。
①常驻没有属性名的属性头结构。
常驻且没有属性名的属性头结构见表4-34。
表4-34 常驻没有属性名的属性头结构
字节偏移 | 字段长度(字节) | 含义 |
0x00 | 4 | 属性类型(如10H、30H等类型) |
0x04 | 4 | 包括属性头在内的本属性的长度(字节) |
0x08 | 1 | 是否为常驻属性(00表示常驻,01H表示非常驻) |
0x09 | 1 | 属性名长度(为0表示没有属性名) |
0x0A | 2 | 属性名的开始偏移(没有属性名) |
0x0C | 2 | 压缩、加密、稀疏标志:为0001H表示该属性是被压缩了的;为4000H表示该属性是被加密了的;为8000H表示该属性是稀疏的[NTFS下压缩文件的第二种压缩类型被称为Sparse File(稀疏文件)] |
0x0E | 2 | 属性ID |
0x10 | 4 | 属性体的长度(L) |
0x14 | 2 | 属性体的开始偏移 |
0x16 | 1 | 索引标志 |
0x17 | 1 | 无意义 |
0x18 | L | 该属性体的内容 |
下面以图4-411中的10H属性为例子来分析属性头:
偏移00H~03H的4个字节为十六进制值“10 00 00 00”,表示此属性是10H类型的属性,即$STANDARD_INFORMATION属性,称为标准信息属性;
偏移04H~07H的4个字节为包括属性头在内的属性长度,这里为十六进制值“60 00 00 00”,也就是说该属性总长度为60H;
偏移08H为非常驻属性标志,“00”表示为常驻属性,10H类型的总是常驻的,所以该字节总为00H;
偏移09H为属性名的长度,此处为十六进制数“00”,表示该属性没有属性名;
偏移0AH~0BH的两个字节为该属性名开始的偏移,此处为十六进制数“18 00”,对于没有属性名的属性,该值无意义;
偏移0CH~0DH的两个字节为属性标志,表示此属性是否是压缩、加密等,为“0000H”表示不是压缩、加密属性,只有非常驻的80H属性(数据属性)此处才可能非0000H;
偏移0EH~0FH为属性ID,此处为十六进制数“00 00”;
偏移10H~13H为属性体长度,此处为十六进制数“48 00 00 00”,也就是说属性体长度为48H;
偏移14H~15H为该属性体开始的偏移,此处为十六进制数“18 00”,表示属性体开始的偏移为18H,这也是属性头的长度;
偏移16H为索引标志,此处为“00”;
偏移17H并没有实际意义,此处为“00”;
偏移18H~98H的48H个字节是属性体的内容,其具体含义将在后文中分析。
这部分参数也可以通过文件记录的模板来查看,如图4-412所示。
图4-412 10H属性的属性头部分
②常驻有属性名的属性头结构。
常驻有属性名的属性头结构见表4-35。
表4-35 常驻有属性名的属性头结构
字节偏移 | 字段长度(字节) | 含义 |
0x00 | 4 | 属性类型(如90H、B0H等类型) |
0x04 | 4 | 包括属性头在内的本属性的长度(字节) |
0x08 | 1 | 是否为常驻属性(00表示常驻,01H表示非常驻) |
0x09 | 1 | 属性名长度(N) |
0x0A | 2 | 属性名开始的偏移 |
0x0C | 2 | 压缩、加密、稀疏标志 |
0x0E | 2 | 属性ID |
0x10 | 4 | 属性体的长度(L) |
0x14 | 2 | 属性体的开始偏移 |
0x16 | 1 | 索引标志 |
0x17 | 1 | 无意义 |
0x18 | 2N | 属性的名字 |
2N+0x18 | L | 属性体的内容 |
③非常驻没有属性名的属性头结构。
非常驻没有属性名的属性头结构见表4-36。
表4-36 非常驻没有属性名的属性头结构
字节偏移 | 字段长度(字节) | 含义 |
0x00 | 4 | 属性类型(如20H、80H等类型) |
0x04 | 4 | 包括属性头在内的本属性的长度(字节) |
0x08 | 1 | 是否为常驻属性(为01表示该属性为非常驻属性) |
0x09 | 1 | 属性名长度(为0表示没有属性名) |
0x0A | 2 | 属性名开始的偏移(没有属性名) |
0x0C | 2 | 压缩、加密、稀疏标志 |
0x0E | 2 | 属性ID |
0x10 | 8 | 属性体的起始虚拟簇号(VCN) |
0x18 | 8 | 属性体的结束虚拟簇号 |
0x20 | 2 | Run List(Run即Data Run,是一个在逻辑簇号上连续的区域,它是不存储在MFT中的数据)信息的偏移地址 |
0x22 | 2 | 压缩单位大小(2x簇,如果为0表示未压缩) |
0x24 | 4 | 无意义 |
0x28 | 8 | 属性体的分配大小[该属性体占的大小,这个属性体大小是该属性体所有的簇所占的空间大小(字节)] |
0x30 | 8 | 属性体的实际大小(因为属性体长度不一定正好占满所有簇) |
0x38 | 8 | 属性体的初始大小 |
0x40 | 属性的Run List信息,它记录了属性体开始的簇号、簇数等信息 |
④非常驻有属性名的属性头结构。
非常驻有属性名的属性头结构见表4-37。
表4-37 非常驻有属性名的属性头结构
字节偏移 | 字段长度(字节) | 含义 |
0x00 | 4 | 属性类型(如80H、A0H等类型) |
0x04 | 4 | 包括属性头在内的本属性的长度(字节) |
0x08 | 1 | 是否为常驻属性(为01表示该属性为非常驻属性) |
0x09 | 1 | 属性名长度(N) |
0x0A | 2 | 属性名开始的偏移 |
0x0C | 2 | 压缩、加密、稀疏标志 |
0x0E | 2 | 属性ID |
0x10 | 8 | 属性体的起始虚拟簇号(VCN) |
0x18 | 8 | 属性体的结束虚拟簇号 |
0x20 | 2 | Run List(Run即Data Run,是一个在逻辑簇号上连续的区域,它是不存储在MFT中的数据)信息的偏移地址 |
0x22 | 2 | 压缩单位大小(2x簇,如果为0表示未压缩) |
0x24 | 4 | 无意义 |
0x28 | 8 | 属性体的分配大小[该属性体占的大小,这个属性体大小是该属性体所有的簇所占的空间大小(字节)] |
0x30 | 8 | 属性体的实际大小(因为属性体长度不一定正好占满所有簇) |
0x38 | 8 | 属性体的初始大小 |
0x40 | 2N | 该属性的属性名 |
2N+0x40 | 属性的Run List信息,它记录了属性体开始的簇号、簇数等信息 |
以上介绍了属性中属性头的结构,属性头之后就是属性体,随后的章节中将对各种类型属性的属性体进行介绍。