使用可加载内核模块的系统调用拦截中的参数似乎已损坏
Arguments in syscall intercept using loadable kernel module seem to be broken
首先 post 所以对于可能低质量的解释我深表歉意。
我试图编写一个可加载的内核模块,它除了拦截对 SYS_open 的系统调用之外什么都不做,将参数打印到 KERN_INFO,然后将参数转发给真正的系统调用。
转发部分似乎工作得很好,但我在打印时遇到问题,从系统调用拦截器函数的角度来看,参数似乎被破坏了。
以下是指向真正打开的系统调用以及拦截器定义的指针。
asmlinkage int (*real_open) (const char __user *, int, umode_t);
asmlinkage int fake_open(const char __user *filename, int flags, umode_t mode)
{
printk(KERN_INFO "interceptor: open() with flags = %d\n", flags);
return real_open(filename, flags, mode);
}
这是我正在测试的系统调用:
syscall(SYS_open, argv[1], 3187236);
根据 strace,这会导致以下调用:
open("test", O_RDONLY|O_TRUNC|__O_SYNC|O_LARGEFILE|O_PATH|FASYNC|0x24) = -1 ENOENT (No such file or directory)
以及拦截器打印的信息:
[18191.407899] interceptor: open() with flags = 0
如您所见,标志参数等于 0,即使我将 3187236 作为标志传递。
更奇怪的是,真正的 open 系统调用似乎在处理参数方面没有问题。
感谢任何形式的帮助,因为我几乎被困在这里。
以下是完整的模块代码,以备不时之需:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/futex.h>
#include <linux/highmem.h>
#include <asm/unistd.h>
#include <linux/slab.h>
/*
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
*/
unsigned long long *sys_call_table = (unsigned long long*) 0xffffffffaf800260; //sudo cat /proc/kallsyms | grep sys_call_table (/boot/System.map)
asmlinkage int (*real_open) (const char __user *, int, umode_t);
asmlinkage int fake_open(const char __user *filename, int flags, umode_t mode)
{
printk("interceptor: open() with flags = %d\n", flags);
return real_open(filename, flags, mode);
}
//make the memory page writable
int make_rw(unsigned long long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
if(pte->pte & ~_PAGE_RW)
pte->pte |= _PAGE_RW;
return 0;
}
//make the memory page read only
int make_ro(unsigned long long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;
return 0;
}
static int __init init(void)
{
printk(KERN_INFO "Attempting to install hook.\n");
make_rw((unsigned long long) sys_call_table);
real_open = (void*) sys_call_table[__NR_open];
sys_call_table[__NR_open] = (unsigned long long) fake_open;
make_ro((unsigned long long) sys_call_table);
return 0; //no error
}
static void __exit clean(void)
{
printk(KERN_INFO "Uninstalling hook.\n");
make_rw((unsigned long long) sys_call_table);
sys_call_table[__NR_open] = (unsigned long long) real_open;
make_ro((unsigned long long) sys_call_table);
}
module_init(init);
module_exit(clean);
MODULE_LICENSE("GPL");
更新:
内核版本 4.17 及更高版本要求通过 pt_regs 结构传递参数。以前的代码在 4.16 之前都很好。
asmlinkage long (*real_open) (const struct pt_regs *);
asmlinkage long fake_open(const struct pt_regs *regs)
{
printk("interceptor: open() with flags = %ld\n", regs->si);
return real_open(regs);
}
更多信息:https://github.com/milabs/khook/issues/3
感谢所有在评论中做出贡献的人!
首先 post 所以对于可能低质量的解释我深表歉意。
我试图编写一个可加载的内核模块,它除了拦截对 SYS_open 的系统调用之外什么都不做,将参数打印到 KERN_INFO,然后将参数转发给真正的系统调用。 转发部分似乎工作得很好,但我在打印时遇到问题,从系统调用拦截器函数的角度来看,参数似乎被破坏了。
以下是指向真正打开的系统调用以及拦截器定义的指针。
asmlinkage int (*real_open) (const char __user *, int, umode_t);
asmlinkage int fake_open(const char __user *filename, int flags, umode_t mode)
{
printk(KERN_INFO "interceptor: open() with flags = %d\n", flags);
return real_open(filename, flags, mode);
}
这是我正在测试的系统调用:
syscall(SYS_open, argv[1], 3187236);
根据 strace,这会导致以下调用:
open("test", O_RDONLY|O_TRUNC|__O_SYNC|O_LARGEFILE|O_PATH|FASYNC|0x24) = -1 ENOENT (No such file or directory)
以及拦截器打印的信息:
[18191.407899] interceptor: open() with flags = 0
如您所见,标志参数等于 0,即使我将 3187236 作为标志传递。 更奇怪的是,真正的 open 系统调用似乎在处理参数方面没有问题。
感谢任何形式的帮助,因为我几乎被困在这里。
以下是完整的模块代码,以备不时之需:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/futex.h>
#include <linux/highmem.h>
#include <asm/unistd.h>
#include <linux/slab.h>
/*
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
*/
unsigned long long *sys_call_table = (unsigned long long*) 0xffffffffaf800260; //sudo cat /proc/kallsyms | grep sys_call_table (/boot/System.map)
asmlinkage int (*real_open) (const char __user *, int, umode_t);
asmlinkage int fake_open(const char __user *filename, int flags, umode_t mode)
{
printk("interceptor: open() with flags = %d\n", flags);
return real_open(filename, flags, mode);
}
//make the memory page writable
int make_rw(unsigned long long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
if(pte->pte & ~_PAGE_RW)
pte->pte |= _PAGE_RW;
return 0;
}
//make the memory page read only
int make_ro(unsigned long long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;
return 0;
}
static int __init init(void)
{
printk(KERN_INFO "Attempting to install hook.\n");
make_rw((unsigned long long) sys_call_table);
real_open = (void*) sys_call_table[__NR_open];
sys_call_table[__NR_open] = (unsigned long long) fake_open;
make_ro((unsigned long long) sys_call_table);
return 0; //no error
}
static void __exit clean(void)
{
printk(KERN_INFO "Uninstalling hook.\n");
make_rw((unsigned long long) sys_call_table);
sys_call_table[__NR_open] = (unsigned long long) real_open;
make_ro((unsigned long long) sys_call_table);
}
module_init(init);
module_exit(clean);
MODULE_LICENSE("GPL");
更新: 内核版本 4.17 及更高版本要求通过 pt_regs 结构传递参数。以前的代码在 4.16 之前都很好。
asmlinkage long (*real_open) (const struct pt_regs *);
asmlinkage long fake_open(const struct pt_regs *regs)
{
printk("interceptor: open() with flags = %ld\n", regs->si);
return real_open(regs);
}
更多信息:https://github.com/milabs/khook/issues/3
感谢所有在评论中做出贡献的人!