Linux内核模块
内核模块的一些基本概念
内核模块是实现某一个功能的一段内核代码,这段代码可以在内核的运行过程中动态加载到内核,从而增加内核功能。这段代码可以单独编译,不用对整个内核重新进行编译。内核模块不能独立运行, 在运行时它被链接到内核作为内核的一部分在内核空间运行。编译好的模块在本质上是一个elf可重定位文件,后缀名为.ko。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| myslqyr@myslqyr-ubuntu:~/Desktop/module$ readelf -h hello.ko ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 176672 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 48 Section header string table index: 47
|
一个简单的内核模块示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h>
MODULE_LICENSE("GPL"); MODULE_AUTHOR("myslqyr"); MODULE_DESCRIPTION("A simple hello world kernel module"); MODULE_VERSION("1.0");
static int __init hello_init(void) { printk(KERN_INFO "Hello World kernel module loaded.\n"); return 0; }
static void __exit hello_exit(void) { printk(KERN_INFO "Hello World kernel module unloaded.\n"); }
module_init(hello_init); module_exit(hello_exit);
|
makefile如下:
1 2 3 4 5 6 7 8 9 10
| obj-m += hello.o
DIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd)
all: make -C $(DIR) M=$(PWD) modules
clean: make -C $(DIR) M=$(PWD) clean
|
sudo insmod hello.ko可以加载模块,sudo rmmod hello卸载模块。
内核模块签名
大多数发行版在编译内核时没有添加CONFIG_MODULE_SIG_FORCE这个内核配置项,这个配置项用于检查内核模块的签名。如果内核模块的签名无效,内核将拒绝加载该模块。大部分的电脑也默认没有使用uefi security boot,开启security boot也会强制模块签名。当强制开启内核模块签名时,会拒绝加载没有签名或者签名预期值不符的模块。
1 2 3 4 5 6 7
| cat /boot/config-$(uname -r) | grep MODULE_SIG ... CONFIG_MODULE_SIG=y
... mokutil --sb-state SecureBoot disabled
|
内核模块签名的流程
首先生成一对公钥和私钥。
1 2 3 4 5 6
| openssl req -new -x509 -newkey rsa:2048 \ -keyout MOK.key \ -out MOK.crt \ -nodes \ -days 36500 \ -subj "/CN=My Kernel Module Signing/"
|
然后对hello.ko进行签名。签名工具使用的是/usr/src/linux-headers-$(uname -r)/scripts/sign-file。
1 2 3 4 5
| /usr/src/linux-headers-$(uname -r)/scripts/sign-file \ sha256 \ MOK.key \ MOK.crt \ hello.ko
|
这时候我们运行modinfo hello.ko可以看到hello.ko带上了签名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| modinfo hello.ko filename: ? version: 1.0 description: A simple hello world kernel module author: myslqyr license: GPL srcversion: D98FC66A28194B60CF68194 depends: name: hello retpoline: Y vermagic: 6.14.0-33-generic SMP preempt mod_unload modversions sig_id: PKCS#7 signer: My Kernel Module Signing sig_key: 2E:65:EA:52:3B:75:... sig_hashalgo: sha256 signature: 13:87:A6:A7:6C:32:...
|
之后要给内核导入公钥。
1 2
| openssl x509 -in MOK.crt -outform DER -out MOK.der sudo mokutil --import MOK.der
|