安全卸载内核模块

时间:2014-05-06 08:19:57

标签: linux linux-kernel locking kernel-module

我必须写LKM,它拦截一些系统调用。 解决方案是:

  1. 查找sys_call_table符号的地址,检查地址是否正确(例如检查sys_call_table [__ NR_close]是否指向sys_close的地址)
  2. 禁用中断
  3. 禁用CR0中的WP位
  4. 将sys_call_table [__ NR_close]更改为我自己的功能
  5. 启用WP位
  6. 启用中断。 加载模块工作正常。
  7. 但是,安全卸载模块呢?

    考虑将sys_call_table恢复到原始状态并卸载模块时的情况 - 如果内核仍然在其他CPU的其他进程的系统调用上下文中执行代码,该怎么办?我将在内核模式下出现页面错误(因为模块的代码段的页面不再可用,因为模块已卸载)。 共享资源是sys_call_table中的条目。如果我可以访问受锁保护的这个条目 - 那么我可以安全地卸载我的模块。

    但是,由于内核系统调用处理程序没有任何这种锁(例如/arch / x86 / kernel / entry_32.S) - 这意味着没有安全的方法来卸载我的模块?这是真的吗?

    UPDATE1

    我需要从2.4内核版本开始获取有关旧内核(其中fanotify(2)不可用)的文件访问的信息。我需要这些信息才能通过防病毒引擎进行访问扫描。

2 个答案:

答案 0 :(得分:1)

您已经确定,一旦您完成此操作,就无法安全地卸载您的模块。这就是为什么以这种方式替换/包装系统调用表条目是不赞成的一个原因。

在大多数最新版本中,sys_call_table不是导出符号 - 至少部分是为了阻止这一点。

理论上 可以支持更强大的系统调用替换机制,但是内核维护者认为整个概念充满了他们拒绝支持的错误和混淆的可能性。它。 (网络搜索将在linux内核邮件列表中显示关于此主题的几个很久以前的争论。)

(在这里讲的是几年前使用完全相同的技术的人。)

你当然可以这样做。然后,您可以“只是冒险”#34;卸载你的模块 - 因此可能导致内核恐慌(但当然它可能会在99%的时间内工作)。或者您根本无法卸载模块(需要重新启动才能升级或卸载)。

答案 1 :(得分:1)

在内核模块中的 uninit 函数结束时,您可以等到所有自定义钩子结束。 这可以使用计数器来实现。 当你的自定义钩子被击中时增加计数器,在它返回之前减少它。 当计数器达到零时,才从 uninit 函数返回。 您还需要锁定计数器变量。