如何从内核 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);
}
//...
我为 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);
}
//...