clinuxlinux-kernelsystem-calls

Modifying (stealing) Linux syscalls using kprobe


I am trying to steal the statx syscall on Linux 6.8 and to modify the returning arguments.

However, when attemp to load the module, I got a BUG about NULL pointer deference at line

r = strncpy_from_user(path, (char __user *)user_regs->si, 128);

Here is my code:

I use this question (Cannot read syscall arguments from a kprobe handler). But I did manage to deal with the argument.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kprobes.h> 

MODULE_VERSION("v.0");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Flavien <flav> ASTRAUD <flav@epita.fr>");
MODULE_DESCRIPTION("ex. pour interception d'un syscall");

static int m_statx(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    long r = 0;
    char path[128];
    struct pt_regs *user_regs = (struct pt_regs *)regs->di;
    struct statx statxbuf; // (struct statx *) regs->r8;

    r = strncpy_from_user(path, (char __user *)user_regs->si, 128);
    
    r = copy_from_user(&statxbuf, (struct statx *)user_regs->r8,
               sizeof (struct statx));

    pr_info("STATX_V3 %s size=%lld\n", path, statxbuf.stx_size);

    statxbuf.stx_size = 42;

    r = copy_to_user((struct statx *)user_regs->r8,
             &statxbuf, sizeof(statxbuf));

    return 0;
}

static struct kretprobe kret = { 
    //  .symbol_name = "__x64_sys_statx", 
    .handler = m_statx,
};

static int override_statx(void)
{
    kret.kp.symbol_name = "__x64_sys_statx";
    if (register_kretprobe(&kret) < 0) 
        return 0; 
    return 0;
}

static void pullback_syscall(void)
{
    unregister_kretprobe(&kret);
    
    return;
}

static int __init kstatx_init(void)
{
    pr_info("kstatx INIT\n=============\n");

    override_statx();

    return 0;
}

static void __exit kstatx_exit(void)
{
        pr_info("kstatx END\n=============\n"); 
    
    pullback_syscall();
}

module_init(kstatx_init);
module_exit(kstatx_exit);


Solution

  • Here the solution with the help of @tsyvarev

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kprobes.h> 
    
    MODULE_VERSION("v.0");
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("flav <flav@epita.fr>");
    MODULE_DESCRIPTION("ex. pour interception d'un syscall");
    
    struct my_data {
        char *ppath;
        struct statx *s_statx;
    };
    
    static int m_statx_pre(struct kretprobe_instance *ri, struct pt_regs *regs)
    {
        struct my_data *data;
        char *ppath;
    
        pr_info("STATX entry\n");
        ppath = (char *)((struct pt_regs *)regs->di)->si;
        struct statx *statx = (struct statx *)((struct pt_regs *)regs->di)->r8;
    
        data = (struct my_data *)ri->data;
        data->s_statx = statx;
        data->ppath = ppath;
    
        return 0;
    }
    
    static int m_statx(struct kretprobe_instance *ri, struct pt_regs *regs)
    {
        
        struct my_data *data;
        
        
        
        long r = 0;
        char path[128];
    
        struct statx statxbuf;
    
        data = (struct my_data *)ri->data;
        r = strncpy_from_user(path, data->ppath, 128);
        
        r = copy_from_user(&statxbuf, data->s_statx, sizeof (struct statx));
    
        pr_info("STATX_V3 %s size=%lld\n", path, statxbuf.stx_size);
        pr_info("STATX_ret: [%s]\n", path);
    
    
        statxbuf.stx_size = 42;
        statxbuf.stx_uid = 42;
        statxbuf.stx_gid = 42;
    
        r = copy_to_user(data->ppath, path, strlen(path)+1);
        r = copy_to_user(data->s_statx, &statxbuf, sizeof(statxbuf));
    
        return 0;
    }
    
    static struct kretprobe kret = { 
        .handler = m_statx,
        .entry_handler = m_statx_pre,
        .data_size = sizeof (struct my_data),
    };
    
    static int override_statx(void)
    {
        kret.kp.symbol_name = "__x64_sys_statx";
        if (register_kretprobe(&kret) < 0) 
            return 0; 
        return 0;
    }
    
    static void pullback_syscall(void)
    {
        unregister_kretprobe(&kret);
        
        return;
    }
    
    static int __init kstatx_init(void)
    {
        pr_info("kstatx INIT\n=============\n");
    
        override_statx();
    
        return 0;
    }
    
    static void __exit kstatx_exit(void)
    {
            pr_info("kstatx END\n=============\n"); 
        
        pullback_syscall();
    }
    
    module_init(kstatx_init);
    module_exit(kstatx_exit);
    

    Without the module :

    # stat kparm.ko 
      File: kparm.ko
      Size: 302816          Blocks: 592        IO Block: 4096   regular file
    Device: 252,0   Inode: 393542      Links: 1
    Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
    
    # ls -ln kparm.ko 
    -rw-r--r-- 1 0 0 302816 Oct  7 10:23 kparm.ko
    

    With the module :

    # stat kparm.ko 
      File: kparm.ko
      Size: 42              Blocks: 592        IO Block: 4096   regular file
    Device: 252,0   Inode: 393542      Links: 1
    Access: (0644/-rw-r--r--)  Uid: (   42/    _apt)   Gid: (   42/  shadow)
    
    # ls -ln kparm.ko 
    -rw-r--r-- 1 42 42 42 Oct  7 10:23 kparm.ko