PFS support and parsing fixes. (#196)

This commit is contained in:
andrew-furtak
2017-04-21 09:34:41 -07:00
committed by c7zero
parent 1c09ea5ada
commit f826cb0e78
3 changed files with 124 additions and 30 deletions
+46 -29
View File
@@ -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
+1 -1
View File
@@ -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)
+77
View File
@@ -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 = "<II8s"
PFS_FTR_STRUC_SIZE = struct.calcsize(PFS_FTR_STRUC)
class PfsFile:
def __init__(self, data, concat = False):
self.data = data
self.concat = concat
self.valid = (len(data) >= (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)