計(jì)算機(jī)畢業(yè)論文:探索NTFS

時(shí)間:2022-11-17 10:21:00

導(dǎo)語:計(jì)算機(jī)畢業(yè)論文:探索NTFS一文來源于網(wǎng)友上傳,不代表本站觀點(diǎn),若需要原創(chuàng)文章可咨詢客服老師,歡迎參考。

計(jì)算機(jī)畢業(yè)論文:探索NTFS

探索ntfs

NTFS是WindowsNT引入的新型文件系統(tǒng),它具有許多新特性。本文旨在探索NTFS的底層結(jié)構(gòu),所敘述的也僅是文件在NTFS卷上的分布。NTFS中,卷中所有存放的數(shù)據(jù)均在一個(gè)叫$MFT的文件中,叫主文件表(MasterFileTable)。而$MFT則由文件記錄(FileRecord)數(shù)組構(gòu)成。FileRecord的大小一般是固定的,通常情況下均為1KB,這個(gè)概念相當(dāng)于Linux中的inode。FileRecord在$MFT文件中物理上是連續(xù)的,且從0開始編號。$MFT僅供FileSystem本身組織、架構(gòu)文件系統(tǒng)使用,這在NTFS中稱為元數(shù)據(jù)(Metadata)。以下列出Windows2000Release出的NTFS的元數(shù)據(jù)文件(我將要給出的示例代碼的部分輸出結(jié)果)。

FileRecord(inode)FileName

--------------------------

0$MFT

1$MFTMirr

2$LogFile

3$Volume

4$AttrDef

5.

6$Bitmap

7$Boot

8$BadClus

9$Secure

10$UpCase

11$Extend

Windows2000中不能使用dir命令(甚至加上/ah參數(shù))像普通文件一樣列出這些元數(shù)據(jù)文件。實(shí)際上FileSystemDriver(ntfs.sys)維護(hù)了一個(gè)系統(tǒng)變量NtfsProtectSystemFiles用于隱藏這些元數(shù)據(jù)。默認(rèn)情況下,這個(gè)變量被設(shè)為TRUE,所以使用dir/ah將得不到任何文件。知道這個(gè)行為后使用i386kd修改NtfsProtectSystemFiles后即可以列出元數(shù)據(jù)文件:

kd>xntfs!NtfsProtect*

fe213498Ntfs!NtfsProtectSystemFiles

fe21349cNtfs!NtfsProtectSystemAttributes

kd>ddntfs!NtfsProtectSystemFilesl2

fe2134980000000100000001

kd>edntfs!NtfsProtectSystemFiles0

kd>ddntfs!NtfsProtectSystemFilesl2

fe2134980000000000000001

kd>

D:\>ver

MicrosoftWindows2000[Version5.00.2195]

D:\>dir/ah$*

驅(qū)動(dòng)器D中的卷是W2KNTFS

卷的序列號是E831-9D04

D:\的目錄

2000-04-2719:3136,000$AttrDef

2000-04-2719:310$BadClus

2000-04-2719:3167,336$Bitmap

2000-04-2719:318,192$Boot

2000-04-2719:31<DIR>$Extend

2000-04-2719:3113,139,968$LogFile

2000-04-2719:3127,575,296$MFT

2000-04-2719:314,096$MFTMirr

2000-04-2719:31131,072$UpCase

2000-04-2719:310$Volume

9個(gè)文件40,961,960字節(jié)

1個(gè)目錄51,863,552可用字節(jié)

需要指出的是ntfs.sys將元數(shù)據(jù)文件以一種特殊的方式打開,所以在打開NtfsProtectSystemFiles后,如果使用ReadFile等產(chǎn)生IRP_MJ_READ等IRP包時(shí)將會(huì)導(dǎo)致PageFault(詳見GaryNebbett的《WindowsNT/2000NativeAPIReference》)。

以上的討論均是基于$MFT文件而討論的,即基于$MFT中的FileRecord(inode)討論的。為更好的繼續(xù)以下的討論,這兒我列出FileRecordHeader的結(jié)構(gòu):

typedefstruct{

ULONGType;

USHORTUsaOffset;

USHORTUsaCount;

USNUsn;

}NTFS_RECORD_HEADER,*PNTFS_RECORD_HEADER;

typedefstruct{

NTFS_RECORD_HEADERNtfs;

USHORTSequenceNumber;

USHORTLinkCount;

USHORTAttributesOffset;

USHORTFlags;//0x0001=InUse,0x0002=Directory

ULONGBytesInUse;

ULONGBytesAllocated;

ULONGLONGBaseFileRecord;

USHORTNextAttributeNumber;

}FILE_RECORD_HEADER,*PFILE_RECORD_HEADER;

下面我將討論如何定位$MFT。稍微有點(diǎn)操作系統(tǒng)知識(shí)的人都會(huì)知道引導(dǎo)扇區(qū)(BootSector),其物理位置為卷中的第一個(gè)扇區(qū)。以下由dskprobe.exe(Windows2000ResourceKit中的一個(gè)小工具)分析的第一個(gè)扇區(qū)(當(dāng)然也可以使用WinHex等其他應(yīng)用程序):

file:d:\Sector00.bin

Size:0x00000200(512)

Address|00010203-04050607:08090A0B-0C0D0E0F|0123456789ABCDEF

---------|-------------------------:-------------------------|-----------------

00000000|EB52904E-54465320:20202000-02080000|?R?NTFS.....

00000010|00000000-00F80000:3F00F000-3F000000|.....?..?.e.?...

00000020|00000000-80008000:90C04100-00000000|....€.€.惱A.....

00000030|04000000-00000000:091C0400-00000000|................

00000040|F6000000-01000000:049D31E8-BB31E894|?.......?杌1钄

..

..

..

000001F0|00000000-00000000:83A0B3C9-000055AA|........儬成..U?

這512字節(jié)為如下的格式:(摘自GaryNebbett書中,本文許多代碼均來自或參考此書。)

#pragmapack(push,1)

typedefstruct{

UCHARJump[3];

UCHARFormat[8];

USHORTBytesPerSector;

UCHARSectorsPerCluster;

USHORTBootSectors;

UCHARMbz1;

USHORTMbz2;

USHORTReserved1;

UCHARMediaType;

USHORTMbz3;

USHORTSectorsPerTrack;

USHORTNumberOfHeads;

ULONGPartitionOffset;

ULONGReserved2[2];

ULONGLONGTotalSectors;

ULONGLONGMftStartLcn;

ULONGLONGMft2StartLcn;

ULONGClustersPerFileRecord;

ULONGClustersPerIndexBlock;

ULONGLONGVolumeSerialNumber;

UCHARCode[0x1AE];

USHORTBootSignature;

}BOOT_BLOCK,*PBOOT_BLOCK;

#pragmapack(pop)

各個(gè)字段的詳細(xì)意義從字段名中即可大致清楚。在linux-ntfs的GNU工程(/projects/linux-ntfs)中也有詳細(xì)的文檔,限于篇幅我不將其列出??梢允褂萌缦麓a讀出卷中的第一個(gè)扇區(qū):

hVolume=CreateFile(drive,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,

OPEN_EXISTING,0,0);

ReadFile(hVolume,&bootb,sizeof(bootb),&n,0);

bootb是一個(gè)BOOT_BLOCK結(jié)構(gòu),在我的卷中如下格式(請對應(yīng)Sector00.bin分析):

DumpBootBlockatbelow:

BytesPerSector:200

SectorsPerCluster:8

BootSectors:0

SectorsPerTrack:3F

NumberOfHeads:F0

PartitionOffset:3F

TotalSectors:41C090

MftStartLcn:4

Mft2StartLcn:41C09

ClustersPerFileRecord:F6

ClustersPerIndexBlock:1

VolumeSerialNumber:E8319D04

BootSignature:AA55

以上的MftStartLcn其實(shí)是$MFT在卷中的簇(Cluster)號。簇是NTFS的基本單位,最小單位。一個(gè)只有1Byte的文件也要占用一簇的空間。NTFS使用LCN(LogicalClusterNumber)來代表NTFS卷中的物理位置,其簡單的從0到卷中的總簇?cái)?shù)減一進(jìn)行編號。對于一個(gè)特定的文件NTFS則使用VCN(VirtualClusterNumber)來映射LCN實(shí)現(xiàn)文件的組織。從MftStartLcn的值4可以知道$MFT的LCN為4與SectorsPerCluster、BytesPerSector的大小即可定位$MFT的位置。得到$MFT的位置后,如果遍歷$MFT中所有的FileRecord即可以得到卷中所有的文件列表(前面已經(jīng)提到FileRecord只是簡單的從0開始編號)。也就是說到目前為止已經(jīng)可以對文件組織有最簡單的認(rèn)識(shí),但如何得到文件的信息呢,如文件名等等。NTFS中所有文件包括普通的用戶文件、元數(shù)據(jù)文件均用同樣的方式組織數(shù)據(jù)、屬性等。我將nfi.exe(來自WindowsNT/2000OEMSupportTools)的輸出結(jié)果列出,作為我敘述的開始:

D:\>copyconfile

testforntfs^Z

已復(fù)制1個(gè)文件。

D:\>nfid:\file

NTFSFileSectorInformationUtility.

Copyright(C)MicrosoftCorporation1999.Allrightsreserved.

\file

$STANDARD_INFORMATION(resident)

$FILE_NAME(resident)

$DATA(resident)

D:\>echotestforattr>file:ATTR

D:\>nfid:\file

NTFSFileSectorInformationUtility.

Copyright(C)MicrosoftCorporation1999.Allrightsreserved.

\file

$STANDARD_INFORMATION(resident)

$FILE_NAME(resident)

$DATA(resident)

$DATAATTR(resident)

nfi的輸出結(jié)果$STANDARD_INFORMATION、$FILE_NAME、$DATA等在NTFS中稱為屬性(Attribute)。屬性分為常駐屬性(ResidentAttribute)與非常駐屬性(NonresidentAttribute)。文件的數(shù)據(jù)也包含在屬性中,似乎與屬性這個(gè)名稱有點(diǎn)混謠。不過這又讓NTFS有了更加統(tǒng)一的組織文件的形式。這也同時(shí)讓NTFS有MultiStreams的特性(上面也演示了這個(gè)特性)。通過指定的FileRecord定位給定的Attribute的實(shí)現(xiàn)代碼如下:

template<classT1,classT2>inline

T1*Padd(T1*p,T2n){return(T1*)((char*)p+n);}

PATTRIBUTEFindAttribute(PFILE_RECORD_HEADERfile,

ATTRIBUTE_TYPEtype,PWSTRname)

{

for(PATTRIBUTEattr=PATTRIBUTE(Padd(file,file->AttributesOffset));

attr->AttributeType!=-1;

attr=Padd(attr,attr->Length)){

if(attr->AttributeType==type){

if(name==0&&attr->NameLength==0)returnattr;

if(name!=0&&wcslen(name)==attr->NameLength

&&_wcsicmp(name,PWSTR(Padd(attr,attr->NameOffset)))==0)returnattr;

}

}

return0;

}

GaryNebbett提供的這個(gè)FindAttribute函數(shù)在Attributename(即第三個(gè)參數(shù))不為空串時(shí)可能會(huì)出現(xiàn)bug,主要原因是_wcsicmp對UNICODE字符串比較時(shí)應(yīng)該是以\0結(jié)束的標(biāo)準(zhǔn)的C字符串。我在提供的代碼中已經(jīng)糾正了這個(gè)錯(cuò)誤。

下面我將通過使用SoftICE來分析這段代碼得到$MFT的$FILE_NAME屬性來得到$MFT的filename。這個(gè)示例同樣適用于得到其它文件的$FILE_NAME(如上面的file)、還有其它的屬性如$DATA等等。

:bpxFindAttribute

BreakduetoBPXFindAttribute(ET=6.89seconds)

:locals

[EBP-4]+structATTRIBUTE*attr=0x00344D68<{...}>

[EBP+8]+structFILE_RECORD_HEADER*file=0x00344D38<{...}>

[EBP+C]enumATTRIBUTE_TYPEtype=AttributeFileName(30)

[EBP+10]+unsignedshort*name=0x004041BC<"$MFT">

:?file

structFILE_RECORD_HEADER*=0x00344D38<{...}>

structNTFS_RECORD_HEADERNtfs={...}

unsignedshortSequenceNumber=0x1,"\0\x01"

unsignedshortLinkCount=0x1,"\0\x01"

unsignedshortAttributesOffset=0x30,"\00"

unsignedshortFlags=0x1,"\0\x01"

unsignedlongBytesInUse=0x2D8,"\0\0\x02\xD8"

unsignedlongBytesAllocated=0x400,"\0\0\x04\0"

unsignedquadBaseFileRecord=0x0,"\0\0\0\0\0\0\0\0"

unsignedshortNextAttributeNumber=0x6,"\0\x06"

file參數(shù)我傳入的是$MFT,從$MFT的LCN=4可以得到其在卷中的物理地址,這在上面已說明。你也可以使用dskprobe(我機(jī)子中為第LCN*SectorsPerCluster=4*8扇區(qū))得到底下SoftICE的輸出結(jié)果:

:dd@file//以下的注釋可對照文中開頭列出的FILE_RECORD_HEADER定義。

0023:00344D38454C49460003002A6D4AC04D00000000FILE*...M.Jm....

0023:00344D480001000100010030000002D800000400....0...........

----

|__AttributeOffset

0023:00344D580000000000000000043400060000FA0D..........4.....

0023:00344D6800000010000000600018000000000000....`...........

----------------

||_指出這個(gè)Attribute的長度。定義如下。

|_根據(jù)AttributeOffset得到的Attribute頭,定義如下。00000010指出這個(gè)Attribute為StandardInformation

0023:00344D7800000048000000182C1761D001BFB03CH........a.,<...

Attribute頭如下定義:

typedefstruct{

ATTRIBUTE_TYPEAttributeType;

ULONGLength;

BOOLEANNonresident;

UCHARNameLength;

USHORTNameOffset;

USHORTFlags;//0x0001=Compressed

USHORTAttributeNumber;

}ATTRIBUTE,*PATTRIBUTE;

typedefstruct{

ATTRIBUTEAttribute;

ULONGValueLength;

USHORTValueOffset;

USHORTFlags;//0x0001=Indexed

}RESIDENT_ATTRIBUTE,*PRESIDENT_ATTRIBUTE;

typedefstruct{

ULONGLONGDirectoryFileReferenceNumber;

ULONGLONGCreationTime;//Savedwhenfilenamelastchanged

ULONGLONGChangeTime;//ditto

ULONGLONGLastWriteTime;//ditto

ULONGLONGLastAccessTime;//ditto

ULONGLONGAllocatedSize;//ditto

ULONGLONGDataSize;//ditto

ULONGFileAttributes;//ditto

ULONGAlignmentOrReserved;

UCHARNameLength;

UCHARNameType;//0x01=Long,0x02=Short

WCHARName[1];

}FILENAME_ATTRIBUTE,*PFILENAME_ATTRIBUTE;

ATTRIBUTE_TYPE是一個(gè)Enum型定義。其中00000010為StandardInformation。30為FileName。因?yàn)镕ileNameAttribute總是一個(gè)常駐Attribute,所以我將RESIDENT_ATTRIBUTE定義也給出。OK,現(xiàn)在可以繼續(xù)Dump下一個(gè)Attribute:

//dd@file+file->AttributeOffset+length(StandardInformationAttribute)

:dd@file+30+60

0023:00344DC8000000300000006800180000000300000...h...........

--------------

||___這里的NameLength與NameOffset指FileNameAttribute名。不要與$MFTFileName混謠。

|_指出這是一個(gè)FileNameAttribute。

0023:00344DD80000004A000100180000000500050000J...............

--------------------

|||_根據(jù)ValueOffset的值,得到FILENAME_ATTRIBUTE的具體位置。

||_ValueOffset值

|_ValueLength值

0023:00344DE82C1761D001BFB03C2C1761D001BFB03C.a.,<....a.,<...

0023:00344DF82C1761D001BFB03C2C1761D001BFB03C.a.,<....a.,<...

0023:00344E0800004000000000000000400000000000.@.......@......

0023:00344E180000000600000000002403040046004D..........$.M.F.

----------

||___找到$MFT的FileName了吧。

|_NameLength

0023:00344E2800000054000000000000008000000190T...............

0023:00344E3800400001000100000000000000000000..@.............

這兒給出了DumpAttribute的一個(gè)具體方法。最后我將給出遍歷FileRecord的代碼,在給出代碼前應(yīng)該說明一下$MFT中$BITMAP屬性。NTFS的這個(gè)Attribute相當(dāng)于LINUXEXT2的s_inode_bitmap數(shù)組(Linux2.0版本)。所以很容易明白$BITMAP的作用,即每bit指出相應(yīng)FileRecord的在用情況。以下是DumpAllFileRecord的代碼:

BOOLbitset(PUCHARbitmap,ULONGi)

{

return(bitmap[i>>3]&(1<<(i&7)))!=0;

}

VOIDDumpAllFileRecord()

{

PATTRIBUTEattr=FindAttribute(MFT,AttributeBitmap,0);

PUCHARbitmap=newUCHAR[AttributeLengthAllocated(attr)];

ReadAttribute(attr,bitmap);

ULONGn=AttributeLength(FindAttribute(MFT,AttributeData,0))/BytesPerFileRecord;

PFILE_RECORD_HEADERfile=PFILE_RECORD_HEADER(newUCHAR[BytesPerFileRecord]);

for(ULONGi=0;i<n;i++){

if(!bitset(bitmap,i))continue;

ReadFileRecord(i,file);

if(file->Ntfs.Type==''''ELIF''''&&(file->Flags&3)){

attr=FindAttribute(file,AttributeFileName,0);

if(attr==0)continue;

PFILENAME_ATTRIBUTEname

=PFILENAME_ATTRIBUTE(Padd(attr,PRESIDENT_ATTRIBUTE(attr)->ValueOffset));

printf("%8lu%.*ws\n",i,int(name->NameLength),name->Name)

}

}

}

本文引用GaryNebbett的些定義可能對Windows2000版本有些很小的出入,不過Internet有其神奇的地方,雖然Microsoft不提供這些信息,但諸如linux-ntfsGNU工程等均是學(xué)習(xí)NTFS的一個(gè)很好的資料,本文也參考了很多它提供的文檔。另外MarkRussinovich的《InsideWin2KNTFS》、《InsideNTFS》、《ExploringNTFSOn-diskStructures》等也是很好的NTFS資料。本文仍未涉及NTFS中目錄的組織(B+樹)等等,可能的話我會(huì)另行介紹。文中介紹的完整代碼可到下載。出現(xiàn)的錯(cuò)誤也歡迎來信指教(tsu00@)!

最后感謝AntonAltaparmakov,感謝我的同事在出差時(shí)抽空給我買到GaryNebbett的書。感謝我看到的所有資料的原作者們。感謝他們!

參考資料:

1.GaryNebbett《WindowsNT/2000NativeAPIReference》

2.Linux-NTFSProjectNTFSDocumentationVersion0.4

3.MarkRussinovich相關(guān)文檔

4.DavidSolomom《InsideWindowsNT,2ndEdition》