驱动签名与部署:从测试到上线的完整链路

驱动开发做到最后,最头疼的往往不是代码本身,而是怎么让系统乖乖认你这个驱动。Windows 要签名,Linux 也要签名,部署方式还完全不一样。今天我就把这两条线彻底讲透。

核心要点:驱动签名不是形式主义,是操作系统对驱动身份的核验。没签名的驱动,Windows 直接拒绝加载,Linux 也会弹警告。

一、Windows 驱动签名流程

Windows 的驱动签名分两种:测试签名和正式签名。说白了,一个是你开发时自己玩的,一个是给用户用的。

1.1 测试签名(开发阶段)

我刚开始做驱动时,觉得测试签名很麻烦。后来发现,其实就三步:

  1. 生成测试证书:用 MakeCert 或 Visual Studio 自带的工具
  2. 签名驱动文件:用 SignTool 给 .sys 文件打上签名
  3. 开启测试模式:在目标机器上运行 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 驱动签名其实更简单,就两步:

  1. 生成密钥对:用 openssl 生成私钥和公钥
  2. 签名模块:用 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 驱动 测试签名(MakeCert + SignTool) 正式签名(EV证书 + 微软认证) 安装:INF文件 + pnputil 卸载:pnputil /delete-driver 模块签名(openssl + sign-file) 内核配置(CONFIG_MODULE_SIG) 安装:insmod / modprobe 卸载:rmmod 核心目标:让操作系统信任你的驱动,安全加载到内核空间 不同体系

这张图把 Windows 和 Linux 的签名部署流程做了对比。你会发现,虽然实现方式不同,但核心逻辑是一样的——都是通过签名机制建立信任链。

总结一下:驱动签名不是可选项,是必选项。Windows 这边,测试签名帮你快速迭代,正式签名帮你上线发布。Linux 这边,模块签名配合内核配置,能有效防止恶意模块加载。安装卸载虽然命令不同,但思路相通——都是把驱动文件放到系统认的地方,然后告诉系统"我来了"。

想要更多资料,联系可可