mft常驻属性是直接存储在mft记录体内的小字段(如文件名、时间戳),其nonresident标志为0,数据紧随attribute_header之后;非常驻属性则仅存指针。用户态程序无法直接读取运行中卷的mft,因ntfs驱动内核层硬拦截所有对$mft等元数据的i/o请求,返回status_access_denied。

什么是MFT常驻属性,为什么不能直接读
NTFS的MFT记录里,小字段(比如文件名、时间戳、标准信息)会直接存进记录体内部,叫「常驻属性」;大字段(比如文件数据、大索引)则只存指针,数据另放别处,叫「非常驻属性」。但用户态程序根本没法直接读MFT——\.C:打开物理卷后调用ReadFile去读MFT起始扇区,系统会立刻返回STATUS_ACCESS_DENIED(错误码5)。这不是权限不够,是NTFS驱动在内核层硬拦截所有对元数据区域(包括$MFT、$Bitmap)的I/O请求。
想解析常驻属性,得先拿到合法MFT镜像
真实开发中能走的路只有一条:不碰实时卷,改用离线MFT镜像。比如用dd或WinHex从已卸载的NTFS卷上完整复制出MFT原始扇区数据(通常前几个扇区+连续几百KB),保存为mft.raw。之后才能在用户态安全解析:
- 确认镜像开头是MFT记录签名
FILE0(ASCII,偏移0x00) - 每条MFT记录固定大小(通常是1024字节),从偏移0开始逐条解析
- 记录头后紧跟属性列表,每个属性以
ATTRIBUTE_HEADER结构开头,含类型(如0x30为$FILE_NAME)、长度、是否常驻等标志 - 常驻属性的
Nonresident字段为0,数据紧接在header之后,ValueOffset和ValueLength告诉你从哪开始读、读多少字节
解析$STANDARD_INFORMATION和$FILE_NAME的关键点
这两个是最常用的常驻属性,但字段布局依赖NTFS版本和记录是否被重用:
-
$STANDARD_INFORMATION(类型0x10)含创建/修改时间、文件属性(只读、隐藏等)、硬链接数。注意:其Flags字段低4位才是真实属性,高位可能被系统复用 -
$FILE_NAME(类型0x30)含文件名、父目录MFT引用号、名字长度(Unicode字符数,不是字节数)。名字编码是UTF-16LE,末尾无 ,必须按NameLength截取 - 同一MFT记录里可能有多个
$FILE_NAME属性(长名+短名),需遍历属性列表全扫一遍,不能只取第一个 - 若属性
Resident为0但ValueLength非0,说明解析逻辑错——常驻属性值必须紧贴header存放
容易被忽略的边界情况
实际解析时最常栽在这些细节上:
- MFT记录可能被标记为“已删除”(header中
Flags第0位为0),此时$FILE_NAME里的名字可能是脏数据,得结合$STANDARD_INFORMATION的CreationTime是否为0来交叉验证 - 某些系统(如Windows 10 20H1后)会在MFT记录末尾填充
0x00或0xFF用于对齐,别把填充字节误判成下一个属性的header -
$FILE_NAME中的ParentReference是8字节:低6字节为父目录MFT记录号,高2字节为序列号,提取时务必用le64toh()或手动拼接,别直接reinterpret_cast - 没有公开的Windows API能帮你解析MFT二进制结构——
FSCTL_READ_MFT_RECORD这个IOCTL根本不存在于winioctl.h,任何声称“调用系统API直接解析”的代码都是错的
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











