昨天追踪EXT4文件系统的过程中出了点问题,就是找不到文件,于是试了一下追踪FAT32文件系统的,成功之后有了点信心,今天继续嗑EXT4文件系统,终于找到啦,记录一下。
- 操作系统:linux(centos 6.5)
- 文件系统:EXT4
- 工具:hexdump,windows自带计算器
- 参考书目:《数据重现-文件系统原理精解与数据恢复最佳实践》(马林 著),题为《Ext4文件系统架构分析》的系列博客,不知道原作是谁了。
EXT4文件系统架构:
补充说明:EXT4文件系统中只有0号块组的超级块和块组描述符表的位置是固定的,其他都不固定。其中,超级块总是开始于偏移位置1024(字节),占据1024个字节,块组描述符表紧随超级块后面,占用的大小是不定。
步骤:
1、查看文件系统基本情况,新建子目录和文件
可以看到挂载在/boot目录下的文件系统类型是EXT4,因此在改目录下新建子目录及文件:
文件内容为:“This test is belong to Boot folder!”文件基本信息如下:
2、查看超级块,找到0号块组起始块号、块大小、每块组所含块数、每块组i节点数、第一个非保留i节点、每个i节点大小。
命令:hexdump -s 1024 -n 1024 -C /dev/sda1
查看结果:
首先可以看到0x38-0x39是EXT系列文件系统的签名标志:“53 ef”
0x14-0x17是0号块组起始块号:0x01,说明超级块前面有一个块为保留块,用来存储引导程序。
0x18-0x1b是块大小:0x00,这里的值指的是将1024字节左移的位数,移动0位也就是1024字节,移动一位相当于乘以2,就是2048字节。
0x20-0x23是每块组所含块数:0x2000(十进制8192)
0x28-0x2b是每块组所含i节点数:0x07f0(十进制2032)
0x54-0x57是第一个非保留i节点号:0x0b(11),一般为lost+found目录
0x58-0x59是每个i节点结构的大小:0x80(十进制128),也就是每个i节点表项占用128个字节。
3、查看块组描述符表,找到块位图块、i节点位图块、i节点表起始块号、块组目录数。
命令:hexdump -s 2048 -n 1024 -C /dev/sda1
查看结果:
块组描述符表中每个块组使用32个字节来描述,因此第一个32字节描述的就是0号块组。
0x00-0x04是块位图块起始块号:0x0104
0x05-0x07是i节点位图块起始块号:0x0114
0x08-0x0b是i节点表起始块号:0x0124
0x10-0x11是该块组的目录数:0x02
这里获取的起始块号是逻辑块号(将文件系统所有的块从0开始递增编号),因此在计算偏移量时可以直接乘以每块字节数(0x400,也就是十进制的1024)
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
下面就可以读取根目录i节点表项值了。
命令:hexdump -s 0x49080 -n 128 -C /dev/sda1
查看结果:
0xa8-0xd7是12个直接块指针,其中四个字节为一个单位,表示一个块号。
图中可以看出根目录只占用了一个块,块号为:0x1104
根目录的起始偏移字节为=根目录所在块号*每块所占字节数
则根目录的起始偏移字节:0x1104*0x400=0x441000
使用命令:hexdump -s 0x441000 -n 1024 -C /dev/sda1 查看根目录内容:
查看/boot目录下的文件:
可以看到两者的内容是相符的,说明我们找的没有错。根目录中BOOTDIR的目录项用黑色底纹标注。
0x6c-0x6f是该文件内容所在i节点号:0x7f01
0x70-0x71是本目录项长度:0x10(16字节)
0x72是本目录项名字长度0x07(7个字节)
0x73是本文件类型:0x02(表示目录)
0x74开始是文件名的ASCCI码:“42 4f 4f 54 44 49 52 00”
在这一步中与FAT32文件系统的区别有两个:
一是怎么寻找根目录。FAT32中根目录在数据区的开头,因此我们可以直接去数据区读取;而EXT4文件系统中,我们需要通过2号i节点表找到根目录所在的块号,才能看到根目录内容,这里就可以看出EXT4文件系统将目录也看作文件了,因为他的读取方式和普通文件是一样的,只不过普通文件需要从目录中得到i节点号,而根目录是一开始就定好了i节点号。
二是目录的大小。FAT32中目录的大小是固定的(短文件名目录占32字节,长文件名目录占多个32字节),所以当文件名过长时,使用了长文件名机制来解决,而EXT4文件系统的目录项大小是在目录项中灵活定的。比如这一步中我们查看到的根目录结果中,开始的12个字节是本目录项,紧接着12个字节是根目录项,而我们要找的目标目录项的长度是16个字节,其中说明部分(i节点号,本目录项长度字节数,名字长度,文件类型)占用都是一样的,差就差在文件名部分。但我们也看到文件名后面总有“00”补齐,这是因为目录项的长度总要是4的倍数,因此不够时会用0补齐。
4、从i节点表中找到子目录所在块号
第三步中我们找到指向子目录的i节点号为0x7f01,也是逻辑i节点号,因此我们要先找到0x7f01在哪个块组中:
某i节点所在块组=该i节点号/每块组i节点个数
0x7f01/0x7f0=0x10(十进制16)
某i节点所在i节点表号=该i节点号%每块组i节点个数
0x7f01%0x7f0=0x01
因此0x7f01在16号块组的i节点表中,在改i节点表的1号表项中。
接下来我们要从块组描述符表中找到16号块组的i节点表起始块号,以便找到子目录所在块号。
某块组在块组描述符表中偏移字节=块组描述符表起始字节+块组号*每块组描述符表项字节数
2048+16*32=2560字节
我们读取2560偏移字节开始的32字节:
可以看到:0x08-0x0b为i节点表起始块号:0x020021
读取16号块组i节点表的1号i节点表项值:
从0x28-0x57是12个直接块指针,以4个字节为单位读取,其中有的指向超级块(0x01)。0x21002指向的块存储的是子目录内容。
该块的偏移字节:0x8400800
5、从子目录对应的i节点号得到目标文件块号
查看结果第4步中子目录块内容:
加黑色底纹的是本目录(“.”)和根目录(“..”)接下来就是目标文件:BOOTTEXT.txt,他的i节点号为:0x7f04
查看i节点表,找到目标文件的块号:
改i节点表项的偏移字节为:0x020021*0x400+(0x7f04%0x7f0-1)*0x80=0x8008580
读取该偏移字节处开始的128字节内容:
得到的目标文件所在块号为0x024005(偏移字节为0x9001400),接下来读取该块内容:
找到啦!和第一步中使用cat命令查看的文件内容一致。