Question

I am trying to intercept system call to change the behaviour of some of them. I use a LKM following among other thing what is described here: http://syprog.blogspot.co.uk/2011/10/hijack-linux-system-calls-part-iii.html.

However, I face some problems when doing this for some system calls, for example:

/* 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);
}

Create a segmentation fault.

/* 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);
}

This generate a "unable to handle kernel paging request" in system_call_fastpath (Bad RIP value).

The system call are modified in the sys_call_table this way:

// 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);

And put back when the module is unloaded:

make_rw((unsigned long)sys_call_table);
sys_call_table[ __NR_clone] = (unsigned long *)real_clone;
make_ro((unsigned long)sys_call_table);

It is probably something very simple, but I really don't get it. Intercepting call to open, mkdir or exit cause no problem, but some other cause issue I cannot really understand as the replacement are simply calling the original functions.

EDIT: it fails at the call time not during the initialisation.

EDIT2: I found a "solution" using kprobe. Given that I only wanted to use the returned value of do_fork (what is actually called in sys_clone) which is the children process pid, if I wanted to change the behaviour of sys_clone it won't actually work for obvious reason. Used this as reference http://www-users.cs.umn.edu/~boutcher/kprobes/

The actual solution look like:

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",
    }
};

And in the module initialisation:

if ((retval = register_kretprobe(&do_fork_kretprobe)) < 0) {
    printk(KERN_ERR "register_kretprobe failed, returned %d\n", 
        retval);                 
    return -1;
}

When unregistering the module:

unregister_kretprobe(&my_kretprobe);

No correct solution

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top