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不匹配。因此,本课程会反复强调“分层排查”的方法论。

Android内核模块签名与加载工程 · 知识框架 ① 签名层 · Signing 生成密钥对 编译 .ko 模块 sign-file 附加签名 输出已签名 .ko ② 校验层 · Verification 内核内置公钥 解析模块签名段 RSA/ECDSA 验签 签名有效/无效 ③ 策略层 · Policy SELinux 上下文 vermagic 匹配 KMI 符号白名单 modules_disabled 模块加载成功 / 失败定位

二、方法流程:从源码到加载的工程链路

一个可加载的内核模块,其工程链路通常包含以下步骤。每一步都有对应的工具与检查点,建议在实操中逐项确认,而不是一次性执行到底。

  1. 准备内核源码与配置:确认目标内核的.config中启用了CONFIG_MODULES、CONFIG_MODULE_SIG,并记录CONFIG_MODULE_SIG_KEY指向的密钥路径。
  2. 生成签名密钥:使用openssl生成私钥与X.509证书,或直接使用内核构建系统自动生成的signing_key.pem。
  3. 编译模块:通过make M=drivers/xxx modules生成未签名的.ko文件。
  4. 附加签名:使用内核源码中的scripts/sign-file工具,将签名追加到模块尾部。
  5. 部署到设备:通过adb push将.ko推送到/vendor/lib/modules或/lib/modules,并设置正确的SELinux上下文。
  6. 加载与验证:执行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内核模块签名与加载工程的核心能力。