锁定
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
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个参数都表示属性内容的大小, 这里可以写成一样大的。
原文链接: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_STREAM | EFS加密属性 |
红色标记:表示非常重要必须要记住
绿色标记:表示比较重要最好记住
没标记的了解下即可
转载于:https://blog.51cto.com/shujvhuifu/1801556
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+树结构 )
这个属性,我也不是很清楚,等弄清楚了,再接着完善.................
强调:
1、标注是结合其它资料和自己的理解完成的,有可能标注不一定正确,所以仅供参考!!
2、如转载,请保持图片的完整性,这也是对原创人员的尊重,谢谢!!
原始
基本标注
文件记录头标注
文件记录头标志字节的注解
属性标注
属性头 属性值标注
常驻、无属性名的属性头标注
常驻、有属性名的属性头标注
非常驻、无属性名的属性头标注
非常驻、有属性名的属性头标注
联系客服