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

问题描述 投票:0回答:2

我为

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;
}
c linux linux-kernel system-calls
2个回答
7
投票

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

从用户空间访问字符串的正确方法是首先使用

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] = '\0';

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

    return original_execl(filename, argv, envp);
}

在这种情况下,由于我们专门讨论文件名,因此您可以使用

getname_kernel()
中的
putname()
linux/fs.h
,它们使用
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);
}

3
投票

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

具体来说,这里您想在系统调用处理程序中使用一些从用户模式到达的“字符串”(指向字符的指针)。所以你可以看看在真实的系统调用中如何处理这些字符串。例如。

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

接受从

filename
 函数返回的 
getname()
,最终调用
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);
    }
    //...
© www.soinside.com 2019 - 2024. All rights reserved.