驱动签名与部署:从测试到上线的完整链路
驱动开发做到最后,最头疼的往往不是代码本身,而是怎么让系统乖乖认你这个驱动。Windows 要签名,Linux 也要签名,部署方式还完全不一样。今天我就把这两条线彻底讲透。
核心要点:驱动签名不是形式主义,是操作系统对驱动身份的核验。没签名的驱动,Windows 直接拒绝加载,Linux 也会弹警告。
一、Windows 驱动签名流程
Windows 的驱动签名分两种:测试签名和正式签名。说白了,一个是你开发时自己玩的,一个是给用户用的。
1.1 测试签名(开发阶段)
我刚开始做驱动时,觉得测试签名很麻烦。后来发现,其实就三步:
- 生成测试证书:用 MakeCert 或 Visual Studio 自带的工具
- 签名驱动文件:用 SignTool 给 .sys 文件打上签名
- 开启测试模式:在目标机器上运行
bcdedit /set testsigning on
我的习惯:测试签名时,我会在 INF 文件中加上 CatalogFile 字段,这样签名后会自动生成 .cat 文件,方便后续验证。
具体命令长这样:
# 生成测试证书
makecert -r -pe -ss PrivateCertStore -n "CN=MyTestCert" MyTestCert.cer
# 签名驱动
signtool sign /v /s PrivateCertStore /n "MyTestCert" /t http://timestamp.digicert.com mydriver.sys
# 开启测试模式(管理员权限)
bcdedit /set testsigning on
我曾经踩过的坑:测试签名后一定要重启机器,否则系统不会识别。还有,测试模式在 Windows 10 以上版本会显示水印,这是正常的。
1.2 正式签名(发布阶段)
正式签名需要购买 EV 证书(扩展验证证书),价格不便宜,但这是必经之路。流程如下:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 提交驱动到 Windows Hardware Dev Center | 需要微软账号和合作伙伴中心权限 |
| 2 | 等待微软认证 | 一般 3-5 个工作日 |
| 3 | 下载签名后的驱动包 | 微软会返回 .cat 文件和签名后的 .sys |
| 4 | 交叉签名 | 用 EV 证书对驱动进行交叉签名,确保兼容性 |
你想想看,为什么微软要搞这么复杂?说白了,就是为了防止恶意驱动。我见过有人用测试签名驱动伪装成正式驱动,结果系统直接蓝屏。
二、Linux 驱动模块签名
Linux 的模块签名验证(Module Signature Verification)从内核 3.7 开始引入。它的逻辑和 Windows 不太一样——Linux 更信任内核自带的公钥。
2.1 签名流程
Linux 驱动签名其实更简单,就两步:
- 生成密钥对:用
openssl生成私钥和公钥 - 签名模块:用
sign-file工具给 .ko 文件签名
# 生成密钥对
openssl req -new -x509 -newkey rsa:2048 -keyout signing_key.pem -out cert.pem -nodes -days 365
# 签名模块
/usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 signing_key.pem cert.pem mydriver.ko
# 验证签名
modinfo mydriver.ko | grep signature
我建议:把公钥编译进内核,这样系统启动时自动信任你的签名。具体做法是在内核配置中启用 CONFIG_MODULE_SIG 并指定公钥路径。
2.2 内核配置要求
要让模块签名生效,内核必须开启以下选项:
CONFIG_MODULE_SIG=y:启用模块签名CONFIG_MODULE_SIG_FORCE=y:强制验证签名(可选)CONFIG_MODULE_SIG_ALL=y:自动签名所有模块
嗯,这里要注意:如果内核开启了强制验证,没签名的模块直接加载失败。我遇到过客户反馈驱动装不上,最后发现是内核配置问题。
三、驱动安装与卸载
安装卸载这块,Windows 和 Linux 完全是两套逻辑。Windows 靠 INF 文件,Linux 靠 insmod/rmmod。
3.1 Windows:INF 文件
INF 文件是 Windows 驱动的身份证。它告诉系统:这个驱动叫什么、支持哪些硬件、怎么安装。
一个典型的 INF 文件结构:
[Version]
Signature="$WINDOWS NT$"
Class=System
ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
[Manufacturer]
%MfgName%=MyDevice,NTamd64
[MyDevice.NTamd64]
%DeviceDesc%=MyDevice_Inst, PCI\VEN_1234&DEV_5678
[MyDevice_Inst]
CopyFiles=MyDevice_CopyFiles
[MyDevice_CopyFiles]
mydriver.sys
[DestinationDirs]
MyDevice_CopyFiles=12
我曾经犯过的错:INF 文件中的 ClassGuid 写错了,导致驱动被归类到错误设备类别,安装后设备管理器里找不到。后来我养成了习惯,每次写完 INF 先用 InfVerif 工具检查一遍。
安装命令:
# 命令行安装
pnputil /add-driver mydriver.inf /install
# 或者右键 INF 文件选择"安装"
卸载命令:
# 查看已安装的驱动
pnputil /enum-drivers
# 删除指定驱动
pnputil /delete-driver oem0.inf
3.2 Linux:insmod/rmmod
Linux 的驱动安装就直白多了。模块编译好之后,直接加载:
# 加载模块
insmod mydriver.ko
# 查看已加载模块
lsmod | grep mydriver
# 卸载模块
rmmod mydriver
# 查看模块信息
modinfo mydriver.ko
我的习惯:调试阶段用 insmod,正式部署用 modprobe。modprobe 会自动处理依赖关系,insmod 不会。比如你的驱动依赖另一个模块,insmod 会报错,modprobe 会自动加载依赖。
自动加载配置:
# 将模块复制到标准目录
cp mydriver.ko /lib/modules/$(uname -r)/kernel/drivers/
# 更新模块依赖
depmod -a
# 设置开机自动加载
echo "mydriver" >> /etc/modules-load.d/mydriver.conf
四、知识体系总览
下面这张图把整个驱动签名与部署的脉络梳理清楚了:
这张图把 Windows 和 Linux 的签名部署流程做了对比。你会发现,虽然实现方式不同,但核心逻辑是一样的——都是通过签名机制建立信任链。
总结一下:驱动签名不是可选项,是必选项。Windows 这边,测试签名帮你快速迭代,正式签名帮你上线发布。Linux 这边,模块签名配合内核配置,能有效防止恶意模块加载。安装卸载虽然命令不同,但思路相通——都是把驱动文件放到系统认的地方,然后告诉系统"我来了"。
想要更多资料,联系可可