对于NTFS文件系统而言,无论文件内容本身,抑或真实的文件属性,都被称之为属性。
而正如前文说到的,NTFS预定义了16种属性用于文件系统的管理。
而通常情况下,往往只需要关注其中的某些属性即可。
为了便于管理,NTFS文件系统为所有的属性定义了统一的头部结构,可以称之为属性头。
而由于每种属性的长度不一,因此又额外定义了常驻属性和非常驻属性。
// 长度:0x10
typedef struct {
b32 attr_type; // 当前属性类型
b32 length; // 属性长度
b8 non_resident; // 常驻属性标识:0 - 常驻属性, 1 - 非常驻属性
b8 name_length; // 属性名长度:0 - 无名属性
ub16 name_offset; // 属性名偏移:0x18
ub16 flags; //
ub16 attr_id; //
} AttributeHeader;
// 长度:0x8 + 0x10
typedef struct {
AttributeHeader header; // 属性头
b32 length; // 属性体长度
ub16 offset; // 属性偏移
b8 index_flag; // Indexed flag
b8 padding; // Padding
} ResidentAttrHeader;
// 长度:0x30 + 0x10
typedef struct {
AttributeHeader header; // 属性头
b64 vcn_begin; // 起始VCN号
b64 vcn_end; // 结束VCN号
ub16 data_run_offset; // Data Runs偏移
ub16 compression_size; // Compression unit size
b32 padding; // Padding
b64 byte_alloc; // 分配大小
b64 byte_use; // 实际使用大小
b64 init_size; // Initialized data size of the stream
} NonResidentAttrHeader;
常驻属性。通常情况下我们可以通过本属性获取文件时间和文件访问属性。
// 文件属性
typedef enum {
READONLY = 0x0001,
HIDDEN = 0x0002,
SYSTEM = 0x0004,
ARCHIVE = 0x0020,
DEVICE = 0x0040,
NORMAL = 0x0080,
TEMPORARY = 0x0100,
SPARSEFILE = 0x0200,
REPARES_Point = 0x0400,
COMPRESSED = 0x0800,
OFFLINE = 0x1000,
NOT_CONTENT_INDEXED = 0x2000,
ENCRYPTED = 0x4000,
DIRECTORY = 0x10000000, //(copy from corresponding bit in MFT record)
INDEX_VIEW = 0x20000000, //(copy from corresponding bit in MFT record)
} FileAttrFlags;
// STANDARD_INFORMATION = 0x10,
typedef struct {
b64 time_create; // 文件创建时间
b64 time_update; // 文件修改时间
b64 time_mft_change; // 文件记录修改时间
b64 time_access; // 文件访问时间
b32 file_attribute; // 文件属性
b32 max_version; // 文件最大版本
b32 version; // 当前文件版本
b32 class_id; // Class Id
b32 owner_id; // Owner Id
b32 security_id; // Security Id
b64 quota_charged; // Quota charged
b64 usn; // USN Journel
} StdInformation;
常驻/非常驻属性。
当一个文件或者文件夹存在多个文件记录时,主文件记录中会生成一个或多个ATTRIBUTE_LIST属性。在这种情况下,需要通过遍历ATTRIBUTE_LIST属性读取base_record_ref对应的文件记录。
// ATTRIBUTE_LIST = 0x20,
typedef struct {
b32 attr_type; // Attribute Type
ub16 record_length;
b8 name_length;
b8 name_offset;
b64 start_vcn;
b64 base_record_ref; // 文件记录号
ub16 attr_id;
} AttrList;
常驻属性。通常情况下,一个文件只存在一个FILE_NAME属性,这种情况下,文件的基本属性与STANDARD_INFORMATION是保持一致的。
但是当文件系统结构变化时,如上图所示。或者当文件产生硬链接时,FILE_NAME属性都会增加。这种情况下,就需要对本属性做特殊处理。
// 文件名命名空间
typedef enum {
POSIX_STYLE = 0,
WIN32_STYLE,
DOS_STYLE,
WIN_DOS_STYLE,
} FileNameSpace;
// FILE_NAME = 0x30,
typedef struct {
b64 parent_ref; // 低6位存储当前文件记录号
b64 time_create; // 文件创建时间
b64 time_update; // 文件修改时间
b64 time_mft_change; // 文件记录修改时间
b64 time_access; // 文件访问时间
b64 byte_alloc; // 分配大小
b64 byte_use; // 实际使用大小
b32 flags; // 文件属性
b32 ea_flags; // 文件EA属性
ub8 filename_length; // 文件名长度(单位):字符
b8 name_space; // 命名空间
} FileName;
常驻属性。通常情况下,卷信息属性只存储在Volume中。不需要特殊处理
// VOLUME_INFORMATION = 0x70,
typedef struct {
b8 resv1[8]; // 00
b8 major_ver; // major version 1--winNT, 3--Win2000/XP
b8 minor_ver; // minor version 0--win2000, 1--WinXP/7
ub16 flag; // mark
b8 resv2[4]; // 00
} VolumeInformation;
常驻/非常驻属性。DATA属性用于存储文件本身的数据。当文件内容较少时,数据直接存储在DATA中。
当数据长度超过一定时,DATA中存储Data Runs,数据本身则异地存储。
注意:文件内容数据只存储在有名DATA属性中,因此通过DATA读取文件内容时需要对name_length进行判断。
常驻属性。索引根节点通常由标准属性头,索引根属性头,索引属性头和索引属性组成。
索引根属性头决定当前块存储的索引类型。通常情况下,索引中存储的都是0x30文件名属性。
正如上图所示,绿色部分代表IndexEntry的头部,紧接着的红色部分和黄色部分就是去除了标准属性头之后的0x30属性,而最后的紫色部分则存储着子节点的VCN号。
typedef enum {
ENTRY_SUBNODE = 1, // 存在子节点
ENTRY_LAST = 2, // 叶子节点
} IdxEntryTypes;
// INDEX_ROOT = 0x90,
typedef struct {
// Index Root Header
b32 attr_type; // 属性类型
b32 collation_rule;
b32 index_size; // 索引块大小
b8 clusters_per_index; // Clusters per index block (same as Bpb?)
b8 padding[3]; // Padding
// Index Header
b32 entry_offset; // 第一个索引的偏移
b32 total_size; // 索引数据的总大小
b32 alloc_size; // Allocated size of the index entries
b8 flags; // 索引标志
b8 padding2[3]; // Padding
} IndexRoot;
typedef struct {
b64 mft_ref;
ub16 size; // 索引大小
ub16 stream_size; // 数据流长度
b8 flags; // 索引标志
b8 padding[3]; // Padding
// copy of body(without header) of attribute
/* Name | Index Of | Used By
* --------------------------------------
* $I30 | Filenames | 目录
* $SDH | Security Descriptors | $Secure
* $SII | Security Ids | $Secure
* $O | Object Ids | $ObjId
* $O | Owner Ids | $Quota
* $Q | Quotas | $Quota
* $R | Reparse Points | $Reparse
*/
b8 stream[1]; // align to 8
// VCN of the sub-node in the Index Allocation
} IndexEntry;
非常驻属性。INDEX_ALLOCATION的结构与INDEX_ROOT基本一致。唯一的差别在于一个是常驻属性,另一个是非常驻属性。
typedef struct {
// Index Record Header
b32 magic; // "INDX"
ub16 usn_offset; // offset of update sequence number
ub16 usn_size; // size of update sequence number and array, by words
b64 lsn; // $LogFile sequence number
b64 vcn; // vcn of this index block in the index allocation
// Index Header
b32 entry_offset; // Offset to the first index entry
b32 total_size; // Total size of the index entries
b32 alloc_size; // Allocated size of the index entries
b8 flags; // Non-leaf node Flag
b8 padding[3]; // Padding
} IndexBlock;
typedef struct {
b64 mft_ref;
ub16 size; // 索引大小
ub16 stream_size; // 数据流长度
b8 flags; // 索引标志
b8 padding[3]; // Padding
// copy of body(without header) of attribute
/* Name | Index Of | Used By
* --------------------------------------
* $I30 | Filenames | 目录
* $SDH | Security Descriptors | $Secure
* $SII | Security Ids | $Secure
* $O | Object Ids | $ObjId
* $O | Owner Ids | $Quota
* $Q | Quotas | $Quota
* $R | Reparse Points | $Reparse
*/
b8 stream[1]; // align to 8
// VCN of the sub-node in the Index Allocation
} IndexEntry;