如何从内核模块添加系统调用?

时间:2013-09-20 10:50:24

标签: c linux kernel system-calls

好的,我正在尝试从内核模块向我的Linux添加一个系统调用。 这是模块代码。

asmlinkage int my_syscall() {
    printk(KERN_INFO "AWESOME!\n");
    return 0;
}

int load() {
    unsigned long ** addr;
    unsigned long int i = START;

    printk(KERN_INFO "IN\n");

    while (i < END) {
        addr = (unsigned long **)i;
        if (addr[__NR_close] == (unsigned long *)sys_close) {
            break;
        }
        i += sizeof(void *);
    }

    if (i != END) {
        addr += __NR_vserver;
        struct page * p = virt_to_page(addr);
        unsigned long paddr = (unsigned long)page_address(p);
        set_memory_rw(paddr, 15);
        *addr = &my_syscall;
        set_memory_ro(paddr, 15);
    }

    return 0;
}

void unload() {
    printk(KERN_INFO "OUT\n");
}

module_init(load);
module_exit(unload);

所以我正在寻找sys_call_table,一旦找到它,我就试图覆盖未实现的系统调用(vserver)。 当我insmod得到.ko时,dmesg就是这样说的:

BUG: unable to handle kernel paging request at ffffffff81801bc0

0xffffffff81801bc0实际上是我尝试编写&my_syscall的地址。 我不确切地知道我做错了什么,但我认为当我尝试编写时,内存页面可能仍处于ro模式...

3 个答案:

答案 0 :(得分:0)

我不明白的是以下几行:

set_memory_rw(paddr, 15);

这里应该发生什么?

您无法更改实际地址的属性!

以下代码可能是正确的:

set_memory_rw(addr, 15);

(在这种情况下,您不需要物理地址“paddr”也不需要页面“p”。)

或者,您可以尝试在内核空间中第二次映射物理地址。

您应该在“unload()”函数中恢复原始指针!!

答案 1 :(得分:0)

如果0xffffffff81801bc0是您传递给set_memory_rw()的实际地址,那么它肯定是错误的值。该地址应该向上舍入到PAGE_MASK边界,即页面的起始地址,对于64位系统,通常以4个零结束。

set_memory_rw()使用内存页表的U / S位进行操作。但是x86 / amd64 CPU有另一个属性:WP位(在cr0内)。根据英特尔手册,WP位覆盖了内存页表属性,即无论内存是否只读,启用/禁用WP位仍然有效。但是WP位是per-cpu,因此要小心地同步你的内存访问,这样在更改内存内容时,另一个CPU不会读取它。 (对于你未使用的系统调用号,这应该没问题。)

http://vulnfactory.org/blog/2011/08/12/wp-safe-or-not/

答案 2 :(得分:-2)

看起来你在系统调用的实现中遗漏了一些东西。只需看看http://arvindsraj.wordpress.com/2012/10/05/adding-hello-world-system-call-to-linux/,它解释了实施系统调用的逐步程序,并检查您是否正确地执行了所有步骤。