如何从内核 space 中的用户 space 指针正确提取字符串?

How to correctly extract a string from a user space pointer in kernel space?

我为 execve 系统调用写了一个钩子,一开始我写它来打印 "hi",每次执行一个文件。它运行良好,但是当我尝试打印传递给系统调用的 filename 时,这导致了崩溃,当然我不得不重新启动计算机。

这是我的代码:

static asmlinkage long our_execl(const char __user * filename,
            const char __user * const __user * argv,
            const char __user * const __user * envp) {
    printk("%s\n",filename);
    return original_execl(filename, argv, envp);
}

这就是我注入新系统调用的方式:

static int lkm_example_init(void)
{

    printk("new new new 2");

    write_cr0(read_cr0()&(~ 0x10000));

    sys_call_table = (void*)0xdd8c4240//the syscall address from the   /proc/kallsyms ;

    execl= sys_call_table[__NR_execve];
    sys_call_table[__NR_execve]=our_execl;

    write_cr0(read_cr0() | 0X10000);
    return 0;
}

这里最有可能发生的是 SMAP (Supervisor Mode Access Prevention) 阻止内核访问原始用户 space 指针,从而导致恐慌。

从用户 space 访问字符串的正确方法是先使用 strncpy_from_user() 复制其内容。另外,要小心并确保正确终止字符串。

static asmlinkage long our_execl(const char __user * filename,
            const char __user * const __user * argv,
            const char __user * const __user * envp) {
    char buf[256];
    buf[255] = '[=10=]';

    long res = strncpy_from_user(buf, filename, 255);
    if (res > 0)
        printk("%s\n", buf);

    return original_execl(filename, argv, envp);
}

在这种情况下,由于我们专门讨论文件名,您可以使用 getname() and putname() functions, which work using a struct filename.

static asmlinkage long our_execl(const char __user * filename,
            const char __user * const __user * argv,
            const char __user * const __user * envp) {

    struct filename *fname = getname(filename);
    if (!IS_ERR(fname)) {
        printk("%s\n", fname->name);
        putname(fname);
    }

    return original_execl(filename, argv, envp);
}

除了 Marco 的回答。
如果出现问题,您可以随时查看它是如何实现的。幸运的是,资源是开放的,任何人都可以访问它。

具体来说,在这里你想在从用户模式到达的系统调用处理程序中使用一些"string"(指向字符的指针)。因此,您可以查看在实际系统调用中如何处理此类字符串。例如。 do_execve() 用于 execve 系统调用:

SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}

接受从getname()函数返回的filename,最终调用strncpy_from_user():

struct filename *
getname_flags(const char __user *filename, int flags, int *empty)
{

    //...
    len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX);
    if (unlikely(len < 0)) {
        __putname(result);
        return ERR_PTR(len);
    }
    //...