☰
UEFITool-UE 0.10.0实战:拆解UEFI固件与提取BIOS模块
2026/10/22 9:05:44 网站建设 项目流程

简介:UEFITool-UE-0.10.0是一款面向UEFI固件开发与调试的经典工具源码包,适用于系统固件分析师、BIOS工程师及对UEFI内部实现感兴趣的进阶开发者。该版本提供固件解析、FFS文件提取与固件信息查看等核心能力,能帮助使用者深入理解UEFI启动流程、固件模块组织与安全机制。压缩包共70个文件,以33个C头文件和18个C++源文件为骨干,配合9个C源文件、Qt界面文件(ui/pro)及工程配置文件,整体约191KB,便于直接阅读源码、编译学习或二次移植。资源包含UEFIExtract与UEFITool两个工程模块,可学习FFS解析、固件块提取、GUID/PE映像处理等关键实现,目录中还附有README及工程配置说明。目前已有402人学习下载,对想从代码层面掌握UEFI固件结构与工具链的人来说,是一份体量紧凑、内容明确的参考资料。

1. UEFITool-UE 0.10.0 是谁的刚需:把 BIOS 固件拆成一棵可读的树

拿到一块主板 BIOS 或者电脑厂商发布的固件升级包,多数人第一反应是“先反汇编”,这个思路十有八九会卡住。UEFI 固件不是一段连续可执行代码,而是一层层嵌套的容器:镜像里包着 Firmware Volume,Volume 里包着 File,File 里又包着 Section,中间还夹杂压缩段、填充区、GUID 表。直接拖进反汇编工具,你看到的是失效的地址和没解开的 LZMA 数据块。UEFITool-UE 0.10.0 这类解析器解决的就是“拆”的问题:它把整片固件还原成一棵可视化的树,让你能按模块导出 PE32、ACPI 表、驱动和变量区,再做哈希比对、差异分析或反汇编。这个系列是 UEFITool 新引擎分支,专门优化了对现代固件里非标准嵌套、新压缩算法和怪异对齐的识别。适合搞固件安全测试、供应链审计、主板维修定位问题,以及想在固件层面做二道检验的运维和开发。

2. UEFI 固件为什么难拆:从 Firmware Volume 到 Section 的嵌套模型

想用好 UEFITool-UE 0.10.0,得先理解它在拆什么。UEFI 规范和 Intel 的 PI 规范定义了一套“容器套容器”的固件布局。表面上看 BIOS 是一颗 SPI Flash 芯片的完整二进制,实际上是多个区域的拼盘:最前面是 Intel 固件描述符(IFD),中间是若干个 Firmware Volume,后面可能还有 NVRAM 变量区、Gigabyte 或 OEM 自定义布局。解析固件,核心工作就是把这些区域按头和指针逐步解开。

2.1 FV→File→Section 三层容器:反汇编之前得先解对壳

Firmware Volume(FV)是最大的逻辑容器,头部有 FV Header,记录着 GUID、块大小、格式版本。FV 内部被切成一连串 Firmware File,每个 File 有 FFS Header,头部里定义了文件类型:有 PEIM、DXE 驱动、ACPI 表、NVRAM 变量、FFS_PAD 填充文件等。而一个 File 内部还可以继续拆成多个 Section,常见的 Section 类型包括 PE32 镜像、UI 字符串、版本字符串、依赖表达式(DXE_DEPEX)和 GUID 定义数据。

UEFITool-UE 解析时,就是沿着这条链逐级剥:读 FV Header 找 FFS 条目边界,读 FFS Header 判断 File 类型和大小,如果 File 包含压缩段(通常是 EFI_COMPRESSION_SECTION),就用内嵌的 Tiano LZMA 或 Efi 压缩算法解开,再递归进去找嵌套的 FV 或 File。理解了这个模型,你才看得懂工具界面里那些带缩进的树节点是什么意思,也才知道为什么一个“DXE 驱动”真正的可执行文件藏在好几层里。多数固件分析翻车,都是因为绕过了这层嵌套,直接拿整机固件当二进制看。

2.2 为什么选 UEFITool-UE 而不是老版 UEFITool 或 MMTool

老版本 UEFITool 和 MMTool 本身能完成基本提取,但对两个场景无能为力:遇到非 Intel 标准的 FV 对齐方式时,边界识别错乱,导致后续模块位置全偏移;遇到嵌套压缩段或新版本 LZMA 时,要么解不开,要么直接报错退出。UEFITool-UE 是社区维护的新引擎分支,改动不是加几个按钮,而是把“解析骨架”重写了。它按规范逐个解释 FV Map、FfsMap 和 GUID 定义,遇到未知结构会标注出来而不是静默跳过,这对找隐藏模块意义很大。

还有一个选型理由:UE 分支能同时输出“原样保护区”和“拆解结果”,你看得到哪个包头被保留、哪一层被解压。相比之下,不少老工具把解压过程当黑匣子,只给结果不给过程。对调试来说,能知道“这个文件在镜像里的物理偏移是多少、逻辑偏移是多少”,非常有价值。同类的工具还有 UBU 和 ifrextractor,但 UE 的覆盖面最全,对新固件支持最主动。如果你手里既有 BIOS 整片镜像,又有 OEM 出的 .cap 升级包,我一般建议只留 UE 一个工具走完全流程,避免多个工具导出来的中间产物互相矛盾。

3. 拿到 tar.gz 后本地编译 0.10.0:依赖、构建和第一次打开镜像

UEFITool-UE 官方发布形式里,Linux 平台经常给的是源码包。这个包不大,编译也不慢,但有一个很多人栽过的坑:不装 ICU 库就直接 cmake,编出来的程序要么无法处理 UTF-16 字符串,要么在解析 UI Section 时段错误。我建议按下面这套顺序走,能少踩两个坑。

3.1 编译前的依赖检查:Qt 与 ICU 版本为什么不能省

图形界面依赖 Qt,命令行工具依赖 ICU。ICU 在这里不是可选项,UEFI 内部字符串大量使用 UTF-16LE,若没有 ICU 的转换支持,UNI 文本和 UI 段内容会乱码,严重时直接崩溃。此外还需要 cmake 和一个可用的 C++ 编译器。

# Debian/Ubuntu 系示例,其他发行版对应包名自行替换 sudo apt update sudo apt install -y cmake g++ build-essential qtbase5-dev libicu-dev tar -xf UEFITool-UE-0.10.0.tar.gz cd UEFITool-UE-0.10.0 cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build -j"$(nproc)"

这段命令背后的逻辑:先让系统具备三样东西——Qt 头文件和库(提供窗口框架)、ICU(提供字符集和文本处理)、cmake(提供构建配置)。cmake 后面-DCMAKE_BUILD_TYPE=Release不是玄学,它会让编译器开优化,UEFITool 处理几十 MB 固件时,Release 和 Debug 的耗时差距能到数倍。-j"$(nproc)"是并行编译,核心多时能明显提速。构建完,build目录里会出现UEFITool、UEFIExtract等可执行文件。

注意:如果你用的是 Qt6 环境,需要确认 CMakeLists 里是否已经把find_package(Qt6)的兼容处理写好;某些版本源码只适配 Qt5,硬编译会报错。这时最简单的做法是同时装 qt5-default 并显式把 QT_DIR 指到 Qt5 的 lib/cmake 路径。别用系统里残留的旧 Qt4,初期版本名看着一样,编译出来的界面能跑,一打开固件就崩。

3.2 用图形界面定位一个 DXE 驱动并导出 body

编译完成后,先把整片固件丢进图形界面跑一遍。

./build/UEFITool firmware.rom

界面左侧是一棵完整的树。展开第一个 Firmware Volume,再展开里面的 Firmware File,你会看到每个文件的信息区里有 GUID、类型、大小、物理偏移和校验状态。要导出一个模块,右键某个 File,选项里能看到两种导出:Extract as is和Export body。它们的区别直接决定你后续工作是否顺利:

选项导出的内容适用场景
Extract as is包含 FFS Header 在内的完整原始区段需要保持镜像布局、回写打补丁时
Export body去掉 FFS 头,只保留 Section 数据把驱动拖进反汇编器、算哈希、做差异对比

我第一次用的时候没注意这两个选项,全用 Extract as is 导出,结果对比某个驱动和官方清单时哈希怎么都对不上,折腾了半天才发现多了几十字节的头部。理解体导出逻辑之后,习惯是先Export body拿到干净的 PE32,再视情况决定是否回原镜像里做 patch,两条路径分开走,而不是混在一起。

界面右下角的 Structures 标签页值得养成习惯:选中一个 File,这里会逐字节解释各字段含义,包括 EFI_FFS_FILE_HEADER 里的 IntegrityCheck、Type、Size。遇到解析警告,先切到这个页看哪一段偏移没对上,再决定是修正对齐还是换别的 FV 布局去解析。

4. 命令行批量提取固件:UEFIExtract 的参数、输出与自动化

图形界面适合单次、探索式分析,但固件库稍大一点就撑不住了。一次固件更新里往往包含 EFI 驱动、OpROM、ACPI 表、微码、Logo 图片,逐个人工导出会累到怀疑人生。这时候应该切到命令行工具 UEFIExtract,把它跑成批处理脚本,再把输出目录交给下游分析。

4.1 先跑 -h 看清楚开关:不同版本的 CLI 语义有差异

UE 0.10.0 的命令行参数和更老的 0.28.x 不是完全兼容的,所以我建议你路径确定后先执行一次帮助命令,把本版本的开关含义确认清楚,别凭记忆直接写脚本。

./build/UEFIExtract -h

常见输出里会出现几个核心开关:一个作用于提取模式,一个用于指定输出类型,还有一个用于只提取文件名匹配的条目。不同版本写法略有差异,有的版本用-a表示 all files,有的版本要求-all;也有的版本允许-acpi只提 ACPI 表、-drivers只提驱动。实际使用中,我一般这样落地:

# 全量提取:把所有能识别的内容按目录结构导出 ./build/UEFIExtract firmware.rom -all # 仅输出概要而不提取,适合快速摸清固件组成 ./build/UEFIExtract firmware.rom -table

这两种模式对应两类诉求:-all是要真正拿到文件做后续分析,-table只是先看这个固件里有什么。前者耗时和镜像大小成正比,后者速度快、输出精炼,适合大批量固件先“扫雷”。如果固件是 Qcow2 里导出的原始 flash 或者带 SPI 描述符的完整镜像,UEFIExtract 也能直接吃,不需要预先切分区域。它会自动跳过 IFD 里的非 UEFI 区域,只解析可识别的 FV。

4.2 全量导出后,怎么从 dump 目录里找关键文件

全量提取完成后,输出目录会按 GUID 命名生成大量子目录,结构大致是:

firmware.rom.dump/ 9454F4D0-A2C7-4F8E-9F2A-.../ body header <某个 Section 名>.<扩展名> ...

一个重要经验:不要靠文件名找东西。UEFI 固件里的 File 很少带人类可读的名字,UI Section 里可能有,也可能没有。可靠的定位方式是先看 table 输出,拿到关键 GUID,再去 dump 目录里按 GUID 对应文件夹找。另一个好用的小习惯是导完后直接对 body 文件批量算 SHA256,把结果作为基线存下来,后续固件版本做比对时省很多时间。

dump_dir="firmware.rom.dump" find "$dump_dir" -name body -print0 | sort -z | xargs -0 sha256sum > baseline_sha.txt wc -l baseline_sha.txt

这里find ... -name body只挑导出的正文文件,不碰 header;sort -z是为了让后续 diff 时文件顺序固定;sha256sum输出的一列哈希加一列路径,就是天然的指纹清单。执行完可以用wc -l检查数量是否和 table 里展示的 Firmware File 总数一致,不一致时先不要启动下游分析,回到图形界面看是哪些文件没有被正确解析。

批量脚本在这里体现价值:你不需要打开窗口、右键、层层展开,一条命令把所有固件拍完,输出稳定的目录结构,后面无论是 grep 还是接入自动化流程都很顺。这也是命令行版存在的真正意义——把“拆固件”从人工操作变成投产流程里的一个环节。

5. UEFI 解包避坑清单:5 个让分析翻车的常见问题

做得多了,遇到的问题翻来覆去就是那几类。我把最容易让人卡住的现象、原因和解决办法列一下,每一条背后都有真实的排查经历,照着排查能帮你少走很多弯路。

5.1 FV 文件解析失败:不是固件坏了,是引擎版本不对

现象:用旧版 UEFITool 打开某个 2023 年后发布的板卡固件,树形结构里几个 FV 直接显示红色错误,或者展开后是空的;有时整个界面只解析出第一个 FV,后面全断掉。直觉判断是固件损坏,重新下载还是同样结果。

原因:这些新固件采用了非标准 FV 对齐方式,或者 FV Header 里的 ExtHeaderOffset 指到了一个旧引擎没预料的偏移。旧引擎按固定对齐扫描,边界就错了,整个 Volume 的 FFS 条目全偏移。

解决:改用 UE 0.10.0 打开同一镜像;如果还是失败,看一下 Structures 页里具体是哪一段偏移对不上,然后手动在设置里尝试不同 FV 对齐猜测。UE 的解析器对这类“非标”容忍度高很多,多数情况能自动跳过填充区并正确识别边界。记住一个判断标准:解析后 FFS 条目数量和布局符合预期才叫正常,一片红不要视为“固件加密了”。

5.2 提取回来的模块哈希对不上:小心 Extract as is 和 body 的区别

现象:从固件里提取了某个 DXE 驱动,和之前抓的官方发布版对比 SHA256,怎么都不一样。查了文件大小,发现比官方版本大了几十字节,或者恰好小了几十字节。身体感受:以为自己拿错了固件,或者怀疑驱动被修改过,白排查了半天。

原因:提取时选了Extract as is,导出的内容包含 FFS Header(通常 24 或 32 字节),而官方发布版通常只对应 body 里的 PE32。带不带头,哈希当然不同。

解决:需要做版本比对时,统一用Export body导出,再算哈希。脚本里也一样,直接用find ... -name body取正文件即可。如果某个模块 body 还是个嵌套容器(例如一个 FV 文件里包含多个 FFS),用图形界面对该文件再次执行提取,直到拿到真正的 PE32 数据段。

5.3 满屏 Pad File 不是异常:真正的风险藏在未命名模块里

现象:展开 Firmware File 列表时,目录里一半条目是Pad File,旁边还跟着一堆大小只有几十字节的小文件。新手看着觉得解包没解对,或者固件被恶意填充了,于是反复重跑。

原因:FFS_PAD 是规范允许的填充条目,用于对齐或预留空间,不是异常。但另一个情况要警惕:有些 OEM 会把实际模块的 Name 字段留空或者写成不常见 GUID,导致工具识别成“未命名模块”,排在列表末尾。

解决:Pad File 直接跳过,不要浪费时间。未命名模块则要认真对待:记录其 GUID、类型、大小和依赖表达式。如果在两个固件版本的差异分析里,新版固件多出一个未命名 DXE 驱动,这就是需要重点审计的对象。我的习惯是:先把所有未命名模块单独导出,看是否有导入表、是否有 EFI Shell 命令字符串,至少确认它不是空壳。

5.4 升级包不等于整片镜像:cap/exe 要先解出内核

现象:分析主板厂商给的 BIOS 升级包,下载下来是个 .exe 或 .cap 文件,直接拖进 UEFITool-UE 0.10.0,界面提示无法识别文件类型。有人因此判断升级包加密了,或者 UEFITool 不支持该厂商。

原因:.cap 或 Windows 的 .exe 升级包外壳里往往包着一段 EFI 应用程序,运行时校验完硬件型号才把真正的 FV 释放到内存或写入 flash。UEFITool 只识别裸固件镜像、FV 镜像和部分带 IFD 的完整 ROM,它不认识这个“可执行外壳”。

解决:先拆外壳。常见做法是把 .exe 用解包工具拆出内部的 .cap;如果升级包是 .fd 或 .bin,但带非标准前缀,可以先在 010 Editor 或十六进制工具里搜索_FVH(FV 头签名),从该位置往后裁剪出合法 FV 区域,再丢回 UEFITool。裁剪时注意保留完整 FV Header 和全部块数据,只去掉前面的 shell 段。这个流程熟练后,处理 OEM 升级包比处理完整 ROM 还快。

5.5 NVRAM 不能当普通文件乱改:改完校验直接挂

现象:全量提取后发现了 NVRAM 变量区文件,有人觉得改掉其中某个启动项再刷回去,就能实现启动配置调整。结果刷完机器无法点亮,或者反复自动重置到默认值。

原因:NVRAM 区有自身的变量存储结构,每个变量还有 Attribute,区域级有校验和和写保护状态;更重要的是,多数固件在 DXE 阶段会校验变量区完整性。直接替换整个 NVRAM 文件,等于把固件打成了不一致状态,轻则配置被清空,重则启动卡死在变量初始化。

解决:UEFITool 导出的 NVRAM 只用于分析,不要直接回写。要修改单个变量,要么用成熟的 UEFI 变量编辑工具连到设备的 SPI 上进行规范化改写并处理好校验,要么在 UEFI Shell 环境里用命令调整。把“解析固件”和“修改固件”分开看待,是我在做固件分析时最看重的一条边界。

6. 进阶用法:把 UEFITool-UE 0.10.0 做成固件差异分析基线

工具顺手之后,下一步是把它嵌进固件安全测试流程。我觉得最实用的进阶技巧,是用脚本对比两次导出的 table,让固件升级带来的差异一目了然,而不是肉眼在一堆 GUID 里翻找。另一个习惯是提前维护一份 GUID 白名单,减少无效告警。

6.1 用脚本对比两次导出的 JSON 表,抓新增模块

先在每个固件版本上执行UEFIExtract -table,保留结构化的输出。两个版本的表格分别保存为old_table.json和new_table.json,然后用下面的脚本比较:

import json, sys def load(fn): with open(fn, encoding="utf-8") as f: return json.load(f) def key(item): # 以 GUID 和类型作为唯一标识 return (item.get("guid", ""), item.get("type", "")) base = {key(x) for x in load("old_table.json")} new = {key(x) for x in load("new_table.json")} for item in load("new_table.json"): k = key(item) if k in base: continue print("新增:", item.get("guid"), item.get("type"), item.get("size"))

说明:load读两个 JSON;key函数用 GUID 加类型做唯一键;集合运算直接筛出新增条目;最后打印的新增项往往是最需要人工审计的候选对象。脚本只看 GUID 和类型,不看文件名,是因为 UEFI 固件里文件名字段不可靠,GUID 才是规范定义的稳定标识。跑完之后,把新增项粘贴到查询平台,看它属于哪个厂家、哪个协议,再决定要不要继续往下挖。这里一个细节:部分固件每次升级都会重新生成签名证书的 GUID,第一次跑会发现证书相关条目全是新增,这不是异常,属于正常发布行为,白名单里放过即可。

6.2 提前维护一份 GUID 白名单,让差异分析少一点噪音

差异分析跑多了,会发现 80% 的新增条目是固定的:微码更新、板卡 Logo、ACPI 表。只有剩下 20% 需要人肉看。把前者沉淀成白名单,能显著减少每次升级审计的噪音。我的做法是在固件分析工作目录下维持一个whitelist.json,记着已知安全模块的 GUID、用途和最后验证日期,脚本比对时直接过滤掉。

# 把当前固件导出的 GUID 列表和历史的正常基线打一个 diff grep -oE '[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}' new_table.json | sort -u > current_guids.txt comm -13 <(sort -u whitelist_guids.txt) current_guids.txt

这个操作等于把白名单当“已知好人”,comm -13输出的是当前固件里出现了但白名单里没有的 GUID。出现一个陌生条目,再回到图形界面定位、导出 body、查导入表或字符串,效率高很多。以前没有这套流水线时,每次升级公告出来我都得重新摸一遍固件,重复劳动严重;现在把白名单做进流程,升级审计基本能压缩到十几分钟。

这些年折腾固件的最大教训,是别把解析工具当黑匣子。它给出的 GUID、边界、校验和,本身就是审计线索;比对着线索去核对模块,远比闷头反汇编或盲目信任旧脚本可靠。工具轮子很多,建立一套自己的差异基线,才是让分析从手工走向半自动的关键。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询