1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185 |
- #!/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 debugger
- 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`<path...>/DxeCore.dll load = 0x00000000
- EntryPoint = 0x000030d2 TextAddress = 0x00000240 DataAddress = 0x000042c0
- .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 GuidNames)
- 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 use 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 the
- # debugger python to make sure you are in sync.
- #
- # Splitting out the _field_ from the Structure (LittleEndianStructure) class
- # allows it to be patched.
- #
- class EFI_LOADED_IMAGE_PROTOCOL(LittleEndianStructure):
- pass
- EFI_LOADED_IMAGE_PROTOCOL_fields_ = [
- ('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_ = [
- ('Data1', c_uint32),
- ('Data2', c_uint16),
- ('Data3', c_uint16),
- ('Data4', ARRAY(c_uint8, 8))
- ]
- class EFI_SYSTEM_TABLE_POINTER(LittleEndianStructure):
- _fields_ = [
- ('Signature', c_uint64),
- ('EfiSystemTableBase', c_uint64),
- ('Crc32', c_uint32)
- ]
- class EFI_DEBUG_IMAGE_INFO_NORMAL(LittleEndianStructure):
- pass
- EFI_DEBUG_IMAGE_INFO_NORMAL_fields_ = [
- ('ImageInfoType', c_uint32),
- ('LoadedImageProtocolInstance', c_void_p),
- ('ImageHandle', c_void_p)
- ]
- class EFI_DEBUG_IMAGE_INFO(LittleEndianStructure):
- pass
- EFI_DEBUG_IMAGE_INFO_fields_ = [
- ('NormalImage', c_void_p),
- ]
- class EFI_DEBUG_IMAGE_INFO_TABLE_HEADER(LittleEndianStructure):
- pass
- EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_ = [
- ('UpdateStatus', c_uint32),
- ('TableSize', c_uint32),
- ('EfiDebugImageInfoTable', c_void_p),
- ]
- class EFI_TABLE_HEADER(LittleEndianStructure):
- _fields_ = [
- ('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_ = [
- ('VendorGuid', EFI_GUID),
- ('VendorTable', c_void_p)
- ]
- class EFI_SYSTEM_TABLE(LittleEndianStructure):
- pass
- EFI_SYSTEM_TABLE_fields_ = [
- ('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_ = [
- ('VirtualAddress', c_uint32),
- ('Size', c_uint32)
- ]
- class EFI_TE_IMAGE_HEADER(LittleEndianStructure):
- _fields_ = [
- ('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_ = [
- ('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_ = [
- ('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_ = [
- ('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_ = [
- ('Signature', c_uint32),
- ('FileHeader', EFI_IMAGE_FILE_HEADER),
- ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER32)
- ]
- class EFI_IMAGE_OPTIONAL_HEADER64(LittleEndianStructure):
- _fields_ = [
- ('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_ = [
- ('Signature', c_uint32),
- ('FileHeader', EFI_IMAGE_FILE_HEADER),
- ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER64)
- ]
- class EFI_IMAGE_DEBUG_DIRECTORY_ENTRY(LittleEndianStructure):
- _fields_ = [
- ('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_ = [
- ('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 = 0x10b
- EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
- DIRECTORY_DEBUG = 6
- image_machine_dict = {
- 0x014c: "IA32",
- 0x0200: "IPF",
- 0x0EBC: "EBC",
- 0x8664: "X64",
- 0x01c2: "ARM",
- 0xAA64: "AArch64",
- 0x5032: "RISC32",
- 0x5064: "RISC64",
- 0x5128: "RISCV128",
- }
- 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 = []
- for name, c_type in to_patch:
- if type(c_type) == type(c_void_p):
- result.append((name, c_uint32))
- else:
- result.append((name, c_type))
- return result
- def patch_ctypes(pointer_width=8):
- '''
- 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) == pointer_width:
- patch_type = None
- elif pointer_width == 16:
- assert False
- elif pointer_width == 8:
- patch_type = c_uint64
- elif pointer_width == 4:
- patch_type = c_uint32
- else:
- raise Exception(f'ERROR: Unkown pointer_width = {pointer_width}')
- # If you add a ctypes Structure class with a c_void_p you need to add
- # it to this list. Note: you should use c_void_p for UINTN values.
- EFI_LOADED_IMAGE_PROTOCOL._fields_ = patch_void_p_to_ctype(
- patch_type, EFI_LOADED_IMAGE_PROTOCOL_fields_)
- EFI_DEBUG_IMAGE_INFO_NORMAL._fields_ = patch_void_p_to_ctype(
- patch_type, EFI_DEBUG_IMAGE_INFO_NORMAL_fields_)
- EFI_DEBUG_IMAGE_INFO._fields_ = patch_void_p_to_ctype(
- patch_type, EFI_DEBUG_IMAGE_INFO_fields_)
- EFI_DEBUG_IMAGE_INFO_TABLE_HEADER._fields_ = patch_void_p_to_ctype(
- patch_type, EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_)
- EFI_CONFIGURATION_TABLE._fields_ = patch_void_p_to_ctype(
- patch_type, EFI_CONFIGURATION_TABLE_fields_)
- EFI_SYSTEM_TABLE._fields_ = 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='', hide_list=[]):
- '''
- Given a ctype object print out as a string by walking the _fields_
- in the cstring Class
- '''
- result = ''
- for field in ctype._fields_:
- attr = getattr(ctype, field[0])
- tname = type(attr).__name__
- if field[0] in hide_list:
- continue
- result += indent + f'{field[0]} = '
- if tname == 'EFI_GUID':
- result += GuidNames.to_name(GuidNames.to_uuid(attr)) + '\n'
- elif issubclass(type(attr), Structure):
- result += f'{tname}\n' + \
- ctype_to_str(attr, indent + ' ', hide_list)
- elif isinstance(attr, int):
- result += f'0x{attr:x}\n'
- else:
- result += f'{attr}\n'
- return result
- def hexline(addr, data):
- hexstr = ''
- printable = ''
- for i in range(0, len(data)):
- hexstr += f'{data[i]:02x} '
- printable += chr(data[i]) if data[i] > 0x20 and data[i] < 0x7f else '.'
- return f'{addr:04x} {hexstr:48s} |{printable:s}|'
- def hexdump(data, indent=''):
- if not isinstance(data, bytearray):
- data = bytearray(data)
- result = ''
- for i in range(0, len(data), 16):
- result += indent + hexline(i, data[i:i+16]) + '\n'
- return result
- class EfiTpl:
- ''' Return string for EFI_TPL'''
- def __init__(self, tpl):
- self.tpl = tpl
- def __str__(self):
- if self.tpl < 4:
- result = f'{self.tpl:d}'
- elif self.tpl < 8:
- result = "TPL_APPLICATION"
- if self.tpl - 4 > 0:
- result += f' + {self.tpl - 4:d}'
- elif self.tpl < 16:
- result = "TPL_CALLBACK"
- if self.tpl - 8 > 0:
- result += f' + {self.tpl - 8:d}'
- elif self.tpl < 31:
- result = "TPL_NOTIFY"
- if self.tpl - 16 > 0:
- result += f' + {self.tpl - 16:d}'
- elif self.tpl == 31:
- result = "TPL_HIGH_LEVEL"
- else:
- result = f'Invalid TPL = {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 = {
- 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 = boot_mode
- def __str__(self):
- return self.to_str(self._boot_mode)
- @classmethod
- def to_str(cls, boot_mode, default=''):
- 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) == 8.
- Attributes
- ??????
- _dict_ : dictionary
- dictionary of EFI_STATUS that has beed updated to match
- pointer_width.
- Methods
- -----------
- patch_dictionary(pointer_width)
- to_str(status, default)
- '''
- _dict_ = {}
- _EFI_STATUS_UINT32_dict = {
- 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=None, pointer_width=8):
- self.status = 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=''):
- 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 == 4:
- cls._dict = cls._EFI_STATUS_UINT32_dict
- elif pointer_width == 8:
- for key, value in cls._EFI_STATUS_UINT32_dict.items():
- mask = (key & 0xE0000000) << 32
- new_key = (key & 0x1FFFFFFF) | mask
- cls._dict_[new_key] = 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 with 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
- ??????----
- _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.xref
- 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_file.
- '''
- _dict_ = { # 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 = []
- def __init__(self, uuid=None, pointer_width=8):
- self.uuid = None if uuid is None else self.to_uuid(uuid)
- def __str__(self):
- if self.uuid is None:
- result = ''
- for key, value in GuidNames._dict_.items():
- result += f'{key}: {value}\n'
- else:
- result = self.to_name(self.uuid)
- return result
- @classmethod
- def to_uuid(cls, obj):
- try:
- return uuid.UUID(bytes_le=bytes(obj))
- except (ValueError, TypeError):
- try:
- return uuid.UUID(bytes_le=obj)
- except (ValueError, TypeError):
- return uuid.UUID(obj)
- @classmethod
- def to_name(cls, uuid):
- if not isinstance(uuid, str):
- uuid = str(uuid)
- if cls.is_c_guid(uuid):
- uuid = 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 == 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('-') >= 4
- @classmethod
- def to_c_guid(cls, value):
- if isinstance(value, uuid.UUID):
- guid = value
- else:
- guid = uuid.UUID(value)
- (data1, data2, data3,
- data4_0, data4_1, data4_2, data4_3,
- data4_4, data4_5, data4_6, data4_7) = struct.unpack(
- '<IHH8B', guid.bytes_le)
- return (f'{{ 0x{data1:08X}, 0x{data2:04X}, 0x{data3:04X}, '
- f'{{ 0x{data4_0:02X}, 0x{data4_1:02X}, 0x{data4_2:02X}, '
- f'0x{data4_3:02X}, 0x{data4_4:02X}, 0x{data4_5:02X}, '
- f'0x{data4_6:02X}, 0x{data4_7:02X} }} }}')
- @ classmethod
- def from_c_guid(cls, value):
- try:
- hex = [int(x, 16) for x in re.findall(r"[\w']+", value)]
- return (f'{hex[0]:08X}-{hex[1]:04X}-{hex[2]:04X}'
- + f'-{hex[3]:02X}{hex[4]:02X}-{hex[5]:02X}{hex[6]:02X}'
- + f'{hex[7]:02X}{hex[8]:02X}{hex[9]:02X}{hex[10]:02X}')
- except ValueError:
- return value
- @ classmethod
- def is_c_guid(cls, name):
- if not isinstance(name, str):
- return False
- return name.count('{') == 2 and name.count('}') == 2
- @ classmethod
- def add_build_guid_file(cls, module_path, custom_file=None):
- if custom_file is not None:
- xref = custom_file
- else:
- # module_path will look like:
- # <repo>/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 = os.path.join(
- module_path.rsplit(i, 1)[0], i)
- break
- xref = 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 = 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 = lines.split()
- cls._dict_[words[0].upper()] = words[1].strip('\n')
- except ValueError:
- pass
- return True
- return False
- class EFI_HOB_GENERIC_HEADER(LittleEndianStructure):
- _fields_ = [
- ('HobType', c_uint16),
- ('HobLength', c_uint16),
- ('Reserved', c_uint32)
- ]
- class EFI_HOB_HANDOFF_INFO_TABLE(LittleEndianStructure):
- _fields_ = [
- ('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_ = [
- ('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_ = [
- ('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_ = [
- ('Header', EFI_HOB_GENERIC_HEADER),
- ('Name', EFI_GUID),
- ]
- class EFI_HOB_FIRMWARE_VOLUME(LittleEndianStructure):
- _fields_ = [
- ('Header', EFI_HOB_GENERIC_HEADER),
- ('BaseAddress', c_uint64),
- ('Length', c_uint64),
- ]
- class EFI_HOB_CPU(LittleEndianStructure):
- _fields_ = [
- ('Header', EFI_HOB_GENERIC_HEADER),
- ('SizeOfMemorySpace', c_uint8),
- ('SizeOfIoSpace', c_uint8),
- ('Reserved', ARRAY(c_uint8, 6)),
- ]
- class EFI_HOB_MEMORY_POOL(LittleEndianStructure):
- _fields_ = [
- ('Header', EFI_HOB_GENERIC_HEADER),
- ]
- class EFI_HOB_FIRMWARE_VOLUME2(LittleEndianStructure):
- _fields_ = [
- ('Header', EFI_HOB_GENERIC_HEADER),
- ('BaseAddress', c_uint64),
- ('Length', c_uint64),
- ('FvName', EFI_GUID),
- ('FileName', EFI_GUID)
- ]
- class EFI_HOB_FIRMWARE_VOLUME3(LittleEndianStructure):
- _fields_ = [
- ('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_ = [
- ('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
- ??????
- 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 = []
- verbose = False
- hob_dict = {
- 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=None, verbose=False, count=1000):
- self._file = file
- EfiHob.verbose = verbose
- if len(EfiHob.Hob) != 0 and address is None:
- return
- if address is not None:
- hob_ptr = address
- else:
- hob_ptr = 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 = ""
- for (Name, HobType, HobLen, chob, extra) in cls.Hob:
- if hob_type is not None:
- if hob_type != HobType:
- continue
- result += f'Type: {Name:s} (0x{HobType:01x}) Len: 0x{HobLen:03x}\n'
- result += ctype_to_str(chob, ' ', ['Reserved'])
- if cls.verbose:
- if extra is not None:
- result += hexdump(extra, ' ')
- return result
- def read_hobs(self, hob_ptr, count=1000):
- if hob_ptr is None:
- return
- try:
- for _ in range(count): # while True
- hdr, _ = self._ctype_read_ex(EFI_HOB_GENERIC_HEADER, hob_ptr)
- if hdr.HobType == 0xffff:
- break
- type_str = self.hob_dict.get(
- hdr.HobType, EFI_HOB_GENERIC_HEADER)
- hob, extra = self._ctype_read_ex(
- type_str, hob_ptr, hdr.HobLength)
- EfiHob.Hob.append(
- (type(hob).__name__,
- hdr.HobType,
- hdr.HobLength,
- hob,
- extra))
- hob_ptr += hdr.HobLength
- except ValueError:
- pass
- def _ctype_read_ex(self, ctype_struct, offset=0, rsize=None):
- if offset != 0:
- self._file.seek(offset)
- type_size = sizeof(ctype_struct)
- size = rsize if rsize else type_size
- data = self._file.read(size)
- cdata = 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_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('Function', c_uint8),
- ('Device', c_uint8)
- ]
- class PCCARD_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('FunctionNumber', c_uint8),
- ]
- class MEMMAP_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('StartingAddress', c_uint64),
- ('EndingAddress', c_uint64),
- ]
- class VENDOR_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('Guid', EFI_GUID),
- ]
- class CONTROLLER_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('ControllerNumber', c_uint32),
- ]
- class BMC_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('InterfaceType', c_uint8),
- ('BaseAddress', ARRAY(c_uint8, 8)),
- ]
- class BBS_BBS_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('DeviceType', c_uint16),
- ('StatusFlag', c_uint16)
- ]
- class ACPI_HID_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('HID', c_uint32),
- ('UID', c_uint32)
- ]
- class ACPI_EXTENDED_HID_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('HID', c_uint32),
- ('UID', c_uint32),
- ('CID', c_uint32)
- ]
- class ACPI_ADR_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('ARD', c_uint32)
- ]
- class ACPI_NVDIMM_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('NFITDeviceHandle', c_uint32)
- ]
- class ATAPI_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("PrimarySecondary", c_uint8),
- ("SlaveMaster", c_uint8),
- ("Lun", c_uint16)
- ]
- class SCSI_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Pun", c_uint16),
- ("Lun", c_uint16)
- ]
- class FIBRECHANNEL_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Reserved", c_uint32),
- ("WWN", c_uint64),
- ("Lun", c_uint64)
- ]
- class F1394_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Reserved", c_uint32),
- ("Guid", c_uint64)
- ]
- class USB_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("ParentPortNumber", c_uint8),
- ("InterfaceNumber", c_uint8),
- ]
- class I2O_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Tid", c_uint32)
- ]
- class INFINIBAND_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Guid", EFI_GUID),
- ("FlowControlMap", c_uint32)
- ]
- class SAS_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ("Addr", ARRAY(c_uint8, 32)),
- ]
- class MAC_ADDR_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('MacAddress', EFI_MAC_ADDRESS),
- ('IfType', c_uint8)
- ]
- class IPv4_ADDRESS(LittleEndianStructure):
- _fields_ = [
- ("Addr", ARRAY(c_uint8, 4)),
- ]
- class IPv4_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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_ = [
- ("Addr", ARRAY(c_uint8, 16)),
- ]
- class IPv6_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('InterfaceNumber', c_uint16),
- ('VendorId', c_uint16),
- ('ProductId', c_uint16),
- ]
- class DEVICE_LOGICAL_UNIT_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('Lun', c_uint8)
- ]
- class SATA_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('HBAPortNumber', c_uint16),
- ('PortMultiplierPortNumber', c_uint16),
- ('Lun', c_uint16),
- ]
- class ISCSI_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('NetworkProtocol', c_uint16),
- ('LoginOption', c_uint16),
- ('Lun', c_uint64),
- ('TargetPortalGroupTag', c_uint16),
- ]
- class VLAN_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("VlandId", c_uint16)
- ]
- class FIBRECHANNELEX_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Reserved", c_uint16),
- ("WWN", ARRAY(c_uint8, 8)),
- ("Lun", ARRAY(c_uint8, 8)),
- ]
- class SASEX_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("NamespaceId", c_uint32),
- ("NamespaceUuid", c_uint64)
- ]
- class DNS_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("IsIPv6", c_uint8),
- ("DnsServerIp", IPv6_ADDRESS)
- ]
- class UFS_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Pun", c_uint8),
- ("Lun", c_uint8),
- ]
- class SD_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("SlotNumber", c_uint8)
- ]
- class BLUETOOTH_ADDRESS(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ("Address", ARRAY(c_uint8, 6))
- ]
- class BLUETOOTH_LE_ADDRESS(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ("Format", c_uint8),
- ("Class", c_uint16)
- ]
- class BLUETOOTH_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("BD_ADDR", BLUETOOTH_ADDRESS)
- ]
- class WIFI_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("SSId", ARRAY(c_uint8, 32))
- ]
- class EMMC_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("SlotNumber", c_uint8)
- ]
- class BLUETOOTH_LE_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("BD_ADDR", BLUETOOTH_LE_ADDRESS)
- ]
- class NVDIMM_NAMESPACE_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("Uuid", EFI_GUID)
- ]
- class REST_SERVICE_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("RESTService", c_uint8),
- ("AccessMode", c_uint8)
- ]
- class REST_VENDOR_SERVICE_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ("RESTService", c_uint8),
- ("AccessMode", c_uint8),
- ("Guid", EFI_GUID),
- ]
- class HARDDRIVE_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('BootEntry', c_uint32),
- ('PartitionStart', c_uint64),
- ('PartitionSize', c_uint64)
- ]
- class MEDIA_PROTOCOL_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('Protocol', EFI_GUID)
- ]
- class MEDIA_FW_VOL_FILEPATH_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('FvFileName', EFI_GUID)
- ]
- class MEDIA_FW_VOL_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('FvName', EFI_GUID)
- ]
- class MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('Header', EFI_DEVICE_PATH),
- ('Reserved', c_uint32),
- ('StartingOffset', c_uint64),
- ('EndingOffset', c_uint64)
- ]
- class MEDIA_RAM_DISK_DEVICE_PATH(LittleEndianStructure):
- _pack_ = 1
- _fields_ = [
- ('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
- ??????
- 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 string
- '''
- DevicePath = []
- device_path_dict = {
- # ( 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 = {
- 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=None, verbose=False, count=64):
- '''
- Convert ptr into a list of Device Path nodes. If verbose also hexdump
- extra data.
- '''
- self._file = file
- self._verbose = verbose
- if ptr is None:
- return
- try:
- instance = []
- for _ in range(count): # while True
- hdr, _ = self._ctype_read_ex(EFI_DEVICE_PATH, ptr)
- if hdr.Length < sizeof(EFI_DEVICE_PATH):
- # Not a valid device path
- break
- if hdr.Type == 0x7F: # END_DEVICE_PATH_TYPE
- self.DevicePath.append(instance)
- if hdr.SubType == 0xFF: # END_ENTIRE_DEVICE_PATH_SUBTYPE
- break
- if hdr.SubType == 0x01: # END_INSTANCE_DEVICE_PATH_SUBTYPE
- # start new device path instance
- instance = []
- type_str = self.device_path_dict.get(
- (hdr.Type, hdr.SubType), EFI_DEVICE_PATH)
- node, extra = self._ctype_read_ex(type_str, ptr, hdr.Length)
- if 'VENDOR_DEVICE_PATH' in type(node).__name__:
- guid_type = self.guid_override_dict.get(
- GuidNames.to_uuid(node.Guid), None)
- if guid_type:
- # use the ctype associated with the GUID
- node, extra = self._ctype_read_ex(
- guid_type, ptr, hdr.Length)
- instance.append((type(node).__name__, hdr.Type,
- hdr.SubType, hdr.Length, node, extra))
- ptr += hdr.Length
- except ValueError:
- pass
- def __str__(self):
- ''' '''
- if not self.valid():
- return '<class: EfiDevicePath>'
- result = ""
- for instance in self.DevicePath:
- for (Name, Type, SubType, Length, cnode, extra) in instance:
- result += f'{Name:s} {Type:2d}:{SubType:2d} Len: {Length:3d}\n'
- result += ctype_to_str(cnode, ' ', ['Reserved'])
- if self._verbose:
- if extra is not None:
- result += hexdump(extra, ' ')
- result += '\n'
- return result
- def valid(self):
- return True if self.DevicePath else False
- def device_path_node(self, address):
- try:
- hdr, _ = self._ctype_read_ex(EFI_DEVICE_PATH, address)
- if hdr.Length < sizeof(EFI_DEVICE_PATH):
- return None, None, None
- type_str = self.device_path_dict.get(
- (hdr.Type, hdr.SubType), EFI_DEVICE_PATH)
- cnode, extra = 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=False):
- hdr, cnode, extra = self.device_path_node(address)
- if hdr is None:
- return ''
- cname = type(cnode).__name__
- result = f'{cname:s} {hdr.Type:2d}:{hdr.SubType:2d} '
- result += f'Len: 0x{hdr.Length:03x}\n'
- result += ctype_to_str(cnode, ' ', ['Reserved'])
- if verbose:
- if extra is not None:
- result += hexdump(extra, ' ')
- return result
- def _ctype_read_ex(self, ctype_struct, offset=0, rsize=None):
- if offset != 0:
- self._file.seek(offset)
- type_size = sizeof(ctype_struct)
- size = rsize if rsize else type_size
- data = self._file.read(size)
- if data is None:
- return None, None
- cdata = 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->ConfigurationTable
- and gST->NumberOfTableEntries. Pass in the gST pointer from EFI,
- likely you need to look up this address after you have loaded symbols
- Attributes
- ??????
- 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 = {}
- def __init__(self, file, gST_addr=None):
- self._file = file
- if gST_addr is None:
- # ToDo add code to search for gST via EFI_SYSTEM_TABLE_POINTER
- return
- gST = 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 = ""
- for key, value in cls.ConfigurationTableDict.items():
- result += f'{GuidNames().to_name(key):>37s}: '
- result += f'VendorTable = 0x{value:08x}\n'
- return result
- def _ctype_read(self, ctype_struct, offset=0):
- '''ctype worker function to read data'''
- if offset != 0:
- self._file.seek(offset)
- data = 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 = EFI_CONFIGURATION_TABLE * table_cnt
- Tables = ctype_read(EmptryTables, table_ptr)
- for i in range(table_cnt):
- cls.ConfigurationTableDict[str(GuidNames.to_uuid(
- Tables[i].VendorGuid)).upper()] = Tables[i].VendorTable
- 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=None):
- '''
- Walk the debug image info table to find the LoadedImage protocols
- for all the loaded PE/COFF images and return a list of load address
- and image size.
- '''
- ImageLoad = []
- if table is None:
- table = self.GetConfigTable('49152e77-1ada-4764-b7a2-7afefed95e8b')
- DbgInfoHdr = self._ctype_read(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER, table)
- NormalImageArray = EFI_DEBUG_IMAGE_INFO * DbgInfoHdr.TableSize
- NormalImageArray = self._ctype_read(
- NormalImageArray, DbgInfoHdr.EfiDebugImageInfoTable)
- for i in range(DbgInfoHdr.TableSize):
- ImageInfo = self._ctype_read(
- EFI_DEBUG_IMAGE_INFO_NORMAL, NormalImageArray[i].NormalImage)
- LoadedImage = self._ctype_read(
- EFI_LOADED_IMAGE_PROTOCOL,
- ImageInfo.LoadedImageProtocolInstance)
- ImageLoad.append((LoadedImage.ImageBase, LoadedImage.ImageSize))
- 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 parsed,
- 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
- ??????
- 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=0):
- self._file = file
- # book keeping, but public
- self.PeHdr = None
- self.TeHdr = None
- self.Machine = None
- self.Subsystem = None
- self.CodeViewSig = None
- self.e_lfanew = 0
- self.NumberOfSections = 0
- self.Sections = None
- # Things debuggers may want to know
- self.LoadAddress = 0 if address is None else address
- self.EndLoadAddress = 0
- self.AddressOfEntryPoint = 0
- self.TextAddress = 0
- self.DataAddress = 0
- self.CodeViewPdb = None
- self.CodeViewUuid = None
- self.TeAdjust = 0
- self.dir_name = {
- 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 "<class: PeTeImage>"
- if self.CodeViewPdb:
- pdb = f'{self.Machine}`{self.CodeViewPdb}'
- else:
- pdb = 'No Debug Info:'
- if self.CodeViewUuid:
- guid = f'{self.CodeViewUuid}:'
- else:
- guid = ''
- slide = f'slide = {self.TeAdjust:d} ' if self.TeAdjust != 0 else ' '
- res = guid + f'{pdb} load = 0x{self.LoadAddress:08x} ' + slide
- 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=None):
- """
- 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=None):
- data = 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='little', signed=False)
- def pcToPeCoff(self,
- address,
- step=None,
- max_range=None,
- rom_range=[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 = 0x1000
- if max_range is None:
- max_range = 0x200000
- if address in range(*rom_range):
- # The XIP code in the ROM ends up 4 byte aligned.
- step = 4
- max_range = min(max_range, 0x100000)
- # Align address to page boundary for memory image search.
- address = address & ~(step-1)
- # Search every step backward
- offset_range = 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=None):
- """Probe to see if this offset is likely a PE/COFF or TE file """
- self.LoadAddress = 0
- e_magic = self._read_offset(2, offset)
- header_ok = e_magic == b'MZ' or e_magic == b'VZ'
- if offset is not None and header_ok:
- self.LoadAddress = offset
- return header_ok
- def parse(self):
- """Parse PE/COFF (TE) debug directory entry """
- DosHdr = self._read_ctype(EFI_IMAGE_DOS_HEADER, 0)
- if DosHdr.e_magic == self._unsigned(b'VZ'):
- # TE image
- self.TeHdr = self._read_ctype(EFI_TE_IMAGE_HEADER, 0)
- self.TeAdjust = sizeof(self.TeHdr) - self.TeHdr.StrippedSize
- self.Machine = image_machine_dict.get(self.TeHdr.Machine, None)
- self.Subsystem = self.TeHdr.Subsystem
- self.AddressOfEntryPoint = self.TeHdr.AddressOfEntryPoint
- debug_dir_size = self.TeHdr.DataDirectoryDebug.Size
- debug_dir_offset = (self.TeAdjust +
- self.TeHdr.DataDirectoryDebug.VirtualAddress)
- else:
- if DosHdr.e_magic == self._unsigned(b'MZ'):
- self.e_lfanew = DosHdr.e_lfanew
- else:
- self.e_lfanew = 0
- self.PeHdr = self._read_ctype(
- EFI_IMAGE_NT_HEADERS64, self.e_lfanew)
- if self.PeHdr.Signature != self._unsigned(b'PE\0\0'):
- return False
- if self.PeHdr.OptionalHeader.Magic == \
- EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
- self.PeHdr = self._read_ctype(
- EFI_IMAGE_NT_HEADERS32, self.e_lfanew)
- if self.PeHdr.OptionalHeader.NumberOfRvaAndSizes <= \
- DIRECTORY_DEBUG:
- return False
- self.Machine = image_machine_dict.get(
- self.PeHdr.FileHeader.Machine, None)
- self.Subsystem = self.PeHdr.OptionalHeader.Subsystem
- self.AddressOfEntryPoint = \
- self.PeHdr.OptionalHeader.AddressOfEntryPoint
- self.TeAdjust = 0
- debug_dir_size = self.PeHdr.OptionalHeader.DataDirectory[
- DIRECTORY_DEBUG].Size
- debug_dir_offset = 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 += self.LoadAddress
- self.sections()
- return self.processDebugDirEntry(debug_dir_offset, debug_dir_size)
- 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 = self.TeHdr.NumberOfSections
- offset = sizeof(EFI_TE_IMAGE_HEADER)
- elif self.PeHdr is not None:
- self.NumberOfSections = self.PeHdr.FileHeader.NumberOfSections
- offset = sizeof(c_uint32) + \
- sizeof(EFI_IMAGE_FILE_HEADER)
- offset += self.PeHdr.FileHeader.SizeOfOptionalHeader
- offset += self.e_lfanew
- else:
- return
- self.Sections = EFI_IMAGE_SECTION_HEADER * self.NumberOfSections
- self.Sections = self._read_ctype(self.Sections, offset)
- for i in range(self.NumberOfSections):
- name = str(self.Sections[i].Name, 'ascii', 'ignore')
- addr = self.Sections[i].VirtualAddress
- addr += self.LoadAddress + self.TeAdjust
- if name == '.text':
- self.TextAddress = addr
- elif name == '.data':
- self.DataAddress = addr
- end_addr = addr + self.Sections[i].VirtualSize - 1
- if end_addr > self.EndLoadAddress:
- self.EndLoadAddress = end_addr
- def sections_to_str(self):
- # return text summary of sections
- # name virt addr (virt size) flags:Characteristics
- result = ''
- for i in range(self.NumberOfSections):
- name = str(self.Sections[i].Name, 'ascii', 'ignore')
- result += f'{name:8s} '
- result += f'0x{self.Sections[i].VirtualAddress:08X} '
- result += f'(0x{self.Sections[i].VirtualSize:05X}) '
- result += f'flags:0x{self.Sections[i].Characteristics:08X}\n'
- return result
- def directory_to_str(self):
- result = ''
- if self.TeHdr:
- debug_size = self.TeHdr.DataDirectoryDebug.Size
- if debug_size > 0:
- debug_offset = (self.TeAdjust
- + self.TeHdr.DataDirectoryDebug.VirtualAddress)
- result += f"Debug 0x{debug_offset:08X} 0x{debug_size}\n"
- relocation_size = self.TeHdr.DataDirectoryBaseReloc.Size
- if relocation_size > 0:
- relocation_offset = (
- self.TeAdjust
- + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress)
- result += f'Relocation 0x{relocation_offset:08X} '
- result += f' 0x{relocation_size}\n'
- elif self.PeHdr:
- for i in range(self.PeHdr.OptionalHeader.NumberOfRvaAndSizes):
- size = self.PeHdr.OptionalHeader.DataDirectory[i].Size
- if size == 0:
- continue
- virt_addr = self.PeHdr.OptionalHeader.DataDirectory[
- i].VirtualAddress
- name = self.dir_name.get(i, '?')
- result += f'{name:s} 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 == 0 or
- virt_size < sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)):
- return False
- data = bytearray(self._read_offset(virt_size, virt_address))
- for offset in range(0,
- virt_size,
- sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)):
- DirectoryEntry = EFI_IMAGE_DEBUG_DIRECTORY_ENTRY.from_buffer(
- data[offset:])
- if DirectoryEntry.Type != 2:
- continue
- entry = self._read_offset(
- DirectoryEntry.SizeOfData, DirectoryEntry.RVA + self.TeAdjust)
- self.CodeViewSig = entry[:4]
- if self.CodeViewSig == b'MTOC':
- self.CodeViewUuid = uuid.UUID(bytes_le=entry[4:4+16])
- PdbOffset = 20
- elif self.CodeViewSig == b'RSDS':
- self.CodeViewUuid = uuid.UUID(bytes_le=entry[4:4+16])
- PdbOffset = 24
- elif self.CodeViewSig == b'NB10':
- PdbOffset = 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 = 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 = PeTeImage(f)
- print(image)
- res = f'EntryPoint = 0x{image.AddressOfEntryPoint:08x} '
- res += f'TextAddress = 0x{image.TextAddress:08x} '
- res += f'DataAddress = 0x{image.DataAddress:08x}'
- print(res)
- print(image.sections_to_str())
- print('Data Directories:')
- print(image.directory_to_str())
- if __name__ == "__main__":
- main()
|