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

Qt/C++ 了解NTFS文件系统,解析0x80 $Data属性,获取Run Lists数据列表

系列文章目录

整个专栏系列是根据GitHub开源项目NTFS-File-Search获取分区所有文件/目录列表的思路。
具体的如下:

  1. Qt/C++ 了解NTFS文件系统,了解MFT(Master File Table)主文件表(一)
    介绍NTFS文件系统,对比通过MFT(Master File Table)主文件表获取数据的优劣,简单介绍开源项目NTFS-File-Search,以及了解借鉴NTFS系统中所参考的所有文章。
  2. Qt/C++ 了解NTFS文件系统,解析盘符引导扇区数据获取MFT(Master File Table)主文件表偏移地址
    读取$Boot引导分区扇区数据,获取单个簇大小(4096字节),$MFT元数据大小(1024字节)和$MFT元数据的起始簇号,计算出$MFT元数据在磁盘的偏移地址。
  3. Qt/C++ 了解NTFS文件系统,获取首张MFT表数据,解析文件记录头内容找到第一个属性偏移地址
    解析$MFT元数据结构,根据$MFT元数据文件记录头获取属性的偏移地址,用于后面解析0x80 $Data属性,获取Run List数据列表
  4. Qt/C++ 了解NTFS文件系统,解析MFT主文件表中的常驻属性与非常驻属性
    简单介绍$MFT元数据中的常驻属性与非常驻属性结构.
  5. Qt/C++ 了解NTFS文件系统,解析0x80 $Data属性,获取Run List数据列表
    根据0x80 $Data属性,找到存放所有$MFT元数据的区间列表(Run List数据列表)
  6. Qt/C++ 了解NTFS文件系统,遍历Run Lists数据列表,读取0x30 $FILE_NAME属性,获取所有文件/目录数据
    根据前面获取的 获取Run List数据列表,
    遍历所有Run List数据列表读取所有$MFT元数据,并解析 $FILE_NAME属性和$STANDARD_INFORMATION属性获取文件或目录的实际大小,名称,磁盘分配大小,记录号 ,文件属性等信息

目录导读

  • 系列文章目录
  • MFT元数据常见属性类型介绍
  • 前言
  • 了解Run List数据
  • 解析0x80 $Data属性获取 Run List数据
  • 代码示例:


MFT元数据常见属性类型介绍

MFT元数据属性类型很多,这里以表格的形式简单介绍常见的数据类型。

MFT元数据常见属性类型介绍,参考:
NTFS - Attributes
File - $AttrDef (4)

NTFS文件系统详解->NTFS文件系统中的所有属性体的简介
关于NTFS-MFT

TypeOSName名称描述
0x10$STANDARD_INFORMATION标准信息在旧版本的NTFS中,此属性仅包含DOS文件权限和文件时间。Windows 2000引入了四个新字段,用于引用配额、安全、文件大小和日志信息。正如在$AttrDef中定义的那样,这个属性的大小最小为48字节,最大为72字节。
0x20$ATTRIBUTE_LIST属性列表当属性很多且MFT记录中的空间很短时,可以将所有非常驻属性移出MFT。如果仍然没有足够的空间,则需要使用 $ATTRIBUTE_LIST 属性。其余的属性放在一个新的MFT记录中,$ATTRIBUTE_LIST 描述在哪里可以找到它们。看到这个属性是非常不寻常的。
0x30$FILE_NAME文件名此属性存储文件属性的名称,并且始终是常驻属性。正如在$AttrDef中定义的那样,这个属性的最小大小为68字节,最大大小为578字节。这相当于文件名的最大长度为255个Unicode字符。
0x40NT$VOLUME_VERSION在早期的NTFS c1.2中为卷版本
0x402K$OBJECT_ID对象ID对象Id是在Windows 2000中引入的。每个MFT记录被分配一个唯一的GUID。此外,一条记录可能有一个Birth Volume Id、一个Birth Object Id和一个Domain Id,它们都是guid。正如在$AttrDef中定义的那样,这个属性没有最小大小,但最大256字节。
0x50$SECURITY_DESCRIPTOR安全描述符安全描述符
0x60$VOLUME_NAME卷名(卷标识)该属性仅包含卷的名称。正如在$AttrDef中定义的那样,这个属性的大小最小为2字节,最大为256字节。这相当于卷名的最大长度为127个Unicode字符。
0x70$VOLUME_INFORMATION卷信息卷的版本和状态。正如在$AttrDef中定义的那样,这个属性的最小和最大大小为12字节。
0x80$DATA文件数据此属性包含文件的数据。文件的大小是其未命名数据流的大小。正如在$AttrDef中定义的那样,这个属性没有最小或最大大小。
0x90$INDEX_ROOT索引根这是实现索引(例如目录)的B+树的根节点。这个文件属性始终是常驻的。
0xA0$INDEX_ALLOCATION索引分配这是实现索引(例如目录)的B+树的所有子节点的存储位置。此文件属性始终是非常驻的。
0xB0$BITMAP位图这个文件属性是一个比特序列,每个比特代表一个实体的状态。正如在$AttrDef中定义的那样,这个属性没有最小或最大大小。
0xC0NT$SYMBOLIC_LINK符号链接符号链接
0xC02K$REPARSE_POINT重解析点正如在$AttrDef中定义的那样,这个属性没有最小大小,但最大为16384字节。
0xD0$EA_INFORMATION扩充属性信息用于在NTFS下实现Windows NT服务器的OS/2信息子系统和OS/2客户端所使用的HPFS扩展属性。此文件属性可能是非常驻的,因为它的流可能会增长。正如在$AttrDef中定义的那样,这个属性的最小和最大大小为8字节。
0xE0$EA扩充属性用于在NTFS下实现HPFS扩展属性。此文件属性可能是非常驻的,因为它的流可能会增长。正如在$AttrDef中定义的那样,这个属性没有最小大小,但最大为65536字节。
0xF0NT$PROPERTY_SET早期的NFT v1.2中才有
0x1002K$LOGGED_UTILITY_STREAMEFS加密属性正如在$AttrDef中定义的那样,这个属性没有最小大小,但最大为65536字节。

文件记录属性类型 宏定义示例:
#define MFT_FILERECORD_ATTR_STANDARD_INFO 0x10
#define MFT_FILERECORD_ATTR_ATTRIBUTE_LIST 0x20
#define MFT_FILERECORD_ATTR_FILENAME 0x30
#define MFT_FILERECORD_ATTR_OBJECT_ID 0x40
#define MFT_FILERECORD_ATTR_SECURITY_DESCRIPTOR 0x50
#define MFT_FILERECORD_ATTR_VOLUME_NAME 0x60
#define MFT_FILERECORD_ATTR_VOLUME_INFORMATION 0x70
#define MFT_FILERECORD_ATTR_DATA 0x80
#define MFT_FILERECORD_ATTR_INDEX_ROOT 0x90
#define MFT_FILERECORD_ATTR_INDEX_ALLOCATION 0xA0
#define MFT_FILERECORD_ATTR_BITMAP 0xB0
#define MFT_FILERECORD_ATTR_REPARSE_POINT 0xC0
#define MFT_FILERECORD_ATTR_EA_INFORMATION 0xD0
#define MFT_FILERECORD_ATTR_EA 0xE0
#define MFT_FILERECORD_ATTR_LOGGED_UTILITY_STREAM 0x100
#define MFT_FILERECORD_ATTR_STOP_TAG 0xFFFFFFFF

前言

根据获取的第一个MFT元数据的属性列表,获取到0x80 $Data属性数据。
解析数据获取到Run List数据
有了Run List数据才能找到具体的文件MFT元数据。

了解Run List数据

当MFT元数据属性不能存放完数据,系统就会在NTFS数据区域开辟一个空间存放,这个区域是以簇为单位的。Run List就是记录这个数据区域的起始簇号和大小。

NTFS系统中的Run List数据是什么:
在NTFS(New Technology File System)系统中,Run List数据是一个重要的概念,它用于描述文件或数据属性在磁盘上的存储位置和大小。具体来说,Run List记录了一个或多个数据流的起始簇号(Starting Cluster Number,SCN)和每个数据流的长度(即占用的簇数),这些信息对于定位和访问文件或数据属性的实际存储位置至关重要。

  • Run List通常由一个或多个Run组成,每个Run包含以下信息:
  • 起始簇号(SCN): 表示数据流在磁盘上的起始位置。这是一个簇的编号,用于定位数据流的第一个簇。
  • 长度: 表示数据流占用的簇数。这是一个整数,用于指定从起始簇号开始连续占用的簇的数量。

摘要出自:文言一心

  • 定义Run Lists数据结构

开源项目NTFS-File-Search中Run List的数据结构:

/* Cluster info of Non-Residental Attributes (Data runs)-- 非居住属性聚类信息(数据运行) */
typedef struct MFT_DATARUN
{
    //! 长度(以簇为单位)
    UINT64	Length;
    //! 起始簇号
    INT64	Offset;
}*PMFT_DATARUN;

解析0x80 $Data属性获取 Run List数据

以获取的第一个MFT元数据为例,获取到的0x80属性数据:
在这里插入图片描述
按照前文定义的 PMFT_ATTRIBUTE_HEADER 结构可以判断为非常驻属性,获取 MFT_NONRESIDENT_ATTRIBUTE_HDR 结构中的 DataRunOffset 字段值即可或得Run Lists 数据偏移地址。
按字节计算,也就是从第32字节开始,占2字节数据为 40 00H 换算成十进制为64,即从64为开始到 00 为Run Lists数据偏移地址。即【33 20 C8 00 00 00 0C … … … … 5C 72 00 00 00】都为Run Lists数据,以 00 结束。

  • 解析Run Lists数据 :

第一个字节 33 是压缩字节,高位和低位相加,3 + 3 = 6,
表示这个Data Run信息占用6个字节,即
20 C8 00 00 00 0C
其中高位表示起始簇号占用多少个字节,低位表示大小占用的字节数。在这里,起始簇号占用3个字节,为00 00 0C,大小占用3个字节,为 20 C8 00。
解析后,得到这个数据流起始簇号为786432,大小为51232簇。(小端序计算(little endian))

  • 如图示 :

请添加图片描述
包含获取到五个Run List数据;

解析runlist数据参考:
NTFS文件系统详解(三)之NTFS元文件解析 : 分析80H属性
关于NTFS-MFT : (3)80H属性$DATA

代码示例:

参考开源项目NTFS-File-Search中获取Run list数据的示例:

UINT64 GetDataRuns(PMFT_ATTRIBUTE_HEADER pAttribute, MFTDataRunList *prgDataRuns)
 {
     if (!pAttribute || !prgDataRuns)
     {
         return 0;
     }

     prgDataRuns->clear();

     VCN_t nStartVCN	= MFT_NONRESIDENT_ATTR(pAttribute).StartVCN;
     VCN_t nLastVCN		= MFT_NONRESIDENT_ATTR(pAttribute).LastVCN;
     VCN_t nCurrentVCN	= 0;

     qDebug()<<"[DataRunOffset] :" <<QString::number(MFT_NONRESIDENT_ATTR(pAttribute).DataRunOffset,10);

     PBYTE pbCurrent = POINTER_ADD(PBYTE, pAttribute, MFT_NONRESIDENT_ATTR(pAttribute).DataRunOffset);
     do
     {
         MFT_DATARUN DataRun = { 0 };

         /*!
          * 在C或C++中,表达式 *pbCurrent & 0xF 执行了位与(bitwise AND)操作。这里,*pbCurrent 是解引用指针 pbCurrent,即获取该指针指向的 BYTE(或等效的8位无符号整数)值。& 是位与操作符,而 0xF 是一个十六进制数,等价于二进制的 00001111。
          * 位与操作符 & 对其两边的操作数进行逐位比较,只有在两个相应的位都为1时,结果的该位才为1,否则为0。因此,*pbCurrent & 0xF 的作用是将 *pbCurrent 的低4位保留下来,而将高4位清零。
          * 例如:
          * 如果 *pbCurrent 的值是 0xF3(二进制 11110011),
          * 那么 *pbCurrent & 0xF 的结果是 0x3(二进制 00000011),
          * 因为只有低4位(0011)与 0xF(00001111)的对应位都为1,所以结果保留了这些位,而高4位被清零。
          * 这种操作通常用于提取或掩码特定的位字段。在这个例子中,它用于提取 *pbCurrent 值的低4位。
          */
         int LengthSize = *pbCurrent & 0xF;

         /*!
          * 在C或C++中,PBYTE是一个指向BYTE类型的指针,其中BYTE通常是一个无符号字符类型,用于表示8位(1字节)的数据。表达式*pbCurrent >> 4执行了两个操作:
          * pbCurrent:这是解引用操作,它获取指针pbCurrent指向的值。换句话说,它获取了pbCurrent指向的内存地址中存储的BYTE值。
          * >> 4:这是一个位右移操作,它将*pbCurrent的值向右移动4位。位右移操作通常用于从位字段中提取值或将数值除以2的幂。在这个上下文中,它将BYTE值的高4位移动到低4位的位置,同时丢弃原始的低4位。
          * 例如,如果*pbCurrent的值是0xF3(二进制11110011),那么*pbCurrent >> 4的结果是0xF(二进制1111),因为原始值向右移动了4位,丢弃了最低的4位(0011),保留了最高的4位(1111)。
          * 这种操作通常用于处理位字段或执行特定的位操作任务,如提取、修改或组合数据。
          */
         int OffsetSize = *pbCurrent >> 4;
         if ((LengthSize < 1 || LengthSize > 8) || (OffsetSize < 1 || OffsetSize > 8))
         {
             prgDataRuns->clear();
             return 0;
         }
         ++pbCurrent;

         //打印 长度
         QString Lengsizestr="";
         for(int i=0;i<LengthSize;i++)
             Lengsizestr+=QString(" %1").arg(pbCurrent[i],2,16,QLatin1Char('0')).toUpper();
         qDebug()<<"LengthSize: "<<Lengsizestr;

         CopyMemory(&DataRun.Length, pbCurrent, LengthSize);

         pbCurrent += LengthSize;
         if (pbCurrent[OffsetSize - 1] & 0x80) {
             DataRun.Offset = -1;
         }

         //打印 偏移量
         QString OffsetSizestr="";
         for(int i=0;i<OffsetSize;i++)
             OffsetSizestr+=QString(" %1").arg(pbCurrent[i],2,16,QLatin1Char('0')).toUpper();
         qDebug()<<"OffsetSize: "<<OffsetSizestr;
         CopyMemory(&DataRun.Offset, pbCurrent, OffsetSize);

         prgDataRuns->push_back(DataRun);

         nCurrentVCN += DataRun.Length;
         pbCurrent	+= OffsetSize;
     } while (nCurrentVCN <= nLastVCN);

     return prgDataRuns->size();
 }
/* 输出的数据
[DataRunOffset] : "64"
LengthSize:  " 20 C8 00"
OffsetSize:  " 00 00 0C"
LengthSize:  " B3 C8 00"
OffsetSize:  " 8D 90 98 00"
LengthSize:  " 4E D8 00"
OffsetSize:  " E8 72 97 00"
LengthSize:  " 0A C8 00"
OffsetSize:  " 01 87 1C FF"
LengthSize:  " D5 78"
OffsetSize:  " B8 5C 72"
*/

http://www.kler.cn/a/317377.html

相关文章:

  • OceanStor Pacific系列 8.1.0 功能架构
  • 01:(手撸HAL+CubeMX)时钟篇
  • MySQL与Oracle对比及区别
  • linux c/c++最高效的计时方法
  • Python 连接 Redis 进行增删改查(CRUD)操作
  • HarmonyOS 如何实现传输中的数据加密
  • 仓颉编程语言4,遇到BUG求助
  • 数据中心里全速运行的处理器正在浪费能源
  • golang格式化输入输出
  • 【3D打印】使用simplify 3D切片更改Gcode手动断电续打、掉电、未打完继续打印、补救
  • Parallels Desktop 20 for Mac 推出:完美兼容 macOS Sequoia 与 Win11 24H2
  • 【Godot4.3】自定义数列类NumList
  • 【Qt | Qstring】Qstring详细介绍(字符串的添加、删除、修改、查询、截断、删除空白字符等)
  • Gitlab runner的使用示例(二):Maven + Docker 自动化构建与部署
  • 【游戏引擎】C++自制游戏引擎 Lunar Game Engine
  • 基于vue框架的宠物销售管理系统3m9h3(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
  • 微软开源GraphRAG的使用教程-使用自定义数据测试GraphRAG
  • Java中的快速排序算法详解
  • c++ pair
  • ubuntu下检查端口是否占用问题,编写shell脚本检查端口是否占用
  • 使用Python实现图形学曲线和曲面的NURBS算法
  • ChartLlama: A Multimodal LLM for Chart Understanding and Generation论文阅读
  • unity Compute Shaders 使程序在GPU中运行
  • LeetCode54. 螺旋矩阵(2024秋季每日一题 21)
  • 计算机毕业设计Hadoop+PySpark深圳共享单车预测系统 PyHive 共享单车数据分析可视化大屏 共享单车爬虫 共享单车数据仓库 机器学习 深度学习
  • 工博会蓝卓逛展攻略