1. 课程导入
在Android系统中,内核模块(Kernel Module,常以.ko文件形式存在)是扩展内核功能的重要载体。无论是厂商定制驱动、安全增强模块,还是调试与性能分析工具,都可能以可加载内核模块的形式进入系统。然而,Android从早期版本开始就逐步收紧了内核模块的加载策略:从CONFIG_MODULE_SIG强制签名,到dm-verity与AVB对系统完整性的保护,再到GKI(Generic Kernel Image)与KMI(Kernel Module Interface)的引入,内核模块的签名与加载已经不再是“insmod一下”那么简单,而是一项涉及密码学、构建系统、内核配置与设备启动链的系统工程。
本课程的目标,是帮助学习者建立从“密钥生成”到“模块加载成功”的完整工程能力。我们不会停留在概念层面,而是围绕真实Android设备与AOSP构建环境,逐步拆解签名格式、内核校验逻辑、模块加载路径、SELinux策略、GKI兼容性以及常见失败场景。学完本课程,你应当能够独立完成一个内核模块的签名、部署与加载验证,并能够定位“签名通过但加载失败”这类复合问题。
核心学习目标:
- 理解Android内核模块签名的密码学基础与内核校验机制;
- 掌握
openssl、sign-file、kmod等工具生成与附加签名的完整流程; - 能够根据内核配置(
CONFIG_MODULE_SIG_FORCE、CONFIG_MODULE_SIG_ALL等)判断模块加载策略; - 掌握GKI架构下KMI符号版本与模块兼容性的检查方法;
- 能够分析
dmesg、logcat与SELinux拒绝日志,定位加载失败根因。
一、核心概念:签名、校验与加载的三层关系
Android内核模块的加载过程可以抽象为三个层次:签名层负责为.ko文件生成数字签名并附加到模块尾部;校验层由内核在load_module()路径中完成,依据内置公钥验证签名有效性;策略层则包括SELinux、模块版本魔法(vermagic)、KMI符号白名单以及modules_disabled等运行时限制。三层中任意一层不通过,模块都无法成功加载。
需要特别注意的是,签名有效并不等于加载成功。很多初学者在insmod返回Operation not permitted时误以为签名失败,实际上可能是SELinux上下文错误或vermagic不匹配。因此,本课程会反复强调“分层排查”的方法论。
二、方法流程:从源码到加载的工程链路
一个可加载的内核模块,其工程链路通常包含以下步骤。每一步都有对应的工具与检查点,建议在实操中逐项确认,而不是一次性执行到底。
- 准备内核源码与配置:确认目标内核的
.config中启用了CONFIG_MODULES、CONFIG_MODULE_SIG,并记录CONFIG_MODULE_SIG_KEY指向的密钥路径。 - 生成签名密钥:使用
openssl生成私钥与X.509证书,或直接使用内核构建系统自动生成的signing_key.pem。 - 编译模块:通过
make M=drivers/xxx modules生成未签名的.ko文件。 - 附加签名:使用内核源码中的
scripts/sign-file工具,将签名追加到模块尾部。 - 部署到设备:通过
adb push将.ko推送到/vendor/lib/modules或/lib/modules,并设置正确的SELinux上下文。 - 加载与验证:执行
insmod或modprobe,观察dmesg输出,确认模块状态。
# 典型签名命令示例
scripts/sign-file sha256 \
signing_key.pem \
signing_key.x509 \
my_module.ko
# 查看模块签名信息
modinfo my_module.ko | grep -E "sig|signer"
# 加载并查看内核日志
insmod my_module.ko
dmesg | tail -n 20
三、案例场景:一个厂商驱动的签名加载实战
假设某Android设备需要加载一个自定义传感器驱动sensor_custom.ko。设备运行GKI内核,CONFIG_MODULE_SIG_FORCE=y,且SELinux为enforcing模式。开发者完成编译后直接insmod,得到Operation not permitted。此时按三层模型排查:
- 签名层:
modinfo显示无签名信息,说明模块未附加签名,需重新执行sign-file。 - 校验层:签名后再次加载,
dmesg提示Loading of unsigned module is rejected消失,但出现module verification failed: signature and/or required key missing,说明公钥未内置到内核。 - 策略层:将公钥编译进内核后,加载仍失败,
dmesg显示avc: denied,需为模块文件配置正确的SELinux类型并加载策略。
排查口诀:先看签名有没有,再看公钥对不对,最后看策略放不放。三层依次确认,避免在错误层次反复尝试。
四、误区与复盘:初学者最容易踩的坑
| 常见误区 | 真实现象 | 正确做法 |
|---|---|---|
认为insmod失败就是签名问题 |
实际可能是SELinux或vermagic不匹配 | 先读dmesg,按三层模型定位 |
| 使用错误的私钥签名 | 验签失败,提示key missing | 确认CONFIG_MODULE_SIG_KEY与签名私钥一致 |
| 忽略GKI的KMI限制 | 模块加载后符号解析失败 | 检查模块是否使用白名单内符号 |
直接push到/system分区 |
分区只读或AVB校验失败 | 使用/vendor/lib/modules或/data调试路径 |
| 忘记更新模块依赖 | modprobe找不到模块 |
执行depmod生成modules.dep |
重要提醒:在生产设备上,CONFIG_MODULE_SIG_FORCE通常为y,任何未签名或签名不匹配的模块都会被内核直接拒绝。调试阶段可以在开发版内核上临时关闭强制签名,但绝不可将此配置带入量产版本。
五、实战路线图:30章学习路径总览
本课程后续章节将沿着“基础理论 → 工具实操 → 内核机制 → GKI适配 → 故障排查 → 工程化落地”的主线展开。建议学习者按照以下节奏推进:
- 第1–6章:密码学基础、内核模块格式、签名算法与密钥管理;
- 第7–12章:
sign-file、openssl、Kbuild集成与自动化签名脚本; - 第13–18章:内核校验源码分析、
load_module()路径、公钥内置与密钥环; - 第19–24章:GKI架构、KMI符号版本、模块兼容性与ABI检查;
- 第25–30章:SELinux策略、加载失败案例库、性能与安全权衡、量产工程规范。
学习过程中,建议始终围绕一个真实的内核模块进行练习,从生成密钥到最终加载成功,完整走通一遍。只有亲手处理过“签名有效但加载失败”的复合问题,才能真正掌握Android内核模块签名与加载工程的核心能力。