linux-kernel模块中的系统调用拦截(内核3.5)
Posted
技术标签:
【中文标题】linux-kernel模块中的系统调用拦截(内核3.5)【英文标题】:System call interception in linux-kernel module (kernel 3.5) 【发布时间】:2012-12-02 07:38:10 【问题描述】:我需要用我自己的实现替换标准系统调用(例如 SYS_mkdir)。
正如我在一些资料中看到的,包括 *** 上的 this question,自内核版本 2.6
以来,sys_call_table
未导出符号。
我尝试了以下代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/unistd.h>
#include <asm/syscall.h>
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]’
如何替换系统调用?
编辑:有没有不打内核补丁的解决方案?
【问题讨论】:
尝试将类型转换为char*
然后分配
可能是this和this对你有帮助
没有不打补丁的通用解决方案。
【参考方案1】:
这对我有用。
看 Linux Kernel: System call hooking example 和 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();
【讨论】:
【参考方案2】:是的,有一个无需修补/重建内核的解决方案。使用Kprobes 基础架构(或 SystemTap)。
这将允许您使用内核模块在内核中的任何点放置“探针”(函数)。
现在可以防止通过修改 sys_call_table 来做类似的事情(它是只读的)并且被认为是一种肮脏的黑客攻击! Kprobes/Jprobes/etc 是一种“干净”的方式。此外,内核源代码树中提供的文档和samples 非常好(查看内核源代码树下的Documentation/kprobes.txt)。
【讨论】:
kprobes/systemtap 不会让您替换系统调用处理程序,但可以补充它/在它之前。 嘿,kprobes 使用补丁 :) @fche:是的,我同意。关键是效果是相似的。。@IlyaMatveychikov:AFAIK,不,kprobes 是一个内核特性;你不需要应用任何补丁。此外,大多数发行版都启用了 kprobes..【参考方案3】:问题是由于 sys_call_table 是只读的。为了避免该错误,在操作 sys_call_table 之前,您还必须使其可写。内核提供了一个函数来实现它。该函数以 set_mem_rw() 的形式给出。
只需在操作 sys_call_table 之前添加以下代码 sn-p
set_mem_rw((long unsigned int)sys_call_table,1);
在内核模块的退出函数中,请不要忘记将sys_call_table恢复为只读。可以如下实现。
set_mem_ro((long unsigned int)sys_call_table,1);
【讨论】:
【参考方案4】:首先,您需要确定 sys_call_table 的位置。见here。
在写入刚刚定位的系统表之前,您必须使其内存页可写。检查here,如果这不起作用,请尝试this。
【讨论】:
【参考方案5】:使用 LSM 基础架构。
查看 LSM 挂钩 path_mkdir
或 inode_mkdir
了解详情。需要解决的一个问题是如何在系统不允许的情况下注册自己的 LSM 模块。在此处查看答案以获取详细信息:
How can I implement my own hook function with LSM?
【讨论】:
以上是关于linux-kernel模块中的系统调用拦截(内核3.5)的主要内容,如果未能解决你的问题,请参考以下文章