diff --git a/chipsec/hal/spi_uefi.py b/chipsec/hal/spi_uefi.py index 8aed2150..12c5189f 100644 --- a/chipsec/hal/spi_uefi.py +++ b/chipsec/hal/spi_uefi.py @@ -253,40 +253,46 @@ def build_efi_modules_tree( _uefi, fwtype, data, Size, offset, polarity ): _off, next_offset, _name, _type, _img, _hdrsz = NextFwFileSection( data, Size, offset, polarity ) while next_offset is not None: - sec = EFI_SECTION( _off, _name, _type, _img, _hdrsz ) - # pick random file name in case dumpall=False - we'll need it to decompress the section - sec_fs_name = "sect%02d_%s" % (secn, ''.join(random.choice(string.ascii_lowercase) for _ in range(4))) + if _name is not None: + sec = EFI_SECTION( _off, _name, _type, _img, _hdrsz ) + # pick random file name in case dumpall=False - we'll need it to decompress the section + sec_fs_name = "sect%02d_%s" % (secn, ''.join(random.choice(string.ascii_lowercase) for _ in range(4))) - if sec.Type in EFI_SECTIONS_EXE: - # "leaf" executable section: update hashes and check against match criteria - sec.calc_hashes( sec.HeaderSize ) - elif sec.Type == EFI_SECTION_USER_INTERFACE: - # "leaf" UI section: update section's UI name - sec.ui_string = unicode(sec.Image[sec.HeaderSize:], "utf-16-le", errors="ignore")[:-1] - elif sec.Type == EFI_SECTION_GUID_DEFINED: - guid0, guid1, guid2, guid3, sec.DataOffset, sec.Attributes = struct.unpack(EFI_GUID_DEFINED_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_GUID_DEFINED_SECTION_size]) - sec.Guid = guid_str(guid0, guid1, guid2, guid3) - - # "container" sections: keep parsing - if sec.Type in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW): - if sec.Type == EFI_SECTION_COMPRESSION: - ul, ct = struct.unpack(EFI_COMPRESSION_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_COMPRESSION_SECTION_size]) - d = decompress_section_data( _uefi, "", sec_fs_name, sec.Image[sec.HeaderSize+EFI_COMPRESSION_SECTION_size:], ct, True ) - if d: - sec.children = build_efi_modules_tree( _uefi, fwtype, d, len(d), 0, polarity ) + if sec.Type in EFI_SECTIONS_EXE: + # "leaf" executable section: update hashes and check against match criteria + sec.calc_hashes( sec.HeaderSize ) + elif sec.Type == EFI_SECTION_USER_INTERFACE: + # "leaf" UI section: update section's UI name + sec.ui_string = unicode(sec.Image[sec.HeaderSize:], "utf-16-le", errors="ignore")[:-1] elif sec.Type == EFI_SECTION_GUID_DEFINED: - if sec.Guid == EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID: - sec.children = build_efi_modules_tree( _uefi, fwtype, sec.Image[sec.DataOffset:], Size - sec.DataOffset, 0, polarity ) - elif sec.Guid == LZMA_CUSTOM_DECOMPRESS_GUID or sec.Guid == TIANO_DECOMPRESSED_GUID: - d = decompress_section_data( _uefi, "", sec_fs_name, sec.Image[sec.DataOffset:], 2, True ) + guid0, guid1, guid2, guid3, sec.DataOffset, sec.Attributes = struct.unpack(EFI_GUID_DEFINED_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_GUID_DEFINED_SECTION_size]) + sec.Guid = guid_str(guid0, guid1, guid2, guid3) + + # "container" sections: keep parsing + if sec.Type in (EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW): + if sec.Type == EFI_SECTION_COMPRESSION: + ul, ct = struct.unpack(EFI_COMPRESSION_SECTION, sec.Image[sec.HeaderSize:sec.HeaderSize+EFI_COMPRESSION_SECTION_size]) + d = decompress_section_data( _uefi, "", sec_fs_name, sec.Image[sec.HeaderSize+EFI_COMPRESSION_SECTION_size:], ct, True ) + if (d is None) and (ct == 2) and (len(sec.Image[sec.HeaderSize+EFI_COMPRESSION_SECTION_size:]) > 4): + d = decompress_section_data( _uefi, "", sec_fs_name, sec.Image[sec.HeaderSize+EFI_COMPRESSION_SECTION_size + 4:], ct, True ) if d: sec.children = build_efi_modules_tree( _uefi, fwtype, d, len(d), 0, polarity ) - elif sec.Guid == FIRMWARE_VOLUME_GUID or sec.Guid == VOLUME_SECTION_GUID: + elif sec.Type == EFI_SECTION_GUID_DEFINED: + if sec.Guid == EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID: + sec.children = build_efi_modules_tree( _uefi, fwtype, sec.Image[sec.DataOffset:], Size - sec.DataOffset, 0, polarity ) + elif sec.Guid == LZMA_CUSTOM_DECOMPRESS_GUID or sec.Guid == TIANO_DECOMPRESSED_GUID: + d = decompress_section_data( _uefi, "", sec_fs_name, sec.Image[sec.DataOffset:], 2, True ) + if d is None: + d = decompress_section_data( _uefi, "", sec_fs_name, sec.Image[sec.HeaderSize+EFI_GUID_DEFINED_SECTION_size:], 2, True ) + if d: + sec.children = build_efi_modules_tree( _uefi, fwtype, d, len(d), 0, polarity ) + #elif sec.Guid == FIRMWARE_VOLUME_GUID: + else: + sec.children = build_efi_model( _uefi, sec.Image[sec.HeaderSize:], fwtype ) + elif sec.Type in (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW): sec.children = build_efi_model( _uefi, sec.Image[sec.HeaderSize:], fwtype ) - elif sec.Type in (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW): - sec.children = build_efi_model( _uefi, sec.Image[sec.HeaderSize:], fwtype ) - sections.append(sec) + sections.append(sec) _off, next_offset, _name, _type, _img, _hdrsz = NextFwFileSection( data, Size, next_offset, polarity ) secn += 1 return sections @@ -368,7 +374,18 @@ def update_efi_tree(modules, parent_guid=None): return ui_string def build_efi_model( _uefi, data, fwtype ): - model = build_efi_tree( _uefi, data, fwtype ) + # Try PFS first + result = ParsePFS(data) + if result is not None: + model = [] + for d in result[0]: + m = build_efi_tree( _uefi, d, fwtype ) + model.extend(m) + if len(result[1]) > 0: + m = build_efi_tree( _uefi, result[1], fwtype ) + model.extend(m) + else: + model = build_efi_tree( _uefi, data, fwtype ) update_efi_tree(model) return model diff --git a/chipsec/hal/uefi_common.py b/chipsec/hal/uefi_common.py index 84cf9cdd..95415f8b 100644 --- a/chipsec/hal/uefi_common.py +++ b/chipsec/hal/uefi_common.py @@ -632,7 +632,7 @@ def NextFwFile(FvImage, FvLength, fof, polarity): next_offset = None res = None update_or_deleted = False - if (fof + file_header_size) <= FvLength: + if (fof + file_header_size) <= min(FvLength, len(FvImage)): if ('\xff\xff\xff\xff' == FvImage[fof+file_header_size-4:fof+file_header_size]): next_offset = fof + 8 return (cur_offset, next_offset, None, None, None, None, None, None, None, None, update_or_deleted, None) diff --git a/chipsec/hal/uefi_platform.py b/chipsec/hal/uefi_platform.py index 87087b4f..039b743c 100644 --- a/chipsec/hal/uefi_platform.py +++ b/chipsec/hal/uefi_platform.py @@ -41,6 +41,83 @@ from collections import namedtuple from chipsec import defines from chipsec.hal.uefi_common import * +################################################################################################# +# Dell PFS support, +# relied heavily on uefi-firmware-parser (https://github.com/theopolis/uefi-firmware-parser) +################################################################################################# + +PFS_SEC_HDR = "<16sIIIIIIIIII16s" +PFS_SEC_HDR_SIZE = struct.calcsize(PFS_SEC_HDR) +U1_GUID = '\xf6\xe2\xb3YBN\xf3A\xb1\xf4Dj\x84\xbf\xc6\xd0' + +class PfsFileSection: + def __init__(self, data): + self.data = data + self.valid = (len(data) >= PFS_SEC_HDR_SIZE) + data_offset = 0 + if (self.valid): + gu1, u1, u2, u3, u4, u5, u6, sec_size, size1, size2, size3, gu2 = struct.unpack(PFS_SEC_HDR, data[:PFS_SEC_HDR_SIZE]) + self.valid = (len(data) >= (PFS_SEC_HDR_SIZE+sec_size+size1+size2+size3)) + if gu1 == U1_GUID: data_offset = 0x248 + if (self.valid): + self.body = data[PFS_SEC_HDR_SIZE+data_offset:PFS_SEC_HDR_SIZE+sec_size] + self.tail = data[PFS_SEC_HDR_SIZE+sec_size+size1+size2+size3:] + + def parse(self): + return self.body + +PFS_HDR_SIG = "PFS.HDR." +PFS_FTR_SIG = "PFS.FTR." +PFS_HDR_STRUC = "<8sII" +PFS_HDR_STRUC_SIZE = struct.calcsize(PFS_HDR_STRUC) +PFS_FTR_STRUC = "= (PFS_HDR_STRUC_SIZE + PFS_FTR_STRUC_SIZE)) + self.size = 0 + hdr_sig = "" + ver = 0 + if (self.valid): + hdr_sig, ver, self.size = struct.unpack(PFS_HDR_STRUC, data[:PFS_HDR_STRUC_SIZE]) + self.valid = (PFS_FTR_STRUC_SIZE <= len(data[PFS_HDR_STRUC_SIZE+self.size:])) + if (self.valid): + ftr_size, u, ftr_sig = struct.unpack(PFS_FTR_STRUC, data[PFS_HDR_STRUC_SIZE+self.size:PFS_HDR_STRUC_SIZE+self.size+PFS_FTR_STRUC_SIZE]) + self.valid = (hdr_sig == PFS_HDR_SIG) and (ftr_sig == PFS_FTR_SIG) and (self.size == ftr_size) and ((self.size + PFS_HDR_STRUC_SIZE + PFS_FTR_STRUC_SIZE) <= len(data)) + if (self.valid): + self.body = data[PFS_HDR_STRUC_SIZE:PFS_HDR_STRUC_SIZE + self.size + PFS_FTR_STRUC_SIZE] + self.tail = data[PFS_HDR_STRUC_SIZE + self.size + PFS_FTR_STRUC_SIZE:] + + def parse(self): + pfs_sec = PfsFileSection(self.body) + pfs_sec_data = [] + while pfs_sec.valid: + sec_data = pfs_sec.parse() + if sec_data[:len(PFS_HDR_SIG)] == PFS_HDR_SIG: + sec_data = PfsFile(sec_data, True).parse() + if sec_data is not None: + pfs_sec_data.append(sec_data) + pfs_sec = PfsFileSection(pfs_sec.tail) + if self.concat: + return ''.join(pfs_sec_data) + else: + return pfs_sec_data + +def ParsePFS(data): + pfs_file = PfsFile(data, True) + if not pfs_file.valid: + return None + pfs_file_data = [] + while pfs_file.valid: + pfs_data = pfs_file.parse() + if pfs_data is not None: + pfs_file_data.append(pfs_data) + pfs_file = PfsFile(pfs_file.tail) + return (pfs_file_data, pfs_file.data) #################################################################################################3 # List of supported types of EFI NVRAM format (platform/vendor specific)