在利用ebpf监控文件变更操作中,记录一些内核参数获取的过程。
1.环境信息
openeuler 2203
2.常用的修改文件的方式或者命令
1).直接使用文件系统的操作接口,比如write等;
2.)系统命令,比如vim/vi,sed,echo,cp,mv
涉及到的系统调用主要有:
sys_enter_write
sys_enter_renameat2
sys_enter_rename
sys_enter_copy_file_range
sys_enter_unlinkat
3.监控中参数获取遇到问题
1).根据fd获取文件的名称
在ebpf中函数sys_enter_write函数的参数,可以通过命令bpftrace:
# bpftrace -vl tracepoint:syscalls:sys_enter_write
tracepoint:syscalls:sys_enter_write
int __syscall_nr
unsigned int fd
const char * buf
size_t count
如果需要在sys_entrer_write中获取fd对应文件名称,通过以下方式:
unsigned int fd;
struct task_struct* t;
struct task_struct* p;
struct files_struct* f;
struct fdtable* fdt;
struct file** fdd;
struct file* file;
struct path path;
struct dentry* dentry;
struct inode* inode;
struct qstr pathname;
umode_t mode;
unsigned long i_ino;
char filename[128];
fd =args->fd;
t = (struct task_struct*)bpf_get_current_task();
bpf_probe_read(&f, sizeof(f), &(t->files));
bpf_probe_read(&fdt, sizeof(fdt), (void*)&f->fdt);
int ret = bpf_probe_read(&fdd, sizeof(fdd), (void*)&fdt->fd);
bpf_probe_read(&file, sizeof(file), (void*)&fdd[fd]);
//可以获取到文件的inode
bpf_probe_read(&inode, sizeof(inode), &file->f_inode);
bpf_probe_read(&e.i_ino, sizeof(i_ino), &inode->i_ino);
bpf_probe_read(&mode, sizeof(mode), &inode->i_mode);
//可以对文件的类型过滤
if(!S_ISREG(mode)){
return 0;
}
//获取文件的名称
bpf_probe_read(&path, sizeof(path), (const void*)&file->f_path);
bpf_probe_read(&dentry, sizeof(dentry), (const void*)&path.dentry);
bpf_probe_read(&pathname, sizeof(pathname), (const void*)&dentry->d_name);
2).文件绝对路径获取
操作文件的时候,传入参数文件路径可以是相对路径或者是绝对路径,如果是相对路径可能需要在获取文件的完成路径。
在ebpf安全保护机制下,不可控循环可能不让操作,所以这边固定了循环次数,也就是固定目录的深度,这个地方举例是4级目录结构。
struct dentry* d_parent;
#pragma unroll
for (int i = 0; i < MAX_DEPTH; i++) {
bpf_probe_read(&d_parent, sizeof(d_parent), (const void*)&dentry->d_parent);
if (d_parent == dentry) {
break;
}
if(i == 0){
bpf_probe_read(&e.dir1, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
}else if(i == 1){
bpf_probe_read(&e.dir2, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
}else if(i == 2){
bpf_probe_read(&e.dir3, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
}else if(i == 3){
bpf_probe_read(&e.dir4, sizeof(d_parent->d_iname), (const void*)&d_parent->d_iname);
}
dentry = d_parent;
}
上边循环中,可以用数组来保存目录的名称,但是具体实现过程,编译没有过,暂时用这个临时方法代替。报错问题还在研究中。
3).文件唯一性标记问题
sed,vim等命令修改文件,每次更新,文件的inode会变化,这个和命令的实现机制有关,有的时候可能还是需要用文件的名称来做唯一标记。