一如既往的叨叨
首先要对硬盘分区(MBR、GPT)和文件系统(NTFS、FAT32等)有一定的认识,要知道MBR扇区以及DBR扇区的基本结构,如果后面遇到不清楚的地方可以参考上一篇文章/hilavergil/article/details/79270379,如果觉得这个文章不行的话,Emmm...还有Google呢。
接下来的代码目前只适用于MBR格式的分区,如果后面有时间的话再加上GPT的分区定位,实际上GPT的分区表更简洁明了一些。
我尽量少用Windows的Api,从硬盘的MBR扇区(0扇区)开始逐步定位,确定每一个主分区和扩展分区的位置,如果是NTFS分区则定位到MFT,从根目录开始使用DFS进行遍历。
我们的目标是
由于硬盘的分区定位在上面那篇文章已经写过了,因此这篇文章的重点会放在NTFS文件系统的解析上。我们从一个硬盘的MBR扇区开始,定位到一个NTFS分区,然后从NTFS分区的第一个扇区(Boot扇区)开始,逐步分析并定位到该分区中的每个目录和文件。
好了开始写正文
前面那篇文章曾提到过,MBR扇区中的分区表只有四项,当分区数小于等于3的时候,所有的分区都属于主分区,安装操作系统的主分区也叫活动分区,这些在Windows的磁盘管理中都可以看到,MBR扇区中分区表的每一项直接指向该主分区的起始位置,也就是该主分区的DBR扇区。当分区数大于等于4的时候呢怎么办?一般来说这种情况下MBR扇区中分区表的第四项将会指向扩展分区,一个扩展分区可以包含多个逻辑分区,每个逻辑分区都有一个EBR扇区与之对应。而EBR扇区和MBR扇区的结构是一致的,但是在EBR扇区中仅用到了其分区表的前两项,其中第一项指向本逻辑分区的起始偏移(注意逻辑分区是存在于扩展分区内的,见下图),第二项指向下一个逻辑分区的EBR扇区。如此循环往复,你会发现实际上EBR扇区构成了一个逻辑分区的链表,链表的每一个节点都包含两项,第一项指向该EBR对应的逻辑分区,第二项指向下一个逻辑分区的EBR扇区。最后一个节点的第二项为零,表示此后不再有逻辑分区。
图1 名字可能不太对但结构应该没差.png
当找到一个NTFS文件系统的分区之后,接下来就可以开始着手遍历目录树和文件了。你可能要问为什么不是FAT32或者其他的分区,因为我只看了NTFS……
第一步:读取NTFS的DBR扇区
DBR扇区的结构在之前那片文章写过了,下面给个DBR的截图。
图2 DBR扇区
图中红色方框里面的数据是MFT的偏移(小端字节序),即0X 00 00 00 00 00 0C 00 00,单位是簇,即MFT偏移786432个簇(相对于该分区的起始位置而言),蓝色方框里是每簇的扇区数0X08,黑色方框里是每扇区字节数0X 02 00,即512个字节。有了这些就可以算出MFT偏移的扇区数:786432 乘 8 等于 6291456个扇区,我们转到该分区的第 6291456扇区,就是MFT文件的第一个扇区了。接下来该分析MFT了。
第二步:解析MFT
MFT是什么鬼?百度一下。
图3 ???
不好意思,进错片场了。
图4 MFT主文件表
实际上MFT也是一个文件,在winhex中可以看到它:
图5 WinHex中的MFT文件
也能看到MFT的偏移和我们之前计算的结果时一致的。MFT由一个个表项构成,每一个表项是一个文件记录,大小一般为1KB(两个扇区),记录着卷中每一个目录和文件的信息,每个文件记录的结构都是固定的,由文件记录头和若干属性构成。下面给一个截图:
图6 一个文件记录
其中,文件记录头的结构定义如下:
// 文件记录头typedef struct _FILE_RECORD_HEADER{/*+0x00*/ BYTE Type[4]; // 固定值'FILE'/*+0x04*/ UINT16 USNOffset; // 更新序列号偏移, 与操作系统有关/*+0x06*/ UINT16 USNCount; // 固定列表大小Size in words of Update Sequence Number & Array (S)/*+0x08*/ UINT64 Lsn;// 日志文件序列号(LSN)/*+0x10*/ UINT16 SequenceNumber; // 序列号(用于记录文件被反复使用的次数)/*+0x12*/ UINT16 LinkCount; // 硬连接数/*+0x14*/ UINT16 AttributeOffset; // 第一个属性偏移/*+0x16*/ UINT16 Flags; // flags, 00表示删除文件,01表示正常文件,02表示删除目录,03表示正常目录/*+0x18*/ UINT32 BytesInUse; // 文件记录实时大小(字节) 当前MFT表项长度,到FFFFFF的长度+4/*+0x1C*/ UINT32 BytesAllocated; // 文件记录分配大小(字节)/*+0x20*/ UINT64 BaseFileRecord; // = 0 基础文件记录 File reference to the base FILE record/*+0x28*/ UINT16 NextAttributeNumber; // 下一个自由ID号/*+0x2A*/ UINT16 Pading; // 边界/*+0x2C*/ UINT32 MFTRecordNumber; // windows xp中使用,本MFT记录号/*+0x30*/ UINT16 USN;// 更新序列号/*+0x32*/ BYTE UpdateArray[0];// 更新数组} FILE_RECORD_HEADER, *pFILE_RECORD_HEADER;
文件记录头后面就是属性了,属性由属性头和属性体构成,有如下几种类型:
图7 属性类型
属性有常驻属性和非常驻属性之分,当一个属性的数据能够在1KB的文件记录中保存的时候,该属性为常驻属性;而当属性的数据无法在文件记录中存放,需要存放到MFT外的其他位置时,该属性为非常驻属性。常驻属性和非常驻属性的头部结构定义如下:
//常驻属性和非常驻属性的公用部分typedef struct _CommonAttributeHeader {UINT32 ATTR_Type; //属性类型UINT32 ATTR_Size; //属性头和属性体的总长度BYTE ATTR_ResFlag; //是否是常驻属性(0常驻 1非常驻)BYTE ATTR_NamSz; //属性名的长度UINT16 ATTR_NamOff; //属性名的偏移 相对于属性头UINT16 ATTR_Flags; //标志(0x0001压缩 0x4000加密 0x8000稀疏)UINT16 ATTR_Id; //属性唯一ID}CommonAttributeHeader,*pCommonAttributeHeader;//常驻属性 属性头typedef struct _ResidentAttributeHeader {CommonAttributeHeader ATTR_Common;UINT32 ATTR_DatSz; //属性数据的长度UINT16 ATTR_DatOff; //属性数据相对于属性头的偏移BYTE ATTR_Indx; //索引BYTE ATTR_Resvd; //保留BYTE ATTR_AttrNam[0];//属性名,Unicode,结尾无0}ResidentAttributeHeader, *pResidentAttributeHeader;//非常驻属性 属性头typedef struct _NonResidentAttributeHeader {CommonAttributeHeader ATTR_Common;UINT64 ATTR_StartVCN; //本属性中数据流起始虚拟簇号 UINT64 ATTR_EndVCN; //本属性中数据流终止虚拟簇号UINT16 ATTR_DatOff; //簇流列表相对于属性头的偏移UINT16 ATTR_CmpSz; //压缩单位 2的N次方UINT32 ATTR_Resvd;UINT64 ATTR_AllocSz; //属性分配的大小UINT64 ATTR_ValidSz; //属性的实际大小UINT64 ATTR_InitedSz; //属性的初始大小BYTE ATTR_AttrNam[0];}NonResidentAttributeHeader, *pNonResidentAttributeHeader;
比较重要的几个属性如0X30文件名属性,其中记录着该目录或者文件的文件名;0X80数据属性记录着文件中的数据;0X90索引根属性,存放着该目录下的子目录和子文件的索引项;当某个目录下的内容比较多,从而导致0X90属性无法完全存放时,0XA0属性会指向一个索引区域,这个索引区域包含了该目录下所有剩余内容的索引项。
比较重要的几个属性的结构定义将在后面给出。
下面是一个90属性和A0属性的截图:
图8 90属性和A0属性
从图上大致也能看出来,90属性中包含了2个目录:Program Files (x86) 和 Users,剩下的其他目录就在A0的属性体所指向的索引区了。索引的具体含义将在后面给出。
前面说过,每一个文件记录都会对应一个目录或者文件,因此,如果我们按顺序读取每一个MFT表项,我们就能得到该卷中所有的文件和目录信息,其中还包括了部分被删除的文件和目录(被删除但未覆盖掉)。但我们的目的并不是目录和文件的简单罗列,我们要的是按照目录的树形结构去列出该卷的目录树,并且去定位某一个给定的文件,读出文件的数据。因此到这里还不够,继续向下。
由于所有的目录和文件都在MFT中有对应的记录,因此,一个卷中目录和文件的数量越多,MFT的大小就越大,$MFT文件的大小是动态增长的,NTFS默认给MFT分配了该卷的12.5%的存储空间。MFT的前16项(元数据文件)是固定的(现在第九项$Quota基本上不存在了):
图9 NTFS的元数据文件
有了前面这些基础,接下来我们就能去解析目录树,定位文件位置啦!
大致的思路如下:
首先,MFT的第五项是卷的根目录的文件记录,见上图的$Root项,这是我们遍历的起点。其次,通过根目录的文件记录的90属性和A0属性,可以定位根目录下所有内容的索引项,而这些索引项又指向了它自己的文件记录项(MFT中的一项),该文件记录的90和A0属性又指向了它的子目录的索引项。所以呢。写个递归呀,我们的目录树就出来啦。这个树形结构大致上是这样的:
图10 一棵树
接下来以E盘中的E:\dir1_0\dir2_0\dir3_1\新建文本文档.txt为例,一步步详细的写出遍历的步骤,并读取出这个文本文档中的数据。
第三步:得到根目录的文件记录
前面我们通过计算得到,MFT的偏移为6,291,456个扇区,而根目录的文件记录是MFT的第5项,一个MFT表项占2个扇区,所以根目录的文件记录的偏移为6,291,456 + 2 * 5 = 6,291,466个扇区。转到该扇区,可以看到根目录的文件记录如下:
图11 根目录的文件记录
其中,30属性的属性体结构定义如下(不包含属性头):
//FILE_NAME 0X30属性体typedef struct _FILE_NAME {UINT64 FN_ParentFR; /*父目录的MFT记录的记录索引。注意:该值的低6字节是MFT记录号,高2字节是该MFT记录的序列号*/FILETIME FN_CreatTime;FILETIME FN_AlterTime;FILETIME FN_MFTChg;FILETIME FN_ReadTime;UINT64 FN_AllocSz;UINT64 FN_ValidSz;//文件的真实尺寸UINT32 FN_DOSAttr;//DOS文件属性UINT32 FN_EA_Reparse;//扩展属性与链接BYTE FN_NameSz;//文件名的字符数BYTE FN_NamSpace;/*命名空间,该值可为以下值中的任意一个0:POSIX可以使用除NULL和分隔符“/”之外的所有UNICODE字符,最大可以使用255个字符。注意:“:”是合法字符,但Windows不允许使用。1:Win32Win32是POSIX的一个子集,不区分大小写,可以使用除““”、“*”、“?”、“:”、“/”、“<”、“>”、“/”、“|”之外的任意UNICODE字符,但名字不能以“.”或空格结尾。2:DOSDOS命名空间是Win32的子集,只支持ASCII码大于空格的8BIT大写字符并且不支持以下字符““”、“*”、“?”、“:”、“/”、“<”、“>”、“/”、“|”、“+”、“,”、“;”、“=”;同时名字必须按以下格式命名:1~8个字符,然后是“.”,然后再是1~3个字符。3:Win32&DOS这个命名空间意味着Win32和DOS文件名都存放在同一个文件名属性中。*/BYTE FN_FileName[0];}FILE_NAME,*pFILE_NAME;
对照上图,可以得到根目录的文件名为 “ . ”。
第四步:计算根目录下索引项的偏移
90属性的属性体由3部分构成:索引根、索引头和索引项。但是有些情况下90属性中是不存在索引项的(上图的90属性不包含索引项,图8中的90属性包含2个索引项),这个时候该目录的索引项由A0属性中的data runs指出。90属性体的结构如下(不包含属性头):
typedef struct _INDEX_HEADER {UINT32 IH_EntryOff;//第一个目录项的偏移UINT32 IH_TalSzOfEntries;//目录项的总尺寸(包括索引头和下面的索引项)UINT32 IH_AllocSize;//目录项分配的尺寸BYTE IH_Flags;/*标志位,此值可能是以下和值之一:0x00 小目录(数据存放在根节点的数据区中)0x01 大目录(需要目录项存储区和索引项位图)*/BYTE IH_Resvd[3];}INDEX_HEADER,*pINDEX_HEADER;//INDEX_ROOT 0X90属性体typedef struct _INDEX_ROOT {//索引根UINT32 IR_AttrType;//属性的类型UINT32 IR_ColRule;//整理规则UINT32 IR_EntrySz;//目录项分配尺寸BYTE IR_ClusPerRec;//每个目录项占用的簇数BYTE IR_Resvd[3];//索引头INDEX_HEADER IH;//索引项 可能不存在BYTE IR_IndexEntry[0];}INDEX_ROOT,*pINDEX_ROOT;
由于这里90属性没有索引项,我们直接看A0属性。
A0属性的属性体即为Data Runs,指向若干个索引区域的簇流(若干个物理上连续的簇称为一个簇流)。以上图的Data Runs为例:【11012C00 00 00 00 00】第一个字节【11】中的第二个1,表示接下来占用“1”个字节,用来存储该簇流占用簇的个数,即紧随其后的一个字节【01】,表示这个簇流占了0X01个簇的空间。第一个字节【11】中的第一个1,表示接下再占用“1”个字节,用来存储该簇流的偏移,即字节【2C】,表示偏移量为0X2C个簇。再往后一个字节为【00】,是结束标志。也就是说,该Data Runs只有一个簇流,而这个簇流包含一个簇,簇流的起始偏移为0X2C个簇,一个簇为8个扇区,即根目录所指向的索引区的偏移为44 * 8 = 352个扇区。
下面再给一个多簇流的data runs:
图12 Data Runs
十六进制的Data Runs 如下:31 05 F9 FF 0B 21 01 4E FF 11 01 12 31 01 12 CA F4 21 02 31 12 21 02 00 48 21 04 C9 0F 21 04 C9 59 31 04 87 11 01 21 08 57 10 00 02 A0 F8 FF FF。下面是对该Data Runs的解析:
图13 Data Runs解析
从上图可以看出,该Data Runs一共包含了10个簇流,其中第一个簇流占用了5个簇,起始偏移为0X0BFFF9,即786425个簇;第二个簇流占用1个簇,相对于第一个簇流偏移【-178】个簇,即第二个簇流起始偏移为786425 – 178 = 786247个簇。接下来的簇流的计算方法是一样的。
第五步:读取索引项
回到图11,我们计算出data runs指向的索引区的偏移为352个扇区,转到第352扇区,即索引区第一个扇区的位置,我们将看到如下数据(只截取了部分索引项):
图14 索引区域
索引区域占用了若干个簇,每一个簇都包含了两部分:一个标准索引头和若干个标准索引项。这两部分的结构定义如下:
//标准索引头的结构typedef struct _STD_INDEX_HEADER {BYTE SIH_Flag[4]; //固定值 "INDX"UINT16 SIH_USNOffset;//更新序列号偏移UINT16 SIH_USNSize;//更新序列号和更新数组大小UINT64 SIH_Lsn;// 日志文件序列号(LSN)UINT64 SIH_IndexCacheVCN;//本索引缓冲区在索引分配中的VCNUINT32 SIH_IndexEntryOffset;//索引项的偏移 相对于当前位置UINT32 SIH_IndexEntrySize;//索引项的大小UINT32 SIH_IndexEntryAllocSize;//索引项分配的大小UINT8 SIH_HasLeafNode;//置一 表示有子节点BYTE SIH_Fill[3];//填充UINT16 SIH_USN;//更新序列号BYTE SIH_USNArray[0];//更新序列数组}STD_INDEX_HEADER,*pSTD_INDEX_HEADER;//标准索引项的结构typedef struct _STD_INDEX_ENTRY {UINT64 SIE_MFTReferNumber;//文件的MFT参考号UINT16 SIE_IndexEntrySize;//索引项的大小UINT16 SIE_FileNameAttriBodySize;//文件名属性体的大小UINT16 SIE_IndexFlag;//索引标志BYTE SIE_Fill[2];//填充UINT64 SIE_FatherDirMFTReferNumber;//父目录MFT文件参考号FILETIME SIE_CreatTime;//文件创建时间FILETIME SIE_AlterTime;//文件最后修改时间FILETIME SIE_MFTChgTime;//文件记录最后修改时间FILETIME SIE_ReadTime;//文件最后访问时间UINT64 SIE_FileAllocSize;//文件分配大小UINT64 SIE_FileRealSize;//文件实际大小UINT64 SIE_FileFlag;//文件标志UINT8 SIE_FileNameSize;//文件名长度UINT8 SIE_FileNamespace;//文件命名空间BYTE SIE_FileNameAndFill[0];//文件名和填充}STD_INDEX_ENTRY,*pSTD_INDEX_ENTRY;
在标准索引头中,几个比较重要的数据如下:
(1)索引项的偏移:即第一个标准索引项的偏移。在上图中为0X 00 00 00 40,这个偏移是相对于当前位置的偏移,即相对于字节【40】而言,偏移0X40个字节。
(2)索引项的大小:表示这个簇中,有效的索引项和索引头的总字节数。在上图中为0X 00 00 0A 80,即2688个字节,超出该范围的索引项为无效的索引项。
我们顺序读取每一个索引项,找到路径E:\dir1_0\dir2_0\dir3_1\新建文本文档.txt中目录dir1_0的索引项:
图15 目录dir1_0的索引项
标准索引项的结构已给出,其中几个重要的数据:
(1)文件的MFT参考号:低6字节是目录或者文件对应的文件记录的编号,由于MFT是顺序存储的,根据该编号可以定位到该文件记录在MFT中的位置。在上图中,目录dir1_0的MFT参考号为0X 00 00 00 00 00 2A,即dir1_0的文件记录是MFT的第0X2A,即第42项。之前已计算出MFT的偏移为6,291,456扇区,每一项占用2个扇区,因此dir1_0的文件记录的偏移为6,291,456 + 2 * 42 = 6291540扇区。
(2)索引项的长度:即本索引项占用的字节数,定义该索引项的边界。
第六步:现在可以递归啦
我们转到第6291540扇区,即dir1_0的文件记录,按照先前的步骤,去解析90属性和A0属性,就能进入下一层目录,再去定位索引项,越来越深,直到列出该目录的树形结构。OK,还是慢慢来,dir1_0的文件记录如下:
图16 dir1_0的文件记录
可以看到,30属性中的文件名为dir1_0,90属性中无索引项,A0属性的data runs为【11 01 2A 00 00 00 00 00】,计算得出dir1_0的索引区占用0X01个簇,偏移为0X2A个簇,即0X2A * 8 = 336扇区,转到336扇区,会看到如下索引数据:
图17 目录dir1_0下的索引项
找到路径E:\dir1_0\dir2_0\dir3_1\新建文本文档.txt中dir2_0的索引项,即上图的标准索引项1。得到dir2_0的文件记录的MFT编号为0X2D,可以计算出dir2_0的文件记录的偏移:MFT的起始偏移+MFT编号*2,即6,291,456 + 0X2D * 2 = 6291546扇区。读取该文件记录:
图18 dir2_0的文件记录
这个和以前有些不一样了,因为dir2_0只有两个子目录,90属性就够用了,因此没有A0属性。上面说过,90属性体由三部分构成:索引根、索引头和索引项。而90属性中的索引项和标准索引项的结构是一致的。接下来呢,我们找到路径E:\dir1_0\dir2_0\dir3_1\新建文本文档.txt中dir3_1的索引项,即上图中的索引项2,像以前一样计算出它的文件记录在MFT的位置并算出偏移扇区……
后面不再列举啦,一直找到新建文本文档.txt的文件记录,如下:
图19 新建文本文档.txt的文件记录
第七步:读取文件内容
对于文件呢,我们关注的是它的80属性,即数据属性。因为这个txt文件比较大,在MFT中无法存放其所有数据,因此这个文件的80属性为非常驻属性,属性体是一个data runs,指向存放该文件数据的簇。这里Data Runs的计算方法和前面是一致的,从上图可以看到,该data runs指向一个簇流,该簇流一共占用了0X 00 A9 89个簇,起始偏移为0X 4C C8 40个簇,即40256000扇区。我们转到该扇区,就可以读取这个txt中存储的数据了,截图如下:
图20 .txt中的数据
到这里我们已经读到了这个文本文档中的数据了。对于小文件而言,常驻的80属性就能够存放它的数据内容,比如下面这个文件:
图21 小文件的80属性
这个文本文档中只有一个字符串“sadfasdfasdf”,能够存放在常驻的80属性中,因此80属性的属性体并不是Data Runs,而是直接存放了文件内容。
嗯,到这里其实也差不多了,后面附上我写的测试代码吧。代码跳过了活动分区,因为活动分区比较活跃,最好是能够先打一个快照再去读取。
也传到github了:/Hilaver/NtfsResolution/
如果后面想到了其他的东西再做补充。
下面是主程序的测试代码:
// ConsoleApplication.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "ntfs.h"#include "fat32.h"using namespace std;//#pragma comment(lib, "Shlwapi.lib")//#pragma comment(lib, "Kernel32.lib")//#pragma comment(lib, "version.lib")#pragma pack(1)#pragma warning(disable : 4996) typedef struct _PHY_INFO {DWORD number;vector<TCHAR> vols;}PHY_INFO,*pPHY_INFO;typedef struct _VCN_LCN_SIZE {UINT64 VCN;UINT64 LCN;UINT64 SIZE;_VCN_LCN_SIZE() {}_VCN_LCN_SIZE(UINT64 vcn, UINT64 lcn, UINT64 size) {this->VCN = vcn;this->LCN = lcn;this->SIZE = size;}~_VCN_LCN_SIZE() {}}VCN_LCN_SIZE, *pVCN_LCN_SIZE;FILE *fp;//error msgvoid GetErrorMessage(DWORD dwErrCode, DWORD dwLanguageId) {//dwLanguageId=0DWORD dwRet = 0;LPTSTR szResult = NULL;setlocale(LC_ALL, "chs");dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,dwErrCode, dwLanguageId, (LPTSTR)&szResult, 0, NULL);if (dwRet == 0) { szResult = NULL; _tprintf(_T("No such errorCode\n")); }else { _tprintf(_T("%s"), szResult); }szResult = NULL;return;}//char to WCHARWCHAR * charToWCHAR(char *s) {int w_nlen = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);WCHAR *ret;ret = (WCHAR*)malloc(sizeof(WCHAR)*w_nlen);memset(ret, 0, sizeof(ret));MultiByteToWideChar(CP_ACP, 0, s, -1, ret, w_nlen);return ret;}//读取物理磁盘//物理磁盘设备号 起始偏移(Byte) 读取长度(最小一个扇区) 输出缓冲DWORD ReadDisk(DWORD physicalDriverNumber, UINT64 startOffset, DWORD size, PVOID ret) {OVERLAPPED over = { 0 };over.Offset = startOffset & (0xFFFFFFFF);over.OffsetHigh = (startOffset >> 32)&(0xFFFFFFFF);CHAR PHYSICALDRIVE[MAX_PATH]; memset(PHYSICALDRIVE, 0, sizeof(PHYSICALDRIVE));strcpy(PHYSICALDRIVE, "\\\\.\\PHYSICALDRIVE");PHYSICALDRIVE[strlen(PHYSICALDRIVE)] = '0' + physicalDriverNumber;LPCWSTR PD = charToWCHAR(PHYSICALDRIVE);HANDLE handle = CreateFile(PD, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);if (handle == INVALID_HANDLE_VALUE) return 0;DWORD readsize;if (ReadFile(handle, ret, size, &readsize, &over) == 0){CloseHandle(handle);return 0;}CloseHandle(handle);return readsize;}//根据逻辑分区获取其物理磁盘设备号DWORD GetPhysicalDriveFromPartitionLetter(TCHAR letter){HANDLE hDevice;// handle to the drive to be examinedBOOL result; // results flagDWORD readed; // discard resultsSTORAGE_DEVICE_NUMBER number; //use this to get disk numbersCHAR path[MAX_PATH];sprintf(path, "\\\\.\\%c:", letter);//printf("%s\n", path);hDevice = CreateFile(charToWCHAR(path), // drive to openGENERIC_READ | GENERIC_WRITE, // access to the driveFILE_SHARE_READ | FILE_SHARE_WRITE, //share modeNULL, // default security attributesOPEN_EXISTING, // disposition0,// file attributesNULL); // do not copy file attributeif (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive{GetErrorMessage(GetLastError(), 0);//fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());return DWORD(-1);}result = DeviceIoControl(hDevice,// handle to deviceIOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCodeNULL, // lpInBuffer0, // nInBufferSize&number, // output buffersizeof(number), // size of output buffer&readed, // number of bytes returnedNULL// OVERLAPPED structure);if (!result) // fail{fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());(void)CloseHandle(hDevice);return (DWORD)-1;}//printf("DeviceType(设备类型) is %d, DeviceNumber(物理设备号) is %d, PartitionNumber(分区号) is %d\n", number.DeviceType, number.DeviceNumber, number.PartitionNumber);(void)CloseHandle(hDevice);return number.DeviceNumber;}//获取逻辑分区的信息如卷标、空间等void getVolumeInfo(LPCWSTR volumeName) {DWORD dwTotalClusters;//总的簇 DWORD dwFreeClusters;//可用的簇 DWORD dwSectPerClust;//每个簇有多少个扇区 DWORD dwBytesPerSect;//每个扇区有多少个字节 BOOL bResult = GetDiskFreeSpace((volumeName), &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);printf(("总簇数:%d\n可用簇数:%d\n簇内扇区数:%d\n扇区内字节数:%d\n"), dwTotalClusters, dwFreeClusters, dwSectPerClust, dwBytesPerSect);//GetErrorMessage(GetLastError(),0);}//print buffer in bytevoid printBuffer(PVOID buffer, __int64 size) {BYTE *p = (BYTE*)buffer;__int64 pos = 0;while (pos < size) {fprintf(fp,"%+02X ", *(p++));//printf("%+02X ", *(p++));if (++pos % 16 == 0) { fprintf(fp,"\n"); /*printf("\n");*/}}}void printBuffer2(PVOID buffer, __int64 size) {BYTE *p = (BYTE*)buffer;__int64 pos = 0;while (pos < size) {/*fprintf(fp, "%+02X ", *(p++));*/printf("%+02X ", *(p++));if (++pos % 16 == 0) { /*fprintf(fp, "\n");*/ printf("\n"); }}}//不固定字节数的number转为带符号的INT64INT64 Bytes2Int64(BYTE *num,UINT8 bytesCnt) {INT64 ret = 0; bool isNegative = false;memcpy(&ret, num, bytesCnt);if (ret&(1 << (bytesCnt * 8 - 1))) {isNegative = true;INT64 tmp = (INT64(0X01)) << bytesCnt * 8;for (int i = 0; i < 64 - bytesCnt * 8; i++) {ret |= (tmp);tmp <<= 1;}}return isNegative ? -(~(ret)+1) : ret;}//获取物理磁盘设备号vector<PHY_INFO> getPhyDriverNumber() {//这个地方应该有更好的实现方法vector<TCHAR> phyvols[32];DWORD dwSize = GetLogicalDriveStrings(0, NULL);char* drivers = (char*)malloc(dwSize * 2);DWORD dwRet = GetLogicalDriveStrings(dwSize, (LPWSTR)drivers);wchar_t* lp = (wchar_t*)drivers;//所有逻辑驱动器的根驱动器路径 用0隔开DWORD tmpNum = 0;while (*lp) {tmpNum = GetPhysicalDriveFromPartitionLetter(lp[0]);phyvols[tmpNum].push_back(lp[0]);lp += (wcslen(lp) + 1);//下一个根驱动器路径}vector<PHY_INFO> tmpPhyInfo;for (int i = 0; i < 32; i++) {if (phyvols[i].size() != 0) {PHY_INFO tmp;tmp.number = i;tmp.vols = phyvols[i];tmpPhyInfo.push_back(tmp);}}return tmpPhyInfo;}void parseMFTEntry(PVOID MFTEntry, DWORD IndexEntrySize, DWORD phyDriverNumber, UINT64 volByteOffset, UINT8 secPerCluster, UINT64 mftOffset, WCHAR *filePath);void dfsIndexEntry(PVOID IndexEntryBuf, DWORD IndexEntrySize, DWORD phyDriverNumber, UINT64 volByteOffset, UINT8 secPerCluster, UINT64 mftOffset, WCHAR *filePath);//DFS 索引项//索引项 物理磁盘设备号 每个簇的扇区数 卷的物理偏移(字节) MFT的相对偏移(相对于本分区)void dfsIndexEntry(PVOID IndexEntryBuf, DWORD IndexEntrySize, DWORD phyDriverNumber, UINT64 volByteOffset, UINT8 secPerCluster, UINT64 mftOffset, WCHAR *filePath) {printf("dfsIndexEntry\n");//getchar();//printf("IndexEntryBuf:\n");//printBuffer2(IndexEntryBuf, IndexEntrySize);//printf("\n");//getchar();pSTD_INDEX_ENTRY ptrIndexEntry = (pSTD_INDEX_ENTRY)IndexEntryBuf;//BYTE *ptrOfIndexEntry = (BYTE*)IndexEntryBuf;//获取MFT编号UINT64 mftReferNumber = (ptrIndexEntry->SIE_MFTReferNumber) & 0XFFFFFFFFFFFF;//取低六字节printf("SIE_MFTReferNumber is 0X%X\n", mftReferNumber);fprintf(fp,"SIE_MFTReferNumber is 0X%X\n", mftReferNumber);//读取文件名//ptrOfIndexEntry = ptrIndexEntry->SIE_FileNameAndFill;//UINT32 fileNameBytes = (ptrIndexEntry->SIE_FileNameSize) * 2;//WCHAR *fileName = (WCHAR *)malloc(fileNameBytes + 2);//memset(fileName, 0, fileNameBytes + 2);//memcpy(fileName, ptrIndexEntry->SIE_FileNameAndFill, fileNameBytes);//printf("SIE_FileName is %ls\n", fileName);//读取该索引项对应的MFTUINT64 mftEntryByteOffset = mftReferNumber * MFTEntrySize + mftOffset + volByteOffset;printf("mftOffset is %llu\n", mftOffset);printf("volByteOffset is %llu\n", volByteOffset);printf("mftEntryByteOffset is %llu\n", mftEntryByteOffset);PVOID mftEntryBuf = malloc(MFTEntrySize);ReadDisk(phyDriverNumber, mftEntryByteOffset, MFTEntrySize, mftEntryBuf);//printf("mftEntryBuf:\n");//printBuffer2(mftEntryBuf, MFTEntrySize);//printf("\n");//getchar();//解析MFT的0X90 0XA0属性parseMFTEntry(mftEntryBuf, MFTEntrySize, phyDriverNumber, volByteOffset, secPerCluster, mftOffset, filePath);}//解析MFT表项 获取0X90 0XA0属性//MFT表项 物理设备号 卷物理偏移(字节) 每个簇的扇区数void parseMFTEntry(PVOID MFTEntry, DWORD IndexEntrySize, DWORD phyDriverNumber, UINT64 volByteOffset, UINT8 secPerCluster, UINT64 mftOffset, WCHAR *filePath) {printf("parseMFTEntry\n");//getchar();//printf("MFTEntry:\n");//printBuffer2(MFTEntry, MFTEntrySize);//printf("\n");//getchar();pFILE_RECORD_HEADER MFTEntryHeader = (pFILE_RECORD_HEADER)malloc(sizeof(FILE_RECORD_HEADER));memcpy(MFTEntryHeader, MFTEntry, sizeof(FILE_RECORD_HEADER));fprintf(fp, "MFTEntry : BytesAllocated is %u\n", MFTEntryHeader->BytesAllocated);fprintf(fp, "MFTEntry : BytesInUse is %u\n", MFTEntryHeader->BytesInUse);fprintf(fp,"MFTEntry : MFTRecordNumber is 0X%0X\n", UINT32((MFTEntryHeader->MFTRecordNumber)));printf("MFTEntry : BytesAllocated is %u\n", MFTEntryHeader->BytesAllocated);printf("MFTEntry : BytesInUse is %u\n", MFTEntryHeader->BytesInUse);printf("MFTEntry : MFTRecordNumber is 0X%X\n", UINT32((MFTEntryHeader->MFTRecordNumber)));//UINT32 MFTEntryNumber = MFTEntryHeader->MFTRecordNumber;//printBuffer2(MFTEntryHeader, sizeof(FILE_RECORD_HEADER));UINT16 attriOffset = MFTEntryHeader->AttributeOffset;//第一个属性偏移printf("MFTEntry : AttributeOffset is %hu\n", MFTEntryHeader->AttributeOffset);UINT8 *pointerInMFTEntry;pointerInMFTEntry = (UINT8*)MFTEntry;pointerInMFTEntry += attriOffset;BYTE *pIndexOfMFTEntry = (BYTE*)MFTEntry;pIndexOfMFTEntry += attriOffset;//pIndexOfMFTEntry偏移MFT头部大小 指向第一个属性头UINT32 attriType, attriLen;//BYTE ATTR_ResFlagAttri;//get attributepCommonAttributeHeader pComAttriHeader = (pCommonAttributeHeader)malloc(sizeof(CommonAttributeHeader));//存放目录下所有索引项的vectorvector<pSTD_INDEX_ENTRY>indexEntryOfDir;//一个set 删除重复索引项map<UINT64, BOOL>visMftReferNum;bool finishFlag = FALSE;UINT32 attrCnt = 0;//file pathWCHAR currentFilePath[2048] = { 0 };//MFT项的类型 00表示删除文件,01表示正常文件,02表示删除目录,03表示正常目录printf("isDirectoryOrFile[00删除文件,01正常文件,02删除目录,03正常目录]: %hu\n", MFTEntryHeader->Flags);while (TRUE) {//pIndexOfMFTEntry 现在指向属性头, 后面会加上这个属性的总长 指向下一个属性头memcpy(&attriType, pIndexOfMFTEntry, 4);//attri typeif (attriType == 0xFFFFFFFF) { fprintf(fp, "\nATTR_Type is 0xFFFFFFFF, break\n"); break; }fprintf(fp, "\nattrCnt : %u\n", attrCnt++);fprintf(fp, "##### AttributeHeader #####\n");memset(pComAttriHeader, 0, sizeof(CommonAttributeHeader));//属性头的通用部分memcpy(pComAttriHeader, pIndexOfMFTEntry, sizeof(CommonAttributeHeader));fprintf(fp, "ATTR_Type is 0X%X ATTR_Size is %d\n", pComAttriHeader->ATTR_Type, pComAttriHeader->ATTR_Size);//如果属性总长>1024 先breakif (pComAttriHeader->ATTR_Size > 0x400) { fprintf(fp, "\attriLen is more than 1024, break\n"); break; }fprintf(fp, "ATTR_NamOff is %hu ATTR_NamSz is %d", pComAttriHeader->ATTR_NamOff, pComAttriHeader->ATTR_NamSz);//如果当前指针偏移大于MFT已用字节数 breakif ((pIndexOfMFTEntry - MFTEntry) > MFTEntryHeader->BytesInUse) {fprintf(fp, "\nreach end of BytesInUse, break\n");break;}//resolve attribute headerUINT16 attriHeaderSize = 0;bool isResidentAttri = false;switch (pComAttriHeader->ATTR_ResFlag)//是否常驻属性{case BYTE(0x00): {isResidentAttri = true;//get attribute headerpResidentAttributeHeader residentAttriHeader = (pResidentAttributeHeader)malloc(sizeof(ResidentAttributeHeader));memcpy(residentAttriHeader, pIndexOfMFTEntry, sizeof(ResidentAttributeHeader));fprintf(fp, "\n\n常驻属性\n\n");//fprintf(fp, "ATTR_Size is %u\n", residentAttriHeader->ATTR_Size);fprintf(fp, "ATTR_DatOff[属性头长度] is %hu\n", residentAttriHeader->ATTR_DatOff);attriHeaderSize = residentAttriHeader->ATTR_DatOff;UINT16 ResidentAttributeHeaderSize = residentAttriHeader->ATTR_DatOff;residentAttriHeader = (pResidentAttributeHeader)realloc(residentAttriHeader, ResidentAttributeHeaderSize);memcpy(residentAttriHeader, pIndexOfMFTEntry, ResidentAttributeHeaderSize);//fprintf(fp, "ATTR_AttrNam[属性名] is %ls\n", residentAttriHeader->ATTR_AttrNam);fprintf(fp, "ATTR_DatSz[属性体长度] is %u\n", residentAttriHeader->ATTR_DatSz);fprintf(fp, "ATTR_Indx[属性索引] is %u\n", residentAttriHeader->ATTR_Indx);break;}case BYTE(0x01): {isResidentAttri = false;//get attribute headerfprintf(fp, "\n\n非常驻属性\n\n");pNonResidentAttributeHeader nonResidentAttriHeader = (pNonResidentAttributeHeader)malloc(sizeof(NonResidentAttributeHeader));memcpy(nonResidentAttriHeader, pIndexOfMFTEntry, sizeof(NonResidentAttributeHeader));//fprintf(fp, "\n\n非常驻属性\nATTR_Type is 0x%X\n", nonResidentAttriHeader->ATTR_Type);fprintf(fp, "ATTR_DatOff[属性头长度] is %hu\n", nonResidentAttriHeader->ATTR_DatOff);attriHeaderSize = nonResidentAttriHeader->ATTR_DatOff;UINT16 NonResidentAttributeHeaderSize = nonResidentAttriHeader->ATTR_DatOff;nonResidentAttriHeader = (pNonResidentAttributeHeader)realloc(nonResidentAttriHeader, NonResidentAttributeHeaderSize);memcpy(nonResidentAttriHeader, pIndexOfMFTEntry, NonResidentAttributeHeaderSize);fprintf(fp, "ATTR_StartVCN[起始VCN] is %llu\n", nonResidentAttriHeader->ATTR_StartVCN);fprintf(fp, "ATTR_EndVCN[终止VCN] is %llu\n", nonResidentAttriHeader->ATTR_EndVCN);fprintf(fp, "ATTR_ValidSz[属性实际长度 is %llu\n", nonResidentAttriHeader->ATTR_ValidSz);fprintf(fp, "ATTR_AllocSz[属性分配长度] is %llu\n", nonResidentAttriHeader->ATTR_AllocSz);fprintf(fp, "ATTR_InitedSz[属性初始长度] is %llu\n", nonResidentAttriHeader->ATTR_InitedSz);//fprintf(fp, "ATTR_AttrNam[属性名] is %ls\n", nonResidentAttriHeader->ATTR_AttrNam);break;}default:break;}fprintf(fp, "\n##### END of AttributeHeader #####\n");//resolve attribute dataBYTE *tmpAttriDataIndex = pIndexOfMFTEntry;//待修改//tmpAttriDataIndex += (pComAttriHeader->ATTR_ResFlag == BYTE(0x00) ? sizeof(ResidentAttributeHeader) : sizeof(NonResidentAttributeHeader));tmpAttriDataIndex += (attriHeaderSize);//tmpAttriDataIndex指向属性体switch (pComAttriHeader->ATTR_Type){case 0x00000030: {//文件名属性 可能多个pFILE_NAME ptrFileName = (pFILE_NAME)malloc(sizeof(FILE_NAME));memcpy(ptrFileName, tmpAttriDataIndex, sizeof(FILE_NAME));if (ptrFileName->FN_NamSpace == 0X02) { break; }fprintf(fp, "\n##### FILE_NAME #####\n");printf("\n##### FILE_NAME #####\n");fprintf(fp, "FN_NameSz is %d\n", ptrFileName->FN_NameSz);printf("FN_NameSz is %d\n", ptrFileName->FN_NameSz);fprintf(fp, "FN_NamSpace is %d\n", ptrFileName->FN_NamSpace);printf("FN_NamSpace is %d\n", ptrFileName->FN_NamSpace);//get file nameUINT32 fileNameLen = UINT32(0xFFFF & (ptrFileName->FN_NameSz) + 1) << 1;WCHAR *fileName = (WCHAR*)malloc(fileNameLen);memset(fileName, 0, fileNameLen);memcpy(fileName, tmpAttriDataIndex + sizeof(FILE_NAME), fileNameLen - 2);//printf("FILENAME[0X] is:\n");//printBuffer2(fileName, fileNameLen);//printf("\n");fprintf(fp, "FILENAME is %ls\n", fileName);printf("FILENAME is %ls\n", fileName);memset(currentFilePath, 0, sizeof(currentFilePath));wcscpy(currentFilePath, filePath);wcscat(currentFilePath,L"\\");wcscat(currentFilePath, fileName);printf("\n\n------>\ncurrentFilePath is %ls\n", currentFilePath);fprintf(fp, "---> currentFilePath is %ls\n", currentFilePath);fprintf(fp, "\n##### END of FILE_NAME #####\n");printf("\n##### END of FILE_NAME #####\n");getchar();break;}case 0x00000090: {//INDEX_ROOT 索引根pINDEX_ROOT pIndexRoot = (INDEX_ROOT*)malloc(sizeof(INDEX_ROOT));memcpy(pIndexRoot, tmpAttriDataIndex, sizeof(INDEX_ROOT));fprintf(fp, "\n##### INDEX_ROOT #####\n");fprintf(fp, "IR_EntrySz[目录项的大小,一般是一个簇] is %u\n", pIndexRoot->IR_EntrySz);fprintf(fp, "IR_ClusPerRec[目录项占用的簇数,一般是一个] is %u\n", pIndexRoot->IR_ClusPerRec);fprintf(fp, "IH_TalSzOfEntries[索引根和紧随其后的索引项的大小] is %u\n", pIndexRoot->IH.IH_TalSzOfEntries);fprintf(fp, "IH_EntryOff[第一个索引项的偏移] is %u\n", pIndexRoot->IH.IH_EntryOff);printf("\n##### INDEX_ROOT #####\n");printf("IR_EntrySz[目录项的大小,一般是一个簇] is %u\n", pIndexRoot->IR_EntrySz);printf("IR_ClusPerRec[目录项占用的簇数,一般是一个] is %u\n", pIndexRoot->IR_ClusPerRec);printf("IH_TalSzOfEntries[索引根和紧随其后的索引项的大小] is %u\n", pIndexRoot->IH.IH_TalSzOfEntries);printf("IH_EntryOff[第一个索引项的偏移] is %u\n", pIndexRoot->IH.IH_EntryOff);//0X90属性的实际大小UINT32 attri90Size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + pIndexRoot->IH.IH_TalSzOfEntries;pIndexRoot = (INDEX_ROOT*)realloc(pIndexRoot, attri90Size);memcpy(pIndexRoot, tmpAttriDataIndex, attri90Size);//获取90属性中的索引头INDEX_HEADER IR_IH = pIndexRoot->IH;UINT32 indexTotalSize = pIndexRoot->IH.IH_TalSzOfEntries;//索引头和接下来的索引项的总大小 注意可能没有索引项//获取90属性中的索引项BYTE *pIndexOfEntry, *ptrIndexHeaderStart;//索引项的指针 索引头的指针pIndexOfEntry = pIndexRoot->IR_IndexEntry;ptrIndexHeaderStart = (BYTE*)(&(pIndexRoot->IH));UINT32 indexEntryIn90AttriCnt = 0;while (TRUE) {UINT64 isIndexEntryFinish = 0;memcpy(&isIndexEntryFinish, pIndexOfEntry, 8);if (isIndexEntryFinish == 0X00) {//MFT号是0 breakbreak;}if (pIndexOfEntry - ptrIndexHeaderStart > indexTotalSize) {//超出有效长度 breakbreak;}INDEX_ENTRY *pIndexEntry = (INDEX_ENTRY *)pIndexOfEntry;//printf("IE_FileNameSize is %d\n", pIndexEntry->IE_FileNameSize);UINT32 fileNameBytes = (pIndexEntry->IE_FileNameSize) * 2;WCHAR *fileName = (WCHAR *)malloc(fileNameBytes + 2);memset(fileName, 0, fileNameBytes + 2);memcpy(fileName, pIndexEntry->IE_FileNameAndFill, fileNameBytes);//printf("IE_FileName is %ls\n", fileName);indexEntryIn90AttriCnt++;//printBuffer2(pIndexEntry, pIndexEntry->IE_Size);//printf("\n");//getchar();/*******************************/if (visMftReferNum.find((pIndexEntry->IE_MftReferNumber) & 0XFFFFFFFFFFFF) == visMftReferNum.end() && ((((pIndexEntry->IE_MftReferNumber) >> (8 * 6)) & 0XFFFF) != 0X00)) {// $ObjId的索引项正常 但其指向的MFT的90属性异常 其中该MFT号的高2位为0 90属性中的MFT引用号异常//这里读到了索引项printf("find index entry in 90 attribute, cnt is %d\n", indexEntryIn90AttriCnt);fprintf(fp, "find index entry in 90 attribute, cnt is %d\n", indexEntryIn90AttriCnt);indexEntryOfDir.push_back(pSTD_INDEX_ENTRY(pIndexEntry));visMftReferNum.insert(pair<UINT64, BOOL>((pIndexEntry->IE_MftReferNumber) & 0XFFFFFFFFFFFF, TRUE));}//dfsIndexEntry((PVOID)pIndexEntry, pIndexEntry->IE_Size, phyDriverNumber, volByteOffset, secPerCluster, mftOffset);/*******************************/pIndexOfEntry += pIndexEntry->IE_Size;//getchar();}printf("\n##### END of INDEX_ROOT #####\n");break;}case 0x000000A0: {//INDEX_ALLOCATIONfprintf(fp, "\n##### INDEX_ALLOCATION #####\n");printf("\n##### INDEX_ALLOCATION #####\n");//A0属性体//存放VCN LCNvector<VCN_LCN_SIZE>dataRuns;//data runs 起始指针BYTE *dataRunsStartOffset = tmpAttriDataIndex;//data runs 总字节数UINT32 attriBodySize = (pIndexOfMFTEntry + pComAttriHeader->ATTR_Size) - tmpAttriDataIndex;//printf("attriBodySize is %u\n", attriBodySize);//getchar();UINT64 vcnCnt = 0;while ((*dataRunsStartOffset) != 0X00 && dataRunsStartOffset - tmpAttriDataIndex < attriBodySize) {UINT8 bytesClustersOfStdIndex = (*dataRunsStartOffset) & 0X0F, bytesCluOffsetOfStdIndex = (*dataRunsStartOffset >> 4) & 0X0F;UINT32 totalBytes = bytesClustersOfStdIndex + bytesCluOffsetOfStdIndex;//VCN.push_back(vcnCnt++);UINT64 clustersOfStdIndex = 0;INT64 cluOffsetOfStdIndex = 0;//BYTE *ptrInDataRuns = (BYTE *)(&dataRuns);memcpy(&clustersOfStdIndex, dataRunsStartOffset + 1, bytesClustersOfStdIndex);//memcpy(&cluOffsetOfStdIndex, dataRunsStartOffset + 1 + bytesClustersOfStdIndex, bytesCluOffsetOfStdIndex);cluOffsetOfStdIndex = Bytes2Int64(dataRunsStartOffset + 1 + bytesClustersOfStdIndex, bytesCluOffsetOfStdIndex);//printf("Bytes2Int64 is %lld\n",Bytes2Int64(dataRunsStartOffset + 1 + bytesClustersOfStdIndex, bytesCluOffsetOfStdIndex));if (vcnCnt == 0) {dataRuns.push_back(VCN_LCN_SIZE(vcnCnt, cluOffsetOfStdIndex, clustersOfStdIndex));}else {dataRuns.push_back(VCN_LCN_SIZE(vcnCnt, dataRuns[vcnCnt-1].LCN + cluOffsetOfStdIndex, clustersOfStdIndex));}vcnCnt++;dataRunsStartOffset += (totalBytes + 1);}printf("-->INDEX_ALLOCATION Cluster Info\n");for (int i = 0; i < dataRuns.size(); i++) {printf("VCN, LCN, SIZE: %llu, %llu, %llu\n",dataRuns[i].VCN, dataRuns[i].LCN, dataRuns[i].SIZE);}printf("-->End of INDEX_ALLOCATION Cluster Info\n");getchar();for (int i = 0; i < dataRuns.size(); i++) {DWORD stdIndexSize = dataRuns[i].SIZE * secPerCluster*SectorSize;//标准索引占用的总字节数UINT64 stdIndexByteOffset = volByteOffset + dataRuns[i].LCN * secPerCluster*SectorSize;//标准索引的物理偏移//读取标准索引区的数据 该data run 的N个簇全部读取PVOID pStdIndexBuffer = malloc(stdIndexSize);ReadDisk(phyDriverNumber, stdIndexByteOffset, stdIndexSize, pStdIndexBuffer);fprintf(fp, "->data run cnt: %d\n", i);printf("->data run cnt: %d\n", i);UINT32 indexClusterCnt = 0;//逐个簇分析UINT32 indexEntryInIndxArea = 0;while (indexClusterCnt < dataRuns[i].SIZE) {//fprintf(fp, "->cluster cnt: %d\n", indexClusterCnt);printf("->cluster cnt: %d\n", indexClusterCnt);//读取标准索引的头部BYTE *ptrOfStdIndexBuffer = (BYTE*)pStdIndexBuffer + (indexClusterCnt*secPerCluster*SectorSize);//标准索引的指针indexClusterCnt++;//ptrIndex指向该索引簇的起始位置BYTE *ptrIndex = ptrOfStdIndexBuffer;pSTD_INDEX_HEADER pStdIndexHeader = (pSTD_INDEX_HEADER)malloc(sizeof(STD_INDEX_HEADER));memcpy(pStdIndexHeader, ptrOfStdIndexBuffer, sizeof(STD_INDEX_HEADER));UINT32 SIH_Flag;memcpy(&SIH_Flag, pStdIndexHeader->SIH_Flag, 4);if (SIH_Flag == 0X00000000) { break; }//标准索引头的大小=第一个索引项的偏移+24字节UINT32 stdIndexHeaderSize = pStdIndexHeader->SIH_IndexEntryOffset + 8 * 3;pStdIndexHeader = (pSTD_INDEX_HEADER)realloc(pStdIndexHeader, stdIndexHeaderSize);memcpy(pStdIndexHeader, ptrOfStdIndexBuffer, stdIndexHeaderSize);//printfprintf(fp, "\n##### STD_INDEX_HEADER #####\n");fprintf(fp, "SIH_IndexEntryOffset[索引项偏移,从此位置开始] is %u\n", pStdIndexHeader->SIH_IndexEntryOffset);fprintf(fp, "SIH_IndexEntrySize[索引项总大小] is %u\n", pStdIndexHeader->SIH_IndexEntrySize);printf("\n##### STD_INDEX_HEADER #####\n");printf("SIH_IndexEntryOffset[索引项偏移,从此位置开始] is %u\n", pStdIndexHeader->SIH_IndexEntryOffset);printf("SIH_IndexEntrySize[索引项总大小] is %u\n", pStdIndexHeader->SIH_IndexEntrySize);UINT32 stdIndexTotalSize = pStdIndexHeader->SIH_IndexEntrySize;printf("SIH_IndexEntryAllocSize[索引项总分配大小] is %u\n", pStdIndexHeader->SIH_IndexEntryAllocSize);printf("\n##### END of STD_INDEX_HEADER #####\n");fprintf(fp, "SIH_IndexEntryAllocSize[索引项总分配大小] is %u\n", pStdIndexHeader->SIH_IndexEntryAllocSize);fprintf(fp, "\n##### END of STD_INDEX_HEADER #####\n");//指针移动到索引项ptrOfStdIndexBuffer += stdIndexHeaderSize;//BYTE *ptrIndexEntryStartOffset = ptrOfStdIndexBuffer;while (TRUE) {//MFT编号为0 breakUINT64 isIndexEntryFinish = 0;memcpy(&isIndexEntryFinish, ptrOfStdIndexBuffer, 8);//超出有效长度 breakif (ptrOfStdIndexBuffer - ptrIndex > stdIndexTotalSize) {fprintf(fp, "超出INDEX有效范围, break\n");printf("超出INDEX有效范围, break\n");break;}//if (isIndexEntryFinish == UINT64(0)) { break; }if (isIndexEntryFinish == UINT64(0)) { fprintf(fp, "find mft refer number[00]\n"); }if (isIndexEntryFinish == UINT64(0)) { printf("find mft refer number[00]\n"); }//printf("ptrOfStdIndexBuffer - ptrIndexEntryStartOffset is %d\n", ptrOfStdIndexBuffer - ptrIndexEntryStartOffset);//逐个读取索引项pSTD_INDEX_ENTRY pStdIndexEntry = (pSTD_INDEX_ENTRY)malloc(sizeof(STD_INDEX_ENTRY));memcpy(pStdIndexEntry, ptrOfStdIndexBuffer, sizeof(STD_INDEX_ENTRY));UINT32 stdIndexEntrySize = pStdIndexEntry->SIE_IndexEntrySize;pStdIndexEntry = (pSTD_INDEX_ENTRY)realloc(pStdIndexEntry, stdIndexEntrySize);memcpy(pStdIndexEntry, ptrOfStdIndexBuffer, stdIndexEntrySize);if (pStdIndexEntry->SIE_MFTReferNumber == UINT64(0)) { break; }//printf("\n##### STD_INDEX_ENTRY #####\n");//fprintf(fp, "\n");//printBuffer(pStdIndexEntry, stdIndexEntrySize);//fprintf(fp, "\n");//printf("SIE_MFTReferNumber is %d\n", (pStdIndexEntry->SIE_MFTReferNumber) & 0XFFFFFFFFFFFF);//printf("SIE_IndexEntrySize[索引项大小] is %u\n", (pStdIndexEntry->SIE_IndexEntrySize));//printf("SIE_FileNameSize is %d\n", (pStdIndexEntry->SIE_FileNameSize));//printf("SIE_FileAllocSize is %llu\n", (pStdIndexEntry->SIE_FileAllocSize));//printf("SIE_FileRealSize is %llu\n", (pStdIndexEntry->SIE_FileRealSize));//UINT8 fileNameBytes = (pStdIndexEntry->SIE_FileNameSize) * 2;//WCHAR *fileName = (WCHAR *)malloc(fileNameBytes + 2);//memcpy(fileName, pStdIndexEntry->SIE_FileNameAndFill, fileNameBytes);//printf("SIE_FileName is %ls\n", fileName);//printf("\n##### END of STD_INDEX_ENTRY #####\n");//这里读到了索引项/********************************/if (visMftReferNum.find((pStdIndexEntry->SIE_MFTReferNumber) & 0XFFFFFFFFFFFF) == visMftReferNum.end() && ((((pStdIndexEntry->SIE_MFTReferNumber) >> (8 * 6)) & 0XFFFF) != 0X00)) {printf("find index entry in INDX area, cnt is %d\n", indexEntryInIndxArea);fprintf(fp, "find index entry in INDX area, cnt is %d\n", indexEntryInIndxArea);indexEntryInIndxArea++;indexEntryOfDir.push_back(pSTD_INDEX_ENTRY(pStdIndexEntry));visMftReferNum.insert(pair<UINT64, BOOL>((pStdIndexEntry->SIE_MFTReferNumber) & 0XFFFFFFFFFFFF, TRUE));}//dfsIndexEntry((PVOID)pStdIndexEntry, stdIndexEntrySize, phyDriverNumber, volByteOffset, secPerCluster, mftOffset);/********************************///ptrOfStdIndexBuffer指向下一个索引项ptrOfStdIndexBuffer += stdIndexEntrySize;//getchar();}//printBuffer2(pStdIndexBuffer, stdIndexSize);//getchar();//fprintf(fp, "\n##### END of INDEX_ALLOCATION #####\n");//printf("\n##### END of INDEX_ALLOCATION #####\n");}}fprintf(fp,"\n##### END of INDEX_ALLOCATION #####\n");printf("\n##### END of INDEX_ALLOCATION #####\n");break;}case 0x00000010: {break;}case 0x00000020: {break;}case 0x00000040: {break;}case 0x00000050: {break;}case 0x00000060: {break;}case 0x00000070: {break;}case 0x00000080: {//文件数据属性//80属性体长度UINT32 attriBodySize = (pIndexOfMFTEntry + pComAttriHeader->ATTR_Size) - tmpAttriDataIndex;//属性体pDATA ptrData = (pDATA)malloc(attriBodySize);memcpy(ptrData, tmpAttriDataIndex, attriBodySize);//如果是常驻属性 80属性体就是文件内容if (isResidentAttri) {//80属性体中的文件内容printf("content in 0X80[resident]:\n");printBuffer2(ptrData, attriBodySize);printf("\n");}//如果是非常驻属性 80属性体是data runselse {printf("content in 0X80[nonresident]:\n");printBuffer2(ptrData, attriBodySize);printf("\n");//存放VCN LCNvector<VCN_LCN_SIZE>dataRuns;//data runs 起始指针BYTE *dataRunsStartOffset = tmpAttriDataIndex;//data runs 总字节数UINT32 attriBodySize = (pIndexOfMFTEntry + pComAttriHeader->ATTR_Size) - tmpAttriDataIndex;//printf("attriBodySize is %u\n", attriBodySize);//getchar();UINT64 vcnCnt = 0;while ((*dataRunsStartOffset) != 0X00 && dataRunsStartOffset - tmpAttriDataIndex < attriBodySize) {UINT8 bytesClustersOfStdIndex = (*dataRunsStartOffset) & 0X0F, bytesCluOffsetOfStdIndex = (*dataRunsStartOffset >> 4) & 0X0F;UINT32 totalBytes = bytesClustersOfStdIndex + bytesCluOffsetOfStdIndex;//VCN.push_back(vcnCnt++);UINT64 clustersOfStdIndex = 0;INT64 cluOffsetOfStdIndex = 0;//BYTE *ptrInDataRuns = (BYTE *)(&dataRuns);memcpy(&clustersOfStdIndex, dataRunsStartOffset + 1, bytesClustersOfStdIndex);//memcpy(&cluOffsetOfStdIndex, dataRunsStartOffset + 1 + bytesClustersOfStdIndex, bytesCluOffsetOfStdIndex);cluOffsetOfStdIndex = Bytes2Int64(dataRunsStartOffset + 1 + bytesClustersOfStdIndex, bytesCluOffsetOfStdIndex);//printf("Bytes2Int64 is %lld\n",Bytes2Int64(dataRunsStartOffset + 1 + bytesClustersOfStdIndex, bytesCluOffsetOfStdIndex));if (vcnCnt == 0) {dataRuns.push_back(VCN_LCN_SIZE(vcnCnt, cluOffsetOfStdIndex, clustersOfStdIndex));}else {dataRuns.push_back(VCN_LCN_SIZE(vcnCnt, dataRuns[vcnCnt - 1].LCN + cluOffsetOfStdIndex, clustersOfStdIndex));}vcnCnt++;dataRunsStartOffset += (totalBytes + 1);}printf("-->DATA Cluster Info\n");for (int i = 0; i < dataRuns.size(); i++) {printf("VCN, LCN, SIZE: %llu, %llu, %llu\n", dataRuns[i].VCN, dataRuns[i].LCN, dataRuns[i].SIZE);}//拿到了文件每一块的LCN和对应的簇数//能够直接读取文件内容了printf("-->End of DATA Cluster Info\n");getchar();}break;}case 0x000000B0: {break;}case 0x000000C0: {break;}case 0x000000D0: {break;}case 0x000000E0: {break;}case 0x000000F0: {break;}case 0x00000100: {break;}default: {finishFlag = TRUE;break;}}pIndexOfMFTEntry += pComAttriHeader->ATTR_Size;if (finishFlag) { break; }}for (int i = 0; i < indexEntryOfDir.size(); i++) {//fprintf(fp,"WATCH :: IndexEntryOfDir -> %d:\n", i);printBuffer(indexEntryOfDir[i], indexEntryOfDir[i]->SIE_IndexEntrySize);fprintf(fp,"\n");//printf("WATCH :: IndexEntryOfDir -> %d:\n", i);//printBuffer2(indexEntryOfDir[i], indexEntryOfDir[i]->SIE_IndexEntrySize);//printf("\n");//getchar();//dfsIndexEntry((PVOID)(indexEntryOfDir[i]), indexEntryOfDir[i]->SIE_IndexEntrySize, phyDriverNumber, volByteOffset, secPerCluster, mftOffset);}for (int i = 0; i < indexEntryOfDir.size(); i++) {////printf("indexEntryOfDir of root:\n");//printBuffer2(indexEntryOfDir[i], indexEntryOfDir[i]->SIE_IndexEntrySize);//printf("\n");//getchar();fprintf(fp,"begin DFS in Dir\n");fprintf(fp,"Index Entry Buff:\n");printBuffer(indexEntryOfDir[i], indexEntryOfDir[i]->SIE_IndexEntrySize);fprintf(fp,"\n");printf("begin DFS in Dir\n");//printf("Index Entry Buff:\n");//printBuffer2(indexEntryOfDir[i], indexEntryOfDir[i]->SIE_IndexEntrySize);printf("\n");if (UINT64((indexEntryOfDir[i]->SIE_MFTReferNumber) & 0XFFFFFFFFFFFF) < 16) {fprintf(fp,"SIE_MFTReferNumber < 16, continue\n");//printf("SIE_MFTReferNumber < 16, continue\n");}else {fprintf(fp,"SIE_MFTReferNumber >= 16, begin search\n");//printf("SIE_MFTReferNumber >= 16, begin search\n");dfsIndexEntry((PVOID)(indexEntryOfDir[i]), indexEntryOfDir[i]->SIE_IndexEntrySize, phyDriverNumber, volByteOffset, secPerCluster, mftOffset, currentFilePath);}//dfsIndexEntry((PVOID)(indexEntryOfDir[i]), indexEntryOfDir[i]->SIE_IndexEntrySize, phyDriverNumber, volByteOffset, secPerCluster, mftOffset);}indexEntryOfDir.clear();visMftReferNum.clear();//getchar();}//解析MFT表项void resolveMFTEntry(PVOID MFTEntry, DWORD phyDriverNumber, UINT64 volByteOffset, UINT8 secPerCluster, UINT64 mftOffset) {//MFT表项 物理设备号 卷物理偏移(字节) 每个簇的扇区数 mft相对于该分区的偏移//printBuffer(MFTEntry, MFTEntrySize);//get MFTEntryHeaderpFILE_RECORD_HEADER MFTEntryHeader = (pFILE_RECORD_HEADER)malloc(sizeof(FILE_RECORD_HEADER));memcpy(MFTEntryHeader, MFTEntry, sizeof(FILE_RECORD_HEADER));fprintf(fp, "MFTEntry : BytesAllocated is %u\n", MFTEntryHeader->BytesAllocated);fprintf(fp, "MFTEntry : BytesInUse is %u\n", MFTEntryHeader->BytesInUse);fprintf(fp, "MFTEntry : MFTRecordNumber is %u\n", MFTEntryHeader->MFTRecordNumber);UINT32 MFTEntryNumber = MFTEntryHeader->MFTRecordNumber;//printBuffer2(MFTEntryHeader, sizeof(FILE_RECORD_HEADER));UINT16 attriOffset = MFTEntryHeader->AttributeOffset;//第一个属性偏移UINT8 *pointerInMFTEntry;pointerInMFTEntry = (UINT8*)MFTEntry;pointerInMFTEntry += attriOffset;BYTE *pIndexOfMFTEntry = (BYTE*)MFTEntry;pIndexOfMFTEntry += attriOffset;//pIndexOfMFTEntry偏移MFT头部大小 指向第一个属性头UINT32 attriType, attriLen;//BYTE ATTR_ResFlagAttri;//get attributepCommonAttributeHeader pComAttriHeader = (pCommonAttributeHeader)malloc(sizeof(CommonAttributeHeader));bool finishFlag = FALSE;UINT32 attrCnt = 0;while (TRUE) {//pIndexOfMFTEntry 现在指向属性头, 后面会加上这个属性的总长 指向下一个属性头memcpy(&attriType, pIndexOfMFTEntry, 4);//attri typeif (attriType == 0xFFFFFFFF) { fprintf(fp, "\nATTR_Type is 0xFFFFFFFF, break\n"); break; }fprintf(fp, "\nattrCnt : %u\n", attrCnt++);fprintf(fp, "##### AttributeHeader #####\n");memset(pComAttriHeader, 0, sizeof(CommonAttributeHeader));//属性头的通用部分memcpy(pComAttriHeader, pIndexOfMFTEntry, sizeof(CommonAttributeHeader));fprintf(fp, "ATTR_Type is 0X%X ATTR_Size is %d\n", pComAttriHeader->ATTR_Type, pComAttriHeader->ATTR_Size);//如果属性总长>1024 先breakif (pComAttriHeader->ATTR_Size > 0x400) { fprintf(fp, "\attriLen is more than 1024, break\n"); break; }fprintf(fp, "ATTR_NamOff is %hu ATTR_NamSz is %d", pComAttriHeader->ATTR_NamOff, pComAttriHeader->ATTR_NamSz);//如果当前指针偏移大于MFT已用字节数 breakif ((pIndexOfMFTEntry - MFTEntry) > MFTEntryHeader->BytesInUse) {fprintf(fp, "\nreach end of BytesInUse, break\n");break;}//resolve attribute headerUINT16 attriHeaderSize = 0;switch (pComAttriHeader->ATTR_ResFlag)//是否常驻属性{case BYTE(0x00): {//get attribute headerpResidentAttributeHeader residentAttriHeader = (pResidentAttributeHeader)malloc(sizeof(ResidentAttributeHeader));memcpy(residentAttriHeader, pIndexOfMFTEntry, sizeof(ResidentAttributeHeader));fprintf(fp, "\n\n常驻属性\n\n");//fprintf(fp, "ATTR_Size is %u\n", residentAttriHeader->ATTR_Size);fprintf(fp, "ATTR_DatOff[属性头长度] is %hu\n", residentAttriHeader->ATTR_DatOff);attriHeaderSize = residentAttriHeader->ATTR_DatOff;UINT16 ResidentAttributeHeaderSize = residentAttriHeader->ATTR_DatOff;residentAttriHeader = (pResidentAttributeHeader)realloc(residentAttriHeader, ResidentAttributeHeaderSize);memcpy(residentAttriHeader, pIndexOfMFTEntry, ResidentAttributeHeaderSize);//fprintf(fp, "ATTR_AttrNam[属性名] is %ls\n", residentAttriHeader->ATTR_AttrNam);fprintf(fp, "ATTR_DatSz[属性体长度] is %u\n", residentAttriHeader->ATTR_DatSz);fprintf(fp, "ATTR_Indx[属性索引] is %u\n", residentAttriHeader->ATTR_Indx);break;}case BYTE(0x01): {//get attribute headerfprintf(fp, "\n\n非常驻属性\n\n");pNonResidentAttributeHeader nonResidentAttriHeader = (pNonResidentAttributeHeader)malloc(sizeof(NonResidentAttributeHeader));memcpy(nonResidentAttriHeader, pIndexOfMFTEntry, sizeof(NonResidentAttributeHeader));//fprintf(fp, "\n\n非常驻属性\nATTR_Type is 0x%X\n", nonResidentAttriHeader->ATTR_Type);fprintf(fp, "ATTR_DatOff[属性头长度] is %hu\n", nonResidentAttriHeader->ATTR_DatOff);attriHeaderSize = nonResidentAttriHeader->ATTR_DatOff;UINT16 NonResidentAttributeHeaderSize = nonResidentAttriHeader->ATTR_DatOff;nonResidentAttriHeader = (pNonResidentAttributeHeader)realloc(nonResidentAttriHeader, NonResidentAttributeHeaderSize);memcpy(nonResidentAttriHeader, pIndexOfMFTEntry, NonResidentAttributeHeaderSize);fprintf(fp, "ATTR_StartVCN[起始VCN] is %llu\n", nonResidentAttriHeader->ATTR_StartVCN);fprintf(fp, "ATTR_EndVCN[终止VCN] is %llu\n", nonResidentAttriHeader->ATTR_EndVCN);fprintf(fp, "ATTR_ValidSz[属性实际长度 is %llu\n", nonResidentAttriHeader->ATTR_ValidSz);fprintf(fp, "ATTR_AllocSz[属性分配长度] is %llu\n", nonResidentAttriHeader->ATTR_AllocSz);fprintf(fp, "ATTR_InitedSz[属性初始长度] is %llu\n", nonResidentAttriHeader->ATTR_InitedSz);//fprintf(fp, "ATTR_AttrNam[属性名] is %ls\n", nonResidentAttriHeader->ATTR_AttrNam);break;}default:break;}fprintf(fp, "\n##### END of AttributeHeader #####\n");//resolve attribute dataBYTE *tmpAttriDataIndex = pIndexOfMFTEntry;//指向属性体tmpAttriDataIndex += (attriHeaderSize);//tmpAttriDataIndex指向属性体switch (pComAttriHeader->ATTR_Type){case 0x00000010: {//STANDARD_INFORMATION 10属性pSTANDARD_INFORMATION stdInfo = (pSTANDARD_INFORMATION)malloc(sizeof(STANDARD_INFORMATION));memcpy(stdInfo, tmpAttriDataIndex, sizeof(STANDARD_INFORMATION));SYSTEMTIME sysTime;FileTimeToSystemTime(&(stdInfo->SI_CreatTime), &sysTime);fprintf(fp, "\n##### STANDARD_INFORMATION #####\n");//printBuffer(stdInfo, sizeof(STANDARD_INFORMATION));fprintf(fp, "\n");fprintf(fp, "SI_CreatTime-wYear is %hu\n", sysTime.wYear);fprintf(fp, "SI_CreatTime-wMonth is %hu\n", sysTime.wMonth);fprintf(fp, "SI_CreatTime-wDay is %hu\n", sysTime.wDay);fprintf(fp, "SI_CreatTime-wHour is %hu\n", sysTime.wHour);fprintf(fp, "SI_CreatTime-wMinute is %hu\n", sysTime.wMinute);fprintf(fp, "SI_CreatTime-wSecond is %hu\n", sysTime.wSecond);fprintf(fp, "SI_AlterTime is %lld\n", stdInfo->SI_AlterTime);fprintf(fp, "SI_DOSAttr is %d\n", stdInfo->SI_DOSAttr);fprintf(fp, "SI_MFTChgTime is %lld\n", stdInfo->SI_MFTChgTime);fprintf(fp, "\n");fprintf(fp, "\n#####END of STANDARD_INFORMATION#####\n\n");break;}case 0x00000020: {//ATTRIBUTE_LIST 20属性break;}case 0x00000030: {//FILE_NAME 可能不止一个pFILE_NAME fileNameInfo = (pFILE_NAME)malloc(sizeof(FILE_NAME));memcpy(fileNameInfo, tmpAttriDataIndex, sizeof(FILE_NAME));fprintf(fp, "\n##### FILE_NAME #####\n");//SYSTEMTIME sysTime;//FileTimeToSystemTime(&(fileNameInfo->FN_AlterTime), &sysTime);//fprintf(fp, "FN_AlterTime-wYear is %hu\n", sysTime.wYear);//fprintf(fp, "FN_AlterTime-wMonth is %hu\n", sysTime.wMonth);//fprintf(fp, "FN_AlterTime-wDay is %hu\n", sysTime.wDay);//fprintf(fp, "FN_AlterTime-wHour is %hu\n", sysTime.wHour);//fprintf(fp, "FN_AlterTime-wMinute is %hu\n", sysTime.wMinute);//fprintf(fp, "FN_NameSz is %hu\n", 0xFFFF & (fileNameInfo->FN_NameSz));//fprintf(fp, "FN_AllocSz is %llu\n", fileNameInfo->FN_AllocSz);//fprintf(fp, "FN_ValidSz is %llu\n", fileNameInfo->FN_ValidSz);//get file nameUINT32 fileNameLen = UINT32(0xFFFF & (fileNameInfo->FN_NameSz) + 1) << 1;WCHAR *fileName = (WCHAR*)malloc(fileNameLen);memset(fileName, 0, fileNameLen);memcpy(fileName, tmpAttriDataIndex + sizeof(FILE_NAME), fileNameLen - 2);fprintf(fp, "FILENAME is %ls\n", fileName);fprintf(fp, "FN_NameSz is %d\n", fileNameInfo->FN_NameSz);fprintf(fp, "FN_NamSpace is %d\n", fileNameInfo->FN_NamSpace);fprintf(fp, "FILENAME is %ls\n", fileName);fprintf(fp, "FN_ParentFR[父目录的MFT号] is %llu\n", (fileNameInfo->FN_ParentFR) & 0XFFFFFFFFFFFF);//getchar();fprintf(fp, "\n#####END of FILE_NAME#####\n\n");break;}case 0x00000040: {//VOLUME_VERSION OBJECT_IDbreak;}case 0x00000050: {//SECURITY_DESCRIPTORbreak;}case 0x00000060: {//VOLUME_NAMEbreak;}case 0x00000070: {//VOLUME_INFORMATIONbreak;}case 0x00000080: {//DATAbreak;}case 0x00000090: {//INDEX_ROOT 索引根//pINDEX_ROOT pIndexRoot = (INDEX_ROOT*)malloc(sizeof(INDEX_ROOT));//memcpy(pIndexRoot, tmpAttriDataIndex, sizeof(INDEX_ROOT));//fprintf(fp, "\n##### INDEX_ROOT #####\n");//fprintf(fp, "IR_EntrySz[目录项的大小,一般是一个簇] is %u\n", pIndexRoot->IR_EntrySz);//fprintf(fp, "IR_ClusPerRec[目录项占用的簇数,一般是一个] is %u\n", pIndexRoot->IR_ClusPerRec);//fprintf(fp, "IH_TalSzOfEntries[索引根和紧随其后的索引项的大小] is %u\n", pIndexRoot->IH.IH_TalSzOfEntries);//fprintf(fp, "IH_EntryOff[第一个索引项的偏移] is %u\n", pIndexRoot->IH.IH_EntryOff);0X90属性的实际大小//UINT32 attri90Size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + pIndexRoot->IH.IH_TalSzOfEntries;//fprintf(fp, "\n##### END of INDEX_ROOT #####\n");break;}case 0x000000A0: {//INDEX_ALLOCATION//fprintf(fp, "\n##### INDEX_ALLOCATION #####\n");A0属性体//pINDEX_ALLOCATION pIndexAlloc = (pINDEX_ALLOCATION)malloc(sizeof(INDEX_ALLOCATION));//memcpy(pIndexAlloc, tmpAttriDataIndex, sizeof(INDEX_ALLOCATION));//fprintf(fp, "\n##### END of INDEX_ALLOCATION #####\n");break;}case 0x000000B0: {//BITMAPbreak;}case 0x000000C0: {//SYMBOL_LINK REPARSE_POINTbreak;}case 0x000000D0: {//EA_INFORMATIONbreak;}case 0x000000E0: {//EAbreak;}case 0x000000F0: {//PROPERTY_SETbreak;}case 0x00000100: {//LOGGED_UNTILITY_STREAMbreak;}default: {finishFlag = TRUE;break;}}pIndexOfMFTEntry += pComAttriHeader->ATTR_Size;if (finishFlag) { break; }}}//解析ntfs DBR扇区void resolveNTFSDBRSector(DWORD phyDriverNumber, UINT64 startSecOffset, pNTFSDBR DBRBuf) {//物理磁盘设备号,DBR物理偏移(Byte),DBR扇区数据fprintf(fp, "bytePerSector is %hu\n", DBRBuf->bytePerSector);fprintf(fp, "secPerCluster is %hu\n", DBRBuf->secPerCluster);fprintf(fp, "totalSectors is %llu\n", DBRBuf->totalSectors);fprintf(fp, "MFT offset(logical cluster number) is %llu\n", DBRBuf->MFT);printf("MFT offset(logical cluster number) is %llu\n", DBRBuf->MFT);//ntfs mft offset(byte)UINT64 MFToffset = UINT64((DBRBuf->MFT)*(DBRBuf->secPerCluster)*(DBRBuf->bytePerSector)) + startSecOffset;fprintf(fp, "MFTMirror offset(logical cluster number) is %llu\n", DBRBuf->MFTMirror);fprintf(fp, "\n");//直接读取MFT第五项 根目录项UINT64 MFTEntryOffset = UINT64(5 * 1024 + MFToffset);fprintf(fp, "MFTEntry[5] Offset is %llu\n", MFTEntryOffset);PVOID tmpMFTEntryBuf = malloc((UINT)MFTEntrySize);memset(tmpMFTEntryBuf, 0, (UINT)MFTEntrySize);ReadDisk(phyDriverNumber, MFTEntryOffset, MFTEntrySize, tmpMFTEntryBuf);//路径WCHAR filePath[2048] = { 0 };//这里开始深搜目录树parseMFTEntry(tmpMFTEntryBuf, MFTEntrySize, phyDriverNumber, startSecOffset, DBRBuf->secPerCluster, UINT64((DBRBuf->MFT)*(DBRBuf->secPerCluster)*(DBRBuf->bytePerSector)), filePath);return;//这里是顺序读取MFT 能读到被删的文件信息//读取MFT表项UINT64 MFTEntryCnt = 0;UINT32 mftEntryFlag;printf("Analyzing MFT......\n");printf("MFTEntryCnt");while (TRUE) {//while (MFTEntryCnt<128) {fprintf(fp, "\n\n########### MFT ENTRY ##########\n");fprintf(fp, "MFTEntryCnt is %llu\n", MFTEntryCnt);printf("%llu\t", MFTEntryCnt);//PVOID tmpMFTEntryBuf = malloc((UINT)MFTEntrySize);//get MFTEntryUINT64 MFTEntryOffset = UINT64((MFTEntryCnt++) * 1024 + MFToffset);fprintf(fp, "MFTEntryOffset is %llu\n", MFTEntryOffset);PVOID tmpMFTEntryBuf = malloc((UINT)MFTEntrySize);memset(tmpMFTEntryBuf, 0, (UINT)MFTEntrySize);ReadDisk(phyDriverNumber, MFTEntryOffset, MFTEntrySize, tmpMFTEntryBuf);//解析MFT表项mftEntryFlag = 0X00;memcpy(&mftEntryFlag, tmpMFTEntryBuf, (UINT)4);if (mftEntryFlag != MFTEntryFlag) { fprintf(fp, "\nMiss MFTEntryFlag, break\n"); break; }//printBuffer(tmpMFTEntryBuf, MFTEntrySize);resolveMFTEntry(tmpMFTEntryBuf, phyDriverNumber, startSecOffset, DBRBuf->secPerCluster, UINT64((DBRBuf->MFT)*(DBRBuf->secPerCluster)*(DBRBuf->bytePerSector)));fprintf(fp, "\n########### END MFT ENTRY ##########\n");}printf("\n");}void run() {fp = fopen("output.txt", "w");vector<PHY_INFO>driverInfos = getPhyDriverNumber();pMBRSector MBRs[64];short int MBRcnt = 0;for (int i = 0; i < driverInfos.size(); i++) {DWORD phyVolCnt = 0;//phyVolCnt = 0;fprintf(fp, "### NOW IN PHYDRIVER - %d ###\n", driverInfos[i].number);printf("### NOW IN PHYDRIVER - %d ###\n", driverInfos[i].number);//MBRs[MBRcnt] = (pMBRSector)malloc(sizeof(MBRSector));//read MBR sectorPVOID tmpMBRBuf = malloc(UINT32(MBRSectorSize));ReadDisk(driverInfos[i].number, 0, MBRSectorSize, tmpMBRBuf);MBRs[MBRcnt] = (pMBRSector)tmpMBRBuf;//for testfprintf(fp, "MBR in physicalDriveNumber%lu:\n", driverInfos[i].number);printf("MBR in physicalDriveNumber%lu:\n", driverInfos[i].number);printBuffer(tmpMBRBuf, MBRSectorSize);printBuffer2(tmpMBRBuf, MBRSectorSize);fprintf(fp, "\n");int PTEntryCnt = 0;while (PTEntryCnt < 4) {fprintf(fp, "PartitionTableEntry%d:\n", PTEntryCnt);fprintf(fp, "bootSignature(引导标志) is %+02X\n", MBRs[MBRcnt]->ptEntrys[PTEntryCnt].bootSignature);fprintf(fp, "systemSignature(分区类型标志) is %+02X\n", MBRs[MBRcnt]->ptEntrys[PTEntryCnt].systemSignature);fprintf(fp, "startSectorNo is %+08X\n", MBRs[MBRcnt]->ptEntrys[PTEntryCnt].startSectorNo);//printf("startSectorNo is %+08X\n", MBRs[MBRcnt - 1]->ptEntrys[PTEntryCnt].startSectorNo);fprintf(fp, "totalSectorsNum is %+08X\n", MBRs[MBRcnt]->ptEntrys[PTEntryCnt].totalSectorsNum);fprintf(fp, "\n");//PTEntryCnt不足四个if (MBRs[MBRcnt]->ptEntrys[PTEntryCnt].startSectorNo == 0x00) {break;}//先跳过活动分区if (MBRs[MBRcnt]->ptEntrys[PTEntryCnt].bootSignature == 0X80) {printf("\n\n\nVolume letter is %c\n\n\n", driverInfos[i].vols[phyVolCnt]);printf("\n\n活动分区, leap it\n");fprintf(fp, "\n\n活动分区, leap it\n");PTEntryCnt++;phyVolCnt++;getchar();continue;}//read DBR or EBR sector PVOID readBuf = malloc(UINT32(SectorSize));UINT64 startSecOffset = UINT64(MBRs[MBRcnt]->ptEntrys[PTEntryCnt].startSectorNo);startSecOffset *= UINT64(SectorSize);fprintf(fp, "startSecOffset is %lld\n", startSecOffset);ReadDisk(driverInfos[i].number, startSecOffset, SectorSize, readBuf);//判断分区类型switch (MBRs[MBRcnt]->ptEntrys[PTEntryCnt].systemSignature){case BYTE(0x0C): {//FAT32printf("\n\n\nVolume letter is %c\n\n\n", driverInfos[i].vols[phyVolCnt++]);getchar();fprintf(fp, "FAT32 DBR sector is\n");//printf("FAT32 DBR sector, continue\n");printBuffer(readBuf, SectorSize);fprintf(fp, "\n");pFAT32_DBR DBRBuf = (pFAT32_DBR)readBuf;//sizeof(FAT32_DBR);fprintf(fp, "Sectors_per_Cluster is %hu\n", DBRBuf->BPB.Sectors_per_Cluster);fprintf(fp, "FATs is %hu\n", DBRBuf->BPB.FATs);fprintf(fp, "FATs is %u\n", DBRBuf->BPB.Large_Sector);fprintf(fp, "System_ID is %llu\n", DBRBuf->Extend_BPB.System_ID);fprintf(fp, "\n");printf("resolve fat32 volume\n");//每扇区字节数UINT16 bytesPerSector = DBRBuf->BPB.Bytes_per_Sector;//每簇扇区数UINT8 sectorsPerCluster = DBRBuf->BPB.Sectors_per_Cluster;//保留扇区数UINT16 reservedSectorNum = DBRBuf->BPB.Reserved_Sector;//fat表占用的扇区数UINT32 fat32Sectors = DBRBuf->BPB.Fat32_Sector.Sectors_per_FAT_FAT32;//根目录簇号UINT32 rootCluster = DBRBuf->BPB.Fat32_Sector.Root_Cluster_Number;//定位根目录 保留扇区数+每个FAT表占用扇区数*2+(根目录簇号-2)*每簇扇区数UINT64 rootSectorOffset = reservedSectorNum + fat32Sectors * 2 + (rootCluster - 2)*sectorsPerCluster;printf("rootSectorOffset is %llu\n", rootSectorOffset);getchar();UINT64 rootSectorByteOffset = rootSectorOffset * bytesPerSector + startSecOffset;PVOID tmpBuff = malloc(SectorSize);ReadDisk(driverInfos[i].number, rootSectorByteOffset, SectorSize, tmpBuff);printBuffer2(tmpBuff, SectorSize);getchar();break;}case BYTE(0x07): {//ntfsprintf("\n\n\nVolume letter is %c\n\n\n", driverInfos[i].vols[phyVolCnt++]);getchar();fprintf(fp, "ntfs DBR sector is\n");printf("find ntfs DBR sector\n");printBuffer(readBuf, SectorSize);fprintf(fp, "\n");pNTFSDBR DBRBuf = (pNTFSDBR)readBuf;//解析ntfs dbrresolveNTFSDBRSector(driverInfos[i].number, startSecOffset, DBRBuf);break;}case BYTE(0x0F): {//Extendedfprintf(fp, "EBR sector is\n");printBuffer(readBuf, SectorSize);fprintf(fp, "\n");pMBRSector EBRBuf = (pMBRSector)readBuf;bool isEbrFinish = FALSE;while (TRUE) {if (!(EBRBuf->ptEntrys[1].totalSectorsNum)) { isEbrFinish = TRUE; }printf("find EBR sector, continue\n");fprintf(fp, "this volume : logical startSectorNo is %+08X\n", EBRBuf->ptEntrys[0].startSectorNo);fprintf(fp, "this volume : totalSectorsNum is %+08X\n", EBRBuf->ptEntrys[0].totalSectorsNum);fprintf(fp, "next volume : logical startSectorNo is %+08X\n", EBRBuf->ptEntrys[1].startSectorNo);//本分区DBR的物理偏移UINT64 thisVolPhyStartSecOffset = UINT64(MBRs[MBRcnt]->ptEntrys[PTEntryCnt].startSectorNo)+ UINT64(EBRBuf->ptEntrys[0].startSectorNo);thisVolPhyStartSecOffset *= UINT64(SectorSize);//下一个EBR的物理偏移UINT64 nextVolPhyStartSecOffset = UINT64(MBRs[MBRcnt]->ptEntrys[PTEntryCnt].startSectorNo)+ UINT64(EBRBuf->ptEntrys[1].startSectorNo);nextVolPhyStartSecOffset *= UINT64(SectorSize);printf("nextVolPhyStartSecOffset is %llu\n", nextVolPhyStartSecOffset);getchar();//读取本分区的DBRPVOID tempDBRBuf = malloc(int(DBRSectorSize));ReadDisk(driverInfos[i].number, thisVolPhyStartSecOffset, DBRSectorSize, tempDBRBuf);fprintf(fp, "\nDBR after EBR:\n");printBuffer(tempDBRBuf, DBRSectorSize);fprintf(fp, "\n");switch (EBRBuf->ptEntrys[0].systemSignature){case BYTE(0x07): {//ntfsprintf("\n\n\nVolume letter is %c\n\n\n", driverInfos[i].vols[phyVolCnt++]);getchar();printf("find ntfs DBR sector\n");pNTFSDBR DBRBuf = (pNTFSDBR)tempDBRBuf;//解析ntfs dbrresolveNTFSDBRSector(driverInfos[i].number, thisVolPhyStartSecOffset, DBRBuf);break;}case BYTE(0x0C): {//fat32printf("\n\n\nVolume letter is %c\n\n\n", driverInfos[i].vols[phyVolCnt++]);getchar();printf("FAT32 DBR sector, continue\n");break;}default:break;}if (isEbrFinish) { break; }//读取下一个EBRPVOID tempEBRBuf = malloc(int(EBRSectorSize));ReadDisk(driverInfos[i].number, nextVolPhyStartSecOffset, EBRSectorSize, tempEBRBuf);EBRBuf = pMBRSector(tempEBRBuf);fprintf(fp, "\nnext EBR sector is\n");printBuffer(tempEBRBuf, EBRSectorSize);fprintf(fp, "\n");}break;}default:break;}PTEntryCnt++;fprintf(fp, "\n");}MBRcnt++;fprintf(fp, "\n");}fclose(fp);}//判断卷是否属于USBbool isUsbDev(TCHAR volumePath[]) {HANDLE deviceHandle = CreateFile(volumePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);STORAGE_PROPERTY_QUERY query;memset(&query, 0, sizeof(query));DWORD bytes;STORAGE_DEVICE_DESCRIPTOR devd;//STORAGE_BUS_TYPE用于记录结构,类型要初始化STORAGE_BUS_TYPE busType = BusTypeUnknown;if (DeviceIoControl(deviceHandle, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &devd, sizeof(devd), &bytes, NULL)) {busType = devd.BusType;}CloseHandle(deviceHandle);return busType == BusTypeUsb;}//发现USB设备bool findUsbDev() {bool ret = false;DWORD dwSize = GetLogicalDriveStrings(0, NULL);char* drivers = (char*)malloc(dwSize * 2);DWORD dwRet = GetLogicalDriveStrings(dwSize, (LPWSTR)drivers);wchar_t* lp = (wchar_t*)drivers;//所有逻辑驱动器的根驱动器路径 用0隔开DWORD tmpNum = 0;while (*lp) {CHAR path[MAX_PATH];sprintf(path, "\\\\.\\%c:", lp[0]);if (isUsbDev(charToWCHAR(path))) {//printf("find usb\n");ret = true;break;}lp += (wcslen(lp) + 1);//下一个根驱动器路径}return ret;}void test() {}int _tmain(int argc, TCHAR *argv[], TCHAR *env[]) {setlocale(LC_ALL, "chs");//test();run();system("pause");return 0;}
接下来是结构定义:
//ntfs.h
#pragma once#pragma pack(1)#pragma warning(disable : 4996) #include "stdafx.h"#define MBRlen 446#define minReadSize 512#define MFTEntrySize 1024#define SectorSize 512#define DBRSectorSize 512#define EBRSectorSize 512#define MBRSectorSize 512#define MFTEntryFlag 0x454C4946 //FILE//DPT表项typedef struct _PartTableEntry {BYTE bootSignature;//引导标志BYTE startHead;//CHS寻址方式,起始磁头BYTE startSector;//起始扇区,本字节低六位BYTE startCylinder;//起始磁道(柱面),startSector高二位和本字节BYTE systemSignature;//分区类型标志BYTE endHead;//终止磁头BYTE endSector;//终止扇区BYTE endCylinder;//终止磁道unsigned int startSectorNo;//LBA寻址,起始扇区号unsigned int totalSectorsNum;//该分区扇区总数}PartTableEntry, *pPartTableEntry;//MBR扇区typedef struct _MBRSector {BYTE MBR[MBRlen];PartTableEntry ptEntrys[4];BYTE endSignature[2];}MBRSector, *pMBRSector;//NTFS DBR扇区typedef struct _NTFSDBR {BYTE JMP[3];//跳转指令BYTE FsID[8];//文件系统IDunsigned short int bytePerSector;//每扇区字节数BYTE secPerCluster;//每簇扇区数BYTE reservedBytes[2];//2个保留字节BYTE zeroBytes[3];//三个0字节BYTE unusedBytes1[2];//2个未用字节BYTE mediaType;//媒体类型BYTE unusedBytes2[2];//2个未用字节unsigned short int secPerTrack;//每磁道扇区数unsigned short int Heads;//磁头数unsigned int hideSectors;//隐藏扇区数BYTE unusedBytes3[4];//4个未用字节BYTE usedBytes[4];//4个固定字节unsigned __int64 totalSectors;//总扇区数unsigned __int64 MFT;//MFT起始簇号unsigned __int64 MFTMirror;//MFTMirror文件起始簇号char fileRecord;//文件记录BYTE unusedBytes4[3];//3个未用字节char indexSize;//索引缓冲区大小BYTE unusedBytes5[3];//未用字节BYTE volumeSerialID64[8];//卷序列号unsigned int checkSum;//校验和BYTE bootCode[426];//引导代码BYTE endSignature[2];//结束标志}NTFSDBR, *pNTFSDBR;//MFT表项的结构// 文件记录头typedef struct _FILE_RECORD_HEADER{/*+0x00*/BYTE Type[4]; // 固定值'FILE'/*+0x04*/UINT16 USNOffset; // 更新序列号偏移, 与操作系统有关/*+0x06*/UINT16 USNCount; // 固定列表大小Size in words of Update Sequence Number & Array (S)/*+0x08*/ UINT64 Lsn;// 日志文件序列号(LSN)/*+0x10*/ UINT16 SequenceNumber; // 序列号(用于记录文件被反复使用的次数)/*+0x12*/ UINT16 LinkCount; // 硬连接数/*+0x14*/ UINT16 AttributeOffset; // 第一个属性偏移/*+0x16*/ UINT16 Flags; // flags, 00表示删除文件,01表示正常文件,02表示删除目录,03表示正常目录/*+0x18*/ UINT32 BytesInUse; // 文件记录实时大小(字节) 当前MFT表项长度,到FFFFFF的长度+4/*+0x1C*/ UINT32 BytesAllocated; // 文件记录分配大小(字节)/*+0x20*/ UINT64 BaseFileRecord; // = 0 基础文件记录 File reference to the base FILE record/*+0x28*/ UINT16 NextAttributeNumber; // 下一个自由ID号/*+0x2A*/ UINT16 Pading; // 边界/*+0x2C*/ UINT32 MFTRecordNumber; // windows xp中使用,本MFT记录号/*+0x30*/ UINT16 USN;// 更新序列号/*+0x32*/ BYTE UpdateArray[0];// 更新数组} FILE_RECORD_HEADER, *pFILE_RECORD_HEADER;//常驻属性和非常驻属性的公用部分typedef struct _CommonAttributeHeader {UINT32 ATTR_Type; //属性类型UINT32 ATTR_Size; //属性头和属性体的总长度BYTE ATTR_ResFlag; //是否是常驻属性(0常驻 1非常驻)BYTE ATTR_NamSz; //属性名的长度UINT16 ATTR_NamOff; //属性名的偏移 相对于属性头UINT16 ATTR_Flags; //标志(0x0001压缩 0x4000加密 0x8000稀疏)UINT16 ATTR_Id; //属性唯一ID}CommonAttributeHeader,*pCommonAttributeHeader;//常驻属性 属性头typedef struct _ResidentAttributeHeader {CommonAttributeHeader ATTR_Common;UINT32 ATTR_DatSz; //属性数据的长度UINT16 ATTR_DatOff; //属性数据相对于属性头的偏移BYTE ATTR_Indx; //索引BYTE ATTR_Resvd; //保留BYTE ATTR_AttrNam[0];//属性名,Unicode,结尾无0}ResidentAttributeHeader, *pResidentAttributeHeader;//非常驻属性 属性头typedef struct _NonResidentAttributeHeader {CommonAttributeHeader ATTR_Common;UINT64 ATTR_StartVCN; //本属性中数据流起始虚拟簇号 UINT64 ATTR_EndVCN; //本属性中数据流终止虚拟簇号UINT16 ATTR_DatOff; //簇流列表相对于属性头的偏移UINT16 ATTR_CmpSz; //压缩单位 2的N次方UINT32 ATTR_Resvd;UINT64 ATTR_AllocSz; //属性分配的大小UINT64 ATTR_ValidSz; //属性的实际大小UINT64 ATTR_InitedSz; //属性的初始大小BYTE ATTR_AttrNam[0];}NonResidentAttributeHeader, *pNonResidentAttributeHeader;/*下面是索引结构的定义*///标准索引头的结构typedef struct _STD_INDEX_HEADER {BYTE SIH_Flag[4]; //固定值 "INDX"UINT16 SIH_USNOffset;//更新序列号偏移UINT16 SIH_USNSize;//更新序列号和更新数组大小UINT64 SIH_Lsn;// 日志文件序列号(LSN)UINT64 SIH_IndexCacheVCN;//本索引缓冲区在索引分配中的VCNUINT32 SIH_IndexEntryOffset;//索引项的偏移 相对于当前位置UINT32 SIH_IndexEntrySize;//索引项的大小UINT32 SIH_IndexEntryAllocSize;//索引项分配的大小UINT8 SIH_HasLeafNode;//置一 表示有子节点BYTE SIH_Fill[3];//填充UINT16 SIH_USN;//更新序列号BYTE SIH_USNArray[0];//更新序列数组}STD_INDEX_HEADER,*pSTD_INDEX_HEADER;//标准索引项的结构typedef struct _STD_INDEX_ENTRY {UINT64 SIE_MFTReferNumber;//文件的MFT参考号UINT16 SIE_IndexEntrySize;//索引项的大小UINT16 SIE_FileNameAttriBodySize;//文件名属性体的大小UINT16 SIE_IndexFlag;//索引标志BYTE SIE_Fill[2];//填充UINT64 SIE_FatherDirMFTReferNumber;//父目录MFT文件参考号FILETIME SIE_CreatTime;//文件创建时间FILETIME SIE_AlterTime;//文件最后修改时间FILETIME SIE_MFTChgTime;//文件记录最后修改时间FILETIME SIE_ReadTime;//文件最后访问时间UINT64 SIE_FileAllocSize;//文件分配大小UINT64 SIE_FileRealSize;//文件实际大小UINT64 SIE_FileFlag;//文件标志UINT8 SIE_FileNameSize;//文件名长度UINT8 SIE_FileNamespace;//文件命名空间BYTE SIE_FileNameAndFill[0];//文件名和填充}STD_INDEX_ENTRY,*pSTD_INDEX_ENTRY;/****下面定义的均是属性体的结构 不包括属性头****///STANDARD_INFORMATION 0X10属性体/*SI_DOSAttr取值:0x0001 只读0x0002 隐藏0x0004 系统0x0020 归档0x0040 设备0x0080 常规0x0100 临时文件0x0200 稀疏文件0x0400 重解析点0x0800 压缩0x1000 离线0x2000 无内容索引0x4000 加密*/typedef struct _STANDARD_INFORMATION {FILETIME SI_CreatTime;//创建时间FILETIME SI_AlterTime;//最后修改时间FILETIME SI_MFTChgTime;//文件的MFT修改的时间FILETIME SI_ReadTime;//最后访问时间UINT32 SI_DOSAttr;//DOS文件属性UINT32 SI_MaxVer;//文件可用的最大版本号 0表示禁用UINT32 SI_Ver;//文件版本号 若最大版本号为0 则值为0UINT32 SI_ClassId;//??//UINT64 SI_OwnerId;//文件拥有者ID//UINT64 SI_SecurityId;//安全ID//UINT64 SI_QuotaCharged;//文件最大可使用的空间配额 0表示无限制//UINT64 SI_USN;//文件最后一次更新的记录号#if 0 uint32 QuotaId;uint32 SecurityId;uint64 QuotaCharge;USN Usn;#endif }STANDARD_INFORMATION,*pSTANDARD_INFORMATION;//ATTRIBUTE_LIST 0X20属性体typedef struct _ATTRIBUTE_LIST {UINT32 AL_RD_Type;UINT16 AL_RD_Len;BYTE AL_RD_NamLen;BYTE AL_RD_NamOff;UINT64 AL_RD_StartVCN;//本属性中数据流开始的簇号UINT64 AL_RD_BaseFRS;/*本属性记录所属的MFT记录的记录号注意:该值的低6字节是MFT记录号,高2字节是该MFT记录的序列号*/UINT16 AL_RD_AttrId;//BYTE AL_RD_Name[0];UINT16 AlignmentOrReserved[3];}ATTRIBUTE_LIST,*pATTRIBUTE_LIST;//FILE_NAME 0X30属性体typedef struct _FILE_NAME {UINT64 FN_ParentFR; /*父目录的MFT记录的记录索引。注意:该值的低6字节是MFT记录号,高2字节是该MFT记录的序列号*/FILETIME FN_CreatTime;FILETIME FN_AlterTime;FILETIME FN_MFTChg;FILETIME FN_ReadTime;UINT64 FN_AllocSz;UINT64 FN_ValidSz;//文件的真实尺寸UINT32 FN_DOSAttr;//DOS文件属性UINT32 FN_EA_Reparse;//扩展属性与链接BYTE FN_NameSz;//文件名的字符数BYTE FN_NamSpace;/*命名空间,该值可为以下值中的任意一个0:POSIX可以使用除NULL和分隔符“/”之外的所有UNICODE字符,最大可以使用255个字符。注意:“:”是合法字符,但Windows不允许使用。1:Win32Win32是POSIX的一个子集,不区分大小写,可以使用除““”、“*”、“?”、“:”、“/”、“<”、“>”、“/”、“|”之外的任意UNICODE字符,但名字不能以“.”或空格结尾。2:DOSDOS命名空间是Win32的子集,只支持ASCII码大于空格的8BIT大写字符并且不支持以下字符““”、“*”、“?”、“:”、“/”、“<”、“>”、“/”、“|”、“+”、“,”、“;”、“=”;同时名字必须按以下格式命名:1~8个字符,然后是“.”,然后再是1~3个字符。3:Win32&DOS这个命名空间意味着Win32和DOS文件名都存放在同一个文件名属性中。*/BYTE FN_FileName[0];}FILE_NAME,*pFILE_NAME;//VOLUME_VERSION typedef struct _VOLUME_VERSION {//??}VOLUME_VERSION,*pVOLUME_VERSION;//OBJECT_ID 0X40属性体typedef struct _OBJECT_ID {BYTE OID_ObjID[16];//文件的GUIDBYTE OID_BirthVolID[16];//文件建立时所在卷的IDBYTE OID_BirthID[16];//文件的原始IDBYTE OID_DomainID[16];//对象所创建时所在域的ID}OBJECT_ID, *pOBJECT_ID;//SECRUITY_DESCRIPTOR 0X50属性体typedef struct _SECRUITY_DESCRIPTOR {//??}SECRUITY_DESCRIPTOR,*pSECRUITY_DESCRIPTOR;//VOLUME_NAME 0X60属性体typedef struct _VOLUME_NAME {BYTE VN_Name[0];}VOLUME_NAME,*pVOLUME_NAME;//VOLUME_INFORMATION 0X70属性体typedef struct _VOLUME_INFORMATION{UINT64 VI_Resvd;BYTE VI_MajVer;//卷主版本号BYTE VI_MinVer;//卷子版本号UINT16 VI_Flags;/*标志位,可以是以下各值组合0x0001 脏位,当该值被设置时Windows将会在下次启动时运行chkdsk/F命令。0x0002 日志文件改变尺寸0x0004 卷挂接时升级0x0008 由Windows NT 4挂接0x0010 启动时删除USN0x0020 修复过的ID0x8000 被chkdsk修改过*/}VOLUME_INFORMATION,*pVOLUME_INFORMATION;//DATA 0X80属性体typedef struct _DATA {//??///*+0x10*/ UINT64 StartVcn;// LowVcn 起始VCN 起始簇号 ///*+0x18*/ UINT64 LastVcn;// HighVcn 结束VCN 结束簇号 ///*+0x20*/ UINT16 RunArrayOffset; // 数据运行的偏移 ///*+0x22*/ UINT16 CompressionUnit; // 压缩引擎 ///*+0x24*/ UINT32 Padding0; // 填充 ///*+0x28*/ UINT32 IndexedFlag; // 为属性值分配大小(按分配的簇的字节数计算) ///*+0x30*/ UINT64 AllocatedSize; // 属性值实际大小 ///*+0x38*/ UINT64 DataSize;// 属性值压缩大小 ///*+0x40*/ UINT64 InitializedSize; // 实际数据大小 ///*+0x48*/ UINT64 CompressedSize; // 压缩后大小 BYTE D_data[0];}DATA,*pDATA;typedef struct _INDEX_ENTRY {UINT64 IE_MftReferNumber;/*该文件的MFT参考号。注意:该值的低6字节是MFT记录号,高2字节是该MFT记录的序列号*/UINT16 IE_Size;//索引项的大小 相对于索引项开始的偏移量UINT16 IE_FileNAmeAttriBodySize;//文件名属性体的大小UINT16 IE_Flags;/*标志。该值可能是以下值之一:0x00 普通文件项0x01 有子项0x02 当前项是最后一个目录项在读取索引项数据时应该首先检查该成员的值以确定当前项的类型*/UINT16 IE_Fill;//填充 无意义UINT64 IE_FatherDirMftReferNumber;//父目录的MFT文件参考号FILETIME IE_CreatTime;//文件创建时间FILETIME IE_AlterTime;//文件最后修改时间FILETIME IE_MFTChgTime;//文件记录最后修改时间FILETIME IE_ReadTime;//文件最后访问时间UINT64 IE_FileAllocSize;//文件分配大小UINT64 IE_FileRealSize;//文件实际大小UINT64 IE_FileFlag;//文件标志UINT8 IE_FileNameSize;//文件名长度UINT8 IE_FileNamespace;//文件命名空间BYTE IE_FileNameAndFill[0];//文件名和填充//BYTE IE_Stream[0];//目录项数据,结构与文件名属性的数据相同//UINT64 IE_SubNodeFR;//子项的记录索引。该值的低6字节是MFT记录号,高2字节是该MFT记录的序列号}INDEX_ENTRY,*pINDEX_ENTRY;typedef struct _INDEX_HEADER {UINT32 IH_EntryOff;//第一个目录项的偏移UINT32 IH_TalSzOfEntries;//目录项的总尺寸(包括索引头和下面的索引项)UINT32 IH_AllocSize;//目录项分配的尺寸BYTE IH_Flags;/*标志位,此值可能是以下和值之一:0x00 小目录(数据存放在根节点的数据区中)0x01 大目录(需要目录项存储区和索引项位图)*/BYTE IH_Resvd[3];}INDEX_HEADER,*pINDEX_HEADER;//INDEX_ROOT 0X90属性体typedef struct _INDEX_ROOT {//索引根UINT32 IR_AttrType;//属性的类型UINT32 IR_ColRule;//整理规则UINT32 IR_EntrySz;//目录项分配尺寸BYTE IR_ClusPerRec;//每个目录项占用的簇数BYTE IR_Resvd[3];//索引头INDEX_HEADER IH;//索引项 可能不存在BYTE IR_IndexEntry[0];}INDEX_ROOT,*pINDEX_ROOT;//INDEX_ALLOCATION 0XA0属性体typedef struct _INDEX_ALLOCATION {//UINT64 IA_DataRuns;BYTE IA_DataRuns[0];}INDEX_ALLOCATION,*pINDEX_ALLOCATION;//BITMAPtypedef struct _MFT_ATTR_BITMAP {//??}MFT_ATTR_BITMAP,*pMFT_ATTR_BITMAP;//SYMBOL_LINKtypedef struct _SYMBOL_LINK {//??}SYMBOL_LINK,*pSYMBOL_LINK;//REPARSE_POINTtypedef struct _REPARSE_POINT{UINT32 RP_Type;/*重解析数据类型,该值可以是以下值之一0x20000000 别名0x40000000 最高等待时间0x80000000 微软使用0x68000005 NSS0x68000006 NSS恢复0x68000007 SIS0x68000008 DFS0x88000003 卷挂接点0xA8000004 HSM0xE8000000 硬连接*/UINT16 RP_DatSz;//重解析数据尺寸UINT16 RP_Resvd;//BYTE RP_Data[0];//重解析数据}REPARSE_POINT,*pREPARSE_POINT;//EA_INFORMATIONtypedef struct _EA_INFORMATION {UINT16 EI_PackedSz;//压缩扩展属性尺寸UINT16 EI_NumOfEA;//拥有NEED_EA记录的扩展属性个数UINT32 EI_UnpackedSz;//未压缩扩展属性尺寸}EA_INFORMATION,*pEA_INFORMATION;//EAtypedef struct _EA {UINT32 EA_Next;//下一个扩展属性的偏移(本记录的尺寸)BYTE EA_Flags;//标志位,值取0x80表示需要EABYTE EA_NamLen;//名字数据的长度(M)UINT16 EA_ValLen;//值数据的长度BYTE EA_NameVal[0];//名字数据和值数据}EA,*pEA;//PROPERTY_SETtypedef struct _PROPERTY_SET {//??}PROPERTY_SET,*pPROPERTY_SET;//LOGGED_UNTILITY_STREAMtypedef struct _LOGGED_UNTILITY_STREAM {//??}LOGGED_UNTILITY_STREAM,*pLOGGED_UNTILITY_STREAM;
//fat32.h
#pragma once#pragma pack(1)#pragma warning(disable : 4996) #include "stdafx.h"/*【FAT32分区的结构】【[保留扇区,其中第一个扇区为DBR]||||[FAT1]||||[FAT2]||||[数据区]】*///fat32相关typedef struct _FAT32_Sector{UINT32 Sectors_per_FAT_FAT32;//FAT32中FAT表占用总扇区数UINT16 Extend_Flag;//0-3位表示活动FAT数,7位:0表示在运行时FAT映射到所有FAT//1表示只有一个FAT是活动的,其他位保留UINT16 FS_Version;//文件系统版本,高字节表示主要修订号,低直接表示次要修订号UINT32 Root_Cluster_Number;//根目录簇号,一般取值为2 UINT16 FS_Info_Sector;//文件系统扇区号,一般取1UINT16 Backup_Sector;//备份引导扇区,一般取值为6BYTE Reserved_Sector[12];//保留扇区}FAT32_Sector, *pFAT32_Sector;typedef struct _Basic_BPB{UINT16Bytes_per_Sector;//每个扇区字节数,可取minReadSize,1024,2048,4096,通常取minReadSizeBYTESectors_per_Cluster;//每簇扇区数,可取1,2,4,8,16,32,64,128,FAT32最多跟踪//268,435,445个簇UINT16Reserved_Sector;//保留扇区,表示第一个FAT前的扇区数 【也就是FAT1的偏移】BYTEFATs;//FAT表个数,通常取2UINT16RootEntry;//根目录项数,对FAT32,取值必为0UINT16SmallSector;//小扇区数,对FAT32,取值必为0BYTEMedia;//存储介质描述,F8表示硬盘,F0表示3.5软盘UINT16Sector_per_FAT_FAT16;//针对FAT12/16的每个FAT扇区数,对FAT32,取值为0UINT16Sector_per_Track;//每道扇区数,描述磁盘物理结构UINT16Heads;//磁头数UINT32Hidden_Sector;//该块硬盘前用于存放引导代码及分区表的扇区数 【隐藏扇区】UINT32Large_Sector;//总扇区数,若SmallSector为0,此处表示分区上扇区总数 //可用扇区数 = 总扇区数-保留扇区-FAT表占用扇区_FAT32_SectorFat32_Sector;//FAT32文件系统扇区信息}Basic_BPB, *pBasic_BPB;typedefstruct _FAT32_Extend_BPB{BYTE Physical_Drive;//物理驱动器号,0x80表示物理硬盘,0x00表示软盘驱动器BYTE Reserved;//保留BYTE Extend_Singure;//0x28或0x29以供Windows NT识别UINT32 Vol_Serial;//卷序列号,由格式化时随机获得BYTE Vol_Label[11];//卷标示BYTE System_ID[8];//系统ID,根据格式化的格式为FAT32,FAT16等}FAT32_Extend_BPB, *pFAT32_Extend_BPB;//FAT32 DBR扇区typedef struct _FAT32_DBR{BYTEJumpInstrction[3];//0x00,跳转指令,通常为EB 58 90,其中58指示了,跳转位置,在X86中,58+2就代表跳转到5A处BYTEOEMID[8];//0x03,厂商标示和OS版本信息_Basic_BPBBPB;//0x0B_FAT32_Extend_BPBExtend_BPB;//0x40BYTE Boot_Strap[420];//文件系统引导代码//0x5A,引导区代码BYTE endSignature[2];//0x01FE,结束标示}FAT32_DBR, *pFAT32_DBR;
//stdafx.h
// stdafx.h : 标准系统包含文件的包含文件,// 或是经常使用但不常更改的// 特定于项目的包含文件//#pragma once#include "targetver.h"#include <stdio.h>#include <tchar.h>#include <stdarg.h>#include <windows.h>#include <map>#include <vector>#include <Setupapi.h>//#include <winioctl.h>//#include <tchar.h>//#include <Shlwapi.h>//#include <math.h>//#include <time.h>#include <string>//#include <TlHelp32.h>//#include <process.h>//#include <iostream>//#include <VersionHelpers.h>//#include <ShlObj.h>//#include <assert.h>//#include <WinBase.h>//#include <fstream>//#include <strsafe.h>//#include <io.h>//#include "md5.h"//#include <unordered_map>// TODO: 在此处引用程序需要的其他头文件