使用Linux内核中的模块覆盖功能

时间:2008-11-14 18:04:46

标签: c linux module kernel

没有深入了解 why 的细节,我正在寻找一种干净(尽可能)的方法来替换可加载模块的内核函数和系统调用。我最初的想法是编写一些代码来覆盖一些函数,这些函数将采用原始函数(如果可能的话,调用函数),然后添加一些我自己的代码。关键是我写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时将访问我的。

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

想到#defines和typedef的想法,但我不能完全破解它。

简而言之:有没有人知道如何有效地覆盖Linux内核中的函数(来自模块)?

编辑:因为有问题,我基本上想要从内核中记录某些功能(创建/删除目录等),但为了理智,一个可加载的模块似乎有意义而不是必须为内核代码编写一个大补丁并重新编译每个更改。内核添加的代码量很少,但是我希望将大部分工作卸载到模块中。

13 个答案:

答案 0 :(得分:6)

我意识到这个问题已经有三年了,但为了其他人尝试做这类事情的好处,内核有一个名为kprobes的界面来完成你需要的工作。

答案 1 :(得分:4)

您可能希望hook the system calls(PDF链接),这将有效地让您记录调用内核函数的用户进程。如果你真的想记录内核使用内核函数,你想查看kernel function trace

答案 2 :(得分:3)

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

答案 3 :(得分:3)

您是否考虑过使用LD_PRELOAD部署功能?

您的函数将通过共享库进行部署,该库将存在于由环境变量LD_PRELOAD指定的目录中。

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

也许看看文章“Building library interposers for fun and profit”。虽然它是特定于Solaris的,但它也适用于Linux。

BTW这就是大多数内存分析工具的方法,例如:净化,工作。

答案 4 :(得分:1)

内核已经做了很多工作来确保不会发生这种情况,特别是不将syscall表公开给模块。记录文件访问的唯一受支持机制是LSM,但它面向安全性并且具有不确定futureHere是用于记录API的PDF,但可能不是最新的。

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
自2.6.13起,

inotify存在于内核中,其前身是dnotify(http://en.wikipedia.org/wiki/Dnotify)。

答案 5 :(得分:1)

我认为你可以使用audit

答案 6 :(得分:1)

This可能对您有用。

基本上,由于系统调用表不是直接在较新的内核中导出,因此您必须进行一些搜索以自行确定其位置。然后,您可以拦截您选择的系统调用并操纵它们。但是,替换其他内核函数会更加困难,除非它们中的一些组织方式与系统调用相同(它们出现在某些调度表等) - 这一点并不常见。

答案 7 :(得分:0)

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

假设您要记录的位与文件系统相关,并且这些文件系统例程构建为模块,您应该能够更改您感兴趣的文件系统模块,并重新加载它们。

如果这些假设不正确,或者不能成真,那么事情显然变得更加棘手,而且我真的无法进一步指出你。

答案 8 :(得分:0)

由于你只想记录调用(即你实际上不会覆盖它们),并且可以接受对内核代码的少量更改,最简单的方法是为你感兴趣的每个函数添加一个钩子。 (使用通知程序链或甚至是普通函数指针)。然后,您的模块只会将自己注册到您添加的所有挂钩(并在卸载时从它们取消注册)。

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

答案 9 :(得分:0)

您不想修改现有的系统调用,而是想要对它们进行检测。这就是SystemTap的用途。如果你真的想通过编写自己的模块来解决困难和拦截系统调用,我建议你阅读一些rootkit文献,但我没有任何链接方便(虽然想到了phrack)。

答案 10 :(得分:0)

答案 11 :(得分:0)

根据KernelTrap.org you can do a simple patch并重新编译内核以导出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内核模块编程指南中的this procedure for replacing system calls进行操作:

  

这里的源代码就是一个例子   这样的内核模块。我们想'间谍'   对某个用户,以及printk() a   用户打开时的消息   文件。为此,我们取代了   系统调用打开我们的文件   自己的函数,称为our_sys_open。   此函数检查uid(用户的   id)当前进程,如果   它等于我们侦察的uid,它   调用printk()来显示名称   要打开的文件。然后,要么   方式,它调用原始open()   具有相同参数的函数   实际上打开文件。

     

init_module函数取代了   sys_call_table中的适当位置   并将原始指针保持在一个   变量。 cleanup_module功能   使用该变量进行恢复   一切恢复正常。这个   方法很危险,因为   两个内核模块的可能性   改变相同的系统调用。想像   我们有两个内核模块,A和B.   一个open系统调用将是A_open   和B将是B_open。现在,当A是   插入内核,系统   呼叫被替换为A_open,其中   会在什么时候调用原始sys_open   完成。接下来,插入B   内核,它取代了系统   与B_open通话,这将调用什么   它认为是最初的系统调用,   A_open,当它完成时。

答案 12 :(得分:0)

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