#!/usr/bin/python # # Copyright (c) 2013 - 2016 Apple Inc. All rights reserved # # Dump out a PE/COFF, PE/COFF+, or TE file using the EfiPeCoff class # # Read from memory in lldb # T=pecoff.EfiPeCoff(lldb.target, 0x86c7d000) # # Read from a Python file object # T=pecoff.EfiPeCoff(file) # # Read from a Python string # T=pecoff.EfiPeCoff(file.read()) # import sys import struct import collections import optparse import commands import os import platform #---------------------------------------------------------------------- # Code that auto imports LLDB #---------------------------------------------------------------------- try: # Just try for LLDB in case PYTHONPATH is already correctly setup import lldb except ImportError: lldb_python_dirs = list() # lldb is not in the PYTHONPATH, try some defaults for the current platform platform_system = platform.system() if platform_system == 'Darwin': # On Darwin, try the currently selected Xcode directory xcode_dir = commands.getoutput("xcode-select --print-path") if xcode_dir: lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') success = False for lldb_python_dir in lldb_python_dirs: if os.path.exists(lldb_python_dir): if not (sys.path.__contains__(lldb_python_dir)): sys.path.append(lldb_python_dir) try: import lldb except ImportError: pass else: success = True break if not success: print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" sys.exit(1) class ReadOnlyFile: '''Abstract reading data from an object: Duck type an lldb.SBTarget, string (output of file.read()), or Python File object. ''' def __init__(self, readAbstraction, address = 0): # Python file object self.file = None # offset for FAT binaries. self.offset = None # lldb SBTarget self.address = None self.startingAddress = None self.SBTarget = None self.SBError = None # Python string (file.read()) self.data = None self.dataIndex = 0 if isinstance(readAbstraction, lldb.SBTarget): # duck type lldb memory reads self.address = address self.startingAddress = address self.SBTarget = readAbstraction self.SBError = lldb.SBError() elif isinstance(readAbstraction, file): # duck type to a Python file self.file = readAbstraction self.offset = address elif isinstance(readAbstraction, str): # string, like the result of reading the file in via Python self.data = readAbstraction self.dataIndex = 0 else: raise SyntaxError('Unsupported type for readAbstraction') def Read (self, size, offset=None): if offset is not None: self.Seek (offset) if self.file: return self.file.read(size) if self.address: data = self.SBTarget.process.ReadMemory (self.address, size, self.SBError) self.address += size return bytearray(data) if self.data: data = self.data[self.dataIndex:self.dataIndex+size] self.dataIndex += size return data def ReadCString (self, offset=None, maxSize=512): if offset: self.Seek (offset) if self.file: data = self.file.read(maxSize) str = data.split('\x00')[0] # seek to end of string self.file.seek (-(maxSize - len(str)), os.SEEK_CUR) return data if self.address: data = self.SBTarget.process.ReadCStringFromMemory (self.address, maxSize, self.SBError) self.address += len(data) return data if self.data: data = self.data[self.dataIndex:self.dataIndex+maxSize] str = data.split('\x00')[0] self.dataIndex += len(str) return str def Seek (self, offset, whence = os.SEEK_SET): if self.file: return self.file.seek(offset, whence) if self.address: if whence == os.SEEK_SET: self.address = self.startingAddress + offset elif whence == os.SEEK_CUR: self.address = self.address + offset elif whence == os.SEEK_END: raise SyntaxError('whence does not support SEEK_END due to memory not having an end') else: raise SyntaxError('illegal whence value') if self.data: if whence == os.SEEK_SET: self.dataIndex = offset elif whence == os.SEEK_CUR: self.dataIndex = self.dataIndex + offset elif whence == os.SEEK_END: raise SyntaxError('whence does not support SEEK_END due to memory not having an end') else: raise SyntaxError('illegal whence value') def Tell (self): if self.file: return self.file.tell() if self.address: return self.address if self.data: return self.dataIndex def __del__(self): if self.file: self.file.close() class EfiPeCoff: ''' class to abstract PE/COFF walking''' # PE/COFF class definitions # # typedef struct { # UINT32 VirtualAddress; # UINT32 Size; # } EFI_IMAGE_DATA_DIRECTORY; # # typedef struct { # UINT16 Signature; ///< The signature for TE format = "VZ". # UINT16 Machine; ///< From the original file header. # UINT8 NumberOfSections; ///< From the original file header. # UINT8 Subsystem; ///< From original optional header. # UINT16 StrippedSize; ///< Number of bytes we removed from the header. # UINT32 AddressOfEntryPoint; ///< Offset to entry point -- from original optional header. # UINT32 BaseOfCode; ///< From original image -- required for ITP debug. # UINT64 ImageBase; ///< From original file header. # EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< Only base relocation and debug directory. # } EFI_TE_IMAGE_HEADER; # EFI_TE_IMAGE_HEADER_fmt = '<HHBBHLLQLLLL' TeHdrLength = struct.calcsize(EFI_TE_IMAGE_HEADER_fmt) EFI_TE_IMAGE_HEADER_tuple = 'Signature Machine NumberOfSections Subsystem StrippedSize AddressOfEntryPoint BaseOfCode ImageBase DataDirVirt_Reloc DataDirSize_Reloc DataDirVirt_Debug DataDirSize_Debug' EFI_TE_IMAGE_HEADER = collections.namedtuple ('EFI_TE_IMAGE_HEADER', EFI_TE_IMAGE_HEADER_tuple) EFI_IMAGE_NT_SIGNATURE = 0x00004550 # # /// # /// PE images can start with an optional DOS header, so if an image is run # /// under DOS it can print an error message. # /// # typedef struct { # UINT16 e_magic; ///< Magic number. # UINT16 e_cblp; ///< Bytes on last page of file. # UINT16 e_cp; ///< Pages in file. # UINT16 e_crlc; ///< Relocations. # UINT16 e_cparhdr; ///< Size of header in paragraphs. # UINT16 e_minalloc; ///< Minimum extra paragraphs needed. # UINT16 e_maxalloc; ///< Maximum extra paragraphs needed. # UINT16 e_ss; ///< Initial (relative) SS value. # UINT16 e_sp; ///< Initial SP value. # UINT16 e_csum; ///< Checksum. # UINT16 e_ip; ///< Initial IP value. # UINT16 e_cs; ///< Initial (relative) CS value. # UINT16 e_lfarlc; ///< File address of relocation table. # UINT16 e_ovno; ///< Overlay number. # UINT16 e_res[4]; ///< Reserved words. # UINT16 e_oemid; ///< OEM identifier (for e_oeminfo). # UINT16 e_oeminfo; ///< OEM information; e_oemid specific. # UINT16 e_res2[10]; ///< Reserved words. # UINT32 e_lfanew; ///< File address of new exe header. # } EFI_IMAGE_DOS_HEADER; # # cheat as 58s is really e_cblp -> e_res2[10] EFI_IMAGE_DOS_HEADER_fmt = '<H58sI' DosHdrLength = struct.calcsize(EFI_IMAGE_DOS_HEADER_fmt) EFI_IMAGE_DOS_HEADER_tuple = 'e_magic e_ignore e_lfanew' EFI_IMAGE_DOS_HEADER = collections.namedtuple ('EFI_IMAGE_DOS_HEADER', EFI_IMAGE_DOS_HEADER_tuple) #define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 # # /// # /// @attention # /// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and # /// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary # /// after NT additional fields. # /// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b # # UINT32 Signature; # typedef struct { # UINT16 Machine; # UINT16 NumberOfSections; # UINT32 TimeDateStamp; # UINT32 PointerToSymbolTable; # UINT32 NumberOfSymbols; # UINT16 SizeOfOptionalHeader; # UINT16 Characteristics; # } EFI_IMAGE_FILE_HEADER; # /// # /// Optional Header Standard Fields for PE32. # /// # typedef struct { # /// # /// Standard fields. # /// # UINT16 Magic; # UINT8 MajorLinkerVersion; # UINT8 MinorLinkerVersion; # UINT32 SizeOfCode; # UINT32 SizeOfInitializedData; # UINT32 SizeOfUninitializedData; # UINT32 AddressOfEntryPoint; # UINT32 BaseOfCode; # UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+. # /// # /// Optional Header Windows-Specific Fields. # /// # UINT32 ImageBase; # UINT32 SectionAlignment; # UINT32 FileAlignment; # UINT16 MajorOperatingSystemVersion; # UINT16 MinorOperatingSystemVersion; # UINT16 MajorImageVersion; # UINT16 MinorImageVersion; # UINT16 MajorSubsystemVersion; # UINT16 MinorSubsystemVersion; # UINT32 Win32VersionValue; # UINT32 SizeOfImage; # UINT32 SizeOfHeaders; # UINT32 Checksum; # UINT16 Subsystem; # UINT16 DllCharacteristics; # UINT32 SizeOfStackReserve; # UINT32 SizeOfStackCommit; # UINT32 SizeOfHeapReserve; # UINT32 SizeOfHeapCommit; # UINT32 LoaderFlags; # UINT32 NumberOfRvaAndSizes; # EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; # } EFI_IMAGE_OPTIONAL_HEADER32; # EFI_IMAGE_DATA_DIRECTORY_tuple = ''' DataDirVirt_Export DataDirSize_Export DataDirVirt_Import DataDirSize_Import DataDirVirt_Resource DataDirSize_Resource DataDirVirt_Exception DataDirSize_Exception DataDirVirt_Security DataDirSize_Security DataDirVirt_Reloc DataDirSize_Reloc DataDirVirt_Debug DataDirSize_Debug DataDir7Virt DataDir7Size DataDir8Virt DataDir8Size DataDir9Virt DataDir9Size DataDir10Virt DataDir10Size DataDir11Virt DataDir11Size DataDir12Virt DataDir12Size DataDir13Virt DataDir13Size DataDir14Virt DataDir14Size DataDir15Virt DataDir15Size ''' EFI_IMAGE_OPTIONAL_HEADER32_fmt = '<IHHIIIHHHBBIIIIIIIIIHHHHHHIIIIHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII' OptionalHeader32Length = struct.calcsize(EFI_IMAGE_OPTIONAL_HEADER32_fmt) EFI_IMAGE_OPTIONAL_HEADER32_tuple = ''' Signature Machine NumberOfSections TimeDateStamp PointerToSymbolTable NumberOfSymbols SizeOfOptionalHeader Characteristics Magic MajorLinkerVersion MinorLinkerVersion SizeOfCode SizeOfInitializedData SizeOfUninitializedData AddressOfEntryPoint BaseOfCode BaseOfData ImageBase SectionAlignment FileAlignment MajorOperatingSystemVersion MinorOperatingSystemVersion MajorImageVersion MinorImageVersion MajorSubsystemVersion MinorSubsystemVersion Win32VersionValue SizeOfImage SizeOfHeaders Checksum Subsystem DllCharacteristics SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve SizeOfHeapCommit LoaderFlags NumberOfRvaAndSizes ''' + EFI_IMAGE_DATA_DIRECTORY_tuple EFI_IMAGE_OPTIONAL_HEADER32 = collections.namedtuple ('EFI_IMAGE_OPTIONAL_HEADER32', EFI_IMAGE_OPTIONAL_HEADER32_tuple) # # /// # /// @attention # /// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and # /// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary # /// after NT additional fields. # /// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b # # /// # /// Optional Header Standard Fields for PE32+. # /// # UINT32 Signature; # typedef struct { # UINT16 Machine; # UINT16 NumberOfSections; # UINT32 TimeDateStamp; # UINT32 PointerToSymbolTable; # UINT32 NumberOfSymbols; # UINT16 SizeOfOptionalHeader; # UINT16 Characteristics; # } EFI_IMAGE_FILE_HEADER; # /// # /// COFF File Header (Object and Image). # /// # typedef struct { # /// # /// Standard fields. # /// # UINT16 Magic; # UINT8 MajorLinkerVersion; # UINT8 MinorLinkerVersion; # UINT32 SizeOfCode; # UINT32 SizeOfInitializedData; # UINT32 SizeOfUninitializedData; # UINT32 AddressOfEntryPoint; # UINT32 BaseOfCode; # /// # /// Optional Header Windows-Specific Fields. # /// # UINT64 ImageBase; # UINT32 SectionAlignment; # UINT32 FileAlignment; # UINT16 MajorOperatingSystemVersion; # UINT16 MinorOperatingSystemVersion; # UINT16 MajorImageVersion; # UINT16 MinorImageVersion; # UINT16 MajorSubsystemVersion; # UINT16 MinorSubsystemVersion; # UINT32 Win32VersionValue; # UINT32 SizeOfImage; # UINT32 SizeOfHeaders; # UINT32 Checksum; # UINT16 Subsystem; # UINT16 DllCharacteristics; # UINT64 SizeOfStackReserve; # UINT64 SizeOfStackCommit; # UINT64 SizeOfHeapReserve; # UINT64 SizeOfHeapCommit; # UINT32 LoaderFlags; # UINT32 NumberOfRvaAndSizes; # EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; # } EFI_IMAGE_OPTIONAL_HEADER64; # EFI_IMAGE_OPTIONAL_HEADER64_fmt = '<IHHIIIHHHBBIIIIIQIIHHHHHHIIIIHHQQQQIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII' OptionalHeader64Length = struct.calcsize(EFI_IMAGE_OPTIONAL_HEADER64_fmt) EFI_IMAGE_OPTIONAL_HEADER64_tuple = ''' Signature Machine NumberOfSections TimeDateStamp PointerToSymbolTable NumberOfSymbols SizeOfOptionalHeader Characteristics Magic MajorLinkerVersion MinorLinkerVersion SizeOfCode SizeOfInitializedData SizeOfUninitializedData AddressOfEntryPoint BaseOfCode ImageBase SectionAlignment FileAlignment MajorOperatingSystemVersion MinorOperatingSystemVersion MajorImageVersion MinorImageVersion MajorSubsystemVersion MinorSubsystemVersion Win32VersionValue SizeOfImage SizeOfHeaders Checksum Subsystem DllCharacteristics SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve SizeOfHeapCommit LoaderFlags NumberOfRvaAndSizes ''' + EFI_IMAGE_DATA_DIRECTORY_tuple EFI_IMAGE_OPTIONAL_HEADER64 = collections.namedtuple ('EFI_IMAGE_OPTIONAL_HEADER64', EFI_IMAGE_OPTIONAL_HEADER64_tuple) # # #define EFI_IMAGE_SIZEOF_SHORT_NAME 8 # # /// # /// Section Table. This table immediately follows the optional header. # /// # typedef struct { # UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME]; # union { # UINT32 PhysicalAddress; # UINT32 VirtualSize; # } Misc; # UINT32 VirtualAddress; # UINT32 SizeOfRawData; # UINT32 PointerToRawData; # UINT32 PointerToRelocations; # UINT32 PointerToLinenumbers; # UINT16 NumberOfRelocations; # UINT16 NumberOfLinenumbers; # UINT32 Characteristics; # } EFI_IMAGE_SECTION_HEADER; # EFI_IMAGE_SECTION_HEADER_fmt = '<QIIIIIIHHI' PeCoffSectionLength = struct.calcsize(EFI_IMAGE_SECTION_HEADER_fmt) EFI_IMAGE_SECTION_HEADER_tuple = 'Name VirtualSize VirtualAddress SizeOfRawData PointerToRawData PointerToRelocations PointerToLinenumbers NumberOfRelocations NumberOfLinenumbers Characteristics' EFI_IMAGE_SECTION_HEADER = collections.namedtuple ('EFI_IMAGE_SECTION_HEADER', EFI_IMAGE_SECTION_HEADER_tuple) # # /// # /// Debug Directory Format. # /// # typedef struct { # UINT32 Characteristics; # UINT32 TimeDateStamp; # UINT16 MajorVersion; # UINT16 MinorVersion; # UINT32 Type; # UINT32 SizeOfData; # UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base. # UINT32 FileOffset; ///< The file pointer to the debug data. # } EFI_IMAGE_DEBUG_DIRECTORY_ENTRY; # EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt = '<IIHHIIII' EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_tuple = 'Characteristics TimeDateStamp MajorVersion MinorVersion Type SizeOfData RVA FileOffset' EFI_IMAGE_DEBUG_DIRECTORY_ENTRY = collections.namedtuple ('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY', EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_tuple) ##define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C') #typedef struct { # UINT32 Signature; ///< "MTOC". # GUID MachOUuid; # // # // Filename of .DLL (Mach-O with debug info) goes here # // #} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY; #typedef struct { # UINT32 Data1; # UINT16 Data2; # UINT16 Data3; # UINT8 Data4[8]; #} GUID; EFI_GUID_fmt = '<IHHBBBBBBBB' # typedef struct { # UINT32 VirtualAddress; # UINT32 SizeOfBlock; # } EFI_IMAGE_BASE_RELOCATION; EFI_IMAGE_BASE_RELOCATION_fmt = '<II' BaseRelocationLength = struct.calcsize(EFI_IMAGE_BASE_RELOCATION_fmt) # /// # /// The WIN_CERTIFICATE structure is part of the PE/COFF specification. # /// # typedef struct { # /// # /// The length of the entire certificate, # /// including the length of the header, in bytes. # /// # UINT32 dwLength; # /// # /// The revision level of the WIN_CERTIFICATE # /// structure. The current revision level is 0x0200. # /// # UINT16 wRevision; # /// # /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI # /// certificate types. The UEFI specification reserves the range of # /// certificate type values from 0x0EF0 to 0x0EFF. # /// # UINT16 wCertificateType; # /// # /// The following is the actual certificate. The format of # /// the certificate depends on wCertificateType. # /// # /// UINT8 bCertificate[ANYSIZE_ARRAY]; # /// # } WIN_CERTIFICATE; WIN_CERTIFICATE_fmt = "<IHH" WinCertLength = struct.calcsize(WIN_CERTIFICATE_fmt) def __init__(self, readAbstraction, address = 0): self.f = ReadOnlyFile(readAbstraction, address) # ( ImageType: 'TE'/'PE32'/'PE32+' # OptionalHeaderCollection: Optional Header and Data Directory # HeaderFormat: in struct.Struct() form # TeOffset # HeaderSize) self.PeHdr = None self.TeHdr = None self.TeAdjust = 0 self.PeCoffType = '' self.MachineType = '' self.PeHdrFmt = None self.PeSections = 0 self.FvSection = False self.ZeroList = [] self.PeCoffHdrRead () def TeHdrTuple (self, offset=0): data = self.f.Read (EfiPeCoff.TeHdrLength,offset) TeHdr = EfiPeCoff.EFI_TE_IMAGE_HEADER._make (struct.Struct(EfiPeCoff.EFI_TE_IMAGE_HEADER_fmt).unpack_from (data)) return (TeHdr, EfiPeCoff.TeHdrLength - TeHdr.StrippedSize) def DosHdrTuple (self, offset=0): data = self.f.Read(EfiPeCoff.DosHdrLength,offset) return EfiPeCoff.EFI_IMAGE_DOS_HEADER._make (struct.unpack_from(EfiPeCoff.EFI_IMAGE_DOS_HEADER_fmt, data)) ImageFileMachine = { 0x014c : "IA32", 0x0200 : "IPF", 0x0EBC : "EBC", 0x8664 : "X64", 0x01c2 : "ARM", 0xAA64 : "AArch64", } def PeCoffHdrRead (self): # Test for FV Section (*.te build output) image = self.f.Read(4, 0) if image[0:2] == 'MZ' or image[0:2] == 'VZ' or image[0:4] == 'PE\0\0': offset = 0 else: offset = 4 self.FvSection = True image = self.f.Read(2, offset) if image[0:2] == 'MZ': # PE/COFF starts with DOS Header DosHdr = self.DosHdrTuple (offset) offset += DosHdr.e_lfanew elif image[0:2] == 'VZ': # PE/COFF starts with TE Header self.TeHdr, self.TeAdjust = self.TeHdrTuple (offset) self.PeCoffType = "TE" self.MachineType = self.ImageFileMachine.get (self.TeHdr.Machine, 'Unknown') self.PeSections = EfiPeCoff.TeHdrLength data = self.f.Read(EfiPeCoff.TeHdrLength, offset) self.PeHdrFmt = EfiPeCoff.EFI_TE_IMAGE_HEADER_fmt return else: # PE/COFF starts with PE/COFF header offset = 0 image = self.f.Read(4, offset) if image[0:4] != 'PE\0\0': print "Bad PE/COFF Signature = %4s" % image return ("Unknown", None, "", 0, 0) # Check the magic to figure out if 32 or 64 bit PE/COFF (Magic,) = struct.unpack_from ('<H', self.f.Read(2, offset+24)) if Magic == EfiPeCoff.EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: data = self.f.Read(EfiPeCoff.OptionalHeader32Length, offset) self.PeHdr = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32._make (struct.Struct(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt).unpack_from (data)) self.PeCoffType = "PE32" self.PeHdrFmt = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt # TimeDateStamp: Offset, size self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt[0:4]), 4]) # Checksum: Offset, size self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER32_fmt[0:29]), 4]) elif Magic == EfiPeCoff.EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC: data = self.f.Read(EfiPeCoff.OptionalHeader64Length, offset) self.PeHdr = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64._make (struct.Struct(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt).unpack_from (data)) self.PeCoffType = "PE32+" self.PeHdrFmt = EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt # TimeDateStamp: Offset, size self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt[0:4]), 4]) # Checksum: Offset, size self.ZeroList.append([offset + struct.calcsize(EfiPeCoff.EFI_IMAGE_OPTIONAL_HEADER64_fmt[0:29]), 4]) else: print "Unknown Magic 0x%02x" % Magic return ("Unknown", None, "", 0, 0) self.MachineType = self.ImageFileMachine.get (self.PeHdr.Machine, 'Unknown') # ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader; self.PeSections = offset + 4 + 20 + self.PeHdr.SizeOfOptionalHeader def PeCoffDumpHdr (self): PeHdr = self.PeHdr if self.TeHdr is None else self.TeHdr Width = max (len (s) for s in PeHdr._fields) return "\n".join('{0} = {1:#0{2}x}'.format(s.ljust(Width), getattr(PeHdr, s), FmtStrToWidth(self.PeHdrFmt[i+1])+2) for i, s in enumerate (PeHdr._fields)) def PeCoffZeroInfo (self): # Return the files offsets and number of bytes that need to get zero'ed return self.ZeroList def NumberOfSections (self): if self.PeHdr is not None: return self.PeHdr.NumberOfSections elif self.TeHdr is not None: return self.TeHdr.NumberOfSections else: return 0 def PeCoffGetSection (self, index): offset = self.PeSections + (index * EfiPeCoff.PeCoffSectionLength) data = self.f.Read(EfiPeCoff.PeCoffSectionLength, offset) return (data[0:8].split('\x00')[0], EfiPeCoff.EFI_IMAGE_SECTION_HEADER._make (struct.Struct(EfiPeCoff.EFI_IMAGE_SECTION_HEADER_fmt).unpack_from (data))) def PeCoffDumpSectionHdr (self, Name, Section): Width = max (len (s) for s in Section._fields) result = '' for i, s in enumerate (Section._fields): result += '{0} = '.format(s.ljust(Width)) if i == 0 and Name != '': # print name as a string, not a hex value result += Name + '\n' else: result += '{0:#0{1}x}\n'.format(getattr(Section, s), FmtStrToWidth(EfiPeCoff.EFI_IMAGE_SECTION_HEADER_fmt[i+1])+2) return result def PeCoffDumpSection (self, Name, Section): data = self.f.Read (Section.SizeOfRawData, Section.VirtualAddress) result = [] Address = Section.VirtualAddress for i in xrange (0, Section.SizeOfRawData, 16): HexStr = ' '.join(["%02X"%ord(x) for x in data[i:i+16]]) TextStr = ''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in data[i:i+16]]) result.append("%08X %-*s |%s|\n" % (Address + i, 16*3, HexStr, TextStr)) return ''.join(result) def PeCoffGetPdePointer (self, DebugEntry = 0, DebugEntrySize = 0, adjust = 0): # if DebugEntrySize == 0: if self.PeHdr is not None: DebugEntry = self.PeHdr.DataDirVirt_Debug DebugEntrySize = self.PeHdr.DataDirSize_Debug elif self.TeHdr is not None: DebugEntry = self.TeHdr.DataDirVirt_Debug DebugEntrySize = self.TeHdr.DataDirSize_Debug adjust = self.TeAdjust else: return ('','') offset = DebugEntry + adjust data = self.f.Read(DebugEntrySize, offset) DirectoryEntry = EfiPeCoff.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY._make (struct.Struct(EfiPeCoff.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt).unpack_from (data)) offset = DirectoryEntry.FileOffset + adjust data = self.f.Read(4, offset) guid = '' if data == 'MTOC': data = self.f.Read(16) tup = struct.unpack (EfiPeCoff.EFI_GUID_fmt, data) guid = '{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}'.format(*tup) Str = self.f.ReadCString () elif data == 'NB10': Str = self.f.ReadCString (offset + 16) elif data == 'RSDS': Str = self.f.ReadCString (offset + 24) else: Str = "\x00" # Python is more that happy to print out a NULL return (Str.split('\x00')[0], guid) def PeCoffDumpRelocations (self, offset, size): data = self.f.Read(size, offset) base = 0 baseEnd = size - EfiPeCoff.BaseRelocationLength value = '' while base < baseEnd: (VirtualAddress, SizeOfBlock) = struct.unpack_from (EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt, data[base:base + EfiPeCoff.BaseRelocationLength]) if SizeOfBlock == 0 or SizeOfBlock > size: break reloc = base + EfiPeCoff.BaseRelocationLength relocEnd = base + SizeOfBlock value += '0x%08x SizeOfBlock 0x%x\n' % (VirtualAddress, SizeOfBlock) while reloc < relocEnd: rel, = struct.unpack_from ('<H', data[reloc:reloc+2]) value += ' 0x%04x 0x%x\n' % ((rel & 0xFFF), rel >> 12, ) reloc += 2 base = relocEnd return value def PeCoffDumpCert (self, offset, size): data = self.f.Read(size, offset) value = '\n' if size > 8: (dwLength, wRevision, wCertificateType) = struct.unpack_from (EfiPeCoff.WIN_CERTIFICATE_fmt, data) value += "dwLength = 0x%04x wRevision = 0x%02x wCertificateType = 0x%02x\n" % (dwLength, wRevision, wCertificateType) # UEFI Scheme for i in range(struct.calcsize(EfiPeCoff.WIN_CERTIFICATE_fmt), size, 0x10): value += "0x{:04x}:".format(i), value += " ".join("{:02x}".format(ord(c)) for c in data[i:i+0x10]) value += '\n' else: # Loki Scheme start = 0 while start < size: (VirtualAddress, SizeOfBlock) = struct.unpack_from (EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt, data[start: start + struct.calcsize(EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt)]) start += struct.calcsize(EfiPeCoff.EFI_IMAGE_BASE_RELOCATION_fmt) value += "CERT: 0x%X size 0x%x\n" % (VirtualAddress, SizeOfBlock) cert = self.f.Read(SizeOfBlock, VirtualAddress) for i in range(0, SizeOfBlock, 0x10): value += "0x{:04x}:".format(i) value += " ".join("{:02x}".format(ord(c)) for c in cert[i:i+0x10]) value += '\n' return value def __str__(self): return self.PeCoffDumpHdr() def FmtStrToWidth (c): c = c.upper() if c== 'B': return 1*2 if c== 'H': return 2*2 if c== 'I' or c=='L': return 4*2 if c== 'Q': return 8*2 return 0 #define EFI_FAT_BINARY_MAGIC 0x0ef1fab9 # typedef struct _EFI_FAT_BINARY_HEADER { # UINT32 magic; /* FAT_MAGIC */ # UINT32 nfat_arch; /* number of structs that follow */ # } EFI_FAT_BINARY_HEADER; # typedef struct _EFI_FAT_BINARY_ARCH { # UINT32 cputype; /* cpu specifier (int) */ # UINT32 cpusubtype; /* machine specifier (int) */ # UINT32 offset; /* file offset to this object file */ # UINT32 size; /* size of this object file */ # UINT32 align; /* alignment as a power of 2 */ # } EFI_FAT_BINARY_ARCH; EFI_FAT_BINARY_ARCH_fmt = '<IIIII' fatCpuType = { 0x01000007: 'x86_64 (X64)', 0x00000007: 'i386 (Ia32)', 0x0000000C: 'ARM (Arm)', 0x0100000C: 'ARM64 (AArch64)', } def CheckForFatBinary (f): '''Return a list of PE/COFF binary objects, from a file object. ''' fatEntry = 4 + 4 data = f.read(fatEntry) (magic, nfat_arch) = struct.unpack_from ('<II', data) if magic == 0x0ef1fab9: res = [] for i in range (nfat_arch): f.seek(fatEntry) fatEntry += struct.calcsize(EFI_FAT_BINARY_ARCH_fmt) data = f.read(struct.calcsize(EFI_FAT_BINARY_ARCH_fmt)) (cputype, cpusubtype, offset, size, align) = struct.unpack_from (EFI_FAT_BINARY_ARCH_fmt, data) f.seek(offset) res.append((EfiPeCoff(f.read (size)), "FAT Binary of type %s: offset 0x%x size 0x%x alignment 0x%x" % (fatCpuType.get(cputype, 'Unknown'), offset, size, align))) else: # entire file is a PE/COFF image f.seek(0) res = [(EfiPeCoff(f.read()), "")] return res if __name__ == "__main__": usage = "usage: %prog [options] PECOFF_FILE" parser = optparse.OptionParser(usage=usage) parser.add_option('-r', '--relocations', action='store_true', dest='relocation', help='display relocation info', default=False) parser.add_option('-c', '--cert', action='store_true', dest='cert', help='display security cert info', default=False) parser.add_option('-s', '--section', type=str, dest='section', help='dump info on a section', default='') parser.add_option('-z', '--zero', action='store_true', dest='zero', help='Zero out fields to enable build reproducibility', default=False) (options, args) = parser.parse_args(sys.argv) if len(args) <= 1: parser.print_help() sys.exit(-1) with open(args[1], "rb" if not options.zero else "r+b") as f: for (pecoff, description) in CheckForFatBinary (f): if not options.zero: if description != '': print description print "%s is a %s:%s image%s" % (args[1], pecoff.PeCoffType, pecoff.MachineType, ' wrapped in FV Section:' if pecoff.FvSection else ':') if pecoff.PeCoffType == '': sys.exit(-1) # print header print pecoff.PeCoffDumpHdr() print print "\nSections:" for i in range (0, pecoff.NumberOfSections()): (Name, Section) = pecoff.PeCoffGetSection (i) print pecoff.PeCoffDumpSectionHdr (Name, Section) print if '.reloc' in Name and options.relocation: print pecoff.PeCoffDumpRelocations (Section.PointerToRawData, Section.VirtualSize) elif '.debug' in Name: (PdbPointer, Guid) = pecoff.PeCoffGetPdePointer () if Guid == '': print "PdbPointer:{0}\n".format(PdbPointer) else: print "PdbPointer (Mach-O symbol file):{0}\n (Mach-O UUID): {1}".format(PdbPointer, Guid) if options.section in Name and options.section != '': print pecoff.PeCoffDumpSection(Name, Section) if options.cert: print pecoff.PeCoffDumpCert (pecoff.PeHdr.DataDirVirt_Security, pecoff.PeHdr.DataDirSize_Security) else: # this is necessary to find the 'zero list' entries for the debug section for i in range (0, pecoff.NumberOfSections()): (Name, Section) = pecoff.PeCoffGetSection (i) if Section.SizeOfRawData > Section.VirtualSize: sizeDiff = Section.SizeOfRawData - Section.VirtualSize offset = pecoff.TeAdjust + Section.PointerToRawData + Section.SizeOfRawData - sizeDiff pecoff.ZeroList.append( [offset, sizeDiff] ) # The .debug section also contains a timestamp if '.debug' in Name: pecoff.ZeroList.append([pecoff.TeAdjust + Section.PointerToRawData + struct.calcsize(EfiPeCoff.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt[0:2]), 4]) for patch, size in pecoff.PeCoffZeroInfo(): #print 'patching 0x%x for 0x%x bytes' % (patch, size) if patch != 0: if size == -1: # -1 means to the end of the file f.seek(0,2) size = f.tell() - patch f.seek(patch) f.write(bytearray(size))