""" EFI Platform Initialization Firmware Volume parser. Author: Alexandre Boeglin Creation date: 08 jul 2007 """ from hachoir.parser import Parser from hachoir.field import (FieldSet, UInt8, UInt16, UInt24, UInt32, UInt64, Enum, CString, String, PaddingBytes, RawBytes, NullBytes) from hachoir.core.endian import LITTLE_ENDIAN from hachoir.core.tools import paddingSize, humanFilesize from hachoir.parser.common.win32 import GUID EFI_SECTION_COMPRESSION = 0x1 EFI_SECTION_GUID_DEFINED = 0x2 EFI_SECTION_PE32 = 0x10 EFI_SECTION_PIC = 0x11 EFI_SECTION_TE = 0x12 EFI_SECTION_DXE_DEPEX = 0x13 EFI_SECTION_VERSION = 0x14 EFI_SECTION_USER_INTERFACE = 0x15 EFI_SECTION_COMPATIBILITY16 = 0x16 EFI_SECTION_FIRMWARE_VOLUME_IMAGE = 0x17 EFI_SECTION_FREEFORM_SUBTYPE_GUID = 0x18 EFI_SECTION_RAW = 0x19 EFI_SECTION_PEI_DEPEX = 0x1b EFI_SECTION_TYPE = { EFI_SECTION_COMPRESSION: "Encapsulation section where other sections" + " are compressed", EFI_SECTION_GUID_DEFINED: "Encapsulation section where other sections" + " have format defined by a GUID", EFI_SECTION_PE32: "PE32+ Executable image", EFI_SECTION_PIC: "Position-Independent Code", EFI_SECTION_TE: "Terse Executable image", EFI_SECTION_DXE_DEPEX: "DXE Dependency Expression", EFI_SECTION_VERSION: "Version, Text and Numeric", EFI_SECTION_USER_INTERFACE: "User-Friendly name of the driver", EFI_SECTION_COMPATIBILITY16: "DOS-style 16-bit EXE", EFI_SECTION_FIRMWARE_VOLUME_IMAGE: "PI Firmware Volume image", EFI_SECTION_FREEFORM_SUBTYPE_GUID: "Raw data with GUID in header to" + " define format", EFI_SECTION_RAW: "Raw data", EFI_SECTION_PEI_DEPEX: "PEI Dependency Expression", } EFI_FV_FILETYPE_RAW = 0x1 EFI_FV_FILETYPE_FREEFORM = 0x2 EFI_FV_FILETYPE_SECURITY_CORE = 0x3 EFI_FV_FILETYPE_PEI_CORE = 0x4 EFI_FV_FILETYPE_DXE_CORE = 0x5 EFI_FV_FILETYPE_PEIM = 0x6 EFI_FV_FILETYPE_DRIVER = 0x7 EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER = 0x8 EFI_FV_FILETYPE_APPLICATION = 0x9 EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0xb EFI_FV_FILETYPE_FFS_PAD = 0xf0 EFI_FV_FILETYPE = { EFI_FV_FILETYPE_RAW: "Binary data", EFI_FV_FILETYPE_FREEFORM: "Sectioned data", EFI_FV_FILETYPE_SECURITY_CORE: "Platform core code used during the SEC" + " phase", EFI_FV_FILETYPE_PEI_CORE: "PEI Foundation", EFI_FV_FILETYPE_DXE_CORE: "DXE Foundation", EFI_FV_FILETYPE_PEIM: "PEI module (PEIM)", EFI_FV_FILETYPE_DRIVER: "DXE driver", EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: "Combined PEIM/DXE driver", EFI_FV_FILETYPE_APPLICATION: "Application", EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: "Firmware volume image", EFI_FV_FILETYPE_FFS_PAD: "Pad File For FFS", } for x in range(0xc0, 0xe0): EFI_FV_FILETYPE[x] = "OEM File" for x in range(0xe0, 0xf0): EFI_FV_FILETYPE[x] = "Debug/Test File" for x in range(0xf1, 0x100): EFI_FV_FILETYPE[x] = "Firmware File System Specific File" class BlockMap(FieldSet): static_size = 8 * 8 def createFields(self): yield UInt32(self, "num_blocks") yield UInt32(self, "len") def createDescription(self): return "%d blocks of %s" % ( self["num_blocks"].value, humanFilesize(self["len"].value)) class FileSection(FieldSet): COMPRESSION_TYPE = { 0: 'Not Compressed', 1: 'Standard Compression', } def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) self._size = self["size"].value * 8 section_type = self["type"].value if section_type in (EFI_SECTION_DXE_DEPEX, EFI_SECTION_PEI_DEPEX): # These sections can sometimes be longer than what their size # claims! It's so nice to have so detailled specs and not follow # them ... if self.stream.readBytes(self.absolute_address + self._size, 1) == b'\0': self._size = self._size + 16 def createFields(self): # Header yield UInt24(self, "size") yield Enum(UInt8(self, "type"), EFI_SECTION_TYPE) section_type = self["type"].value if section_type == EFI_SECTION_COMPRESSION: yield UInt32(self, "uncomp_len") yield Enum(UInt8(self, "comp_type"), self.COMPRESSION_TYPE) elif section_type == EFI_SECTION_FREEFORM_SUBTYPE_GUID: yield GUID(self, "sub_type_guid") elif section_type == EFI_SECTION_GUID_DEFINED: yield GUID(self, "section_definition_guid") yield UInt16(self, "data_offset") yield UInt16(self, "attributes") elif section_type == EFI_SECTION_USER_INTERFACE: yield CString(self, "file_name", charset="UTF-16-LE") elif section_type == EFI_SECTION_VERSION: yield UInt16(self, "build_number") yield CString(self, "version", charset="UTF-16-LE") # Content content_size = (self.size - self.current_size) // 8 if content_size == 0: return if section_type == EFI_SECTION_COMPRESSION: compression_type = self["comp_type"].value if compression_type == 1: while not self.eof: yield RawBytes(self, "compressed_content", content_size) else: while not self.eof: yield FileSection(self, "section[]") elif section_type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE: yield FirmwareVolume(self, "firmware_volume") else: yield RawBytes(self, "content", content_size, EFI_SECTION_TYPE.get(self["type"].value, "Unknown Section Type")) def createDescription(self): return EFI_SECTION_TYPE.get(self["type"].value, "Unknown Section Type") class File(FieldSet): def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) self._size = self["size"].value * 8 def createFields(self): # Header yield GUID(self, "name") yield UInt16(self, "integrity_check") yield Enum(UInt8(self, "type"), EFI_FV_FILETYPE) yield UInt8(self, "attributes") yield UInt24(self, "size") yield UInt8(self, "state") # Content while not self.eof: yield FileSection(self, "section[]") def createDescription(self): return "%s: %s containing %d section(s)" % ( self["name"].value, self["type"].display, len(self.array("section"))) class FirmwareVolume(FieldSet): def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) if not self._size: self._size = self["volume_len"].value * 8 def createFields(self): # Header yield NullBytes(self, "zero_vector", 16) yield GUID(self, "fs_guid") yield UInt64(self, "volume_len") yield String(self, "signature", 4) yield UInt32(self, "attributes") yield UInt16(self, "header_len") yield UInt16(self, "checksum") yield UInt16(self, "ext_header_offset") yield UInt8(self, "reserved") yield UInt8(self, "revision") while True: bm = BlockMap(self, "block_map[]") yield bm if bm['num_blocks'].value == 0 and bm['len'].value == 0: break # TODO must handle extended header # Content while not self.eof: padding = paddingSize(self.current_size // 8, 8) if padding: yield PaddingBytes(self, "padding[]", padding) yield File(self, "file[]") def createDescription(self): return "Firmware Volume containing %d file(s)" % len(self.array("file")) class PIFVFile(Parser): endian = LITTLE_ENDIAN MAGIC = b'_FVH' PARSER_TAGS = { "id": "pifv", "category": "program", "file_ext": ("bin", ""), "min_size": 64 * 8, # smallest possible header "magic_regex": ((b"\0{16}.{24}" + MAGIC, 0), ), "description": "EFI Platform Initialization Firmware Volume", } def validate(self): if self.stream.readBytes(40 * 8, 4) != self.MAGIC: return "Invalid magic number" if self.stream.readBytes(0, 16) != b"\0" * 16: return "Invalid zero vector" return True def createFields(self): while not self.eof: yield FirmwareVolume(self, "firmware_volume[]")