3. UEFI 安全启动架构:固件、数据库与启动策略

好,我们进入第三章。这一章是安全启动的骨架——UEFI 架构本身。说白了,你得先知道房子怎么盖的,才能谈怎么修漏水。

UEFI 安全启动不是凭空冒出来的。它是 UEFI 规范里定义的一套固件级验证机制。我刚开始接触时也觉得这东西很玄乎,后来拆开一看,其实就是几个数据库加一段启动逻辑。嗯,咱们一层层剥开。

3.1 UEFI 固件组成

UEFI 固件,你可以把它理解成主板上的一个小型操作系统。它负责在 CPU 上电后、操作系统加载前,把硬件初始化好,然后找到启动设备。

它的核心组件包括:

  • SEC (Security Phase):安全阶段,CPU 上电后第一个执行的代码。负责建立临时栈,验证后续阶段的签名。我见过有些板卡在这里就挂了,因为 PEI 阶段的签名没通过。
  • PEI (Pre-EFI Initialization):预初始化阶段。主要做内存初始化。这时候内存还没完全可用,代码在缓存里跑。
  • DXE (Driver Execution Environment):驱动执行环境。内存可用了,各种驱动(SATA、NVMe、USB)开始加载。安全启动的数据库也是在这个阶段被读取和验证的。
  • BDS (Boot Device Selection):启动设备选择。根据你设置的启动顺序,去加载操作系统引导程序。
  • TSL (Transient System Load):临时系统加载。比如你按 F12 进启动菜单,或者直接加载 bootmgfw.efi。
  • RT (Runtime):运行时。操作系统接管后,UEFI 只提供一些运行时服务,比如设置变量、重启。

关键点:安全启动的验证点主要卡在 DXE 和 BDS 阶段。DXE 阶段验证驱动签名,BDS 阶段验证引导加载程序签名。任何一个环节没通过,启动就中断了。

我画了一张图,帮你把整个流程串起来:

UEFI 固件启动流程与安全启动验证点 SEC PEI DXE 验证驱动签名 BDS 验证引导加载器 TSL RT ⚠ 安全启动验证点 DXE 阶段验证所有 UEFI 驱动和 Option ROM 的签名;BDS 阶段验证引导加载程序(如 bootmgfw.efi)的签名。 💡 我的经验 曾经有一台服务器,DXE 阶段加载网卡 Option ROM 时签名验证失败,直接卡死在开机自检。后来发现是网卡固件过期,DBX 里已经吊销了旧签名。

3.2 安全启动数据库

安全启动的核心,是四个数据库。它们存储在 UEFI 的 NVRAM 里,断电不丢失。我习惯把它们比作一个门禁系统:

  • PK (Platform Key):平台密钥。这是最高权限,相当于门禁系统的总管理员。只有持有 PK 私钥的人,才能修改 PK 本身或 KEK。
  • KEK (Key Exchange Key):密钥交换密钥。相当于门禁系统的授权管理员。KEK 的持有者可以修改 DB 和 DBX。
  • DB (Signature Database):签名数据库。白名单。里面存放的是被信任的签名或证书哈希。只有签名匹配 DB 里条目的 EFI 程序才能运行。
  • DBX (Revoked Signature Database):吊销签名数据库。黑名单。即使签名在 DB 里,如果也在 DBX 里,照样不让跑。

避坑指南:我曾经遇到过一台设备,刷了新版固件后安全启动一直失败。查了半天,发现是 DBX 里多了一条旧版引导加载程序的吊销记录。新固件自带的引导加载程序恰好被这条记录匹配上了。解决办法?要么更新引导加载程序,要么在固件设置里清空 DBX(不推荐,有安全风险)。

这四个数据库的关系,可以用一张图说清楚:

安全启动数据库层级关系 PK(平台密钥) KEK(密钥交换密钥) DB(签名数据库) DBX(吊销数据库) PK 签名 KEK → KEK 签名 DB/DBX → DB 允许运行 / DBX 禁止运行

3.3 启动策略与验证流程

有了数据库,还得有规则。安全启动的验证策略,说白了就是三步走:

  1. 检查 DBX:先看这个 EFI 程序的签名或哈希是否在吊销列表里。如果在,直接拒绝,启动失败。
  2. 检查 DB:如果不在 DBX 里,再看是否在 DB 白名单里。如果在,允许运行。
  3. 检查 Setup Mode:如果既不在 DBX 也不在 DB,固件会看当前是否处于 Setup Mode(设置模式)。如果是,允许运行并提示用户是否添加签名;如果不是,直接拒绝。

注意:Setup Mode 是安全启动的“后门”。在 Setup Mode 下,你可以用 KEK 私钥签名新的 DB 条目。但一旦退出 Setup Mode 进入 User Mode(用户模式),没有有效签名的 EFI 程序就再也跑不起来了。我见过有人为了省事,长期把设备放在 Setup Mode 下跑——嗯,那安全启动就形同虚设了。

验证流程的伪代码大概是这样:

// 安全启动验证伪代码
EFI_STATUS SecureBootVerify(EFI_FILE *image) {
    // 1. 检查 DBX
    if (IsInDBX(image->signature)) {
        return EFI_SECURITY_VIOLATION;  // 被吊销,拒绝
    }

    // 2. 检查 DB
    if (IsInDB(image->signature)) {
        return EFI_SUCCESS;  // 白名单,允许
    }

    // 3. 检查 Setup Mode
    if (IsSetupMode()) {
        // 提示用户是否添加签名
        return EFI_SUCCESS;  // 允许,但记录日志
    }

    // 4. 都不满足,拒绝
    return EFI_SECURITY_VIOLATION;
}

你可能会问:为什么先查 DBX 再查 DB?顺序重要吗?

重要。你想,如果一个签名同时出现在 DB 和 DBX 里,那应该以哪个为准?规范明确说了:DBX 优先。这是为了防止有人通过往 DB 里添加已被吊销的签名来绕过安全启动。我当年第一次看规范时也忽略了这点,后来在测试中才发现顺序问题。

3.4 数据库的存储与操作

这四个数据库都存储在 UEFI NVRAM 变量里。变量名是固定的:

数据库 UEFI 变量名 GUID 权限
PK PK 8be4df61-93ca-11d2-aa0d-00e098032b8c 仅 PK 自身可修改
KEK KEK 8be4df61-93ca-11d2-aa0d-00e098032b8c PK 可修改
DB db d719b2cb-3d3a-4596-a3bc-dad00e67656f KEK 可修改
DBX dbx d719b2cb-3d3a-4596-a3bc-dad00e67656f KEK 可修改

每个数据库条目可以是以下三种格式之一:

  • SHA-256 哈希:直接存储 EFI 程序镜像的哈希值。匹配最快,但更新固件后需要重新添加。
  • X.509 证书:存储证书的 SHA-256 哈希。只要是用该证书签名的 EFI 程序都能通过验证。灵活性高,我比较推荐这种方式。
  • EFI_CERT_TYPE_PKCS7_GUID:存储完整的 PKCS#7 签名。用得少,主要用于吊销场景。

我的习惯:在生产环境中,我倾向于用 X.509 证书来管理 DB。这样如果某个驱动需要更新,只要用同一张证书重新签名就行,不用动数据库。但要注意证书过期问题——我曾经吃过这个亏,证书过期后所有用该证书签名的驱动都跑不起来了。

3.5 启动策略的三种模式

UEFI 规范定义了三种安全启动模式:

模式 描述 典型场景
Setup Mode PK 变量为空。任何人都可以修改数据库。固件会提示用户安装 PK。 出厂首次启动、固件重置后
User Mode PK 已安装。只有持有 PK 私钥才能修改 PK 和 KEK;持有 KEK 私钥才能修改 DB/DBX。 正常生产环境
Audit Mode 类似 User Mode,但签名验证失败时不会阻止启动,只会记录日志。用于调试。 开发和测试阶段

Audit Mode 是个好东西。我调试安全启动问题时,第一件事就是切到 Audit Mode。这样既能收集签名验证失败的日志,又不会影响系统启动。等找到问题根因,再切回 User Mode 修复。