Reputation: 140
I wrote a hook for the execve
system call and in the beginning when I wrote it to print "hi"
, each time that a file is executed. It worked fine, but when I tried to print the filename
that is passed to the system call this resulted in a crash and of course I had to restart my computer.
This is my code:
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);
}
This is how I inject the new syscall:
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;
}
Upvotes: 3
Views: 2394
Reputation: 69346
What is most likely happening here is that SMAP (Supervisor Mode Access Prevention) is preventing the kernel from accessing a raw user space pointer, causing a panic.
The correct way to access a string from user space is to copy its content using strncpy_from_user()
first. Also, be careful and make sure to correctly terminate the string.
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);
}
In this case, since we are specifically talking about a file name, depending on the kernel version you may be able to use the getname()
and putname()
from linux/fs.h
(if they are exported in the kernel version you are working with). These 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);
}
Upvotes: 7
Reputation: 236
Just in addition to Marco's answer.
If something goes wrong you can always look at how this is already implemented. Fortunately, the sources are open and anyone can access it.
Specifically, here you want to use some "string" (pointer to char) in syscall handler which arrives from user-mode. So you can take a look at how are such strings handled in real syscalls. E.g. do_execve()
for execve
syscall:
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);
}
takes filename
returned from getname()
function, which eventually invokes 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);
}
//...
Upvotes: 3