使用可加载内核模块向 Linux 3.2.x 添加新的系统调用

使用可加载内核模块向 Linux 3.2.x 添加新的系统调用

我想在 linux 内核 3.2.x 中添加一个特定的新系统调用,但作为可加载的内核模块(因为我不想一次又一次地重新编译内核)

我阅读了互联网上以及 SO 上的许多帖子,有些地方声称将系统调用实现为可加载模块是不可能的,而另一些地方则说这是可能的。

是哪一个?如果可以的话,如何实现呢?

答案1

这是不可能的,因为系统调用表(称为sys_call_table)是一个静态大小的数组。它的大小在编译时由注册的系统调用的数量决定。这意味着没有空间容纳另一个。

arch/x86/kernel/syscall_64.c您可以检查文件中sys_call_table定义的x86 架构的实现。它的大小正好是__NR_syscall_max+1.__NR_syscall_max定义为arch/x86/kernel/asm-offsets_64.csizeof(syscalls) - 1它是最后一个系统调用的编号),其中syscall是包含所有系统调用的表。

一种可能的解决方案是重复使用一些现有的(或已弃用的系统调用号,如果您的体系结构有的话,请参见sys_setaltroot示例)系统调用号,因为这不需要更多的内存空间。某些架构的系统调用表中也可能存在漏洞(例如 x86 的 64 位版本),因此您也可以使用它。

如果您正在开发新的系统调用并且只想避免在试验时重新启动,则可以使用此技术。您必须定义新的系统调用,在系统调用表中找到现有条目,然后从模块中替换它。

从内核模块执行此操作并不简单,因为从版本 2.6 开始,内核不会导出sys_call_table到模块(导出此符号的最后一个内核版本是2.5.41)。

解决此问题的一种方法是更改​​内核以将sys_call_table符号导出到模块。为此,您必须将以下两行添加到kernel/kallsyms.c(不要在生产机器上这样做):

extern void *sys_call_table;
EXPORT_SYMBOL(sys_call_table);

另一种技术是动态查找系统调用表。您遍历内核内存,将每个字与指向已知系统调用函数的指针进行比较。由于您知道该系统调用在表中的偏移量,因此您可以计算表的起始地址。

答案2

不幸的是,您无法将系统调用作为可加载模块添加到内核。每次添加新的系统调用时,您都必须忍受编译内核的痛苦。

相关内容