NTFS文件系统数据恢复----解析MFT表

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的显示

NTFS文件系统数据恢复----解析MFT表_第1张图片

        以下就偷懒不从MBR寻址分区的DBR了,而是直接打开盘符读取 (这样打开的第一个扇区就是DBR),这样做有个缺点,就是你用这个handle值将不能进行内存映射,只能一次多读取几个扇区来加快分析磁盘的速度(当前用的是一次读取20M数据然后分析)。

[cpp]  view plain copy
  1. HANDLE  handle = CreateFile ( TEXT("\\\\.\\C:") ,  
  2.                           GENERIC_READ,  
  3.                           FILE_SHARE_READ|FILE_SHARE_WRITE,  
  4.                           NULL,  
  5.                           OPEN_EXISTING,  
  6.                           FILE_ATTRIBUTE_NORMAL,  
  7.                           NULL);  

        DBR结构定义为(对照winhex模板信息查看):


NTFS文件系统数据恢复----解析MFT表_第2张图片

[cpp]  view plain copy
  1. ////////////////////////////////////////////////////////////////////////////  
  2. //  
  3. //  NTFS 的DBR 数据结构  
  4. //  
  5. ////////////////////////////////////////////////////////////////////////////  
  6. typedef struct _BIOS_PARAMETER_BLOCK {  
  7.   
  8.  /*+0x0B*/    uint16  BytesPerSector;    // 字节/扇区一般为0x0200 即512  
  9.  /*+0x0D*/    uchar   SectorsPerCluster; // 扇区/簇   
  10.  /*+0x0E*/    uint16  ReservedSectors;   // 保留扇区  
  11.  /*+0x0F*/    uchar   Fats;              //   
  12.  /*+0x11*/    uint16  RootEntries;       //   
  13.  /*+0x13*/    uint16  Sectors;           //   
  14.  /*+0x15*/    uchar   Media;             // 媒介描述  
  15.  /*+0x16*/    uint16  SectorsPerFat;     //   
  16.  /*+0x18*/    uint16  SectorsPerTrack;   // 扇区/磁轨  
  17.  /*+0x1A*/    uint16  Heads;             // 头  
  18.  /*+0x1C*/    uint32  HiddenSectors;     // 隐藏扇区  
  19.  /*+0x20*/    uint32  LargeSectors;      // checked when volume is mounted  
  20.   
  21. }BIOS_PARAMETER_BLOCK, *pBIOS_PARAMETER_BLOCK;  
  22.   
  23. typedef struct _NTFS_Boot_Sector{  
  24.  /*+0x00*/  uchar    JmpCode[3];        // 跳转指令  
  25.  /*+0x03*/  char     OemID[8];          // 文件系统ID  
  26.  /*+0x0B*/  BIOS_PARAMETER_BLOCK PackedBpb;   // BPB  
  27.  /*+0x24*/  uchar    Unused[4];           // 未使用,总是为  
  28.  /*+0x28*/  uint64   NumberSectors;       // 扇区总数  
  29.  /*+0x30*/  lcn      MftStartLcn;        // 开始C# $MFT  (簇) 乘以 BIOS_PARAMETER_BLOCK.SectorsPerCluster 值得到扇区号  
  30.  /*+0x38*/  lcn      Mft2StartLcn;       // 开始C# $MFTMirr (簇)  
  31.  /*+0x40*/  uchar    ClustersPerFileRecordSegment;  // 文件记录大小指示器  
  32.  /*+0x41*/  uchar   Reserved0[3];       // 未使用  
  33.  /*+0x44*/  uchar DefaultClustersPerIndexAllocationBuffer;     // 簇/索引块  
  34.  /*+0x45*/  uchar   Reserved1[3];       // 未使用  
  35.  /*+0x48*/  uint64  SerialNumber;       // 64位序列号  
  36.  /*+0x50*/  uint32  Checksum;           // 校验和  
  37.  /*+0x54*/  uchar   BootStrap[426];     // 启动代码  
  38.  /*+0x1FE*/ uint16  RecordEndSign;      // 0xAA55 结束标记  
  39. }NTFS_Boot_Sector, *pNTFS_Boot_Sector;  
        在读取DBR的时候,一些数据以后经常会用到,那么需要根据DBR里面的信息保存以后会用到的信息,下面定义一个常用的保存信息结构:

[cpp]  view plain copy
  1. // 保存 NTFS 的基本信息  
  2. typedef struct _NTFS_INFO  
  3. {  
  4.     uint32 BytesPerSector;              // 每扇区的字节数  
  5.     uint32 SectorsPerCluster;           // 每簇的扇区数  
  6.     uint32 BytesPerCluster;             // 每簇的字节数  
  7.     uint64 SectorCount;                 // 扇区总数  
  8.     uint64 MftStart;                    // MFT表开始簇  
  9.     uint64 MftMirrStart;                // MFT备份表开始簇  
  10.     uint32 BytesPerFileRecord;          // 每个文件记录的字节数一般为512*2  
  11.       
  12.     uint16 VolumeLabelLength;           //  卷名长度,卷名从MFT第4个项0x60属性得到(与0x30属性相似)  
  13.     wchar VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH];  // 卷名  
  14.       
  15.     uint16 vcnlen;  
  16.     uchar vcn[VCN_LENTH];  
  17. } NTFS_INFO, *PNTFS_INFO;  
其中 MAXIMUM_VOLUE_LABEL_LENGTH定义为
[cpp]  view plain copy
  1. #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表里面的内容是根据簇号来读取数据的,那么可以定义一个根据簇号,读取数据的函数,如下形式:

[cpp]  view plain copy
  1. typedef struct _Partition_Stand_Post  
  2. {  
  3.     HANDLE handle;  // 分区句柄  
  4.     uint64 CluNnum; // 簇号  
  5.     uint32 BytesPerCluster; // 每簇字节  
  6.     uint64 CluCount;  // 簇数量  
  7.     PNTFS_INFO NtfsInfo; // 指向NTFS_INFO 结构  
  8. }Partition_Stand_Post, *pPartition_Stand_Post;  
  9.   
  10. // 按簇读取数据,  
  11. // 传入 一个Partition_Stand_Post结构体指针,并指定buf,读取大小  
  12. // 结果返回读取的数据指针  
  13. PBYTE ReadClues(pPartition_Stand_Post post, PBYTE buf, DWORD lenth)  
  14. {  
  15.     DWORD dwbytes = 0;  
  16.     LARGE_INTEGER li = {0};  
  17.     li.QuadPart = post->CluNnum*post->BytesPerCluster;  
  18.     SetFilePointer(post->handle, li.LowPart, &li.HighPart, FILE_BEGIN);  
  19.     ReadFile(post->handle, buf, lenth, &dwbytes, NULL);  
  20.     if (lenth == dwbytes)  
  21.     {  
  22.         return buf;  
  23.     }  
  24.     return NULL;  
  25. }  
下面先说MFT表的结构:

NTFS文件系统数据恢复----解析MFT表_第3张图片

首先,看到的是头部,标记为"FILE", 结构体如下定义:


[cpp]  view plain copy
  1. // 文件记录头  
  2. typedef struct _FILE_RECORD_HEADER  
  3. {  
  4.  /*+0x00*/  uint32 Type;            // 固定值'FILE'  
  5.  /*+0x04*/  uint16 UsaOffset;       // 更新序列号偏移, 与操作系统有关  
  6.  /*+0x06*/  uint16 UsaCount;        // 固定列表大小Size in words of Update Sequence Number & Array (S)  
  7.  /*+0x08*/  uint64 Lsn;             // 日志文件序列号(LSN)  
  8. } FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;  
  9.   
  10. // 文件记录体  
  11. typedef struct _FILE_RECORD{  
  12.  /*+0x00*/  FILE_RECORD_HEADER Ntfs;  // MFT表头  
  13.  /*+0x10*/  uint16  SequenceNumber;   // 序列号(用于记录文件被反复使用的次数)  
  14.  /*+0x12*/  uint16  LinkCount;        // 硬连接数  
  15.  /*+0x14*/  uint16  AttributeOffset;  // 第一个属性偏移  
  16.  /*+0x16*/  uint16  Flags;            // falgs, 00表示删除文件,01表示正常文件,02表示删除目录,03表示正常目录  
  17.  /*+0x18*/  uint32  BytesInUse;       // 文件记录实时大小(字节) 当前MFT表项长度,到FFFFFF的长度+4  
  18.  /*+0x1C*/  uint32  BytesAllocated;   // 文件记录分配大小(字节)  
  19.  /*+0x20*/  uint64  BaseFileRecord;   // = 0 基础文件记录 File reference to the base FILE record  
  20.  /*+0x28*/  uint16  NextAttributeNumber; // 下一个自由ID号  
  21.  /*+0x2A*/  uint16  Pading;           // 边界  
  22.  /*+0x2C*/  uint32  MFTRecordNumber;  // windows xp中使用,本MFT记录号  
  23.  /*+0x30*/  uint32  MFTUseFlags;      // MFT的使用标记  
  24. }FILE_RECORD, *pFILE_RECORD;  

        这里主要关注的就是文件头大小(0x38)可以找到第一个属性地址,紧跟在后面的是文件类型,00表示被删除的文件,01表示正常文件,02表示删除目录,03表示正常目录.再后面就是这个MFT记录的数据大小(很奇怪,为什么数据大小是从头到0xFFFFFFFF的大小+4,这个值为什么不是直接从头到0xFFFFFFFF的大小呢?).

 

       根据FILE头部数据找到下面的一个个属性,接下来分析的就是一个个属性了.

属性由属性头跟属性体组成,属性头的结构定义如下:

[cpp]  view plain copy
  1. // 属性头  
  2. typedef struct  
  3. {  
  4.  /*+0x00*/  ATTRIBUTE_TYPE AttributeType;    // 属性类型  
  5.  /*+0x04*/  uint16 RecordLength;             // 总长度(Header+body长度)  
  6.  /**0x06*/  uint16 unknow0;  
  7.  /*+0x08*/  uchar Nonresident;               // 非常驻标志  
  8.  /*+0x09*/  uchar NameLength;                // 操作属性名长度  
  9.   
  10.                                           // 0X0001为压缩标记  
  11.                                         // 0X4000为加密标记  
  12.                                         // 0X8000为系数文件标志  
  13.  /*+0x0A*/  uint16 NameOffset;           // 属性名偏移(从属性起始位置的偏移)  
  14.                                               // NameLength 如果不为零,则用这个值去寻址数据偏移  
  15.  /*+0x0C*/  uint16 Flags;                    // ATTRIBUTE_xxx flags.  
  16.  /*+0x0E*/  uint16 AttributeNumber;          // The file-record-unique attribute instance number for this attribute.  
  17. } ATTRIBUTE, *PATTRIBUTE;  
  18.   
  19. // 属性头   
  20. typedef struct _RESIDENT_ATTRIBUTE  
  21. {  
  22.  /*+0x00*/  ATTRIBUTE Attribute;   // 属性  
  23.  /*+0x10*/  uint32 ValueLength;    // Data部分长度  
  24.  /*+0x14*/  uint16 ValueOffset;    // Data内容起始偏移  
  25.  /*+0x16*/  uchar Flags;           // 索引标志  
  26.  /*+0x17*/  uchar Padding0;        // 填充  
  27. } RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;  

        其中  ATTRIBUTE_TYPE 是一个枚举类型,里面定义了可能出现的所有类型。查看这个结构主要是看AttributeType(上图中,染上绿色的为属性类型,跟在后面的的红色框框内数据为属性头+属性体的大小(这个值必须是大于0x10,小于512的,程序中必须判断),由这个值可以得到下一个属性的地址),这个类型是什么,然后再跟去类型定义的Data部分去解析下面的属性体。遍历属性的时候可以根据属性类型来判断是否已经到达结尾,如果属性类型为0xFFFFFFFF则表示已经到达末尾(注意遍历的时候还是要结合FILE头部指定的大小来遍历,这样程序健壮性好很多,还有如果属性头后面跟着的大小值小于0x10也要结束遍历,因为这时候这个MFT项已经不可靠了,再继续下去程序出错可能性比较大(0x00值可能出现死循环))。

 

        属性类型定义,及各个类型属性的结构(缺少无关紧要的结构,其他结构可参考nt4里面的源码并对照winhex分析):

[cpp]  view plain copy
  1. // 属性类型定义  
  2. typedef enum _ATTRIBUTE_TYPE  
  3. {  
  4.     AttributeStandardInformation = 0x10,  
  5.     AttributeAttributeList = 0x20,  
  6.     AttributeFileName = 0x30,  
  7.     AttributeObjectId = 0x40,  
  8.     AttributeSecurityDescriptor = 0x50,  
  9.     AttributeVolumeName = 0x60,  
  10.     AttributeVolumeInformation = 0x70,  
  11.     AttributeData = 0x80,  
  12.     AttributeIndexRoot = 0x90,  
  13.     AttributeIndexAllocation = 0xA0,  
  14.     AttributeBitmap = 0xB0,  
  15.     AttributeReparsePoint = 0xC0,  
  16.     AttributeEAInformation = 0xD0,  
  17.     AttributeEA = 0xE0,  
  18.     AttributePropertySet = 0xF0,  
  19.     AttributeLoggedUtilityStream = 0x100  
  20. } ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;  
  21.   
  22. // 基础信息ATTRIBUTE.AttributeType == 0x10   
  23. typedef struct _STANDARD_INFORMATION  
  24. {  
  25.     uint64 CreationTime;         // 创建时间  
  26.     uint64 ChangeTime;           // 修改时间  
  27.     uint64 LastWriteTime;        // 最后写入时间  
  28.     uint64 LastAccessTime;       // 最后访问时间  
  29.     uint32 FileAttribute;        // 文件属性  
  30.     uint32 AlignmentOrReserved[3];  //   
  31. #if 0  
  32.     uint32 QuotaId;  
  33.     uint32 SecurityId;  
  34.     uint64 QuotaCharge;  
  35.     USN Usn;  
  36. #endif  
  37. } STANDARD_INFORMATION, *PSTANDARD_INFORMATION;  
  38.   
  39.   
  40. // 属性列表ATTRIBUTE.AttributeType == 0x20  
  41. typedef struct _ATTRIBUTE_LIST  
  42. {  
  43.     ATTRIBUTE_TYPE AttributeType;  
  44.     uint16 Length;  
  45.     uchar NameLength;  
  46.     uchar NameOffset;  
  47.     uint64 StartVcn; // LowVcn  
  48.     uint64 FileReferenceNumber;  
  49.     uint16 AttributeNumber;  
  50.     uint16 AlignmentOrReserved[3];  
  51. } ATTRIBUTE_LIST, *PATTRIBUTE_LIST;  
  52.   
  53.   
  54. // 文件属性ATTRIBUTE.AttributeType == 0x30  
  55. typedef struct  
  56. {  
  57.  /*+0x00*/  uint64 DirectoryFile:48;    // 父目录记录号(前个字节)  
  58.  /*+0x06*/  uint64 ReferenceNumber:16;  // +序列号(与目录相关)  
  59.  /*+0x08*/  uint64 CreationTime;        // 文件创建时间  
  60.  /*+0x10*/  uint64 ChangeTime;          // 文件修改时间          
  61.  /*+0x18*/  uint64 LastWriteTime;       // MFT更新的时间  
  62.  /*+0x20*/  uint64 LastAccessTime;      // 最后一次访问时间  
  63.  /*+0x28*/  uint64 AllocatedSize;       // 文件分配大小  
  64.  /*+0x30*/  uint64 DataSize;            // 文件实际大小  
  65.  /*+0x38*/  uint32 FileAttributes;      // 标志,如目录\压缩\隐藏等  
  66.  /*+0x3C*/  uint32 AlignmentOrReserved; // 用于EAS和重解析  
  67.  /*+0x40*/  uchar NameLength;      // 以字符计的文件名长度,没字节占用字节数由下一字节命名空间确定  
  68.   
  69.             // 文件名命名空间, 0 POSIX大小写敏感,1 win32空间,2 DOS空间, 3 win32&DOS空间  
  70.  /*+0x41*/  uchar NameType;          
  71.  /*+0x42*/  wchar Name[1];         // 以Unicode方式标识的文件名  
  72. } FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE;  
  73.   
  74. // 数据流属性 ATTRIBUTE.AttributeType == 0x80   
  75. typedef struct _NONRESIDENT_ATTRIBUTE  
  76. {  
  77.     /*+0x00*/   ATTRIBUTE Attribute;    
  78.   
  79.     /*+0x10*/   uint64 StartVcn;     // LowVcn 起始VCN  起始簇号  
  80.     /*+0x18*/   uint64 LastVcn;      // HighVcn  结束VCN  结束簇号  
  81.   
  82.     /*+0x20*/   uint16 RunArrayOffset;    // 数据运行的偏移  
  83.     /*+0x22*/   uint16 CompressionUnit;   // 压缩引擎  
  84.     /*+0x24*/   uint32  Padding0;       // 填充  
  85.     /*+0x28*/   uint32  IndexedFlag;    // 为属性值分配大小(按分配的簇的字节数计算)  
  86.     /*+0x30*/   uint64 AllocatedSize;   // 属性值实际大小  
  87.     /*+0x38*/   uint64 DataSize;     // 属性值压缩大小  
  88.     /*+0x40*/   uint64 InitializedSize;   // 实际数据大小  
  89.     /*+0x48*/   uint64 CompressedSize;    // 压缩后大小  
  90. } NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE;  

        以下特别要说明就是数据恢复中要使用的一些结构

       0x30 AttributeFileName属性 (如果文件名很长,那么有多个0x30属性,一个记录短文件名,一个记录长文件名),记录了很多文件信息,可能使用到的有文件创建时间、文件修改时间、最后写入时间、文件最后一次访问时间。这里面的几个值相当于用GetFileTime 或者GetFileInformationByHandle得到的几个FILETIME值,可以调用FileTimeToSystemTime转换时间。其中DataSize为实际文件使用的大小,在0x80属性中寻找簇恢复数据的时候会用到这个值。最后就是wchar的文件名,此名在cmd中显示需用WideCharToMultiByte转换后用printf显示,如果用wprintf显示中文会出现乱码问题。数据恢复的时候如果需要目录结构可由此属性中的DirectoryFile值得到,此值为父目录的记录号(相对于$MFT元所在扇区的偏移,即:$MFT + DirectoryFile*2)例如:

NTFS文件系统数据恢复----解析MFT表_第4张图片

上图,父目录号为0x0000000002A4,从DBR中得到$MFT起始簇为786432(上面图一簇为8扇区),则父目录的MFT表项扇区为: 786432*8+0x0000000002A4*2 = 6292808,再查看6292808扇区:

NTFS文件系统数据恢复----解析MFT表_第5张图片

所以这个文件为 X:\windows\notepad.exe(其中X表示为根目录,具体看前面CreateFile参数值是什么了).

       0x80 AttributeData属性( 注意:如果是目录的话, 此结构属性是0xA0 )

       如果有多个0x80属性,则应该认为这个文件里面存在数据流文件,数据流表示形式为"0x30记录文件名:流文件名",并且在explorer浏览中查看不到这个文件,NTFS刚出来的时候,文件流属性进程被病毒作者使用,比如如果要将hack.exe数据加到 1.txt 数据流里面,那么可以如下方式:

 

[cpp]  view plain copy
  1. void CrateDataStream()  
  2. {  
  3.     HANDLE hfile = CreateFile(  TEXT("1.txt:DataStream.exe"), //1.txt中数据流名字为DataStream.exe(随意取的)  
  4.                                 GENERIC_WRITE,  
  5.                                 0,  
  6.                                 NULL,  
  7.                                 CREATE_ALWAYS,  
  8.                                 FILE_ATTRIBUTE_NORMAL,  
  9.                                 NULL);  
  10.     if (hfile == INVALID_HANDLE_VALUE)  
  11.     {  
  12.         return ; // 打开文件错误  
  13.     }  
  14.     HANDLE hExeFile = CreateFile(   TEXT("hack.exe"),   
  15.                                     GENERIC_READ,  
  16.                                     0,  
  17.                                     NULL,  
  18.                                     OPEN_EXISTING,  
  19.                                     FILE_ATTRIBUTE_NORMAL,  
  20.                                     NULL);  
  21.     if (hExeFile == INVALID_HANDLE_VALUE)  
  22.     {  
  23.         CloseHandle(hfile);  
  24.         return ; // 打开文件错误  
  25.     }  
  26.     DWORD dwsize = GetFileSize(hExeFile, NULL);  
  27.     BYTE* buf = new BYTE[dwsize];  
  28.     DWORD wbytes;  
  29.     ReadFile(hExeFile, buf, dwsize, &wbytes, NULL);  
  30.     WriteFile(hfile, buf, wbytes, &wbytes, NULL);  
  31.     CloseHandle(hExeFile);  
  32.     CloseHandle(hfile);  
  33. }  

一般是病毒作者将这个 1.txt 添加到压缩文件(高级里面选上保存文件流数据), 然后搞成自解压包, 在解压后命令中写入1.txt: DataStream.exe, 在用户双击解压的时候就会运行里面的数据流程序。不过现在杀毒软件对这种数据流病毒基本都能杀了。

      再说数据恢复的关键点:数据内容由NONRESIDENT_ATTRIBUTE. RunArrayOffset偏移指定DATA数据地址。如果属性头 ATTRIBUTE.Nonresident标记为1表示非常驻,则下面会解析怎么需要解析DATA部分, 如果为0则表示DATA就是文件内容, 比如一个txt文件里面记录的数据很少, 则此标记为0, 记事本里面数据就保存在DATA中。上图因为查看的是MFT自身,$MFT比较特殊,非常驻属性设置成0(即FALSE)但是却要像非常驻属性一样去分析。

         上图蓝色的部分, 看不太清数据,原始数据如下:
NTFS文件系统数据恢复----解析MFT表_第6张图片

        最开始的数据为32,其中高4bits表示数据起始簇地址占用字节数,后4bits为数据大小(簇个数)占用字节数..这里要特别注意,第一个起始簇信息是无符号的,后面第二个开始就是相对于前面一个簇的偏移,是有正负的,查了很多资料,这点基本上都没提及,这也是数据恢复最容易出错的地方,辛辛苦苦写好了程序,一测试发现恢复出来的数据有问题。

NTFS文件系统数据恢复----解析MFT表_第7张图片

        上面第一个扇区地址为52604144(0x64559E*8,相对去当前分区的扇区偏移),第二个扇区地址为(0x64559E - 0x 77)*8  =  6575399 扇区(这里值0x89为-119)。

       恢复数据的时候可以根据簇大小读取文件,然后保存,再根据前面的NONRESIDENT_ATTRIBUTE. DataSize值去SetFilePointer设置文件大小继而调用SetEndOfFile

        定义一个结构体表示这个结构:

[cpp]  view plain copy
  1. typedef struct _VCN_FLASH  
  2. {  
  3.     uchar VcnLen:4;        // 簇流长度 *8*512 才是得到的文件字节数  
  4.     uchar StartVcnLen:4;   // 簇流起始位置--簇号  
  5.   
  6.     uchar Data[1];         // 簇流长度&Data + 簇流起始位置&Data+VcnLen 数据部分  
  7. }VCN_FLASH, *PVCN_FLASH;  
再定义2个函数计算有符号与无符号数:

[cpp]  view plain copy
  1. uint64 make_uint64(uchar* buf, int lenth)  
  2. {  
  3.     int64 ui=0;  
  4.     if (lenth > 8)  
  5.     {  
  6.         return (int64)0;  
  7.     }  
  8.       
  9.     for (int i=0; i
  10.     {  
  11.         ui = (buf[i]<<8*i)|ui;  
  12.     }  
  13.     return ui;  
  14. }  
  15.   
  16.   
  17. int64 make_int64(uchar* buf, int lenth)  
  18. {  
  19.     int64 ui=0;  
  20.     if (lenth > 8)  
  21.     {  
  22.         return (int64)0;  
  23.     }  
  24.   
  25.     for (int i=0; i
  26.     {  
  27.         ui = (buf[i]<<8*i)|ui;  
  28.     }  
  29.   
  30.     // 判断符号位,为负则需减取反  
  31.     if (buf[lenth-1] >= 0x80)  
  32.     {  
  33.         int64 xorval = 0;  
  34.         for (i=0; i
  35.         {  
  36.             xorval = xorval|(0xFF<<8*i);  
  37.         }  
  38.         ui = -((ui - 1)^xorval);  
  39.     }  
  40.     return ui;  
  41. }  

         0x90  AttributeIndexRoot  属性 ( 目录索引B+树结构 )

        这个属性,我也不是很清楚,等弄清楚了,再接着完善.................


你可能感兴趣的:(数据恢复)