4、启动度量流程:BIOS/UEFI阶段度量、Option ROM度量、Bootloader度量

启动度量,说白了就是给固件的启动过程装上一台「监控摄像机」。从按下电源键到操作系统内核跑起来,这中间每一个环节的代码,都得被记录、被验证。我个人习惯把启动度量比作「接力赛」——每一棒选手(固件模块)起跑前,先让裁判(TPM/硬件信任根)检查一下他的身份和状态。

这一章我们聚焦三个关键阶段:BIOS/UEFI 阶段度量、Option ROM 度量、Bootloader 度量。这三个阶段覆盖了从硬件初始化到操作系统加载的完整信任链。

4.1 BIOS/UEFI 阶段度量:信任链的起点

BIOS 或 UEFI 是平台启动后执行的第一段代码。它的度量结果,决定了整个信任链的根基是否牢固。

我在项目中遇到过一个问题:某款服务器在启动时总是报 PCR 扩展值异常。查了两天才发现,是 BIOS 在度量自身代码时,漏掉了 NVRAM 中的一段配置数据。你看,度量范围没覆盖全,信任链就断了。

4.1.1 度量时机与对象

UEFI 规范定义了明确的度量时机。每个固件卷(FV)或驱动在执行前,都要先被度量。具体来说:

  • SEC(安全验证)阶段:度量 Boot Guard 或类似硬件信任根的配置
  • PEI(EFI 前初始化)阶段:度量 PEI 核心、PEIM 模块、S3 恢复代码
  • DXE(驱动执行环境)阶段:度量 DXE 核心、UEFI 驱动、ACPI 表、SMBIOS 表
  • BDS(启动设备选择)阶段:度量启动管理器、启动选项、NVRAM 变量

核心原则:度量必须在执行之前完成。先度量,后执行。这是信任链传递的铁律。

4.1.2 度量方式:Hash 与扩展

UEFI 固件使用 TPM 的 PCR(平台配置寄存器)来存储度量值。度量过程分两步:

  1. 计算 Hash:对要度量的代码或数据计算 SHA-256(或 SHA-1)摘要
  2. 扩展 PCR:将新 Hash 与 PCR 当前值拼接,再计算一次 Hash,写回 PCR

扩展操作保证了 PCR 值无法被回滚。你想想看,攻击者就算篡改了某个模块,PCR 值也会跟着变,远程验证方一眼就能看出来。

// 伪代码:UEFI 度量扩展操作
EFI_STATUS MeasureAndExtend(
    IN  UINT32       PcrIndex,
    IN  VOID*        Data,
    IN  UINTN        DataSize,
    IN  UINT16       AlgorithmId  // TPM_ALG_SHA256
)
{
    UINT8 Digest[32];
    // 1. 计算 Hash
    HashData(AlgorithmId, Data, DataSize, Digest);
    // 2. 扩展 PCR
    Tpm2PcrExtend(PcrIndex, AlgorithmId, Digest);
    return EFI_SUCCESS;
}

经验之谈:我建议在固件中为每个度量事件记录日志(Event Log)。日志里要包含 PCR 索引、度量类型、被度量数据的描述和 Hash 值。这样在调试时,你可以把日志和 PCR 值做比对,快速定位问题。

4.2 Option ROM 度量:容易被忽视的盲区

Option ROM 是插在 PCIe 设备上的固件,比如显卡的 GOP、网卡的 PXE、RAID 卡的 Option ROM。这些代码在系统启动时由 UEFI 加载执行,但很多固件工程师会忽略对它们的度量。

我曾经参与过一个安全审计项目,发现某厂商的服务器在启动时,网卡的 Option ROM 完全没有被度量。攻击者只要刷一个恶意 Option ROM,就能在操作系统加载前植入后门。嗯,这个漏洞的严重程度,你想想看。

4.2.1 度量策略

Option ROM 的度量策略有两种:

策略 描述 优点 缺点
全量度量 度量整个 Option ROM 镜像 覆盖完整,安全性高 启动时间增加,PCR 扩展次数多
摘要度量 只度量 Option ROM 的 Hash 摘要 速度快,PCR 占用少 无法检测内部模块替换

我个人更推荐全量度量。启动时间多几百毫秒,换来的是整个信任链的完整性,这笔账划算。

4.2.2 度量流程

UEFI 在加载 Option ROM 时,会调用平台安全策略驱动(如 Security Stub)。这个驱动负责:

  1. 从 PCIe 设备的扩展 ROM 空间中读取 Option ROM 数据
  2. 调用 TPM 服务对 Option ROM 数据进行 Hash 计算和 PCR 扩展
  3. 将度量事件记录到 TCG 事件日志中
  4. 验证 Option ROM 的数字签名(如果平台策略要求)

注意:有些 Option ROM 是延迟加载的(Deferred Load),即在系统启动后才加载。这种情况下,度量必须在加载时完成,不能提前度量。否则攻击者可以在度量后、加载前篡改 Option ROM。

4.3 Bootloader 度量:操作系统的守门员

Bootloader(如 GRUB2、systemd-boot、Windows Boot Manager)是固件和操作系统之间的桥梁。它负责加载操作系统内核、initramfs、内核模块等。Bootloader 的度量,是信任链从固件传递到操作系统的最后一环。

4.3.1 GRUB2 的度量机制

GRUB2 是目前 Linux 系统中最常用的 Bootloader。它通过 TPM 驱动模块(tpm.mod)实现度量功能。

我记得在调试一个 GRUB2 度量问题时,发现度量事件日志里缺少内核镜像的 Hash。查了半天,原来是 GRUB2 配置文件中没有启用 measure 命令。你看,功能是有的,但默认没开,很多人就忽略了。

# GRUB2 配置文件示例:启用度量
# /etc/grub.d/40_custom
insmod tpm
measure /vmlinuz-linux
measure /initramfs-linux.img
measure /dtbs/board.dtb

GRUB2 的度量流程如下:

  1. GRUB2 加载 tpm.mod 模块,初始化 TPM 通信
  2. 读取配置文件中的 measure 指令,获取待度量文件列表
  3. 对每个文件计算 SHA-256 Hash,扩展 PCR 8(通常用于 Bootloader 度量)
  4. 将度量事件写入 TCG 事件日志
  5. 加载并执行度量后的文件

4.3.2 度量范围:不只是内核

很多工程师只度量内核镜像,但 Bootloader 需要度量的对象远不止这些:

  • 内核镜像(vmlinuz):操作系统的核心
  • initramfs:早期用户空间文件系统
  • 设备树(DTB):硬件描述信息
  • 内核模块:驱动和扩展功能
  • Bootloader 配置文件:grub.cfg 等
  • Bootloader 自身模块:如 tpm.mod、ext2.mod 等

关键点:配置文件也必须度量。攻击者如果篡改 grub.cfg,可以绕过内核度量,加载恶意内核。我在实际项目中就见过这种攻击手法。

4.4 启动度量流程图

下面这张图展示了从硬件复位到 Bootloader 完成度量的完整流程。我把它画出来,方便你理解信任链的传递关系。

启动度量流程图 启动度量信任链传递流程 硬件信任根 Boot Guard / 不可变ROM SEC / PEI 阶段 度量PEIM / S3恢复代码 DXE 阶段 度量UEFI驱动 / ACPI表 BDS 阶段 度量启动选项 Option ROM 度量 PCIe设备固件度量 Bootloader 度量 GRUB2 / systemd-boot 操作系统内核 度量内核 / initramfs PCR 分配说明 • PCR 0:BIOS/UEFI 固件代码 • PCR 1:BIOS/UEFI 配置数据 • PCR 2:Option ROM 代码 • PCR 8:Bootloader 度量

4.5 常见问题与避坑指南

做启动度量这么多年,我踩过不少坑。下面这几个问题,你大概率也会遇到。

坑1:度量顺序错误

我曾经在调试一个 UEFI 固件时,发现某个驱动在度量之前就被执行了。原因是固件代码中,驱动加载和度量是异步的,度量回调还没触发,驱动已经跑起来了。解决方案:确保度量操作在驱动调度之前完成,使用同步回调或屏障机制。

坑2:PCR 冲突

不同固件模块可能会使用同一个 PCR 索引。比如,Option ROM 和某些 UEFI 驱动都扩展 PCR 2。这会导致 PCR 值无法区分是哪个模块被篡改。我建议在固件设计阶段就规划好 PCR 分配表,避免冲突。

坑3:事件日志溢出

启动过程中度量事件可能非常多,尤其是全量度量 Option ROM 时。如果事件日志缓冲区太小,会导致日志溢出,丢失度量记录。我在一个项目中就把日志缓冲区从 4KB 改到了 64KB,问题才解决。建议根据平台启动模块数量,合理设置日志缓冲区大小。

4.6 小结

启动度量是信任链的基石。BIOS/UEFI 阶段度量建立了硬件信任根,Option ROM 度量堵住了外设固件的漏洞,Bootloader 度量则确保了操作系统的完整性。这三个阶段环环相扣,缺一不可。

嗯,这里要注意:度量不是目的,验证才是。度量只是把代码的「指纹」记录下来,真正的安全决策(比如是否允许启动)需要由远程验证方或本地策略引擎来做。下一层,我们会讨论如何利用这些度量结果进行验证和证明。