Qt/C++ 了解NTFS文件体系,遍历Run Lists数据列表,读取0x30 $FILE_NAME属 ...

打印 上一主题 下一主题

主题 981|帖子 981|积分 2943

系列文章目录

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

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

  

媒介

根据前面获取的 获取Run List数据列表 遍历读取全部MFT元数据,再根据0x30 $FILE_NAME属性0x10 $STANDARD_INFORMATION属性获取NTFS体系中的全部文件和目录数据。
遍历Run Lists数据列表

以开源项目NTFS-File-Search中的方法为例:
已知一个簇包含8个扇区,每个扇区512字节,
一个簇8*512=4096个字节,
每个MFT表元数据1024字节(前面根据$boot表得到的数据);
一个簇包含4个MFT元数据
遍历每个Run Lists数据:
定义每次读取 #define FILE_RECORDS_PER_FILE_BUF 65536个簇 的磁盘数据,不敷 65536个簇 的读取剩余字节数据,并按 一个MFT元数据巨细(1024字节) 循环剖析读取的磁盘数据。
直到读取完这一个Run List(MFT_DATARUN)数据
再读取下一个Run List(MFT_DATARUN)数据,
直到全部数据全部读取完.


  • 代码示例
m_ullRecordSize ☞每个MFT表巨细 - 1024字节
m_ullClusterSize☞ 每个簇巨细 -8*512字节
  1. //! 开始读取每个文件记录
  2. DWORD cbFileRecordBuffer = FILE_RECORDS_PER_FILE_BUF * m_ullRecordSize;
  3. UINT64 ullVolumeOffset=0;
  4. pbFileRecordBuffer=new BYTE[cbFileRecordBuffer];
  5.      for (size_t i = 0; i < m_rgDataRuns.size(); ++i)
  6. {
  7.     //UINT64 nRemainingFiles = RunLength * m_pVolume->ClusterSize() / m_pVolume->RecordSize();
  8.        
  9.         //! 当前run list 列表长度
  10.     VCN_t RunLength = m_rgDataRuns[i].Length;
  11.     //! 偏移量 -以簇为单位
  12.     ullVolumeOffset += m_rgDataRuns[i].Offset;
  13.     qDebug()<<"";
  14.     qDebug()<<"[i] "<<QString::number(i,10)<<" [Offset] "<<QString::number(ullVolumeOffset,10)<<" [Length] "<<QString::number(m_rgDataRuns[i].Length,10);
  15.     /* Read the files in chunks */
  16.     //! 读取(当前run list 列表长度* 簇大小)字节后结束
  17.     for (UINT64 nChunkOffset = 0; nChunkOffset < RunLength * m_ullClusterSize;)
  18.     {
  19.         /* Determine the number of files to read */
  20.         //! 每次读取的大小 -固定65536* 4096字节大小
  21.         UINT64 cbReadLength = cbFileRecordBuffer;
  22.         if (RunLength * m_ullClusterSize - nChunkOffset < cbReadLength) {
  23.                 //! 读取几次固定65536* 4096字节后,剩下的数据
  24.             cbReadLength = RunLength * m_ullClusterSize - nChunkOffset;
  25.         }
  26.         qDebug()<<"[cbReadLength]: "<<QString::number(cbReadLength,16)<<" -> "<<QString::number(cbReadLength,10);
  27.         /* Read the file records from the volume */
  28.         //! ullVolumeOffset * m_ullClusterSize + nChunkOffset 相对偏移量
  29.         //! 开始读取磁盘数据
  30.         DWORD cbBytesRead = ReadBytes(m_hVolume,pbFileRecordBuffer, cbReadLength, ullVolumeOffset * m_ullClusterSize + nChunkOffset);
  31.         if (!cbBytesRead) {
  32.             break;
  33.         }
  34.                
  35.                 //! 计算此次读取了多少字节,算作偏移量
  36.         nChunkOffset += cbReadLength;
  37.                
  38.                 //! 解析MFT元数据
  39.         ParseRecordChunk(pbFileRecordBuffer,cbReadLength);
  40.     }
  41. }
复制代码

MFT元数据 属性结构剖析

参考 :
NTFS文件体系详解(三)之NTFS元文件剖析 : 分析30H属性和
属性-$文件名(0x30)
两篇文章,剖析 $STANDARD_INFORMATION属性和 $FILE_NAME属性获取文件/目录相关内容

0x10 $STANDARD_INFORMATION属性

   0x10 $STANDARD_INFORMATION属性存储尺度信息,包括一些基本文件属性,如只读,体系,存档;时间属性,如文件的创建和末了修改时间;有多少目录指向该文件(即其硬链接)
在旧版本的NTFS中,这个属性只包含DOS文件权限和文件时间。
Windows 2000引入了四个新字段,用于引用配额、安全性、文件巨细和日志记录信息。
如$AttrDef,此属性的中所定义,最小巨细为48字节,最大巨细为72字节。
  

  • 数据结构:



  • C++结构体声昭示例:
  1. /*
  2. * $MFT Standard Information Attribute - $MFT标准信息属性
  3. * http://inform.pucp.edu.pe/~inf232/Ntfs/ntfs_doc_v0.5/attributes/standard_information.html
  4. * https://flatcap.github.io/linux-ntfs/ntfs/attributes/standard_information.html
  5. * 部分字段不一致,但是属性前面字段是一样的。
  6. */
  7. typedef struct MFT_STANDARD_INFORMATION_ATTRIBUTE_HDR
  8. {
  9.     //! 8字节
  10.     LONGLONG        CreationTime;   //C Time - File Creation
  11.     //! 8字节
  12.     LONGLONG        ChangeTime;     //A Time - File Altered
  13.     //! 8字节
  14.     LONGLONG        LastWriteTime;  //M Time - MFT Changed
  15.     //! 8字节
  16.     LONGLONG        LastAccessTime; //R Time - File Read
  17.     //! 4字节
  18.     ULONG                FileAttributes; //DOS File Permissions(Also called attributes in DOS terminology.)
  19.     //! 4字节
  20.     ULONG                Unknown[3];
  21.     //! 4字节
  22.     ULONG                QuotaId;
  23.     //! 4字节
  24.     ULONG                SecurityId;
  25.     //! 8字节
  26.     ULONGLONG        QuotaChange;
  27.     //! 8字节
  28.     USN                        Usn;
  29. }*PMFT_STANDARD_INFORMATION_ATTRIBUTE_HDR;
复制代码
偏移0×20处的文件属性表明如下:
标志常用字母缩写 (WizTree软件中的文件属性就是这个缩写)名称形貌0x0001R只读(Read-only)表示文件只能被读取,不能被修改或删除。0x0002H隐蔽(Hidden)表示文件或文件夹是隐蔽的,通常不会在文件欣赏器中显示。0x0004S体系(System)表示文件或文件夹是体系文件或体系文件夹,这些通常是操纵体系的一部分。0x0020A存档(Archive)表示文件可以被备份步伐标志为必要备份0x0040设备文件设备文件0x0080通例文件通例文件0x0100临时文件临时文件0x0200L希奇文件(Sparse File)表示文件具有希奇性,即文件中存在大量的空白地区,这些空白地区在磁盘上并不现实占用空间。0x0400O重剖析点(Reparse Point)表示文件是一个重剖析点,如符号链接或快捷方式等。0x0800C压缩(Compressed)表示文件已经被压缩以节省磁盘空间。0x1000脱机文件脱机文件0x2000I未编入索引 孤立(Indexed)表示文件不包含任何索引信息,主要用于NTFS文件体系中,影响文件的搜刮速度。0x4000E加密(Encrypted)表示文件使用了EFS(加密文件体系)进行了加密。

  • 文件属性宏定义示例:
   // MFT File 属性- $STANDARD_INFORMATION (MFT_FILERECORD_ATTR_STANDARD_INFO)
// https://flatcap.github.io/linux-ntfs/ntfs/attributes/standard_information.html
#define MFT_STANDARD_INFORMATION_ATTR_R 0x0001
#define MFT_STANDARD_INFORMATION_ATTR_H 0x0002
#define MFT_STANDARD_INFORMATION_ATTR_S 0x0004
#define MFT_STANDARD_INFORMATION_ATTR_A 0x0020
#define MFT_STANDARD_INFORMATION_ATTR_L 0x0200
#define MFT_STANDARD_INFORMATION_ATTR_O 0x0400
#define MFT_STANDARD_INFORMATION_ATTR_C 0x0800
#define MFT_STANDARD_INFORMATION_ATTR_I 0x2000
#define MFT_STANDARD_INFORMATION_ATTR_E 0x4000
  
0x30 $FILE_NAME属性

   0x30 $FILE_NAME属性 用于存储文件名 ,用Unicode字符表示的文件名,由于MS DOS 不能辨认长文件名,以是NTFS体系会自动生成一个8.3文件名,
并且始终是常驻属性
正如在$AttrDef中定义的那样,这个属性的最小巨细为68字节,最大巨细为578字节。这相当于文件名的最大长度为255个Unicode字符。
  

  • 数据结构:



  • C++ 结构体的定义:
  1. /*
  2. * $FILE_NAME Attribute Header Layout - 属性头布局
  3. * https://flatcap.github.io/linux-ntfs/ntfs/attributes/file_name.html
  4. */
  5. typedef struct MFT_FILENAME_ATTRIBUTE_HDR
  6. {
  7.     MFT_FILE_ID        ParentReference;        // File reference to the parent directory -对父目录的文件引用
  8.     ULONGLONG        CreationTime;                // File creation time - 文件创建时间
  9.     ULONGLONG        ModiciationTime;        // File altered time - 文件修改时间
  10.     ULONGLONG        MFTTime;                        // MFT changed time - MFT改变时间
  11.     ULONGLONG        ReadTime;                        // File read time - 文件读取时间
  12.     ULONGLONG        AllocatedSize;                // Allocated size of the file -已分配的文件大小
  13.     ULONGLONG        RealSize;                        // Real size of the file - 文件的实际大小
  14.     DWORD                Flags;                                // Flags - 标志
  15.     DWORD                ER;
  16.     BYTE                NameLength;                        // Filename length in characters - 文件名长度(以字符为单位)
  17.     BYTE                NameSpaceType;                // File namespace type - 文件命名空间类型
  18.     WCHAR                Name[1];                        // Filename - 文件名
  19. }*PMFT_FILENAME_ATTRIBUTE_HDR;
复制代码

获取文件/目录相关内容 代码示例

   参考开源项目NTFS-File-Search的示例:
在每次剖析MFT元数据时,都必要先实行ApplyFixup方法修正数据。
在开源项目NTFS-File-Search是直接声明了一整块内存供数据处理使用
我这里为了方便,每条数据都是单独处理的,由体系声明内存,尤其是文件名的读取直接复制的内存,
这里是将文件名同样生存到了两个字段。
  1. //! 数据结构体
  2. typedef struct NTFS_FILE_ENTRYW
  3. {
  4.     qint64                AllocatedSize;                // File size on disk (bytes)
  5.     qint64                RealSize;                // File size on disk (bytes)
  6.     qint64                FileAttributes;
  7.     qint64                NextEntryOffset;
  8.     ULONGLONG   FileUpdateTime;
  9.     qint64      NumberFolders;               // 文件夹数量
  10.     qint64      NumberDocuments;             // 文件数量
  11.     MFT_FILE_ID        MFTFileId;                                // $MFT Record Number
  12.     UINT64                ParentDirectoryRecord;        // Parent Directory $MFT-Record Number
  13.     BOOL                IsDirectory;
  14.     LPWSTR      Name;                   // filename/directory nane
  15.     LPWSTR                lpszFileName;                        // Full path of file/directory
  16. }*PNTFS_FILE_ENTRYW;
  17. //按照1024字节解析 MFT元数据
  18. void ParseRecordChunk(PBYTE pbRecordChunk, UINT64 cbRecordChunk)
  19. {
  20.     for (UINT64 iRecord = 0; iRecord < cbRecordChunk / m_ullRecordSize; ++iRecord)
  21.     {
  22.        PMFT_FILE_RECORD_HEADER m_pFileRecord=POINTER_ADD(PMFT_FILE_RECORD_HEADER, pbRecordChunk, iRecord * m_ullRecordSize);
  23.        //! 文件是否在使用
  24.        if (!(m_pFileRecord->Flags & MFT_FILERECORD_FLAG_IN_USE))
  25.        {
  26.            continue;
  27.        }
  28.        ApplyFixup(m_pFileRecord);
  29.        RecordAttrMultiMap mpAttributes = GetAttributes(m_pFileRecord);
  30.        auto FileNamesList = mpAttributes.equal_range(MFT_FILERECORD_ATTR_FILENAME);
  31.        /* Find a valid (non-DOS file-name namespace) $FILE_NAME attribute */
  32.        for (auto i = FileNamesList.first; i != FileNamesList.second; ++i)
  33.        {
  34.            PMFT_FILENAME_ATTRIBUTE_HDR pFileName = POINTER_ADD(PMFT_FILENAME_ATTRIBUTE_HDR,
  35.                i->second,
  36.                i->second->Resdient.AttributeOffset
  37.            );
  38.            if (pFileName && pFileName->NameSpaceType != FILENAME_NAMESPACE_DOS)
  39.            {
  40.                NTFS_FILE_ENTRY FileEntry;
  41.                //
  42.                // Set the file entry info
  43.                //
  44.                FileEntry.AllocatedSize              = pFileName->AllocatedSize;
  45.                FileEntry.RealSize                   = pFileName->RealSize;
  46.                FileEntry.NumberFolders              = 0;
  47.                FileEntry.NumberDocuments            = 0;
  48.                FileEntry.FileUpdateTime             = pFileName->ModiciationTime;
  49.                FileEntry.FileAttributes             = 0;
  50.                FileEntry.MFTFileId.MftRecordIndex        = m_pFileRecord->RecordNumber;
  51.                FileEntry.MFTFileId.SequenceNumber   = m_pFileRecord->SequenceNumber;
  52.                FileEntry.IsDirectory                            = (m_pFileRecord->Flags & MFT_FILERECORD_FLAG_IS_DIRECTORY) ? TRUE : FALSE;
  53.                FileEntry.ParentDirectoryRecord      = pFileName->ParentReference.MftRecordIndex;
  54.                FileEntry.Name  =new WCHAR[pFileName->NameLength];
  55.                CopyMemory(FileEntry.Name , pFileName->Name, pFileName->NameLength * sizeof(WCHAR));
  56.                FileEntry.Name[pFileName->NameLength] = L'\0';
  57.                FileEntry.lpszFileName  =new WCHAR[pFileName->NameLength];
  58.                CopyMemory(FileEntry.lpszFileName , pFileName->Name, pFileName->NameLength * sizeof(WCHAR));
  59.                FileEntry.lpszFileName[pFileName->NameLength] = L'\0';
  60.                if (FindAttribute(m_pFileRecord,MFT_FILERECORD_ATTR_STANDARD_INFO))
  61.                {
  62.                    PMFT_STANDARD_INFORMATION_ATTRIBUTE_HDR pStandardInfo = POINTER_ADD(
  63.                        PMFT_STANDARD_INFORMATION_ATTRIBUTE_HDR,
  64.                        FindAttribute(m_pFileRecord,MFT_FILERECORD_ATTR_STANDARD_INFO),
  65.                        FindAttribute(m_pFileRecord,MFT_FILERECORD_ATTR_STANDARD_INFO)->Resdient.AttributeOffset
  66.                    );
  67.                    FileEntry.FileAttributes = pStandardInfo->FileAttributes;
  68.                }
  69.                /* Check if file should be skipped */
  70.                if (QString::fromWCharArray(FileEntry.lpszFileName)=="")
  71.                {
  72.                    break;
  73.                }
  74.                /* Add entry to map */
  75.                if (FileEntry.IsDirectory) {
  76.                    m_DirectoryMap[m_pFileRecord->RecordNumber] = FileEntry;
  77.                    NumberFolders++;
  78.                }
  79.                else {
  80.                    m_FileMap[m_pFileRecord->RecordNumber] = FileEntry;
  81.                    NumberDocuments++;
  82.                }
  83.                 break;
  84.            }
  85.        }
  86.     }
  87. }
复制代码

总结

通过开源项目NTFS-File-Search
获取到的数据会出现数据巨细不一致,会多出/缺少部分文件的问题.
参考 「NTFS:让你的硬盘更安全、更高效!」NTFS文件体系详解,一文中遍历分区文件列表的思路,
发现是没有读取索引属性(IndexEntries列表)不知道是不是这个原因,后面看NTFS-File-Search的源码作者会不会再次优化。
整个了解NTFS文件体系示例到此完毕。
后面另有不了解的内容发起直接参考开源NTFS-File-Search项目源码。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

冬雨财经

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表