+#!/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))
+