当前位置: 首页 > news >正文

Qt/C++ 了解NTFS文件系统,解析盘符引导扇区数据获取MFT(Master File Table)主文件表偏移地址

系列文章目录

一、Qt/C++ 了解NTFS文件系统,了解MFT(Master File Table)主文件表(一)
二、Qt/C++ 了解NTFS文件系统,解析盘符引导扇区数据获取MFT(Master File Table)主文件表偏移地址
三、Qt/C++ 了解NTFS文件系统,获取首张MFT表数据,解析文件记录头内容找到第一个属性偏移地址


目录导读

  • 系列文章目录
  • 前言
  • BOOTICE工具介绍
  • 读取分区引导扇区(PBS)


前言

根据NT File System (NTFS) 一文中对引导扇区数据结构的分析,结合前文介绍的GitHub开源项目 NTFS-File-Search中的NTFS_BOOT_SECTOR结构体设计,使用Bootice工具查看分区引导扇区实际数据,计算首张MFT(Master File Table)主文件表在磁盘的偏移地址。

BOOTICE工具介绍

BOOTICE 引导修复工具是款USM的启动相关维护的工具。用于编辑修改磁盘上的引导扇区的信息,也就是MBR,BOOTICE可在磁盘(硬盘、移动硬盘、U盘、SD卡等)上安装磁盘引导程序。BOOTICE引导修复工具此外还具有磁盘扇区编辑、磁盘填充、分区管理等等功能。BOOTICE有着强大的兼容性,在硬盘维护中起来了很大的作用。
BOOTICE支持的引导程序主要有 WEE, GRUB4DOS, SYSLINUX, Plop BootManager, Ms-Dos 及 Windows NT 5/6 等。BOOTICE 还具有分区管理、扇区查看以及对 USB 移动存储设备进行重新格式化的功能。
具体参考 BOOTICE 1.3.4.0最新版

这里注意用于查看磁盘扇区数据,进行数据字节对照,
其中显示的数值都是16进制数据,以C盘目录为例:
在这里插入图片描述


读取分区引导扇区(PBS)

分区引导扇区是从NTFS文件系统格式 分区 的第一个扇区,

只有盘符是NTFS文件系统格式才能通过MFT表获取数据,
其他FAT,FAT32,REFS文件格式有其他的读取方式

如C盘的第一个扇区,以上面显示的C盘数据为例,
引导扇区在第878592个扇区。
读取整段扇区数据,到55 AA结束,一般都是512字节大小,
解析第一个扇区(引导扇区)数据,获取NTFS文件系统中第一个MFT表对应起始地址。

  • NTFS引导扇区内容结构:

表格出自Windows NT文件系统(NT File System)[使用QQ浏览器翻译]

字节偏移量段长度平均数字段名目的
0x003字节0xEB5290x86JMP和nototherwiseprovided(for)除非另有规定说明 导致在该引导扇区中的数据结构之后继续执行。
0x038字节"NTFS    "单词“NTFS”后跟四个尾随空格(0x20)OEM ID这是一个神奇的数字,表明这是一个NTFS文件系统。
0x0B2字节0x0200BPB每扇区字节数磁盘扇区中的字节数。
0x0D1字节0x08BPB每簇扇区簇中的扇区数量。如果该值大于0x80,则扇区数是2的绝对值的幂,认为该字段为负。
0x0E2字节0x0000BPB未使用的保留扇区
0x103字节0x000000BPB不用的该字段始终为0
0x132字节0x0000BPBNTFS未使用该字段始终为0
0x151字节0xF8BPB媒体描述符驱动器的类型。0xF8用于表示硬盘驱动器(与几种大小的软盘不同)。
0x162字节0x0000BPB不用的该字段始终为0
0x182字节0x003FBPB每个磁道的扇区驱动器磁道中的磁盘扇区数量。
0x1A2字节0x00FFBPB头数驱动器上的磁头数。
0x1C4字节0x000D6800BPB隐藏扇区分区前的扇区数量。
0x204字节0x00000000BPB不用的NTFS不使用
0x244字节0x00800080EBPB不用的NTFS不使用
0x288字节0x000000000C8007FFEBPB总部门扇区中的分区大小。
0x308字节0x00000000000C0000EBPB$MFT集群号包含主文件表的群
0x388字节0x0000000000000002EBPB$MFTMirr群集号包含主文件表备份的群集
0x401字节0xF6EBPB每个文件记录段的字节或簇正值表示文件记录段中簇的数量。负值表示文件记录段中的字节数,在这种情况下,大小是绝对值的2次方。(0xF6 = -10 → 210 = 1024).
0x413字节0x000000EBPB不用的NTFS不使用此字段
0x441字节0x01EBPB每个索引缓冲区的字节或簇正值表示索引缓冲区中的簇数量。负值表示字节数,它对负数使用与“每个文件记录段的字节数或簇数”相同的算法
0x453字节0x000000EBPB不用的NTFS不使用此字段
0x488字节0x86EEA7DEEEA7C4B1EBPB卷序列号分配给该分区的唯一随机数,以保持有序。
0x504字节0x00000000校验和,未使用应该是校验和。
0x54426字节引导代码加载操作系统其余部分的代码。这由该扇区的前3个字节指向。
0x01FE2字节0xAA55扇区结束标记 此标志表示这是一个有效的引导扇区。

需要注意的是:磁盘扇区数据中除字符串外的所有值都以小端序(little endian)存储,如
磁盘大小为0x000000000C8007FF字节
但是数据格式是按FF 07 80 0C 00 00 00 00顺序存储

大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。
参考:
Big-endian and Little-endian (大小端)
字节序:Big Endian 和 Little Endian

  • 查看实际扇区数据:

使用BOOTICE工具查看磁盘扇区实际数据,并按上面表格占用字节划分标注;
请添加图片描述
结合引导分区结构表格与盘符数据进行对照,就可以设计一个512字节的C++结构体,来获取引导分区数据:
例如 GitHub开源项目 NTFS-File-Search 设计的NTFS_BOOT_SECTOR 结构

  • NTFS_BOOT_SECTOR结构体

/*
* BIOS Parameter Block (BPB) - BIOS参数块(BPB)
* https://en.wikipedia.org/wiki/NTFS
* https://en.wikipedia.org/wiki/BIOS_parameter_block
* https://www.ntfs.com/ntfs-partition-boot-sector.htm
* NTFS文件系统详解(三)之NTFS元文件解析(https://blog.csdn.net/enjoy5512/article/details/50966009/)
*/
typedef struct NTFS_BOOT_SECTOR
{/* Jump instruction *//* 跳转指令 EB 52 90*/BYTE Jmp[3];/* Signature*//* 文件系统的ASSIIC码表示形式 NTFS*/BYTE Signature[8];//// BPB and extended BPB////!字节2 每个扇区的字节总数 一般是00 02H  (一般后面描述加H表示十六进制)WORD		BytesPerSector;//!字节1 簇大小 08 每个簇占8个扇区BYTE		SectorsPerCluster;//!字节2 保留扇区WORD		ReservedSectors;//!字节3 总为0BYTE		Zeros1[3];//!字节2 不使用WORD		Unused1;//!字节1 介质描述,硬盘为F8BYTE		MediaDescriptor;//!字节2 总为0WORD		Zeros2;//!字节2 每磁头扇区数WORD		SectorsPerTrack;//!字节2 每柱面磁头数WORD		NumberOfHeads;//!字节4 隐含扇区数 (从MBR到DBR的扇区总数)DWORD		HiddenSectors;//!字节4 不使用DWORD		Unused2;//!字节4 不使用 总为80 00 80 00DWORD		Unused3;//!字节8 扇区总数,即分区大小ULONGLONG	TotalSectors;//!字节8 $MFT的开始簇号ULONGLONG	MFT_LCN;		/* $MFT Logical Cluster Number (LCN) - $MFT逻辑簇数(LCN) *///!字节8 $MFTMirr的开始簇号ULONGLONG	MFTMirr_LCN;	/* $MFTMirr Logical Cluster Number (LCN) - $MFTMirr 逻辑集群号(LCN) *///!字节4 每个MFT记录的簇数DWORD		ClustersPerFileRecord;//!字节4 每索引的簇数DWORD		ClustersPerIndexBlock;//! 分区的逻辑序列号BYTE		VolumeSN[8];/* Boot Code - 引导代码 */BYTE BootCode[430];//// 0xAA55//BYTE _AA;BYTE _55;
};

注意使用
#pragma pack( push, 1 )
#pragma pack( pop )
进行字节对齐。

学习C/C++源码案例的时候,pragma pack是经常会遇到
参考#pragma pack 详解

  • 读取引导分区数据

读取引导分区数据转换成指定结构体,获取首个MFT表地址

    qDebug()<<" Start ---->";//!磁盘第一个512字节结构 BIOS参数NTFS_BOOT_SECTOR	m_BootRecord;const WCHAR lpszVolumeName[7] = { L'\\', L'\\', L'.', L'\\', L'C', L':' };HANDLE m_hVolume= CreateFile(lpszVolumeName,GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,NULL);if (m_hVolume == INVALID_HANDLE_VALUE)goto out ;DWORD dwNumberOfBytesRead;if (ReadFile(m_hVolume, &m_BootRecord, NTFS_BOOTSECTOR_SIZE, &dwNumberOfBytesRead, NULL)){if (dwNumberOfBytesRead == NTFS_BOOTSECTOR_SIZE){qDebug()<<"[磁盘扇区中的字节数] :  "<<QString::number(m_BootRecord.BytesPerSector,16).toUpper()<<" -> "<<QString::number(m_BootRecord.BytesPerSector,10);qDebug()<<"[簇中的扇区数量] :     "<<QString::number(m_BootRecord.SectorsPerCluster,16).toUpper()<<" -> "<<QString::number(m_BootRecord.SectorsPerCluster,10);qDebug()<<"[每磁头扇区数] :       "<<QString::number(m_BootRecord.SectorsPerTrack,16).toUpper()<<" -> "<<QString::number(m_BootRecord.SectorsPerTrack,10);qDebug()<<"[每柱面磁头数] :       "<<QString::number(m_BootRecord.NumberOfHeads,16).toUpper()<<" -> "<<QString::number(m_BootRecord.NumberOfHeads,10);qDebug()<<"[分区大小] :          "<<QString::number(m_BootRecord.TotalSectors,16).toUpper()<<" -> "<<QString::number(m_BootRecord.TotalSectors,10);qDebug()<<"[$MFT的开始簇号] :     "<<QString::number(m_BootRecord.MFT_LCN,16).toUpper()<<" -> "<<QString::number(m_BootRecord.MFT_LCN,10);qDebug()<<"[$MFTMirr的开始簇号] : "<<QString::number(m_BootRecord.MFTMirr_LCN,16).toUpper()<<" -> "<<QString::number(m_BootRecord.MFTMirr_LCN,10);}}CloseHandle(m_hVolume);
out:qDebug()<<" End ---->";
/*
QCoreApplication Start ---->
[磁盘扇区中的字节数] :   "200"  ->  "512"
[簇中的扇区数量] :      "8"  ->  "8"
[每磁头扇区数] :        "3F"  ->  "63"
[每柱面磁头数] :        "FF"  ->  "255"
[分区大小] :           "C8007FF"  ->  "209717247"
[$MFT的开始簇号] :      "C0000"  ->  "786432"
[$MFTMirr的开始簇号] :  "2"  ->  "2"
QCoreApplication End ---->
*/

由此得到首个MFT在簇号 786432 位置,
计算首张MFT表磁盘地址字节偏移量MFT_LCN:
UINT64 MFT_LCN= (UINT64)(m_BootRecord.MFT_LCN * m_BootRecord.BytesPerSector * m_BootRecord.SectorsPerCluster);
MFT偏移簇号786432
7864328512= “3221225472” =“0XC0000000”

计算出的地址通过ReadFile设置偏移量读取出来确实是MFT结构表数据,
但是这在其他文章中计算方式又不一样:

  • 【1】.MFT偏移簇号786432
    (786432∗4096(偏移)+63∗512(C盘偏移地址)=3221257728=0xC0007E00)

出自 NTFS文件系统详解

  • 【2】.文件记录由两部分构成,一部分是文件记录头,另一部分是属性列表,最后结尾是四个“FF”。然后我们根据上面BPB中的偏移簇号偏移盘偏移地址找到系统MFT所在地址 (789632 * 8 +63 = 6291519),

出自 NTFS文件系统详解(三)之NTFS元文件解析

  • 【3】.在NTFS文件系统中,每个文件记录都有一个唯一的标识符,称为文件记录号(File Record Number,简称FRN)。要访问某个文件记录,需要先找到该文件记录在$MFT元文件中的偏移地址。计算偏移地址的公式如下:
    $MFTOffset = MFTStartCluster * ClusterSize + FRN * RecordSize
    其中,MFTStartCluster是$MFT元文件的起始簇号ClusterSize是簇的大小FRN是文件记录号RecordSize是文件记录的大小

出自 「NTFS:让你的硬盘更安全、更高效!」NTFS文件系统详解

带入上面【1】,【2】的公式,我读取的数据反而是错误的,而C://盘符的文件记录号一般为5固定值,带入计算也错误,带入0计算正常,不知道是不是NTFS版本的问题,后面还是以NTFS-File-Search 示例中的计算方式为准。

下一篇:Qt/C++ 了解NTFS文件系统,获取首张MFT表数据,解析文件记录头内容找到第一个属性偏移地址

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 基于STM32的温度、电流、电压检测proteus仿真系统(OLED、DHT11、继电器、电机)
  • git 问题 --- fatal: detected dubious ownership in repository at
  • python特性记录
  • 【C++前后缀分解 动态规划】2100. 适合野炊的日子|1702
  • Mybatis中Like模糊查询三种处理方式
  • QCustomPlot笔记(一)
  • Redis: 学习参考资料
  • 集团门户网站设计与实现
  • 文生视频算法
  • Unet改进35:添加FastKANConv2DLayer(2024最新改进方法)
  • 关键错误 你的开始菜单出现了问题 我们将尝试在你下一次登录时修复它。【笔记】
  • golang学习笔记21——golang协程管理及sync.WaitGroup的使用
  • leetcode 199.二叉树的右视图
  • docker容器中的内存占用高的问题分析
  • MybatisPlus实现多租户 全局拦截器
  • Create React App 使用
  • CSS相对定位
  • Fabric架构演变之路
  • laravel with 查询列表限制条数
  • LeetCode算法系列_0891_子序列宽度之和
  • Linux各目录及每个目录的详细介绍
  • SpringBoot 实战 (三) | 配置文件详解
  • vue 个人积累(使用工具,组件)
  • 机器学习 vs. 深度学习
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 事件委托的小应用
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • ionic入门之数据绑定显示-1
  • k8s使用glusterfs实现动态持久化存储
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​必胜客礼品卡回收多少钱,回收平台哪家好
  • #HarmonyOS:基础语法
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (2.2w字)前端单元测试之Jest详解篇
  • (52)只出现一次的数字III
  • (Git) gitignore基础使用
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (poj1.3.2)1791(构造法模拟)
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)负载均衡,回话保持,cookie
  • (转载)Linux 多线程条件变量同步
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .net开发日常笔记(持续更新)
  • .NET业务框架的构建
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)
  • [100天算法】-实现 strStr()(day 52)
  • [1159]adb判断手机屏幕状态并点亮屏幕
  • [120_移动开发Android]008_android开发之Pull操作xml文件
  • [AHOI2009]中国象棋 DP,递推,组合数
  • [AI 大模型] Meta LLaMA-2
  • [BUUCTF]-PWN:wustctf2020_number_game解析(补码,整数漏洞)