技术笔记:mft(转载)

简介: 技术笔记:mft(转载)

1前言


NTFS是现在流行的磁盘格式.想想当年FAT32的时代都差不多过去了,现在装机差不多都是NTFS.我们平时编程时使用文件操作函数基于操作系统,所以不管磁盘是何种格式,都只是用相同的几个函数就可搞掂,似乎分析NTFS文件系统没有什么必要.但是如果要搞些底层一点的东西,比如数据恢复啊,磁盘分析啊等等,甚至搞一些高级病毒之类,了解NTFS文件系统都很有必要!


现在我准备开始这个研究,现在的我对于这个是一窍不通,所以也不知从哪里开始,只能见一步学一步.所以借爱delphi()网站发一些杂乱无章的笔记.


先定一个目标,要实现NTFS文件系统解析,写出一个程序,能在NTFS下1.读取所有文件目录结构. 2.建立新文件/目录 3.删除文件/目录. 4.读出存在文件的数据. 5.向存在文件写入数据.


从今天起,将要开始这个笔记,直到完成目标.每有什么新的发现或研究成果我都会到这个贴子下跟贴发表,其间可能遇到好多好多的困难,希望得到各位高手的帮助.在此先谢谢各位!


要分析NTFS文件系统,必须先获得分区上的数据.可以写一个函数获取指定分区上指定扇区(512字节)的数据.


添加一个单元用来做这个任务.


unit Wu_DiskSectorUnit;


interface


uses


Windows,SysUtils,Wu_BasicUnit;


function Wu_OpenDiskSector(Drive:char):boolean; //打开指定分区,准备读取数据 Drive:代表指定分区A-Z 例:Wu_OpenDiskSector('C');


function Wu_ReadDiskSector(SectorNum:cardinal;var retbuf:Pchar):boolean; //读取分区指定扇区的数据(512字节),并将数据指针返回retbuf


function Wu_WriteDiskSector(SectorNum:cardinal;inbuf:Pchar):boolean; //将数据写入分区指定扇区(512字节),inbuf代表要写入内容的指针


procedure Wu_CloseDiskSector(); //退出时用来结束打开指定分区


implementation


var


hDeviceHandle:cardinal;


ReadBuf:array【0..511】 of char;


WriteBuf:array【0..511】 of char;


procedure Wu_CloseDiskSector();


begin


if closehandle(hDeviceHandle) then


begin


hDeviceHandle:=INVALID_HANDLE_VALUE;


end;


end;


function Wu_OpenDiskSector(Drive:char):boolean;


var


D:string;


begin


Wu_CloseDiskSector();


D:='\.\'+Drive+':';


hDeviceHandle := CreateFile(Pchar(D), GENERIC_ALL,FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,0, 0);


if (hDeviceHandle INVALID_HANDLE_VALUE) then


begin


result:=true;


end


else


begin


result:=false;


end;


end;


function Wu_ReadDiskSector(SectorNum:cardinal;var retbuf:Pchar):boolean;


var


n:integer;


I_LARGE:LARGE_INTEGER;


I_INT64:INT64;


I_SectorNum:INT64;


begin


for n:=0 to 511 do


begin


ReadBuf【n】:=#0;


end;


if (hDeviceHandle INVALID_HANDLE_VALUE) then


begin


//FileSeek(hDevicehandle,SectorNum512,0);这样写有一个错误,就是不支持大于2146959360的数


I_SectorNum:=SectorNum;


I_INT64:=I_SectorNum512;


Wu_Copy(@I_INT64,1,8,@I_LARGE,1);


SetFilePointer(hDevicehandle,I_LARGE.LowPart,@I_LARGE.highpart,0);


if FileRead(hDevicehandle,ReadBuf【0】,512)=512 then


begin


result:=true;


end


else


begin


result:=false;


end;


end


else


begin


result:=false;


end;


retbuf:=@ReadBuf【0】;


end;


function Wu_WriteDiskSector(SectorNum:cardinal;inbuf:Pchar):boolean;


var


n:integer;


I_LARGE:LARGE_INTEGER;


I_INT64:INT64;


I_SectorNum:INT64;


begin


for n:=0 to 511 do


begin


WriteBuf【n】:=inbuf【n】;


end;


if (hDeviceHandle INVALID_HANDLE_VALUE) then


begin


//FileSeek(hDevicehandle,SectorNum512,0);这样写有一个错误,就是不支持大于2146959360的数


I_SectorNum:=SectorNum;


I_INT64:=I_SectorNum512;


Wu_Copy(@I_INT64,1,8,@I_LARGE,1);


SetFilePointer(hDevicehandle,I_LARGE.LowPart,@I_LARGE.highpart,0);


if FileWrite(hDevicehandle,WriteBuf【0】,512)=512 then


begin


result:=true;


end


else


begin


result:=false;


end;


end


else


begin


result:=false;


end;


end;


end.


现在我们用Wu_ReadDiskSector读取分区第一个扇区的数据


var


P:Pchar;


Wu_OpenDiskSector('G'); //打开G盘


Wu_ReadDiskSector(0,P); //读取G盘0扇区


Wu_CloseDiskSector;


获得这个扇区称为引导扇区,512个字节数据的意义如下表(这个表从网上拿来的)


字节偏移 长度(字节) 常用值 意义


0X00 3 0XEB5290 JMP指令


0X03 4 NTFS 文件系统 ID


0X0B 2 0X0002 每扇区字节数


0X0D 1 0X08 每簇扇区数


0X0E 2 0X0000 保留扇区


0X10 3 0X000000 总为0


0X13 2 0X0000 NTFS未使用,为0


0X15 1 0XF8 介质描述


0X16 1 0X0000 总为0


0X18 2 0X3F00 每磁道扇区数


0X1A 2 0XFF00 磁头数


0X1C 4 0X3F000000 隐含扇区


0X20 4 0X00000000 NTFS未使用,为0


0X24 4 0X80008000 NTFS未使用,为0


0X28 8 0X4AF57F0000000000 扇区总数


0X30 8 0X0400000000000000 $MFT的逻辑簇号


0X38 8 0X54FF070000000000 $MFTMirr的逻辑簇号


0X40 4 0XF6000000 每MFT记录簇数


0X44 4 0X0100000 每索引簇数


0X48 8 0X14A51B74C91B741C 卷标


0X50 4 0X00000000 校验和


0X54 430 略 引导代码


0X1FE 2 0X55AA 签名


根据此表我们写出数据结构.


type


R_Wu_NTFSBPB=packed record


bJmp:array【0..2】 of byte; //跳转指令【0】


bNTFlags:array【0..3】 of char; //"NTFS"标识【3】


breserve1:array【0..3】 of char; //这个上表没有说明【7】


wBytePerSector:word; //每扇区字节数(一般都是512)(重要)【B】


bSectorPerCluster:byte; //每簇扇区数(重要)【D】


wReserveSectors:word; //保留扇区数(重要)【E】


bFatNum:byte; //?未知【10】


wRootDirNum:word; //?未知【11】


wSectorOfParti:word; //?未知【13】


bMedium:byte; //介质描述【15】


wSectorPerFat:word; //?未知【16】


wSectorPerTrack:word; //每磁道扇区数【18】


wHeadNum:word; //磁头数【1A】


dwHideSector:cardinal; //隐含扇区【1C】


dwSectoOfParti:cardinal;//NTFS未使用,为0【20】


bDeviceFlag:byte; //?未知【24】


bReserve2:byte; //?未知【25】


wReserve3:word; //?未知【26】


ullSectorsOfParti:int64;//扇区总数(这里理论上是一个64位无符号类型,不知用int64是否有错)(重要)【28】


ullMFTAddr:int64; //$MFT的起始逻辑簇号(这里理论上是一个64位无符号类型,不知用int64是否有错)(重要)【30】


ullMFTMirrAddr:int64; //$MFTMirr的起始逻辑簇号(这里理论上是一个64位无符号类型,不知用int64是否有错)(重要)【38】


bClusterPerFile:cardinal;//每MFT记录簇数【40】


dwClusterPerINDX:cardinal;//每索引簇数【44】


bSerialID:array【0..7】 of char;//卷标【48】


//【50】


end;


现在我们已经可以分析引导扇区所记录的内容.


var


P:Pchar;


BPR:R_Wu_NTFSBPB;


Wu_OpenDiskSector('G'); //打开G盘


Wu_ReadDiskSector(0,P); //读取G盘0扇区


wu_Copy(P,1,80,@BPR,1); //将数据直接拷贝到结构变量中即可


Wu_CloseDiskSector;


下面我们可以根据BPR的数据来进行一些初始化的设置


unit Wu_NTFSLook;


interface


uses


Wu_DiskSectorUnit,Wu_NTFSTypeUnit,Wu_BasicUnit;


procedure OpenDisk(Disk:Char); //打开某个盘


procedure CloseDisk; //退出时关闭


implementation


var


BPR:R_Wu_NTFSBPR; //用来保存当前打开分区的引导数据


ClusterSize:cardinal;//定义当前打开分区中每一个簇等于多少个基本扇区


MFTRecordSize:cardinal=2; //定义当前打开分区中每一个MFT等于多少个基本扇区


MFTBegin:int64; //定义当前打开分区中MFT的起始位置(每几个基本扇区)


procedure OpenDisk(Disk:Char);


var


P:Pchar;


begin


Wu_CloseDiskSector;


Wu_OpenDiskSector(Disk);


Wu_ReadDiskSector(0,P); //读取指定盘0扇区


Wu_Copy(P,1,80,@BPR,1);


ClusterSize:=BPR.wBytePerSector div 512 BPR.bSectorPerCluster;


MFTBegin:=BPR.ullMFTAddr ClusterSize;


end;


procedure CloseDisk;


begin


Wu_CloseDiskSector;


end;


end.


下一步就可以通过【$MFT的起始逻辑簇号】来访问$MFT.


MFT应该是一个数据结构,由每一条MFT整齐排列组成一整个MFT列表,用来记录磁盘内所有文件目录的关系.


既然MFT是一个接着一个排列的,那么我们必须要知道每一个MFT的大小,以方便我们的指针对应位置.


(注:这里MFT的大小是一个很奇怪的问题,现在所查到的资料都说得不清不楚,有些说固定大小为1024字节,有些又说不固定,有待研究)


我们暂且定义MFT的大小为1024字节.


const


MFTRecordSize=1024; //不管怎样都一定是512的整数倍这是肯定的


然后【$MFT的起始逻辑簇号】指向了列表第一项的位置.


BPR.ullMFTAddr;


下面是从网上找来的MFT文件记录属性头结构


偏移 长度 描述


0X00 4 固定值“FILE”


0X04 2 更新序列号偏移,与操作系统有关


0X06 2 固定列表大小


0X08 8 日志文件序列号


0X10 2 序列号(用于记录文件被反复使用的次数)


0X12 2 硬连接数,跟目录中的项目关联,非常重要的参数


0X14 2 第一个属性的偏移


0X16 2 标志字节①


0X18 4 文件记录实时大小(字节)②


0X1C 4 文件记录分配大小(字节)


0C20 8 基础记录 (0: itself)


0X28 2 下一个自由ID号


0X2A 2 边界


0X2C 4 WINDOWS XP中使用,本MFT记录号


0X30 4 MFT的使用标记③


经详细研究,MFT记录中前52($34)个字节的内容所表达的意思是固定的,那么我们又可以写数据结构了


type


R_Wu_MFT1=packed record


bHeadID:array 【0..3】 of char; //MFT标志,'FILE'【0】


usFixupOffset:word; //更新序列号偏移,与操作系统有关【4】


usFixupNum:word; //更新序列号的大小,固定列表大小【6】


bReserve1:array 【0..7】 of byte; //日志文件序列号【8】


wUnknownSeqNum:word; //序列号(用于记录文件被反复使用的次数)【10】


usLinkNum:word; //硬连接数【12】


usAttrOffset:word; //第一个属性的偏移地址(重要)【14】


wResident:word; //文件属性标志(重要,表示这个MFT文件的状态1:普通文件 0:文件被删除 3:普通目录 2:目录被删除)【16】


ulMFTSize:cardinal; //文件记录的实际长度,用来记录本MFT的实时大小(重要,用于MFT中每个属性分析到结束)【18】


ulMFTAllocSize:cardinal; //记录分配的大小(重要)【1C】


ullMainMFT:int64; //基本文件记录的文件索引号(重要)【20】


wNextFreeID:word; //下一个自由ID号(重要)【28】


wBianJie:word; //边界【2A】


ulXpMFTNum:cardinal; //WINDOWS XP中使用,本MFT记录号【2C】


MFTusesID:word; //MFT的使用标记(重要,它在MFT记录的两个扇区中与每扇区的最末两个字节相对应,如若不然,系统将示此记录为非法记录)【30】


//【32】


MFTUsesTemp:array of word; //MFT的使用标记(重要,上面由于MFT记录扇区将每个扇区中最后的两个字节占用掉了,所以在这里补回来)【32】


end;


MFT是由一个固定的头52($34)字节和一系列属性(不固定,每一条属性长度不固定,属性列表是由哪一些属性组成也不固定)组成,但总大小不超过1024字节(超过时由多条记录组成).


那么现在我们就可以读取指定一项MFT的记录


我们写一个函数用来获得指定第几个MFT的MFT头数据.


var


MFTHead:R_Wu_MFT1; //用来保存调用GetMFTHead所获得的MFT记录基本头数据


function GetMFTHead(N:int64):Pchar;


var


P:Pchar;


begin


Wu_ReadDiskSector(MFTBegin+NMFTRecordSize,P);


Wu_Copy(P,1,52,@MFTHead,1); //拷贝到结构变量中


Result:=@MFTHead;


end;


现在我们再重新分析一下它们的关系.


引导数据->MFT列表的第一项位置(指向)


MFT列表->每一个MFT(包含)


上一个MFT->下一个MFT(固定偏移1024)


每一个MFT所包含


{


MFT头(固定包含52字)


MFT属性列表(一个或多个)


}


MFT头->MFT属性列表的第一项位置(偏移指向)


上一个MFT属性->下一个MFT属性(偏移指向)


每一个MFT属性所包含


{


MFT属性头(固定包含9字)


MFT属性数据(各种属性)


}


我们上面已经可以获取MFT头,现在要进行的是对MFT所包含的属性数据分析.


MFT属性头是固定的,可以用一个数据结构来表达


type


R_Wu_MFTATTRIBUTEHead=packed record


Attribute:cardinal; //属性类型(重要)【0】


AllLen:cardinal; //属性的总长度(重要)【4】


ResidentSymbol:byte; //常驻或非常驻标志(0X00:常驻属性;0X01:非常驻属性)(重要)【8】


//【9】


end;


每一个MFT属性的类型是由MFT属性头的属性类型决定的


每一条MFT记录都可能包含任意几条属性


下面拿一个网上的NTFS卷上常用属性说明表


属性号 属性名 属性描述


0X10 $STANDRD_INFORMATION(标准属性) 包括基//代码效果参考:http://www.lyjsj.net.cn/wz/art_23348.html

本文件属性,如只读、存档;时间标记,如文件的创建时间和最近一次修改的时间;有多少目录指向本文件

0X20 $ATTRIBUTE_LIST(属性列表) 当一个文件需要使用多个MFT文件记录时,用来表示该文件的属性列表


0X30 $FILE_NAME(文件名属性) 这是以Unicode字符表示的,由于MS-DOS不能正确识别Win32子系统创建的文件名,当Win32子系统创建一个文件名时,MTFS会自动生成一个备用的MS-DOS文件名,所以一个文件可以有多种文件名属性。


0X40 $VOLUME_VERSION(卷版本) 卷版本号


0X50 $SECURITY_DEscriptOR(安全描述符) 这是为了向后兼容而被保留的,主要用于保护文件以防止未授权访问。


0X60 $VOLUME_NAME(卷名) 卷名称或卷标识


0X70 $VOLUME_INFORMATION(卷信息) 卷信息


0X80 $DATA(数据属性) 这是文件的内容


0X90 $INDEX_ROOT(索引根属性) 索引根


0XA0 $INDEX_ALLOCATION(索引分配属性) 索引分配


0XB0 $BITMAP(位图属性) 位图


0XC0 $SYMBOLIC_LINK(符号链接) 符号链接


0XD0 $EA_INFORMATION(EA信息) 扩充属性信息:主要为与OS/2兼容


0XE0 $EA 扩充属性:主要为与OS/2兼容


0X100 $OBJECT_ID 对象ID:一个具有64个字节的标识符,其中最低的16个字节对卷来说是唯一的


**


标准属性($10)


$STANDRD_INFORMATION的类型值为$10,总是常驻属性.它包含一个文件或目录的基本元数据.


虽然标准信息属性中含用的信息比较多,但对于文件系统来说并不重要.这个属性中的日期和时间值会相应地随着对文件的各种操作而与操作系统的时间保持一致.


网上很难找到该结构的定义信息


**


*


属性列表($20)


$ATTRIBUTE_LIST 当一个文件(或目录)的属性不能放在一个MFT文件记录中,当一个文件需要使用多个MFT文件记录时,用来表示该文件的属性列表


虽然数据属性常常因太大而存储//代码效果参考:http://www.lyjsj.net.cn/wx/art_23346.html

在运行中,但是其他属性也可能因MFT文件记录没有足够空间而需要存储在运行中。另外,如果一个文件有太多的属性而不能存放在MFT记录中,那么第二个MFT文件记录就可用来容纳这些额外的属性(或非常驻属性的头)。在这种情况下,一个叫作“属性列表”(attribute list)的属性就加进来。属性列表包括文件属性的名称和类型代码以及属性所在MFT的文件引用。属性列表通常用于太大或太零散的文件,这种文件因VCN-LCN映射关系太大而需要多个MFT文件记录。具有超过200个运行的文件通常需要属性列表。

这个结构也不是很清楚


*


*


文件名属性($30)


$FILE_NAME 文件名属性,这个是我们读取文件名的关键


网上的表:


偏移 大小 值 描述


0X00 4 0X30 属性类型


0X04 4 0X68 总长度


0X08 1 0X00 非常驻标志(0X00:常驻属性;0X01:非常驻属性)


0X09 1 0X00 属性名的名称长度


0X0A 2 0X18 属性名的名称偏移


0X0C 2 0X00 标志


0X0E 2 0X03 标识


0X10 4 0X4A 属性长度(L)


0X14 2 0X18 属性内容起始偏移


0X16 1 0X01 索引标志


0X17 1 0X00 填充


0X18 8 0500000000000500 父目录记录号(前6个字节)+序列号(与目录相关)


0X20 8 e0e3e1a066e9c301 文件创建时间


0X28 8 同上 文件修改时间


0X30 8 同上 最后一次MFT更新的时间


0X38 8 同上 最后一次访问时间


0X40 8 0040000000000000 文件分配大小


0X48 8 0040000000000000 文件实际大小


0X50 4 06000000 标志,如目录、压缩、隐藏等


0X54 4 00000000 用于EAS和重解析点


0X58 1 04 以字符计的文件名长度⑤,每字节占用字节数由下一字节命名空间确定,一个字节长度,所以文件名最大为255字节长(L)


0X59 1 03 文件名命名空间,见表8


0X60 2L 24004d004600 以Unicode方式标识的文件名


标志 意义 描述


0 POSIX 这是最大的命名空间。它大小写敏感,并允许使用除NULL(0)和左斜框(/)以外的所有Unicode字符作为文件名,文件名最大长度为255个字符。有一些字符,如冒号(:),在NTFS下有效,但WINDOWS不让使用。


1 WIN32 WIN32和POSIX命名空间的一个子集,不区分大小写,可以使用除“/:?|”以外的所有Unicode字符。另外,文件不能以句点和空格结束。


2 DOS DOS是WIN32命名空间的一个子集,要求比空格的ASC||码要大,且不能使用 + / , : ; ? \ | 等字符,另外其格式是1-8个字符的文件名,然后是句点分隔,然后是1-3个字符的扩展名。


3 Win32 & DOS 该命名空间要求文件名对Win32和DOS命名空间都有效,这样,文件名就可以在文件记录中只保存一个文件名


上面的表好像有一个错误,就是0X59再加一个字节应该为0X5A,而上表写0X60不知为什么


我们定义它的数据结构


type


R_Wu_FILENAMEATTRIBUTE=packed record //MFT文件名属性结构($30)


Attribute:cardinal; //属性类型【0】


AllLen:cardinal; //总长度,包括标准属性头本身【4】


ResidentSymbol:byte; //常驻或非常驻标志【8】


{其实上面那三个就是刚才MFT属性头,和R_Wu_MFTATTRIBUTEHead定义是一样的,现在这里重写只是为了数据拷贝操作的方便}


NameLen:byte; //名称长度【9】


NameOffset:word; //名称偏移【A】


FileSymbol:word; //标志,如0x0001为压缩标志,0x4000为加密标志,0x8000为稀疏标志【C】


Identifier:word; //标识【E】


AttributeLen:cardinal;//属性长度【10】


AttributeContentOffset:word;//属性内容起始偏移【14】


IndexSymbol:byte; //索引标志【16】


Fill:byte; //填充【17】


FileConferenceNumOfFartherDir:array【0..5】 of byte;//父目录的文件参考号(6Byte)【18】


FileConferenceNumOfFartherDir_Temp:word;//父目录的文件参考号【1E】


FileCreateTime:int64; //文件创建时间【20】


FileModifyTime:int64; //文件修改时间【28】


LastMFTRenewTime:int64;//最后一次的MFT更新时间【30】


FileLastVisitTime:int64;//文件最后一次的访问时间【38】


FileAllocSize:int64; //文件分配大小【40】


FileRealSize:int64; //文件实际大小【48】


FileSymbol_C:cardinal; //文件标志,如目录,压缩,隐藏等【50】


EAS:cardinal; //用于EAs和重解析点【54】


FileNameLen:byte; //以字符计算的文件名长度,每字符字节数由下一字节命名空间决定【58】


FileNameSpace:byte; //文件名命名空间【59】


//【5A】


FileName:string; //以Unicode 方式表示的文件名【5A】


end;


*


*


数据属性($80)


$DATA:数据流属性,也就是磁盘文件的内容.这是我们读取文件的内容的关键.


NTFS对于文件数据有两种不同的处理方式.当文件大小小于一定数时,它就直接将内容放在MFT中(不可思议吧,MFT里面有时也管这个),只有文件到达一定长度,MFT中放不下了,才将数据移动磁盘空间中存放.


那么直接放在MFT中的称之为常驻,否则为非常驻.(理论上MFT中每一种属性都可以这么干的)


下面用网上的表来说明:


偏移 大小 值 意义


0X00 4 0X80 属性类型(0X80,数据流属性)


0X04 4 0X48 属性长度(包括本头部的总大小)


0X08 1 0X01 非常驻标志(0X00:常驻属性;0X01:非常驻属性)


0X09 1 0X00 名称长度,$AttrDef中定义,所以名称长度为0


0X0A 2 0X0040 名称偏移


0X0C 2 0X00 标志,0X0001为压缩标志,0X4000为加密标志,0X8000为系数文件标志


0X0E 2 0X0001 标识


0X10 8 0X00 其实VCN


0X18 8 0X1FF1 结束VCN


0X20 2 0X40 数据运行的偏移


0X22 2 0X00 压缩引擎


0X24 4 0X00 填充


0X28 8 0X1FF2000 为属性值分配大小(按分配的簇的字节数计算)


0X30 8 0X1FF1C00 属性值实际大小


0X38 8 0X1FF1C00 属性压缩大小


0X40 … 2148062431… 数据运行④


④ 第四处为数据运行,是在数据流属性为非常驻的状况下索引到数据流的关键。其具体计算方式如下:


这是例子中的80H属性,其中蓝色部分为该80H属性(数据属性)的运行:


80 00 00 00 68 00 00 00 01 04 40 00 00 00 08 00


00 00 00 00 00 00 00 00 4b 00 00 00 00 00 00 00


48 00 00 00 00 00 00 00 00 c0 04 00 00 00 00 00


88 bc 04 00 00 00 00 00 88 bc 04 00 00 00 00 00


24 00 53 00 44 00 53 00 21 48 06 24 31 01 f3 aa


02 31 01 0d 7a fd 31 01 f3 38 02 31 01 c3 4b 05


00 a2 6b 81 d0 50 3d e1


该运行分为


子运行1:21 48 06 24


子运行2:31 01 f3 aa 02


子运行3:31 01 0d 7a fd


子运行4:31 01 f3 38 02


子运行5:31 01 c3 4b 05


以子运行1:“21 48 06 24”为例,“2”表示后面4个字节中后面2个字节是子运行的起始簇号,即子运行的起始簇号为“24 06”,“1”表示前面的1个字节表示子运行的大小,即该子运行的大小为“48”。所以该文件数据实际是从起始扇区号为0x2406的地方,占用0x48个簇。接下来是子运行2,运行2的簇号的起始位置为0x2406 + 0x02aaf3 = 0x2cef9。占用0x01个簇。接下来是子运行3,按照前面的理论,子运行3的起始簇号应该是0x2cef9+0xfd0a0d。但是0xfd0a0d的第一个字节为1(fd的首字节),证明此数为负数,所以不能简单的做加法,而应该取该数的补数来计算。既,0xfd7a0d取反加1,得到0x2F5F2,所以运行3的起始扇区号为0x2cef9 - 0x285f3 = 0x4096。占用0x01个簇。依此类推直到子运行之后为“00”时结束。


在程序中,一个Run代表整个运行,每一个子运行以链表的方式链接在一起。


表中没有关于常驻数据属性的说明,其实我们可以自己分析一下来自定义一个结构.


type


R_Wu_DATAATTRIBUTE_1=packed record //MFT数据属性结构(常驻)($80)


Attribute:cardinal; //属性类型【0】


AllLen:cardinal; //总长度,包括标准属性头本身【4】


ResidentSymbol:byte; //常驻或非常驻标志【8】


{其实上面那三个就是刚才MFT属性头,和R_Wu_MFTATTRIBUTEHead定义是一样的,现在这里重写只是为了数据拷贝操作的方便}


NameLen:byte; //名称长度,$AttrDef中定义,所以名称长度为0【9】


NameOffset:word; //名称偏移【A】


FileSymbol:word; //标志,如0x0001为压缩标志,0x4000为加密标志,0x8000为稀疏标志【C】


Identifier:word; //标识【E】


DataLen:cardinal; //数据长度(?,判断它就是)【10】


DataOffset:cardinal; //数据起始位置(?,估计它就是)【14】


//【18】


Data:string; //数据内容【18】


end;


在研究中发现这里有一点,XP中对于后面数据的处理,一定是8的整倍数.就是说如果文件长度小于8,那么一定会留8个空间,如果文件长度为9,那么又会留到16个空间.可能是以统一$30属性中MS-DOS文件名8字节的处理.


非常驻数据属性结构


type


R_Wu_DATAATTRIBUTE_2=packed record //MFT数据属性结构(非常驻)($80)


Attribute:cardinal; //属性类型【0】


AllLen:cardinal; //总长度,包括标准属性头本身【4】


ResidentSymbol:byte; //常驻或非常驻标志【8】


{其实上面那三个就是刚才MFT属性头,和R_Wu_MFTATTRIBUTEHead定义是一样的,现在这里重写只是为了数据拷贝操作的方便}


NameLen:byte; //名称长度【9】


NameOffset:word; //名称偏移【A】


FileSymbol:word; //标志,如0x0001为压缩标志,0x4000为加密标志,0x8000为稀疏标志【C】


Identifier:word; //标识【E】


StartVCN:int64; //起始VCN【10】


EndVCN:int64; //结束VCN【18】


RunOffset:word; //数据运行的偏移【20】


CompressEngine:word; //压缩引擎【22】


Fill:cardinal; //填充【24】


MallocForAttribute:int64;//为属性值分配的大小【28】


AttributeRealSize:int64;//属性值实际大小【30】


AttributeCompressSize:int64;//属性值压缩大小【38】


//【40】


DataRun:string; //数据运行的内容【40】


end;


*


*


索引根属性($90)


$INDEX_ROOT:这个属性记录当前目录(目录有这个属性)下一级的目录或文件的信息,关键!


在NTFS系统说,目录也是文件,目录以文件的形式存在.


一般来说,目录包括头属性、标准属性(0X10)、文件名属性(0X30)、索引根属性(0X90),大多数时候还会包含索引分配属性(0XA0)。


目录中包含的保存在该目录下的所有文件的信息


保存在该目录下的每一个文件都有一个项目。这个项目我们把它称作索引项,因为目录通过它索引到文件。因此,与文件的数据属性一样,目录的“数据属性”有一个新的名字叫做“索引根属性”(0X90属性)。索引根属性包括两个方面的内容。一个是该属性的头,这里我把他叫做索引头。还有一个部分是索引部分。索引部分又包括“索引项”和“索引项尾”。


引用网上的表:


属性头


10属性头


10属性


30属性头


30属性


90属性头(索引头)


90属性 (索引项)


索引项1


索引项2


………


00 00 00 00 00 00 00 00 10 00 00 00 02 00 00 00 (索引尾)


FF FF FF FF 82 79 47 11


那么一整个索引根属性($90)又是怎么组成的呢?看下面:


MFT属性头(固定9字)|索引头(也是固定的)|索引项...|索引尾|


小目录组成就是这样的.


网上关于索引根属性表:


偏移 大小 意义


0X00 4 属性号


0X04 4 属性长度


0X08 1 常驻标志


0X09 1 名称长度


0X0A 2 名称偏移


0X0C 2 标志(常驻属性不能压缩)


0X0E 2 属性ID


0X10 4 属性长度(不含头)


0X14 2 属性偏移


0X16 1 索引标志


0X17 1 填充


0X18 8 属性名


0X20 4 索引属性类型


0X24 4 排序规则


0X28 4 索引项分配大小


0X2C 1 每索引记录的簇数


0X2D 3 填充


0X30 4 每索引的偏移


0X34 4 索引项的总大小


0X38 4 索引项的分配


0X3C 1 标志,(0X01大索引)


0X3C 3 填充


上表最后有一个地方标志的位置是3C,但后面为什么填充也是3C


我们写下面的结构:


type


R_Wu_INDEXATTRIBUTE=packed record //MFT索引根属性结构($90)


Attribute:cardinal; //属性类型【0】


AllLen:cardinal; //总长度,包括标准属性头本身【4】


ResidentSymbol:byte; //常驻或非常驻标志【8】


{其实上面那三个就是刚才MFT属性头,和R_Wu_MFTATTRIBUTEHead定义是一样的,现在这里重写只是为了数据拷贝操作的方便}


NameLen:byte; //名称长度【9】


NameOffset:word; //名称偏移【A】


FileSymbol:word; &nbs

相关文章
|
18天前
|
监控 安全 网络协议
技术笔记:TOMOYOLinux(undone)
技术笔记:TOMOYOLinux(undone)
12 0
|
18天前
|
Linux 开发工具 C++
技术笔记:RustGUI编程
技术笔记:RustGUI编程
|
27天前
|
安全 网络安全
Day1:360培训学习重点笔记(7.13)
Day1:360培训学习重点笔记(7.13)
15 1
|
18天前
|
Linux 开发工具 C++
技术笔记:NanoGUI使用
技术笔记:NanoGUI使用
|
18天前
|
存储 JSON NoSQL
技术笔记:Stargate入门
技术笔记:Stargate入门
13 0
|
19天前
|
前端开发 关系型数据库 MySQL
技术好文:R基础学习(三)
技术好文:R基础学习(三)
|
17天前
|
数据可视化 BI OLAP
技术经验分享:DundasBI简介
技术经验分享:DundasBI简介
17 0
|
18天前
|
NoSQL Linux Shell
技术笔记:linux系统开发基础
技术笔记:linux系统开发基础
17 0
|
存储 前端开发 芯片
计算机理论基础(1)| 青训营笔记
计算机理论基础(1)| 青训营笔记
111 0