1. 程式人生 > >NTFS 檔案系統解析

NTFS 檔案系統解析

1. windows 下磁碟檔案讀寫

下面是讀取D:\磁碟上的第0扇區 512 Bytes

CreateFile()開啟磁碟,獲取檔案控制代碼;

SetFilePointer()設定讀寫的位置;

ReadFile()讀取磁碟扇區資料。

複製程式碼
HANDLE hFile;
char drive[] = "\\\\.\\D:";    //------- \\.\D:  -----
//------- 建立檔案控制代碼  ------
hFile = CreateFile(drive,    // 還可以為硬碟"\\\\.\\physicalDrive0"絕對讀寫, 或類似"D:\\abc\\fileName.txt"檔案形式,
          GENERIC_READ,      // 開啟方式           FILE_SHARE_WRITE|FILE_SHARE_READ, // 前者表示之後開啟該檔案的程式 only write, 後者為only read           NULL,                // 安全屬性 LPSECURITY_ATTRIBUTES           OPEN_EXISTING,           // how to Create           0, NULL);              // if(hFile == INVALID_HANDLE_VALUE) return
; // 中間兩個引數分別是偏移位元組數的低32Bit數值,和高32Bit的地址, 高32Bit不用則為零; 此處表示D:\從首扇區起,偏移位元組數為0 SetFilePointer(hFile, 0, NULL, FILE_BEGIN); DWORD lenRead; ReadFile(hFile, buf, 512, &lenRead, NULL); // 在設定的位置起,讀取512Byte到快取buf; 這裡讀取的是分割槽引導扇區
複製程式碼

 2. 獲取磁碟所有驅動器

DWORD allDrive = GetLogicalDrive().
// 返回值共有 32 Bits,從低位到高位分別表示A,B,C,D,E,.....
// 比如0x0000 007C, 即 01111100, 表示有C,D,E,F,G 共5個驅動器;

3. NTFS 檔案系統

  一個NTFS系統是由引導扇區,主檔案表MFT,和資料區組成;另外MFT有一部分重要備份在資料區。

(1) 引導扇區

 若果D:\盤是NTFS檔案系統,那麼上面得到的第0個扇區資料 buf 偏移0x03開始的8個Bytes就是"NTFS    ",表示這個扇區就是NTFS

的引導記錄。這第0個扇區也就是$Boot扇區,這個扇區包含了該卷的 BPB 和擴充套件BPB引數,可以得到該卷的卷大小,磁頭數,扇區大小,簇

大小等等引數;要解析一個NTFS卷的檔案結構也是從這裡的BPB引數開始的。

解析時有用到的是這個扇區的前 88 (0x58) 個Bytes,剩餘的是引導程式碼和結束標誌"55 AA",前88個位元組具體結構如下:

複製程式碼
 1 typedef struct NTFS_BPB{     // 在cmd 輸入 fsutil fsinfo ntfsinfo d: 查詢 NTFS 資訊
 2     UCHAR jmpCmd[3];
 3     UCHAR s_ntfs[8];            // "NTFS    " 標誌
 4 // 0x0B
 5     UCHAR bytesPerSec[2];        //  0x0200  扇區大小,512B
 6     UCHAR SecsPerClu;            //  0x08    每簇扇區數,4KB
 7     UCHAR rsvSecs[2];            //       保留扇區
 8     UCHAR noUse01[5];            //
 9 // 0x15
10     UCHAR driveDscrp;            //  0xF8     磁碟介質 -- 硬碟
11     UCHAR noUse02[2];            //
12 // 0x18
13     UCHAR SecsPerTrack[2];     //  0x003F  每道扇區數 63
14     UCHAR Headers[2];           //  0x00FF 磁頭數
15     UCHAR secsHide[4];          //  0x3F  隱藏扇區
16     UCHAR noUse03[8];           //
17 // 0x28
18     UCHAR allSecsNum[8];        // 卷總扇區數, 高位在前, 低位在後
19 // 0x30
20     UCHAR MFT_startClu[8];      // MFT 起始簇
21     UCHAR MFTMirr_startClu[8]; // MTF 備份 MFTMirr 位置
22 //0x40
23     UCHAR cluPerMFT[4];     // 每記錄簇數 0xF6
24     UCHAR cluPerIdx[4];     // 每索引簇數
25 //0x48
26     UCHAR SerialNum[8];     // 卷序列號
27     UCHAR checkSum[8];     // 校驗和
28 }Ntfs_Bpb,*pNtfs_Bpb;
複製程式碼

(2) 關於簇

  在一個分割槽中引導記錄扇區所在的簇編號為0,往後的簇編號1,2,3等等一直到卷尾,這就是一個分割槽的邏輯簇號(LCN);計算

邏輯扇區號:LCN * 簇大小,簇的大小在BPB引數中找到,一般為8個扇區4KB;以此可以由 MFT 起始簇 MFT_startClu 計算

出第一個 MFT 項(記錄)的位置。

  VCN,虛擬簇號,給一個檔案從它的首簇開始編號,為0,依次遞增一直到檔案的尾簇,在物理上不一定連續。

(3) 主檔案表  (Master File Table, MFT)

  MFT 是由一條條 MFT 項(記錄)所組成的,而且每項大小是固定的(一般為1KB),MFT保留了前16項用於特殊檔案記錄,稱為元資料,

元資料在磁碟上是物理連續的,編號為0~15;如果$MFT的偏移為0x0C0000000, 那麼下一項的偏移就是0x0C0000400,在下一項就是

0x0C0000800等等;

     

  MFT記錄了整個卷的所有檔案 (包括MFT本身、資料檔案、資料夾等等) 資訊,包括空間佔用,檔案基本屬性,檔案位置索引,建立時

間使用者許可權,加密資訊等等,每一個檔案在 MFT 中都有一個或多個 MFT 項記錄檔案屬性資訊,這裡的屬性包括資料,如果這個檔案很小

在 MFT 項中就可以放下,那麼這條屬性就定義為常駐屬性,常駐標誌位記為1,如果是非常駐,則有一個索引指向另一條記錄(稱為一個執行)。

(3) 第一條 MFT 項:  $MFT

  MFT 的第一項記錄$MFT描述的是主分割槽表MFT本身,它的編號為0,MFT項的頭部都是如下結構:

複製程式碼
 1 typedef struct MFT_HEADER{
 2     UCHAR    mark[4];             // "FILE" 標誌 
 3     UCHAR    UsnOffset[2];        // 更新序列號偏移     30 00
 4     UCHAR    usnSize[2];          // 更新序列陣列大小+1   03 00
 5     UCHAR    LSN[8];              // 日誌檔案序列號(每次記錄修改後改變)  58 8E 0F 34 00 00 00 00
 6 // 0x10
 7     UCHAR    SN[2];               // 序列號 隨主檔案表記錄重用次數而增加
 8     UCHAR    linkNum[2];          // 硬連線數 (多少目錄指向該檔案) 01 00
 9     UCHAR    firstAttr[2];        // 第一個屬性的偏移  38 00
10     UCHAR    flags[2];            // 0已刪除 1正常檔案 2已刪除目錄 3目錄正使用
11 // 0x18
12     UCHAR    MftUseLen[4];        // 記錄有效長度    A8 01 00 00
13     UCHAR    maxLen[4];            // 記錄佔用長度   00 04 00 00
14 // 0x20
15     UCHAR    baseRecordNum[8];     // 索引基本記錄, 如果是基本記錄則為0
16     UCHAR    nextAttrId[2];        // 下一屬性Id  07 00
17     UCHAR    border[2];            //
18     UCHAR    xpRecordNum[4];       // 用於xp, 記錄號
19 // 0x30
20     UCHAR    USN[8];          // 更新序列號(2B) 和 更新序列陣列
21 }Mft_Header, *pMft_Header;
複製程式碼

       

上面的頭部結構體在扇區的資料偏移 0x00 ~0x38;

在0x38之後的4大塊顏色資料是4條屬性,描述名稱,時間,索引等等資訊,最後以"FF FF FF FF"結束。它們分別以0x10,0x30,

0x80, 0xB0作為標誌;這裡的四種屬性所描述的的資訊型別可以由下表查得,對照資料和結構體可以把這4條屬性解析出來。

   

複製程式碼
 1 //------------------  屬性頭通用結構 ----
 2 typedef struct NTFSAttribute //所有偏移量均為相對於屬性型別 Type 的偏移量
 3 {
 4     UCHAR Type[4];           // 屬性型別 0x10, 0x20, 0x30, 0x40,...,0xF0,0x100
 5     UCHAR Length[4];         // 屬性的長度 
 6     UCHAR NonResidentFiag;   // 是否是非常駐屬性,l 為非常駐屬性,0 為常駐屬性 00
 7     UCHAR NameLength;        // 屬性名稱長度,如果無屬性名稱,該值為 00
 8     UCHAR ContentOffset[2];  // 屬性內容的偏移量  18 00
 9     UCHAR CompressedFiag[2]; // 該檔案記錄表示的檔案資料是否被壓縮過 00 00
10     UCHAR Identify[2];       // 識別標誌  00 00
11 //--- 0ffset: 0x10 ---
12 //--------  常駐屬性和非常駐屬性的公共部分 ----
13     union CCommon
14     {
15     //---- 如果該屬性為 常駐 屬性時使用該結構 ----
16         struct CResident
17         {
18             UCHAR StreamLength[4];        // 屬性值的長度, 即屬性具體內容的長度。"48 00 00 00"
19             UCHAR StreamOffset[2];        // 屬性值起始偏移量  "18 00"
20             UCHAR IndexFiag[2];           // 屬性是否被索引項所索引,索引項是一個索引(如目錄)的基本組成  00 00
21         };
22     //------- 如果該屬性為 非常駐 屬性時使用該結構 ----
23         struct CNonResident
24         {
25             UCHAR StartVCN[8];            // 起始的 VCN 值(虛擬簇號:在一個檔案中的內部簇編號,0起)
26             UCHAR LastVCN[8];             // 最後的 VCN 值
27             UCHAR RunListOffset[2];       // 執行列表的偏移量
28             UCHAR CompressEngineIndex[2]; // 壓縮引擎的索引值,指壓縮時使用的具體引擎。
29             UCHAR Reserved[4];
30             UCHAR StreamAiiocSize[8];     // 為屬性值分配的空間 ,單位為B,壓縮檔案分配值小於實際值
31             UCHAR StreamRealSize[8];      // 屬性值實際使用的空間,單位為B
32             UCHAR StreamCompressedSize[8]; // 屬性值經過壓縮後的大小, 如未壓縮, 其值為實際值
33         };
34     };
35 };
複製程式碼

    由這個結構體可以知道,屬性頭的長度取決於是否有屬性名,屬性名長度是多少;是否常駐,如果常駐,屬性內容長度是多少,如果非常駐,執行列表有多長。

   (0x08)日誌檔案序列號,它又叫檔案參考號、檔案引用號,一共 8Byte,前6個位元組是檔案稱為檔案號;後2個位元組是檔案順序號,檔案順序號隨重用而增加。

   10H 型別:10H屬性$STANDART_INFORMATION,描述的是檔案的建立、訪問、修改時間,傳統屬性,以及版本資訊等等。

複製程式碼
 1 struct Value0x10
 2 {
 3     UCHAR fileCreateTime[8];    // 檔案建立時間 
 4     UCHAR fileChangeTime[8];    // 檔案修改時間 
 5     UCHAR MFTChangeTime[8];     // MFT修改時間 
 6     UCHAR fileLatVisited[8];    // 檔案最後訪問時間 
 7     UCHAR tranAtrribute[4];     // 檔案傳統屬性
 8     UCHAR otherInfo[28];        // 版本,所有者,配額,安全等等資訊(詳細略)
 9     UCHAR updataNum[8];         // 檔案更新序列號 
10 }; 
複製程式碼

    

  下面的偏移都是相對於屬性首位元組,其值加上0x38 就是實際偏移(圖中的offset)。

0x00 4B: (0x10) 型別標誌

0x04 4B: (0x60) 整條10H屬性的長度

0x08 1B: (0x00) 非常駐

0x09 1B: (0x00) 無屬性名稱

0x0A 2B: (0x18) 屬性內容偏移位置

0x18 8B: (ED 46 39 6B 6B 93 CF 01) 8個位元組是檔案建立時間,緊隨其後的3x8個位元組分別是檔案最後修改時間,MFT修改

時間,檔案最後訪問時間。64位數值是相對於1601-01-01零點整的千萬分之一秒數。可以用FileTimeToSystemTime()轉換成

我們通常見到的形式。

0x38 8B: (06 00 00 00 00 00 00 00)傳統屬性,這裡是系統隱含檔案,位描述:

後面還有0x28個位元組是版本和管理資訊等等。

 20H型別  $ATTRIBUTE_LIST

當一個檔案需要多個MFT項來記錄,20H是用來描述屬性的屬性列表;當非常駐屬性依然不夠空間,則需要屬性列表。20H類屬性也有

可能為常駐或非常駐,可以應用上面的通用屬性頭。以此結構體得到屬性值的偏移地址,進而得到屬性內容。

複製程式碼
 //------- 這個結構只是資料內容部分,不包括屬性頭 NTFSAttribute ----
 //------- 由屬性頭的屬性值偏移量確定屬性值的起始位置 ---
 1 struct Value0x20{
 2     UCHAR type[4];           // 型別
 3     UCHAR recordType[2];     // 記錄型別
 4     UCHAR nameLen[2];        // 屬性名長度
 5     UCHAR nameOffset;        // 屬性名偏移
 6     UCHAR startVCN[8];       // 起始 VCN
 7     UCHAR baseRecordNum[8];  // 基本檔案記錄索引號
 8     UCHAR attributeId[2];    // 屬性 id
 9     //---- 屬性名(Unicode) 長度取決於 nameLen 的值 ---
10 };
複製程式碼

    30H 型別  $FILE_NAME

  30H 型別屬性描述的是檔案或資料夾的名字和建立基本資訊,屬性頭不在贅述,屬性值的結構如下:

複製程式碼
 1 struct Value0x30
 2 {
 3     UCHAR parentFileNum[8];     // 父目錄的檔案參考號,前6B
 4     UCHAR createTime[8];        // 檔案建立時間
 5     UCHAR changeTime[8];        // 檔案修改時間
 6     UCHAR MFTchangeTime[8];     // MFT 修改時間 
 7     UCHAR lastVisitTime[8]      // 最後一次訪問時間 
 8     UCHAR AllocSize[8];         // 檔案分配大小 
 9     UCHAR realSize[8];          // 實際大小 
10     UCHAR fileFlag[4];          // 檔案標誌,系統 隱藏 壓縮等等 
11     UCHAR EAflags[4]            // EA擴充套件屬性和重解析點
12     UCHAR nameLength;           // 檔名長 
13     UCHAR nameSpace;            // 檔案名稱空間 
14     //----- Name (Unicode編碼) 長度為 2 * nameLength ---- 
15 }
複製程式碼

  NTFS通過為一個檔案建立多個30H屬性實現POSIX (Portable Operating System Interface, 可移植作業系統介面) 式硬連線,

每個30H屬性都有自己的詳細資料和父目錄;一個硬連線刪除時,就從MFT中刪除這個檔名,最後一個硬連線被刪除時,這個檔案就算是

真正被刪除了。

 檔案參考號:包括前 6B 的檔案記錄號,後 2B 的檔案引用計數;當檔案記錄號為0x05時,是根目錄。

 名稱空間:0 --- POSIX, 1 -- Win32,  2 --- DOS,  3 --- Win32 & DOS

  40H 屬性  $OBJECT_ID

 MTFS統一給所有 MFT 記錄分配的一個標識 --- 物件ID,即結構體第一個16B,可能只有一個全域性物件ID,後面的3個ID可能沒有。

複製程式碼
1 struct Value0x40
2 {
3     UCHAR GObjectId[16];            // 全域性物件ID 給檔案的唯一ID號 
4     UCHAR GOriginalVolumeId[16];    // 全域性原始卷ID 永不改變 
5     UCHAR GOriginalObjectId[16];    // 全域性原始物件ID 派給本MFT記錄的第一個物件ID 
6     UCHAR GDomain[16];              // 全域性域ID (未使用)
7 };
複製程式碼

  50H 屬性  $SECURITY_DESCRIPTOR ( 安全描述符) 略。

  60H 屬性  $VOLUME_NAME 卷名屬性

1 struct Value0x60
2 {
3     //---- 通用屬性頭 --
4     UCHAR VolumeName[N];    //(Unicode) N 最大為 127 外加一個結束符'\0'
5 };

   70H 屬性  $VOLUME_INFORMATION  卷版本、狀態

複製程式碼
 1 struct Value0x70
 2 {    //----- 通用屬性頭 --- 
 3     UCHAR noUsed1[8];        // 00
 4     UCHAR mainVersion;       // 主版本號 1--winNT, 3--Win2000/XP
 5     UCHAR SecVersion;        // 次版本號 當主為3, 0--win2000, 1--WinXP/7
 6     UCHAR flag[2];           // 標誌 
 7     UCHAR noUsed2[4];        // 00 
 8 };
 9 /* flag:
10 *  0x0001  壞區標誌 下次重啟時chkdsk/f進行修復
11 *  0x0002  調整日誌檔案大小 
12 *  0x0004  更新裝載 
13 *  0x0008  裝載到 NT4 
14 *  0x0010  刪除進行中的USN 
15 *  0x0020  修復物件 Ids 
16 *  0x8000  用 chkdsk 修正卷 
17 */ 
複製程式碼

  80H 屬性  $DATA  容納檔案資料(未命名資料流),檔案的大小一般指是未命名資料流的大小,沒有長度限制,當它為常駐時,資料

  長度最小。它的結構為屬性頭加上資料流,如果資料流太大,則標記為非常駐,以執行的方式索引到外部。例如找一個MP3檔案,從它的MFT

 項中0x80屬性中可以看到它一定是非常駐,它的執行所指向的一系列簇就是音樂檔案資料流;

  一個80H屬性例項:

  

   0x00~0x37  是屬性頭;執行列表偏移是紫色和橙色區域,0x40開始,可以得到執行列表 32 40 34 00 00 0C 32 80 31 07 54 16 ;分析如下:

 首先0x32,低4位是2,表示緊隨其後的2Byte 0x3440作為執行簇大小,高4位是3,表示簇大小之後的3個Byte 0x0C0000 是起始簇,這

是一個執行結束;接下來的0x32同理得簇起始號0x165407,執行大小為0x3180簇;一個執行的結束後跟0x00 為列表結束,之後填充無效字元。

 90H 屬性  $INDEX_ROOT  索引根。實現NTFS的B+樹索引的根節點,總是常駐。索引根屬性由屬性頭、索引根和索引項組成。屬性頭是通用

屬性頭的常駐部分。結構體如下(可能有些偏差):

複製程式碼
 1 struct indexHeader 
 2 {
 3     UCHAR type[4];             //  屬性型別 
 4     UCHAR checkRule[4];        // 校對規則 
 5     UCHAR allocSize[4];        // 索引項分配大小 
 6     UCHAR CluPerIdx;           // 每索引記錄的簇數
 7     UCHAR noUse01[3];          // 填充 
 8 
 9     UCHAR firstIdxOffset[4];    // 第一個索引項偏移 
10     UCHAR idxTotalSize[4];      // 索引項總大小 
11     UCHAR indxAllocSize[4];     // 索引項分配大小 
12     UCHAR flag;                 // 標誌
13     UCHAR noUse02[3];
14 };
15 // 一般小目錄在90屬性中有常駐目錄項,目錄項的結構與INDX快取中的目錄項一樣
16 struct indexItem
17 {    
18     UCHAR MFTnum[8]                 // 檔案的 MFT 記錄號,前6B是MFT編號,用於定位此檔案記錄
19     UCHAR ItemLen:[2];              // 索引項大小
20     UCHAR IndexIdentifier:[2];      // 索引標誌 R
21     UCHAR isNode[2];                // 1---此索引包含一個子節點,0---此為最後一項 
22     UCHAR noUse03[2];               // 填充
23     UCHAR FMFTnum[8];               // 父目錄 MFT檔案參考號 0x05表示根目錄 
24     UCHAR CreateTime[8];            //檔案建立時間
25     UCHAR fileModifyTime[8];        //檔案修改時間
26     UCHAR MFTModifyTime[8];         //檔案記錄最後修改時間
27     UCHAR LastVisitTime[8];         //檔案最後訪問時間
28     UCHAR FileAllocSize[8];         //檔案分配大小 (B)
29     UCHAR FileRealSize[8];          //檔案實際大小 (B)
30     UCHAR FileIdentifier[8];        //檔案標誌
31     UCHAR FileNameLen;              //檔名長度
32     UCHAR FileNameSpace;           //檔名名稱空間
33 //---- 0x52 --- 
34     //FileName;                     // 檔名 (末尾填滿 8 位元組)
35     UCHAR nextItemVCN[8];           // 下一項的 VCN (有子節點才有)
36 };
複製程式碼

 A0H   屬性  $INDEX_ALLOCATION  索引分配屬性,也是索引,由屬性頭和執行列表組成,一般指向一個或多個目錄檔案(INDX檔案,即4K快取);

 A0H屬性和90H屬性共同描述了磁碟檔案和目錄的 MFT 記錄的位置。第5項MFT的A0H屬性記錄根目錄的位置。

 B0H   屬性  $BITMAP 點陣圖屬性,虛擬簇使用(佔用)情況,這條屬性用在$MFT和索引中;在Bitmap檔案中,每一個 Bit 代表分割槽的一個簇,置1代表其已使用;

第0個位元組的第0位表示分割槽第0簇,之後依次遞增。

 C0H   屬性  $REPARSE_POINT 重解析點。使應用程式為檔案或目錄關聯一個應用程式資料塊,詳細略。

 D0H   $EA_INFORMATION  擴充資訊屬性。為在NTFS下實現HPFS的OS/2子系統資訊,及WinNT伺服器的OS/2客戶端應用而設定的,一般為非常駐;

 E0H   $EA  擴充屬性  也是為了實現NTFS下的 HPFS,一般為非常駐;

 100H  $LOGGED_UTILITY_STREAM,EFS加密屬性,儲存用於實現EFS加密有關的資訊,合法使用者列表,解密金鑰等等

(4) 解析一個磁碟分割槽的檔案目錄的順序:

  引導扇區($Boot) ----> 第0項記錄($MFT) ----> 根目錄記錄(第5項,90H,A0H) ----> 根目錄(INDX)

複製程式碼
 1 struct indxHeader    // A0H外部快取檔案結構,最大長度一般為4K
 2 {
 3     UCHAR mark[4];        //  標誌 "INDX"
 4     UCHAR usnOffset[2];   // 更新序列偏移
 5     UCHAR usnSize[2];     // 更新序列陣列大小S
 6     UCHAR LSN[8];         // 日誌檔案序列號
 7     UCHAR indxVCN[8];     // 本索引快取在分配索引中的VCN
 8     UCHAR itemOffset[4];  // 第一項索引的偏移(從這裡開始計算)
 9     UCAHR itemSize[4];    // 索引項實際大小(B)
10     UCHAR itemAlloc[4];   // 索引項分配大小(B)(不包括頭部) 略小於4K
11     UCHAR isNode;         // 是結點置1,表示有子節點
12     UCHAR noUse[3];        
13     //UCHAR USN[2S];      // 更新序列號和陣列
14 };
複製程式碼

  在檔案頭之後就是目錄項了,項的結構就是在上面90H的介紹裡定義的indexItem,每一個項代表一個檔案或目錄的MFT項,通過

項的 MFT 記錄號可以計算出MFT項的磁碟地址,它等於$MFT 的偏移地址 + 編號*0x400,以此可以找到該索引項對應的檔案或子目錄

的MFT項。

(5)  搜尋一個已刪除的檔案或目錄的MFT項

 上面說了,一個檔案的MFT項的地址等於$MFT的地址+MFT編號*0x400,如果目錄中的對應項刪除了,那麼可以從MFT的首部開始檢索,

因為MFT一般是連續的,而一個MFT項的大小又是固定的,一項項讀取,找到各自的0x30屬性,解析出檔名,進行比較 (MFT中有一些

空白區域需要跳過)。

(6) 關於檔名

 一般在檔名的前一個位元組是檔名的名稱空間,不管是INDX檔案中,還是0x30屬性中。

  0x00 ---- POSI ,最大的名稱空間,大小寫敏感,支援除 '\0' 和 '/' 所有Unicode字元,最大程度255個字元;

  0x01 ---- Win32,是POSI的子集,不支援字元:* / < > | \  : ?  ,不能用句點或空格結束;

  0x02 ---- DOS , 是Win32的子集,字元必須比空格0x20大,檔名1~8個字元,然後句點分割接字尾副檔名1~3個字元;

  0x03 --- DOS&Win32,必須相容Win32和DOS命名方式

    在INDX檔案中,經常可以看到含有0x02和0x03或者0x01的兩個不同名稱空間、相同MFT編號的項,也就是說這兩個目錄項指向同一個記錄,同樣的

在這個檔案的MFT項中也有兩個0X30屬性,其中一個是0x01或0x03,表示的是完整的檔名;另一個是0x02,DOS命名方式,它是一個短檔名,它在

我們命名的基礎上,截斷 '.' 之前的超出6個字元的所有字元,只剩前6個,之後接上"~1" ,這樣正好8個字元,當然後面的句點和副檔名保留。另外,它必須

滿足DOS命名規則,必須大寫,刪除禁止使用的字元等等。如果檔名重複了,在 "~1" 基礎上遞增,"~2","~3"等等。檢索比對時,我們自然要使用前者。

 (7) 關於字符集

 字符集是字元在計算機上的編碼方式,可以看成一種協議,一種約定規則,我們處理一串二進位制數所代表的字元時,必須清楚它用的是哪一種編碼方式;

在windows系統中檔案的命名是固定用兩個位元組表示一個字元,在MFT中可以發現英文檔名字元之間都填充一個 '\0' ,這是寬字符集與變長字符集相容,

在寬字符集中,小於128的字元數值上是等於ASCII碼;我們的檔案資料一般用的是變長字符集(GB2312等等);

為了比較輸入的檔名和NTFS中的檔名,我們必須要先轉換;

兩個WinAPI 函式,用於寬字元和變長字元轉換

複製程式碼
// 函式原型
int WideCharToMultiByte(
            UINT     CodePage,            // code page
            DWORD    dwFlags,            // performance and mapping flags
            LPCWSTR  lpWideCharStr,     // address of wide-character string
            int      cchWideChar,        // number of characters in string
            LPSTR    lpMultiByteStr,        // address of buffer for new string
            int      cchMultiByte,        // size of buffer
            LPCSTR   lpDefaultChar,        // address of default for unmappable 
                                          // characters
            LPBOOL   lpUsedDefaultChar    // address of flag set when default 
                                        // char. used
);
int MultiByteToWideChar(
            UINT   CodePage,         // code page
            DWORD  dwFlags,         // character-type options
            LPCSTR lpMultiByteStr, // address of string to map
            int    cchMultiByte,      // number of bytes in string
            LPWSTR lpWideCharStr,  // address of wide-character buffer
            int    cchWideChar        // size of buffer
);

//--- WCHAR 定義在tchar.h中 ----
void charTest()
{
    TCHAR    tc1[16] ;  //=  _T("後來");
    WCHAR    tc2[8] = {0x540E, 0x6765, 0, 0, 0, 0, 0, 0};
//    memset(tc2, 0, 20);
//  MultiByteToWideChar(CP_ACP, 0, tc1, 4, (LPWSTR)tc2, 4);
    WideCharToMultiByte(CP_ACP, 0 ,(WCHAR*)tc2, 2, tc1, sizeof(tc1), 0, 0);  
    
    cout<<"tc1 "<<tc1<<sizeof(tc1)<<" "<<strlen(tc1)<<endl;
    PrintHex(tc1);
    cout<<endl;
    cout<<"tc2 "<<sizeof(tc2)<<" "<