MFT(主文件表(Master File Table))锁定 MFT,即主文件表(Master File Table)的简称,它是NTFS文件系统的核心。MFT由一个个MFT项(也称为文件记录)组成,每个MFT项占用1024字节的空间。每个MFT项的前部几十个字节有着固定的头结构,用来描述本MFT项的相关信息。后面的字节存放着“属性”。每个文件和目录的信息都包含在MFT中,每个文件和目录至少有一个MFT项。除了引导扇区外,访问其他任何一个文件前都需要先访问MFT,在MFT中找到该文件的MFT项,根据MFT项中记录的信息找到文件内容并对其进行访问。NTFS(New Technology File System),是一种新型文件系统。 软件名称:Master File Table 软件平台:Windows NT 英文缩写:MFT 地位:NTFS文件系统的核心 MFT简介 (1)NTFS是Windows NT引入的新型文件系统,它具有许多新特性。NTFS中,卷中所有存放的数据均在一个叫$MFT的文件中,叫主文件表(Master File Table)。而$MFT则由文件记录(File Record)数组构成。File Record的大小一般是固定的,通常情况下均为1KB,这个概念相当于Linux中的inode。File Record在$MFT文件中物理上是连续的,且从0开始编号。$MFT仅供File System本身组织、架构文件系统使用,这在NTFS中称为元数据(Metadata)。 在NTFS文件系统里面,磁盘上的所有东西都以文件的形式出现。即使是元数据也是以一组文件的形式存储的。 主文件表( MFT )是这个卷上每一个文件的索引。 MFT 为每一个文件保存着一组称为“属性”的记录,每个属性存储了不同类型的信息。为主文件表(MFT)保留适当的空间。MFT在NTFS卷中扮演着重要的角色,对其性能的影响很大,系统空间分配、读写磁盘时会频繁地访问MFT,因此 MFT对NTFS的卷的性能有着至关重要的影响。NTFS文件系统的开发者在MFT附近预留着一个特定区域,用来减少MFT中的碎片,缺省状态下,这一区域占整个卷大小的12.5%,尽管这个区域能使得MFT中的碎片最少,但它并非总是合适的。 MFT操作说明 要对MFT的空间进行管理,可以在HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \FileSystem中增加一个类型为REG_DWORD的NtfsMftZoneReservation,它的缺省值是1,其范围是1-4(1表示 MFT占整个卷的12.5%,2表示25%,3表示37.5%,4表示50%)。 NTFS 中包含一个称为主文件表 (MFT) 的文件。MFT 是一个映射磁盘中储存的所有对象的索引文件。在 MFT 中,NTFS 磁盘上的每个文件(包括 MFT 自身)至少有一映射项。MFT 中的各项包含如下数据: 大小、时间及时间戳、安全属性和数据位置。 一但 MFT 产生碎片,磁盘碎片整理程序无法对其进行碎片整理。但是,由于可以持续使用 MFT 来存取磁盘上所有的其它文件,因此它也会逐渐形成碎片,从而导致磁盘存取时间加长,降低磁盘性能。NTFS 通过保留 1/8 的磁盘空间留作 MFT 专用而将此影响降至最低。磁盘的此区域(称为 MFT 区域)尽可能在 MFT 增加时保持其连续性。 ———————————————— 版权声明:本文为CSDN博主「千么漾漾」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_41786318/article/details/79791263 NTFS文件系统-MFT的属性头2016-07-07 12:51:02 海天数据恢复 阅读数 4461 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/a307871404/article/details/51850356 前面说过MFT是有一个个属性组成,那么每个属性的具体结构又是如何呢?MFT属性的类型很多,但它们都有个共同的特点,那就是每个属性都有属性头和属性体。属性头又分为常驻属性和非常驻属性。常驻属性和非常驻数据最大的区别是常驻属性的只是在MFT内部记录,非常驻数据由于MFT记录不下(一个MFT项只有1024)所以需要在其它数据区记录。不管是常驻属性还是非常驻属性,它的属性头的前面16个字节是一样的。 MFT属性结构图 从上图可以看出MFT头很小,只有几行代码 ,剩下都是MFT的属性。图中10属性和30属性都是常驻属性,而80属性是非常驻属性 ,因为80属性是记录文件内容的属性,一般不是几个字节就能记录的。下面我们来看下非常驻属性的属性头具体结构
非常驻属性头的数据结构
非常驻属性头的最后3个参数都表示属性内容的大小, 这里可以写成一样大的。 NTFS文件系统MFT的属性列表原文链接:http://blog.51cto.com/shujvhuifu/1801556 MFT是由一个个属性体组成,每个属性体都有一个对应的属性名。如0x10类型的属性表示标准属性,这个属性记录着文件的基本信息。 NTFS文件系统的MFT属性列表
红色标记:表示非常重要必须要记住 绿色标记:表示比较重要最好记住 没标记的了解下即可 转载于:https://blog.51cto.com/shujvhuifu/1801556 NTFS文件系统数据恢复----解析MFT表2015-05-11 21:50:12 weinierbian 阅读数 10406 分类专栏: 数据恢复 http://blog.csdn.net/jha334201553/article/details/9089119 开始先说下DBR, DBR是继MBR 之后最先访问的地方,MBR利用int 13h 读取MBR并将之加载到物理地址0x7c00的地方. 然后将段地址:代码地址入栈后retf跳过去运行. MBR利用BIOS中断int 13h读取数据加载到内存指定位置..传统的int 13h调用最多只能识别1024个磁头: 前面MBR讲解MBR的时候,有结构如下 /*+0x01*/ uchar StartHead; // 分区起始磁头号 (1磁头 = 63 扇区,取值 0~255 之间) /*+0x02*/ uint16 StartSector:10; // 启始柱面号 10位 (1柱面 = 255 磁头,取值 0~1023 之间) /*+0x02*/ uint16 StartCylinder:6; // 启始扇区号 6位 (取值 1 到 63 之间) 此结构可容纳最大值为FF FF FF (现在这个值基本都写成FE FF FF, 而废弃不用), 即最大能寻址的就是255柱面, 1023磁头, 63扇区,计算扇区个数为: 1023*255*63+255*63+63 = 16450623 再按每扇区 512 字节算, 那么它容量为 8 GB ≈ 512*16450623 B = 7.84 GB 传统的int 13h中断就受限于8G的限制, Microsoft等多家公司制定了int 13h扩展标准,让int 13h读写磁盘中断可以突破8G限制. 现在的计算机BIOS都是按扩展int 13h标准编写的代码.(具体详细内容可参考"扩展 int 13h规范"). 按MBR分区表里面的 SectionPrecedingPartition 逻辑扇区偏移(注意,这个逻辑扇区偏移是从0开始算的,读取出来值为63,而物理扇区是从1开始计算的,逻辑扇区转换物理扇区的时候必须+1才是正确的) 可以找到DBR的位置.可以看看winhex的显示 以下就偷懒不从MBR寻址分区的DBR了,而是直接打开盘符读取 (这样打开的第一个扇区就是DBR),这样做有个缺点,就是你用这个handle值将不能进行内存映射,只能一次多读取几个扇区来加快分析磁盘的速度(当前用的是一次读取20M数据然后分析)。 HANDLE handle = CreateFile ( TEXT("\\\\.\\C:") , GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); DBR结构定义为(对照winhex模板信息查看): //////////////////////////////////////////////////////////////////////////// // NTFS 的DBR 数据结构 //////////////////////////////////////////////////////////////////////////// typedef struct _BIOS_PARAMETER_BLOCK { /*+0x0B*/ uint16 BytesPerSector; //字节/扇区一般为0x0200 即512 /*+0x0D*/ uchar SectorsPerCluster; //扇区/簇 /*+0x0E*/ uint16 ReservedSectors; //保留扇区 /*+0x0F*/ uchar Fats; // /*+0x11*/ uint16 RootEntries; // /*+0x13*/ uint16 Sectors; // /*+0x15*/ uchar Media; //媒介描述 /*+0x16*/ uint16 SectorsPerFat; // /*+0x18*/ uint16 SectorsPerTrack; //扇区/磁轨 /*+0x1A*/ uint16 Heads; //头 /*+0x1C*/ uint32 HiddenSectors; //隐藏扇区 /*+0x20*/ uint32 LargeSectors; //checked when volume is mounted }BIOS_PARAMETER_BLOCK, *pBIOS_PARAMETER_BLOCK; typedef struct _NTFS_Boot_Sector{ /*+0x00*/ uchar JmpCode[3]; //跳转指令 /*+0x03*/char OemID[8]; //文件系统ID /*+0x0B*/ BIOS_PARAMETER_BLOCK PackedBpb; //BPB /*+0x24*/ uchar Unused[4]; //未使用,总是为 /*+0x28*/ uint64 NumberSectors; //扇区总数 /*+0x30*/ lcn MftStartLcn; //开始C# $MFT (簇) 乘以 BIOS_PARAMETER_BLOCK.SectorsPerCluster 值得到扇区号 /*+0x38*/ lcn Mft2StartLcn; //开始C# $MFTMirr (簇) /*+0x40*/ uchar ClustersPerFileRecordSegment;//文件记录大小指示器 /*+0x41*/ uchar Reserved0[3]; //未使用 /*+0x44*/ uchar DefaultClustersPerIndexAllocationBuffer; //簇/索引块 /*+0x45*/ uchar Reserved1[3]; //未使用 /*+0x48*/ uint64 SerialNumber; //64位序列号 /*+0x50*/ uint32 Checksum; //校验和 /*+0x54*/ uchar BootStrap[426]; //启动代码 /*+0x1FE*/ uint16 RecordEndSign; //0xAA55 结束标记 }NTFS_Boot_Sector, *pNTFS_Boot_Sector; 在读取DBR的时候,一些数据以后经常会用到,那么需要根据DBR里面的信息保存以后会用到的信息,下面定义一个常用的保存信息结构: //保存 NTFS 的基本信息 typedef struct _NTFS_INFO uint32 BytesPerSector; //每扇区的字节数 uint32 SectorsPerCluster; //每簇的扇区数 uint32 BytesPerCluster; //每簇的字节数 uint64 SectorCount; //扇区总数 uint64 MftStart; //MFT表开始簇 uint64 MftMirrStart; //MFT备份表开始簇 uint32 BytesPerFileRecord; //每个文件记录的字节数一般为512*2 uint16 VolumeLabelLength; // 卷名长度,卷名从MFT第4个项0x60属性得到(与0x30属性相似) wchar VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH]; //卷名 uint16 vcnlen; uchar vcn[VCN_LENTH]; } NTFS_INFO, *PNTFS_INFO; 其中 MAXIMUM_VOLUE_LABEL_LENGTH定义为 #define MAXIMUM_VOLUME_LABEL_LENGTH (32*sizeof(wchar)) NTFS_Boot_Sector .MftStartLcn*NTFS_Boot_Sector. PackedBpb .SectorsPerCluster得到MFT所在扇区号,这里为 786432*8 = 6291456扇区(字节偏移为 6291456*512= 3221225472 ( 十六进制0xC0000000))。然后MFT表里面的内容是根据簇号来读取数据的,那么可以定义一个根据簇号,读取数据的函数,如下形式: typedef struct _Partition_Stand_Post { HANDLE handle;//分区句柄 uint64 CluNnum; //簇号 uint32 BytesPerCluster; //每簇字节 uint64 CluCount; //簇数量 PNTFS_INFO NtfsInfo; //指向NTFS_INFO 结构 }Partition_Stand_Post, *pPartition_Stand_Post; //按簇读取数据, //传入 一个Partition_Stand_Post结构体指针,并指定buf,读取大小 //结果返回读取的数据指针 PBYTE ReadClues(pPartition_Stand_Post post, PBYTE buf, DWORD lenth) { DWORD dwbytes = 0; LARGE_INTEGER li = {0}; li.QuadPart = post->CluNnum*post->BytesPerCluster; SetFilePointer(post->handle, li.LowPart, &li.HighPart, FILE_BEGIN); ReadFile(post->handle, buf, lenth, &dwbytes, NULL); if (lenth == dwbytes) { return buf; } return NULL; } 下面先说MFT表的结构: 首先,看到的是头部,标记为"FILE", 结构体如下定义: //文件记录头 typedef struct _FILE_RECORD_HEADER { /*+0x00*/ uint32 Type; //固定值'FILE' /*+0x04*/ uint16 UsaOffset; //更新序列号偏移, 与操作系统有关 /*+0x06*/ uint16 UsaCount; //固定列表大小Size in words of Update Sequence Number & Array (S) /*+0x08*/ uint64 Lsn; //日志文件序列号(LSN) } FILE_RECORD_HEADER, *PFILE_RECORD_HEADER; //文件记录体 typedef struct _FILE_RECORD{ /*+0x00*/ FILE_RECORD_HEADER Ntfs;//MFT表头 /*+0x10*/ uint16 SequenceNumber; //序列号(用于记录文件被反复使用的次数) /*+0x12*/ uint16 LinkCount; //硬连接数 /*+0x14*/ uint16 AttributeOffset;//第一个属性偏移 /*+0x16*/ uint16 Flags; //falgs, 00表示删除文件,01表示正常文件,02表示删除目录,03表示正常目录 /*+0x18*/ uint32 BytesInUse; //文件记录实时大小(字节) 当前MFT表项长度,到FFFFFF的长度+4 /*+0x1C*/ uint32 BytesAllocated; //文件记录分配大小(字节) /*+0x20*/ uint64 BaseFileRecord; //= 0 基础文件记录 File reference to the base FILE record /*+0x28*/ uint16 NextAttributeNumber; //下一个自由ID号 /*+0x2A*/ uint16 Pading; //边界 /*+0x2C*/ uint32 MFTRecordNumber;//windows xp中使用,本MFT记录号 /*+0x30*/ uint32 MFTUseFlags; //MFT的使用标记 }FILE_RECORD, *pFILE_RECORD; 这里主要关注的就是文件头大小(0x38)可以找到第一个属性地址,紧跟在后面的是文件类型,00表示被删除的文件,01表示正常文件,02表示删除目录,03表示正常目录.再后面就是这个MFT记录的数据大小(很奇怪,为什么数据大小是从头到0xFFFFFFFF的大小+4,这个值为什么不是直接从头到0xFFFFFFFF的大小呢?). 根据FILE头部数据找到下面的一个个属性,接下来分析的就是一个个属性了. 属性由属性头跟属性体组成,属性头的结构定义如下: //属性头 typedef struct { /*+0x00*/ ATTRIBUTE_TYPE AttributeType; //属性类型 /*+0x04*/ uint16 RecordLength; //总长度(Header+body长度) /**0x06*/ uint16 unknow0; /*+0x08*/ uchar Nonresident; //非常驻标志 /*+0x09*/ uchar NameLength; //操作属性名长度 //0X0001为压缩标记 //0X4000为加密标记 //0X8000为系数文件标志 /*+0x0A*/ uint16 NameOffset; //属性名偏移(从属性起始位置的偏移) //NameLength 如果不为零,则用这个值去寻址数据偏移 /*+0x0C*/ uint16 Flags; //ATTRIBUTE_xxx flags. /*+0x0E*/ uint16 AttributeNumber; //The file-record-unique attribute instance number for this attribute. } ATTRIBUTE, *PATTRIBUTE; //属性头 typedef struct _RESIDENT_ATTRIBUTE { /*+0x00*/ ATTRIBUTE Attribute; //属性 /*+0x10*/ uint32 ValueLength; //Data部分长度 /*+0x14*/ uint16 ValueOffset; //Data内容起始偏移 /*+0x16*/ uchar Flags; //索引标志 /*+0x17*/ uchar Padding0; //填充 } RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE; 其中ATTRIBUTE_TYPE是一个枚举类型,里面定义了可能出现的所有类型。查看这个结构主要是看AttributeType(上图中,染上绿色的为属性类型,跟在后面的的红色框框内数据为属性头+属性体的大小(这个值必须是大于0x10,小于512的,程序中必须判断),由这个值可以得到下一个属性的地址),这个类型是什么,然后再跟去类型定义的Data部分去解析下面的属性体。遍历属性的时候可以根据属性类型来判断是否已经到达结尾,如果属性类型为0xFFFFFFFF则表示已经到达末尾(注意遍历的时候还是要结合FILE头部指定的大小来遍历,这样程序健壮性好很多,还有如果属性头后面跟着的大小值小于0x10也要结束遍历,因为这时候这个MFT项已经不可靠了,再继续下去程序出错可能性比较大(0x00值可能出现死循环))。 属性类型定义,及各个类型属性的结构(缺少无关紧要的结构,其他结构可参考nt4里面的源码并对照winhex分析): //属性类型定义 typedef enum _ATTRIBUTE_TYPE { AttributeStandardInformation = 0x10, AttributeAttributeList = 0x20, AttributeFileName = 0x30, AttributeObjectId = 0x40, AttributeSecurityDescriptor = 0x50, AttributeVolumeName = 0x60, AttributeVolumeInformation = 0x70, AttributeData = 0x80, AttributeIndexRoot = 0x90, AttributeIndexAllocation = 0xA0, AttributeBitmap = 0xB0, AttributeReparsePoint = 0xC0, AttributeEAInformation = 0xD0, AttributeEA = 0xE0, AttributePropertySet = 0xF0, AttributeLoggedUtilityStream = 0x100 } ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE; //基础信息ATTRIBUTE.AttributeType == 0x10 typedef struct _STANDARD_INFORMATION { uint64 CreationTime; //创建时间 uint64 ChangeTime; //修改时间 uint64 LastWriteTime; //最后写入时间 uint64 LastAccessTime; //最后访问时间 uint32 FileAttribute; //文件属性 uint32 AlignmentOrReserved[3]; // #if 0 uint32 QuotaId; uint32 SecurityId; uint64 QuotaCharge; USN Usn; #endif } STANDARD_INFORMATION, *PSTANDARD_INFORMATION; //属性列表ATTRIBUTE.AttributeType == 0x20 typedef struct _ATTRIBUTE_LIST { ATTRIBUTE_TYPE AttributeType; uint16 Length; uchar NameLength; uchar NameOffset; uint64 StartVcn; //LowVcn uint64 FileReferenceNumber; uint16 AttributeNumber; uint16 AlignmentOrReserved[3]; } ATTRIBUTE_LIST, *PATTRIBUTE_LIST; //文件属性ATTRIBUTE.AttributeType == 0x30 typedef struct { /*+0x00*/ uint64 DirectoryFile:48; //父目录记录号(前个字节) /*+0x06*/ uint64 ReferenceNumber:16;//+序列号(与目录相关) /*+0x08*/ uint64 CreationTime; //文件创建时间 /*+0x10*/ uint64 ChangeTime; //文件修改时间 /*+0x18*/ uint64 LastWriteTime; //MFT更新的时间 /*+0x20*/ uint64 LastAccessTime; //最后一次访问时间 /*+0x28*/ uint64 AllocatedSize; //文件分配大小 /*+0x30*/ uint64 DataSize; //文件实际大小 /*+0x38*/ uint32 FileAttributes; //标志,如目录\压缩\隐藏等 /*+0x3C*/ uint32 AlignmentOrReserved; //用于EAS和重解析 /*+0x40*/ uchar NameLength; //以字符计的文件名长度,没字节占用字节数由下一字节命名空间确定 //文件名命名空间, 0 POSIX大小写敏感,1 win32空间,2 DOS空间, 3 win32&DOS空间 /*+0x41*/ uchar NameType; /*+0x42*/ wchar Name[1]; //以Unicode方式标识的文件名 } FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE; //数据流属性 ATTRIBUTE.AttributeType == 0x80 typedef struct _NONRESIDENT_ATTRIBUTE { /*+0x00*/ ATTRIBUTE Attribute; /*+0x10*/ uint64 StartVcn; //LowVcn 起始VCN 起始簇号 /*+0x18*/ uint64 LastVcn; //HighVcn 结束VCN 结束簇号 /*+0x20*/ uint16 RunArrayOffset; //数据运行的偏移 /*+0x22*/ uint16 CompressionUnit; //压缩引擎 /*+0x24*/ uint32 Padding0; //填充 /*+0x28*/ uint32 IndexedFlag; //为属性值分配大小(按分配的簇的字节数计算) /*+0x30*/ uint64 AllocatedSize; //属性值实际大小 /*+0x38*/ uint64 DataSize; //属性值压缩大小 /*+0x40*/ uint64 InitializedSize; //实际数据大小 /*+0x48*/ uint64 CompressedSize; //压缩后大小 } NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE; 以下特别要说明就是数据恢复中要使用的一些结构 ;0x30 AttributeFileName属性 (如果文件名很长,那么有多个0x30属性,一个记录短文件名,一个记录长文件名),记录了很多文件信息,可能使用到的有文件创建时间、文件修改时间、最后写入时间、文件最后一次访问时间。这里面的几个值相当于用GetFileTime 或者GetFileInformationByHandle得到的几个FILETIME值,可以调用FileTimeToSystemTime转换时间。其中DataSize为实际文件使用的大小,在0x80属性中寻找簇恢复数据的时候会用到这个值。最后就是wchar的文件名,此名在cmd中显示需用WideCharToMultiByte转换后用printf显示,如果用wprintf显示中文会出现乱码问题。数据恢复的时候如果需要目录结构可由此属性中的DirectoryFile值得到,此值为父目录的记录号(相对于$MFT元所在扇区的偏移,即:$MFT + DirectoryFile*2)例如: 上图,父目录号为0x0000000002A4,从DBR中得到$MFT起始簇为786432(上面图一簇为8扇区),则父目录的MFT表项扇区为: 786432*8+0x0000000002A4*2 = 6292808,再查看6292808扇区: 所以这个文件为 X:\windows\notepad.exe(其中X表示为根目录,具体看前面CreateFile参数值是什么了). 0x80 AttributeData属性( 注意:如果是目录的话, 此结构属性是0xA0 ) 如果有多个0x80属性,则应该认为这个文件里面存在数据流文件,数据流表示形式为"0x30记录文件名:流文件名",并且在explorer浏览中查看不到这个文件,NTFS刚出来的时候,文件流属性进程被病毒作者使用,比如如果要将hack.exe数据加到 1.txt 数据流里面,那么可以如下方式: void CrateDataStream() HANDLE hfile = CreateFile( TEXT("1.txt:DataStream.exe"), //1.txt中数据流名字为DataStream.exe(随意取的) GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hfile == INVALID_HANDLE_VALUE) { return ; //打开文件错误 } HANDLE hExeFile = CreateFile( TEXT("hack.exe"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hExeFile == INVALID_HANDLE_VALUE) { CloseHandle(hfile); return ; //打开文件错误 } DWORD dwsize = GetFileSize(hExeFile, NULL); BYTE* buf = new BYTE[dwsize]; DWORD wbytes; ReadFile(hExeFile, buf, dwsize, &wbytes, NULL); WriteFile(hfile, buf, wbytes, &wbytes, NULL); CloseHandle(hExeFile); CloseHandle(hfile); } 一般是病毒作者将这个 1.txt 添加到压缩文件(高级里面选上保存文件流数据), 然后搞成自解压包, 在解压后命令中写入1.txt: DataStream.exe, 在用户双击解压的时候就会运行里面的数据流程序。不过现在杀毒软件对这种数据流病毒基本都能杀了。 再说数据恢复的关键点:数据内容由NONRESIDENT_ATTRIBUTE. RunArrayOffset偏移指定DATA数据地址。如果属性头 ATTRIBUTE.Nonresident标记为1表示非常驻,则下面会解析怎么需要解析DATA部分, 如果为0则表示DATA就是文件内容, 比如一个txt文件里面记录的数据很少, 则此标记为0, 记事本里面数据就保存在DATA中。上图因为查看的是MFT自身,$MFT比较特殊,非常驻属性设置成0(即FALSE)但是却要像非常驻属性一样去分析。 上图蓝色的部分,看不太清数据,原始数据如下: 最开始的数据为32,其中高4bits表示数据起始簇地址占用字节数,后4bits为数据大小(簇个数)占用字节数..这里要特别注意,第一个起始簇信息是无符号的,后面第二个开始就是相对于前面一个簇的偏移,是有正负的,查了很多资料,这点基本上都没提及,这也是数据恢复最容易出错的地方,辛辛苦苦写好了程序,一测试发现恢复出来的数据有问题。 上面第一个扇区地址为52604144(0x64559E*8,相对去当前分区的扇区偏移),第二个扇区地址为(0x64559E - 0x 77)*8 = 6575399 扇区(这里值0x89为-119)。 恢复数据的时候可以根据簇大小读取文件,然后保存,再根据前面的NONRESIDENT_ATTRIBUTE. DataSize值去SetFilePointer设置文件大小继而调用SetEndOfFile。 定义一个结构体表示这个结构: typedef struct _VCN_FLASH { uchar VcnLen:4; //簇流长度 *8*512 才是得到的文件字节数 uchar StartVcnLen:4; //簇流起始位置--簇号 uchar Data[1]; //簇流长度&Data + 簇流起始位置&Data+VcnLen 数据部分 }VCN_FLASH, *PVCN_FLASH; 再定义2个函数计算有符号与无符号数: uint64 make_uint64(uchar* buf, int lenth) { int64 ui=0; if (lenth > 8) { return (int64)0; } for (int i=0; i<lenth; i++) { ui = (buf[i]<<8*i)|ui; } return ui; } int64 make_int64(uchar* buf, int lenth) { int64 ui=0; if (lenth > 8) { return (int64)0; } for (int i=0; i<lenth; i++) { ui = (buf[i]<<8*i)|ui; } //判断符号位,为负则需减取反 if (buf[lenth-1] >= 0x80) { int64 xorval = 0; for (i=0; i<lenth; i++) { xorval = xorval|(0xFF<<8*i); } ui = -((ui - 1)^xorval); } return ui; } 0x90 AttributeIndexRoot 属性 ( 目录索引B+树结构 ) 这个属性,我也不是很清楚,等弄清楚了,再接着完善................. NTFS文件系统中MFT项中主要字节的标注(***)强调: 1、标注是结合其它资料和自己的理解完成的,有可能标注不一定正确,所以仅供参考!! 2、如转载,请保持图片的完整性,这也是对原创人员的尊重,谢谢!! 原始 基本标注 文件记录头标注 文件记录头标志字节的注解 属性标注 属性头 属性值标注 常驻、无属性名的属性头标注 常驻、有属性名的属性头标注 非常驻、无属性名的属性头标注 非常驻、有属性名的属性头标注 |
|