本来的需求是XEN下的镜像取证,但这篇仅包括他支持的一种格式,就是VHD,此项目从头开始大概用了两周时间,中间遇到了很多让人头大的问题,光是思考的笔记就写了十几页纸,不过实际上并没有那么难,主要是很久没编码了,还有很多概念没搞清楚。好吧,搬家过来的第一个博客就从这个项目开始吧。

要求:

1、解析vhd格式文件,判断合法性

2、该vhd装的文件系统是NTFS格式

3、拿到该格式下的目录结构,即包含哪些文件和目录。

4、跨平台

思路:

一、vhd格式解析

解析首先要弄懂数据结构,网上关于他的官方格式说明是找不到的,但是XEN竟然能支持vhd格式肯定能有相应的数据结构,所以去搜索,xen的源码,果然里面就含有一个vhd.h,格式旁边还有注释,非常完美。此外,网上还找到了一篇《storage_layout系列之VHD结构详解》,是北亚数据的创始人写的,也很详细。

这个格式主要就是一个位于文件尾部512字节的数据结构:

// VHD Footer structure
typedef struct {u_char        cookie[MT_CKS];    // Cookieu_int32_t    features;    // Featuresu_int32_t    ffversion;    // File format versionu_int64_t    dataoffset;    // Data offsetu_int32_t    timestamp;    // Timestampu_int32_t    creatorapp;    // Creator applicationu_int32_t    creatorver;    // Creator versionu_int32_t    creatorhos;    // Creator host OSu_int64_t    origsize;    // Original sizeu_int64_t    currsize;    // Current sizeu_int32_t    diskgeom;    // Disk geometryu_int32_t    disktype;    // Disk typeu_int32_t    checksum;    // Checksumu_char        uniqueid[16];    // Unique IDu_char        savedst;    // Saved stateu_char        reserved[427];    // Reserved
}__attribute__((__packed__)) vhd_footer_t;

【lseek可能遇到的问题 】

由于vhd若装的是windows 7那么很可能就是5G以上,我测试的是一个11G的,但是lseek参数是ULONG只能支持到32位即4GB空间,超过4GB会报错,所以定位取尾部一个扇区的数据结构时,要用SEEK_END。当然之后还会出现类似的指针地址太大问题,解决方法有俩:

 1、posix标准下,可以用在Makefile里面写上
CFLAGS +=  -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES
CXXFLAGS +=  -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES

别的不用动就能支持了。

2、一个比较生硬的转换法:

 

int  count= 要lseek的数字/(65535*65535);
for( int i = 0;i<count;i++ )
lseek( 65535*65535 );
lseek( 要lseek的数字%(65535*65535) )

未测试成功性。

通过disktype能判断是静态vhd磁盘还是动态vhd磁盘,这里只考虑动态vhd磁盘,通过dataoffset就能跳到动态磁盘头部,动态vhd磁盘数据结构如下:

// VHD Dynamic Disk Header structure
typedef struct {u_char        cookie[MT_CKS];    // Cookieu_int64_t    dataoffset;    // Data offsetu_int64_t    tableoffset;    // Table offsetu_int32_t    headerversion;    // Header versionu_int32_t    maxtabentries;    // Max table entriesu_int32_t    blocksize;    // Block sizeu_int32_t    checksum;    // Checksumu_char        parentuuid[16];    // Parent Unique IDu_int32_t    parentts;    // Parent Timestampu_char        reserved1[4];    // Reservedu_char        parentname[512];// Parent Unicode Nameu_char        parentloc1[24];    // Parent Locator Entry 1u_char        parentloc2[24];    // Parent Locator Entry 2u_char        parentloc3[24];    // Parent Locator Entry 3u_char        parentloc4[24];    // Parent Locator Entry 4u_char        parentloc5[24];    // Parent Locator Entry 5u_char        parentloc6[24];    // Parent Locator Entry 6u_char        parentloc7[24];    // Parent Locator Entry 7u_char        parentloc8[24];    // Parent Locator Entry 8u_char        reserved2[256];    // Reserved
}__attribute__((__packed__)) vhd_ddhdr_t;

动态vhd格式的整体布局如下:

刚才拿到了尾部扇区,头部的0扇区是对他的备份,接着利用头部扇区再定位到BAT。BAT就是块地址表,他的数组中的每个值对应一个块地址:

定位方法就是batmap[i]*扇区大小

取数据我们只需要关注块数据的位置即可,上图的BAT的y0 y1……代表的就是块的首地址,跳到y0首地址,这里是一个512B+2MB的结构,前512字节(4096位)说明后面4096个扇区(2MB)的数据的分配情况,我们不需要管分配情况,只需要定位即可,所以我们每次定位要跳过512字节。

定位思路就是先算出块的位置,再算出块内扇区位置,非常简单,最终得出的地址转换函数如下:

/*
* 功能:将磁盘地址转到vhd地址位置获取到对应扇区信息
* offset偏移扇区数,size读取长度
*/
u_char * Address2VHD(u_int64_t offset,int size)
{int blk_index = offset/(blk_size / MT_SECS);int sc_index = offset % (blk_size / MT_SECS);u_char sc[MT_SECS*size];int lk = lseek(vhdfd,SwapInt32(batmap[blk_index])*(u_int32_t)MT_SECS + 512 + sc_index*(u_int32_t)MT_SECS,SEEK_SET);if(lk < 0 ){perror("lseek");}else{read(vhdfd,sc,MT_SECS*size);return sc;}
}

【块设备的问题】

I/O设备分为块设备与字符设备,磁盘是块设备,他与内存交互的单位就是块,块都有自己地址,一个块一般含1到64个扇区,但是虚拟磁盘的块有2MB这么大,也就是4096个扇区,而且每块前面都有一个位图,形式不太一样,不能当一般磁盘设备来看待,毕竟是虚拟的磁盘,不必大惊小怪。

此外是文件系统存储的最小单位,再高级格式化时能设置,与块没什么直接关系,不要搞乱了。

二、找到第一个扇区MBR并解析

有了地址转换函数再找MBR主引导扇区就容易多了,但首先要做的是定义MBR的格式。MBR的数据结构网上非常容易找,我这里主要参考的《数据重现 文件系统原理精解与数据恢复最佳实践》这本书,讲解得非常详细。

MBR的数据结构如下:

#pragma pack(1)/*一个分区表的表头信息*/
typedef struct DPT_Header
{u_char State;   //分区状态, 0 = 未激活,0x80 = 激活u_char StartHead;  //分区起始磁头号u_int16_t StartSC;  //分区起始扇区和柱面号,低字节(低6位为扇区号,高2位为柱面号的第9,10位),高字节(柱面号的低8位),考虑到字节序问题可能不一样u_char Type;   //分区类型,00 表示此项未用,其他标号代表各种系统u_char EndHead;  //分区结束磁头号u_int16_t EndSC;   //分区结束扇区和柱面号,定义同前u_int32_t Relative;  //在线性寻址方式下的分区相对扇区地址(对于基本分区即为绝对地址),从0开始的扇区数u_int32_t Sectors;  //分区大小 (总扇区数)
} DPT_Header;//引导区512u_char结构
typedef struct _MBR_SECTOR
{u_char BootCode[440];//启动记录440 u_charu_int32_t DiskSignature;//磁盘签名 4字节//u_int16_t blank1;u_int16_t NoneDisk;//二个字节DPT_Header Partition[4];//分区表结构64 u_charu_int16_t Signature;//结束标志2 u_char 55 AA//u_int16_t blank2;
} MBR_SECTOR, *PMBR_SECTOR;
#pragma pack()


分区中的Type类型可以定义各种操作系统,对应编码表:

【字节对齐问题】

由于读取的时候我们会是一个个字节读进来,结构体默认的字节对齐(一般是4字节对齐,也就每个类型总在4字节内,或者恰好是4的整数倍,否则就填充)并且sizeof()函数会按字节对齐后所消耗的真正空间。这样在后期运算会带来很大麻烦,所以这里加上#pragma pack(1) #pragma pack()规定就使用1字节对齐,就不会出现问题了。

取到了MBR,其中有4个分区表结构DPT_Header(因为NTFS最多支持4个主分区,但是动态磁盘支持更多,这里暂时不考虑动态磁盘),DPT_Header中有个起始扇区号,这就是每个扇区的起始扇区,那么就一个一个跳过去查看。

【显示空间大小问题】

由于空间时不时是上百G,那么在将扇区数转成GB或MB显示的时候要十分小心,例如一般是939393*每扇区字节数/1024/1024,若是int,那么第一个乘法就溢出了,所以应该把乘法放后面。

【malloc及传参问题】

这是基础内容了,传参的时候func(uchar**)必须是双重指针,否则在内部malloc后只是值改变,而指针本身没有变化。

 

三、找到MFT表的具体位置

在ntfs中,MFT表记录了整个文件系统的布局,每个mft都会记录一个文件的详细信息,包括创建时间、修改时间、作者、文件名、目录名等等。整个表一般占全系统的12.5%,当然他还可以往上配置得更大一点,一个文件被删除,也只是更改里面的属性而已,若不被重新分配,通过mft表就能很快恢复过来,他一般集中在文件系统的某个特定位置,现在我们的目标就是找到他。

《数据重现 文件系统原理精解与数据恢复最佳实践》这本书详细介绍了mft表的内容这里不再赘述。

这里开始就要引入ntfs.h了,里头有一个PACKED_BOOT_SECTOR结构,在分区表的其实扇区偏移地址就是指向这里,所以第一步就是跳到这里。

typedef struct _PACKED_BOOT_SECTOR {UCHAR Jump[3];                                                  //  offset = 0x000UCHAR Oem[8];                                                   //  offset = 0x003BIOS_PARAMETER_BLOCK PackedBpb;                          //  offset = 0x00BUCHAR Unused[4];                                                //  offset = 0x024LONGLONG NumberSectors;                                         //  offset = 0x028LCN MftStartLcn;                                                //  offset = 0x030LCN Mft2StartLcn;                                               //  offset = 0x038CHAR ClustersPerFileRecordSegment;                              //  offset = 0x040UCHAR Reserved0[3];CHAR DefaultClustersPerIndexAllocationBuffer;                   //  offset = 0x044UCHAR Reserved1[3];LONGLONG SerialNumber;                                          //  offset = 0x048ULONG Checksum;                                                 //  offset = 0x050UCHAR BootStrap[0x200-0x044];                                   //  offset = 0x054

} PACKED_BOOT_SECTOR;   

拿到这个数据结构,里面又有很多的内容,其中有一个是MftStartLcn,这就是Mft表的起始簇。一个簇有多大呢?这里有个数据结构BIOS_PARAMETER_BLOCK,就是上面的0x00B,他的定义如下:

typedef struct BIOS_PARAMETER_BLOCK {USHORT BytesPerSector;UCHAR  SectorsPerCluster;USHORT ReservedSectors;UCHAR  Fats;USHORT RootEntries;USHORT Sectors;UCHAR  Media;USHORT SectorsPerFat;USHORT SectorsPerTrack;USHORT Heads;ULONG  HiddenSectors;ULONG  LargeSectors;} BIOS_PARAMETER_BLOCK;

显然这个结构里包含了,每个簇的扇区数,每个扇区的字节数等,通过这些指标我们就能定位mft里了。

实际上,网上能搜到一个toysntfs的程序,他采用符号链接和MBR的两种方法对MFT的表解析并读取根目录内容(只针对本地磁盘而不是针对虚拟机的),但是下下来后竟然发现后一种方法是不成功的,虽然传入的参数完全一样,但是最终read得到的结果却不同,再苦闷中分析了两天,才发现,原来是对WINAPI的操作理解不太到位。这个起始簇是相对于分区的,也就是说这个分区的第一个簇是0,下一个分区的第一个簇也是0,所以绝对的地址必须在前面加上一个起始扇区的偏移量,否则定位错误,经修改,运行正常。

定位到MFT就用下面的公式:

NtfsData.MftStartLcn.QuadPart * NtfsData.SectorsPerCluster

四、MFT的解析及遍历

MFT(主文件表也可以称为文件记录块)本身也是一个文件,他占2个扇区,文件系统的前16个MFT是元文件,包括用于文件定位和恢复的数据结构、引导程序数据以及整个卷的分配位图等信息,用户是不能访问的,这里列出前5个:

0    $MFT    //mft本身
  1  $MftMirr    //mft 元数据文件的镜像,用于备份恢复
  2  $LogFile    //文件操作历史记录文件
  3  $Volume    //文件卷信息文件
  4  $AttrDef    //属性定义文件
  5  $Root(\)    //根目录文件

取根目录的扇区位置即:

ulStartSector.QuadPart = NtfsData.MftStartLcn.QuadPart * NtfsData.SectorsPerCluster + ulStartMft.QuadPart*2;

获取目录结构的方法有两个:

1、从$root开始递归遍历,首先查看0x16~17偏移看是否是目录,是的话通过文件记录号跳转递归查询:

mft记录头的结构如下:

后面紧跟着各种属性:

属性分析的框架:

ReadSector( MFT索引号, 长度)
{
  解析MFT记录头;(大小0x38字节)
  解析属性部分
  {
    Switch(属性类型)
    {
      Case: 对不同的属性做处处理,读取或修改,break;
      Default:
    }
  }

我们要的主要是文件名

2、遍历所有的MFT,一般来说MFT是连续的,但也有不连续的可能,主要是$data属性超出的情况,但是不是做文件恢复这里影响不大,只要读取filename即可,另一种说法是遍历MFT在磁盘上分布的区域大小,计算出总的MFT记录数量,然后发送控制码FSCTL_GET_NTFS_FILE_RECORD来获取文件引用号。这个方法不知道对虚拟机镜像可不可行,未测试。

下面这篇文章对解析有较多内容的阐述:

http://www.cnblogs.com/guanlaiy/archive/2013/02/24/2924089.html

主要参考:

《storage_layout系列之VHD结构详解》

《XEN虚拟机分析》 薛海峰,卿斯汉,张焕国

《数据重现 文件系统原理精解与数据恢复最佳实践》

《数据恢复技术》

http://www.cnblogs.com/guanlaiy/archive/2013/02/24/2924132.html

http://www.cnblogs.com/guanlaiy/archive/2013/02/24/2924089.html

转载于:https://www.cnblogs.com/fafaly/p/3915836.html

虚拟机VHD格式解析到NTFS文件系统解析相关推荐

  1. Tuxera NTFS教程:在Mac上如何将MS-DOS文件系统格式化为NTFS文件系统?

    咋一看标题,小伙伴一定会感到困惑,MacOS怎么能够将MS-DOS文件系统格式化为NTFS文件系统呢?用过Mac的小伙伴一定也知道,MacOS磁盘工具是可以将磁盘格式化为MS-DOS(FAT).MS- ...

  2. NTFS文件系统详解(三)NTFS元文件解析

    NTFS文件系统详解(三)NTFS元文件解析 一. 分析$Boot文件 二.分析文件记录 1. MFT偏移地址计算 2. 文件记录的结构 3. 属性的属性头分析 4. 属性的属性体分析 NTFS文件系 ...

  3. 大容量nc文件解析_分布式文件系统浅谈

    最近接触分布式系统开发,从头开始熟悉分布式文件系统开发,整理了下相关资料(我是互联网搬运工,资料都是网上大神的精华萃取,如有侵权联系我删除),适合刚开始入门了解阶段. 1. 概述 1. 存储系统 存储 ...

  4. Xml 格式数据的生成和解析

    相关阅读 XML约束 Xml 格式数据的生成和解析 XML解析器 什么是XML XML全称为Extensible Markup Language, 意思是可扩展的标记语言,它是 SGML(标准通用标记 ...

  5. mac镜像cdr格式_eps是什么格式怎么打开?全面解析图片的eps是什么格式

    eps是什么格式怎么打开?全面解析图片的eps是什么格式 eps是什么格式怎么打开?对于平面设计师来说eps格式文件相信肯定不会陌生,但是对于刚接触软件或者设计爱好者们来说,可能并不怎么熟悉和了解这个 ...

  6. AVI音视频封装格式学习(三)——AVI 数据结构解析

    这里介绍AVI会使用到的数据结构,为了避免翻译引入歧义,决定该部分还是使用英文原文,如后续有时间再进行翻译. AVIMAINHEADER structure The AVIMAINHEADER str ...

  7. 社区说|Android 13 新特性 EROFS-只读文件系统解析

    活动时间 7 月 28 日(本周四) 20:00 - 21:00 活动日程 20:00 - 20:45 主题分享 Android 13新特性 EROFS-只读文件系统解析 介绍 Android 13的 ...

  8. AAC音频格式详解和实战解析

    AAC音频格式详解和实战解析 一.基本概念 AAC:即MPEG-2 Advanced Audio Coding,分为流格式和文件格式.文件格式主要用于文件存储和文件播放,流格式主要用于流媒体在线播放. ...

  9. sql解析json格式字段、sql关联json格式字段,mysql解析json、sql解析json字符串

    sql解析json格式字段.sql关联json格式字段,mysql解析json.sql解析json字符串 sql解析字符串 sql关联json中的某个字段 sql解析字符串 表名user_login ...

最新文章

  1. flask部署机器学习_如何开发端到端机器学习项目并使用Flask将其部署到Heroku
  2. Spark详解(一):Spark及其生态圈概述
  3. Implementing Synchronization Operations
  4. JVM_06 垃圾收集器[ 三 ]
  5. Linux 和 Windows 平台不同的 UCS-2 编码
  6. java完全解耦_java-完全解耦 - osc_bc7dotjc的个人空间 - OSCHINA - 中文开源技术交流社区...
  7. 关于C语言逻辑值的说法错误的是( ),2017年计算机二级c语言题库及答案
  8. 博弈论(一):Nim游戏
  9. VC2010 运行时闪退
  10. 在图像中剪切圆形图片
  11. NetWare网络操作系统
  12. wamp5数据库密码修改
  13. 光环:研发云搭建及人才梯队建设——姚冬
  14. python编程<十五>
  15. Sql执行平时都很快但是偶尔就会很慢
  16. 什么是OTDR光时域反射仪,以及其基本工作原理
  17. Hadoop启停服务命令大全
  18. python3比较两个excel表头的异同列
  19. 购物以及退换流程图是什么?分享购物及退换货流程图模板
  20. 关于电影制作方和观众之间的思考

热门文章

  1. 国际化 i18n ———— 国家语言代码
  2. 摄影构图的基本要领!
  3. css中伪类和伪元素有什么不一样
  4. 重学 PyTorch 第四天:Module 和 Optimizer
  5. mysql查询某学期开设的课程_查询KCXX表中2、3、4学期开设的课程情况
  6. 中国红霉素市场深度分析与投资前景调研报告2022-2028年
  7. SAP自动检验批应用中检验开始日期和检验结束日期的产生逻辑
  8. Apple公司的官方声明
  9. 机器学习:分类模型的评估精确率Presicion和召回率Recall
  10. 亲身历时两个月,这可能是一篇最全面的2021大厂技术岗实习面经