1

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 <[email protected]>");
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);

12
  • @Tsyvarev It's only for teaching matter ; I try with statx but I could do it with other syscall. Commented Oct 7, 2024 at 7:31
  • 1
    Yes, the question is much better now. But ... post-handler doesn't look as a proper mechanism for execute your code after the system call returns. You place your kprobe at the very first instruction of the statx system call, so post-handler is executed after given instruction, not after the entire function. For execute your code after the function, use kretprobe and its .handler field. Commented Oct 7, 2024 at 10:00
  • 1
    "Module crashing" is not a problem description sufficient for solving the problem. Have you checked the call stack in the crash message? Which line of your code is executed according to that call stack? And so on. If you have modified your code, then update the question post with the actual code. Commented Oct 7, 2024 at 12:00
  • 1
    So, what do you want to achieve in your new code with the post-handler? If you want to just print some values of a syscall, then use pre-handler, as in the question you refer to. If you want to modify result of the function, then use kretprobe. Using a random kind of the kprobe is not a way for programming in the kernel. Commented Oct 7, 2024 at 14:43
  • 1
    A handler for kretprobe doesn't have access to the function's arguments: its regs argument contains registers upon function's returning, not the ones upon function's entering. But those arguments could be extracted in the pre-handler, and passed via kretprobe_instance argument. See that my comment: stackoverflow.com/questions/78619294/… Commented Oct 7, 2024 at 15:26

1 Answer 1

0

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 <[email protected]>");
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
Sign up to request clarification or add additional context in comments.

3 Comments

You have removed modification of read-only parameter pathname from the question, but have added it to the answer's code. Really?!
@Tsyvarev Hum ! ok ; so Should I change the question or answer?
1. From the code in the question post remove all manipulations with path. Instead, demonstrate manipulations with stat_ctx field. 2. Update description in the question post to correspond with the code. (Your last code causes NULL pointer dereference, but current description tells about incorrect size observed.) 3. From the code in the answer post remove all manipulations with path.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.