---
name: re-uefi
description: >
  UEFI/BIOS 固件：SEC/PEI/DXE/BDS 阶段判定、DXE 驱动、UEFI 模块、bootkit。
  触发词：UEFI、BIOS、DXE、PEI、SEC、PEIM、HOB、bootkit、Secure Boot、EFI 固件、runtime driver
capabilities: [uefi-analysis]
---

# UEFI 固件逆向（DXE 驱动 / UEFI 模块 / bootkit）

<CORE RULE>
**看到 PE32/PE32+ 就按普通 Windows PE 应用分析，是本域首要要排除的判断错误。**

第一步永远是**判阶段与模块类型**：

```
SEC → PEI → DXE → BDS → OS / Runtime（+ SMM 独立一路）
```

同一份固件里，不同阶段的模块运行在**完全不同的执行环境**：可用内存不同、可调用的服务不同（PPI / Protocol / Runtime Services）、生命周期不同（`DXE_DRIVER` 与 `DXE_RUNTIME_DRIVER` 不是一回事）。

所以顺序是：**先判阶段与类型，再谈逻辑**——类型判错，后面所有"它为什么这么写"的推理都会错。详见 [[pi-stages]]。
</CORE RULE>

## 阶段与模块类型（先分类）

| 类型 | 阶段 | 服务形式 / 生命周期 |
|---|---|---|
| SEC | 最早 | 安全与初始化入口，无通用内存环境 |
| PEIM | PEI | **PPI**；PEI 主要任务之一是建立永久内存 |
| DXE driver | DXE | **Protocol**；仅存在于 boot services 环境 |
| **DXE runtime driver** | DXE + Runtime | **跨生命周期**：`ExitBootServices()` 后仍存在，`SetVirtualAddressMap()` 时被重定位 |
| UEFI driver / application | DXE/BDS 之后 | 跑在 UEFI 环境里的驱动/应用，与固件内建模块不是一回事 |
| SMM/MM driver | 独立 | SMM 内执行，特权层级与生命周期自成一套 |

三条高频判据（细节与决策树见 [[pi-stages]]）：

- **读一串没有 producer xref 的结构** → 先想 **HOB**（PEI→DXE 单向交接，DXE 侧只读）
- **`LocateProtocol(&guid)`** → 做 **GUID 字典解析**，恢复成 `EFI_XXX_PROTOCOL_GUID` 与接口 vtable；**协议图比直接调用图更有意义**
- **固件地址 ≠ OS runtime 地址** → 不一定是 hook 或重定位错误，先确认比对的是哪个阶段的映射

## 何时使用 / 何时不用

- 前置：拿到固件镜像但不确定是 UEFI/PI 结构还是嵌入式镜像 → [[re-analyze/system-fingerprints]] 的固件段（FV → FFS → section 层次）
- 用：固件镜像内含 EFI 结构（Firmware Volume / FFS 文件 / DXE 驱动）——BIOS 更新包（.fd/.rom/.bin）、UEFI 驱动、EFI 应用
- 用：bootkit 定位（SMM handler、定时器回调、启动路径挂钩）与验证
- 用：Secure Boot / 启动链相关分析（签名、证书、NVRAM 变量）——需合法授权，见坑 4
- 不用：固件是传统 Linux 嵌入式 rootfs（走 [[re-fw-extract]] / [[re-fw-rootfs]]；[[re-firmware]] 网关先判定）
- 不用：Legacy BIOS Option ROM / 非 EFI 传统固件（binwalk 走 [[re-fw-extract]]）
- 不用：只需整体解包看内容（先 [[re-fw-extract]] 初判，确认是 UEFI 结构再进本技能）
- 注意：静态分析先行（大型固件原则）；仿真 bootkit = 运行恶意代码，默认沙箱（[[re-analyze/platform-tips]] 最高原则）

## 工具准备

所有工具先验证再使用。固件镜像解析/模块静态分析可免沙箱；OVMF 仿真（步骤 5）是动态执行，默认沙箱 + 网络隔离（[[re-analyze/platform-tips]] 最高原则）。

### UEFITool / UEFIExtract（固件解析主力）

- Debian/Ubuntu: `apt install uefitool uefitool-cli`（官方包：GUI + CLI；CLI 含 UEFIExtract/UEFIFind）
- Arch: AUR `yay -S uefitool`（或 `uefitool-git`；非官方仓库）
- Fedora: 无官方包 → GitHub releases 的 Linux x64 二进制（https://github.com/LongSoft/UEFITool/releases）或源码构建（qmake + Qt6）
- macOS: GitHub releases 的 universal macOS 二进制（同上页面）
- Windows: GitHub releases 的 win64 二进制（或 `choco install uefitool` 如镜像可用）
- 验证: `uefiextract --help` 输出用法；GUI `uefitool` 能打开固件镜像

### UEFIFind（按模式串定位文件，随 uefitool-cli）

- NE 版只有位置参数语法：`uefifind imagefile {header|body|all} {list|count} pattern`，无 -g/-t 选项
- 按 GUID 定位改用 UEFIExtract 的 GUID 参数模式：`uefiextract fw.bin <GUID>`（见下）
- 装法同上（Debian/Ubuntu 的 uefitool-cli 内含）
- 验证: `uefifind --help`

### ghidra / ida（模块反编译；装法见 [[re-ghidra]] / [[re-ida]]）

- .efi 模块是 PE/COFF（subsystem EFI_BOOT_SERVICE_DRIVER / EFI_APPLICATION），两家都能直接导入
- EfiRom 插件（可选）：EDK2 工具，用于 ROM 文件与 .efi 互转/体积查询
- 验证: 导入提取出的 .efi 能自动识别 subsystem 为 EFI boot service driver

### ovmf —— OVMF（Open Virtual Machine Firmware，UEFI 仿真固件）

- Debian/Ubuntu: `apt install ovmf`（/usr/share/OVMF/OVMF_CODE.fd、OVMF_VARS.fd）
- Fedora/RHEL: `dnf install edk2-ovmf`（路径 **/usr/share/edk2/ovmf/OVMF_CODE.fd**——旧 /usr/share/OVMF 布局已废弃）
- Arch: `pacman -S edk2-ovmf`（仅装 **/usr/share/edk2/x64/OVMF_CODE.4m.fd**，4m 格式）
- macOS: `brew install qemu` 自带固件（`$(brew --prefix)/share/qemu/edk2-x86_64-code.fd`，随 qemu 包）
- Windows: QEMU 安装包自带（share/qemu/ 下 edk2-x86_64-code.fd）
- 验证: 按发行版查对应路径——Debian/Ubuntu `ls /usr/share/OVMF/OVMF_CODE.fd`；Fedora/RHEL `ls /usr/share/edk2/ovmf/OVMF_CODE.fd`；Arch `ls /usr/share/edk2/x64/OVMF_CODE.4m.fd`；macOS 用 brew 前缀路径

### qemu-system-x86_64（OVMF 运行载体）

- Debian/Ubuntu: `apt install qemu-system-x86`
- Fedora: `dnf install qemu-system-x86-core`（完整虚拟化组为 `dnf install @virtualization`）
- Arch: `pacman -S qemu-system-x86`
- macOS: `brew install qemu`
- Windows: qemu.weilnetz.de 安装包
- 验证: `qemu-system-x86_64 --version`

## 操作步骤

按顺序执行，每步记录证据（路径 + sha256，见 [[re-triage]]）。初勘先做：`file fw.bin` + 熵确认是否 UEFI 结构（[[re-triage]]）。

1. **固件镜像解析（Firmware Volume → File → Section）**：
   ```sh
   file fw.bin
   uefiextract fw.bin all            # 全量递归解包（含嵌套 FV 与压缩节，见坑 1/2）
   ls fw.bin.dump/                   # 树：Volume0/ → File0/ → Section0/ → PE32 等
   ```
   - GUI: UEFITool 打开 fw.bin，左侧树自上而下 FV → FFS File → Section；先数 FV 数量与类型——主 DXE FV 通常最大、含成百上千模块；PEI FV 与 SEC Core 在最前
   - 先确认镜像里有几层 FV（嵌套/恢复卷），别只盯着第一层（坑 1）

2. **DXE 驱动提取**：
   ```sh
   ueifind fw.bin all list <GUID>    # 按模式串（含 GUID 文本）定位文件——NE 版仅位置参数语法，无 -g/-t
   # 按 GUID 直接提取指定文件（UEFIExtract 的 GUID 参数模式）:
   #   -m 合法值: all|body|header|info|file；-t 是十六进制节类型（PE32=0x10，即 -t 10）
   uefiextract fw.bin <GUID> -o driver.efi -m body -t 10
   file driver.efi                   # 期望输出: PE32+ executable (EFI boot service driver)
   ```
   - UEFITool GUI: 右键 File → Extract body，导出"裸 PE"（去掉 FFS/节头，见坑 3）
   - 提取产物先 `sha256sum` 存证（[[re-triage]]），再进反编译器

3. **模块分析（入口 / Protocol 服务）**：
   - [[re-ghidra]] / [[re-ida]] 打开 .efi；入口 = 镜像 entry（DriverEntry），它是理解驱动的起点
   - Protocol 服务是 UEFI 的"API"：`gBS->LocateProtocol(&guid,...)`（获取服务）、`gBS->InstallProtocolInterface(...)`（注册服务）、`gBS->CreateEvent(...)`（事件/回调）、`gST->ConOut`（控制台输出）
   - GUID 即"符号"——识别出 LocateProtocol 的 GUID 等于识别 API 调用；用 UEFITool 内置 GUID 数据库（界面里 GUID 旁显示协议名）或搜 UEFI 规范/UEFI GUID 表
   ```sh
   strings driver.efi | grep -iE 'protocol|runtime|efi_' | head -30
   ```
   - 记录：入口函数、安装/获取了哪些 Protocol、是否注册了事件回调（接步骤 4）

4. **bootkit 定位（SMM / 定时器回调 / 启动路径挂钩）**：
   - **定时器回调**: `gBS->CreateEvent(EVT_TIMER, TPL, Callback, ...)` + `gBS->SetTimer(..., PERIODIC/ONESHOT, ...)` → 回调函数就是周期执行恶意逻辑的地方（最常见挂点）
   - **SMM**: `SmiHandlerRegister` / SMST 服务 → SMM driver 注册 SMI handler（SMM 内执行的代码，比内核 ring0 更高特权、SMRAM 内执行的持久层）
   - **启动路径挂钩**: BDS 阶段篡改（NVRAM 变量 BootOrder/BootNext、替换 EFI 启动项、hook 启动管理器/PXE 路径）；S3 恢复路径（AP 唤醒代码）
   - **运行时服务钩子**: 拦截/替换 `gRT->SetVariable`/`GetVariable` 等 Runtime Services 指针
   - 判定特征: 驱动不卸载、携带自定义 GUID、DXE_DEPEX 依赖其他启动器模块、strings 里出现路径/密钥/URL；确认可疑模块 GUID 与依赖关系后进步骤 5

5. **OVMF 仿真验证**（运行恶意代码——沙箱内，网络 `-net none`）：
   ```sh
   # 只读代码固件 + 可写变量固件（两片 pflash 标准做法）；OVMF_CODE.fd 路径按发行版：
   #   Debian/Ubuntu: /usr/share/OVMF/OVMF_CODE.fd
   #   Fedora/RHEL:   /usr/share/edk2/ovmf/OVMF_CODE.fd（旧 /usr/share/OVMF 布局已废弃）
   #   Arch:          /usr/share/edk2/x64/OVMF_CODE.4m.fd（4m 格式）
   qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \
     -drive if=pflash,format=raw,file=OVMF_VARS.fd \
     -m 1024 -net none -boot menu=on
   ```
   - 验证固件能启动到 UEFI Shell/系统 → 再把提取的驱动/固件修改（UEFITool 插入或重建固件镜像）挂入同一 OVMF 观察行为差异
   - 无真实固件时 OVMF 就是基线环境（无 Secure Boot 签名约束，见坑 4）；仿真细节与用户态替代见 [[re-fw-emulate]]
   - 动态行为确认（回调触发/变量篡改）→ 沙箱 + 快照（[[re-sandbox]]），行为分析转 [[re-malware]]；每步产物存证

## 分支（references）

- [[pi-stages]] —— **PI 阶段模型**：SEC/PEI/DXE/BDS/Runtime/SMM 的判定与执行环境差异；**HOB**（PHIT 开头、PEI 单向生产、DXE 只读、`Build*Hob` 在 DXE 会断言）；**GUID 字典解析**（Protocol/PPI/FFS/HOB/变量命名空间/配置表）；**FV → FFS → section → PE** 层次（别按 PE 魔数直接 carve）；**`DXE_DRIVER` vs `DXE_RUNTIME_DRIVER`**（`ExitBootServices` 后 boot services 全不可用、`EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE` 通知函数禁调任何服务、`ConvertPointer`、`EFI_RUNTIME_ARCH_PROTOCOL` 的 `VirtualMode`/`AtRuntime` 判据）

## 跨域联合

- [[re-firmware]]：本技能由 re-firmware 网关引用——UEFI 固件分支；整体流程仍按网关 提取→rootfs→仿真 编排
- [[re-fw-emulate]]：步骤 5 的 OVMF/QEMU 全系统仿真就是本技能对 [[re-fw-emulate]] 的 UEFI 具体化
- [[re-binary-core]]：DXE 驱动静态反编译（[[re-ghidra]] / [[re-ida]] / [[re-radare2]]）、导入表 [[re-imports]]
- [[re-triage]]：初勘（hash / 架构 / 熵），每层产物存证
- [[re-malware]]：bootkit 动态验证进沙箱（[[re-sandbox]]），恶意行为分析转 [[re-malware]]
- 非 EFI 固件（传统 BIOS / 嵌入式镜像）→ [[re-fw-extract]]（binwalk 初判）
- [[re-analyze/platform-tips]] 相关分支：静态优先（大型固件样本）、动态默认沙箱（仿真 bootkit 时网络 -net none）、Linux/Windows 平台分支

## 常见坑与陷阱

- **固件镜像多 FV 嵌套**：现象——UEFITool 打开只看到少量文件/模块，找不到要找的驱动；原因——现代固件把 FV 嵌套封装（PEI FV 内嵌 DXE FV、恢复卷/Capsule、ACPI 表内嵌 FV），只分析外层自然缺件；对策——用 `uefiextract fw.bin all` 全量递归展开（自动处理嵌套），或 GUI 里逐层展开子 FV；先确认镜像结构再分析
- **压缩/填充区遮挡内容**：现象——FFS 文件段显示为压缩数据或 FREE_SPACE/FIXED 填充，看不到 PE32 代码；原因——厂商对整个 DXE 卷做压缩（EFI/LZMA 压缩节），留空区是正常布局；对策——UEFITool/UEFIExtract 会自动解压并显示解压后的 Section（用它看，别用 binwalk 的原始字节）；binwalk 结果里"找不到代码"不代表没有，回 UEFITool 确认
- **PE 头在 FFS 内偏移**：现象——从固件里抠出的"文件"直接 file/IDA 打开失败或反编译全是乱码；原因——FFS File Header（24/32 字节）+ Section Header + 对齐填充后才是 PE32 主体，PE 头不在文件偏移 0；对策——用 UEFITool 右键 Extract body / `uefiextract fw.bin <GUID> -m body` 导出裸 PE，导出后 `file` 确认输出含 "PE32+ executable (EFI boot service driver)" 再进反编译器；手工提取要按 Section 布局算偏移
- **Secure Boot 签名验证绕过分析需合法授权**：现象——想"绕过 Secure Boot"/"给固件重签名"来做实验，动真实签名固件出问题；原因——签名绕过、固件密钥/证书提取、在真实设备上验证 bootkit 都涉及法律与授权边界；对策——默认在 OVMF 开发固件（无签名约束）里分析与验证行为；对真实固件动手前确认授权范围（自有设备、漏洞研究授权），未授权不碰；报告里明确边界（与 [[re-hardware-io]] 的授权边界同类表述）
- **把跨阶段数据当缺失 producer 的普通全局变量**：现象——某函数读的一串结构找不到构造点，怀疑被裁剪或代码不完整；原因——那可能是 **HOB**（PEI→DXE 的单向交接，DXE 侧只读，构造调用还会断言）；对策——先按 HOB list 解析，建立"哪个阶段生产、谁消费"的对应（[[pi-stages]]）
- **把 GUID 当无语义 UUID**：现象——大量 GUID 常量无法解释，分析停在"传了个常量进去"；原因——GUID 可能是 Protocol/PPI/FFS 文件/HOB/变量命名空间/配置表中的任意一种；对策——建 GUID→符号字典，把 `LocateProtocol` 一类调用还原成语义名与接口 vtable，**用协议图补足调用图**
- **不区分 `DXE_DRIVER` 与 `DXE_RUNTIME_DRIVER`**：现象——拿固件里的地址与 OS runtime 阶段抓到的地址比对，判定被 hook；原因——runtime driver 在 `ExitBootServices()` 后仍存在、并会按 `SetVirtualAddressMap()` 的映射被重定位，固件地址本就不等于 runtime 地址；对策——先判模块类型与所处生命周期（`EFI_RUNTIME_ARCH_PROTOCOL` 的 `VirtualMode`/`AtRuntime` 是现成判据），再谈一致性（[[pi-stages]]）
