基于VFS的Rootkit
Virtual file systems (VFS) are an abstraction layer to allow easy communication with other filesystems such as ext4, reiser fs, or other special filesystems like procfs. This extra layer translates easy to use VFS functions to their appropriate functions offered by the given filesystem. This allows a developer to interact solely with the VFS and not needing to find, handle, and support the different functions and types of individual filesystems.
为什么VFS可以实现文件系统隐藏:
- First, we can hook a VFS function and deal with that one function to hide information from the concrete filesystem.
- procfs is a supported filesystem
Procfs (Proc filesystem)
The proc filesystem is an interface to easily manage kernel data structures. This includes being able to retrieve and even change data inside the linux kernel at runtime. More importantly, for us, it also provides an interface for process data. Each process is mapped to procfs by its given process id number. Retrieving this pid number allows any tool to pull, with appropriate privileges, whatever data it needs to find out about that given process. This includes its memory mapping, memory usage, network usage, parameters, environment variables, and etc. Given this, if we know the pid and we’re hooked into the VFS for procfs, we can also manipulate data returned to these tools to hide processes.
1 | SYSCALL_DEFINE3(getdents, unsigned int, fd, |
fs.h有iterate_dir函数
1 | struct dir_context { |
调用了file_operations里面的iterate函数,在VFS中有关file_operation定义如下:
1 | const struct file_operations ext4_dir_operations = { |
可以看到,实现的是readdir
,中间过程比较复杂,简单描述如下:通过getdents系统调用了来获取当前目录下的文件时,file->f_op->readdir(file, buf, filler)调用的实际上是ext4_dir_operations函数集中的readdir()函数。即由ex4文件系统驱动来读取当前目录文件中的一个个目录项。 ext4_readdir最终会通过filldir把目录里面的项目一个一个的填到getdents返回的缓冲区里,缓冲区里是一个个的linux_dirent。
所以真正重要的是filler部分,在fs.h中可以找到:
1 | typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned); |
总的来说,调用层次如下:
1 | sys_getdents-> iterate_dir-> struct file_operations.iterate->省略若干层次 -> struct dir_context.actor(mostly filldir) |
要达到隐藏文件的目的,我们需要hooking filldir,在hooking function中去掉我们需要隐藏的文件记录,不填到缓冲区,这样应用程序就收不到相应的记录,也就打到了隐藏文件的目的。
1 | int fake_filldir(struct dir_context *ctx, const char *name, int namlen, |
附:VFS 层
1 | struct file_operations { |
Readdir
1 | struct linux_dirent { |
fillter
1 | dirent = buf->previous; |