今天我们来在内核里做点有意思的事----禁止用户删除文件(包括root用户)
先来一点原理
删除文件的流程如下:
- 应用层
rm
->unlinkat()
- 内核
sys_unlinkat()
->do_unlinkat()
->vfs_unlink()
最后vfs_unlink()
会调用具体文件系统的unlink()
函数, 例如ext4文件系统对应的ext4_unlink()
几种思路
如果想禁止删除文件, hook上述的任意一个函数都可以实现, 最简单的应该是hook系统调用sys_unlinkat()
了, 这种方式网上很多教程, 不是我们今天要讲的; 当然还可以使用inline hook
搞do_unlinkat()
或者vfs_unlink()
, 这种方式稍微复杂一些, 我们今天也不讨论; 今天我们搞一个小众的玩法: hook具体文件系统的unlink()
实现.
我们的玩法
我们今天以ext4文件系统为例, 通过替换inode_operations
里的unlink
函数指针实现禁止删除文件的目的.
流程如下:
- 使用
kallsyms_lookup_name()
获取ext4文件系统的struct inode_operations
结构地址 - 关闭写保护
- 把
struct inode_operations
中的unlink
换成我们自己的函数 - 开启写保护
- 在我们自己的函数中拒绝删除文件
代码如下(5.4内核测试通过):
// 获取ext4文件系统的struct inode_operations结构地址
void* __get_ext4_inode_ops(void)
{
void *p = (void *)kallsyms_lookup_name("ext4_dir_inode_operations");
return p;
}
// 关闭写保护
unsigned long write_protect_disable(void)
{
unsigned long cr0 = 0;
unsigned long ret = 0;
// 获取原始cr0的值
#ifndef __LP64__
asm volatile("movl %%cr0, %%eax": "=a"(cr0));
#else
asm volatile("movq %%cr0, %%rax": "=a"(cr0));
#endif
// 保存原始cr0的值
ret = cr0;
// 清除CR0寄存器的第16位(写保护位)
#ifndef __LP64__
cr0 &= 0xfffeffff;
asm volatile("movl %%eax, %%cr0":: "a"(cr0));
#else
cr0 &= 0xfffffffffffeffff;
asm volatile("movq %%rax, %%cr0":: "a"(cr0));
#endif
// 返回原始cr0值
return ret;
}
// 开启写保护
void write_protect_enable(unsigned long oldval)
{
// 恢复cr0
#ifndef __LP64__
asm volatile("movl %%eax, %%cr0":: "a"(oldval));
#else
asm volatile("movq %%rax, %%cr0":: "a"(oldval));
#endif
}
// 我们的unlink
static int my_ext4_unlink(struct inode *dir, struct dentry *dentry)
{
klogw(log_tag "you can not rm [%s]", dentry->d_name.name);
return -1;
}
// 保存ext4文件系统的struct inode_operations结构地址
struct inode_operations *ext4_iops;
// 保存原来的unlink
int (*orig_ext4_unlink)(struct inode *dir, struct dentry *dentry);
/*
* 模块初始化
*/
static __init int main_init(void)
{
int rv = -1;
unsigned long cr0;
// 1. 获取ext4文件系统的struct inode_operations结构地址
ext4_iops = __get_ext4_inode_ops();
// 2. 替换我们的unlink
orig_ext4_unlink = ext4_iops->unlink;
cr0 = write_protect_disable();
ext4_iops->unlink = my_ext4_unlink;
write_protect_enable(cr0);
klogw(log_tag "kernel module load.");
// 设置成功标识
rv = 0x0;
goto out;
out:
return rv;
}
/*
* 模块清理
*/
static __exit void main_exit(void)
{
// 恢复原来的unlink
unsigned long cr0;
cr0 = write_protect_disable();
ext4_iops->unlink = orig_ext4_unlink;
write_protect_enable(cr0);
klogw(log_tag "kernel module unload.");
}
module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Kernel Module");
MODULE_AUTHOR("lee");
现在安装模块之后你应该就不能删除任何文件了, 快试一下吧!
都看到这里了,顺手点个 赞 吧!