系列文章目录
整个专栏系列是根据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字节
- //! 开始读取每个文件记录
- DWORD cbFileRecordBuffer = FILE_RECORDS_PER_FILE_BUF * m_ullRecordSize;
- UINT64 ullVolumeOffset=0;
- pbFileRecordBuffer=new BYTE[cbFileRecordBuffer];
- for (size_t i = 0; i < m_rgDataRuns.size(); ++i)
- {
- //UINT64 nRemainingFiles = RunLength * m_pVolume->ClusterSize() / m_pVolume->RecordSize();
-
- //! 当前run list 列表长度
- VCN_t RunLength = m_rgDataRuns[i].Length;
- //! 偏移量 -以簇为单位
- ullVolumeOffset += m_rgDataRuns[i].Offset;
- qDebug()<<"";
- qDebug()<<"[i] "<<QString::number(i,10)<<" [Offset] "<<QString::number(ullVolumeOffset,10)<<" [Length] "<<QString::number(m_rgDataRuns[i].Length,10);
- /* Read the files in chunks */
- //! 读取(当前run list 列表长度* 簇大小)字节后结束
- for (UINT64 nChunkOffset = 0; nChunkOffset < RunLength * m_ullClusterSize;)
- {
- /* Determine the number of files to read */
- //! 每次读取的大小 -固定65536* 4096字节大小
- UINT64 cbReadLength = cbFileRecordBuffer;
- if (RunLength * m_ullClusterSize - nChunkOffset < cbReadLength) {
- //! 读取几次固定65536* 4096字节后,剩下的数据
- cbReadLength = RunLength * m_ullClusterSize - nChunkOffset;
- }
- qDebug()<<"[cbReadLength]: "<<QString::number(cbReadLength,16)<<" -> "<<QString::number(cbReadLength,10);
- /* Read the file records from the volume */
- //! ullVolumeOffset * m_ullClusterSize + nChunkOffset 相对偏移量
- //! 开始读取磁盘数据
- DWORD cbBytesRead = ReadBytes(m_hVolume,pbFileRecordBuffer, cbReadLength, ullVolumeOffset * m_ullClusterSize + nChunkOffset);
- if (!cbBytesRead) {
- break;
- }
-
- //! 计算此次读取了多少字节,算作偏移量
- nChunkOffset += cbReadLength;
-
- //! 解析MFT元数据
- ParseRecordChunk(pbFileRecordBuffer,cbReadLength);
- }
- }
复制代码 MFT元数据 属性结构剖析
参考 :
NTFS文件体系详解(三)之NTFS元文件剖析 : 分析30H属性和
属性-$文件名(0x30)
两篇文章,剖析 $STANDARD_INFORMATION属性和 $FILE_NAME属性获取文件/目录相关内容
0x10 $STANDARD_INFORMATION属性
0x10 $STANDARD_INFORMATION属性存储尺度信息,包括一些基本文件属性,如只读,体系,存档;时间属性,如文件的创建和末了修改时间;有多少目录指向该文件(即其硬链接)
在旧版本的NTFS中,这个属性只包含DOS文件权限和文件时间。
Windows 2000引入了四个新字段,用于引用配额、安全性、文件巨细和日志记录信息。
如$AttrDef,此属性的中所定义,最小巨细为48字节,最大巨细为72字节。
- /*
- * $MFT Standard Information Attribute - $MFT标准信息属性
- * http://inform.pucp.edu.pe/~inf232/Ntfs/ntfs_doc_v0.5/attributes/standard_information.html
- * https://flatcap.github.io/linux-ntfs/ntfs/attributes/standard_information.html
- * 部分字段不一致,但是属性前面字段是一样的。
- */
- typedef struct MFT_STANDARD_INFORMATION_ATTRIBUTE_HDR
- {
- //! 8字节
- LONGLONG CreationTime; //C Time - File Creation
- //! 8字节
- LONGLONG ChangeTime; //A Time - File Altered
- //! 8字节
- LONGLONG LastWriteTime; //M Time - MFT Changed
- //! 8字节
- LONGLONG LastAccessTime; //R Time - File Read
- //! 4字节
- ULONG FileAttributes; //DOS File Permissions(Also called attributes in DOS terminology.)
- //! 4字节
- ULONG Unknown[3];
- //! 4字节
- ULONG QuotaId;
- //! 4字节
- ULONG SecurityId;
- //! 8字节
- ULONGLONG QuotaChange;
- //! 8字节
- USN Usn;
- }*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字符。
- /*
- * $FILE_NAME Attribute Header Layout - 属性头布局
- * https://flatcap.github.io/linux-ntfs/ntfs/attributes/file_name.html
- */
- typedef struct MFT_FILENAME_ATTRIBUTE_HDR
- {
- MFT_FILE_ID ParentReference; // File reference to the parent directory -对父目录的文件引用
- ULONGLONG CreationTime; // File creation time - 文件创建时间
- ULONGLONG ModiciationTime; // File altered time - 文件修改时间
- ULONGLONG MFTTime; // MFT changed time - MFT改变时间
- ULONGLONG ReadTime; // File read time - 文件读取时间
- ULONGLONG AllocatedSize; // Allocated size of the file -已分配的文件大小
- ULONGLONG RealSize; // Real size of the file - 文件的实际大小
- DWORD Flags; // Flags - 标志
- DWORD ER;
- BYTE NameLength; // Filename length in characters - 文件名长度(以字符为单位)
- BYTE NameSpaceType; // File namespace type - 文件命名空间类型
- WCHAR Name[1]; // Filename - 文件名
- }*PMFT_FILENAME_ATTRIBUTE_HDR;
复制代码 获取文件/目录相关内容 代码示例
参考开源项目NTFS-File-Search的示例:
在每次剖析MFT元数据时,都必要先实行ApplyFixup方法修正数据。
在开源项目NTFS-File-Search是直接声明了一整块内存供数据处理使用
我这里为了方便,每条数据都是单独处理的,由体系声明内存,尤其是文件名的读取直接复制的内存,
这里是将文件名同样生存到了两个字段。
- //! 数据结构体
- typedef struct NTFS_FILE_ENTRYW
- {
- qint64 AllocatedSize; // File size on disk (bytes)
- qint64 RealSize; // File size on disk (bytes)
- qint64 FileAttributes;
- qint64 NextEntryOffset;
- ULONGLONG FileUpdateTime;
- qint64 NumberFolders; // 文件夹数量
- qint64 NumberDocuments; // 文件数量
- MFT_FILE_ID MFTFileId; // $MFT Record Number
- UINT64 ParentDirectoryRecord; // Parent Directory $MFT-Record Number
- BOOL IsDirectory;
- LPWSTR Name; // filename/directory nane
- LPWSTR lpszFileName; // Full path of file/directory
- }*PNTFS_FILE_ENTRYW;
- //按照1024字节解析 MFT元数据
- void ParseRecordChunk(PBYTE pbRecordChunk, UINT64 cbRecordChunk)
- {
- for (UINT64 iRecord = 0; iRecord < cbRecordChunk / m_ullRecordSize; ++iRecord)
- {
- PMFT_FILE_RECORD_HEADER m_pFileRecord=POINTER_ADD(PMFT_FILE_RECORD_HEADER, pbRecordChunk, iRecord * m_ullRecordSize);
- //! 文件是否在使用
- if (!(m_pFileRecord->Flags & MFT_FILERECORD_FLAG_IN_USE))
- {
- continue;
- }
- ApplyFixup(m_pFileRecord);
- RecordAttrMultiMap mpAttributes = GetAttributes(m_pFileRecord);
- auto FileNamesList = mpAttributes.equal_range(MFT_FILERECORD_ATTR_FILENAME);
- /* Find a valid (non-DOS file-name namespace) $FILE_NAME attribute */
- for (auto i = FileNamesList.first; i != FileNamesList.second; ++i)
- {
- PMFT_FILENAME_ATTRIBUTE_HDR pFileName = POINTER_ADD(PMFT_FILENAME_ATTRIBUTE_HDR,
- i->second,
- i->second->Resdient.AttributeOffset
- );
- if (pFileName && pFileName->NameSpaceType != FILENAME_NAMESPACE_DOS)
- {
- NTFS_FILE_ENTRY FileEntry;
- //
- // Set the file entry info
- //
- FileEntry.AllocatedSize = pFileName->AllocatedSize;
- FileEntry.RealSize = pFileName->RealSize;
- FileEntry.NumberFolders = 0;
- FileEntry.NumberDocuments = 0;
- FileEntry.FileUpdateTime = pFileName->ModiciationTime;
- FileEntry.FileAttributes = 0;
- FileEntry.MFTFileId.MftRecordIndex = m_pFileRecord->RecordNumber;
- FileEntry.MFTFileId.SequenceNumber = m_pFileRecord->SequenceNumber;
- FileEntry.IsDirectory = (m_pFileRecord->Flags & MFT_FILERECORD_FLAG_IS_DIRECTORY) ? TRUE : FALSE;
- FileEntry.ParentDirectoryRecord = pFileName->ParentReference.MftRecordIndex;
- FileEntry.Name =new WCHAR[pFileName->NameLength];
- CopyMemory(FileEntry.Name , pFileName->Name, pFileName->NameLength * sizeof(WCHAR));
- FileEntry.Name[pFileName->NameLength] = L'\0';
- FileEntry.lpszFileName =new WCHAR[pFileName->NameLength];
- CopyMemory(FileEntry.lpszFileName , pFileName->Name, pFileName->NameLength * sizeof(WCHAR));
- FileEntry.lpszFileName[pFileName->NameLength] = L'\0';
- if (FindAttribute(m_pFileRecord,MFT_FILERECORD_ATTR_STANDARD_INFO))
- {
- PMFT_STANDARD_INFORMATION_ATTRIBUTE_HDR pStandardInfo = POINTER_ADD(
- PMFT_STANDARD_INFORMATION_ATTRIBUTE_HDR,
- FindAttribute(m_pFileRecord,MFT_FILERECORD_ATTR_STANDARD_INFO),
- FindAttribute(m_pFileRecord,MFT_FILERECORD_ATTR_STANDARD_INFO)->Resdient.AttributeOffset
- );
- FileEntry.FileAttributes = pStandardInfo->FileAttributes;
- }
- /* Check if file should be skipped */
- if (QString::fromWCharArray(FileEntry.lpszFileName)=="")
- {
- break;
- }
- /* Add entry to map */
- if (FileEntry.IsDirectory) {
- m_DirectoryMap[m_pFileRecord->RecordNumber] = FileEntry;
- NumberFolders++;
- }
- else {
- m_FileMap[m_pFileRecord->RecordNumber] = FileEntry;
- NumberDocuments++;
- }
- break;
- }
- }
- }
- }
复制代码 总结
通过开源项目NTFS-File-Search
获取到的数据会出现数据巨细不一致,会多出/缺少部分文件的问题.
参考 「NTFS:让你的硬盘更安全、更高效!」NTFS文件体系详解,一文中遍历分区文件列表的思路,
发现是没有读取索引属性(IndexEntries列表)不知道是不是这个原因,后面看NTFS-File-Search的源码作者会不会再次优化。
整个了解NTFS文件体系示例到此完毕。
后面另有不了解的内容发起直接参考开源NTFS-File-Search项目源码。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |