14 KiB
14 KiB
name, description, capabilities
| name | description | capabilities | |
|---|---|---|---|
| re-uefi | UEFI/BIOS 固件:SEC/PEI/DXE/BDS 阶段判定、DXE 驱动、UEFI 模块、bootkit。 触发词:UEFI、BIOS、DXE、PEI、SEC、PEIM、HOB、bootkit、Secure Boot、EFI 固件、runtime driver |
|
UEFI 固件逆向(DXE 驱动 / UEFI 模块 / bootkit)
**看到 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输出用法;GUIuefitool能打开固件镜像
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/RHELls /usr/share/edk2/ovmf/OVMF_CODE.fd;Archls /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)。
-
固件镜像解析(Firmware Volume → File → Section):
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)
-
DXE 驱动提取:
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),再进反编译器
-
模块分析(入口 / 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 表
strings driver.efi | grep -iE 'protocol|runtime|efi_' | head -30- 记录:入口函数、安装/获取了哪些 Protocol、是否注册了事件回调(接步骤 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
- 定时器回调:
-
OVMF 仿真验证(运行恶意代码——沙箱内,网络
-net none):# 只读代码固件 + 可写变量固件(两片 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_DRIVERvsDXE_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)