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

发布于:2024-09-18 ⋅ 阅读:(146) ⋅ 点赞:(0)

系列文章目录

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



前言

根据前文获取到的首张MFT(Master File Table)主文件表偏移地址,
获取到首张MFT(Master File Table)主文件表数据,解析文件记录头内容数据,获取到的第一个属性的偏移地址。
用于遍历查找所有属性,直到找到 $DATA(0X80) 属性,获取DataRun数据列表。

NTFS文件系统中每个文件或目录都由一张或多张MFT(Master File Table)主文件表保存数据
DataRun数据列表是指分区盘符的文件目录的所有MFT(Master File Table)主文件表集合所在的偏移地址列表

  • 读取首张MFT(Master File Table)主文件表数据

设置的MFT_LCN偏移地址,读取m_ullRecordSize(1024) 个字节的数据,
参考NTFS-File-Search

//! 设置偏移量读取数据
DWORD ReadBytes(HANDLE m_hVolume,PVOID pBuffer, DWORD cbReadSize, UINT64 ullAbsoluteOffset)
{
    DWORD	dwNumberOfBytesRead=0;
    BOOL	bSuccess;

    if (ullAbsoluteOffset != UINT64_MAX)
    {
        LARGE_INTEGER liPosition;
        liPosition.QuadPart = ullAbsoluteOffset;
        LARGE_INTEGER liUpdatedPosition;
        if (SetFilePointerEx(m_hVolume, liPosition, &liUpdatedPosition, FILE_BEGIN))
            m_ullCurrentOffset = liUpdatedPosition.QuadPart;
        else
            return 0;
    }

    bSuccess = ReadFile(m_hVolume, pBuffer, cbReadSize, &dwNumberOfBytesRead, NULL);
    if (bSuccess) {
        m_ullCurrentOffset += cbReadSize;
    }
    return dwNumberOfBytesRead;
}

 //! 开始读取首个MFT表
PBYTE pbMFTBuffer = new BYTE[m_ullRecordSize];
UINT64 MFT_LCN= (UINT64)(m_BootRecord.MFT_LCN * m_BootRecord.BytesPerSector * m_BootRecord.SectorsPerCluster);

if (m_ullRecordSize != ReadBytes(m_hVolume,pbMFTBuffer, (INT64)m_ullRecordSize, MFT_LCN))
    goto out;
qDebug()<<"输出...";
for(int i=0;i<m_ullRecordSize;i++)
{
  Val+=QString("%1 ").arg(pbMFTBuffer[i],2,16,QLatin1Char('0')).toUpper();
  if((i+1)%16==0)
  {
      qDebug()<<Val;
      Val="";
  }
}
/*输出...
"46 49 4C 45 30 00 03 00 EC F3 01 ED 15 00 00 00 "
"01 00 01 00 38 00 01 00 B8 01 00 00 00 04 00 00 "
"00 00 00 00 00 00 00 00 17 00 00 00 00 00 00 00 "
"D6 03 00 00 00 00 00 00 10 00 00 00 60 00 00 00 "
"00 00 18 00 00 00 00 00 48 00 00 00 18 00 00 00 "
"A0 73 73 A7 99 E8 D7 01 A0 73 73 A7 99 E8 D7 01 "
"A0 73 73 A7 99 E8 D7 01 A0 73 73 A7 99 E8 D7 01 "
....
....
*/
  • Bootice工具查看数据

在Bootice工具中,第一张MFT(Master File Table)主文件表在引导扇区后第16个扇区。
在这里插入图片描述

解析MFT(Master File Table)主文件表

理解MFT(Master File Table)的结构对于理解NTFS文件系统的运作方式至关重要。以下是MFT结构的详细解释:
MFT记录:
MFT由一系列固定大小的记录组成,每个记录对应一个文件、目录或元数据文件。
MFT记录包含了文件或目录的元数据信息,如文件属性、数据位置、文件名等。
MFT记录号:
每个MFT记录都有一个唯一的标识符,称为MFT记录号。
MFT记录号用于在MFT中定位和识别特定的文件或目录。
摘要出自:MFT(Master File Table,主文件表)是Windows操作系统中NTFS(New Technology File System,新技术文件系统)的关键组成部分,用于存储文件和目录的元数据信息。MFT类似于Unix和Linux系统中的inode,但在实现上有所不同。

MFT是一组文件记录。卷中的每个文件都由一个或多个这样的文件记录完整地描述。在Unix术语中,文件记录相当于索引节点。描述给定文件的第一个文件记录称为基本文件记录,其他记录称为扩展文件记录。
文件记录由一个标题、几个可变长度属性和一个结束标记(简单地说就是0xFFFFFFFF)组成。
出自:File - $MFT (0)
Concept - File Record

以第一张MFT(Master File Table)主文件表为例,解析数据结构
MFT(Master File Table)主文件表以FILE0字符串开始。

解析 文件记录头布局($MFT File Record Header Layout)

MFT表中前48字节是文件记录头内容,根据文件记录头数据获取属性开始偏移地址,其数据结构为:

Offset 字节大小 OS Description 描述
0x00 4字节 (Magic number ‘FILE’ ) 固定值 一定是"FILE"
0x04 2字节 (Offset to the Update Sequence) 更新序列号的偏移
0x06 2字节 (Size in words of Update Sequence) (S) 更新序列号与更新数组以字为单位(s)
0x08 8字节 $LogFile Sequence Number (LSN) 日志序列号(每次记录被修改,都将导致该序列加1)
0x10 2字节 Sequence number 序列号(用于记录本文件记录被重复使用的次数,每次文件删除时加1,跳过0值,如果为0,则保持为0)
0x12 2字节 Hard link count 硬链接数 只出现在基本文件记录中,目录所含项数要使用到他
0x14 2字节 Offset to the first Attribute 第一个属性流的偏移位置
0x16 2字节 Flags 标志字节,1表示记录使用中,2表示该记录为目录
0x18 4字节 Real size of the FILE record 文件记录实际大小(填充到8字节,即以8字节为边界)
0x1C 4字节 Allocated size of the FILE record 文件记录分配大小(填充到8字节,即以8字节为边界)
0x20 8字节 File reference to the base FILE record 所对应的基本文件记录的文件参考号(扩展文件记录中使用,基本文件记录中为0,在基本文件记录的属性列表0x20属性存储中扩展文件记录的相关信息)
0x28 2字节 Next Attribute Id 下一个自由ID号,当增加新的属性时,将该值分配给新属性,然后该值增加,如果IFT记录重新使用,则将它置0,第一个实例总是0
0x2A 2字节 XP Align to 4 byte boundary 边界, WINDOWS XP 中使用,也就是本记录使用的两个扇区的最后两个字节的值
0x2C 4字节 XP Number of this MFT Record WINDOWS XP中使用,本MFT记录号
  • 其中 Flags 字段:
描述
0x01 表示记录正在使用中
0x02 记录是一个目录(文件名索引存在)
0x04 Record是一个扩展名(为$Extend目录中的记录设置)
0x08 存在特殊索引(为包含索引的非目录记录设置: S e c u r e , Secure, SecureObjID, Q u o t a , Quota, QuotaReparse)
  • 实际/分配的大小:

分配的大小是记录在磁盘上占用的空间。这应该是集群大小的倍数,并且可能等于MFT文件记录的大小。实际大小是记录中实际使用的字节数。
注意:实际大小将被填充到一个8字节的边界。

参考:NTFS文件系统详解(三)之NTFS元文件解析
Concept - File Record

  • 实际数据 使用Bootice工具图示

请添加图片描述

  • 定义一个结构体

定义一个48字节的结构体,对齐字节,用于数据结构转换,例如NTFS-File-Search项目中的MFT_FILE_RECORD_HEADER结构:


/*
* $MFT File Record Header Layout - 文件记录头布局 标准属性标题,
* MFT是一组文件记录。卷中的每个文件都由一个或多个这样的文件记录完整地描述。
* 在Unix术语中,文件记录相当于索引节点。描述给定文件的第一个文件记录称为基本文件记录,其他记录称为扩展文件记录。
* 文件记录由一个标题、几个可变长度属性和一个结束标记(简单地说就是0xFFFFFFFF)组成。
* https://flatcap.github.io/linux-ntfs/ntfs/concepts/file_record.html
*/
typedef struct MFT_FILE_RECORD_HEADER
{
    //!字节4 固定值 一定是"FILE"
    DWORD		Magic;					// "FILE" (0x454C4946)
    //!字节2 更新序列号的偏移
    WORD		UpdateSequenceOffset;	// Update Sequence offset - 偏移到更新序列
    //!字节2 更新序列号与更新数组以字为单位(s)
    WORD		SizeOfUpdateSequence;	// Size in words of Update Sequence - 	更新序列的字数
    //!字节8 日志序列号(每次记录被修改,都将导致该序列加1)
    ULONGLONG	LogSequenceNumber;		// $LogFile Sequence Number (LSN) - $日志文件序列号(LSN)
    //!字节2 序列号(用于记录本文件记录被重复使用的次数,每次文件删除时加1,跳过0值,如果为0,则保持为0)
    WORD		SequenceNumber;			// Sequence number - 序列号
    //!字节2 硬链接数 只出现在基本文件记录中,目录所含项数要使用到他
    WORD		HardLinkCount;			// Hard link count - 硬链接计数
    //!字节2 第一个属性流的偏移位置
    WORD		FirstAttributeOffset;	// First attribute offset - 到第一个属性的偏移量
    //!字节2 标志字节,1表示记录使用中,2表示该记录为目录
    WORD		Flags;
    //!字节4 文件记录实际大小(填充到8字节,即以8字节为边界)
    DWORD		RealSize;				// Real size of the FILE record - FILE记录的实际大小
    //!字节4 文件记录分配大小(填充到8字节,即以8字节为边界)
    DWORD		AllocatedSize;			// Allocated size of the FILE record - 文件记录的分配大小
    //!字节8 所对应的基本文件记录的文件参考号(扩展文件记录中使用,基本文件记录中为0,在基本文件记录的属性列表0x20属性存储中扩展文件记录的相关信息)
    ULONGLONG	FileReference;			// File reference to the base FILE record - 对基本文件记录的文件引用
    //!字节2 下一个自由ID号,当增加新的属性时,将该值分配给新属性,然后该值增加,如果IFT记录重新使用,则将它置0,第一个实例总是0
    WORD		NextAttributeId;        // 下一个属性Id
    //!字节2 边界, WINDOWS XP 中使用,也就是本记录使用的两个扇区的最后两个字节的值
    WORD		Align;                  // 对齐4字节边界
    //!字节4 WINDOWS XP中使用,本MFT记录号
    DWORD		RecordNumber;			// MFT Record Number - MFT记录号
}*PMFT_FILE_RECORD_HEADER;

  • 实际数据转换
PMFT_FILE_RECORD_HEADER header=(PMFT_FILE_RECORD_HEADER)pbMFTBuffer;
qDebug()<<"FirstAttributeOffset: "<<QString::number(header->FirstAttributeOffset,16).toUpper()<<" -> "<<QString::number(header->FirstAttributeOffset,10);
qDebug()<<"LogSequenceNumber: "<<QString::number(header->LogSequenceNumber,16).toUpper()<<" -> "<<QString::number(header->LogSequenceNumber,10);
qDebug()<<"Flags: "<<QString::number(header->Flags,16).toUpper()<<" -> "<<QString::number(header->Flags,10);
qDebug()<<"RealSize: "<<QString::number(header->RealSize,16).toUpper()<<" -> "<<QString::number(header->RealSize,10);
qDebug()<<"AllocatedSize: "<<QString::number(header->AllocatedSize,16).toUpper()<<" -> "<<QString::number(header->AllocatedSize,10);
/*
FirstAttributeOffset:  "38"  ->  "56"
LogSequenceNumber:  "15ED01F3EC"  ->  "94170641388"
Flags:  "1"  ->  "1"
RealSize:  "1B8"  ->  "440"
AllocatedSize:  "400"  ->  "1024"
*/

获取到第一个属性偏移地址(FirstAttributeOffset)是从56字节处开始
如下图示:
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到