可加载内核模块编程和系统调用拦截

Posted

技术标签:

【中文标题】可加载内核模块编程和系统调用拦截【英文标题】:Loadable Kernel Module Programming and System Call Interception 【发布时间】:2012-09-18 18:17:23 【问题描述】:

假设我们想要拦截退出系统调用并在任何进程调用它时在控制台上打印一条消息。为了做到这一点,我们必须编写自己的假出口系统调用,然后让内核调用我们的假出口函数,而不是原来的出口调用。在我们的假退出调用结束时,我们可以调用原始的退出调用。为了做到这一点,我们必须操作系统调用表数组(sys_call_table)。 有了 sys_call_table 数组,我们可以操纵它来使 sys_exit 入口点指向我们新的假出口调用。我们必须存储一个指向原始 sys_exit 调用的指针,并在我们完成将消息打印到控制台时调用它。源代码:

 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <sys/syscall.h>

 extern void *sys_call_table[];

 asmlinkage int (*original_sys_exit)(int);

 asmlinkage int our_fake_exit_function(int error_code)
 
    /*print message on console every time we
     *are called*/
    printk("HEY! sys_exit called with error_code=%d\n",error_code);

    /*call the original sys_exit*/
    return original_sys_exit(error_code);
 

 /*this function is called when the module is
 *loaded (initialization)*/
 int init_module()
 
     /*store reference to the original sys_exit*/
     original_sys_exit=sys_call_table[__NR_exit];

     /*manipulate sys_call_table to call our
      *fake exit function instead
      *of sys_exit*/
     sys_call_table[__NR_exit]=our_fake_exit_function;
 


 /*this function is called when the module is
   *unloaded*/
 void cleanup_module()
 
     /*make __NR_exit point to the original
      *sys_exit when our module
      *is unloaded*/
     sys_call_table[__NR_exit]=original_sys_exit;
 

当我编译这个程序时,我收到了警告:

警告:“sys_call_table”[/home/roiht/driver/one.ko] 未定义!

我搜索了一下,发现2.5以后的内核版本改变了sys_call表的概念。 那么,我的问题是在新内核版本中执行此操作的替代方法是什么?

【问题讨论】:

【参考方案1】:

如果已使用 EXPORT_SYMBOL() 在内核中显式导出任何内核变量,则可以在模块中使用它。从内核版本 2.6 开始,sys_call_table 的导出已被删除。因此,如果您想使用这种方法,请显式导出变量。按照惯例,在变量声明之后立即进行导出,但我想从定义了该变量的任何文件中导出也可以。要检查该方法是否有效,只需查看“cat /proc/kallsyms”的输出即可。

捕获退出系统调用的另一种方法是在系统调用执行的 sysenter 部分中放置一个挂钩。更多详情请看这里:http://articles.manugarg.com/systemcallinlinux2_6.html

【讨论】:

【参考方案2】:

你可以从你的内核对应的System.map-xxx文件中读取sys_call_table的地址。该文件通常在/boot 目录下,名称为System.map-&lt;kernel-version&gt;,其中kernel-version 是命令uname -r 的结果。您可以使用模块参数将地址传递给您的模块。

【讨论】:

以上是关于可加载内核模块编程和系统调用拦截的主要内容,如果未能解决你的问题,请参考以下文章

linux内核模块中调用shell脚本

从零开始写 OS 内核 - 加载可执行程序

linux内核编程入门--系统调用监控文件访问

虚拟文件系统

Android 逆向函数拦截原理 ( 可执行程序基本结构 | GOT 全局偏移表 | 可执行程序函数调用步骤 )

在 OS 中,为啥可加载内核模块 (LKM) 不需要调用消息传递来进行通信?