From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from rn-mailsvcp-ppex-lapp44.apple.com (rn-mailsvcp-ppex-lapp44.apple.com [17.179.253.48]) by mx.groups.io with SMTP id smtpd.web08.14940.1628459217439187572 for ; Sun, 08 Aug 2021 14:46:57 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="body hash did not verify" header.i=@apple.com header.s=20180706 header.b=uNBGT8qB; spf=pass (domain: apple.com, ip: 17.179.253.48, mailfrom: afish@apple.com) Received: from pps.filterd (rn-mailsvcp-ppex-lapp44.rno.apple.com [127.0.0.1]) by rn-mailsvcp-ppex-lapp44.rno.apple.com (8.16.1.2/8.16.1.2) with SMTP id 178Li31o006414; Sun, 8 Aug 2021 14:46:57 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=apple.com; h=mime-version : content-transfer-encoding : content-type : from : to : cc : subject : date : message-id : in-reply-to : references; s=20180706; bh=xoO/SBc+/oikO2WMjHJirFPOAD/zSIdxFN9HTM9Zm/c=; b=uNBGT8qBBKBy88hPWr/gO4rrHVPDurlwfCGNfONAMrMZ8oo5aKuwqvmqNUGEdT5JkoWb tnfYDPu0ZJWLEolgcTNNhfyCUwoLIOdc7X+9ljBjZuBh5aRU1TcQyw2F6jZGv1ybIW/E jtcirnp9p1TRi6CA0+pwk0bL+U7LZSw7VGV+zaUW0WGG9r723VGjQ5rE0IBD2xBBx7qI PE20pX4TCh0lpK3zaHBTt0UvPrZam7Z21n2x1hWV0kALZ8kV7kVkMmZkLauV2igzY8XJ bYvW/JHfeeQbke73GvTQNrrBIjJZunGZQ6ivy/A2GklrXC5FGfDOUv94zGN0pTxaiHX1 kA== Received: from rn-mailsvcp-mta-lapp01.rno.apple.com (rn-mailsvcp-mta-lapp01.rno.apple.com [10.225.203.149]) by rn-mailsvcp-ppex-lapp44.rno.apple.com with ESMTP id 3a9np8qbem-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NO); Sun, 08 Aug 2021 14:46:56 -0700 Received: from rn-mailsvcp-relay-lapp02.rno.apple.com (rn-mailsvcp-relay-lapp02.rno.apple.com [17.179.253.11]) by rn-mailsvcp-mta-lapp01.rno.apple.com (Oracle Communications Messaging Server 8.1.0.9.20210415 64bit (built Apr 15 2021)) with ESMTPS id <0QXJ005Z7J68VD00@rn-mailsvcp-mta-lapp01.rno.apple.com>; Sun, 08 Aug 2021 14:46:56 -0700 (PDT) Received: from process_milters-daemon.rn-mailsvcp-relay-lapp02.rno.apple.com by rn-mailsvcp-relay-lapp02.rno.apple.com (Oracle Communications Messaging Server 8.1.0.9.20210415 64bit (built Apr 15 2021)) id <0QXJ00M00ITLBB00@rn-mailsvcp-relay-lapp02.rno.apple.com>; Sun, 08 Aug 2021 14:46:56 -0700 (PDT) X-Va-A: X-Va-T-CD: 3a2c4b67349838e305a7a1f00570e43c X-Va-E-CD: 9be1daee32400b4757873ed762c62857 X-Va-R-CD: afdcc135586d76fc6e41255f733f93d7 X-Va-CD: 0 X-Va-ID: f7c476c1-875e-45a5-9171-50b657bb4125 X-V-A: X-V-T-CD: 3a2c4b67349838e305a7a1f00570e43c X-V-E-CD: 9be1daee32400b4757873ed762c62857 X-V-R-CD: afdcc135586d76fc6e41255f733f93d7 X-V-CD: 0 X-V-ID: 8e76ed53-f254-4d9b-90c3-ac938b5cecbb X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.391,18.0.790 definitions=2021-08-08_07:2021-08-06,2021-08-08 signatures=0 MIME-version: 1.0 Received: from rn-mailsvcp-relay-lapp01.rno.apple.com (unknown [17.235.59.204]) by rn-mailsvcp-relay-lapp02.rno.apple.com (Oracle Communications Messaging Server 8.1.0.9.20210415 64bit (built Apr 15 2021)) with ESMTP id <0QXJ00EJXJ67C820@rn-mailsvcp-relay-lapp02.rno.apple.com>; Sun, 08 Aug 2021 14:46:56 -0700 (PDT) From: "Andrew Fish" To: devel@edk2.groups.io Cc: Andrew Fish , Leif Lindholm , Michael D Kinney , Hao A Wu Subject: [PATCH 1/3] efi_debugging.py: - Add debugger agnostic debugging Python Classes Date: Sun, 08 Aug 2021 14:46:32 -0700 Message-id: X-Mailer: git-send-email 2.30.1 (Apple Git-130) In-reply-to: References: X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.391,18.0.790 definitions=2021-08-08_07:2021-08-06,2021-08-08 signatures=0 X-MIME-Autoconverted: from 8bit to quoted-printable by rn-mailsvcp-ppex-lapp44.rno.apple.com id 178Li31o006414 Content-type: text/plain; charset=y Content-Transfer-Encoding: quoted-printable https://bugzilla.tianocore.org/show_bug.cgi?id=3D3500 Add Python classes that take a Python file like object and provide services to other debugger specific Python scripts. Classes: PeTeImage - Discovery and parse PE/COFF and TE Images. Used to load symbo= ls This may end up in BaseTools eventually. EfiConfigurationTable - Parse EFI Configuration Tables. EfiDevicePath - Decode EFI Device Paths EfiHob - Decode EFI HOBs GuidNames - Manage EFI_GUID's and display the C names used in code. EfiStatusClass - Print EFI_STATUS as text strings. EfiBootMode - Print out the boot mode as strings. EfiTpl - Print out the EFI_TPL relative to standard levels. This module uses Python ctype and patches UINTN and pointer values for 32-bit EFI systems. This module is also a PE/COFF image dumper if it is called directly: $ ./efi_debugging.py DiskIoDxe.efi :X64`\DiskIoDxe.pdb load =3D 0x00000000 EntryPoint =3D 0x000002c4 TextAddress =3D 0x000002c0 DataAddress =3D 0x0= 0004f40 .text 0x000002C0 (0x0369A) flags:0x68000020 .rdata 0x00003960 (0x015E0) flags:0x48000040 .data 0x00004F40 (0x00188) flags:0xC8000040 0x000050E0 (0x00234) flags:0x42000040 .xdata 0x00005320 (0x001B0) flags:0x42000040 .reloc 0x000054E0 (0x0002C) flags:0x42000040 Data Directories: Relocation Table: 0x000054E0 0x2C Debug : 0x00004DB0 0x54 Cc: Leif Lindholm Cc: Michael D Kinney Cc: Hao A Wu Signed-off-by: Andrew Fish --- efi_debugging.py | 2187 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2187 insertions(+) create mode 100755 efi_debugging.py diff --git a/efi_debugging.py b/efi_debugging.py new file mode 100755 index 000000000000..58c00921ec7b --- /dev/null +++ b/efi_debugging.py @@ -0,0 +1,2187 @@ +#!/usr/bin/python3 +''' +Copyright (c) Apple Inc. 2021 +SPDX-License-Identifier: BSD-2-Clause-Patent + +Class that abstracts PE/COFF debug info parsing via a Python file like +object. You can port this code into an arbitrary debugger by invoking +the classes and passing in a file like object that abstracts the debugge= r +reading memory. + +If you run this file directly it will parse the passed in PE/COFF files +for debug info: +python3 ./efi_pefcoff.py DxeCore.efi +IA32`/DxeCore.dll load =3D 0x00000000 +EntryPoint =3D 0x000030d2 TextAddress =3D 0x00000240 DataAddress =3D 0x= 000042c0 +.text 0x00000240 (0x04080) flags:0x60000020 +.data 0x000042C0 (0x001C0) flags:0xC0000040 +.reloc 0x00004480 (0x00240) flags:0x42000040 + +Note: PeCoffClass uses virtual addresses and not file offsets. + It needs to work when images are loaded into memory. + as long as virtual address map to file addresses this + code can process binary files. + +Note: This file can also contain generic worker functions (like GuidName= s) + that abstract debugger agnostic services to the debugger. + +This file should never import debugger specific modules. +''' + +import sys +import os +import uuid +import struct +import re +from ctypes import c_char, c_uint8, c_uint16, c_uint32, c_uint64, c_void= _p +from ctypes import ARRAY, sizeof +from ctypes import Structure, LittleEndianStructure + +# +# The empty LittleEndianStructure must have _fields_ assigned prior to u= se or +# sizeof(). Anything that is size UINTN may need to get adjusted. +# +# The issue is ctypes matches our local machine, not the machine we are +# trying to debug. Call patch_ctypes() passing in the byte width from t= he +# debugger python to make sure you are in sync. +# +# Splitting out the _field_ from the Structure (LittleEndianStructure) c= lass +# allows it to be patched. +# + + +class EFI_LOADED_IMAGE_PROTOCOL(LittleEndianStructure): + pass + + +EFI_LOADED_IMAGE_PROTOCOL_fields_ =3D [ + ('Revision', c_uint32), + ('ParentHandle', c_void_p), + ('SystemTable', c_void_p), + ('DeviceHandle', c_void_p), + ('FilePath', c_void_p), + ('Reserved', c_void_p), + ('LoadOptionsSize', c_uint32), + ('LoadOptions', c_void_p), + ('ImageBase', c_void_p), + ('ImageSize', c_uint64), + ('ImageCodeType', c_uint32), + ('ImageDataType', c_uint32), + ('Unload', c_void_p), +] + + +class EFI_GUID(LittleEndianStructure): + _fields_ =3D [ + ('Data1', c_uint32), + ('Data2', c_uint16), + ('Data3', c_uint16), + ('Data4', ARRAY(c_uint8, 8)) + ] + + +class EFI_SYSTEM_TABLE_POINTER(LittleEndianStructure): + _fields_ =3D [ + ('Signature', c_uint64), + ('EfiSystemTableBase', c_uint64), + ('Crc32', c_uint32) + ] + + +class EFI_DEBUG_IMAGE_INFO_NORMAL(LittleEndianStructure): + pass + + +EFI_DEBUG_IMAGE_INFO_NORMAL_fields_ =3D [ + ('ImageInfoType', c_uint32), + ('LoadedImageProtocolInstance', c_void_p), + ('ImageHandle', c_void_p) +] + + +class EFI_DEBUG_IMAGE_INFO(LittleEndianStructure): + pass + + +EFI_DEBUG_IMAGE_INFO_fields_ =3D [ + ('NormalImage', c_void_p), +] + + +class EFI_DEBUG_IMAGE_INFO_TABLE_HEADER(LittleEndianStructure): + pass + + +EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_ =3D [ + ('UpdateStatus', c_uint32), + ('TableSize', c_uint32), + ('EfiDebugImageInfoTable', c_void_p), +] + + +class EFI_TABLE_HEADER(LittleEndianStructure): + _fields_ =3D [ + ('Signature', c_uint64), + ('Revision', c_uint32), + ('HeaderSize', c_uint32), + ('CRC32', c_uint32), + ('Reserved', c_uint32), + ] + + +class EFI_CONFIGURATION_TABLE(LittleEndianStructure): + pass + + +EFI_CONFIGURATION_TABLE_fields_ =3D [ + ('VendorGuid', EFI_GUID), + ('VendorTable', c_void_p) +] + + +class EFI_SYSTEM_TABLE(LittleEndianStructure): + pass + + +EFI_SYSTEM_TABLE_fields_ =3D [ + ('Hdr', EFI_TABLE_HEADER), + ('FirmwareVendor', c_void_p), + ('FirmwareRevision', c_uint32), + ('ConsoleInHandle', c_void_p), + ('ConIn', c_void_p), + ('ConsoleOutHandle', c_void_p), + ('ConOut', c_void_p), + ('StandardErrHandle', c_void_p), + ('StdErr', c_void_p), + ('RuntimeService', c_void_p), + ('BootService', c_void_p), + ('NumberOfTableEntries', c_void_p), + ('ConfigurationTable', c_void_p), +] + + +class EFI_IMAGE_DATA_DIRECTORY(LittleEndianStructure): + _fields_ =3D [ + ('VirtualAddress', c_uint32), + ('Size', c_uint32) + ] + + +class EFI_TE_IMAGE_HEADER(LittleEndianStructure): + _fields_ =3D [ + ('Signature', ARRAY(c_char, 2)), + ('Machine', c_uint16), + ('NumberOfSections', c_uint8), + ('Subsystem', c_uint8), + ('StrippedSize', c_uint16), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ('ImageBase', c_uint64), + ('DataDirectoryBaseReloc', EFI_IMAGE_DATA_DIRECTORY), + ('DataDirectoryDebug', EFI_IMAGE_DATA_DIRECTORY) + ] + + +class EFI_IMAGE_DOS_HEADER(LittleEndianStructure): + _fields_ =3D [ + ('e_magic', c_uint16), + ('e_cblp', c_uint16), + ('e_cp', c_uint16), + ('e_crlc', c_uint16), + ('e_cparhdr', c_uint16), + ('e_minalloc', c_uint16), + ('e_maxalloc', c_uint16), + ('e_ss', c_uint16), + ('e_sp', c_uint16), + ('e_csum', c_uint16), + ('e_ip', c_uint16), + ('e_cs', c_uint16), + ('e_lfarlc', c_uint16), + ('e_ovno', c_uint16), + ('e_res', ARRAY(c_uint16, 4)), + ('e_oemid', c_uint16), + ('e_oeminfo', c_uint16), + ('e_res2', ARRAY(c_uint16, 10)), + ('e_lfanew', c_uint16) + ] + + +class EFI_IMAGE_FILE_HEADER(LittleEndianStructure): + _fields_ =3D [ + ('Machine', c_uint16), + ('NumberOfSections', c_uint16), + ('TimeDateStamp', c_uint32), + ('PointerToSymbolTable', c_uint32), + ('NumberOfSymbols', c_uint32), + ('SizeOfOptionalHeader', c_uint16), + ('Characteristics', c_uint16) + ] + + +class EFI_IMAGE_OPTIONAL_HEADER32(LittleEndianStructure): + _fields_ =3D [ + ('Magic', c_uint16), + ('MajorLinkerVersion', c_uint8), + ('MinorLinkerVersion', c_uint8), + ('SizeOfCode', c_uint32), + ('SizeOfInitializedData', c_uint32), + ('SizeOfUninitializedData', c_uint32), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ('BaseOfData', c_uint32), + ('ImageBase', c_uint32), + ('SectionAlignment', c_uint32), + ('FileAlignment', c_uint32), + ('MajorOperatingSystemVersion', c_uint16), + ('MinorOperatingSystemVersion', c_uint16), + ('MajorImageVersion', c_uint16), + ('MinorImageVersion', c_uint16), + ('MajorSubsystemVersion', c_uint16), + ('MinorSubsystemVersion', c_uint16), + ('Win32VersionValue', c_uint32), + ('SizeOfImage', c_uint32), + ('SizeOfHeaders', c_uint32), + ('CheckSum', c_uint32), + ('Subsystem', c_uint16), + ('DllCharacteristics', c_uint16), + ('SizeOfStackReserve', c_uint32), + ('SizeOfStackCommit', c_uint32), + ('SizeOfHeapReserve', c_uint32), + ('SizeOfHeapCommit', c_uint32), + ('LoaderFlags', c_uint32), + ('NumberOfRvaAndSizes', c_uint32), + ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY= , 16)) + ] + + +class EFI_IMAGE_NT_HEADERS32(LittleEndianStructure): + _fields_ =3D [ + ('Signature', c_uint32), + ('FileHeader', EFI_IMAGE_FILE_HEADER), + ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER32) + ] + + +class EFI_IMAGE_OPTIONAL_HEADER64(LittleEndianStructure): + _fields_ =3D [ + ('Magic', c_uint16), + ('MajorLinkerVersion', c_uint8), + ('MinorLinkerVersion', c_uint8), + ('SizeOfCode', c_uint32), + ('SizeOfInitializedData', c_uint32), + ('SizeOfUninitializedData', c_uint32), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ('BaseOfData', c_uint32), + ('ImageBase', c_uint32), + ('SectionAlignment', c_uint32), + ('FileAlignment', c_uint32), + ('MajorOperatingSystemVersion', c_uint16), + ('MinorOperatingSystemVersion', c_uint16), + ('MajorImageVersion', c_uint16), + ('MinorImageVersion', c_uint16), + ('MajorSubsystemVersion', c_uint16), + ('MinorSubsystemVersion', c_uint16), + ('Win32VersionValue', c_uint32), + ('SizeOfImage', c_uint32), + ('SizeOfHeaders', c_uint32), + ('CheckSum', c_uint32), + ('Subsystem', c_uint16), + ('DllCharacteristics', c_uint16), + ('SizeOfStackReserve', c_uint64), + ('SizeOfStackCommit', c_uint64), + ('SizeOfHeapReserve', c_uint64), + ('SizeOfHeapCommit', c_uint64), + ('LoaderFlags', c_uint32), + ('NumberOfRvaAndSizes', c_uint32), + ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY= , 16)) + ] + + +class EFI_IMAGE_NT_HEADERS64(LittleEndianStructure): + _fields_ =3D [ + ('Signature', c_uint32), + ('FileHeader', EFI_IMAGE_FILE_HEADER), + ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER64) + ] + + +class EFI_IMAGE_DEBUG_DIRECTORY_ENTRY(LittleEndianStructure): + _fields_ =3D [ + ('Characteristics', c_uint32), + ('TimeDateStamp', c_uint32), + ('MajorVersion', c_uint16), + ('MinorVersion', c_uint16), + ('Type', c_uint32), + ('SizeOfData', c_uint32), + ('RVA', c_uint32), + ('FileOffset', c_uint32), + ] + + +class EFI_IMAGE_SECTION_HEADER(LittleEndianStructure): + _fields_ =3D [ + ('Name', ARRAY(c_char, 8)), + ('VirtualSize', c_uint32), + ('VirtualAddress', c_uint32), + ('SizeOfRawData', c_uint32), + ('PointerToRawData', c_uint32), + ('PointerToRelocations', c_uint32), + ('PointerToLinenumbers', c_uint32), + ('NumberOfRelocations', c_uint16), + ('NumberOfLinenumbers', c_uint16), + ('Characteristics', c_uint32), + ] + + +EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC =3D 0x10b +EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC =3D 0x20b + +DIRECTORY_DEBUG =3D 6 + + +image_machine_dict =3D { + 0x014c: "IA32", + 0x0200: "IPF", + 0x0EBC: "EBC", + 0x8664: "X64", + 0x01c2: "ARM", + 0xAA64: "AArch64", + 0x5032: "RISKV32", + 0x5064: "RISKV64", + 0x5128: "RISKV128", +} + + +def patch_void_p_to_ctype(patch_type, to_patch): + '''Optionally patch c_void_p in the Structure._fields_''' + if patch_type is None: + return to_patch + + result =3D [] + for name, c_type in to_patch: + if type(c_type) =3D=3D type(c_void_p): + result.append((name, c_uint32)) + else: + result.append((name, c_type)) + return result + + +def patch_ctypes(pointer_width=3D8): + ''' + Pass in the pointer width of the system being debugged. If it is not + the same as c_void_p then patch the _fields_ with the correct type. + For any ctypes Structure that has a c_void_p this function needs to = be + called prior to use or sizeof() to initialize _fields_. + ''' + + if sizeof(c_void_p) =3D=3D pointer_width: + patch_type =3D None + elif pointer_width =3D=3D 16: + assert False + elif pointer_width =3D=3D 8: + patch_type =3D c_uint64 + elif pointer_width =3D=3D 4: + patch_type =3D c_uint32 + else: + raise Exception(f'ERROR: Unkown pointer_width =3D {pointer_width= }') + + # If you add a ctypes Structure class with a c_void_p you need to ad= d + # it to this list. Note: you should use c_void_p for UINTN values. + EFI_LOADED_IMAGE_PROTOCOL._fields_ =3D patch_void_p_to_ctype( + patch_type, EFI_LOADED_IMAGE_PROTOCOL_fields_) + EFI_DEBUG_IMAGE_INFO_NORMAL._fields_ =3D patch_void_p_to_ctype( + patch_type, EFI_DEBUG_IMAGE_INFO_NORMAL_fields_) + EFI_DEBUG_IMAGE_INFO._fields_ =3D patch_void_p_to_ctype( + patch_type, EFI_DEBUG_IMAGE_INFO_fields_) + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER._fields_ =3D patch_void_p_to_ctype= ( + patch_type, EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_) + EFI_CONFIGURATION_TABLE._fields_ =3D patch_void_p_to_ctype( + patch_type, EFI_CONFIGURATION_TABLE_fields_) + EFI_SYSTEM_TABLE._fields_ =3D patch_void_p_to_ctype( + patch_type, EFI_SYSTEM_TABLE_fields_) + + # patch up anything else that needs to know pointer_width + EfiStatusClass(pointer_width) + + +def ctype_to_str(ctype, indent=3D'', hide_list=3D[]): + ''' + Given a ctype object print out as a string by walking the _fields_ + in the cstring Class + ''' + result =3D '' + for field in ctype._fields_: + attr =3D getattr(ctype, field[0]) + tname =3D type(attr).__name__ + if field[0] in hide_list: + continue + + result +=3D indent + f'{field[0]} =3D ' + if tname =3D=3D 'EFI_GUID': + result +=3D GuidNames.to_name(GuidNames.to_uuid(attr)) + '\n= ' + elif issubclass(type(attr), Structure): + result +=3D f'{tname}\n' + \ + ctype_to_str(attr, indent + ' ', hide_list) + elif isinstance(attr, int): + result +=3D f'0x{attr:x}\n' + else: + result +=3D f'{attr}\n' + + return result + + +def hexline(addr, data): + hexstr =3D '' + printable =3D '' + for i in range(0, len(data)): + hexstr +=3D f'{data[i]:02x} ' + printable +=3D chr(data[i]) if data[i] > 0x20 and data[i] < 0x7f= else '.' + return f'{addr:04x} {hexstr:48s} |{printable:s}|' + + +def hexdump(data, indent=3D''): + if not isinstance(data, bytearray): + data =3D bytearray(data) + + result =3D '' + for i in range(0, len(data), 16): + result +=3D indent + hexline(i, data[i:i+16]) + '\n' + return result + + +class EfiTpl: + ''' Return string for EFI_TPL''' + + def __init__(self, tpl): + self.tpl =3D tpl + + def __str__(self): + if self.tpl < 4: + result =3D f'{self.tpl:d}' + elif self.tpl < 8: + result =3D "TPL_APPLICATION" + if self.tpl - 4 > 0: + result +=3D f' + {self.tpl - 4:d}' + elif self.tpl < 16: + result =3D "TPL_CALLBACK" + if self.tpl - 8 > 0: + result +=3D f' + {self.tpl - 8:d}' + elif self.tpl < 31: + result =3D "TPL_NOTIFY" + if self.tpl - 16 > 0: + result +=3D f' + {self.tpl - 16:d}' + elif self.tpl =3D=3D 31: + result =3D "TPL_HIGH_LEVEL" + else: + result =3D f'Invalid TPL =3D {self.tpl:d}' + return result + + +class EfiBootMode: + ''' + Class to return human readable string for EFI_BOOT_MODE + + Methods + ----------- + to_str(boot_mode, default) + return string for boot_mode, and return default if there is not = a + match. + ''' + + EFI_BOOT_MODE_dict =3D { + 0x00: "BOOT_WITH_FULL_CONFIGURATION", + 0x01: "BOOT_WITH_MINIMAL_CONFIGURATION", + 0x02: "BOOT_ASSUMING_NO_CONFIGURATION_CHANGES", + 0x03: "BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS", + 0x04: "BOOT_WITH_DEFAULT_SETTINGS", + 0x05: "BOOT_ON_S4_RESUME", + 0x06: "BOOT_ON_S5_RESUME", + 0x07: "BOOT_WITH_MFG_MODE_SETTINGS", + 0x10: "BOOT_ON_S2_RESUME", + 0x11: "BOOT_ON_S3_RESUME", + 0x12: "BOOT_ON_FLASH_UPDATE", + 0x20: "BOOT_IN_RECOVERY_MODE", + } + + def __init__(self, boot_mode): + self._boot_mode =3D boot_mode + + def __str__(self): + return self.to_str(self._boot_mode) + + @classmethod + def to_str(cls, boot_mode, default=3D''): + return cls.EFI_BOOT_MODE_dict.get(boot_mode, default) + + +class EfiStatusClass: + ''' + Class to decode EFI_STATUS to a human readable string. You need to + pass in pointer_width to get the corret value since the EFI_STATUS + code values are different based on the sizeof UINTN. The default is + sizeof(UINTN) =3D=3D 8. + + Attributes + =E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94 + _dict_ : dictionary + dictionary of EFI_STATUS that has beed updated to match + pointer_width. + + Methods + ----------- + patch_dictionary(pointer_width) + + to_str(status, default) + ''' + + _dict_ =3D {} + _EFI_STATUS_UINT32_dict =3D { + 0: "Success", + 1: "Warning Unknown Glyph", + 2: "Warning Delete Failure", + 3: "Warning Write Failure", + 4: "Warning Buffer Too Small", + 5: "Warning Stale Data", + 6: "Warngin File System", + (0x20000000 | 0): "Warning interrupt source pending", + (0x20000000 | 1): "Warning interrupt source quiesced", + + (0x80000000 | 1): "Load Error", + (0x80000000 | 2): "Invalid Parameter", + (0x80000000 | 3): "Unsupported", + (0x80000000 | 4): "Bad Buffer Size", + (0x80000000 | 5): "Buffer Too Small", + (0x80000000 | 6): "Not Ready", + (0x80000000 | 7): "Device Error", + (0x80000000 | 8): "Write Protected", + (0x80000000 | 9): "Out of Resources", + (0x80000000 | 10): "Volume Corrupt", + (0x80000000 | 11): "Volume Full", + (0x80000000 | 12): "No Media", + (0x80000000 | 13): "Media changed", + (0x80000000 | 14): "Not Found", + (0x80000000 | 15): "Access Denied", + (0x80000000 | 16): "No Response", + (0x80000000 | 17): "No mapping", + (0x80000000 | 18): "Time out", + (0x80000000 | 19): "Not started", + (0x80000000 | 20): "Already started", + (0x80000000 | 21): "Aborted", + (0x80000000 | 22): "ICMP Error", + (0x80000000 | 23): "TFTP Error", + (0x80000000 | 24): "Protocol Error", + (0x80000000 | 25): "Incompatible Version", + (0x80000000 | 26): "Security Violation", + (0x80000000 | 27): "CRC Error", + (0x80000000 | 28): "End of Media", + (0x80000000 | 31): "End of File", + (0x80000000 | 32): "Invalid Language", + (0x80000000 | 33): "Compromised Data", + (0x80000000 | 35): "HTTP Error", + + (0xA0000000 | 0): "Interrupt Pending", + } + + def __init__(self, status=3DNone, pointer_width=3D8): + self.status =3D status + # this will convert to 64-bit version if needed + self.patch_dictionary(pointer_width) + + def __str__(self): + return self.to_str(self.status) + + @classmethod + def to_str(cls, status, default=3D''): + return cls._dict_.get(status, default) + + @classmethod + def patch_dictionary(cls, pointer_width): + '''Patch UINTN upper bits like values ''' + + if cls._dict_: + # only patch the class variable once + return False + + if pointer_width =3D=3D 4: + cls._dict =3D cls._EFI_STATUS_UINT32_dict + elif pointer_width =3D=3D 8: + for key, value in cls._EFI_STATUS_UINT32_dict.items(): + mask =3D (key & 0xE0000000) << 32 + new_key =3D (key & 0x1FFFFFFF) | mask + cls._dict_[new_key] =3D value + return True + else: + return False + + +class GuidNames: + ''' + Class to expose the C names of EFI_GUID's. The _dict_ starts with + common EFI System Table entry EFI_GUID's. _dict_ can get updated wit= h the + build generated Guid.xref file if a path to a module is passed + into add_build_guid_file(). If symbols are loaded for any module + in the build the path the build product should imply the + relative location of that builds Guid.xref file. + + Attributes + =E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94---- + _dict_ : dictionary + dictionary of EFI_GUID (uuid) strings to C global names + + Methods + ------- + to_uuid(uuid) + convert a hex UUID string or bytearray to a uuid.UUID + to_name(uuid) + convert a UUID string to a C global constant name. + to_guid(guid_name) + convert a C global constant EFI_GUID name to uuid hex string. + is_guid_str(name) + name is a hex UUID string. + Example: 49152E77-1ADA-4764-B7A2-7AFEFED95E8B + + to_c_guid(value) + convert a uuid.UUID or UUID string to a c_guid string + (see is_c_guid()) + from_c_guid(value) + covert a C guid string to a hex UUID string. + is_c_guid(name) + name is the C initialization value for an EFI_GUID. Example: + { 0x414e6bdd, 0xe47b, 0x47cc, { 0xb2, 0x44, 0xbb, 0x61, + 0x02, 0x0c, 0xf5, 0x16 }} + + add_build_guid_file(module_path, custom_file): + assume module_path is an edk2 build product and load the Guid.xr= ef + file from that build to fill in _dict_. If you know the path and + file name of a custom Guid.xref you can pass it in as custom_fi= le. + + ''' + _dict_ =3D { # Common EFI System Table values + '05AD34BA-6F02-4214-952E-4DA0398E2BB9': + 'gEfiDxeServicesTableGuid', + '7739F24C-93D7-11D4-9A3A-0090273FC14D': + 'gEfiHobListGuid', + '4C19049F-4137-4DD3-9C10-8B97A83FFDFA': + 'gEfiMemoryTypeInformationGuid', + '49152E77-1ADA-4764-B7A2-7AFEFED95E8B': + 'gEfiDebugImageInfoTableGuid', + '060CC026-4C0D-4DDA-8F41-595FEF00A502': + 'gMemoryStatusCodeRecordGuid', + 'EB9D2D31-2D88-11D3-9A16-0090273FC14D': + 'gEfiSmbiosTableGuid', + 'EB9D2D30-2D88-11D3-9A16-0090273FC14D': + 'gEfiAcpi10TableGuid', + '8868E871-E4F1-11D3-BC22-0080C73C8881': + 'gEfiAcpi20TableGuid', + } + + guid_files =3D [] + + def __init__(self, uuid=3DNone, pointer_width=3D8): + self.uuid =3D None if uuid is None else self.to_uuid(uuid) + + def __str__(self): + if self.uuid is None: + result =3D '' + for key, value in GuidNames._dict_.items(): + result +=3D f'{key}: {value}\n' + else: + result =3D self.to_name(self.uuid) + + return result + + @classmethod + def to_uuid(cls, obj): + try: + return uuid.UUID(bytes_le=3Dbytes(obj)) + except (ValueError, TypeError): + try: + return uuid.UUID(bytes_le=3Dobj) + except (ValueError, TypeError): + return uuid.UUID(obj) + + @classmethod + def to_name(cls, uuid): + if not isinstance(uuid, str): + uuid =3D str(uuid) + if cls.is_c_guid(uuid): + uuid =3D cls.from_c_guid(uuid) + return cls._dict_.get(uuid.upper(), uuid.upper()) + + @classmethod + def to_guid(cls, guid_name): + for key, value in cls._dict_.items(): + if guid_name =3D=3D value: + return key.upper() + else: + raise KeyError(key) + + @classmethod + def is_guid_str(cls, name): + if not isinstance(name, str): + return False + return name.count('-') >=3D 4 + + @classmethod + def to_c_guid(cls, value): + if isinstance(value, uuid.UUID): + guid =3D value + else: + guid =3D uuid.UUID(value) + + (data1, data2, data3, + data4_0, data4_1, data4_2, data4_3, + data4_4, data4_5, data4_6, data4_7) =3D struct.unpack( + '/Build/OvmfX64/DEBUG_XCODE5/X64/../DxeCore.dll + # Walk backwards looking for a toolchain like name. + # Then look for GUID database: + # Build/OvmfX64//DEBUG_XCODE5/FV/Guid.xref + for i in reversed(module_path.split(os.sep)): + if (i.startswith('DEBUG_') or + i.startswith('RELEASE_') or + i.startswith('NOOPT_')): + build_root =3D os.path.join( + module_path.rsplit(i, 1)[0], i) + break + + xref =3D os.path.join(build_root, 'FV', 'Guid.xref') + + if xref in cls.guid_files: + # only processes the file one time + return True + + with open(xref) as f: + content =3D f.readlines() + cls.guid_files.append(xref) + + for lines in content: + try: + if cls.is_guid_str(lines): + # a regex would be more pedantic + words =3D lines.split() + cls._dict_[words[0].upper()] =3D words[1].strip(= '\n') + except ValueError: + pass + + return True + + return False + + +class EFI_HOB_GENERIC_HEADER(LittleEndianStructure): + _fields_ =3D [ + ('HobType', c_uint16), + ('HobLength', c_uint16), + ('Reserved', c_uint32) + ] + + +class EFI_HOB_HANDOFF_INFO_TABLE(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('Version', c_uint32), + ('BootMode', c_uint32), + ('EfiMemoryTop', c_uint64), + ('EfiMemoryBottom', c_uint64), + ('EfiFreeMemoryTop', c_uint64), + ('EfiFreeMemoryBottom', c_uint64), + ('EfiEndOfHobList', c_uint64), + ] + + +class EFI_HOB_MEMORY_ALLOCATION(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('Name', EFI_GUID), + ('MemoryBaseAddress', c_uint64), + ('MemoryLength', c_uint64), + ('MemoryType', c_uint32), + ('Reserved', c_uint32), + ] + + +class EFI_HOB_RESOURCE_DESCRIPTOR(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('Owner', EFI_GUID), + ('ResourceType', c_uint32), + ('ResourceAttribute', c_uint32), + ('PhysicalStart', c_uint64), + ('ResourceLength', c_uint64), + ] + + +class EFI_HOB_GUID_TYPE(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('Name', EFI_GUID), + ] + + +class EFI_HOB_FIRMWARE_VOLUME(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('BaseAddress', c_uint64), + ('Length', c_uint64), + ] + + +class EFI_HOB_CPU(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('SizeOfMemorySpace', c_uint8), + ('SizeOfIoSpace', c_uint8), + ('Reserved', ARRAY(c_uint8, 6)), + ] + + +class EFI_HOB_MEMORY_POOL(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ] + + +class EFI_HOB_FIRMWARE_VOLUME2(LittleEndianStructure): + _fields_ =3D [ + ('Header', EFI_HOB_GENERIC_HEADER), + ('BaseAddress', c_uint64), + ('Length', c_uint64), + ('FvName', EFI_GUID), + ('FileName', EFI_GUID) + ] + + +class EFI_HOB_FIRMWARE_VOLUME3(LittleEndianStructure): + _fields_ =3D [ + ('HobType', c_uint16), + ('HobLength', c_uint16), + ('Reserved', c_uint32), + ('BaseAddress', c_uint64), + ('Length', c_uint64), + ('AuthenticationStatus', c_uint32), + ('ExtractedFv', c_uint8), + ('FvName', EFI_GUID), + ('FileName', EFI_GUID), + ] + + +class EFI_HOB_UEFI_CAPSULE(LittleEndianStructure): + _fields_ =3D [ + ('HobType', c_uint16), + ('HobLength', c_uint16), + ('Reserved', c_uint32), + ('BaseAddress', c_uint64), + ('Length', c_uint64), + ] + + +class EfiHob: + ''' + Parse EFI Device Paths based on the edk2 C Structures defined above. + In the context of this class verbose means hexdump extra data. + + + Attributes + =E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94 + Hob : list + List of HOBs. Each entry contains the name, HOB type, HOB length= , + the ctype struct for the HOB, and any extra data. + + Methods + ----------- + get_hob_by_type(hob_type) + return string that decodes the HOBs of hob_type. If hob_type is + None then return all HOBs. + ''' + + Hob =3D [] + verbose =3D False + + hob_dict =3D { + 1: EFI_HOB_HANDOFF_INFO_TABLE, + 2: EFI_HOB_MEMORY_ALLOCATION, + 3: EFI_HOB_RESOURCE_DESCRIPTOR, + 4: EFI_HOB_GUID_TYPE, + 5: EFI_HOB_FIRMWARE_VOLUME, + 6: EFI_HOB_CPU, + 7: EFI_HOB_MEMORY_POOL, + 9: EFI_HOB_FIRMWARE_VOLUME2, + 0xb: EFI_HOB_UEFI_CAPSULE, + 0xc: EFI_HOB_FIRMWARE_VOLUME3, + 0xffff: EFI_HOB_GENERIC_HEADER, + } + + def __init__(self, file, address=3DNone, verbose=3DFalse, count=3D10= 00): + self._file =3D file + EfiHob.verbose =3D verbose + + if len(EfiHob.Hob) !=3D 0 and address is None: + return + + if address is not None: + hob_ptr =3D address + else: + hob_ptr =3D EfiConfigurationTable(file).GetConfigTable( + '7739F24C-93D7-11D4-9A3A-0090273FC14D') + + self.read_hobs(hob_ptr) + + @ classmethod + def __str__(cls): + return cls.get_hob_by_type(None) + + @ classmethod + def get_hob_by_type(cls, hob_type): + result =3D "" + for (Name, HobType, HobLen, chob, extra) in cls.Hob: + if hob_type is not None: + if hob_type !=3D HobType: + continue + + result +=3D f'Type: {Name:s} (0x{HobType:01x}) Len: 0x{HobLe= n:03x}\n' + result +=3D ctype_to_str(chob, ' ', ['Reserved']) + if cls.verbose: + if extra is not None: + result +=3D hexdump(extra, ' ') + + return result + + def read_hobs(self, hob_ptr, count=3D1000): + if hob_ptr is None: + return + + try: + for _ in range(count): # while True + hdr, _ =3D self._ctype_read_ex(EFI_HOB_GENERIC_HEADER, h= ob_ptr) + if hdr.HobType =3D=3D 0xffff: + break + + type_str =3D self.hob_dict.get( + hdr.HobType, EFI_HOB_GENERIC_HEADER) + hob, extra =3D self._ctype_read_ex( + type_str, hob_ptr, hdr.HobLength) + EfiHob.Hob.append( + (type(hob).__name__, + hdr.HobType, + hdr.HobLength, + hob, + extra)) + hob_ptr +=3D hdr.HobLength + except ValueError: + pass + + def _ctype_read_ex(self, ctype_struct, offset=3D0, rsize=3DNone): + if offset !=3D 0: + self._file.seek(offset) + + type_size =3D sizeof(ctype_struct) + size =3D rsize if rsize else type_size + data =3D self._file.read(size) + cdata =3D ctype_struct.from_buffer(bytearray(data)) + + if size > type_size: + return cdata, data[type_size:] + else: + return cdata, None + + +class EFI_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Type', c_uint8), + ('SubType', c_uint8), + + # UINT8 Length[2] + # Cheat and use c_uint16 since we don't care about alignment + ('Length', c_uint16) + ] + + +class PCI_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('Function', c_uint8), + ('Device', c_uint8) + ] + + +class PCCARD_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('FunctionNumber', c_uint8), + ] + + +class MEMMAP_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('StartingAddress', c_uint64), + ('EndingAddress', c_uint64), + ] + + +class VENDOR_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('Guid', EFI_GUID), + ] + + +class CONTROLLER_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('ControllerNumber', c_uint32), + ] + + +class BMC_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('InterfaceType', c_uint8), + ('BaseAddress', ARRAY(c_uint8, 8)), + ] + + +class BBS_BBS_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('DeviceType', c_uint16), + ('StatusFlag', c_uint16) + ] + + +class ACPI_HID_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('HID', c_uint32), + ('UID', c_uint32) + ] + + +class ACPI_EXTENDED_HID_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('HID', c_uint32), + ('UID', c_uint32), + ('CID', c_uint32) + ] + + +class ACPI_ADR_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('ARD', c_uint32) + ] + + +class ACPI_NVDIMM_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('NFITDeviceHandle', c_uint32) + ] + + +class ATAPI_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("PrimarySecondary", c_uint8), + ("SlaveMaster", c_uint8), + ("Lun", c_uint16) + ] + + +class SCSI_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Pun", c_uint16), + ("Lun", c_uint16) + ] + + +class FIBRECHANNEL_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Reserved", c_uint32), + ("WWN", c_uint64), + ("Lun", c_uint64) + ] + + +class F1394_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Reserved", c_uint32), + ("Guid", c_uint64) + ] + + +class USB_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("ParentPortNumber", c_uint8), + ("InterfaceNumber", c_uint8), + ] + + +class I2O_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Tid", c_uint32) + ] + + +class INFINIBAND_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("ResourceFlags", c_uint32), + ("PortGid", ARRAY(c_uint8, 16)), + ("ServiceId", c_uint64), + ("TargetPortId", c_uint64), + ("DeviceId", c_uint64) + ] + + +class UART_FLOW_CONTROL_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Guid", EFI_GUID), + ("FlowControlMap", c_uint32) + ] + + +class SAS_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Guid", EFI_GUID), + ("Reserved", c_uint32), + ("SasAddress", c_uint64), + ("Lun", c_uint64), + ("DeviceTopology", c_uint16), + ("RelativeTargetPort", c_uint16) + ] + + +class EFI_MAC_ADDRESS(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ("Addr", ARRAY(c_uint8, 32)), + ] + + +class MAC_ADDR_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('MacAddress', EFI_MAC_ADDRESS), + ('IfType', c_uint8) + ] + + +class IPv4_ADDRESS(LittleEndianStructure): + _fields_ =3D [ + ("Addr", ARRAY(c_uint8, 4)), + ] + + +class IPv4_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('LocalIpAddress', IPv4_ADDRESS), + ('RemoteIpAddress', IPv4_ADDRESS), + ('LocalPort', c_uint16), + ('RemotePort', c_uint16), + ('Protocol', c_uint16), + ('StaticIpAddress', c_uint8), + ('GatewayIpAddress', IPv4_ADDRESS), + ('SubnetMask', IPv4_ADDRESS) + ] + + +class IPv6_ADDRESS(LittleEndianStructure): + _fields_ =3D [ + ("Addr", ARRAY(c_uint8, 16)), + ] + + +class IPv6_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('LocalIpAddress', IPv6_ADDRESS), + ('RemoteIpAddress', IPv6_ADDRESS), + ('LocalPort', c_uint16), + ('RemotePort', c_uint16), + ('Protocol', c_uint16), + ('IpAddressOrigin', c_uint8), + ('PrefixLength', c_uint8), + ('GatewayIpAddress', IPv6_ADDRESS) + ] + + +class UART_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('Reserved', c_uint32), + ('BaudRate', c_uint64), + ('DataBits', c_uint8), + ('Parity', c_uint8), + ('StopBits', c_uint8) + ] + + +class USB_CLASS_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('VendorId', c_uint16), + ('ProductId', c_uint16), + ('DeviceClass', c_uint8), + ('DeviceCSjblass', c_uint8), + ('DeviceProtocol', c_uint8), + ] + + +class USB_WWID_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('InterfaceNumber', c_uint16), + ('VendorId', c_uint16), + ('ProductId', c_uint16), + ] + + +class DEVICE_LOGICAL_UNIT_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('Lun', c_uint8) + ] + + +class SATA_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('HBAPortNumber', c_uint16), + ('PortMultiplierPortNumber', c_uint16), + ('Lun', c_uint16), + ] + + +class ISCSI_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('NetworkProtocol', c_uint16), + ('LoginOption', c_uint16), + ('Lun', c_uint64), + ('TargetPortalGroupTag', c_uint16), + ] + + +class VLAN_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("VlandId", c_uint16) + ] + + +class FIBRECHANNELEX_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Reserved", c_uint16), + ("WWN", ARRAY(c_uint8, 8)), + ("Lun", ARRAY(c_uint8, 8)), + ] + + +class SASEX_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("SasAddress", ARRAY(c_uint8, 8)), + ("Lun", ARRAY(c_uint8, 8)), + ("DeviceTopology", c_uint16), + ("RelativeTargetPort", c_uint16) + ] + + +class NVME_NAMESPACE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("NamespaceId", c_uint32), + ("NamespaceUuid", c_uint64) + ] + + +class DNS_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("IsIPv6", c_uint8), + ("DnsServerIp", IPv6_ADDRESS) + + ] + + +class UFS_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Pun", c_uint8), + ("Lun", c_uint8), + ] + + +class SD_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("SlotNumber", c_uint8) + ] + + +class BLUETOOTH_ADDRESS(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ("Address", ARRAY(c_uint8, 6)) + ] + + +class BLUETOOTH_LE_ADDRESS(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ("Format", c_uint8), + ("Class", c_uint16) + ] + + +class BLUETOOTH_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("BD_ADDR", BLUETOOTH_ADDRESS) + ] + + +class WIFI_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("SSId", ARRAY(c_uint8, 32)) + ] + + +class EMMC_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("SlotNumber", c_uint8) + ] + + +class BLUETOOTH_LE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("BD_ADDR", BLUETOOTH_LE_ADDRESS) + ] + + +class NVDIMM_NAMESPACE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("Uuid", EFI_GUID) + ] + + +class REST_SERVICE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("RESTService", c_uint8), + ("AccessMode", c_uint8) + ] + + +class REST_VENDOR_SERVICE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ("RESTService", c_uint8), + ("AccessMode", c_uint8), + ("Guid", EFI_GUID), + ] + + +class HARDDRIVE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('PartitionNumber', c_uint32), + ('PartitionStart', c_uint64), + ('PartitionSize', c_uint64), + ('Signature', ARRAY(c_uint8, 16)), + ('MBRType', c_uint8), + ('SignatureType', c_uint8) + ] + + +class CDROM_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('BootEntry', c_uint32), + ('PartitionStart', c_uint64), + ('PartitionSize', c_uint64) + ] + + +class MEDIA_PROTOCOL_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('Protocol', EFI_GUID) + ] + + +class MEDIA_FW_VOL_FILEPATH_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('FvFileName', EFI_GUID) + ] + + +class MEDIA_FW_VOL_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('FvName', EFI_GUID) + ] + + +class MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('Reserved', c_uint32), + ('StartingOffset', c_uint64), + ('EndingOffset', c_uint64) + ] + + +class MEDIA_RAM_DISK_DEVICE_PATH(LittleEndianStructure): + _pack_ =3D 1 + _fields_ =3D [ + ('Header', EFI_DEVICE_PATH), + ('StartingAddr', c_uint64), + ('EndingAddr', c_uint64), + ('TypeGuid', EFI_GUID), + ('Instance', c_uint16) + ] + + +class EfiDevicePath: + ''' + Parse EFI Device Paths based on the edk2 C Structures defined above. + In the context of this class verbose means hexdump extra data. + + + Attributes + =E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94 + DevicePath : list + List of devixe path instances. Each instance is a list of nodes + for the given Device Path instance. + + Methods + ----------- + device_path_node(address) + return the Device Path ctype hdr, ctype, and any extra data in + the Device Path node. This is just a single Device Path node, + not the entire Device Path. + device_path_node_str(address) + return the device path node (not the entire Device Path) as a st= ring + ''' + + DevicePath =3D [] + + device_path_dict =3D { + # ( Type, SubType ) : Device Path C typedef + # HARDWARE_DEVICE_PATH + (1, 1): PCI_DEVICE_PATH, + (1, 2): PCCARD_DEVICE_PATH, + (1, 3): MEMMAP_DEVICE_PATH, + (1, 4): VENDOR_DEVICE_PATH, + (1, 5): CONTROLLER_DEVICE_PATH, + (1, 6): BMC_DEVICE_PATH, + + # ACPI_DEVICE_PATH + (2, 1): ACPI_HID_DEVICE_PATH, + (2, 2): ACPI_EXTENDED_HID_DEVICE_PATH, + (2, 3): ACPI_ADR_DEVICE_PATH, + (2, 4): ACPI_NVDIMM_DEVICE_PATH, + + # MESSAGING_DEVICE_PATH + (3, 1): ATAPI_DEVICE_PATH, + (3, 2): SCSI_DEVICE_PATH, + (3, 3): FIBRECHANNEL_DEVICE_PATH, + (3, 4): F1394_DEVICE_PATH, + (3, 5): USB_DEVICE_PATH, + (3, 6): I2O_DEVICE_PATH, + + (3, 9): INFINIBAND_DEVICE_PATH, + (3, 10): VENDOR_DEVICE_PATH, + (3, 11): MAC_ADDR_DEVICE_PATH, + (3, 12): IPv4_DEVICE_PATH, + (3, 13): IPv6_DEVICE_PATH, + (3, 14): UART_DEVICE_PATH, + (3, 15): USB_CLASS_DEVICE_PATH, + (3, 16): USB_WWID_DEVICE_PATH, + (3, 17): DEVICE_LOGICAL_UNIT_DEVICE_PATH, + (3, 18): SATA_DEVICE_PATH, + (3, 19): ISCSI_DEVICE_PATH, + (3, 20): VLAN_DEVICE_PATH, + (3, 21): FIBRECHANNELEX_DEVICE_PATH, + (3, 22): SASEX_DEVICE_PATH, + (3, 23): NVME_NAMESPACE_DEVICE_PATH, + (3, 24): DNS_DEVICE_PATH, + (3, 25): UFS_DEVICE_PATH, + (3, 26): SD_DEVICE_PATH, + (3, 27): BLUETOOTH_DEVICE_PATH, + (3, 28): WIFI_DEVICE_PATH, + (3, 29): EMMC_DEVICE_PATH, + (3, 30): BLUETOOTH_LE_DEVICE_PATH, + (3, 31): DNS_DEVICE_PATH, + (3, 32): NVDIMM_NAMESPACE_DEVICE_PATH, + + (3, 33): REST_SERVICE_DEVICE_PATH, + (3, 34): REST_VENDOR_SERVICE_DEVICE_PATH, + + # MEDIA_DEVICE_PATH + (4, 1): HARDDRIVE_DEVICE_PATH, + (4, 2): CDROM_DEVICE_PATH, + (4, 3): VENDOR_DEVICE_PATH, + (4, 4): EFI_DEVICE_PATH, + (4, 5): MEDIA_PROTOCOL_DEVICE_PATH, + (4, 6): MEDIA_FW_VOL_FILEPATH_DEVICE_PATH, + (4, 7): MEDIA_FW_VOL_DEVICE_PATH, + (4, 8): MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH, + (4, 9): MEDIA_RAM_DISK_DEVICE_PATH, + + # BBS_DEVICE_PATH + (5, 1): BBS_BBS_DEVICE_PATH, + + } + + guid_override_dict =3D { + uuid.UUID('37499A9D-542F-4C89-A026-35DA142094E4'): + UART_FLOW_CONTROL_DEVICE_PATH, + uuid.UUID('D487DDB4-008B-11D9-AFDC-001083FFCA4D'): + SAS_DEVICE_PATH, + } + + def __init__(self, file, ptr=3DNone, verbose=3DFalse, count=3D64): + ''' + Convert ptr into a list of Device Path nodes. If verbose also he= xdump + extra data. + ''' + self._file =3D file + self._verbose =3D verbose + if ptr is None: + return + + try: + instance =3D [] + for _ in range(count): # while True + hdr, _ =3D self._ctype_read_ex(EFI_DEVICE_PATH, ptr) + if hdr.Length < sizeof(EFI_DEVICE_PATH): + # Not a valid device path + break + + if hdr.Type =3D=3D 0x7F: # END_DEVICE_PATH_TYPE + self.DevicePath.append(instance) + if hdr.SubType =3D=3D 0xFF: # END_ENTIRE_DEVICE_PAT= H_SUBTYPE + break + if hdr.SubType =3D=3D 0x01: # END_INSTANCE_DEVICE_P= ATH_SUBTYPE + # start new device path instance + instance =3D [] + + type_str =3D self.device_path_dict.get( + (hdr.Type, hdr.SubType), EFI_DEVICE_PATH) + node, extra =3D self._ctype_read_ex(type_str, ptr, hdr.L= ength) + if 'VENDOR_DEVICE_PATH' in type(node).__name__: + guid_type =3D self.guid_override_dict.get( + GuidNames.to_uuid(node.Guid), No= ne) + if guid_type: + # use the ctype associated with the GUID + node, extra =3D self._ctype_read_ex( + guid_type, ptr, hdr.Leng= th) + + instance.append((type(node).__name__, hdr.Type, + hdr.SubType, hdr.Length, node, extra)) + ptr +=3D hdr.Length + except ValueError: + pass + + def __str__(self): + ''' ''' + if not self.valid(): + return '' + + result =3D "" + for instance in self.DevicePath: + for (Name, Type, SubType, Length, cnode, extra) in instance: + result +=3D f'{Name:s} {Type:2d}:{SubType:2d} Len: {Leng= th:3d}\n' + result +=3D ctype_to_str(cnode, ' ', ['Reserved']) + if self._verbose: + if extra is not None: + result +=3D hexdump(extra, ' ') + result +=3D '\n' + + return result + + def valid(self): + return True if self.DevicePath else False + + def device_path_node(self, address): + try: + hdr, _ =3D self._ctype_read_ex(EFI_DEVICE_PATH, address) + if hdr.Length < sizeof(EFI_DEVICE_PATH): + return None, None, None + + type_str =3D self.device_path_dict.get( + (hdr.Type, hdr.SubType), EFI_DEVICE_PATH) + cnode, extra =3D self._ctype_read_ex(type_str, address, hdr.= Length) + return hdr, cnode, extra + except ValueError: + return None, None, None + + def device_path_node_str(self, address, verbose=3DFalse): + hdr, cnode, extra =3D self.device_path_node(address) + if hdr is None: + return '' + + cname =3D type(cnode).__name__ + result =3D f'{cname:s} {hdr.Type:2d}:{hdr.SubType:2d} ' + result +=3D f'Len: 0x{hdr.Length:03x}\n' + result +=3D ctype_to_str(cnode, ' ', ['Reserved']) + if verbose: + if extra is not None: + result +=3D hexdump(extra, ' ') + + return result + + def _ctype_read_ex(self, ctype_struct, offset=3D0, rsize=3DNone): + if offset !=3D 0: + self._file.seek(offset) + + type_size =3D sizeof(ctype_struct) + size =3D rsize if rsize else type_size + data =3D self._file.read(size) + if data is None: + return None, None + + cdata =3D ctype_struct.from_buffer(bytearray(data)) + + if size > type_size: + return cdata, data[type_size:] + else: + return cdata, None + + +class EfiConfigurationTable: + ''' + A class to abstract EFI Configuration Tables from gST->Configuration= Table + and gST->NumberOfTableEntries. Pass in the gST pointer from EFI, + likely you need to look up this address after you have loaded symbol= s + + Attributes + =E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94 + ConfigurationTableDict : dictionary + dictionary of EFI Configuration Table entries + + Methods + ----------- + GetConfigTable(uuid) + pass in VendorGuid and return VendorTable from EFI System Table + DebugImageInfo(table) + return tuple of load address and size of PE/COFF images + ''' + + ConfigurationTableDict =3D {} + + def __init__(self, file, gST_addr=3DNone): + self._file =3D file + if gST_addr is None: + # ToDo add code to search for gST via EFI_SYSTEM_TABLE_POINT= ER + return + + gST =3D self._ctype_read(EFI_SYSTEM_TABLE, gST_addr) + self.read_efi_config_table(gST.NumberOfTableEntries, + gST.ConfigurationTable, + self._ctype_read) + + @ classmethod + def __str__(cls): + '''return EFI_CONFIGURATION_TABLE entries as a string''' + result =3D "" + for key, value in cls.ConfigurationTableDict.items(): + result +=3D f'{GuidNames().to_name(key):>37s}: ' + result +=3D f'VendorTable =3D 0x{value:08x}\n' + + return result + + def _ctype_read(self, ctype_struct, offset=3D0): + '''ctype worker function to read data''' + if offset !=3D 0: + self._file.seek(offset) + + data =3D self._file.read(sizeof(ctype_struct)) + return ctype_struct.from_buffer(bytearray(data)) + + @ classmethod + def read_efi_config_table(cls, table_cnt, table_ptr, ctype_read): + '''Create a dictionary of EFI Configuration table entries''' + EmptryTables =3D EFI_CONFIGURATION_TABLE * table_cnt + Tables =3D ctype_read(EmptryTables, table_ptr) + for i in range(table_cnt): + cls.ConfigurationTableDict[str(GuidNames.to_uuid( + Tables[i].VendorGuid)).upper()] =3D Tables[i].VendorTabl= e + + return cls.ConfigurationTableDict + + def GetConfigTable(self, uuid): + ''' Return VendorTable for VendorGuid (uuid.UUID) or None''' + return self.ConfigurationTableDict.get(uuid.upper()) + + def DebugImageInfo(self, table=3DNone): + ''' + Walk the debug image info table to find the LoadedImage protocol= s + for all the loaded PE/COFF images and return a list of load addr= ess + and image size. + ''' + ImageLoad =3D [] + + if table is None: + table =3D self.GetConfigTable('49152e77-1ada-4764-b7a2-7afef= ed95e8b') + + DbgInfoHdr =3D self._ctype_read(EFI_DEBUG_IMAGE_INFO_TABLE_HEADE= R, table) + NormalImageArray =3D EFI_DEBUG_IMAGE_INFO * DbgInfoHdr.TableSize + NormalImageArray =3D self._ctype_read( + NormalImageArray, DbgInfoHdr.EfiDebugImageInfoTable) + for i in range(DbgInfoHdr.TableSize): + ImageInfo =3D self._ctype_read( + EFI_DEBUG_IMAGE_INFO_NORMAL, NormalImageArray[i].NormalI= mage) + LoadedImage =3D self._ctype_read( + EFI_LOADED_IMAGE_PROTOCOL, + ImageInfo.LoadedImageProtocolInstance) + ImageLoad.append((LoadedImage.ImageBase, LoadedImage.ImageSi= ze)) + + return ImageLoad + + +class PeTeImage: + ''' + A class to abstract PE/COFF or TE image processing via passing in a + Python file like object. If you pass in an address the PE/COFF is pa= rsed, + if you pass in NULL for an address then you get a class instance you= can + use to search memory for a PE/COFF hader given a pc value. + + Attributes + =E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94 + LoadAddress : int + Load address of the PE/COFF image + AddressOfEntryPoint : int + Address of the Entry point of the PE/COFF image + TextAddress : int + Start of the PE/COFF text section + DataAddress : int + Start of the PE/COFF data section + CodeViewPdb : str + File name of the symbols file + CodeViewUuid : uuid:UUID + GUID for "RSDS" Debug Directory entry, or Mach-O UUID for "MTOC" + + Methods + ----------- + pcToPeCoff(address, step, max_range, rom_range) + Given an address(pc) find the PE/COFF image it is in + sections_to_str() + return a string giving info for all the PE/COFF sections + ''' + + def __init__(self, file, address=3D0): + self._file =3D file + + # book keeping, but public + self.PeHdr =3D None + self.TeHdr =3D None + self.Machine =3D None + self.Subsystem =3D None + self.CodeViewSig =3D None + self.e_lfanew =3D 0 + self.NumberOfSections =3D 0 + self.Sections =3D None + + # Things debuggers may want to know + self.LoadAddress =3D 0 if address is None else address + self.EndLoadAddress =3D 0 + self.AddressOfEntryPoint =3D 0 + self.TextAddress =3D 0 + self.DataAddress =3D 0 + self.CodeViewPdb =3D None + self.CodeViewUuid =3D None + self.TeAdjust =3D 0 + + self.dir_name =3D { + 0: 'Export Table', + 1: 'Import Table', + 2: 'Resource Table', + 3: 'Exception Table', + 4: 'Certificate Table', + 5: 'Relocation Table', + 6: 'Debug', + 7: 'Architecture', + 8: 'Global Ptr', + 9: 'TLS Table', + 10: 'Load Config Table', + 11: 'Bound Import', + 12: 'IAT', + 13: 'Delay Import Descriptor', + 14: 'CLR Runtime Header', + 15: 'Reserved', + } + + if address is not None: + if self.maybe(): + self.parse() + + def __str__(self): + if self.PeHdr is None and self.TeHdr is None: + # no PE/COFF header found + return "" + + if self.CodeViewPdb: + pdb =3D f'{self.Machine}`{self.CodeViewPdb}' + else: + pdb =3D 'No Debug Info:' + + if self.CodeViewUuid: + guid =3D f'{self.CodeViewUuid}:' + else: + guid =3D '' + + slide =3D f'slide =3D {self.TeAdjust:d} ' if self.TeAdjust !=3D = 0 else ' ' + res =3D guid + f'{pdb} load =3D 0x{self.LoadAddress:08x} ' + sli= de + return res + + def _seek(self, offset): + """ + seek() relative to start of PE/COFF (TE) image + """ + self._file.seek(self.LoadAddress + offset) + + def _read_offset(self, size, offset=3DNone): + """ + read() relative to start of PE/COFF (TE) image + if offset is not None then seek() before the read + """ + if offset is not None: + self._seek(offset) + + return self._file.read(size) + + def _read_ctype(self, ctype_struct, offset=3DNone): + data =3D self._read_offset(sizeof(ctype_struct), offset) + return ctype_struct.from_buffer(bytearray(data), 0) + + def _unsigned(self, i): + """return a 32-bit unsigned int (UINT32) """ + return int.from_bytes(i, byteorder=3D'little', signed=3DFalse) + + def pcToPeCoff(self, + address, + step=3DNone, + max_range=3DNone, + rom_range=3D[0xFE800000, 0xFFFFFFFF]): + """ + Given an address search backwards for PE/COFF (TE) header + For DXE 4K is probably OK + For PEI you might have to search every 4 bytes. + """ + if step is None: + step =3D 0x1000 + + if max_range is None: + max_range =3D 0x200000 + + if address in range(*rom_range): + # The XIP code in the ROM ends up 4 byte aligned. + step =3D 4 + max_range =3D min(max_range, 0x100000) + + # Align address to page boundary for memory image search. + address =3D address & ~(step-1) + # Search every step backward + offset_range =3D list(range(0, min(max_range, address), step)) + for offset in offset_range: + if self.maybe(address - offset): + if self.parse(): + return True + + return False + + def maybe(self, offset=3DNone): + """Probe to see if this offset is likely a PE/COFF or TE file ""= " + self.LoadAddress =3D 0 + e_magic =3D self._read_offset(2, offset) + header_ok =3D e_magic =3D=3D b'MZ' or e_magic =3D=3D b'VZ' + if offset is not None and header_ok: + self.LoadAddress =3D offset + return header_ok + + def parse(self): + """Parse PE/COFF (TE) debug directory entry """ + DosHdr =3D self._read_ctype(EFI_IMAGE_DOS_HEADER, 0) + if DosHdr.e_magic =3D=3D self._unsigned(b'VZ'): + # TE image + self.TeHdr =3D self._read_ctype(EFI_TE_IMAGE_HEADER, 0) + + self.TeAdjust =3D sizeof(self.TeHdr) - self.TeHdr.StrippedSi= ze + self.Machine =3D image_machine_dict.get(self.TeHdr.Machine, = None) + self.Subsystem =3D self.TeHdr.Subsystem + self.AddressOfEntryPoint =3D self.TeHdr.AddressOfEntryPoint + + debug_dir_size =3D self.TeHdr.DataDirectoryDebug.Size + debug_dir_offset =3D (self.TeAdjust + + self.TeHdr.DataDirectoryDebug.VirtualAdd= ress) + else: + if DosHdr.e_magic =3D=3D self._unsigned(b'MZ'): + self.e_lfanew =3D DosHdr.e_lfanew + else: + self.e_lfanew =3D 0 + + self.PeHdr =3D self._read_ctype( + EFI_IMAGE_NT_HEADERS64, self.e_lfanew) + if self.PeHdr.Signature !=3D self._unsigned(b'PE\0\0'): + return False + + if self.PeHdr.OptionalHeader.Magic =3D=3D \ + EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: + self.PeHdr =3D self._read_ctype( + EFI_IMAGE_NT_HEADERS32, self.e_lfanew) + + if self.PeHdr.OptionalHeader.NumberOfRvaAndSizes <=3D \ + DIRECTORY_DEBUG: + return False + + self.Machine =3D image_machine_dict.get( + self.PeHdr.FileHeader.Machine, None) + self.Subsystem =3D self.PeHdr.OptionalHeader.Subsystem + self.AddressOfEntryPoint =3D \ + self.PeHdr.OptionalHeader.AddressOfEntryPoint + self.TeAdjust =3D 0 + + debug_dir_size =3D self.PeHdr.OptionalHeader.DataDirectory[ + DIRECTORY_DEBUG].Size + debug_dir_offset =3D self.PeHdr.OptionalHeader.DataDirectory= [ + DIRECTORY_DEBUG].VirtualAddress + + if self.Machine is None or self.Subsystem not in [0, 10, 11, 12]= : + return False + + self.AddressOfEntryPoint +=3D self.LoadAddress + + self.sections() + return self.processDebugDirEntry(debug_dir_offset, debug_dir_siz= e) + + def sections(self): + '''Parse the PE/COFF (TE) section table''' + if self.Sections is not None: + return + elif self.TeHdr is not None: + self.NumberOfSections =3D self.TeHdr.NumberOfSections + offset =3D sizeof(EFI_TE_IMAGE_HEADER) + elif self.PeHdr is not None: + self.NumberOfSections =3D self.PeHdr.FileHeader.NumberOfSect= ions + offset =3D sizeof(c_uint32) + \ + sizeof(EFI_IMAGE_FILE_HEADER) + offset +=3D self.PeHdr.FileHeader.SizeOfOptionalHeader + offset +=3D self.e_lfanew + else: + return + + self.Sections =3D EFI_IMAGE_SECTION_HEADER * self.NumberOfSectio= ns + self.Sections =3D self._read_ctype(self.Sections, offset) + + for i in range(self.NumberOfSections): + name =3D str(self.Sections[i].Name, 'ascii', 'ignore') + addr =3D self.Sections[i].VirtualAddress + addr +=3D self.LoadAddress + self.TeAdjust + if name =3D=3D '.text': + self.TextAddress =3D addr + elif name =3D=3D '.data': + self.DataAddress =3D addr + + end_addr =3D addr + self.Sections[i].VirtualSize - 1 + if end_addr > self.EndLoadAddress: + self.EndLoadAddress =3D end_addr + + def sections_to_str(self): + # return text summary of sections + # name virt addr (virt size) flags:Characteristics + result =3D '' + for i in range(self.NumberOfSections): + name =3D str(self.Sections[i].Name, 'ascii', 'ignore') + result +=3D f'{name:8s} ' + result +=3D f'0x{self.Sections[i].VirtualAddress:08X} ' + result +=3D f'(0x{self.Sections[i].VirtualSize:05X}) ' + result +=3D f'flags:0x{self.Sections[i].Characteristics:08X}= \n' + + return result + + def directory_to_str(self, indent=3D''): + result =3D '' + if self.TeHdr: + debug_size =3D self.TeHdr.DataDirectoryDebug.Size + if debug_size > 0: + debug_offset =3D (self.TeAdjust + + self.TeHdr.DataDirectoryDebug.VirtualA= ddress) + result +=3D f'{indent:s}' + result +=3D f'Debug 0x{debug_offset:08X} 0x{debug_size}\= n' + + relocation_size =3D self.TeHdr.DataDirectoryBaseReloc.Size + if relocation_size > 0: + relocation_offset =3D ( + self.TeAdjust + + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress) + result +=3D f'{indent:s}Relocation 0x{relocation_offset:= 08X} ' + result +=3D f' 0x{relocation_size}\n' + + elif self.PeHdr: + for i in range(self.PeHdr.OptionalHeader.NumberOfRvaAndSizes= ): + size =3D self.PeHdr.OptionalHeader.DataDirectory[i].Size + if size =3D=3D 0: + continue + + virt_addr =3D self.PeHdr.OptionalHeader.DataDirectory[ + i].VirtualAddress + name =3D self.dir_name.get(i, '?') + result +=3D f'{indent:s}' + result +=3D f'{name:16s}: 0x{virt_addr:08X} 0x{size:X}\n= ' + + return result + + def processDebugDirEntry(self, virt_address, virt_size): + """Process PE/COFF Debug Directory Entry""" + if (virt_address =3D=3D 0 or + virt_size < sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)): + return False + + data =3D bytearray(self._read_offset(virt_size, virt_address)) + for offset in range(0, + virt_size, + sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)): + DirectoryEntry =3D EFI_IMAGE_DEBUG_DIRECTORY_ENTRY.from_buff= er( + data[offset:]) + if DirectoryEntry.Type !=3D 2: + continue + + entry =3D self._read_offset( + DirectoryEntry.SizeOfData, DirectoryEntry.RVA + self.TeA= djust) + self.CodeViewSig =3D entry[:4] + if self.CodeViewSig =3D=3D b'MTOC': + self.CodeViewUuid =3D uuid.UUID(bytes_le=3Dentry[4:4+16]= ) + PdbOffset =3D 20 + elif self.CodeViewSig =3D=3D b'RSDS': + self.CodeViewUuid =3D uuid.UUID(bytes_le=3Dentry[4:4+16]= ) + PdbOffset =3D 24 + elif self.CodeViewSig =3D=3D b'NB10': + PdbOffset =3D 16 + else: + continue + + # can't find documentation about Pdb string encoding? + # guessing utf-8 since that will match file systems in macOS + # and Linux Windows is UTF-16, or ANSI adjusted for local. + # We might need a different value for Windows here? + self.CodeViewPdb =3D entry[PdbOffset:].split(b'\x00')[ + 0].decode('utf-8') + return True + return False + + +def main(): + '''Process arguments as PE/COFF files''' + for fname in sys.argv[1:]: + with open(fname, 'rb') as f: + image =3D PeTeImage(f) + print(image) + res =3D f'EntryPoint =3D 0x{image.AddressOfEntryPoint:08x} = ' + res +=3D f'TextAddress =3D 0x{image.TextAddress:08x} ' + res +=3D f'DataAddress =3D 0x{image.DataAddress:08x}' + print(res) + print(image.sections_to_str()) + print('Data Directories:') + print(image.directory_to_str(' ')) + + +if __name__ =3D=3D "__main__": + main() --=20 2.30.1 (Apple Git-130)