3 # Copyright (c) 2013 - 2016 Apple Inc. All rights reserved
5 # Dump out a PE/COFF, PE/COFF+, or TE file using the EfiPeCoff class
7 # Read from memory in lldb
8 # T=pecoff.EfiPeCoff(lldb.target, 0x86c7d000)
10 # Read from a Python file object
11 # T=pecoff.EfiPeCoff(file)
13 # Read from a Python string
14 # T=pecoff.EfiPeCoff(file.read())
25 #----------------------------------------------------------------------
26 # Code that auto imports LLDB
27 #----------------------------------------------------------------------
29 # Just try for LLDB in case PYTHONPATH is already correctly setup
32 lldb_python_dirs
= list()
33 # lldb is not in the PYTHONPATH, try some defaults for the current platform
34 platform_system
= platform
.system()
35 if platform_system
== 'Darwin':
36 # On Darwin, try the currently selected Xcode directory
37 xcode_dir
= commands
.getoutput("xcode-select --print-path")
39 lldb_python_dirs
.append(os
.path
.realpath(xcode_dir
+ '/../SharedFrameworks/LLDB.framework/Resources/Python'))
40 lldb_python_dirs
.append(xcode_dir
+ '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
41 lldb_python_dirs
.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
43 for lldb_python_dir
in lldb_python_dirs
:
44 if os
.path
.exists(lldb_python_dir
):
45 if not (sys
.path
.__contains
__(lldb_python_dir
)):
46 sys
.path
.append(lldb_python_dir
)
55 print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
59 '''Abstract reading data from an object:
60 Duck type an lldb.SBTarget, string (output of file.read()), or Python File object.
62 def __init__(self
, readAbstraction
, address
= 0):
65 # offset for FAT binaries.
70 self
.startingAddress
= None
74 # Python string (file.read())
78 if isinstance(readAbstraction
, lldb
.SBTarget
):
79 # duck type lldb memory reads
80 self
.address
= address
81 self
.startingAddress
= address
82 self
.SBTarget
= readAbstraction
83 self
.SBError
= lldb
.SBError()
84 elif isinstance(readAbstraction
, file):
85 # duck type to a Python file
86 self
.file = readAbstraction
88 elif isinstance(readAbstraction
, str):
89 # string, like the result of reading the file in via Python
90 self
.data
= readAbstraction
93 raise SyntaxError('Unsupported type for readAbstraction')
95 def Read (self
, size
, offset
=None):
96 if offset
is not None:
100 return self
.file.read(size
)
103 data
= self
.SBTarget
.process
.ReadMemory (self
.address
, size
, self
.SBError
)
105 return bytearray(data
)
108 data
= self
.data
[self
.dataIndex
:self
.dataIndex
+size
]
109 self
.dataIndex
+= size
112 def ReadCString (self
, offset
=None, maxSize
=512):
117 data
= self
.file.read(maxSize
)
118 str = data
.split('\x00')[0]
119 # seek to end of string
120 self
.file.seek (-(maxSize
- len(str)), os
.SEEK_CUR
)
124 data
= self
.SBTarget
.process
.ReadCStringFromMemory (self
.address
, maxSize
, self
.SBError
)
125 self
.address
+= len(data
)
129 data
= self
.data
[self
.dataIndex
:self
.dataIndex
+maxSize
]
130 str = data
.split('\x00')[0]
131 self
.dataIndex
+= len(str)
135 def Seek (self
, offset
, whence
= os
.SEEK_SET
):
137 return self
.file.seek(offset
, whence
)
140 if whence
== os
.SEEK_SET
:
141 self
.address
= self
.startingAddress
+ offset
142 elif whence
== os
.SEEK_CUR
:
143 self
.address
= self
.address
+ offset
144 elif whence
== os
.SEEK_END
:
145 raise SyntaxError('whence does not support SEEK_END due to memory not having an end')
147 raise SyntaxError('illegal whence value')
150 if whence
== os
.SEEK_SET
:
151 self
.dataIndex
= offset
152 elif whence
== os
.SEEK_CUR
:
153 self
.dataIndex
= self
.dataIndex
+ offset
154 elif whence
== os
.SEEK_END
:
155 raise SyntaxError('whence does not support SEEK_END due to memory not having an end')
157 raise SyntaxError('illegal whence value')
161 return self
.file.tell()
167 return self
.dataIndex
175 ''' class to abstract PE/COFF walking'''
177 # PE/COFF class definitions
181 # UINT32 VirtualAddress;
183 # } EFI_IMAGE_DATA_DIRECTORY;
186 # UINT16 Signature; ///< The signature for TE format = "VZ".
187 # UINT16 Machine; ///< From the original file header.
188 # UINT8 NumberOfSections; ///< From the original file header.
189 # UINT8 Subsystem; ///< From original optional header.
190 # UINT16 StrippedSize; ///< Number of bytes we removed from the header.
191 # UINT32 AddressOfEntryPoint; ///< Offset to entry point -- from original optional header.
192 # UINT32 BaseOfCode; ///< From original image -- required for ITP debug.
193 # UINT64 ImageBase; ///< From original file header.
194 # EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< Only base relocation and debug directory.
195 # } EFI_TE_IMAGE_HEADER;
198 EFI_TE_IMAGE_HEADER_fmt
= '<HHBBHLLQLLLL'
199 TeHdrLength
= struct
.calcsize(EFI_TE_IMAGE_HEADER_fmt
)
200 EFI_TE_IMAGE_HEADER_tuple
= 'Signature Machine NumberOfSections Subsystem StrippedSize AddressOfEntryPoint BaseOfCode ImageBase DataDirVirt_Reloc DataDirSize_Reloc DataDirVirt_Debug DataDirSize_Debug'
201 EFI_TE_IMAGE_HEADER
= collections
.namedtuple ('EFI_TE_IMAGE_HEADER', EFI_TE_IMAGE_HEADER_tuple
)
203 EFI_IMAGE_NT_SIGNATURE
= 0x00004550
206 # /// PE images can start with an optional DOS header, so if an image is run
207 # /// under DOS it can print an error message.
210 # UINT16 e_magic; ///< Magic number.
211 # UINT16 e_cblp; ///< Bytes on last page of file.
212 # UINT16 e_cp; ///< Pages in file.
213 # UINT16 e_crlc; ///< Relocations.
214 # UINT16 e_cparhdr; ///< Size of header in paragraphs.
215 # UINT16 e_minalloc; ///< Minimum extra paragraphs needed.
216 # UINT16 e_maxalloc; ///< Maximum extra paragraphs needed.
217 # UINT16 e_ss; ///< Initial (relative) SS value.
218 # UINT16 e_sp; ///< Initial SP value.
219 # UINT16 e_csum; ///< Checksum.
220 # UINT16 e_ip; ///< Initial IP value.
221 # UINT16 e_cs; ///< Initial (relative) CS value.
222 # UINT16 e_lfarlc; ///< File address of relocation table.
223 # UINT16 e_ovno; ///< Overlay number.
224 # UINT16 e_res[4]; ///< Reserved words.
225 # UINT16 e_oemid; ///< OEM identifier (for e_oeminfo).
226 # UINT16 e_oeminfo; ///< OEM information; e_oemid specific.
227 # UINT16 e_res2[10]; ///< Reserved words.
228 # UINT32 e_lfanew; ///< File address of new exe header.
229 # } EFI_IMAGE_DOS_HEADER;
232 # cheat as 58s is really e_cblp -> e_res2[10]
233 EFI_IMAGE_DOS_HEADER_fmt
= '<H58sI'
234 DosHdrLength
= struct
.calcsize(EFI_IMAGE_DOS_HEADER_fmt
)
235 EFI_IMAGE_DOS_HEADER_tuple
= 'e_magic e_ignore e_lfanew'
236 EFI_IMAGE_DOS_HEADER
= collections
.namedtuple ('EFI_IMAGE_DOS_HEADER', EFI_IMAGE_DOS_HEADER_tuple
)
238 #define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16
242 # /// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and
243 # /// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary
244 # /// after NT additional fields.
246 EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
= 0x10b
251 # UINT16 NumberOfSections;
252 # UINT32 TimeDateStamp;
253 # UINT32 PointerToSymbolTable;
254 # UINT32 NumberOfSymbols;
255 # UINT16 SizeOfOptionalHeader;
256 # UINT16 Characteristics;
257 # } EFI_IMAGE_FILE_HEADER;
259 # /// Optional Header Standard Fields for PE32.
263 # /// Standard fields.
266 # UINT8 MajorLinkerVersion;
267 # UINT8 MinorLinkerVersion;
269 # UINT32 SizeOfInitializedData;
270 # UINT32 SizeOfUninitializedData;
271 # UINT32 AddressOfEntryPoint;
273 # UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+.
275 # /// Optional Header Windows-Specific Fields.
278 # UINT32 SectionAlignment;
279 # UINT32 FileAlignment;
280 # UINT16 MajorOperatingSystemVersion;
281 # UINT16 MinorOperatingSystemVersion;
282 # UINT16 MajorImageVersion;
283 # UINT16 MinorImageVersion;
284 # UINT16 MajorSubsystemVersion;
285 # UINT16 MinorSubsystemVersion;
286 # UINT32 Win32VersionValue;
287 # UINT32 SizeOfImage;
288 # UINT32 SizeOfHeaders;
291 # UINT16 DllCharacteristics;
292 # UINT32 SizeOfStackReserve;
293 # UINT32 SizeOfStackCommit;
294 # UINT32 SizeOfHeapReserve;
295 # UINT32 SizeOfHeapCommit;
296 # UINT32 LoaderFlags;
297 # UINT32 NumberOfRvaAndSizes;
298 # EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
299 # } EFI_IMAGE_OPTIONAL_HEADER32;
302 EFI_IMAGE_DATA_DIRECTORY_tuple
= '''
303 DataDirVirt_Export DataDirSize_Export
304 DataDirVirt_Import DataDirSize_Import
305 DataDirVirt_Resource DataDirSize_Resource
306 DataDirVirt_Exception DataDirSize_Exception
307 DataDirVirt_Security DataDirSize_Security
308 DataDirVirt_Reloc DataDirSize_Reloc
309 DataDirVirt_Debug DataDirSize_Debug
310 DataDir7Virt DataDir7Size
311 DataDir8Virt DataDir8Size
312 DataDir9Virt DataDir9Size
313 DataDir10Virt DataDir10Size
314 DataDir11Virt DataDir11Size
315 DataDir12Virt DataDir12Size
316 DataDir13Virt DataDir13Size
317 DataDir14Virt DataDir14Size
318 DataDir15Virt DataDir15Size
322 EFI_IMAGE_OPTIONAL_HEADER32_fmt
= '<IHHIIIHHHBBIIIIIIIIIHHHHHHIIIIHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'
323 OptionalHeader32Length
= struct
.calcsize(EFI_IMAGE_OPTIONAL_HEADER32_fmt
)
324 EFI_IMAGE_OPTIONAL_HEADER32_tuple
= '''
337 SizeOfInitializedData
338 SizeOfUninitializedData
345 MajorOperatingSystemVersion
346 MinorOperatingSystemVersion
349 MajorSubsystemVersion
350 MinorSubsystemVersion
363 ''' + EFI_IMAGE_DATA_DIRECTORY_tuple
364 EFI_IMAGE_OPTIONAL_HEADER32
= collections
.namedtuple ('EFI_IMAGE_OPTIONAL_HEADER32', EFI_IMAGE_OPTIONAL_HEADER32_tuple
)
370 # /// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and
371 # /// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary
372 # /// after NT additional fields.
374 EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
= 0x20b
377 # /// Optional Header Standard Fields for PE32+.
384 # UINT16 NumberOfSections;
385 # UINT32 TimeDateStamp;
386 # UINT32 PointerToSymbolTable;
387 # UINT32 NumberOfSymbols;
388 # UINT16 SizeOfOptionalHeader;
389 # UINT16 Characteristics;
390 # } EFI_IMAGE_FILE_HEADER;
392 # /// COFF File Header (Object and Image).
396 # /// Standard fields.
399 # UINT8 MajorLinkerVersion;
400 # UINT8 MinorLinkerVersion;
402 # UINT32 SizeOfInitializedData;
403 # UINT32 SizeOfUninitializedData;
404 # UINT32 AddressOfEntryPoint;
407 # /// Optional Header Windows-Specific Fields.
410 # UINT32 SectionAlignment;
411 # UINT32 FileAlignment;
412 # UINT16 MajorOperatingSystemVersion;
413 # UINT16 MinorOperatingSystemVersion;
414 # UINT16 MajorImageVersion;
415 # UINT16 MinorImageVersion;
416 # UINT16 MajorSubsystemVersion;
417 # UINT16 MinorSubsystemVersion;
418 # UINT32 Win32VersionValue;
419 # UINT32 SizeOfImage;
420 # UINT32 SizeOfHeaders;
423 # UINT16 DllCharacteristics;
424 # UINT64 SizeOfStackReserve;
425 # UINT64 SizeOfStackCommit;
426 # UINT64 SizeOfHeapReserve;
427 # UINT64 SizeOfHeapCommit;
428 # UINT32 LoaderFlags;
429 # UINT32 NumberOfRvaAndSizes;
430 # EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
431 # } EFI_IMAGE_OPTIONAL_HEADER64;
434 EFI_IMAGE_OPTIONAL_HEADER64_fmt
= '<IHHIIIHHHBBIIIIIQIIHHHHHHIIIIHHQQQQIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'
435 OptionalHeader64Length
= struct
.calcsize(EFI_IMAGE_OPTIONAL_HEADER64_fmt
)
436 EFI_IMAGE_OPTIONAL_HEADER64_tuple
= '''
449 SizeOfInitializedData
450 SizeOfUninitializedData
456 MajorOperatingSystemVersion
457 MinorOperatingSystemVersion
460 MajorSubsystemVersion
461 MinorSubsystemVersion
474 ''' + EFI_IMAGE_DATA_DIRECTORY_tuple
475 EFI_IMAGE_OPTIONAL_HEADER64
= collections
.namedtuple ('EFI_IMAGE_OPTIONAL_HEADER64', EFI_IMAGE_OPTIONAL_HEADER64_tuple
)
478 # #define EFI_IMAGE_SIZEOF_SHORT_NAME 8
481 # /// Section Table. This table immediately follows the optional header.
484 # UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME];
486 # UINT32 PhysicalAddress;
487 # UINT32 VirtualSize;
489 # UINT32 VirtualAddress;
490 # UINT32 SizeOfRawData;
491 # UINT32 PointerToRawData;
492 # UINT32 PointerToRelocations;
493 # UINT32 PointerToLinenumbers;
494 # UINT16 NumberOfRelocations;
495 # UINT16 NumberOfLinenumbers;
496 # UINT32 Characteristics;
497 # } EFI_IMAGE_SECTION_HEADER;
500 EFI_IMAGE_SECTION_HEADER_fmt
= '<QIIIIIIHHI'
501 PeCoffSectionLength
= struct
.calcsize(EFI_IMAGE_SECTION_HEADER_fmt
)
502 EFI_IMAGE_SECTION_HEADER_tuple
= 'Name VirtualSize VirtualAddress SizeOfRawData PointerToRawData PointerToRelocations PointerToLinenumbers NumberOfRelocations NumberOfLinenumbers Characteristics'
503 EFI_IMAGE_SECTION_HEADER
= collections
.namedtuple ('EFI_IMAGE_SECTION_HEADER', EFI_IMAGE_SECTION_HEADER_tuple
)
507 # /// Debug Directory Format.
510 # UINT32 Characteristics;
511 # UINT32 TimeDateStamp;
512 # UINT16 MajorVersion;
513 # UINT16 MinorVersion;
516 # UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base.
517 # UINT32 FileOffset; ///< The file pointer to the debug data.
518 # } EFI_IMAGE_DEBUG_DIRECTORY_ENTRY;
521 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt
= '<IIHHIIII'
522 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_tuple
= 'Characteristics TimeDateStamp MajorVersion MinorVersion Type SizeOfData RVA FileOffset'
523 EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
= collections
.namedtuple ('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY', EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_tuple
)
525 ##define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C')
527 # UINT32 Signature; ///< "MTOC".
530 # // Filename of .DLL (Mach-O with debug info) goes here
532 #} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY;
541 EFI_GUID_fmt
= '<IHHBBBBBBBB'
544 # UINT32 VirtualAddress;
545 # UINT32 SizeOfBlock;
546 # } EFI_IMAGE_BASE_RELOCATION;
548 EFI_IMAGE_BASE_RELOCATION_fmt
= '<II'
549 BaseRelocationLength
= struct
.calcsize(EFI_IMAGE_BASE_RELOCATION_fmt
)
552 # /// The WIN_CERTIFICATE structure is part of the PE/COFF specification.
556 # /// The length of the entire certificate,
557 # /// including the length of the header, in bytes.
561 # /// The revision level of the WIN_CERTIFICATE
562 # /// structure. The current revision level is 0x0200.
566 # /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI
567 # /// certificate types. The UEFI specification reserves the range of
568 # /// certificate type values from 0x0EF0 to 0x0EFF.
570 # UINT16 wCertificateType;
572 # /// The following is the actual certificate. The format of
573 # /// the certificate depends on wCertificateType.
575 # /// UINT8 bCertificate[ANYSIZE_ARRAY];
579 WIN_CERTIFICATE_fmt
= "<IHH"
580 WinCertLength
= struct
.calcsize(WIN_CERTIFICATE_fmt
)
582 def __init__(self
, readAbstraction
, address
= 0):
583 self
.f
= ReadOnlyFile(readAbstraction
, address
)
584 # ( ImageType: 'TE'/'PE32'/'PE32+'
585 # OptionalHeaderCollection: Optional Header and Data Directory
586 # HeaderFormat: in struct.Struct() form
594 self
.MachineType
= ''
597 self
.FvSection
= False
599 self
.PeCoffHdrRead ()
601 def TeHdrTuple (self
, offset
=0):
602 data
= self
.f
.Read (EfiPeCoff
.TeHdrLength
,offset
)
603 TeHdr
= EfiPeCoff
.EFI_TE_IMAGE_HEADER
._make
(struct
.Struct(EfiPeCoff
.EFI_TE_IMAGE_HEADER_fmt
).unpack_from (data
))
604 return (TeHdr
, EfiPeCoff
.TeHdrLength
- TeHdr
.StrippedSize
)
606 def DosHdrTuple (self
, offset
=0):
607 data
= self
.f
.Read(EfiPeCoff
.DosHdrLength
,offset
)
608 return EfiPeCoff
.EFI_IMAGE_DOS_HEADER
._make
(struct
.unpack_from(EfiPeCoff
.EFI_IMAGE_DOS_HEADER_fmt
, data
))
619 def PeCoffHdrRead (self
):
620 # Test for FV Section (*.te build output)
621 image
= self
.f
.Read(4, 0)
622 if image
[0:2] == 'MZ' or image
[0:2] == 'VZ' or image
[0:4] == 'PE\0\0':
626 self
.FvSection
= True
628 image
= self
.f
.Read(2, offset
)
629 if image
[0:2] == 'MZ':
630 # PE/COFF starts with DOS Header
631 DosHdr
= self
.DosHdrTuple (offset
)
632 offset
+= DosHdr
.e_lfanew
633 elif image
[0:2] == 'VZ':
634 # PE/COFF starts with TE Header
635 self
.TeHdr
, self
.TeAdjust
= self
.TeHdrTuple (offset
)
636 self
.PeCoffType
= "TE"
637 self
.MachineType
= self
.ImageFileMachine
.get (self
.TeHdr
.Machine
, 'Unknown')
638 self
.PeSections
= EfiPeCoff
.TeHdrLength
640 data
= self
.f
.Read(EfiPeCoff
.TeHdrLength
, offset
)
641 self
.PeHdrFmt
= EfiPeCoff
.EFI_TE_IMAGE_HEADER_fmt
644 # PE/COFF starts with PE/COFF header
647 image
= self
.f
.Read(4, offset
)
648 if image
[0:4] != 'PE\0\0':
649 print "Bad PE/COFF Signature = %4s" % image
650 return ("Unknown", None, "", 0, 0)
652 # Check the magic to figure out if 32 or 64 bit PE/COFF
653 (Magic
,) = struct
.unpack_from ('<H', self
.f
.Read(2, offset
+24))
654 if Magic
== EfiPeCoff
.EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
655 data
= self
.f
.Read(EfiPeCoff
.OptionalHeader32Length
, offset
)
656 self
.PeHdr
= EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER32
._make (struct
.Struct(EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER32_fmt
).unpack_from (data
))
657 self
.PeCoffType
= "PE32"
658 self
.PeHdrFmt
= EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER32_fmt
660 # TimeDateStamp: Offset, size
661 self
.ZeroList
.append([offset
+ struct
.calcsize(EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER32_fmt
[0:4]), 4])
662 # Checksum: Offset, size
663 self
.ZeroList
.append([offset
+ struct
.calcsize(EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER32_fmt
[0:29]), 4])
664 elif Magic
== EfiPeCoff
.EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
665 data
= self
.f
.Read(EfiPeCoff
.OptionalHeader64Length
, offset
)
666 self
.PeHdr
= EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER64
._make (struct
.Struct(EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER64_fmt
).unpack_from (data
))
667 self
.PeCoffType
= "PE32+"
668 self
.PeHdrFmt
= EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER64_fmt
670 # TimeDateStamp: Offset, size
671 self
.ZeroList
.append([offset
+ struct
.calcsize(EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER64_fmt
[0:4]), 4])
672 # Checksum: Offset, size
673 self
.ZeroList
.append([offset
+ struct
.calcsize(EfiPeCoff
.EFI_IMAGE_OPTIONAL_HEADER64_fmt
[0:29]), 4])
675 print "Unknown Magic 0x%02x" % Magic
676 return ("Unknown", None, "", 0, 0)
678 self
.MachineType
= self
.ImageFileMachine
.get (self
.PeHdr
.Machine
, 'Unknown')
680 # ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader;
681 self
.PeSections
= offset
+ 4 + 20 + self
.PeHdr
.SizeOfOptionalHeader
683 def PeCoffDumpHdr (self
):
684 PeHdr
= self
.PeHdr
if self
.TeHdr
is None else self
.TeHdr
685 Width
= max (len (s
) for s
in PeHdr
._fields
)
686 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
))
688 def PeCoffZeroInfo (self
):
689 # Return the files offsets and number of bytes that need to get zero'ed
692 def NumberOfSections (self
):
693 if self
.PeHdr
is not None:
694 return self
.PeHdr
.NumberOfSections
695 elif self
.TeHdr
is not None:
696 return self
.TeHdr
.NumberOfSections
700 def PeCoffGetSection (self
, index
):
701 offset
= self
.PeSections
+ (index
* EfiPeCoff
.PeCoffSectionLength
)
702 data
= self
.f
.Read(EfiPeCoff
.PeCoffSectionLength
, offset
)
703 return (data
[0:8].split('\x00')[0], EfiPeCoff
.EFI_IMAGE_SECTION_HEADER
._make
(struct
.Struct(EfiPeCoff
.EFI_IMAGE_SECTION_HEADER_fmt
).unpack_from (data
)))
705 def PeCoffDumpSectionHdr (self
, Name
, Section
):
706 Width
= max (len (s
) for s
in Section
._fields
)
708 for i
, s
in enumerate (Section
._fields
):
709 result
+= '{0} = '.format(s
.ljust(Width
))
710 if i
== 0 and Name
!= '':
711 # print name as a string, not a hex value
712 result
+= Name
+ '\n'
714 result
+= '{0:#0{1}x}\n'.format(getattr(Section
, s
), FmtStrToWidth(EfiPeCoff
.EFI_IMAGE_SECTION_HEADER_fmt
[i
+1])+2)
718 def PeCoffDumpSection (self
, Name
, Section
):
719 data
= self
.f
.Read (Section
.SizeOfRawData
, Section
.VirtualAddress
)
721 Address
= Section
.VirtualAddress
722 for i
in xrange (0, Section
.SizeOfRawData
, 16):
723 HexStr
= ' '.join(["%02X"%ord(x
) for x
in data
[i
:i
+16]])
724 TextStr
= ''.join([x
if 0x20 <= ord(x
) < 0x7F else b
'.' for x
in data
[i
:i
+16]])
725 result
.append("%08X %-*s |%s|\n" % (Address
+ i
, 16*3, HexStr
, TextStr
))
727 return ''.join(result
)
730 def PeCoffGetPdePointer (self
, DebugEntry
= 0, DebugEntrySize
= 0, adjust
= 0):
732 if DebugEntrySize
== 0:
733 if self
.PeHdr
is not None:
734 DebugEntry
= self
.PeHdr
.DataDirVirt_Debug
735 DebugEntrySize
= self
.PeHdr
.DataDirSize_Debug
736 elif self
.TeHdr
is not None:
737 DebugEntry
= self
.TeHdr
.DataDirVirt_Debug
738 DebugEntrySize
= self
.TeHdr
.DataDirSize_Debug
739 adjust
= self
.TeAdjust
743 offset
= DebugEntry
+ adjust
744 data
= self
.f
.Read(DebugEntrySize
, offset
)
745 DirectoryEntry
= EfiPeCoff
.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
._make
(struct
.Struct(EfiPeCoff
.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt
).unpack_from (data
))
746 offset
= DirectoryEntry
.FileOffset
+ adjust
748 data
= self
.f
.Read(4, offset
)
751 data
= self
.f
.Read(16)
752 tup
= struct
.unpack (EfiPeCoff
.EFI_GUID_fmt
, data
)
753 guid
= '{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}'.format(*tup
)
754 Str
= self
.f
.ReadCString ()
756 Str
= self
.f
.ReadCString (offset
+ 16)
758 Str
= self
.f
.ReadCString (offset
+ 24)
763 # Python is more that happy to print out a NULL
764 return (Str
.split('\x00')[0], guid
)
766 def PeCoffDumpRelocations (self
, offset
, size
):
767 data
= self
.f
.Read(size
, offset
)
769 baseEnd
= size
- EfiPeCoff
.BaseRelocationLength
771 while base
< baseEnd
:
772 (VirtualAddress
, SizeOfBlock
) = struct
.unpack_from (EfiPeCoff
.EFI_IMAGE_BASE_RELOCATION_fmt
, data
[base
:base
+ EfiPeCoff
.BaseRelocationLength
])
773 if SizeOfBlock
== 0 or SizeOfBlock
> size
:
775 reloc
= base
+ EfiPeCoff
.BaseRelocationLength
776 relocEnd
= base
+ SizeOfBlock
777 value
+= '0x%08x SizeOfBlock 0x%x\n' % (VirtualAddress
, SizeOfBlock
)
778 while reloc
< relocEnd
:
779 rel
, = struct
.unpack_from ('<H', data
[reloc
:reloc
+2])
780 value
+= ' 0x%04x 0x%x\n' % ((rel
& 0xFFF), rel
>> 12, )
786 def PeCoffDumpCert (self
, offset
, size
):
787 data
= self
.f
.Read(size
, offset
)
790 (dwLength
, wRevision
, wCertificateType
) = struct
.unpack_from (EfiPeCoff
.WIN_CERTIFICATE_fmt
, data
)
791 value
+= "dwLength = 0x%04x wRevision = 0x%02x wCertificateType = 0x%02x\n" % (dwLength
, wRevision
, wCertificateType
)
793 for i
in range(struct
.calcsize(EfiPeCoff
.WIN_CERTIFICATE_fmt
), size
, 0x10):
794 value
+= "0x{:04x}:".format(i
),
795 value
+= " ".join("{:02x}".format(ord(c
)) for c
in data
[i
:i
+0x10])
801 (VirtualAddress
, SizeOfBlock
) = struct
.unpack_from (EfiPeCoff
.EFI_IMAGE_BASE_RELOCATION_fmt
, data
[start
: start
+ struct
.calcsize(EfiPeCoff
.EFI_IMAGE_BASE_RELOCATION_fmt
)])
802 start
+= struct
.calcsize(EfiPeCoff
.EFI_IMAGE_BASE_RELOCATION_fmt
)
803 value
+= "CERT: 0x%X size 0x%x\n" % (VirtualAddress
, SizeOfBlock
)
804 cert
= self
.f
.Read(SizeOfBlock
, VirtualAddress
)
805 for i
in range(0, SizeOfBlock
, 0x10):
806 value
+= "0x{:04x}:".format(i
)
807 value
+= " ".join("{:02x}".format(ord(c
)) for c
in cert
[i
:i
+0x10])
812 return self
.PeCoffDumpHdr()
814 def FmtStrToWidth (c
):
820 if c
== 'I' or c
=='L':
826 #define EFI_FAT_BINARY_MAGIC 0x0ef1fab9
828 # typedef struct _EFI_FAT_BINARY_HEADER {
829 # UINT32 magic; /* FAT_MAGIC */
830 # UINT32 nfat_arch; /* number of structs that follow */
831 # } EFI_FAT_BINARY_HEADER;
833 # typedef struct _EFI_FAT_BINARY_ARCH {
834 # UINT32 cputype; /* cpu specifier (int) */
835 # UINT32 cpusubtype; /* machine specifier (int) */
836 # UINT32 offset; /* file offset to this object file */
837 # UINT32 size; /* size of this object file */
838 # UINT32 align; /* alignment as a power of 2 */
839 # } EFI_FAT_BINARY_ARCH;
841 EFI_FAT_BINARY_ARCH_fmt
= '<IIIII'
844 0x01000007: 'x86_64 (X64)',
845 0x00000007: 'i386 (Ia32)',
846 0x0000000C: 'ARM (Arm)',
847 0x0100000C: 'ARM64 (AArch64)',
850 def CheckForFatBinary (f
):
851 '''Return a list of PE/COFF binary objects, from a file object.
854 data
= f
.read(fatEntry
)
855 (magic
, nfat_arch
) = struct
.unpack_from ('<II', data
)
856 if magic
== 0x0ef1fab9:
858 for i
in range (nfat_arch
):
860 fatEntry
+= struct
.calcsize(EFI_FAT_BINARY_ARCH_fmt
)
861 data
= f
.read(struct
.calcsize(EFI_FAT_BINARY_ARCH_fmt
))
862 (cputype
, cpusubtype
, offset
, size
, align
) = struct
.unpack_from (EFI_FAT_BINARY_ARCH_fmt
, data
)
864 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
)))
866 # entire file is a PE/COFF image
868 res
= [(EfiPeCoff(f
.read()), "")]
871 if __name__
== "__main__":
872 usage
= "usage: %prog [options] PECOFF_FILE"
873 parser
= optparse
.OptionParser(usage
=usage
)
874 parser
.add_option('-r', '--relocations', action
='store_true', dest
='relocation', help='display relocation info', default
=False)
875 parser
.add_option('-c', '--cert', action
='store_true', dest
='cert', help='display security cert info', default
=False)
876 parser
.add_option('-s', '--section', type=str, dest
='section', help='dump info on a section', default
='')
877 parser
.add_option('-z', '--zero', action
='store_true', dest
='zero', help='Zero out fields to enable build reproducibility', default
=False)
878 (options
, args
) = parser
.parse_args(sys
.argv
)
884 with open(args
[1], "rb" if not options
.zero
else "r+b") as f
:
885 for (pecoff
, description
) in CheckForFatBinary (f
):
887 if description
!= '':
890 print "%s is a %s:%s image%s" % (args
[1], pecoff
.PeCoffType
, pecoff
.MachineType
, ' wrapped in FV Section:' if pecoff
.FvSection
else ':')
891 if pecoff
.PeCoffType
== '':
895 print pecoff
.PeCoffDumpHdr()
898 for i
in range (0, pecoff
.NumberOfSections()):
899 (Name
, Section
) = pecoff
.PeCoffGetSection (i
)
900 print pecoff
.PeCoffDumpSectionHdr (Name
, Section
)
903 if '.reloc' in Name
and options
.relocation
:
904 print pecoff
.PeCoffDumpRelocations (Section
.PointerToRawData
, Section
.VirtualSize
)
906 elif '.debug' in Name
:
907 (PdbPointer
, Guid
) = pecoff
.PeCoffGetPdePointer ()
909 print "PdbPointer:{0}\n".format(PdbPointer
)
911 print "PdbPointer (Mach-O symbol file):{0}\n (Mach-O UUID): {1}".format(PdbPointer
, Guid
)
913 if options
.section
in Name
and options
.section
!= '':
914 print pecoff
.PeCoffDumpSection(Name
, Section
)
917 print pecoff
.PeCoffDumpCert (pecoff
.PeHdr
.DataDirVirt_Security
, pecoff
.PeHdr
.DataDirSize_Security
)
920 # this is necessary to find the 'zero list' entries for the debug section
921 for i
in range (0, pecoff
.NumberOfSections()):
922 (Name
, Section
) = pecoff
.PeCoffGetSection (i
)
924 if Section
.SizeOfRawData
> Section
.VirtualSize
:
925 sizeDiff
= Section
.SizeOfRawData
- Section
.VirtualSize
926 offset
= pecoff
.TeAdjust
+ Section
.PointerToRawData
+ Section
.SizeOfRawData
- sizeDiff
927 pecoff
.ZeroList
.append( [offset
, sizeDiff
] )
929 # The .debug section also contains a timestamp
931 pecoff
.ZeroList
.append([pecoff
.TeAdjust
+ Section
.PointerToRawData
+ struct
.calcsize(EfiPeCoff
.EFI_IMAGE_DEBUG_DIRECTORY_ENTRY_fmt
[0:2]), 4])
933 for patch
, size
in pecoff
.PeCoffZeroInfo():
934 #print 'patching 0x%x for 0x%x bytes' % (patch, size)
937 # -1 means to the end of the file
939 size
= f
.tell() - patch
941 f
.write(bytearray(size
))