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: 在此處引用程序需要的其他頭文件

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章