NTFS文件定位和HIVE文件解析——1.NTFS文件定位


0x00 前言

  關於NTFS文件系統等知識點的介紹,網上諸多資料,這里不再做過多介紹了,本文僅作為筆者學習NTFS文件系統結構以及HIVE文件結構的總結實踐(win7 x64)。

 

0x01 從MBR定位到HIVE文件

   首先通過winhex手動定位到“system32\config\system”文件,而要定位文件,就需要ntfs的MFT主文件表,首先要找到MFT主文件表的起始位置,也就是它的起始簇號。

   打開物理磁盤,就可以看到作為整個硬盤的第一個扇區的MBR,DPT分區表,先定位到0x1be處的DPT分區表的第一個表項(選中的那16個字節,每個表項16字節),右側貼圖為DPT分區表項各字段含義,可以看到第一個分表項就是活動分區(第一個字節值為0x80,代表活動分區),因此再定位到表項的第九個字節,取一個DWORD,這個值就是分區引導扇區DBR(系統啟動時MBR要移交引導權給它的那個扇區),的起始扇區了:

    

 

   OK,那么直接起始扇區 * 扇區大小0x800 *0x200 = 0x100000,定位到分區引導扇區DBR康康,這里需要關注DBR中的三個字段,每個扇區的字節總數、簇大小、$MFT起始簇的簇號:

       

 

   那么根據分區引導扇區DBR,直接再定位到MFT起始簇康康,偏移地址為,MFT起始簇的簇號 * 簇大小 0xC0000*0x1000 = 0xC000 0000,

   轉到0xC000 0000偏移地址處,看到了“FILE”標志,說明這個MFT主文件表位置沒找錯:

   這里再介紹幾句MFT主文件表:MFT作為NTFS卷的核心,被實現為一個文件記錄數組,每個文件記錄的大小都是固定1kb,為了行文方便,此處不妨將每個文件記錄稱之為MFT Entry.

   

 

0x02  MFT基礎概念

   這里再介紹一下MFT中的NTFS元數據文件:每個NTFS卷包含一組元數據文件——微軟保留了前16個MFT 表項(MFT Entry)作為文件系統元數據文件,每一個文件系統元文件的名字都由“$”開始,並且第一個字母大寫。其中,第一個MFT記錄存儲

 其他MFT記錄的位置,他被稱為基本文件記錄(base file record),這一項的含義也就是也就是將整個MFT看做一個文件。

  那么這組元數據文件什么時候用到呢:當NTFS第一次訪問一個卷時,它必須要掛載(mount)這個卷,這個時候文件系統就需要從磁盤上讀取元數據並構建起內部的數據結構,以應對處理應用程序的的文件系統訪問請求。

 

  定位到了MFT主文件表的起始地址,那下面再來看看MTF文件表的關鍵元文件列表,首先進一步定位到5號文件記錄,根目錄文件,每一個MFT表項的大小是1kb(0x400),那么5號文件記錄,對應的位置是:

  0xC000 0000 + 5*0x400 = 0xC000 1400:

 

 

                      

 

 

   到這里就需要介紹一下MFT Entry中數據結構,以及幾個關鍵屬性了:

  一個MFT entry簡單來講的話,可以分為兩部分,一個是固定大小的MFT Entry Header,一個是各類屬性,而各類屬性的話,又可以被細分為屬性頭和屬性體;而實際上它的大部分空間都被用來存儲屬性,屬性是用來存儲特定類型數據的數據結構。

  MFT entry結構的簡易示意圖如下所示:

  

   MFT Entry Header(MFT記錄頭)結構各字段如下圖所示,其中最關鍵的字段莫過於0x14偏移處,長度為2個字節的“第一個屬性的偏移地址”,根據這個字段可以獲取到文件記錄中第一個屬性的位置。

  

 

  關於屬性頭,它標識了屬性的類型、大小和名稱等。值得關注的一點是0x08偏移處的“屬性是否常駐”標志;

  而屬性體,則用於存儲文件的內容(此處“文件”一詞泛指文件,文件夾等,因為從NTFS的角度,一切皆“文件”,更無文件和文件夾之區分),因此它可以是任意大小,但鑒於MFT Entry數組的每一項只有1kb的大小,所以也就衍生出了“常駐屬性”和“非常駐屬性”,即文件數據能在MFT Entry里全部記錄下的,稱為“常駐屬性”,放不下的,以“run”結構的方式將數據地址記錄在屬性頭中——放在其他簇中,不再本MFT Entry中,稱為”非常駐屬性“。

  (這個“run”結構,《深入解析windows操作系統》的中文版將其翻譯為“行串”一詞,嗯~~,其實我覺得此翻譯實在詞不達意,可能讓初學者看迷糊,我大膽猜想下,命名“run”結構的代碼作者,是否是想表達:”這是一個非常駐屬性吖!文件數據run了(跑到其他簇了)喲!“;鑒於run這個詞很難找到一個合適的翻譯,故下文將直接稱呼run而不翻譯)

  非常駐屬性的結構體示意圖如下所示:

  

 

  

 

   常駐屬性的屬性頭結構字段如下所示,值得關注的關鍵字段是:屬性類型、屬性長度:

  

   非常駐屬性的屬性頭結構字段如下所示,值得關注的關鍵字段是:屬性類型、屬性長度,0x20偏移地址出的run的偏移地址

  

   關於非常駐屬性中的run的含義:每一個run記錄(代表)了若干個連續的簇,例如,如果一個屬性分配了48、49、50、51和52這幾個簇,則它有一個run,這個run開始於簇48,長度為5.如果這個屬性還分配了簇80和81,則它有第二個run,起始於80長度為2。第三個run可能起始於簇56長度為4,這里的多個run稱之為run list;因此,簡單來講,一個非常駐屬性有多少個run,取決於它的文件數據放置在多少段連續的簇中

  

 

 

   每個run描述的第一個字節表示兩個數據的占用字節數:高4bit描述數據的起始簇號,低4Bit則指明了數據占用的簇的數目。因此每個run都是可變長度。其結構示意圖如下所示:

  

 

0x03 MFT文件記錄屬性

   話至此處,就需要再介紹一下MFT Entry中的常見的屬性類型(以屬性區域開頭標志字節為名,H為十六進制省寫):

  關鍵的幾個屬性分別是0x80文件數據屬性,0x90索引根屬性,0xA0索引分配屬性,其中0x80文件數據屬性可能是常駐可能是非常駐,取決於文件數據大小,0x90索引根屬性必然是常駐屬性,0xA0索引分配屬性必然是非常駐屬性

      ① 0x80 文件數據屬性$DATA

  該屬性容納着文件的內容,文件大小一般指的就是未命名數據流的大小。該屬性沒有最大最小限制,最小情況是該屬性為常駐屬性,可以不占用除MFT以外的空間。

  當文件屬性能夠在文件記錄中完全存儲下來而不需要存儲到其他的Data Run(數據流)中時,這種屬性就是常駐屬性。

  

         0x90 索引根屬性$INDEX_ROOT

  該屬性是實現NTFS的B+樹索引的根節點,它總是常駐屬性。該屬性沒有最大最小長度限制。屬性結構如下:

  

 

  

 

   索引根結構:

字節偏移    字段長度(字節)    描述
~    ~    標准屬性頭(已分析過)
0x00    4    屬性類型
0x04    4    校對規則
0x08    4    每個索引緩沖區的分配大小(字節數)
0x0C    1    每個索引緩沖區的簇數
0x0D    3    無意義(填充到屬性長度能被8整除)

   索引頭結構: 

字節偏移    字段長度(字節)    描述
0x00 4  第一個索引項的偏移 0x04    4     索引項的總大小
0x08    4     索引項的分配大小
0x0C    1     標志:當該字節為00時,表示其為小索引(適合於索引根);當該字節為01時,表示其為大索引(適合於索引分配)
0x0D    3     無意義(填充到屬性長度能被8整除)

   索引項結構:

  索引頭后面有着不同長度的索引項的序列,由一個帶有最后一個索引項標志的特殊索引項來結束。當一個目錄比較小,可以全部存儲在索引根屬性中時,該目錄就只需要這一個屬性來描述。而如果目錄太大不能全部存儲在索引根中時,就會有兩個附加的屬性出現:一個是索引分配屬性,描述B+樹目錄的子節點;另一個是索引位圖屬性,描述索引塊的索引分配屬性使用的虛擬簇號。根目錄$Root包含它自身的一個索引項。

0x00    8    該文件的MFT參考號
0x08    2    索引項的大小(相對索引項開始的偏移)
0x0A    2    文件名屬性體大小
0x0C    2    索引標志:此處為1表示這個索引項包含子節點;為2表示這是最后一個項,3表示包含子節點且為結束項 
0x0E    2    用0填充,無意義
0x10    8    父目錄的MFT文件參考號
0x18    8    文件創建時間
0x20    8    文件最后修改時間
0x28    8    文件記錄最后修改時間
0x30    8    文件最后訪問時間
0x38    8    文件的分配大小
0x40    8    文件的實際大小
0x48    8    文件標志
0x50    1    文件名的長度
0x51    1    文件名的命名空間
0x52 2F 文件名
2F+0x52    P    填充到能被8整除(無意義)
P+2F+0x52    8    子節點的索引所在的VCN(需要有子節點時才有)

/*命名空間,該值可為以下值中的任意一個
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文件名都存放在同一個文件名屬性中。*/

      

       ③  0xA0索引分配屬性 $INDEX_ALLOCATION 

  大目錄不能把它的所有索引放在常駐$INDEX_ROOT屬性里面,所以它們需要一個非常駐$INDEX_ALLOCATION屬性。

  它記錄了外部索引分配的空間,其所在目錄稱作大目錄。其結構與0X80屬性完全一致。它是MFT記錄之外的一組索引塊運行,用於描述B+樹目錄的子節點。每個4KB大小的索引緩沖區(索引塊)可以容納20到30個文件項。

  也是一個索引(如目錄)的基本結構,存儲着組成索引的B+樹目錄所有子節點的定位信息。它總是非常駐屬性,沒有最大最小值限制。

  它的屬性結構很簡單,就是一個非常駐屬性的屬性頭加Data Run就完事。A0H屬性的Run List所描述的數據流,也就是NTFS的B+樹結構的索引緩沖區。

  

 

 0x04 NTFS的索引結構分析

  行文至此,不得不再描述下基於0x90,0xA0索引根和索引分配屬性的目錄文件查找邏輯了。

 

   

   

  上圖是一個包含3個節點,每個節點3個項,共9項的目錄的MFT條目,其中索引根(Index Root)屬性指向B+樹的根。這個MFT條目並不能容納所有9個目錄項,NTFS必須把其中一部分項存放在別處,因此,NTFS分配了兩個索引緩沖區來存放另外的項(索引根以及索引緩沖區一般能夠存儲三個以上文件項,這依賴於文件名的長度)。一個MFT條目占1KB的空間,而一個索引緩沖區占4KB的空間。

  從上圖箭頭可以看出NTFS的存儲項是按字母順序進行的。假設運行一個程序試圖打開目錄中的e.doc文件,NTFS會首先去讀索引根屬性,其中包含了d.pic、h.ppt及i.ddt三個文件項,並將名稱“e.doc”與第一個項的名稱“d.pic”進行比較,NTFS得出結論:e.doc在字母序上排在d.pic之后。因此轉而處理下一個項“h.ppt”,經過比較以后,由於e.doc的字母序在h.ppt之前,NTFS就會檢索h.ppt項中索引緩沖區的虛擬簇號(VCN)。這個索引緩沖區中存儲着字母序比h.ppt小但比d.pic大的項。VCN代表一個簇在一個文件或目錄中的順序,NTFS能夠將虛擬簇號映射成邏輯簇號(LCN),而邏輯簇號表示着一個簇相對於一個卷的起始點的相對偏移量。而如果h.ppt項中並不存在這個索引緩沖區的虛擬簇號,則NTFS可以立即斷定這個目錄中不存在e.doc文件,並報告打開文件失敗。

  當獲得索引緩沖區的虛擬簇號以后,NTFS就會繼續查找。從圖4-466中可以看出,索引緩沖區的第一個項正是要找的e.doc,於是NTFS就從e.doc項中讀出它對應的文件記錄的位置。索引項中還存儲着如時間戳(創建時間、最后修改時間等)、文件大小、屬性等其他信息。雖然NTFS在文件的文件記錄中也存儲了這些信息,但將這些信息復制到目錄項中可以提高列目錄或做一些簡單的文件查詢時的效率。

  目錄項是按照字母排序的,這也解釋了NTFS系統在列目錄的時候為什么總是按照字母順序的問題。相反,FAT文件系統並不對目錄項排序,所以在FAT文件系統中列目錄時並沒有順序。另外,由於NTFS將目錄項按照B+樹的結構保存,使得在包含很多文件的目錄中查找文件時效率非常高。一般來說,NTFS只需要掃描所有目錄項中的一小部分即可。在這一點上,FAT文件系統只能使用線性查找,也就是說,為了查找一個文件,很大程度上需要查詢這個目錄中的幾乎每一個文件項。

  索引緩沖區是NTFS的B+目錄管理中非常重要的一個結構,每個索引緩沖區在NTFS中一般是4KB的固定大小,其位置和大小由目錄的文件記錄中A0H屬性的Run List定義,

   

                       根目錄的A0H屬性

 

  從上圖中A0H屬性的Run List可以看到,索引緩沖區的起始簇號是4014H,換算為十進制就是16 404號簇,大小是一個簇。只要跳轉到16 404號簇就是索引緩沖區的開始了,如下圖所示。

    

  索引塊(索引緩沖區)

  目錄的外部索引,以“INDEX”開頭。里面存放有目錄下面的所有文件或者子目錄的索引項,文件名都是以unicode進行編碼的。一個索引塊為一個單位 (與簇的概念類似),其大小在引導記錄$Boot中定義,一般總是4KB(簇大小一般也是4K)。

  索引塊的結構圖(以49 4E 44 58開頭,ASCII碼為“INDX”,一共8個扇區,參見上圖):

  

  索引塊由索引塊頭和一個個的索引項組成(如上圖)。

  索引塊頭(Index Header):在每4KB的一個索引塊中,有一個文件索引的標准索引頭。

  索引項(Index Entry):節點內的Key,按照文件名首字母順序排列。目錄下每一個文件或子目錄對應一個索引項(與90屬性中的索引項完全一樣)。

  當文件被刪除,索引項也會隨之消失。

   

  1.索引(塊)頭的結構(類似MFT頭,天藍色標示的部分同前面索引根屬性頭的最后部分):

  標准索引(塊)頭結構

  

 

   

  索引塊頭部之后,連接的是索引目錄項。其結構與前面的索引根屬性中介紹的“標准索引項結構”是一致的(此處省略)。

  這說明90屬性中的索引項和索引塊的索引項是完全一樣的。

   

 

 

 0x05 從根目錄文件定位到HIVE文件

  前文已經定位到了根目錄文件的MFT文件記錄在0xC0001400偏移處了,此處也就是“C:\”的MFT文件記錄了,接下來需要進一步定位到他的子目錄"windows"目錄的MFT文件記錄;

 

  

  目錄的MFT基本文件記錄,一般包括頭屬性、標准屬性(0X10)、文件名屬性(0X30)、索引根屬性(0X90),大目錄的MFT基本文件記錄下還會包含索引分配屬性(0XA0)和BO屬性。

      

 

   

  首先從MFT記錄頭偏移0x14的地址處,讀取出第一個屬性的相對偏移地址(0x38偏移),可以看到下圖中的第一個屬性是0x10標准信息屬性($STANDARD_INFORMATION)(下圖橙色區域是0x10屬性),同時可以看到根目錄文件記錄的MFT中,

共包含有0x10標准信息屬性,0x30文件名屬性,0x40卷屬性,0x90索引根屬性,0xA0索引分配屬性,0xB0位圖屬性。

   

   先來康康0x90索引根屬性(上圖中紫色區域),怎么定位到索引跟屬性的呢?

  首先上文定位到了第一個屬性0x10屬性的位置是0xC0001438,該屬性0x04偏移處的值是0x60(屬性長度),故下一個屬性的位置是:

  下一個屬性的地址 = 本屬性起始地址 + 本屬性長度  ,即0xC0001438 + 0x60 = 0xC0001498;

 

   以此類推,可以得到0x0x90索引根屬性的位置是0xC0001520(上圖中紫色區域),還記得上文說的嘛,0x90索引根屬性永遠是常駐屬性,所以這里根據常駐屬性的屬性頭結構字段,可以得知0x14偏移處(2個字節大小)是屬性體偏移位置0x20,

   故:

   0x90的屬性體地址 = 0x90的屬性起始地址 + 常駐屬性的屬性頭0x14偏移處記錄的屬性體偏移  0xC0001520 + 0x20 = 0xC0001540;

 

   0x90的屬性體是記錄的索引,稱為索引體,再回顧一下前面說的0x90屬性的索引體結構構成:分為0x10字節的索引根,0x10自己的索引頭,以及緊跟其后索引項,其中索引頭的第一個DWORD字段代表的是第一個索引項的偏移值0x10,

        故:

   第一個索引項的地址  =  索引體起始地址 + 索引根大小 + 索引頭中記錄的第一個索引項的偏移值     0xC0001540+0x10+0x10 = 0xC0001560

 

  

 

  再結合索引項的結構體(如下圖所示),可知索引項0x50偏移處為該索引項對應文件名長度(0x04),索引項0x52偏移處為文件名“Boot”,這並不是我想找的Windows文件名,所以繼續找下一個索引項,找到索引項0x08偏移處記錄的該索引項大小0x68,得到:

  下一個索引項的地址  =  該索引項的地址  +    該索引項的大小     0xC0001560 + 0x68 = 0xC00015C8

       

  同理,得知下一個索引項的文件名為“Program Files (x86)”(0xC00015C8 + 0x52 = 0xC000161A地址處),不是“Windows”文件名,繼續查找下一個索引項地址(0xC00015C8 + 0x80 = 0xC0001648),進一步定位到其文件名長度0x05(0xC0001698)和文件名"Users"(0xC0001648+0x52 = 0xC000169a),所以繼續找下一個索引項(0xC0001648 + 0x68 = 0xC00016B0), 查看這個索引項0x0C偏移處的索引標志是0x03(0xC00016BC),說明這是最后一個項且包含子節點,因此,接下來我們需要到0xA0索引分配屬性中繼續查找了。

 

  在上文中,我們可以計算出0xA0索引分配屬性的地址為0xC00016C8,然會根據非常駐索引頭的結構體,得到Data Run的地址為0xC0001710

  Data Run的地址 = 0xA0非常駐屬性起始地址 +  Data Run的偏移地址(偏移0x20處記錄)  0xC00016C8+ 0x68 = 0xC0001710

  故Data Run為:21 04 A0 02,表明0xA0索引分配屬性記錄的索引塊,起始簇是0x2A0,大小占用4個簇,故:

  索引塊的地址  =  起始簇 * 簇大小     0x2A0 * 0x1000 = 0x2A0000

  

  如下圖,可以看到索引頭的INDX標志,說明索引塊沒找錯。

  

  繼續分析索引塊結構,最關鍵的字段莫過於0x18處記錄的第一個索引項的偏移地址0x40了(0x2A0018),故:

  索引塊的第一個索引項地址 = 索引塊起始地址 + 0x18 + 0x18偏移處記錄的索引項偏移    0x2A0000 + 0x18 + 0x40 = 0x2A0058,這里索引塊的索引項和90屬性中的索引項和是完全一樣的,所以接下來繼續查找文件名:

  $AttrDef(2A00AA = 0x2A0058 + 0x52),

 

       下一個索引項地址(0x2A0058 + 0x68(索引項0x08處記錄) = 0x2A00C0),文件名$BadClus(0x2A0112= 0x2A00C0+ 0x52)......

  依此類推,最終可以在0x0x2A1ED8處找到“windows”目錄對應的索引項,同時值得注意的是,這個索引塊(4KB大小,0x1000)的最后一項(0x2A1FA8起始地址)的Flag標志(0x08偏移處)的值為2,這表示該索引項是這個索引塊的最后一項如果還需要再往后找的話,應當找下一個索引塊了   下一個索引塊地址 = 當前索引塊地址 + 索引塊大小      0x2A1000 + 0x1000 = 0x2A2000,如果是代碼實現查找的話,那就應該獲取到索引項起始地址后,首先判斷Flag標志(0x0C偏移處)的值是否為2,不為2才繼續判斷文件名,為2直接定位到下一個索引塊開始查找(索引塊類似數組結構,是連續的)。

  

   根據索引項的結構體字段,可以得知“Windows”目錄文件對應的MFT參考號是0x28a(索引項第一個字段,占6個字節),因此,根據MFT表是數組結構,可知“Windows”目錄文件的MFT記錄的地址是:

  0xC000 0000 + 0x28a*0x400 = 0xC00A 2800;

  

 

 

       依此類推,可以再次從Windows目錄MFT記錄中,查找0x90索引根屬性,0xA0索引分配屬性,進而進一步定位到Windows目錄的子目錄,“System32”目錄的MFT參考號0x9ec,計算出“System32”目錄的MFT Entry文件記錄地址,

  再進一步定位到子目錄config的MFT Entry參考號0x9fe,再計算出“config”目錄的MFT Entry文件記錄地址,再進一步定位到“system”文件的MFT參考號0xef26,得到“system”文件的MFT Entry文件記錄的地址0xC3BC9800(0xC000 0000 + 0xef26*0x400 = 0xC3BC 9800)

  

  繼續看上圖system文件的MFT文件記錄的0x80文件數據屬性(橙紅色區域),可以看到這是一個非常駐屬性(0x08偏移處),Data Run的偏移地址是0x40(0x20偏移處記錄),即Data Run的值為:

  32 40 0D 88 AD 05

  即文件數據記錄在5AD88個簇,占用D40個簇大小,即地址在0x5AD88 * 0x1000 = 0x5AD88000

  

  

  

  

 

 

 

  

   

 

  

  

 

 

 

 

 

 

  

  

 

 

  

 

 

   

 

 

 

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM