21

在没有详细说明原因的情况下,我正在寻找一种干净的(尽可能)方法来替换可加载模块中的内核函数和系统调用。我最初的想法是写一些代码来覆盖一些函数,它会取原来的函数(也许,如果可能的话,调用函数),然后添加一些我自己的代码。关键是我编写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时会访问我的。

我可以通过将我的代码放入适当的函数中轻松(相对地)直接在内核中执行此操作,但我想知道是否有人知道一点 C 魔法,它不一定是可怕的内核(或 C)编码实践,可以实现同样的结果。

#defines 和 typedefs 的想法浮现在脑海,但我无法在脑海中完全破解它。

简而言之:有没有人知道一种有效地覆盖 Linux 内核(从模块)中的功能的方法?

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

4

13 回答 13

7

我意识到这个问题已经存在三年了,但是为了其他尝试做这类事情的人的利益,内核有一个名为kprobes的接口来做你需要的事情。

于 2011-11-10T17:10:22.850 回答
4

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

于 2008-11-14T20:19:26.837 回答
3

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

于 2008-11-14T18:48:25.380 回答
2

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

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

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

不妨看看文章“为乐趣和利润而构建库插入器”。虽然它是特定于 Solaris 的,但它也适用于 Linux。

顺便说一句,这就是大多数内存分析工具(例如 Purify)的工作方式。

于 2009-07-28T23:03:59.913 回答
1

内核中已经做了很多工作来确保不会发生这种情况,特别是不将系统调用表暴露给模块的工作。唯一支持的日志文件访问机制是LSM,但它是面向安全的,前途未卜是一个记录 API 的 PDF,但它可能不是最新的。

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

引用自 Wikipedia ( 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 )。

于 2008-11-17T20:01:44.497 回答
1

我认为您可以为此使用审核

于 2008-11-19T17:16:52.553 回答
1

可能对您有用。

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

于 2009-11-05T09:31:16.187 回答
0

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

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

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

于 2008-11-14T19:04:02.120 回答
0

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

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

于 2008-11-14T20:30:24.927 回答
0

你不想修改现有的系统调用,你想检测它们。这就是SystemTap的用途。如果您真的想通过编写自己的模块来实现并拦截系统调用,我建议您阅读一些 rootkit 文献,但我没有任何方便的链接(尽管想到了 phrack)。

于 2008-11-14T21:05:29.193 回答
0

看看http://www.tldp.org/LDP/lkmpg/2.6/html/x978.html

于 2008-12-11T00:08:44.333 回答
0

根据 KernelTrap.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

于 2009-11-05T09:14:05.517 回答
0

如果共享库调用系统调用,您将无法创建一个更改该系统调用的模块。有关更改系统调用的更多信息,您可能想查看这里 http://www.xml.com/ldd/chapter/book/ 那里有一些关于它们如何更改 open() 系统调用的内容。一个例子在这里 http://tldp.org/LDP/lkmpg/x931.html

于 2015-09-10T19:50:58.467 回答