一如既往的叨叨
首先要對硬盤分區(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\新建文本文檔.txt中dir2_0的索引項,即上圖的標準索引項1。得到dir2_0的文件記錄的MFT編號爲0X2D,可以計算出dir2_0的文件記錄的偏移:MFT的起始偏移+MFT編號*2,即6,291,456 + 0X2D * 2 = 6291546扇區。讀取該文件記錄:
圖18 dir2_0的文件記錄
這個和以前有些不一樣了,因爲dir2_0只有兩個子目錄,90屬性就夠用了,因此沒有A0屬性。上面說過,90屬性體由三部分構成:索引根、索引頭和索引項。而90屬性中的索引項和標準索引項的結構是一致的。接下來呢,我們找到路徑E:\dir1_0\dir2_0\dir3_1\新建文本文檔.txt中dir3_1的索引項,即上圖中的索引項2,像以前一樣計算出它的文件記錄在MFT的位置並算出偏移扇區……
後面不再列舉啦,一直找到新建文本文檔.txt的文件記錄,如下:
圖19 新建文本文檔.txt的文件記錄
第七步:讀取文件內容
對於文件呢,我們關注的是它的80屬性,即數據屬性。因爲這個txt文件比較大,在MFT中無法存放其所有數據,因此這個文件的80屬性爲非常駐屬性,屬性體是一個data runs,指向存放該文件數據的簇。這裏Data Runs的計算方法和前面是一致的,從上圖可以看到,該data runs指向一個簇流,該簇流一共佔用了0X 00 A9 89個簇,起始偏移爲0X 4C C8 40個簇,即40256000扇區。我們轉到該扇區,就可以讀取這個txt中存儲的數據了,截圖如下:
圖20 .txt中的數據
到這裏我們已經讀到了這個文本文檔中的數據了。對於小文件而言,常駐的80屬性就能夠存放它的數據內容,比如下面這個文件:
圖21 小文件的80屬性
這個文本文檔中只有一個字符串“sadfasdfasdf”,能夠存放在常駐的80屬性中,因此80屬性的屬性體並不是Data Runs,而是直接存放了文件內容。
嗯,到這裏其實也差不多了,後面附上我寫的測試代碼吧。代碼跳過了活動分區,因爲活動分區比較活躍,最好是能夠先打一個快照再去讀取。
也傳到github了: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: 在此處引用程序需要的其他頭文件