## @file # # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent # from __future__ import print_function import array import uuid import re import os import logging import core.pe as pe def GetLogger(): return logging.getLogger('EFI Binary File') class EFIBinaryError(Exception): def __init__(self, message): Exception.__init__(self) self._message = message def GetMessage(self): return self._message class EfiFd(object): EFI_FV_HEADER_SIZE = 0x48 def __init__(self): self._fvs = [] def Load(self, fd, size): index = fd.tell() while (index + self.EFI_FV_HEADER_SIZE < size): fv = EfiFv(self) fv.Load(fd) self._fvs.append(fv) index += fv.GetHeader().GetFvLength() index = align(index, 8) fd.seek(index) def GetFvs(self): return self._fvs class EfiFv(object): FILE_SYSTEM_GUID = uuid.UUID('{8c8ce578-8a3d-4f1c-9935-896185c32dd3}') def __init__(self, parent=None): self._size = 0 self._filename = None self._fvheader = None self._blockentries = [] self._ffs = [] # following field is for FV in FD self._parent = parent self._offset = 0 self._raw = array.array('B') def Load(self, fd): self._offset = fd.tell() self._filename = fd.name # get file header self._fvheader = EfiFirmwareVolumeHeader.Read(fd) #self._fvheader.Dump() self._size = self._fvheader.GetFvLength() if self._fvheader.GetFileSystemGuid() != self.FILE_SYSTEM_GUID: fd.seek(self._offset) self._raw.fromfile(fd, self.GetHeader().GetFvLength()) return # read block map blockentry = BlockMapEntry.Read(fd) self._blockentries.append(blockentry) while (blockentry.GetNumberBlocks() != 0 and blockentry.GetLength() != 0): self._blockentries.append(blockentry) blockentry = BlockMapEntry.Read(fd) if self._fvheader.GetSize() + (len(self._blockentries)) * 8 != \ self._fvheader.GetHeaderLength(): raise EFIBinaryError("Volume Header length not consistent with block map!") index = align(fd.tell(), 8) count = 0 while ((index + EfiFfs.FFS_HEADER_SIZE) < self._size): ffs = EfiFfs.Read(fd, self) if not isValidGuid(ffs.GetNameGuid()): break self._ffs.append(ffs) count += 1 index = align(fd.tell(), 8) fd.seek(self._offset) self._raw.fromfile(fd, self.GetHeader().GetFvLength()) def GetFfs(self): return self._ffs def GetHeader(self): return self._fvheader def GetBlockEntries(self): return self._blockentries def GetHeaderRawData(self): ret = [] ret += self._fvheader.GetRawData() for block in self._blockentries: ret += block.GetRawData() return ret def GetOffset(self): return 0 def GetRawData(self): return self._raw.tolist() class BinaryItem(object): def __init__(self, parent=None): self._size = 0 self._arr = array.array('B') self._parent = parent @classmethod def Read(cls, fd, parent=None): item = cls(parent) item.fromfile(fd) return item def Load(self, fd): self.fromfile(fd) def GetSize(self): """should be implemented by inherited class""" def fromfile(self, fd): self._arr.fromfile(fd, self.GetSize()) def GetParent(self): return self._parent class EfiFirmwareVolumeHeader(BinaryItem): def GetSize(self): return 56 def GetSigunature(self): list = self._arr.tolist() sig = '' for x in list[40:44]: sig += chr(x) return sig def GetAttribute(self): return list2int(self._arr.tolist()[44:48]) def GetErasePolarity(self): list = self.GetAttrStrings() if 'EFI_FVB2_ERASE_POLARITY' in list: return True return False def GetAttrStrings(self): list = [] value = self.GetAttribute() if (value & 0x01) != 0: list.append('EFI_FVB2_READ_DISABLED_CAP') if (value & 0x02) != 0: list.append('EFI_FVB2_READ_ENABLED_CAP') if (value & 0x04) != 0: list.append('EFI_FVB2_READ_STATUS') if (value & 0x08) != 0: list.append('EFI_FVB2_WRITE_DISABLED_CAP') if (value & 0x10) != 0: list.append('EFI_FVB2_WRITE_ENABLED_CAP') if (value & 0x20) != 0: list.append('EFI_FVB2_WRITE_STATUS') if (value & 0x40) != 0: list.append('EFI_FVB2_LOCK_CAP') if (value & 0x80) != 0: list.append('EFI_FVB2_LOCK_STATUS') if (value & 0x200) != 0: list.append('EFI_FVB2_STICKY_WRITE') if (value & 0x400) != 0: list.append('EFI_FVB2_MEMORY_MAPPED') if (value & 0x800) != 0: list.append('EFI_FVB2_ERASE_POLARITY') if (value & 0x1000) != 0: list.append('EFI_FVB2_READ_LOCK_CAP') if (value & 0x00002000) != 0: list.append('EFI_FVB2_READ_LOCK_STATUS') if (value & 0x00004000) != 0: list.append('EFI_FVB2_WRITE_LOCK_CAP') if (value & 0x00008000) != 0: list.append('EFI_FVB2_WRITE_LOCK_STATUS') if (value == 0): list.append('EFI_FVB2_ALIGNMENT_1') if (value & 0x001F0000) == 0x00010000: list.append('EFI_FVB2_ALIGNMENT_2') if (value & 0x001F0000) == 0x00020000: list.append('EFI_FVB2_ALIGNMENT_4') if (value & 0x001F0000) == 0x00030000: list.append('EFI_FVB2_ALIGNMENT_8') if (value & 0x001F0000) == 0x00040000: list.append('EFI_FVB2_ALIGNMENT_16') if (value & 0x001F0000) == 0x00050000: list.append('EFI_FVB2_ALIGNMENT_32') if (value & 0x001F0000) == 0x00060000: list.append('EFI_FVB2_ALIGNMENT_64') if (value & 0x001F0000) == 0x00070000: list.append('EFI_FVB2_ALIGNMENT_128') if (value & 0x001F0000) == 0x00080000: list.append('EFI_FVB2_ALIGNMENT_256') if (value & 0x001F0000) == 0x00090000: list.append('EFI_FVB2_ALIGNMENT_512') if (value & 0x001F0000) == 0x000A0000: list.append('EFI_FVB2_ALIGNMENT_1K') if (value & 0x001F0000) == 0x000B0000: list.append('EFI_FVB2_ALIGNMENT_2K') if (value & 0x001F0000) == 0x000C0000: list.append('EFI_FVB2_ALIGNMENT_4K') if (value & 0x001F0000) == 0x000D0000: list.append('EFI_FVB2_ALIGNMENT_8K') if (value & 0x001F0000) == 0x000E0000: list.append('EFI_FVB2_ALIGNMENT_16K') if (value & 0x001F0000) == 0x000F0000: list.append('EFI_FVB2_ALIGNMENT_32K') if (value & 0x001F0000) == 0x00100000: list.append('EFI_FVB2_ALIGNMENT_64K') if (value & 0x001F0000) == 0x00110000: list.append('EFI_FVB2_ALIGNMENT_128K') if (value & 0x001F0000) == 0x00120000: list.append('EFI_FVB2_ALIGNMENT_256K') if (value & 0x001F0000) == 0x00130000: list.append('EFI_FVB2_ALIGNMENT_512K') return list def GetHeaderLength(self): return list2int(self._arr.tolist()[48:50]) def Dump(self): print('Signature: %s' % self.GetSigunature()) print('Attribute: 0x%X' % self.GetAttribute()) print('Header Length: 0x%X' % self.GetHeaderLength()) print('File system Guid: ', self.GetFileSystemGuid()) print('Revision: 0x%X' % self.GetRevision()) print('FvLength: 0x%X' % self.GetFvLength()) def GetFileSystemGuid(self): list = self._arr.tolist() return list2guid(list[16:32]) def GetRevision(self): list = self._arr.tolist() return int(list[55]) def GetFvLength(self): list = self._arr.tolist() return list2int(list[32:40]) def GetRawData(self): return self._arr.tolist() class BlockMapEntry(BinaryItem): def GetSize(self): return 8 def GetNumberBlocks(self): list = self._arr.tolist() return list2int(list[0:4]) def GetLength(self): list = self._arr.tolist() return list2int(list[4:8]) def GetRawData(self): return self._arr.tolist() def __str__(self): return '[BlockEntry] Number = 0x%X, length=0x%X' % (self.GetNumberBlocks(), self.GetLength()) class EfiFfs(object): FFS_HEADER_SIZE = 24 def __init__(self, parent=None): self._header = None # following field is for FFS in FV file. self._parent = parent self._offset = 0 self._sections = [] def Load(self, fd): self._offset = align(fd.tell(), 8) self._header = EfiFfsHeader.Read(fd, self) if not isValidGuid(self.GetNameGuid()): return index = self._offset fileend = self._offset + self.GetSize() while (index + EfiSection.EFI_SECTION_HEADER_SIZE < fileend): section = EfiSection(self) section.Load(fd) if section.GetSize() == 0 and section.GetHeader().GetType() == 0: break self._sections.append(section) index = fd.tell() # rebase file pointer to next ffs file index = self._offset + self._header.GetFfsSize() index = align(index, 8) fd.seek(index) def GetOffset(self): return self._offset def GetSize(self): return self._header.GetFfsSize() @classmethod def Read(cls, fd, parent=None): item = cls(parent) item.Load(fd) return item def GetNameGuid(self): return self._header.GetNameGuid() def DumpContent(self): list = self._content.tolist() line = [] count = 0 for item in list: if count < 32: line.append('0x%X' % int(item)) count += 1 else: print(' '.join(line)) count = 0 line = [] line.append('0x%X' % int(item)) count += 1 def GetHeader(self): return self._header def GetParent(self): return self._parent def GetSections(self): return self._sections class EfiFfsHeader(BinaryItem): ffs_state_map = {0x01:'EFI_FILE_HEADER_CONSTRUCTION', 0x02:'EFI_FILE_HEADER_VALID', 0x04:'EFI_FILE_DATA_VALID', 0x08:'EFI_FILE_MARKED_FOR_UPDATE', 0x10:'EFI_FILE_DELETED', 0x20:'EFI_FILE_HEADER_INVALID'} def GetSize(self): return 24 def GetNameGuid(self): list = self._arr.tolist() return list2guid(list[0:16]) def GetType(self): list = self._arr.tolist() return int(list[18]) def GetTypeString(self): value = self.GetType() if value == 0x01: return 'EFI_FV_FILETYPE_RAW' if value == 0x02: return 'EFI_FV_FILETYPE_FREEFORM' if value == 0x03: return 'EFI_FV_FILETYPE_SECURITY_CORE' if value == 0x04: return 'EFI_FV_FILETYPE_PEI_CORE' if value == 0x05: return 'EFI_FV_FILETYPE_DXE_CORE' if value == 0x06: return 'EFI_FV_FILETYPE_PEIM' if value == 0x07: return 'EFI_FV_FILETYPE_DRIVER' if value == 0x08: return 'EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER' if value == 0x09: return 'EFI_FV_FILETYPE_APPLICATION' if value == 0x0B: return 'EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE' if value == 0xc0: return 'EFI_FV_FILETYPE_OEM_MIN' if value == 0xdf: return 'EFI_FV_FILETYPE_OEM_MAX' if value == 0xe0: return 'EFI_FV_FILETYPE_DEBUG_MIN' if value == 0xef: return 'EFI_FV_FILETYPE_DEBUG_MAX' if value == 0xf0: return 'EFI_FV_FILETYPE_FFS_PAD' if value == 0xff: return 'EFI_FV_FILETYPE_FFS_MAX' return 'Unknown FFS Type' def GetAttributes(self): list = self._arr.tolist() return int(list[19]) def GetFfsSize(self): list = self._arr.tolist() return list2int(list[20:23]) def GetState(self): list = self._arr.tolist() state = int(list[23]) polarity = self.GetParent().GetParent().GetHeader().GetErasePolarity() if polarity: state = (~state) & 0xFF HighestBit = 0x80 while (HighestBit != 0) and (HighestBit & state) == 0: HighestBit = HighestBit >> 1 return HighestBit def GetStateString(self): state = self.GetState() if state in self.ffs_state_map.keys(): return self.ffs_state_map[state] return 'Unknown Ffs State' def Dump(self): print("FFS name: ", self.GetNameGuid()) print("FFS type: ", self.GetType()) print("FFS attr: 0x%X" % self.GetAttributes()) print("FFS size: 0x%X" % self.GetFfsSize()) print("FFS state: 0x%X" % self.GetState()) def GetRawData(self): return self._arr.tolist() class EfiSection(object): EFI_SECTION_HEADER_SIZE = 4 def __init__(self, parent=None): self._size = 0 self._parent = parent self._offset = 0 self._contents = array.array('B') def Load(self, fd): self._offset = align(fd.tell(), 4) self._header = EfiSectionHeader.Read(fd, self) if self._header.GetTypeString() == "EFI_SECTION_PE32": pefile = pe.PEFile(self) pefile.Load(fd, self.GetContentSize()) fd.seek(self._offset) self._contents.fromfile(fd, self.GetContentSize()) # rebase file pointer to next section index = self._offset + self.GetSize() index = align(index, 4) fd.seek(index) def GetContentSize(self): return self.GetSize() - self.EFI_SECTION_HEADER_SIZE def GetContent(self): return self._contents.tolist() def GetSize(self): return self._header.GetSectionSize() def GetHeader(self): return self._header def GetSectionOffset(self): return self._offset + self.EFI_SECTION_HEADER_SIZE class EfiSectionHeader(BinaryItem): section_type_map = {0x01: 'EFI_SECTION_COMPRESSION', 0x02: 'EFI_SECTION_GUID_DEFINED', 0x10: 'EFI_SECTION_PE32', 0x11: 'EFI_SECTION_PIC', 0x12: 'EFI_SECTION_TE', 0x13: 'EFI_SECTION_DXE_DEPEX', 0x14: 'EFI_SECTION_VERSION', 0x15: 'EFI_SECTION_USER_INTERFACE', 0x16: 'EFI_SECTION_COMPATIBILITY16', 0x17: 'EFI_SECTION_FIRMWARE_VOLUME_IMAGE', 0x18: 'EFI_SECTION_FREEFORM_SUBTYPE_GUID', 0x19: 'EFI_SECTION_RAW', 0x1B: 'EFI_SECTION_PEI_DEPEX'} def GetSize(self): return 4 def GetSectionSize(self): list = self._arr.tolist() return list2int(list[0:3]) def GetType(self): list = self._arr.tolist() return int(list[3]) def GetTypeString(self): type = self.GetType() if type not in self.section_type_map.keys(): return 'Unknown Section Type' return self.section_type_map[type] def Dump(self): print('size = 0x%X' % self.GetSectionSize()) print('type = 0x%X' % self.GetType()) rMapEntry = re.compile('^(\w+)[ \(\w\)]* \(BaseAddress=([0-9a-fA-F]+), EntryPoint=([0-9a-fA-F]+), GUID=([0-9a-fA-F\-]+)') class EfiFvMapFile(object): def __init__(self): self._mapentries = {} def Load(self, path): if not os.path.exists(path): return False try: file = open(path, 'r') lines = file.readlines() file.close() except: return False for line in lines: if line[0] != ' ': # new entry ret = rMapEntry.match(line) if ret is not None: name = ret.groups()[0] baseaddr = int(ret.groups()[1], 16) entry = int(ret.groups()[2], 16) guidstr = '{' + ret.groups()[3] + '}' guid = uuid.UUID(guidstr) self._mapentries[guid] = EfiFvMapFileEntry(name, baseaddr, entry, guid) return True def GetEntry(self, guid): if guid in self._mapentries.keys(): return self._mapentries[guid] return None class EfiFvMapFileEntry(object): def __init__(self, name, baseaddr, entry, guid): self._name = name self._baseaddr = baseaddr self._entry = entry self._guid = guid def GetName(self): return self._name def GetBaseAddress(self): return self._baseaddr def GetEntryPoint(self): return self._entry def list2guid(list): val1 = list2int(list[0:4]) val2 = list2int(list[4:6]) val3 = list2int(list[6:8]) val4 = 0 for item in list[8:16]: val4 = (val4 << 8) | int(item) val = val1 << 12 * 8 | val2 << 10 * 8 | val3 << 8 * 8 | val4 guid = uuid.UUID(int=val) return guid def list2int(list): val = 0 for index in range(len(list) - 1, -1, -1): val = (val << 8) | int(list[index]) return val def align(value, alignment): return (value + ((alignment - value) & (alignment - 1))) gInvalidGuid = uuid.UUID(int=0xffffffffffffffffffffffffffffffff) def isValidGuid(guid): if guid == gInvalidGuid: return False return True