目录
1. Linux模块(LKM)简介 2. 使用Linux模块 3. LKM模块加载原理 4. LKM模块卸载原理 5. Monitor init_module syscall By Kprobe
1. Linux模块(LKM)简介
模块是一种向linux内核添加"设备驱动程序"、"文件系统"、"其他组件"的有效方法,而无须重新编译内核或重启系统,这消除了许多限制,同时带来了很多的优点
1. 通过使用模块,内核程序员能够预先编译大量驱动程序,而不会致使内核映像的尺寸发生膨胀。在自动检测硬件或用户提示后,安装例程会选择适当的模块并将其添加到内核中 2. 内核开发者可以将试验性的代码打包到模块中,模块可疑卸载,修改代码或重新打包后再重新加载,这使得可以快速测试新特性,无需每次都重启系统 3. 模块(LKM)可疑无缝地插入到内核中,同时模块也可以导出一些函数,可以由其他核心模块(以及持久编译到内核中的代码)使用。在模块代码需要卸载时,模块和内核剩余部分之间的关联会自动终止
0x1: 模块的依赖关系和引用
如果模块B使用了模块A提供的函数,那么模块A和模块B之间就存在关系,可以从两个方面来看这种关系
1. 模块B依赖模块A 除非模块A已经驻留在内核内存,否则模块B无法装载 2. 模块B引用模块A 除非模块B已经移除,否则模块A无法从内核移除,在内核中,这种关系称之为"模块B使用模块A"
"struct module_use"和"struct module->module_which_use_me"这两个结果共同组合和保证了内核模块中的依赖关系。
如果模块B使用了模块A提供的函数,那么模块A和模块B之间就存在关系,可以从两个方面来看这种关系
1. 模块B依赖模块A 除非模块A已经驻留在内核内存,否则模块B无法装载 2. 模块B引用模块A 除非模块B已经移除,否则模块A无法从内核移除,在内核中,这种关系称之为"模块B使用模块A"
对每个使用了模块A中函数的模块B,都会创建一个module_use结构体实例,该实例将被添加到模块A(被依赖的模块)的module实例中的modules_which_use_me链表中,modules_which_use_me指向模块B的module实例
我们在编写并加载LKM模块的时候,一定要注意模块间的依赖关系,有时候还需要分步骤单独进行指定模块的加载,才能保证模块间的依赖关系的正确连接
2. 使用Linux模块
0x1: 模块的添加
从用户的角度来看,模块可以通过以下两个"指令”进行添加
1. modprobe 它考虑了各个模块之间可能出现的依赖性(在一个模块依赖于一个或多个合作者模块的功能时),modprobe在识别出目标模块所依赖的模块之后,在内核也会使用insmod(即modprobe只是对insmod的一个包装) 2. insmod insmod只加载一个单一的模块到内核中,且该模块只信赖内核中已经存在的代码(不管是通过模块动态加载的、还是持久编译到内核中的)
从内核系统的角度来看,模块的加载可以通过以下方法完成
1. init_module() init_module()是一个系统调用,用户空间的工具只需要提供二进制数据,所有其他工作(重定位、解决引用问题)由内核自身完成 2. request_module() request_module()不是系统调用,它用于从内核端加载模块,它不仅用于加载模块,还用于实现热插拔功能
0x2: 模块的移除
从用户的角度来看,模块可以通过以下"指令”进行删除
1. rmmod
从内核系统的角度来看,模块的卸载可以通过以下方法完成
1. delete_module() delete_module()是一个系统调用,它用于从内核移除一个模块,前提是该模块代码不再使用,且其他模块也不再使用该模块导出的函数(即不能有依赖关系)
3. LKM模块加载原理
LKM模块的加载的大部分逻辑都在init_module()中,ring3的insmod、modprobe仅仅负责传递一个二进制数据,本文只关注init_module这个系统调用的代码逻辑,关于整个insmod模块加载过程的原理分析,请参阅
http://files.cnblogs.com/LittleHann/Modultils%E5%B7%A5%E5%85%B7%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E2%80%94%E2%80%94insmod%E7%AF%87.pdf
0x1: 代码流程
init_module()系统调用是用户空间和内核之间用于装载新模块的接口,它的大致流程如下