3. 固件与Bootloader:UEFI固件初始化流程、Bootloader(如GRUB、shim)在安全启动中的角色
安全启动这条链,说白了就是从按下电源键到操作系统内核跑起来,每一步都要验签。哪一步断了,机器就黑在那。我排查过太多这种“断链”的案例,今天咱们就把UEFI固件初始化流程和Bootloader的角色彻底捋清楚。
3.1 UEFI固件初始化流程
UEFI固件启动,不是一上来就找硬盘的。它分几个阶段,每个阶段都有自己的签名数据库和校验逻辑。我个人习惯把流程拆成三块:SEC/PEI、DXE、BDS。
核心要点:UEFI安全启动的信任根(Root of Trust)在固件内部。从SEC阶段开始,每一段代码执行前都要验证上一段的签名。如果PEI阶段被篡改,DXE阶段根本不会跑。
3.1.1 SEC(安全验证)阶段
这是CPU复位后的第一条指令。SEC阶段做的事情很简单:验证PEI阶段的代码签名,然后跳转。我在项目中遇到过一台机器,SEC阶段直接卡死,最后发现是固件芯片的SPI Flash被腐蚀,读出来的签名全是0xFF。嗯,硬件问题往往最隐蔽。
3.1.2 PEI(EFI预初始化)阶段
PEI阶段负责初始化CPU、内存、芯片组。这个阶段会加载PEIM(PEI模块),每个PEIM都要验签。如果内存初始化失败,机器连报错的机会都没有——直接黑屏。你想想看,这时候连串口都还没起来,排查全靠示波器量SPI总线。
3.1.3 DXE(驱动执行环境)阶段
DXE阶段是UEFI固件最复杂的部分。它会加载各种驱动:SATA、NVMe、USB、网卡。每个驱动都是一个EFI文件,必须通过签名验证。我记得有一次,客户说换了块NVMe硬盘后安全启动就过不去。查了半天,发现是NVMe驱动固件里嵌了一个过期的签名证书。
3.1.4 BDS(启动设备选择)阶段
BDS阶段负责枚举启动设备,读取启动项,然后加载Bootloader。这里有个关键点:BDS会检查启动项的签名是否在db(允许签名数据库)里。如果不在,直接拒绝启动。
避坑指南:我曾经在BDS阶段踩过一个坑——UEFI固件默认只信任Microsoft签名的Bootloader。如果你用自编译的GRUB,必须先把你的证书导入db。否则BDS会直接报“Security Violation”。
3.2 Bootloader在安全启动中的角色
Bootloader是固件和操作系统之间的桥梁。在安全启动体系里,它既是“被验证者”,也是“验证者”。
为什么这么说?因为Bootloader既要被UEFI固件验签,又要负责验签操作系统内核。如果Bootloader本身被篡改,那它验签内核的结果就毫无意义。
3.2.1 GRUB的角色
GRUB是Linux世界最常用的Bootloader。在安全启动场景下,GRUB必须被签名。我建议你使用Microsoft签名的shim来引导GRUB,而不是直接签名GRUB。原因很简单:GRUB更新频繁,每次更新都要重新签名,太麻烦。
GRUB加载后,会读取配置文件grub.cfg。这里有个安全漏洞:如果grub.cfg没有被签名,攻击者可以修改它来加载恶意内核。所以,安全启动环境下,GRUB必须启用check_signatures和enforce_signatures选项。
# 在grub.cfg中启用签名验证
set check_signatures=enforce
export check_signatures
# 加载内核时自动验签
linux /vmlinuz-5.15.0-generic root=/dev/sda1
initrd /initrd.img-5.15.0-generic
警告:GRUB的check_signatures只验证内核和initrd的签名,不验证grub.cfg本身。如果你修改了grub.cfg,必须重新生成签名。否则GRUB会报“bad signature”错误。
3.2.2 shim的角色
shim是一个很小的Bootloader,由Microsoft签名。它的作用就是充当“中间人”——UEFI固件信任shim,shim信任GRUB。这样,你只需要维护shim和GRUB之间的信任关系,不用每次都找Microsoft签名。
shim的工作原理很简单:
- UEFI固件加载shim.efi,验证Microsoft签名。
- shim读取自己的签名数据库(MOK,Machine Owner Key)。
- shim验证GRUB的签名是否在MOK里。
- 如果验证通过,shim把控制权交给GRUB。
我在项目中遇到过shim版本不兼容的问题。shim 15.3和GRUB 2.06配合得很好,但shim 15.4改了内部接口,导致GRUB 2.04直接崩溃。所以,升级shim时一定要同步升级GRUB。
3.2.3 MOK(Machine Owner Key)管理
MOK是shim用来验证GRUB和内核的本地签名数据库。你可以通过mokutil工具管理MOK。
# 查看当前MOK列表
mokutil --list-enrolled
# 导入新的MOK(需要重启后确认)
mokutil --import /path/to/my.der
# 删除MOK
mokutil --delete <hash>
个人经验:MOK导入后,重启时会进入shim的MOK管理界面。你必须手动选择“Enroll MOK”并确认。很多工程师在这里卡住,以为导入失败。其实shim是故意这么设计的——防止恶意软件静默导入签名。
3.3 固件与Bootloader的信任链
把上面这些串起来,就是一条完整的信任链:
这张图我画了很多次。每次排查安全启动问题,我都会对着这张图从右往左查——先看内核签名对不对,再看GRUB签名,最后看固件。这样效率最高。
3.4 常见故障与排查思路
| 故障现象 | 可能原因 | 排查方向 |
|---|---|---|
| 启动时黑屏,无任何输出 | SEC/PEI阶段崩溃,或固件签名验证失败 | 检查SPI Flash完整性,用编程器读取固件 |
| 显示“Security Violation” | Bootloader签名不在db或MOK中 | 检查Bootloader签名,重新导入MOK |
| GRUB菜单出现,但加载内核时报“bad signature” | 内核或initrd签名无效 | 重新签名内核,或更新MOK |
| shim启动后直接重启 | shim版本与GRUB不兼容 | 升级GRUB到与shim匹配的版本 |
注意:如果你在UEFI Shell下手动启动Bootloader,记得先执行SecureBootConfig查看当前安全启动状态。很多工程师忽略这一步,直接敲fs0:\EFI\grubx64.efi,结果报错后一头雾水。