在Linux内核中使用模块覆盖function

没有深入的细节为什么 ,我正在寻找一个干净(尽可能)的方式来取代可加载模块内核函数和系统调用。 我最初的想法是编写一些代码来覆盖一些函数,这将采取原来的function(也许,如果可能, 调用函数),然后添加一些我自己的代码。 关键是我编写的函数必须具有原始函数的名称,所以其他代码在尝试访问它时将访问我的代码。

我可以很容易地(相对)直接在内核中执行此操作,只需将我的代码投入到相应的函数中,但是我想知道是否有人知道一点点C魔法不一定是可怕的内核(或C)编码实践,同样的结果。

#defines和typedefs的想法浮现在脑海中,但我无法在脑海中把它揪出来。

总之:有没有人知道一种方法来有效地覆盖Linux内核(从一个模块)的function?

编辑:由于它被问到,我基本上想要在内核中logging某些函数(创build/删除目录等),但出于理智的缘故,一个可加载的模块似乎是有道理的,而不是写一个大的补丁内核代码并在每次更改时重新编译。 向内核添加的最小代码是可以的,但是我想把大部分的工作转移到一个模块中。

我意识到这个问题已经三年了,但是为了其他人的利益,内核还有一个叫做kprobes的接口来完成你所需要的工作。

您可能希望挂钩系统调用 (PDF链接),这将有效地让您记录调用内核函数的用户进程。 如果你真的想记录核心功能的内核使用,你想看看内核功能跟踪 。

我不完全相信我明白你想做什么,但我认为ksplice可能是一个很好的解决方案。 它还在开发中,所以我现在不知道它是否处于任何可用状态。

你看过使用LD_PRELOAD部署你的功能吗?

你的函数将通过一个共享库来部署,该共享库将位于由环境变量LD_PRELOAD指定的目录中。

惯例是你拦截系统调用,然后在执行你的魔法之后,将调用传递给实际的系统shlib。 但是你不必这样做。

也许看看文章“ 建立图书馆插件为乐趣和利润 ”。 虽然它是特定于Solaris的,但它也适用于Linux。

顺便说一句,这是大多数内存分析工具,如Purify,如何工作。

在内核中已经做了很多工作,以确保不会发生,特别是不要将syscall表暴露给模块。 记录文件访问的唯一支持机制是LSM ,但是它面向安全并且具有不确定的未来 。 这是一个PDF文件的API,但它可能不是最新的。

inotify是监视文件的创建,删除和修改的一种比试图破坏内核系统调用函数更好的方法,但它可以在用户空间中运行。

引自维基百科( http://en.wikipedia.org/wiki/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( http://en.wikipedia.org/wiki/Dnotify )。

我认为你可以使用审计

这可能会对你有所帮助。

基本上,由于系统调用表不是直接导出到较新的内核中,所以必须进行一些搜索来确定它自己的位置。 然后你可以拦截你选择的系统调用并操作它们。 但是替换其他的内核函数将会困难得多,除非它们中的一部分以系统调用的方式组织起来(它们出现在一些调度表等上) – 这一点并不常见。

大多数文件系统工作都是在模块中完成的,假设文件系统代码是作为一个模块构建的,而不是内置到内核中(这意味着“真正的”答案取决于内核构建选项)。

假设你想要登录的位全是与文件系统相关的,而且那些文件系统例程是作为模块构建的,那么你应该能够改变你感兴趣的文件系统模块,并重新加载它们。

如果这些假设不是真的,或者不能成立的话,事情就会变得更加棘手,我实在无法进一步指出。

既然你只想记录这些调用(也就是说你不会真正覆盖它们),而且对内核代码的一些修改是可以接受的,最简单的方法就是给你感兴趣的每个函数添加一个钩子(使用通知链或甚至一个普通的函数指针)。 你的模块然后简单地注册到你添加的所有钩子(并在卸载时从它们注销)。

其他人已经完成了为你添加钩子的工作。

你不想修改现有的系统调用,你想要测试它们。 这是SystemTap的用途。 如果你确实想要通过编写你自己的模块来实现这个硬性的方法并拦截系统调用,我建议你阅读一些rootkit的文章,但是我没有任何方便的链接(虽然想起了phrack)。

根据coreelTrap.org 你可以做一个简单的补丁和重新编译你的内核来导出sys_call_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()一条消息。 为此,我们用自己的函数替换系统调用来打开一个文件,名为our_sys_open 。 这个函数检查当前进程的uid(用户id),如果它等于我们监视的uid,它调用printk()来显示要打开的文件的名字。 然后,无论哪种方式,它调用原来的open()函数具有相同的参数,实际上打开文件。

init_module函数替换sys_call_table的相应位置,并将原始指针保存在一个变量中。 cleanup_module函数使用该变量将一切恢复到正常状态。 这种方法是危险的,因为两个内核模块可能改变相同的系统调用。 假设我们有两个内核模块A和B.A的open系统调用是A_open ,B是B_open 。 现在,当A被插入到内核中时,系统调用被替换为A_open ,当它完成时它将调用原始的sys_open 。 接下来,B被插入到内核中,用B_open替换系统调用,当它完成时,它将调用它认为是原始系统调用A_open

如果共享库调用一个系统调用,那么你将不会创建一个模块来改变这个系统调用。 有关更改系统调用的更多信息,您可能需要查看http://www.xml.com/ldd/chapter/book/ ,他们如何更改open()系统调用的内容。 http://tldp.org/LDP/lkmpg/x931.html就是一个例子