EXT4文件系统下的文件查找和删除

作者 Lao Yuan, 发布于 Lao-Yuan's Blog , 禁止转载.

这是软件安全课程的文件系统实验记录,主要是了解了解一个文件系统的结构.于是选择Linux下的ext4做一些实验.主要参考内容是网上一些零碎的博客和ext4 layout,后者帮助很大.

目前中文资料比较零碎,少有从根目录开始查找一个文件的实际操作,本文目的是去找一个存在于多级目录中的文件,并查看文件的组成方式

实验内容

  1. 在自己终端上,选定一个100m左右的任意文件,如何根据文件路径找到文件管理信息(簇号,文件索引)和文件内容.要求给出寻找的过程,截图,已经原因.
  2. 如何安全删除该文件?
  3. 该文件删除后,该如何恢复它?
  4. 对该文件设置访问权限,该设置会修改该文件在文件系统中的哪些内容?

实验环境和工具

  • ubuntu16.04

  • ext4文件系统

  • debugfs工具

  • hexdump工具

文件查找实录

目标文件

我们要找的文件路径为

/home/yahweh/largefile

该文件是大小为100M、内容为被填充的0101的序列.

根据ext4 layout给出的信息,ext4文件系统的整体结构如下:

Group 0 Padding ext4 Super Block Group Descriptors Reserved GDT Blocks Data Block Bitmap inode Bitmap inode Table Data Blocks
1024 bytes 1 block many blocks many blocks 1 block 1 block many blocks many more blocks
ext4 standard layout

实际学习中,我们发现group 0 padding和ext super block共同组成了一个block 根据结构,我们首先要找到super block.

ext4 super block

goup 0 padding是一个大小为1024字节全为0的空间,略过,直接查看block group 0 的super block信息.读取超级块:

我的超级块信息

我的超级块信息

一个super block的重要结构如下(表太大,点击查看完整表项):

超级块结构

超级块结构

</a>

得到重要信息如下:

block_size = 0x1000 = 4096

blocks_per_group = 0x8000 = 32786

group_size = block_size * blocks_per_group = 0x8000000

s_inodes_per_group = 0x2000

s_inode_size = 0x100

s_desc_size = 0x0000 即descriptor中未开启64位模式

在0x60偏移处的s_feature_incompat标志中,发现值为0x246,激活了0x200处的flexible group特性,继续查看0x174偏移处的s_log_groups_per_flex值为4,得出flexible group的大小是2^4=16个group的集合,这个特性会在之后的实验中用到,关于flexible group在ext4 layout中的描述如下:

Starting in ext4, there is a new feature called flexible block groups (flex_bg). In a flex_bg, several block groups are tied together as one logical block group; the bitmap spaces and the inode table space in the first block group of the flex_bg are expanded to include the bitmaps and inode tables of all other block groups in the flex_bg. For example, if the flex_bg size is 4, then group 0 will contain (in order) the superblock, group descriptors, data block bitmaps for groups 0-3, inode bitmaps for groups 0-3, inode tables for groups 0-3, and the remaining space in group 0 is for file data. The effect of this is to group the block metadata close together for faster loading, and to enable large files to be continuous on disk. Backup copies of the superblock and group descriptors are always at the beginning of block groups, even if flex_bg is enabled. The number of block groups that make up a flex_bg is given by 2 ^ sb.s_log_groups_per_flex.

group 0的描述符结构

由于一个block的大小为4kb,找到group 0 的descriptor的偏移量为0x1000(第一个块大小),一个descpiptor的大小为32byte:

Group 0 Descriptor

Group 0 Descriptor

一个descriptor的主要结构如下(点击查看完整表项): Descriptor layout

Descriptor layout

根据以上信息,在group descriptor的0x8偏移找到inode table在group内的块偏移量为0x0421,由于一个块的大小为0x1000,则goup0的inode table的实际地址为:0x421000

根目录的inode

要找到根目录的inode,首先要得到inode号,根据layout,ext文件系统中有如下特殊inode号是保留的:

特殊 Inode

特殊 Inode

我们得到,根目录/的inode号是0x02

如何根据incode id找到其所在的块和块内inode偏移呢?ext4 layout文档中信息如下:

The inode table is a linear array of struct ext4_inode. The table is sized to have enough blocks to store at least sb.s_inode_size * sb.s_inodes_per_group bytes. The number of the block group containing an inode can be calculated as (inode_number - 1) / sb.s_inodes_per_group, and the offset into the group’s table is (inode_number - 1) % sb.s_inodes_per_group. There is no inode 0.

由于根目录/的inode号为2,则它在第0的block group,在inodetable中的偏移为:

(2-1)%0x2000[s_inodes_per_group]*0x100[s_inode_size]+0x421000[inode_table]
 = 0x421100

根据以上信息,找到根目录/的inode项:

根目录inode项

根目录inode项

inode table中inode说明了目标文件的除文件名外所有的信息,包括权限\时间\属主等信息,该结构的数据结构如下(请查看完整表项):

inode table layout

inode table layout

</a>

在0x0处找到i_mode = 0x41ed,其中0x4000说明该inode指向一个目录项.

根目录的i_block分析

i_block是inode中存储树形结构或目的块组.

在0x28的偏移处,找到i_blocks表项:

i_blocks

i_blocks

由0xf30a这个magic number可知,该文件系统使用了extent tree的结构.这种结构是ext2/3和ext4之间的最大不同.extent tree大小为28byte,其结构如下,三个表分别是ext4_extent_header(12byte),ext4_extent_idx(12byte),ext4_extent(12byte):

ext4 extend tree

ext4 extend tree

在ext4_extent_header的0x6偏移处,找到depth项,这个项表明这个inode指向一个data block还是一个最多5深度的树.

若depth>0,则header后跟一个idx结构,该结构指向一个类似的i_block块,指向的i_block块是一个树结构或者叶子节点;

则读出depth=0,则header后跟一个extend结构,该结构指向一个数据块.

读出根目录/的inode的depth项为0,说明根目录的inode项指向一个具体的数据块,故其后跟随一个 ext4_extent结构.

在之后的ext4_extent中,我们看到0x0偏移处是该节点在文件数中的顺序,0x4的偏移处是ee_len项,这一项指明了该文件占有的块数,0x6和0x8共同指出该文件的指向的块号.

从根目录的inode中读出,该inode指向的data block号为0x2421.

根目录目录结构分析

由block号0x2421可知其物理地址为:0x2421*0x1000 = 0x2421000,共占据一个块.

读取这个块如下所示:

根目录block

根目录block

已知这个项为一个目录项,且0x0偏移处值为0x02,是根目录的inode号,故参考一个linear directory的结构,该结构几乎是线性的,每个directory entry的结构如下所示:

directory entry

directory entry

在0x02421078的地方找到home的入口,得到其inode号为0x1c0001

/home分析

根据计算,inode号为0x1x0001的项在第(0x1c0001/0x2000 = )E0个group上

由于本文件系统开启了flexible group的特性,那么,则该group的物理地址为:

(((0xE0)/0x10) * 0x10*0x8000[block_per_group] + 16 * (inode_map_size + block_map_size)) * 0x1000[block_size]= 0x700020 * 0x1000 
= 0x700020000

故找到该group的inode_table如下所示:

/home inode_table

/home inode_table

该inode的table内的偏移为:

(0x1c0001 -1) % 0x2000 * 0x100 
= 0x0

由前图所示的inode结构,得知/home的inode指向的是一个目录,且是树的根节点,其指向的地址为0x702020的块,其物理地址为0x702020000,得到下图所示的内容:

/home block

/home block

可以看到该目录下只有一个文件yahweh(user主文件夹),该文件的inode号为1c0002.

/home/yahweh的分析

由/home/yahweh的inode号可知,其inode是其父目录inode的+1项,可知其在inode_table中的偏移量为0x100,因此可在前述inode_table中找到该inode.

由inode表项知这个/home/yahweh的inode项也是一个目录项,其入口块号为0x702021,则实际偏移为0x702021000,找到该block(比较大,不完全):

/home/yahweh block

/home/yahweh block

找到其中名叫largefile的文件,其inode号为0x1cc71e

/home/yahwh/largefile分析

由largefile的inode可知,该文件所在的group为(0x1cc71e/0x2000 = )e5 group

由于启用了超级块特性,该文件和其父目录在同一inode_table下,计算其偏移为:

(0x1cc71e-1) % 0x2000 
= 0xc71d

地址为:

0xc71d * 0x100 + 0x700020000 
= 0x700c91d00

找到该文件inode,如下图所示:

/home/yahwh/largefile inode

/home/yahwh/largefile inode

由inode可知,此inode是一颗二分树,其后接一个idx数据结构,该数据结构指向0x727e0f的extent块,关于extent特性,ext4org描述为:

Traditional, Unix-derived, file systems, like Ext3, use a indirect block mapping scheme to keep track of each block used for the blocks corresponding to the data of a file. This is inefficient for large files, especially during large file delete and truncate operations, because the mapping keeps an entry for every single block, and big files have many blocks -> huge mappings, slow to handle. Modern file systems use a different approach called “extents”. An extent is basically a bunch of contiguous physical blocks. It basically says “The data is in the next n blocks”. For example, a 100 MiB file can be allocated into a single extent of that size, instead of needing to create the indirect mapping for 25600 blocks (4 KiB per block). Huge files are split in several extents. Extents improve the performance and also help to reduce the fragmentation, since an extent encourages continuous layouts on the disk.

找到该块:

/home/yahwh/largefile block

/home/yahwh/largefile block

这个块的inode extend表明自己已经是叶子节点,且偏移量为0x2的地址告诉我们有7个entry,故接下来应是线性放置的entry结构,我们得到了以下7个块地址:

0x877800 	包含block数为0x800
0x87c000 	包含block数为0x800
0x87e800 	包含block数为0x800
0x879000 	包含block数为0x1000
0x87d000 	包含block数为0x1000
0x89b800 	包含block数为0x2000
0x89f000 	包含block数为0xc00

计算总block数: 0x6400

总大小:0x11800 * 0x1000 = 0x11800000 = 100M

打开以上任意一个块:

文件结果 1

文件结果 1

或包含的块:

文件结果 2

文件结果 2

可以看到,这些数据均为0101,是之前写入的largefile.

至此,成功找到该文件.

文件的删除

正常删除

正常情况下,操作系统在删除文件时采用如下策略:

  • a) super block中系统剩余inode数量+1

  • b) group descriptor中组内剩余inode数量 + 1

  • c) 解除文件对应inode 在group inode map中的占用

  • d) 借出文件对应block在group block map 中的占用

  • e) 将目录项中文件信息覆写

安全删除

若要安全删除该文件,在正常删除的基础上,采取以下操作:

  • a) 将inode覆写(防止根据inode找到block和文件相关信息)

  • b) 将文件所占的块全部覆写(防止小文件被攻击者根据文件格式特征找到数据块)

文件恢复

恢复有两种方式:从linux回收站中回收,查找inode节点.

若文件是被用户调用系统命令删除的,则该文件的inode被复制到trash inode_table中,此时inode还未被标记为未使用,可在trash中将该inode复制到原位置.

若文件被从回收站删除,在该inode还没有被使用的情况下,可不参考inode_map和block_map的情况,直接根据原目录项查找相的inode信息,,再根据相邻的inode找到欲恢复的文件的inode,得到block信息,再得到原文件的具体块信息.得到块后,依次恢复目录项、inode_map、block_map、group descriptor、super clock中的信息,使文件系统可再次索引到原文件.

文件权限

linux相关系统中,文件权限的储存策略比较简单,inode包含了文件的所有信息,也包含了文件的权限信息,在inode结构中,0x0处的双字代表了文件的权限,包括针对属主、组、其他的的权限设置,文件的属主、组信息则存储在0x2、0x18处.

参考内容

[1] Ext4 layout

[2] Ext4 wiki