計(jì)算機(jī)畢業(yè)論文:探索NTFS
時(shí)間:2022-11-17 10:21:00
導(dǎo)語:計(jì)算機(jī)畢業(yè)論文:探索NTFS一文來源于網(wǎng)友上傳,不代表本站觀點(diǎn),若需要原創(chuàng)文章可咨詢客服老師,歡迎參考。
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》