reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
173
vendor-new/rev-skills/.claude/skills/re-uefi/SKILL.md
Normal file
173
vendor-new/rev-skills/.claude/skills/re-uefi/SKILL.md
Normal file
@@ -0,0 +1,173 @@
|
||||
---
|
||||
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]])
|
||||
@@ -0,0 +1,129 @@
|
||||
# PI 阶段模型:SEC / PEI / DXE / BDS / Runtime / SMM
|
||||
|
||||
<CORE RULE>
|
||||
**看到 PE32/PE32+ 就按普通 Windows PE 应用分析,是本域首要要排除的判断错误。**
|
||||
|
||||
第一步永远是**判阶段与模块类型**:
|
||||
|
||||
```
|
||||
SEC → PEI → DXE → BDS → OS / Runtime
|
||||
```
|
||||
|
||||
同一份固件里,不同阶段的模块运行在**完全不同的执行环境**里:可用内存不同、可调用的服务不同、生命周期不同。**模块类型判错,后面所有"它为什么这么写"的推理都会错。**
|
||||
</CORE RULE>
|
||||
|
||||
## 一、先分类(决定后面全部)
|
||||
|
||||
| 类型 | 阶段 | 说明 |
|
||||
|---|---|---|
|
||||
| SEC | 最早 | 安全/初始化入口,尚无通用内存环境 |
|
||||
| PEIM | PEI | 早期初始化,服务形式是 **PPI** |
|
||||
| DXE driver | DXE | 服务形式是 **Protocol**;仅存在于 boot services 环境 |
|
||||
| **DXE runtime driver** | DXE + Runtime | **跨生命周期**:`ExitBootServices()` 之后仍然存在 |
|
||||
| UEFI driver / UEFI application | DXE/BDS 之后 | 更接近"跑在 UEFI 环境里的应用",与固件内建模块不是一回事 |
|
||||
| SMM/MM driver | 独立 | SMM 内执行,特权层级与生命周期自成一套 |
|
||||
|
||||
**判据优先级**:INF 的 `MODULE_TYPE`(若能拿到)> 节/文件类型 > 导入的服务(PPI vs Protocol vs Runtime Services)> 行为。
|
||||
|
||||
## 二、PEI 的特殊性:可能还没有"普通内存"
|
||||
|
||||
- PEI 早期**可能尚无普通 DRAM**;PEI 的主要任务之一就是**建立永久内存**,并把信息以 **HOB** 的形式交给 DXE
|
||||
- PEI 用的是 **PPI**,不是 Protocol——**在 PEI 模块里找不到 `LocateProtocol` 是正常的**,要找的是 PPI 的安装/定位
|
||||
- 严格说 DXE 阶段并不依赖 PEI 存在:**DXE 能执行的最低要求是"有一份合法的 HOB list"**
|
||||
|
||||
## 三、HOB:跨阶段数据,"没有 producer 的输入"多半就是它
|
||||
|
||||
**遇到"函数读一串没有普通 producer 交叉引用的结构"——先考虑 HOB list。**
|
||||
|
||||
- HOB list 是 PEI 交给 DXE 的交接状态:由 **PHIT HOB**(首个、必需)开头、以 End of HOB List 结尾的连续表
|
||||
- 表单里通常包含:内存资源描述(DXE 拿它当物理内存用)、固件卷(FV)HOB、内存分配模块 HOB、BSP 栈 HOB 等
|
||||
- **单向交接**:只有 PEI 阶段能添加/修改 HOB;**交给 DXE 后整张表是只读的**
|
||||
- 因此 **DXE 侧构造 HOB 的调用(`Build*Hob` 一族)会断言失败**——在 DXE 代码里看到这些调用点,说明要么这是 PEI 代码被误判,要么是残留/调试代码
|
||||
- HOB 的自洽约束也很有辨识度:长度按 8 字节对齐、不得内含指向 HOB 表内其他数据的指针(必须可整体复制而不需调整内部指针)、**只追加不删除**
|
||||
- DXE 想表达"要进恢复模式"这类状态变化时**不能改 HOB**,只能走复位调用——所以"HOB 里的 Boot Mode 与当前行为不一致"通常不是被篡改
|
||||
|
||||
## 四、GUID 不是普通 UUID 元数据:要做 GUID 字典解析
|
||||
|
||||
同一串 GUID 在 UEFI 里可能是完全不同的东西:
|
||||
|
||||
```
|
||||
Protocol / PPI / Firmware File / HOB / Variable namespace / Configuration table
|
||||
```
|
||||
|
||||
**RE 系统应当把 GUID 解析成符号**(等价于符号恢复):
|
||||
|
||||
```
|
||||
LocateProtocol(&some_guid)
|
||||
→ 恢复成 LocateProtocol(EFI_XXX_PROTOCOL_GUID)
|
||||
→ interface vtable
|
||||
→ method calls
|
||||
```
|
||||
|
||||
- **协议图往往比普通调用图更有意义**:模块之间通过 Protocol 解耦,调用关系藏在"谁安装、谁定位"里,不在直接 call 里
|
||||
- 变量命名空间同理:只看到 `GetVariable`/`SetVariable` 而不知道 GUID,等于没看懂它读的是什么
|
||||
|
||||
## 五、固件 dump 不能只 carve PE magic
|
||||
|
||||
规范本身定义了层次:
|
||||
|
||||
```
|
||||
Firmware Volume → FFS file → section → PE32/TE image
|
||||
```
|
||||
|
||||
- "固件里有很多 PE"是因为有成百上千模块;但**按 PE 魔数直接 carve 会丢掉归属**(属于哪个 FV、哪个 FFS 文件、什么节类型)
|
||||
- 判归属要走 FV/FFS/section 层次(工具见 [[re-uefi]] 主文档步骤 1–2);**嵌套 FV 与压缩节是常态**,只展开外层会缺件
|
||||
|
||||
## 六、生命周期:`DXE_DRIVER` 与 `DXE_RUNTIME_DRIVER` 不是一回事
|
||||
|
||||
- **`DXE_DRIVER`**:只存在于 boot services 环境,`ExitBootServices()` 时被销毁
|
||||
- **`DXE_RUNTIME_DRIVER`**:**同时**运行在 boot services 与 runtime services 环境,其服务在 `ExitBootServices()` 之后、OS 运行时仍然可用;`SetVirtualAddressMap()` 被调用时,**这类模块会按 OS 给出的虚拟地址映射被重定位**
|
||||
- `ExitBootServices()` 之后:**Boot Services、DXE Services、以及 boot service driver 提供的服务全部不可再用**——DXE Foundation 本身属于 boot services,所以**不能有 DXE Foundation 的代码残留到 runtime**
|
||||
- `SetVirtualAddressMap()` 相关的两个动作要认得出:
|
||||
- 驱动在入口注册 `EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE` 事件(例如 `CreateEventEx` 配 `gEfiEventVirtualAddressChangeGuid`)
|
||||
- 通知函数里用 **`ConvertPointer()`** 把自管数据结构里的指针从物理地址改成虚拟地址
|
||||
- **虚拟地址变更通知函数不得调用任何 Boot Services / Console Services / Protocol Services**(直接间接都不行)——因为这些服务此时已经不可用。**在通知函数里看到"没调用什么"往往是设计约束,不是代码被裁剪**
|
||||
- `EFI_RUNTIME_ARCH_PROTOCOL` 自带两个可用作判据的状态:**`VirtualMode`**(`SetVirtualAddressMap()` 是否已调用)与 **`AtRuntime`**(`ExitBootServices()` 是否已调用)——现场判断"现在处于哪个生命周期"的现成依据
|
||||
|
||||
**关键推论**:**固件地址 ≠ OS runtime 地址**。同一份 runtime 代码在两个阶段地址不同,**不一定是 hook、也不一定是重定位出错**。要判断篡改,必须先说明"你比对的是哪一个阶段的映射"。
|
||||
|
||||
## 决策树
|
||||
|
||||
```
|
||||
拿到一个 .efi / 固件模块
|
||||
├─ 判阶段与模块类型(SEC / PEIM / DXE / DXE runtime / UEFI app / SMM)
|
||||
│ ├─ 只有 PPI 语义 → PEI
|
||||
│ ├─ 用 Protocol → DXE 环境
|
||||
│ └─ 有 runtime 服务 / 注册了虚拟地址变更事件 → DXE_RUNTIME_DRIVER
|
||||
├─ 输入没有 producer 交叉引用
|
||||
│ └─ 先当 HOB list(PEI→DXE 单向、DXE 只读);在 DXE 里找不到构造点属正常
|
||||
├─ 一串 GUID 看不懂
|
||||
│ └─ 做 GUID 字典解析:区分 Protocol/PPI/FFS/HOB/变量命名空间/配置表
|
||||
│ → 由"谁安装、谁定位"恢复协议图,而不是追直接 call
|
||||
├─ 文件归属不清
|
||||
│ └─ 按 FV → FFS → section 层次定位(嵌套与压缩是常态),不要按 PE 魔数直接 carve
|
||||
└─ 地址/行为对不上
|
||||
├─ 比对的是哪一个阶段的地址映射?(固件地址 vs OS runtime 地址)
|
||||
├─ runtime 代码是否经历过 SetVirtualAddressMap + ConvertPointer
|
||||
└─ 现在 AtRuntime / VirtualMode 是什么状态
|
||||
```
|
||||
|
||||
## 工具与验证
|
||||
|
||||
- 阶段/类型:优先读模块自身的类型信息(INF 的 `MODULE_TYPE`、节类型、导入服务种类);拿不到就用"它调的服务属于哪一层"反推
|
||||
- HOB:从 PHIT HOB 起逐项解析 HOB list,建立"哪个阶段生产、谁消费"的对应
|
||||
- GUID:建 GUID→符号字典(协议名、PPI 名、FFS 文件、变量命名空间),把 `LocateProtocol` 一类调用还原成语义名
|
||||
- 归属:FV → FFS → section 层次复原,别脱离层次谈某个 PE
|
||||
- 验证:能同时说清「阶段 + 模块类型(是否 runtime)+ 依赖的服务层级(PPI/Protocol/Runtime)+ 地址映射处于哪个阶段」
|
||||
|
||||
## 该平台的坑(汇总)
|
||||
|
||||
- **看到 PE32/PE32+ 就当普通 Windows PE 应用**:先判阶段与模块类型,执行环境完全不同
|
||||
- **在 PEI 模块里找 `LocateProtocol`**:PEI 的服务形式是 PPI
|
||||
- **把 HOB 当"普通的全局数据"**:它是 PEI→DXE 的单向交接,DXE 侧只读;在 DXE 里找构造点会一无所获,构造调用还会断言
|
||||
- **以为 HOB 可以被 DXE 修改**:状态变化只能走复位调用,不能改 HOB
|
||||
- **把 GUID 当无语义的 UUID**:它可能是 Protocol/PPI/FFS/HOB/变量命名空间/配置表中的任意一种,必须解析成符号
|
||||
- **只看直接调用图,忽略协议图**:模块间的真正耦合在"谁安装、谁定位哪个 Protocol"
|
||||
- **按 PE 魔数直接 carve 固件**:丢掉 FV/FFS/section 层次归属,也漏掉嵌套与压缩
|
||||
- **不区分 `DXE_DRIVER` 与 `DXE_RUNTIME_DRIVER`**:前者在 `ExitBootServices()` 后不复存在,后者跨生命周期并被 `SetVirtualAddressMap()` 重定位
|
||||
- **把 runtime 通知函数里"没调用任何服务"当异常**:那是硬性约束(此时服务已不可用),不是代码问题
|
||||
- **拿固件地址与 OS runtime 地址直接比对判 hook**:先确认是哪个阶段的映射,以及是否经过 `ConvertPointer`
|
||||
Reference in New Issue
Block a user