EXT4 二进制文档解析

时间:2024-03-30 09:55:46

说明:本文档通过研究ext4文件系统在flash上的摆放格式,做到对一个ext4 file system的二进制档,分析出包含哪些文件,每个文件的名字,大小和内容。如有不对的地方,还望指正。本文主要参考了《数据重现-文件系统原理精解与数据恢复最佳实践》(马林 著)中关于ExtX文件系统章节(第五章)

EXT4文件系统架构:
EXT4 二进制文档解析

说明:EXT4文件系统中只有0号块组的超级块和块组描述符表的位置是固定的,其他都不固定。其中,超级块总是开始于偏移位置1024(字节),占据1024个字节,块组描述符表在紧跟超级块后面的一个块(中间是否有空闲填充要根据块大小),块组描述符表整体占用的大小是不定。

不同块大小时详细布局结构有差异,下图为块大小为1024字节时结构:
EXT4 二进制文档解析
我手头的板子上mmcblk0p3分区已经被格式化为ext4格式,挂载发现里面没有内容,为了验证追踪到了正确数据,先对其创建了文件和子目录并写入了内容,接下来我将对该分区的二进制档分析出里面的文件是否是写入的内容,以验证方法是否正确。
一个ext4格式的文件系统(/dev/block/mmcblk0p3)被挂载在/usr/local/etc目录,查看如下
EXT4 二进制文档解析
接下来将通过读取元数据一步一步得到文件所在数据块:

1、 查看超级块,找到0号块组起始块号、块大小、每块组所含块数、每块组i节点数、第一个非保留i节点、每个i节点大小。
EXT4 二进制文档解析
超级块的数据结构如上图所示,下面分析会参照该表:
命令:hexdump -s 1024 -n 1024 -C /dev/block/mmcblk0p3

首先可以看到0x38-0x39是EXT系列文件系统的签名标志:“53 ef”
0x14-0x17是0号块组起始块号:0x01,说明超级块前面有一个块为保留块,用来存储引导程序。
0x18-0x1b是块大小:0x00,这里的值指的是将1024字节左移的位数,移动0位也就是1024字节,移动一位相当于乘以2,就是2048字节。
0x20-0x23是每块组所含块数:0x2000(十进制8192)
0x28-0x2b是每块组所含i节点数:0x07e8(十进制2024)
0x54-0x57是第一个非保留i节点号:0x0b(11),一般为lost+found目录
0x58-0x59是每个i节点结构的大小:0x80(十进制128),即每个i节点表项占128字节。
EXT4 二进制文档解析

2、查看块组描述符表,找到块位图块、i节点位图块、i节点表起始块号、块组目录数。
EXT4 二进制文档解析
说明:由读取的超级块知块大小为1024字节,因此块组描述符表起始2号块,每个块组描述符表项占32字节,结构如上图所示。
命令:hexdump -s 2048 -n 1024 -C /dev/block/mmcblk0p3 结果如下图:
块组描述符表中每个块组使用32个字节来描述,因此第一个32字节描述的就是0号块组。
0x00-0x04是块位图块起始块号:0x0104
0x05-0x07是i节点位图块起始块号:0x0114
0x08-0x0b是i节点表起始块号:0x0124
0x10-0x11是该块组的目录数:0x02
这里获取的起始块号是逻辑块号(将文件系统所有的块从0开始递增编号),因此在计算偏移量时可以直接乘以每块字节数(0x400,也就是十进制的1024)

注:块位图块, 块起始块和i节点表在没有使用Flexible 块组时是连续的,这里相差16说明一个Flexible 块组由16个块组构成。
EXT4 二进制文档解析
在这里我们知道了i节点表的起始块号,根目录的i节点号为2,下面将从根目录出发,2号i节点位于0号块组中,0号块组中i节点表的起始块号为0x0124,2号i节点在节点表内的偏移为0x80。

3、找出根目录下的子目录和文件
  第一步我们提到了第一个非保留i节点号为11,那么前面的10个保留i节点的作用是什么呢(i节点号从1开始编号),这里只说明2号节点是存储的是根目录i节点号,因此我们读取i节点表的2号表项值就可以找到根目录所在块号了。
  计算i节点表项的偏移量涉及到了块组描述符表中的i节点表起始块号:0x0124。
  某i节点表项起始字节=i节点起始块号每块所占字节数+(该i节点号-1)每个i节点表项所占字节数
  0x0124*0x400+(0x02-0x01)*0x80=0x49080 注:因为节点号从1起所以减1
下面就可以读取根目录i节点表项值了。
命令:hexdump -s 0x49080 -n 128 -C /dev/block/mmcblk0p3
EXT4 二进制文档解析

从上图i节点表项中相对偏移为0x28-0x63为0x080000表明使用了extent结构。Extent是在ext4中引入的,在这之前在ext3中使用的是12个直接块指针,1个一级间接块指针,1个二级间接块指针,1个三级间接块指针。


下面介绍一下extent的结构:
每个extent结构占用12个字节,所以每个i节点表项中可以有60/12=5个extent结构,这其中第一个extent作为extent头(extent header是一个B+树的描述头),剩下的4个是extent体(extent body,由于extent树中的节点有两种:索引节点和叶子节点,因此当节点为索引节点时(可以从extent header中区分索引节点和叶子节点)extent body存储的是下一级extent树节点信息,当节点为叶子节点时extent body 中存储的是数据块块号信息)
extent树结构
EXT4 二进制文档解析

extent数据结构
extent header
EXT4 二进制文档解析
这里要说明的是:魔数是一个校验值,只有当校验结果是0xf30a时B+树的块才正确
  节点在extent树中的深度是从叶子节点算起,因此根节点的深度是最深的(看到有人说根节点的最大深度不超过5),当深度为0时表明是叶子节点,那么这个节点就是数据节点,他后面的extent body就是指向的数据块,存储数据块号;当深度大于0,后面的extent body表示索引节点。
extent body
当为索引节点时
EXT4 二进制文档解析
当为叶子节点时
EXT4 二进制文档解析
补充说明:当extent body表示索引节点时最后最后两个节点冗余是为了迁就叶子节点。


extent头中说明的信息有:本区段有一个extent body,最大区段个数为4,本段在extent树中深度为0,是叶子节点。
后面紧着的12个字节是extent body说明的信息有:本区段的第一个块号是0,本区段含有1个块,本区段指向的数据块块号为0x10f4。

该块的偏移字节:0x43d000 —根目录文件数据块的偏移字节
命令:hexdump –s 0x43d000 –n 1024 –C /dev/block/mmcblk0p3
EXT4 二进制文档解析
EXT4 二进制文档解析
注:上面中的test_rootdir.txt由test.txt 改名而来
EXT4 二进制文档解析
对于test_rootdir.txt文件
该文件内容所在的i节点号:0x0d
找到该i节点号对应的块组,再由对应块组描述符得到对应的i节点表,注意i节点表表内的偏移。
0x0D在0 号块组中,前面看了0 号块组的块组描述符,可以得到0 号块组对应的i节点表的物理起始块号为0x124,
该i节点表项的偏移字节为:0x124*0x400 + (0xd – 0x1)*0x80 =0x49600
基本i节点的数据结构这里给出(注:在ext4中偏移为0x58-0x63稍有出入,前面已经提到)
EXT4 二进制文档解析
EXT4 二进制文档解析
EXT4 二进制文档解析
0x04-0x07知test_rootdir.txt文件大小为0x2c个字节
由0x20-0x23可知仍然使用了extent结构,参照前面的方法:
得到该文件数据所在block号为0x2105,对应的字节偏移量为0x2105 * 0x400 = 0x841400
读取该偏移字节处开始的1024字节内容,读取文件内容:
hexdump -s 0x841400 -n 1024 -C /dev/block/mmcblk0p3
EXT4 二进制文档解析

4、从根目录项中得到子目录tmp_test节点号,继续分析tmp_test子目录


对于根目录中tmp_test子目录
0x2c-0x2f是该文件内容所在i节点号:0x7e81
0x30-0x31 是本目录项的长度:0x20(32字节)
0x32 是本目录项名字长度0x08(8个字节)
0x33 是本文件类型 0x02(表示目录)
0x34开始连续的8个字节为文件名的ASCCI码


查看i节点号为0x7e81的i节点表项,计算方法同前面
第三步中我们找到指向子目录的i节点号为0x7e81,也是逻辑i节点号,因此我们要先找到0x7e81在哪个块组中:
  某i节点所在块组=该i节点号/每块组i节点个数
  0x7e81/0x07e8=0x10(十进制16)
  某i节点所在i节点表号=该i节点号%每块组i节点个数
  0x7e81%0x07e8=0x01
因此0x7e81在16号块组的i节点表中,在该i节点表的1号表项中。
接下来我们要从块组描述符表中找到16号块组的i节点表起始块号,以便找到子目录所在块号。
  某块组在块组描述符表中偏移字节=块组描述符表起始字节+块组号*每块组描述符表项字节数
  2048+16*32=2560字节
我们读取2560偏移字节开始的32字节:
EXT4 二进制文档解析
可以看到:0x08-0x0b为i节点表起始块号:0x020021
读取16 号块组i节点表的1 号i节点表项值:
命令:hexdump -s 0x8008400 -n 128 -C /dev/block/mmcblk0p3
EXT4 二进制文档解析

查看子文件夹tmp_test中内容
命令:hexdump -s 0x83fc400 -n 1024 -C /dev/block/mmcblk0p3
EXT4 二进制文档解析

得到tmp_testdir.txt文件的i节点号为0x0c,在0 号块组中,i节点表的起始块号为0x0124
0x0124*0x400 + (0x0c – 0x01)*0x80 = 0x49580
注:test.txt和test_tmpdir.txt为被删除的文件

查看i节点号为0x0c的表项内容:
命令:hexdump -s 0x49580 -n 128 -C /dev/block/mmcblk0p3
EXT4 二进制文档解析
可以得知tmp_testdir.txt文件大小为:0x30个字节,内容按照前面的分析:

命令:hexdump -s 0x841000 -n 1024 -C /dev/block/mmcblk0p3
EXT4 二进制文档解析

到此对根目录下的文件和子目录下的文件都追踪到了。