linux-kernel模块中的系统调用拦截(内核3.5)

我需要用自己的实现替换标准系统调用(例如SYS_mkdir)。

正如我在一些来源中读到的,包括Stackoverflow上的这个问题 , sys_call_table不是自内核版本2.6以来的导出符号。

我尝试了以下代码:

  #include  #include  #include  #include  int (*orig_mkdir)(const char *path); .... int init_module(void) { orig_mkdir=sys_call_table[__NR_mkdir]; sys_call_table[__NR_mkdir]=own_mkdir; printk("sys_mkdir replaced\n"); return(0); } .... 

不幸的是我收到编译错误:

  error: assignment of read-only location 'sys_call_table[83]' 

如何更换系统调用?

编辑:有没有内核修补的解决方案?

这对我有用。

请参阅Linux内核:系统调用挂钩示例和https://bbs.archlinux.org/viewtopic.php?id=139406

 asmlinkage long (*ref_sys_open)(const char __user *filename, int flags, umode_t mode); asmlinkage long new_sys_open(const char __user *filename, int flags, umode_t mode) { return ref_sys_open(filename, flags, mode); } static unsigned long **aquire_sys_call_table(void) { unsigned long int offset = PAGE_OFFSET; unsigned long **sct; while (offset < ULLONG_MAX) { sct = (unsigned long **)offset; if (sct[__NR_close] == (unsigned long *) sys_close) return sct; offset += sizeof(void *); } print("Getting syscall table failed. :("); return NULL; } // Crazy copypasted asm stuff. Could use linux function as well... // but this works and will work in the future they say. static void disable_page_protection(void) { unsigned long value; asm volatile("mov %%cr0, %0" : "=r" (value)); if(!(value & 0x00010000)) return; asm volatile("mov %0, %%cr0" : : "r" (value & ~0x00010000)); } static void enable_page_protection(void) { unsigned long value; asm volatile("mov %%cr0, %0" : "=r" (value)); if((value & 0x00010000)) return; asm volatile("mov %0, %%cr0" : : "r" (value | 0x00010000)); } static int __init rootkit_start(void) { //Hide me print("loaded"); if(!(sys_call_table = aquire_sys_call_table())) return -1; disable_page_protection(); { ref_sys_open = (void *)sys_call_table[__NR_open]; sys_call_table[__NR_open] = (unsigned long *)new_sys_open; } enable_page_protection(); return 0; } static void __exit rootkit_end(void) { print("exiting"); if(!sys_call_table) { return; } disable_page_protection(); { sys_call_table[__NR_open] = (unsigned long *)ref_sys_open; } enable_page_protection(); } 

是的,有一个没有修补/重建内核的解决方案。 使用Kprobes基础结构(或SystemTap)。

这将允许您使用内核模块在内核中的任何位置放置“探测器”(函数)。

现在可以通过修改sys_call_table来执行类似的操作(它是只读的)并被视为脏黑客! Kprobes / Jprobes / etc是一种“干净”的方法。此外,内核源代码树中提供的文档和示例非常出色(请查看内核src树 – Documentation / kprobes.txt )。

首先,您需要确定sys_call_table的位置。 看到这里 。

在写入刚刚定位的系统表之前,必须使其内存页面可写。 对于那里的检查,如果这不起作用,试试这个 。

使用LSM infrustructure。

有关详细信息,请查看LSM钩子path_mkdirinode_mkdir 。 需要解决的一个问题是如何在系统不明确允许的情况下注册您自己的LSM模块。 有关详细信息,请参阅答案:

如何使用LSM实现自己的钩子函数?

该问题是由于sys_call_table是只读的。 为了避免错误,在操作sys_call_table之前,你必须使它也可写。 内核提供了实现它的function。 该函数以set_mem_rw()的forms给出。

只需在操作sys_call_table之前添加以下代码段

 set_mem_rw((long unsigned int)sys_call_table,1); 

在内核模块的退出函数中,请不要忘记将sys_call_table恢复为只读。它可以实现如下。

 set_mem_ro((long unsigned int)sys_call_table,1);