打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
NTFS文件系统数据恢复----解析MFT表+NTFS文件系统中MFT项中主要字节的标注(**)

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属性是记录文件内容的属性,一般不是几个字节就能记录的。下面我们来看下非常驻属性的属性头具体结构

偏移字节(16进制)描述
00-03属性类型
04-07属性长度
08常驻属性标志00:常驻 ;01表示非常驻
09属性名长度(为0表示没有属性名)
0A-0B属性名偏移(相对于属性头)
0C-0D标志
0E-0F属性ID标志
10-13属性体大小
14-15属性头的大小
16索引
17保留

非常驻属性头的数据结构

偏移字节(16进制)描述
00-03属性类型
04-07属性长度
08常驻属性标志00:常驻 ;01表示非常驻
09属性名长度(为0表示没有属性名)
0A-0B属性名偏移(相对于属性头)
0C-0D标志
0E-0F属性ID标志
10-17簇流的起始虚拟簇号(总是从0开始)
18-1F簇流的结束虚拟簇号
20-21簇流列表相对本属性头起始处偏移
22-23压缩单位大小
24-27保留
28-2F为属性内容分配的空间大小字节数
30-37属性内容实际占用的大小字节数
38-3F属性内容初始大小字节数

非常驻属性头的最后3个参数都表示属性内容的大小, 这里可以写成一样大的。

NTFS文件系统MFT的属性列表

原文链接:http://blog.51cto.com/shujvhuifu/1801556

MFT是由一个个属性体组成,每个属性体都有一个对应的属性名。如0x10类型的属性表示标准属性,这个属性记录着文件的基本信息。

NTFS文件系统的MFT属性列表

MFT属性类型值(16进制)MFT属性名描述
10$STANDARD_IFORMATION标准属性,包含文件的基本属性,只读 创建时间、最后访问时间等属性。
20$ATTRIBUTE_LIST属性列表
30$FILE_NAME文件名属性(UNICODE编码)
40$OBJECT_ID对象ID属性,文件或目录的16字节唯一标志
50$SECURITY_DESCRIPTOR安全描述符属性,文件的访问控制安全属性
60$VOLUME_NAME卷名属性
70$VOLUME_INFORMATION卷信息属性
80$DATA文件的数据属性
90$INDEX_ROOT索引根属性
A0$INDEX_ALLOCATION是90属性的扩展版(90属性只能在MFT内记录文件列表,A0属性将文件列表记录到数据区可以记录更多的文件)
B0$BITMAP位图属性
C0$REPARSE_POINT重解析点属性
D0$EA_INFORMATION扩展属性信息
E0$EA扩展属性
100$LOGGED_UTILITY_STREAMEFS加密属性

红色标记:表示非常重要必须要记住

绿色标记:表示比较重要最好记住

没标记的了解下即可

转载于: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、如转载,请保持图片的完整性,这也是对原创人员的尊重,谢谢!!

原始

基本标注

文件记录头标注

文件记录头标志字节的注解

属性标注

属性头 属性值标注

常驻、无属性名的属性头标注

常驻、有属性名的属性头标注

非常驻、无属性名的属性头标注

非常驻、有属性名的属性头标注

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
NTFS文件系统数据恢复----解析MFT表
关于NTFS
文件系统详解
NTFS文件系统中MFT项中主要字节的标注(完整版)
【格式化方式NTFS、FAT与FAT32间的区别】移动U盘
解读NTFS
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服