c - 使用可加载内核模块的系统调用拦截中的参数似乎已损坏

标签 c linux linux-kernel system-calls

第一篇文章,因此我对可能低质量的解释表示歉意。

我试图编写一个可加载的内核模块,它除了拦截对 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

如您所见,即使我传递了 3187236 作为标志,flags 参数也等于 0。 更奇怪的是,真正的开放系统调用在处理参数方面似乎没有问题。

感谢任何形式的帮助,因为我几乎被困在这里。

这是完整的模块代码,以防有任何帮助:

#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

感谢所有在评论中做出贡献的人!

关于c - 使用可加载内核模块的系统调用拦截中的参数似乎已损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59346208/

相关文章:

linux - 为什么 linux 和 unix 的中断服务方式存在设计差异?

c - 重写旧版 cpufreq_driver 的 cpufreq_freq_table 初始化

c++ - 了解将结构转换为 void 指针

c - System V 消息队列 - 获取已存在的消息

php - 从/etc/redhat-release 中操作字符串

linux - 如何在不使用 find 的情况下递归设置权限,文件夹 700,文件 600

c - 如何在 Linux 内核中加入多个线程

没有反向引用的 C POSIX ERE

c - For循环两个数字之间的数字和

c - 未定义对 `yylval' 和 `yyerror` 的引用