C 使用Linux内核中的模块覆盖功能
在没有详细说明原因的情况下,我正在寻找一种干净(尽可能)的方法来替换来自可加载模块的内核函数和系统调用。我最初的想法是编写一些代码来覆盖一些函数,这些函数将接受原始函数(如果可能的话,调用函数),然后添加一些我自己的代码。关键是我编写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时,将访问我的函数 我可以很容易(相对而言)直接在内核中完成这项工作,只需将代码放入适当的函数中,但我想知道是否有人知道一点C魔法,这不一定是可怕的内核(或C)编码实践,可以达到同样的效果 我想到了定义和类型定义,但我无法在头脑中完全理解 简言之:有人知道(从模块)有效覆盖Linux内核中函数的方法吗C 使用Linux内核中的模块覆盖功能,c,linux,module,kernel,C,Linux,Module,Kernel,在没有详细说明原因的情况下,我正在寻找一种干净(尽可能)的方法来替换来自可加载模块的内核函数和系统调用。我最初的想法是编写一些代码来覆盖一些函数,这些函数将接受原始函数(如果可能的话,调用函数),然后添加一些我自己的代码。关键是我编写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时,将访问我的函数 我可以很容易(相对而言)直接在内核中完成这项工作,只需将代码放入适当的函数中,但我想知道是否有人知道一点C魔法,这不一定是可怕的内核(或C)编码实践,可以达到同样的效果 我想到了定义和类型定义
编辑:因为有人问过,我基本上想从内核中记录某些函数(创建/删除目录等),但是为了理智起见,一个可加载的模块似乎是有意义的,而不必为内核代码编写一个大补丁并在每次更改时重新编译。向内核添加少量代码是可以的,但我想将大部分工作卸载到一个模块上。我不完全确定我是否理解您想要做什么,但我认为这可能是一个好的解决方案。它仍在开发中,所以我不知道它现在是否处于任何可用状态。大多数文件系统工作已经在模块中完成,假定文件系统代码是作为模块构建的,而不是构建在内核中(这意味着真正的答案取决于内核构建选项) 假设您要记录的位都与文件系统相关,并且这些文件系统例程构建为模块,那么您应该能够更改您感兴趣的文件系统模块,并重新加载它们
如果这些假设不是真的,或者不能成为真的,那么事情显然会变得更棘手,我真的不能再进一步告诉你了。你可能想要(PDF链接),这将有效地让你记录调用内核函数的用户进程。如果您真的想记录内核函数的使用情况,那么您需要查看。因为您只想记录调用(即,您实际上不会覆盖它们),并且可以接受对内核代码的少量更改,最干净的方法是为您感兴趣的每个函数添加一个钩子(使用通知程序链甚至普通函数指针)。然后,您的模块只需将自己注册到您添加的所有钩子(并在卸载时从钩子中注销)
也有可能是其他人已经为您添加了钩子。您不想修改现有的系统调用,而是想对它们进行检测。这就是目的。如果您真的想通过编写自己的模块来进行艰难的操作并截获系统调用,我建议您阅读一些rootkit文献,但是我手头没有任何链接(尽管我想到了phrack) 与试图破坏内核系统调用函数相比,inotify是一种更好的监视文件创建、删除和修改的方法,但它可以从用户空间工作 引自维基百科(): 可以监视的一些事件包括:
* IN_ACCESS - read of the file
* IN_MODIFY - last modification
* IN_ATTRIB - attributes of file change
* IN_OPEN and IN_CLOSE - open or close of file
* IN_MOVED_FROM and IN_MOVED_TO - when the file is moved or renamed
* IN_DELETE - a file/directory deleted
* IN_CREATE - a file/directory created
* IN_DELETE_SELF - file monitored is deleted
inotify从2.6.13开始就存在于内核中,它的前置程序是dnotify()。我想你可以用它来实现它 您的函数将通过一个共享库部署,该库将位于环境变量LD_PRELOAD指定的目录中 惯例是拦截系统调用,然后在执行完魔术后,将调用传递到实际的系统shlib上。但不必这样做 也许可以看一看文章“”。虽然它是特定于Solaris的,但它也适用于Linux
顺便说一句,这就是大多数内存分析工具(例如Purify)的工作原理。根据KernelTrap.org,重新编译内核以导出sys\u call\u table变量:
// add the following in the file arch/i386/kernel/i386_ksyms.c
extern void* sys_call_table[];
EXPORT_SYMBOL(sys_call_table);
然后只需按照《Linux内核模块编程指南》中的说明操作即可:
这里的源代码是一个
这样一个内核模块。我们想要“间谍”
在某个用户上,并转到printk()
a
每当该用户打开
为此,我们将
系统调用以使用
自己的函数,称为“我们的系统打开”。
此函数用于检查uid(用户的
当前流程的id),如果
它等于我们监视的uid,是吗
调用printk()
以显示
要打开的文件。然后
这样,它调用原始的open()
具有相同参数的函数,以
实际打开文件
init_模块
功能取代
sys\u call\u表中的适当位置
并将原始指针保持在
变量。cleanup_模块
函数
使用该变量进行恢复
一切都恢复正常了,这个
这种方法是危险的,因为
两个内核模块的可能性
更改相同的系统调用。想象一下
我们有两个内核模块,A和B。
A的open
系统调用将是A\u open
而B将B_打开
系统被插入内核
调用被替换为A_open
,该
将调用原始的sys\u打开