C 截获某些系统调用时出现Linux可加载内核模块内存错误

C 截获某些系统调用时出现Linux可加载内核模块内存错误,c,linux,linux-kernel,kernel-module,interception,C,Linux,Linux Kernel,Kernel Module,Interception,我试图拦截系统调用以改变其中一些的行为。我使用一个LKM,其中包括这里描述的内容: 但是,在对某些系统调用执行此操作时,我会遇到一些问题,例如: /* original sys_clone pointer */ asmlinkage long (*real_clone)(unsigned long, unsigned long, void* __user, void* __user, struct pt_regs*); /* sys_clone r

我试图拦截系统调用以改变其中一些的行为。我使用一个LKM,其中包括这里描述的内容:

但是,在对某些系统调用执行此操作时,我会遇到一些问题,例如:

/* original sys_clone pointer */
asmlinkage long (*real_clone)(unsigned long, 
    unsigned long, 
    void* __user, 
    void* __user,    
    struct pt_regs*);

/* sys_clone replacement */
asmlinkage long custom_clone(unsigned long clone_flags, 
    unsigned long newsp, 
    void* __user parent_tid, 
    void* __user child_tid, 
    struct pt_regs *regs)
{
    return real_clone(clone_flags, newsp, parent_tid, child_tid, regs);
}
创建一个分段错误

/* original sys_delete_module pointer */
asmlinkage long (*real_delete_module)(const char* __user, unsigned int);

/* sys_delete_module replacement */
asmlinkage long custom_delete_module(const char* __user name_user, 
    unsigned int flags){
    return real_delete_module(name_user, flags);
}
这将在system_call_fastpath(错误的RIP值)中生成“无法处理内核分页请求”

系统调用在sys_call_表中修改如下:

// get sys_call_table address
make_rw((unsigned long)sys_call_table);
real_clone = (void*)sys_call_table[ __NR_clone];
sys_call_table[ __NR_clone] = (unsigned long *)custom_clone;
// etc...
make_ro((unsigned long)sys_call_table);
并在卸载模块时放回:

make_rw((unsigned long)sys_call_table);
sys_call_table[ __NR_clone] = (unsigned long *)real_clone;
make_ro((unsigned long)sys_call_table);
这可能很简单,但我真的不明白。拦截对open、mkdir或exit的调用不会导致任何问题,但我无法真正理解其他一些原因,因为替换只是调用原始函数

编辑:它在呼叫时间失败,而不是在初始化期间

EDIT2:我发现了一个使用kprobe的“解决方案”。考虑到我只想使用do_fork(在sys_clone中实际称为)的返回值,这是子进程pid,如果我想更改sys_clone的行为,它实际上不会工作,原因很明显。作为参考

实际解决方案如下所示:

static int ret_do_fork_handler(struct kretprobe_instance *ri, 
    struct pt_regs *regs)
{
    pid_t ppid, cpid;
    ppid = current->pid;
    cpid = (pid_t)regs->ax;
    if(cpid < 0){
        printk("do_fork returned an error");
        return 0;
    }else{
        printk("do_fork process %lu\n", (unsigned long)ppid);
        printk("do_fork returns %lu\n", (unsigned long)cpid);
    }   
    return 0;
}

static struct kretprobe do_fork_kretprobe = {
    .handler = ret_do_fok_handler,
    .maxactive = 500, // some random value
    .kp = {
        .symbol_name    = "do_fork",
    }
};

sys\u call\u table
是否可能跨越多个页面?如果是这样,在调用
make\u rw
时,您需要考虑
\uu NR\u clone
(例如,
make\u rw((无符号长)和sys\u call\u table[\uu NR\u clone])
否,在初始化时未调用sys\u clone时会发生错误。如果问题出在system calls表的版本上,我会收到一个错误,说明我正在尝试在只读内存中写入。
if ((retval = register_kretprobe(&do_fork_kretprobe)) < 0) {
    printk(KERN_ERR "register_kretprobe failed, returned %d\n", 
        retval);                 
    return -1;
}
unregister_kretprobe(&my_kretprobe);