UEFI固件镜像的“洋葱模型”:从FD到Section的逐层解析
1. 初识UEFI固件的洋葱模型第一次拿到UEFI固件镜像文件时很多人都会感到无从下手。这个看似普通的二进制文件就像一颗洋葱需要一层层剥开才能理解其内部结构。我在逆向分析某厂商BIOS时就曾被这个洋葱辣到眼睛——当时完全看不懂那些复杂的二进制结构。UEFI固件镜像的层级结构可以用FD→FV→FF→Section这条主线来理解。打个比方如果把整个固件比作一栋大楼FD就是整栋建筑FV是各个功能分区如配电房、电梯井FF是每个房间Section则是房间里的具体设施这种层级设计源于UEFI的PI规范Platform Initialization Specification它定义了固件从物理存储到逻辑组织的完整架构。理解这个模型是进行固件分析、漏洞挖掘或定制开发的基础。2. 最外层Firmware Device(FD)解析2.1 FD的物理与逻辑双重身份FD既是物理存储设备如SPI Flash芯片也是逻辑上的完整固件镜像。我拆解过很多开发板发现物理FD通常通过SPI接口与主板连接存储容量从4MB到32MB不等。但在软件层面一个.rom或.fd文件同样可以视为逻辑FD。有个有趣的发现某些厂商的FD会包含多个逻辑分区。比如在某款服务器固件中我通过UEFITool看到前2MB是主板管理控制器(BMC)固件中间8MB是主BIOS最后1MB是配置区2.2 FD的二进制布局特点用hexdump查看FD文件通常会看到以下特征起始部分往往是Intel的Flash描述符0x5AA5F00F签名各区域之间有明显的空白填充0xFF关键结构体都有特定魔数如_FVH这里有个实用技巧用dd命令可以提取FD中的特定区域。例如提取第一个FVdd ifBIOS.rom ofFV1.bin bs1 skip$((0x100000)) count$((0x200000))3. 核心层Firmware Volume(FV)详解3.1 FV的逻辑结构与GUID体系每个FV都是独立的功能单元通过GUID唯一标识。在分析某笔记本固件时我发现这些典型FV0BFC0F70-5B71-4E7A-A9EE-9D069F578DBF (主代码FV)8C8CE578-8A3D-4F1C-9935-896185C32DD3 (ACPI存储)FV头部包含关键信息typedef struct { UINT8 ZeroVector[16]; EFI_GUID FileSystemGuid; UINT64 FvLength; UINT32 Signature; // _FVH UINT32 Attributes; UINT16 HeaderLength; UINT16 Checksum; // ... } EFI_FIRMWARE_VOLUME_HEADER;3.2 FFS2与FFS3的实战区别FFS2和FFS3的主要差异在于文件大小支持FFS2最大16MB适合大多数驱动模块FFS3支持更大文件如图形资源在UEFITool中可以看到两者的GUID差异FFS2: 8C8CE578-8A3D-4F1C-9935-896185C32DD3FFS3: 5473C07A-3DCB-4DCA-BD6F-1E9689E7349A实际案例某显卡BIOS更新包就使用了FFS3来存储大型固件镜像。4. 文件层Firmware File(FF)解剖4.1 FF头部的秘密每个FF都以标准头部开始关键字段包括typedef struct { EFI_GUID Name; EFI_FFS_INTEGRITY_CHECK IntegrityCheck; UINT8 Type; UINT8 Attributes; UINT24 Size; // ... } EFI_FFS_FILE_HEADER;Type字段特别重要常见类型有0x01 SEC核心0x02 PEI模块0x03 DXE驱动0x07 用户界面4.2 特殊文件VTF的定位技巧Volume Top File(VTF)总是位于FV末尾其GUID固定为FFF12B8D-7696-4C8B-A985-2747075B4F50在逆向分析时可以通过以下步骤定位VTF找到FV的_FVH签名解析FvLength获取FV大小从FV末尾向前搜索VTF GUID5. 最内层Section的嵌套艺术5.1 两种基本Section类型Leaf Section直接包含数据常见子类型0x10 PE32镜像0x11 PIC代码0x14 UI字符串Encapsulation Section可以包含其他Section如0x01 压缩Section0x02 GUID定义Section5.2 实际解析案例以提取一个DXE驱动为例在FV中找到Type0x07的FF解析其第一个Section通常是0x19版本信息定位PE32 Section(0x10)用Fixup工具重定位基地址这个过程中最常遇到的坑是Section对齐问题——某些厂商会使用非常规的8字节对齐导致解析工具报错。6. 工具链实战用UEFITool剥洋葱6.1 基础解析流程打开固件镜像文件展开左侧树形结构FD→FV→FF→Section右键任意节点可导出内容使用搜索功能定位特定GUID6.2 高级技巧批量提取PE文件通过Python脚本可以自动化处理from UEFIExtract import UEFIExtract def extract_pe_files(rom_path): uefi UEFIExtract(rom_path) for fv in uefi.FVs: for ff in fv.FFs: if ff.Type 0x07: # DXE驱动 for sec in ff.Sections: if sec.Type 0x10: # PE32 sec.dumpToFile(foutput/{ff.NameGuid}.efi)7. 从理论到实践定制你的固件理解层级结构后可以尝试简单修改替换UI字符串修改对应Section更新特定驱动保持GUID不变添加新模块注意FV剩余空间但要注意三个关键点修改后需要重新计算校验和某些区域有写保护大小变化可能破坏后续偏移我在某次项目中就因为没注意第三点导致修改后的固件无法启动——新驱动比原版大了4KB覆盖了后面的关键结构。