必威体育Betway必威体育官网
当前位置:首页 > IT技术

NTFS文件系统详解 之 文件定位

时间:2019-10-14 16:14:34来源:IT技术作者:seo实验室小编阅读:59次「手机版」
 

ntfs

一如既往的叨叨

  首先要对硬盘分区(MBR、GPT)和文件系统(NTFS、fat32等)有一定的认识,要知道MBR扇区以及DBR扇区的基本结构,如果后面遇到不清楚的地方可以参考上一篇文章https://blog.csdn.net/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:Win32 Win32是POSIX的一个子集,不区分大小写,可以使用除““”、“*”、“?”、“:”、“/”、“<”、“>”、“/”、“|”之外的任意UNICODE字符,但名字不能以“.”或空格结尾。
						2:DOS DOS命名空间是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为例:【11 01 2C 00 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;//本索引缓冲区在索引分配中的VCN
	UINT32 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\新建文本文档.txtdir2_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\新建文本文档.txtdir3_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了:https://github.com/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 msg
void GetErrormessage(DWORD dwErrCode, DWORD dwLanguageId) {//dwLanguageId=0
	DWORD 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 WCHAR
WCHAR * 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 examined
	BOOL result;                 // results flag
	DWORD readed;                   // discard results
	STORAGE_DEVICE_NUMBER number;   //use this to get disk numbers

	CHAR path[MAX_PATH];
	sprintf(path, "\\\\.\\%c:", letter);
	//printf("%s\n", path);
	hDevice = CreateFile(charToWCHAR(path), // drive to open
		GENERIC_READ | GENERIC_WRITE,    // access to the drive
		FILE_SHARE_READ | FILE_SHARE_WRITE,    //share mode
		NULL,             // default security attributes
		OPEN_EXISTING,    // disposition
		0,                // file attributes
		NULL);            // do not copy file attribute
	if (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 device
		IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode
		NULL,                            // lpInBuffer
		0,                               // nInBufferSize
		&number,           // output buffer
		sizeof(number),         // size of output buffer
		&readed,       // number of bytes returned
		NULL      // 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 byte
void 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转为带符号的INT64
INT64 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);
	//读取该索引项对应的MFT
	UINT64 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 attribute
	pCommonAttributeHeader pComAttriHeader = (pCommonAttributeHeader)malloc(sizeof(CommonAttributeHeader));
	//存放目录下所有索引项的vector
	vector<pSTD_INDEX_ENTRY>indexEntryOfDir;
	//一个set 删除重复索引项
	map<UINT64, BOOL>visMftReferNum;
	bool finishFlag = FALSE;
	UINT32 attrCnt = 0;
	//file path
	WCHAR 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 type
		if (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  先break
		if (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已用字节数 break
		if ((pIndexOfMFTEntry - MFTEntry) > MFTEntryHeader->BytesInUse) {
			fprintf(fp, "\nreach end of BytesInUse, break\n");
			break;
		}

		//resolve attribute header
		UINT16 attriheadersize = 0;
		bool isResidentAttri = false;
		switch (pComAttriHeader->ATTR_ResFlag)//是否常驻属性
		{
		case BYTE(0x00): {
			isResidentAttri = true;
			//get attribute header
			pResidentAttributeHeader 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 header
			fprintf(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 data
		BYTE *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 name
			UINT32 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 break
					break;
				}
				if (pIndexOfEntry - ptrIndexHeaderStart > indexTotalSize) {
					//超出有效长度 break
					break;
				}

				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_ALLOCATION

			fprintf(fp, "\n##### INDEX_ALLOCATION #####\n");
			printf("\n##### INDEX_ALLOCATION #####\n");
			//A0属性体
			
			//存放VCN LCN
			vector<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);
					//print
					fprintf(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 break
						UINT64 isIndexEntryFinish = 0;
						memcpy(&isIndexEntryFinish, ptrOfStdIndexBuffer, 8);
						//超出有效长度 break
						if (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 runs
			else {
				printf("content in 0X80[nonresident]:\n");
				printBuffer2(ptrData, attriBodySize);
				printf("\n");
				//存放VCN LCN
				vector<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 MFTEntryHeader
	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 %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 attribute
	pCommonAttributeHeader pComAttriHeader = (pCommonAttributeHeader)malloc(sizeof(CommonAttributeHeader));
	bool finishFlag = FALSE;
	UINT32 attrCnt = 0;

	while (TRUE) {
		//pIndexOfMFTEntry 现在指向属性头, 后面会加上这个属性的总长 指向下一个属性头

		memcpy(&attriType, pIndexOfMFTEntry, 4);//attri type
		if (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  先break
		if (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已用字节数 break
		if ((pIndexOfMFTEntry - MFTEntry) > MFTEntryHeader->BytesInUse) {
			fprintf(fp, "\nreach end of BytesInUse, break\n");
			break;
		}
		//resolve attribute header
		UINT16 attriHeaderSize = 0;
		switch (pComAttriHeader->ATTR_ResFlag)//是否常驻属性
		{
		case BYTE(0x00): {

			//get attribute header
			pResidentAttributeHeader 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 header
			fprintf(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 data
		BYTE *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 name
			UINT32 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_ID
			break;
		}
		case 0x00000050: {//SECURITY_DESCRIPTOR
			break;
		}
		case 0x00000060: {//VOLUME_NAME
			break;
		}
		case 0x00000070: {//VOLUME_INFORMATION
			break;
		}
		case 0x00000080: {//DATA
			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);
			////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: {//BITMAP
			break;
		}
		case 0x000000C0: {//symbol_LINK REPARSE_POINT
			break;
		}
		case 0x000000D0: {//EA_INFORMATION
			break;
		}
		case 0x000000E0: {//EA
			break;
		}
		case 0x000000F0: {//PROPERTY_SET
			break;
		}
		case 0x00000100: {//LOGGED_UNTILITY_STREAM
			break;
		}
		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 MFTEntry
		UINT64 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 sector
		PVOID tmpMBRBuf = malloc(UINT32(MBRSectorSize));
		ReadDisk(driverInfos[i].number, 0, MBRSectorSize, tmpMBRBuf);
		MBRs[MBRcnt] = (pMBRSector)tmpMBRBuf;
		//for test
		fprintf(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): {//FAT32
				printf("\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): {//ntfs
				printf("\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 dbr
				resolveNTFSDBRSector(driverInfos[i].number, startSecOffset, DBRBuf);

				break;
			}
			case BYTE(0x0F): {//Extended
				fprintf(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();
					//读取本分区的DBR
					PVOID 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): {//ntfs
						printf("\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 dbr
						resolveNTFSDBRSector(driverInfos[i].number, thisVolPhyStartSecOffset, DBRBuf);
						break;
					}
					case BYTE(0x0C): {//fat32
						printf("\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; }
					//读取下一个EBR
					PVOID 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);
}

//判断卷是否属于USB
bool 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];	//文件系统ID
	unsigned 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;//本索引缓冲区在索引分配中的VCN
	UINT32 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 则值为0
	UINT32 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:Win32 Win32是POSIX的一个子集,不区分大小写,可以使用除““”、“*”、“?”、“:”、“/”、“<”、“>”、“/”、“|”之外的任意UNICODE字符,但名字不能以“.”或空格结尾。
						2:DOS DOS命名空间是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];//文件的GUID
	BYTE OID_BirthVolID[16];//文件建立时所在卷的ID
	BYTE OID_BirthID[16];//文件的原始ID
	BYTE 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    启动时删除USN
					0x0020    修复过的ID
					0x8000    被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;

//BITMAP
typedef struct _MFT_ATTR_BITMAP {
	//??
}MFT_ATTR_BITMAP,*pMFT_ATTR_BITMAP;

//SYMBOL_LINK
typedef struct _SYMBOL_LINK {
	//??
}SYMBOL_LINK,*pSYMBOL_LINK;

//REPARSE_POINT
typedef struct _REPARSE_POINT{
	UINT32 RP_Type;/*重解析数据类型,该值可以是以下值之一
					0x20000000    别名
					0x40000000    最高等待时间
					0x80000000    微软使用
					0x68000005    NSS
					0x68000006    NSS恢复
					0x68000007    SIS
					0x68000008    DFS
					0x88000003    卷挂接点
					0xA8000004   HSM
					0xE8000000   硬连接*/
	UINT16 RP_DatSz;//重解析数据尺寸
	UINT16 RP_Resvd;//
	BYTE RP_Data[0];//	重解析数据
}REPARSE_POINT,*pREPARSE_POINT;

//EA_INFORMATION
typedef struct _EA_INFORMATION {
	UINT16 EI_PackedSz;//	压缩扩展属性尺寸
	UINT16 EI_NumOfEA;//拥有NEED_EA记录的扩展属性个数
	UINT32 EI_unpackedSz;//未压缩扩展属性尺寸
}EA_INFORMATION,*pEA_INFORMATION;

//EA
typedef struct _EA {
	UINT32 EA_Next;//下一个扩展属性的偏移(本记录的尺寸)
	BYTE EA_Flags;//标志位,值取0x80表示需要EA
	BYTE EA_NamLen;//名字数据的长度(M)
	UINT16 EA_ValLen;//值数据的长度
	BYTE EA_NameVal[0];//名字数据和值数据
}EA,*pEA;

//PROPERTY_SET
typedef struct _PROPERTY_SET {
	//??
}PROPERTY_SET,*pPROPERTY_SET;

//LOGGED_UNTILITY_STREAM
typedef 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;								//文件系统扇区号,一般取1
	UINT16 Backup_Sector;								//备份引导扇区,一般取值为6
	BYTE	   Reserved_Sector[12];		//保留扇区	
}FAT32_Sector, *pFAT32_Sector;
typedef struct _Basic_BPB
{
	UINT16	Bytes_per_Sector;					//每个扇区字节数,可取minReadSize,1024,2048,4096,通常取minReadSize
	BYTE		Sectors_per_Cluster;				//每簇扇区数,可取1,2,4,8,16,32,64,128,FAT32最多跟踪
													//268,435,445个簇
	UINT16	Reserved_Sector;					//保留扇区,表示第一个FAT前的扇区数  【也就是FAT1的偏移】
	BYTE		FATs;								//FAT表个数,通常取2
	UINT16	RootEntry;							//根目录项数,对FAT32,取值必为0
	UINT16	SmallSector;						//小扇区数,对FAT32,取值必为0
	BYTE		Media;								//存储介质描述,F8表示硬盘,F0表示3.5软盘
	UINT16	Sector_per_FAT_FAT16;				//针对FAT12/16的每个FAT扇区数,对FAT32,取值为0
	UINT16	Sector_per_Track;					//每道扇区数,描述磁盘物理结构
	UINT16	Heads;								//磁头数
	UINT32		hidden_Sector;						//该块硬盘前用于存放引导代码及分区表的扇区数  【隐藏扇区】
	UINT32		Large_Sector;						//总扇区数,若SmallSector为0,此处表示分区上扇区总数  
															//可用扇区数 = 总扇区数-保留扇区-FAT表占用扇区
	_FAT32_Sector		Fat32_Sector;						//FAT32文件系统扇区信息				
}Basic_BPB, *pBasic_BPB;
typedef	struct _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
{
	BYTE		JumpInstrction[3];		//0x00,跳转指令,通常为EB 58 90,其中58指示了,跳转位置,在X86中,58+2就代表跳转到5A处
	BYTE		OEMID[8];				//0x03,厂商标示和OS版本信息
	_Basic_BPB			BPB;								//0x0B
	_FAT32_Extend_BPB	Extend_BPB;							//0x40
	BYTE 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: 在此处引用程序需要的其他头文件

相关阅读

文件大小单位转换函数-getFileSize($bytes)

function getFileSize($bytes){ if ($bytes >= pow(2,40)) { # code... $return = round($bytes/pow(1024, 4),2); $suffi

Linux 文件和文件夹权限操作

三、Linux 文件权限首先来查看一下当前目录下的文件内容吧ls -l 查看当前目录下的文件列表 ls -l xxx.xxx (xxx.xxx是文件名) 查

如何取得用户参数文件中的parameters

TCode:SU01或SU3中可以看到用户参数文件(user profile)中的parameter :这个parameters可以通过函数 BAPI_USER_GET_DETAIL例子代

Android中使用file.createNewFile()无法创建文件的问

在写一个保存bitmap文件的方法的时候,遇到了题中问题。为了不出现问题,不要直接File f = new File(Environment.getExternalStorage

curl 模拟 GET\POST 请求,以及 curl post 上传文件

curl 模拟 GET\POST 请求,以及 curl post 上传文件 一般情况下,我们调试数据接口,都会使用一个 postman 的工具,但是这个工具还是有

分享到:

栏目导航

推荐阅读

热门阅读