efi_debugging.py 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185
  1. #!/usr/bin/python3
  2. '''
  3. Copyright (c) Apple Inc. 2021
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. Class that abstracts PE/COFF debug info parsing via a Python file like
  6. object. You can port this code into an arbitrary debugger by invoking
  7. the classes and passing in a file like object that abstracts the debugger
  8. reading memory.
  9. If you run this file directly it will parse the passed in PE/COFF files
  10. for debug info:
  11. python3 ./efi_pefcoff.py DxeCore.efi
  12. IA32`<path...>/DxeCore.dll load = 0x00000000
  13. EntryPoint = 0x000030d2 TextAddress = 0x00000240 DataAddress = 0x000042c0
  14. .text 0x00000240 (0x04080) flags:0x60000020
  15. .data 0x000042C0 (0x001C0) flags:0xC0000040
  16. .reloc 0x00004480 (0x00240) flags:0x42000040
  17. Note: PeCoffClass uses virtual addresses and not file offsets.
  18. It needs to work when images are loaded into memory.
  19. as long as virtual address map to file addresses this
  20. code can process binary files.
  21. Note: This file can also contain generic worker functions (like GuidNames)
  22. that abstract debugger agnostic services to the debugger.
  23. This file should never import debugger specific modules.
  24. '''
  25. import sys
  26. import os
  27. import uuid
  28. import struct
  29. import re
  30. from ctypes import c_char, c_uint8, c_uint16, c_uint32, c_uint64, c_void_p
  31. from ctypes import ARRAY, sizeof
  32. from ctypes import Structure, LittleEndianStructure
  33. #
  34. # The empty LittleEndianStructure must have _fields_ assigned prior to use or
  35. # sizeof(). Anything that is size UINTN may need to get adjusted.
  36. #
  37. # The issue is ctypes matches our local machine, not the machine we are
  38. # trying to debug. Call patch_ctypes() passing in the byte width from the
  39. # debugger python to make sure you are in sync.
  40. #
  41. # Splitting out the _field_ from the Structure (LittleEndianStructure) class
  42. # allows it to be patched.
  43. #
  44. class EFI_LOADED_IMAGE_PROTOCOL(LittleEndianStructure):
  45. pass
  46. EFI_LOADED_IMAGE_PROTOCOL_fields_ = [
  47. ('Revision', c_uint32),
  48. ('ParentHandle', c_void_p),
  49. ('SystemTable', c_void_p),
  50. ('DeviceHandle', c_void_p),
  51. ('FilePath', c_void_p),
  52. ('Reserved', c_void_p),
  53. ('LoadOptionsSize', c_uint32),
  54. ('LoadOptions', c_void_p),
  55. ('ImageBase', c_void_p),
  56. ('ImageSize', c_uint64),
  57. ('ImageCodeType', c_uint32),
  58. ('ImageDataType', c_uint32),
  59. ('Unload', c_void_p),
  60. ]
  61. class EFI_GUID(LittleEndianStructure):
  62. _fields_ = [
  63. ('Data1', c_uint32),
  64. ('Data2', c_uint16),
  65. ('Data3', c_uint16),
  66. ('Data4', ARRAY(c_uint8, 8))
  67. ]
  68. class EFI_SYSTEM_TABLE_POINTER(LittleEndianStructure):
  69. _fields_ = [
  70. ('Signature', c_uint64),
  71. ('EfiSystemTableBase', c_uint64),
  72. ('Crc32', c_uint32)
  73. ]
  74. class EFI_DEBUG_IMAGE_INFO_NORMAL(LittleEndianStructure):
  75. pass
  76. EFI_DEBUG_IMAGE_INFO_NORMAL_fields_ = [
  77. ('ImageInfoType', c_uint32),
  78. ('LoadedImageProtocolInstance', c_void_p),
  79. ('ImageHandle', c_void_p)
  80. ]
  81. class EFI_DEBUG_IMAGE_INFO(LittleEndianStructure):
  82. pass
  83. EFI_DEBUG_IMAGE_INFO_fields_ = [
  84. ('NormalImage', c_void_p),
  85. ]
  86. class EFI_DEBUG_IMAGE_INFO_TABLE_HEADER(LittleEndianStructure):
  87. pass
  88. EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_ = [
  89. ('UpdateStatus', c_uint32),
  90. ('TableSize', c_uint32),
  91. ('EfiDebugImageInfoTable', c_void_p),
  92. ]
  93. class EFI_TABLE_HEADER(LittleEndianStructure):
  94. _fields_ = [
  95. ('Signature', c_uint64),
  96. ('Revision', c_uint32),
  97. ('HeaderSize', c_uint32),
  98. ('CRC32', c_uint32),
  99. ('Reserved', c_uint32),
  100. ]
  101. class EFI_CONFIGURATION_TABLE(LittleEndianStructure):
  102. pass
  103. EFI_CONFIGURATION_TABLE_fields_ = [
  104. ('VendorGuid', EFI_GUID),
  105. ('VendorTable', c_void_p)
  106. ]
  107. class EFI_SYSTEM_TABLE(LittleEndianStructure):
  108. pass
  109. EFI_SYSTEM_TABLE_fields_ = [
  110. ('Hdr', EFI_TABLE_HEADER),
  111. ('FirmwareVendor', c_void_p),
  112. ('FirmwareRevision', c_uint32),
  113. ('ConsoleInHandle', c_void_p),
  114. ('ConIn', c_void_p),
  115. ('ConsoleOutHandle', c_void_p),
  116. ('ConOut', c_void_p),
  117. ('StandardErrHandle', c_void_p),
  118. ('StdErr', c_void_p),
  119. ('RuntimeService', c_void_p),
  120. ('BootService', c_void_p),
  121. ('NumberOfTableEntries', c_void_p),
  122. ('ConfigurationTable', c_void_p),
  123. ]
  124. class EFI_IMAGE_DATA_DIRECTORY(LittleEndianStructure):
  125. _fields_ = [
  126. ('VirtualAddress', c_uint32),
  127. ('Size', c_uint32)
  128. ]
  129. class EFI_TE_IMAGE_HEADER(LittleEndianStructure):
  130. _fields_ = [
  131. ('Signature', ARRAY(c_char, 2)),
  132. ('Machine', c_uint16),
  133. ('NumberOfSections', c_uint8),
  134. ('Subsystem', c_uint8),
  135. ('StrippedSize', c_uint16),
  136. ('AddressOfEntryPoint', c_uint32),
  137. ('BaseOfCode', c_uint32),
  138. ('ImageBase', c_uint64),
  139. ('DataDirectoryBaseReloc', EFI_IMAGE_DATA_DIRECTORY),
  140. ('DataDirectoryDebug', EFI_IMAGE_DATA_DIRECTORY)
  141. ]
  142. class EFI_IMAGE_DOS_HEADER(LittleEndianStructure):
  143. _fields_ = [
  144. ('e_magic', c_uint16),
  145. ('e_cblp', c_uint16),
  146. ('e_cp', c_uint16),
  147. ('e_crlc', c_uint16),
  148. ('e_cparhdr', c_uint16),
  149. ('e_minalloc', c_uint16),
  150. ('e_maxalloc', c_uint16),
  151. ('e_ss', c_uint16),
  152. ('e_sp', c_uint16),
  153. ('e_csum', c_uint16),
  154. ('e_ip', c_uint16),
  155. ('e_cs', c_uint16),
  156. ('e_lfarlc', c_uint16),
  157. ('e_ovno', c_uint16),
  158. ('e_res', ARRAY(c_uint16, 4)),
  159. ('e_oemid', c_uint16),
  160. ('e_oeminfo', c_uint16),
  161. ('e_res2', ARRAY(c_uint16, 10)),
  162. ('e_lfanew', c_uint16)
  163. ]
  164. class EFI_IMAGE_FILE_HEADER(LittleEndianStructure):
  165. _fields_ = [
  166. ('Machine', c_uint16),
  167. ('NumberOfSections', c_uint16),
  168. ('TimeDateStamp', c_uint32),
  169. ('PointerToSymbolTable', c_uint32),
  170. ('NumberOfSymbols', c_uint32),
  171. ('SizeOfOptionalHeader', c_uint16),
  172. ('Characteristics', c_uint16)
  173. ]
  174. class EFI_IMAGE_OPTIONAL_HEADER32(LittleEndianStructure):
  175. _fields_ = [
  176. ('Magic', c_uint16),
  177. ('MajorLinkerVersion', c_uint8),
  178. ('MinorLinkerVersion', c_uint8),
  179. ('SizeOfCode', c_uint32),
  180. ('SizeOfInitializedData', c_uint32),
  181. ('SizeOfUninitializedData', c_uint32),
  182. ('AddressOfEntryPoint', c_uint32),
  183. ('BaseOfCode', c_uint32),
  184. ('BaseOfData', c_uint32),
  185. ('ImageBase', c_uint32),
  186. ('SectionAlignment', c_uint32),
  187. ('FileAlignment', c_uint32),
  188. ('MajorOperatingSystemVersion', c_uint16),
  189. ('MinorOperatingSystemVersion', c_uint16),
  190. ('MajorImageVersion', c_uint16),
  191. ('MinorImageVersion', c_uint16),
  192. ('MajorSubsystemVersion', c_uint16),
  193. ('MinorSubsystemVersion', c_uint16),
  194. ('Win32VersionValue', c_uint32),
  195. ('SizeOfImage', c_uint32),
  196. ('SizeOfHeaders', c_uint32),
  197. ('CheckSum', c_uint32),
  198. ('Subsystem', c_uint16),
  199. ('DllCharacteristics', c_uint16),
  200. ('SizeOfStackReserve', c_uint32),
  201. ('SizeOfStackCommit', c_uint32),
  202. ('SizeOfHeapReserve', c_uint32),
  203. ('SizeOfHeapCommit', c_uint32),
  204. ('LoaderFlags', c_uint32),
  205. ('NumberOfRvaAndSizes', c_uint32),
  206. ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
  207. ]
  208. class EFI_IMAGE_NT_HEADERS32(LittleEndianStructure):
  209. _fields_ = [
  210. ('Signature', c_uint32),
  211. ('FileHeader', EFI_IMAGE_FILE_HEADER),
  212. ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER32)
  213. ]
  214. class EFI_IMAGE_OPTIONAL_HEADER64(LittleEndianStructure):
  215. _fields_ = [
  216. ('Magic', c_uint16),
  217. ('MajorLinkerVersion', c_uint8),
  218. ('MinorLinkerVersion', c_uint8),
  219. ('SizeOfCode', c_uint32),
  220. ('SizeOfInitializedData', c_uint32),
  221. ('SizeOfUninitializedData', c_uint32),
  222. ('AddressOfEntryPoint', c_uint32),
  223. ('BaseOfCode', c_uint32),
  224. ('BaseOfData', c_uint32),
  225. ('ImageBase', c_uint32),
  226. ('SectionAlignment', c_uint32),
  227. ('FileAlignment', c_uint32),
  228. ('MajorOperatingSystemVersion', c_uint16),
  229. ('MinorOperatingSystemVersion', c_uint16),
  230. ('MajorImageVersion', c_uint16),
  231. ('MinorImageVersion', c_uint16),
  232. ('MajorSubsystemVersion', c_uint16),
  233. ('MinorSubsystemVersion', c_uint16),
  234. ('Win32VersionValue', c_uint32),
  235. ('SizeOfImage', c_uint32),
  236. ('SizeOfHeaders', c_uint32),
  237. ('CheckSum', c_uint32),
  238. ('Subsystem', c_uint16),
  239. ('DllCharacteristics', c_uint16),
  240. ('SizeOfStackReserve', c_uint64),
  241. ('SizeOfStackCommit', c_uint64),
  242. ('SizeOfHeapReserve', c_uint64),
  243. ('SizeOfHeapCommit', c_uint64),
  244. ('LoaderFlags', c_uint32),
  245. ('NumberOfRvaAndSizes', c_uint32),
  246. ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
  247. ]
  248. class EFI_IMAGE_NT_HEADERS64(LittleEndianStructure):
  249. _fields_ = [
  250. ('Signature', c_uint32),
  251. ('FileHeader', EFI_IMAGE_FILE_HEADER),
  252. ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER64)
  253. ]
  254. class EFI_IMAGE_DEBUG_DIRECTORY_ENTRY(LittleEndianStructure):
  255. _fields_ = [
  256. ('Characteristics', c_uint32),
  257. ('TimeDateStamp', c_uint32),
  258. ('MajorVersion', c_uint16),
  259. ('MinorVersion', c_uint16),
  260. ('Type', c_uint32),
  261. ('SizeOfData', c_uint32),
  262. ('RVA', c_uint32),
  263. ('FileOffset', c_uint32),
  264. ]
  265. class EFI_IMAGE_SECTION_HEADER(LittleEndianStructure):
  266. _fields_ = [
  267. ('Name', ARRAY(c_char, 8)),
  268. ('VirtualSize', c_uint32),
  269. ('VirtualAddress', c_uint32),
  270. ('SizeOfRawData', c_uint32),
  271. ('PointerToRawData', c_uint32),
  272. ('PointerToRelocations', c_uint32),
  273. ('PointerToLinenumbers', c_uint32),
  274. ('NumberOfRelocations', c_uint16),
  275. ('NumberOfLinenumbers', c_uint16),
  276. ('Characteristics', c_uint32),
  277. ]
  278. EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
  279. EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
  280. DIRECTORY_DEBUG = 6
  281. image_machine_dict = {
  282. 0x014c: "IA32",
  283. 0x0200: "IPF",
  284. 0x0EBC: "EBC",
  285. 0x8664: "X64",
  286. 0x01c2: "ARM",
  287. 0xAA64: "AArch64",
  288. 0x5032: "RISC32",
  289. 0x5064: "RISC64",
  290. 0x5128: "RISCV128",
  291. }
  292. def patch_void_p_to_ctype(patch_type, to_patch):
  293. '''Optionally patch c_void_p in the Structure._fields_'''
  294. if patch_type is None:
  295. return to_patch
  296. result = []
  297. for name, c_type in to_patch:
  298. if type(c_type) == type(c_void_p):
  299. result.append((name, c_uint32))
  300. else:
  301. result.append((name, c_type))
  302. return result
  303. def patch_ctypes(pointer_width=8):
  304. '''
  305. Pass in the pointer width of the system being debugged. If it is not
  306. the same as c_void_p then patch the _fields_ with the correct type.
  307. For any ctypes Structure that has a c_void_p this function needs to be
  308. called prior to use or sizeof() to initialize _fields_.
  309. '''
  310. if sizeof(c_void_p) == pointer_width:
  311. patch_type = None
  312. elif pointer_width == 16:
  313. assert False
  314. elif pointer_width == 8:
  315. patch_type = c_uint64
  316. elif pointer_width == 4:
  317. patch_type = c_uint32
  318. else:
  319. raise Exception(f'ERROR: Unkown pointer_width = {pointer_width}')
  320. # If you add a ctypes Structure class with a c_void_p you need to add
  321. # it to this list. Note: you should use c_void_p for UINTN values.
  322. EFI_LOADED_IMAGE_PROTOCOL._fields_ = patch_void_p_to_ctype(
  323. patch_type, EFI_LOADED_IMAGE_PROTOCOL_fields_)
  324. EFI_DEBUG_IMAGE_INFO_NORMAL._fields_ = patch_void_p_to_ctype(
  325. patch_type, EFI_DEBUG_IMAGE_INFO_NORMAL_fields_)
  326. EFI_DEBUG_IMAGE_INFO._fields_ = patch_void_p_to_ctype(
  327. patch_type, EFI_DEBUG_IMAGE_INFO_fields_)
  328. EFI_DEBUG_IMAGE_INFO_TABLE_HEADER._fields_ = patch_void_p_to_ctype(
  329. patch_type, EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_)
  330. EFI_CONFIGURATION_TABLE._fields_ = patch_void_p_to_ctype(
  331. patch_type, EFI_CONFIGURATION_TABLE_fields_)
  332. EFI_SYSTEM_TABLE._fields_ = patch_void_p_to_ctype(
  333. patch_type, EFI_SYSTEM_TABLE_fields_)
  334. # patch up anything else that needs to know pointer_width
  335. EfiStatusClass(pointer_width)
  336. def ctype_to_str(ctype, indent='', hide_list=[]):
  337. '''
  338. Given a ctype object print out as a string by walking the _fields_
  339. in the cstring Class
  340. '''
  341. result = ''
  342. for field in ctype._fields_:
  343. attr = getattr(ctype, field[0])
  344. tname = type(attr).__name__
  345. if field[0] in hide_list:
  346. continue
  347. result += indent + f'{field[0]} = '
  348. if tname == 'EFI_GUID':
  349. result += GuidNames.to_name(GuidNames.to_uuid(attr)) + '\n'
  350. elif issubclass(type(attr), Structure):
  351. result += f'{tname}\n' + \
  352. ctype_to_str(attr, indent + ' ', hide_list)
  353. elif isinstance(attr, int):
  354. result += f'0x{attr:x}\n'
  355. else:
  356. result += f'{attr}\n'
  357. return result
  358. def hexline(addr, data):
  359. hexstr = ''
  360. printable = ''
  361. for i in range(0, len(data)):
  362. hexstr += f'{data[i]:02x} '
  363. printable += chr(data[i]) if data[i] > 0x20 and data[i] < 0x7f else '.'
  364. return f'{addr:04x} {hexstr:48s} |{printable:s}|'
  365. def hexdump(data, indent=''):
  366. if not isinstance(data, bytearray):
  367. data = bytearray(data)
  368. result = ''
  369. for i in range(0, len(data), 16):
  370. result += indent + hexline(i, data[i:i+16]) + '\n'
  371. return result
  372. class EfiTpl:
  373. ''' Return string for EFI_TPL'''
  374. def __init__(self, tpl):
  375. self.tpl = tpl
  376. def __str__(self):
  377. if self.tpl < 4:
  378. result = f'{self.tpl:d}'
  379. elif self.tpl < 8:
  380. result = "TPL_APPLICATION"
  381. if self.tpl - 4 > 0:
  382. result += f' + {self.tpl - 4:d}'
  383. elif self.tpl < 16:
  384. result = "TPL_CALLBACK"
  385. if self.tpl - 8 > 0:
  386. result += f' + {self.tpl - 8:d}'
  387. elif self.tpl < 31:
  388. result = "TPL_NOTIFY"
  389. if self.tpl - 16 > 0:
  390. result += f' + {self.tpl - 16:d}'
  391. elif self.tpl == 31:
  392. result = "TPL_HIGH_LEVEL"
  393. else:
  394. result = f'Invalid TPL = {self.tpl:d}'
  395. return result
  396. class EfiBootMode:
  397. '''
  398. Class to return human readable string for EFI_BOOT_MODE
  399. Methods
  400. -----------
  401. to_str(boot_mode, default)
  402. return string for boot_mode, and return default if there is not a
  403. match.
  404. '''
  405. EFI_BOOT_MODE_dict = {
  406. 0x00: "BOOT_WITH_FULL_CONFIGURATION",
  407. 0x01: "BOOT_WITH_MINIMAL_CONFIGURATION",
  408. 0x02: "BOOT_ASSUMING_NO_CONFIGURATION_CHANGES",
  409. 0x03: "BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS",
  410. 0x04: "BOOT_WITH_DEFAULT_SETTINGS",
  411. 0x05: "BOOT_ON_S4_RESUME",
  412. 0x06: "BOOT_ON_S5_RESUME",
  413. 0x07: "BOOT_WITH_MFG_MODE_SETTINGS",
  414. 0x10: "BOOT_ON_S2_RESUME",
  415. 0x11: "BOOT_ON_S3_RESUME",
  416. 0x12: "BOOT_ON_FLASH_UPDATE",
  417. 0x20: "BOOT_IN_RECOVERY_MODE",
  418. }
  419. def __init__(self, boot_mode):
  420. self._boot_mode = boot_mode
  421. def __str__(self):
  422. return self.to_str(self._boot_mode)
  423. @classmethod
  424. def to_str(cls, boot_mode, default=''):
  425. return cls.EFI_BOOT_MODE_dict.get(boot_mode, default)
  426. class EfiStatusClass:
  427. '''
  428. Class to decode EFI_STATUS to a human readable string. You need to
  429. pass in pointer_width to get the corret value since the EFI_STATUS
  430. code values are different based on the sizeof UINTN. The default is
  431. sizeof(UINTN) == 8.
  432. Attributes
  433. ??????
  434. _dict_ : dictionary
  435. dictionary of EFI_STATUS that has beed updated to match
  436. pointer_width.
  437. Methods
  438. -----------
  439. patch_dictionary(pointer_width)
  440. to_str(status, default)
  441. '''
  442. _dict_ = {}
  443. _EFI_STATUS_UINT32_dict = {
  444. 0: "Success",
  445. 1: "Warning Unknown Glyph",
  446. 2: "Warning Delete Failure",
  447. 3: "Warning Write Failure",
  448. 4: "Warning Buffer Too Small",
  449. 5: "Warning Stale Data",
  450. 6: "Warngin File System",
  451. (0x20000000 | 0): "Warning interrupt source pending",
  452. (0x20000000 | 1): "Warning interrupt source quiesced",
  453. (0x80000000 | 1): "Load Error",
  454. (0x80000000 | 2): "Invalid Parameter",
  455. (0x80000000 | 3): "Unsupported",
  456. (0x80000000 | 4): "Bad Buffer Size",
  457. (0x80000000 | 5): "Buffer Too Small",
  458. (0x80000000 | 6): "Not Ready",
  459. (0x80000000 | 7): "Device Error",
  460. (0x80000000 | 8): "Write Protected",
  461. (0x80000000 | 9): "Out of Resources",
  462. (0x80000000 | 10): "Volume Corrupt",
  463. (0x80000000 | 11): "Volume Full",
  464. (0x80000000 | 12): "No Media",
  465. (0x80000000 | 13): "Media changed",
  466. (0x80000000 | 14): "Not Found",
  467. (0x80000000 | 15): "Access Denied",
  468. (0x80000000 | 16): "No Response",
  469. (0x80000000 | 17): "No mapping",
  470. (0x80000000 | 18): "Time out",
  471. (0x80000000 | 19): "Not started",
  472. (0x80000000 | 20): "Already started",
  473. (0x80000000 | 21): "Aborted",
  474. (0x80000000 | 22): "ICMP Error",
  475. (0x80000000 | 23): "TFTP Error",
  476. (0x80000000 | 24): "Protocol Error",
  477. (0x80000000 | 25): "Incompatible Version",
  478. (0x80000000 | 26): "Security Violation",
  479. (0x80000000 | 27): "CRC Error",
  480. (0x80000000 | 28): "End of Media",
  481. (0x80000000 | 31): "End of File",
  482. (0x80000000 | 32): "Invalid Language",
  483. (0x80000000 | 33): "Compromised Data",
  484. (0x80000000 | 35): "HTTP Error",
  485. (0xA0000000 | 0): "Interrupt Pending",
  486. }
  487. def __init__(self, status=None, pointer_width=8):
  488. self.status = status
  489. # this will convert to 64-bit version if needed
  490. self.patch_dictionary(pointer_width)
  491. def __str__(self):
  492. return self.to_str(self.status)
  493. @classmethod
  494. def to_str(cls, status, default=''):
  495. return cls._dict_.get(status, default)
  496. @classmethod
  497. def patch_dictionary(cls, pointer_width):
  498. '''Patch UINTN upper bits like values '''
  499. if cls._dict_:
  500. # only patch the class variable once
  501. return False
  502. if pointer_width == 4:
  503. cls._dict = cls._EFI_STATUS_UINT32_dict
  504. elif pointer_width == 8:
  505. for key, value in cls._EFI_STATUS_UINT32_dict.items():
  506. mask = (key & 0xE0000000) << 32
  507. new_key = (key & 0x1FFFFFFF) | mask
  508. cls._dict_[new_key] = value
  509. return True
  510. else:
  511. return False
  512. class GuidNames:
  513. '''
  514. Class to expose the C names of EFI_GUID's. The _dict_ starts with
  515. common EFI System Table entry EFI_GUID's. _dict_ can get updated with the
  516. build generated Guid.xref file if a path to a module is passed
  517. into add_build_guid_file(). If symbols are loaded for any module
  518. in the build the path the build product should imply the
  519. relative location of that builds Guid.xref file.
  520. Attributes
  521. ??????----
  522. _dict_ : dictionary
  523. dictionary of EFI_GUID (uuid) strings to C global names
  524. Methods
  525. -------
  526. to_uuid(uuid)
  527. convert a hex UUID string or bytearray to a uuid.UUID
  528. to_name(uuid)
  529. convert a UUID string to a C global constant name.
  530. to_guid(guid_name)
  531. convert a C global constant EFI_GUID name to uuid hex string.
  532. is_guid_str(name)
  533. name is a hex UUID string.
  534. Example: 49152E77-1ADA-4764-B7A2-7AFEFED95E8B
  535. to_c_guid(value)
  536. convert a uuid.UUID or UUID string to a c_guid string
  537. (see is_c_guid())
  538. from_c_guid(value)
  539. covert a C guid string to a hex UUID string.
  540. is_c_guid(name)
  541. name is the C initialization value for an EFI_GUID. Example:
  542. { 0x414e6bdd, 0xe47b, 0x47cc, { 0xb2, 0x44, 0xbb, 0x61,
  543. 0x02, 0x0c, 0xf5, 0x16 }}
  544. add_build_guid_file(module_path, custom_file):
  545. assume module_path is an edk2 build product and load the Guid.xref
  546. file from that build to fill in _dict_. If you know the path and
  547. file name of a custom Guid.xref you can pass it in as custom_file.
  548. '''
  549. _dict_ = { # Common EFI System Table values
  550. '05AD34BA-6F02-4214-952E-4DA0398E2BB9':
  551. 'gEfiDxeServicesTableGuid',
  552. '7739F24C-93D7-11D4-9A3A-0090273FC14D':
  553. 'gEfiHobListGuid',
  554. '4C19049F-4137-4DD3-9C10-8B97A83FFDFA':
  555. 'gEfiMemoryTypeInformationGuid',
  556. '49152E77-1ADA-4764-B7A2-7AFEFED95E8B':
  557. 'gEfiDebugImageInfoTableGuid',
  558. '060CC026-4C0D-4DDA-8F41-595FEF00A502':
  559. 'gMemoryStatusCodeRecordGuid',
  560. 'EB9D2D31-2D88-11D3-9A16-0090273FC14D':
  561. 'gEfiSmbiosTableGuid',
  562. 'EB9D2D30-2D88-11D3-9A16-0090273FC14D':
  563. 'gEfiAcpi10TableGuid',
  564. '8868E871-E4F1-11D3-BC22-0080C73C8881':
  565. 'gEfiAcpi20TableGuid',
  566. }
  567. guid_files = []
  568. def __init__(self, uuid=None, pointer_width=8):
  569. self.uuid = None if uuid is None else self.to_uuid(uuid)
  570. def __str__(self):
  571. if self.uuid is None:
  572. result = ''
  573. for key, value in GuidNames._dict_.items():
  574. result += f'{key}: {value}\n'
  575. else:
  576. result = self.to_name(self.uuid)
  577. return result
  578. @classmethod
  579. def to_uuid(cls, obj):
  580. try:
  581. return uuid.UUID(bytes_le=bytes(obj))
  582. except (ValueError, TypeError):
  583. try:
  584. return uuid.UUID(bytes_le=obj)
  585. except (ValueError, TypeError):
  586. return uuid.UUID(obj)
  587. @classmethod
  588. def to_name(cls, uuid):
  589. if not isinstance(uuid, str):
  590. uuid = str(uuid)
  591. if cls.is_c_guid(uuid):
  592. uuid = cls.from_c_guid(uuid)
  593. return cls._dict_.get(uuid.upper(), uuid.upper())
  594. @classmethod
  595. def to_guid(cls, guid_name):
  596. for key, value in cls._dict_.items():
  597. if guid_name == value:
  598. return key.upper()
  599. else:
  600. raise KeyError(key)
  601. @classmethod
  602. def is_guid_str(cls, name):
  603. if not isinstance(name, str):
  604. return False
  605. return name.count('-') >= 4
  606. @classmethod
  607. def to_c_guid(cls, value):
  608. if isinstance(value, uuid.UUID):
  609. guid = value
  610. else:
  611. guid = uuid.UUID(value)
  612. (data1, data2, data3,
  613. data4_0, data4_1, data4_2, data4_3,
  614. data4_4, data4_5, data4_6, data4_7) = struct.unpack(
  615. '<IHH8B', guid.bytes_le)
  616. return (f'{{ 0x{data1:08X}, 0x{data2:04X}, 0x{data3:04X}, '
  617. f'{{ 0x{data4_0:02X}, 0x{data4_1:02X}, 0x{data4_2:02X}, '
  618. f'0x{data4_3:02X}, 0x{data4_4:02X}, 0x{data4_5:02X}, '
  619. f'0x{data4_6:02X}, 0x{data4_7:02X} }} }}')
  620. @ classmethod
  621. def from_c_guid(cls, value):
  622. try:
  623. hex = [int(x, 16) for x in re.findall(r"[\w']+", value)]
  624. return (f'{hex[0]:08X}-{hex[1]:04X}-{hex[2]:04X}'
  625. + f'-{hex[3]:02X}{hex[4]:02X}-{hex[5]:02X}{hex[6]:02X}'
  626. + f'{hex[7]:02X}{hex[8]:02X}{hex[9]:02X}{hex[10]:02X}')
  627. except ValueError:
  628. return value
  629. @ classmethod
  630. def is_c_guid(cls, name):
  631. if not isinstance(name, str):
  632. return False
  633. return name.count('{') == 2 and name.count('}') == 2
  634. @ classmethod
  635. def add_build_guid_file(cls, module_path, custom_file=None):
  636. if custom_file is not None:
  637. xref = custom_file
  638. else:
  639. # module_path will look like:
  640. # <repo>/Build/OvmfX64/DEBUG_XCODE5/X64/../DxeCore.dll
  641. # Walk backwards looking for a toolchain like name.
  642. # Then look for GUID database:
  643. # Build/OvmfX64//DEBUG_XCODE5/FV/Guid.xref
  644. for i in reversed(module_path.split(os.sep)):
  645. if (i.startswith('DEBUG_') or
  646. i.startswith('RELEASE_') or
  647. i.startswith('NOOPT_')):
  648. build_root = os.path.join(
  649. module_path.rsplit(i, 1)[0], i)
  650. break
  651. xref = os.path.join(build_root, 'FV', 'Guid.xref')
  652. if xref in cls.guid_files:
  653. # only processes the file one time
  654. return True
  655. with open(xref) as f:
  656. content = f.readlines()
  657. cls.guid_files.append(xref)
  658. for lines in content:
  659. try:
  660. if cls.is_guid_str(lines):
  661. # a regex would be more pedantic
  662. words = lines.split()
  663. cls._dict_[words[0].upper()] = words[1].strip('\n')
  664. except ValueError:
  665. pass
  666. return True
  667. return False
  668. class EFI_HOB_GENERIC_HEADER(LittleEndianStructure):
  669. _fields_ = [
  670. ('HobType', c_uint16),
  671. ('HobLength', c_uint16),
  672. ('Reserved', c_uint32)
  673. ]
  674. class EFI_HOB_HANDOFF_INFO_TABLE(LittleEndianStructure):
  675. _fields_ = [
  676. ('Header', EFI_HOB_GENERIC_HEADER),
  677. ('Version', c_uint32),
  678. ('BootMode', c_uint32),
  679. ('EfiMemoryTop', c_uint64),
  680. ('EfiMemoryBottom', c_uint64),
  681. ('EfiFreeMemoryTop', c_uint64),
  682. ('EfiFreeMemoryBottom', c_uint64),
  683. ('EfiEndOfHobList', c_uint64),
  684. ]
  685. class EFI_HOB_MEMORY_ALLOCATION(LittleEndianStructure):
  686. _fields_ = [
  687. ('Header', EFI_HOB_GENERIC_HEADER),
  688. ('Name', EFI_GUID),
  689. ('MemoryBaseAddress', c_uint64),
  690. ('MemoryLength', c_uint64),
  691. ('MemoryType', c_uint32),
  692. ('Reserved', c_uint32),
  693. ]
  694. class EFI_HOB_RESOURCE_DESCRIPTOR(LittleEndianStructure):
  695. _fields_ = [
  696. ('Header', EFI_HOB_GENERIC_HEADER),
  697. ('Owner', EFI_GUID),
  698. ('ResourceType', c_uint32),
  699. ('ResourceAttribute', c_uint32),
  700. ('PhysicalStart', c_uint64),
  701. ('ResourceLength', c_uint64),
  702. ]
  703. class EFI_HOB_GUID_TYPE(LittleEndianStructure):
  704. _fields_ = [
  705. ('Header', EFI_HOB_GENERIC_HEADER),
  706. ('Name', EFI_GUID),
  707. ]
  708. class EFI_HOB_FIRMWARE_VOLUME(LittleEndianStructure):
  709. _fields_ = [
  710. ('Header', EFI_HOB_GENERIC_HEADER),
  711. ('BaseAddress', c_uint64),
  712. ('Length', c_uint64),
  713. ]
  714. class EFI_HOB_CPU(LittleEndianStructure):
  715. _fields_ = [
  716. ('Header', EFI_HOB_GENERIC_HEADER),
  717. ('SizeOfMemorySpace', c_uint8),
  718. ('SizeOfIoSpace', c_uint8),
  719. ('Reserved', ARRAY(c_uint8, 6)),
  720. ]
  721. class EFI_HOB_MEMORY_POOL(LittleEndianStructure):
  722. _fields_ = [
  723. ('Header', EFI_HOB_GENERIC_HEADER),
  724. ]
  725. class EFI_HOB_FIRMWARE_VOLUME2(LittleEndianStructure):
  726. _fields_ = [
  727. ('Header', EFI_HOB_GENERIC_HEADER),
  728. ('BaseAddress', c_uint64),
  729. ('Length', c_uint64),
  730. ('FvName', EFI_GUID),
  731. ('FileName', EFI_GUID)
  732. ]
  733. class EFI_HOB_FIRMWARE_VOLUME3(LittleEndianStructure):
  734. _fields_ = [
  735. ('HobType', c_uint16),
  736. ('HobLength', c_uint16),
  737. ('Reserved', c_uint32),
  738. ('BaseAddress', c_uint64),
  739. ('Length', c_uint64),
  740. ('AuthenticationStatus', c_uint32),
  741. ('ExtractedFv', c_uint8),
  742. ('FvName', EFI_GUID),
  743. ('FileName', EFI_GUID),
  744. ]
  745. class EFI_HOB_UEFI_CAPSULE(LittleEndianStructure):
  746. _fields_ = [
  747. ('HobType', c_uint16),
  748. ('HobLength', c_uint16),
  749. ('Reserved', c_uint32),
  750. ('BaseAddress', c_uint64),
  751. ('Length', c_uint64),
  752. ]
  753. class EfiHob:
  754. '''
  755. Parse EFI Device Paths based on the edk2 C Structures defined above.
  756. In the context of this class verbose means hexdump extra data.
  757. Attributes
  758. ??????
  759. Hob : list
  760. List of HOBs. Each entry contains the name, HOB type, HOB length,
  761. the ctype struct for the HOB, and any extra data.
  762. Methods
  763. -----------
  764. get_hob_by_type(hob_type)
  765. return string that decodes the HOBs of hob_type. If hob_type is
  766. None then return all HOBs.
  767. '''
  768. Hob = []
  769. verbose = False
  770. hob_dict = {
  771. 1: EFI_HOB_HANDOFF_INFO_TABLE,
  772. 2: EFI_HOB_MEMORY_ALLOCATION,
  773. 3: EFI_HOB_RESOURCE_DESCRIPTOR,
  774. 4: EFI_HOB_GUID_TYPE,
  775. 5: EFI_HOB_FIRMWARE_VOLUME,
  776. 6: EFI_HOB_CPU,
  777. 7: EFI_HOB_MEMORY_POOL,
  778. 9: EFI_HOB_FIRMWARE_VOLUME2,
  779. 0xb: EFI_HOB_UEFI_CAPSULE,
  780. 0xc: EFI_HOB_FIRMWARE_VOLUME3,
  781. 0xffff: EFI_HOB_GENERIC_HEADER,
  782. }
  783. def __init__(self, file, address=None, verbose=False, count=1000):
  784. self._file = file
  785. EfiHob.verbose = verbose
  786. if len(EfiHob.Hob) != 0 and address is None:
  787. return
  788. if address is not None:
  789. hob_ptr = address
  790. else:
  791. hob_ptr = EfiConfigurationTable(file).GetConfigTable(
  792. '7739F24C-93D7-11D4-9A3A-0090273FC14D')
  793. self.read_hobs(hob_ptr)
  794. @ classmethod
  795. def __str__(cls):
  796. return cls.get_hob_by_type(None)
  797. @ classmethod
  798. def get_hob_by_type(cls, hob_type):
  799. result = ""
  800. for (Name, HobType, HobLen, chob, extra) in cls.Hob:
  801. if hob_type is not None:
  802. if hob_type != HobType:
  803. continue
  804. result += f'Type: {Name:s} (0x{HobType:01x}) Len: 0x{HobLen:03x}\n'
  805. result += ctype_to_str(chob, ' ', ['Reserved'])
  806. if cls.verbose:
  807. if extra is not None:
  808. result += hexdump(extra, ' ')
  809. return result
  810. def read_hobs(self, hob_ptr, count=1000):
  811. if hob_ptr is None:
  812. return
  813. try:
  814. for _ in range(count): # while True
  815. hdr, _ = self._ctype_read_ex(EFI_HOB_GENERIC_HEADER, hob_ptr)
  816. if hdr.HobType == 0xffff:
  817. break
  818. type_str = self.hob_dict.get(
  819. hdr.HobType, EFI_HOB_GENERIC_HEADER)
  820. hob, extra = self._ctype_read_ex(
  821. type_str, hob_ptr, hdr.HobLength)
  822. EfiHob.Hob.append(
  823. (type(hob).__name__,
  824. hdr.HobType,
  825. hdr.HobLength,
  826. hob,
  827. extra))
  828. hob_ptr += hdr.HobLength
  829. except ValueError:
  830. pass
  831. def _ctype_read_ex(self, ctype_struct, offset=0, rsize=None):
  832. if offset != 0:
  833. self._file.seek(offset)
  834. type_size = sizeof(ctype_struct)
  835. size = rsize if rsize else type_size
  836. data = self._file.read(size)
  837. cdata = ctype_struct.from_buffer(bytearray(data))
  838. if size > type_size:
  839. return cdata, data[type_size:]
  840. else:
  841. return cdata, None
  842. class EFI_DEVICE_PATH(LittleEndianStructure):
  843. _pack_ = 1
  844. _fields_ = [
  845. ('Type', c_uint8),
  846. ('SubType', c_uint8),
  847. # UINT8 Length[2]
  848. # Cheat and use c_uint16 since we don't care about alignment
  849. ('Length', c_uint16)
  850. ]
  851. class PCI_DEVICE_PATH(LittleEndianStructure):
  852. _pack_ = 1
  853. _fields_ = [
  854. ('Header', EFI_DEVICE_PATH),
  855. ('Function', c_uint8),
  856. ('Device', c_uint8)
  857. ]
  858. class PCCARD_DEVICE_PATH(LittleEndianStructure):
  859. _pack_ = 1
  860. _fields_ = [
  861. ('Header', EFI_DEVICE_PATH),
  862. ('FunctionNumber', c_uint8),
  863. ]
  864. class MEMMAP_DEVICE_PATH(LittleEndianStructure):
  865. _pack_ = 1
  866. _fields_ = [
  867. ('Header', EFI_DEVICE_PATH),
  868. ('StartingAddress', c_uint64),
  869. ('EndingAddress', c_uint64),
  870. ]
  871. class VENDOR_DEVICE_PATH(LittleEndianStructure):
  872. _pack_ = 1
  873. _fields_ = [
  874. ('Header', EFI_DEVICE_PATH),
  875. ('Guid', EFI_GUID),
  876. ]
  877. class CONTROLLER_DEVICE_PATH(LittleEndianStructure):
  878. _pack_ = 1
  879. _fields_ = [
  880. ('Header', EFI_DEVICE_PATH),
  881. ('ControllerNumber', c_uint32),
  882. ]
  883. class BMC_DEVICE_PATH(LittleEndianStructure):
  884. _pack_ = 1
  885. _fields_ = [
  886. ('Header', EFI_DEVICE_PATH),
  887. ('InterfaceType', c_uint8),
  888. ('BaseAddress', ARRAY(c_uint8, 8)),
  889. ]
  890. class BBS_BBS_DEVICE_PATH(LittleEndianStructure):
  891. _pack_ = 1
  892. _fields_ = [
  893. ('Header', EFI_DEVICE_PATH),
  894. ('DeviceType', c_uint16),
  895. ('StatusFlag', c_uint16)
  896. ]
  897. class ACPI_HID_DEVICE_PATH(LittleEndianStructure):
  898. _pack_ = 1
  899. _fields_ = [
  900. ('Header', EFI_DEVICE_PATH),
  901. ('HID', c_uint32),
  902. ('UID', c_uint32)
  903. ]
  904. class ACPI_EXTENDED_HID_DEVICE_PATH(LittleEndianStructure):
  905. _pack_ = 1
  906. _fields_ = [
  907. ('Header', EFI_DEVICE_PATH),
  908. ('HID', c_uint32),
  909. ('UID', c_uint32),
  910. ('CID', c_uint32)
  911. ]
  912. class ACPI_ADR_DEVICE_PATH(LittleEndianStructure):
  913. _pack_ = 1
  914. _fields_ = [
  915. ('Header', EFI_DEVICE_PATH),
  916. ('ARD', c_uint32)
  917. ]
  918. class ACPI_NVDIMM_DEVICE_PATH(LittleEndianStructure):
  919. _pack_ = 1
  920. _fields_ = [
  921. ('Header', EFI_DEVICE_PATH),
  922. ('NFITDeviceHandle', c_uint32)
  923. ]
  924. class ATAPI_DEVICE_PATH(LittleEndianStructure):
  925. _pack_ = 1
  926. _fields_ = [
  927. ('Header', EFI_DEVICE_PATH),
  928. ("PrimarySecondary", c_uint8),
  929. ("SlaveMaster", c_uint8),
  930. ("Lun", c_uint16)
  931. ]
  932. class SCSI_DEVICE_PATH(LittleEndianStructure):
  933. _pack_ = 1
  934. _fields_ = [
  935. ('Header', EFI_DEVICE_PATH),
  936. ("Pun", c_uint16),
  937. ("Lun", c_uint16)
  938. ]
  939. class FIBRECHANNEL_DEVICE_PATH(LittleEndianStructure):
  940. _pack_ = 1
  941. _fields_ = [
  942. ('Header', EFI_DEVICE_PATH),
  943. ("Reserved", c_uint32),
  944. ("WWN", c_uint64),
  945. ("Lun", c_uint64)
  946. ]
  947. class F1394_DEVICE_PATH(LittleEndianStructure):
  948. _pack_ = 1
  949. _fields_ = [
  950. ('Header', EFI_DEVICE_PATH),
  951. ("Reserved", c_uint32),
  952. ("Guid", c_uint64)
  953. ]
  954. class USB_DEVICE_PATH(LittleEndianStructure):
  955. _pack_ = 1
  956. _fields_ = [
  957. ('Header', EFI_DEVICE_PATH),
  958. ("ParentPortNumber", c_uint8),
  959. ("InterfaceNumber", c_uint8),
  960. ]
  961. class I2O_DEVICE_PATH(LittleEndianStructure):
  962. _pack_ = 1
  963. _fields_ = [
  964. ('Header', EFI_DEVICE_PATH),
  965. ("Tid", c_uint32)
  966. ]
  967. class INFINIBAND_DEVICE_PATH(LittleEndianStructure):
  968. _pack_ = 1
  969. _fields_ = [
  970. ('Header', EFI_DEVICE_PATH),
  971. ("ResourceFlags", c_uint32),
  972. ("PortGid", ARRAY(c_uint8, 16)),
  973. ("ServiceId", c_uint64),
  974. ("TargetPortId", c_uint64),
  975. ("DeviceId", c_uint64)
  976. ]
  977. class UART_FLOW_CONTROL_DEVICE_PATH(LittleEndianStructure):
  978. _pack_ = 1
  979. _fields_ = [
  980. ('Header', EFI_DEVICE_PATH),
  981. ("Guid", EFI_GUID),
  982. ("FlowControlMap", c_uint32)
  983. ]
  984. class SAS_DEVICE_PATH(LittleEndianStructure):
  985. _pack_ = 1
  986. _fields_ = [
  987. ('Header', EFI_DEVICE_PATH),
  988. ("Guid", EFI_GUID),
  989. ("Reserved", c_uint32),
  990. ("SasAddress", c_uint64),
  991. ("Lun", c_uint64),
  992. ("DeviceTopology", c_uint16),
  993. ("RelativeTargetPort", c_uint16)
  994. ]
  995. class EFI_MAC_ADDRESS(LittleEndianStructure):
  996. _pack_ = 1
  997. _fields_ = [
  998. ("Addr", ARRAY(c_uint8, 32)),
  999. ]
  1000. class MAC_ADDR_DEVICE_PATH(LittleEndianStructure):
  1001. _pack_ = 1
  1002. _fields_ = [
  1003. ('Header', EFI_DEVICE_PATH),
  1004. ('MacAddress', EFI_MAC_ADDRESS),
  1005. ('IfType', c_uint8)
  1006. ]
  1007. class IPv4_ADDRESS(LittleEndianStructure):
  1008. _fields_ = [
  1009. ("Addr", ARRAY(c_uint8, 4)),
  1010. ]
  1011. class IPv4_DEVICE_PATH(LittleEndianStructure):
  1012. _pack_ = 1
  1013. _fields_ = [
  1014. ('Header', EFI_DEVICE_PATH),
  1015. ('LocalIpAddress', IPv4_ADDRESS),
  1016. ('RemoteIpAddress', IPv4_ADDRESS),
  1017. ('LocalPort', c_uint16),
  1018. ('RemotePort', c_uint16),
  1019. ('Protocol', c_uint16),
  1020. ('StaticIpAddress', c_uint8),
  1021. ('GatewayIpAddress', IPv4_ADDRESS),
  1022. ('SubnetMask', IPv4_ADDRESS)
  1023. ]
  1024. class IPv6_ADDRESS(LittleEndianStructure):
  1025. _fields_ = [
  1026. ("Addr", ARRAY(c_uint8, 16)),
  1027. ]
  1028. class IPv6_DEVICE_PATH(LittleEndianStructure):
  1029. _pack_ = 1
  1030. _fields_ = [
  1031. ('Header', EFI_DEVICE_PATH),
  1032. ('LocalIpAddress', IPv6_ADDRESS),
  1033. ('RemoteIpAddress', IPv6_ADDRESS),
  1034. ('LocalPort', c_uint16),
  1035. ('RemotePort', c_uint16),
  1036. ('Protocol', c_uint16),
  1037. ('IpAddressOrigin', c_uint8),
  1038. ('PrefixLength', c_uint8),
  1039. ('GatewayIpAddress', IPv6_ADDRESS)
  1040. ]
  1041. class UART_DEVICE_PATH(LittleEndianStructure):
  1042. _pack_ = 1
  1043. _fields_ = [
  1044. ('Header', EFI_DEVICE_PATH),
  1045. ('Reserved', c_uint32),
  1046. ('BaudRate', c_uint64),
  1047. ('DataBits', c_uint8),
  1048. ('Parity', c_uint8),
  1049. ('StopBits', c_uint8)
  1050. ]
  1051. class USB_CLASS_DEVICE_PATH(LittleEndianStructure):
  1052. _pack_ = 1
  1053. _fields_ = [
  1054. ('Header', EFI_DEVICE_PATH),
  1055. ('VendorId', c_uint16),
  1056. ('ProductId', c_uint16),
  1057. ('DeviceClass', c_uint8),
  1058. ('DeviceCSjblass', c_uint8),
  1059. ('DeviceProtocol', c_uint8),
  1060. ]
  1061. class USB_WWID_DEVICE_PATH(LittleEndianStructure):
  1062. _pack_ = 1
  1063. _fields_ = [
  1064. ('Header', EFI_DEVICE_PATH),
  1065. ('InterfaceNumber', c_uint16),
  1066. ('VendorId', c_uint16),
  1067. ('ProductId', c_uint16),
  1068. ]
  1069. class DEVICE_LOGICAL_UNIT_DEVICE_PATH(LittleEndianStructure):
  1070. _pack_ = 1
  1071. _fields_ = [
  1072. ('Header', EFI_DEVICE_PATH),
  1073. ('Lun', c_uint8)
  1074. ]
  1075. class SATA_DEVICE_PATH(LittleEndianStructure):
  1076. _pack_ = 1
  1077. _fields_ = [
  1078. ('Header', EFI_DEVICE_PATH),
  1079. ('HBAPortNumber', c_uint16),
  1080. ('PortMultiplierPortNumber', c_uint16),
  1081. ('Lun', c_uint16),
  1082. ]
  1083. class ISCSI_DEVICE_PATH(LittleEndianStructure):
  1084. _pack_ = 1
  1085. _fields_ = [
  1086. ('Header', EFI_DEVICE_PATH),
  1087. ('NetworkProtocol', c_uint16),
  1088. ('LoginOption', c_uint16),
  1089. ('Lun', c_uint64),
  1090. ('TargetPortalGroupTag', c_uint16),
  1091. ]
  1092. class VLAN_DEVICE_PATH(LittleEndianStructure):
  1093. _pack_ = 1
  1094. _fields_ = [
  1095. ('Header', EFI_DEVICE_PATH),
  1096. ("VlandId", c_uint16)
  1097. ]
  1098. class FIBRECHANNELEX_DEVICE_PATH(LittleEndianStructure):
  1099. _pack_ = 1
  1100. _fields_ = [
  1101. ('Header', EFI_DEVICE_PATH),
  1102. ("Reserved", c_uint16),
  1103. ("WWN", ARRAY(c_uint8, 8)),
  1104. ("Lun", ARRAY(c_uint8, 8)),
  1105. ]
  1106. class SASEX_DEVICE_PATH(LittleEndianStructure):
  1107. _pack_ = 1
  1108. _fields_ = [
  1109. ('Header', EFI_DEVICE_PATH),
  1110. ("SasAddress", ARRAY(c_uint8, 8)),
  1111. ("Lun", ARRAY(c_uint8, 8)),
  1112. ("DeviceTopology", c_uint16),
  1113. ("RelativeTargetPort", c_uint16)
  1114. ]
  1115. class NVME_NAMESPACE_DEVICE_PATH(LittleEndianStructure):
  1116. _pack_ = 1
  1117. _fields_ = [
  1118. ('Header', EFI_DEVICE_PATH),
  1119. ("NamespaceId", c_uint32),
  1120. ("NamespaceUuid", c_uint64)
  1121. ]
  1122. class DNS_DEVICE_PATH(LittleEndianStructure):
  1123. _pack_ = 1
  1124. _fields_ = [
  1125. ('Header', EFI_DEVICE_PATH),
  1126. ("IsIPv6", c_uint8),
  1127. ("DnsServerIp", IPv6_ADDRESS)
  1128. ]
  1129. class UFS_DEVICE_PATH(LittleEndianStructure):
  1130. _pack_ = 1
  1131. _fields_ = [
  1132. ('Header', EFI_DEVICE_PATH),
  1133. ("Pun", c_uint8),
  1134. ("Lun", c_uint8),
  1135. ]
  1136. class SD_DEVICE_PATH(LittleEndianStructure):
  1137. _pack_ = 1
  1138. _fields_ = [
  1139. ('Header', EFI_DEVICE_PATH),
  1140. ("SlotNumber", c_uint8)
  1141. ]
  1142. class BLUETOOTH_ADDRESS(LittleEndianStructure):
  1143. _pack_ = 1
  1144. _fields_ = [
  1145. ("Address", ARRAY(c_uint8, 6))
  1146. ]
  1147. class BLUETOOTH_LE_ADDRESS(LittleEndianStructure):
  1148. _pack_ = 1
  1149. _fields_ = [
  1150. ("Format", c_uint8),
  1151. ("Class", c_uint16)
  1152. ]
  1153. class BLUETOOTH_DEVICE_PATH(LittleEndianStructure):
  1154. _pack_ = 1
  1155. _fields_ = [
  1156. ('Header', EFI_DEVICE_PATH),
  1157. ("BD_ADDR", BLUETOOTH_ADDRESS)
  1158. ]
  1159. class WIFI_DEVICE_PATH(LittleEndianStructure):
  1160. _pack_ = 1
  1161. _fields_ = [
  1162. ('Header', EFI_DEVICE_PATH),
  1163. ("SSId", ARRAY(c_uint8, 32))
  1164. ]
  1165. class EMMC_DEVICE_PATH(LittleEndianStructure):
  1166. _pack_ = 1
  1167. _fields_ = [
  1168. ('Header', EFI_DEVICE_PATH),
  1169. ("SlotNumber", c_uint8)
  1170. ]
  1171. class BLUETOOTH_LE_DEVICE_PATH(LittleEndianStructure):
  1172. _pack_ = 1
  1173. _fields_ = [
  1174. ('Header', EFI_DEVICE_PATH),
  1175. ("BD_ADDR", BLUETOOTH_LE_ADDRESS)
  1176. ]
  1177. class NVDIMM_NAMESPACE_DEVICE_PATH(LittleEndianStructure):
  1178. _pack_ = 1
  1179. _fields_ = [
  1180. ('Header', EFI_DEVICE_PATH),
  1181. ("Uuid", EFI_GUID)
  1182. ]
  1183. class REST_SERVICE_DEVICE_PATH(LittleEndianStructure):
  1184. _pack_ = 1
  1185. _fields_ = [
  1186. ('Header', EFI_DEVICE_PATH),
  1187. ("RESTService", c_uint8),
  1188. ("AccessMode", c_uint8)
  1189. ]
  1190. class REST_VENDOR_SERVICE_DEVICE_PATH(LittleEndianStructure):
  1191. _pack_ = 1
  1192. _fields_ = [
  1193. ('Header', EFI_DEVICE_PATH),
  1194. ("RESTService", c_uint8),
  1195. ("AccessMode", c_uint8),
  1196. ("Guid", EFI_GUID),
  1197. ]
  1198. class HARDDRIVE_DEVICE_PATH(LittleEndianStructure):
  1199. _pack_ = 1
  1200. _fields_ = [
  1201. ('Header', EFI_DEVICE_PATH),
  1202. ('PartitionNumber', c_uint32),
  1203. ('PartitionStart', c_uint64),
  1204. ('PartitionSize', c_uint64),
  1205. ('Signature', ARRAY(c_uint8, 16)),
  1206. ('MBRType', c_uint8),
  1207. ('SignatureType', c_uint8)
  1208. ]
  1209. class CDROM_DEVICE_PATH(LittleEndianStructure):
  1210. _pack_ = 1
  1211. _fields_ = [
  1212. ('Header', EFI_DEVICE_PATH),
  1213. ('BootEntry', c_uint32),
  1214. ('PartitionStart', c_uint64),
  1215. ('PartitionSize', c_uint64)
  1216. ]
  1217. class MEDIA_PROTOCOL_DEVICE_PATH(LittleEndianStructure):
  1218. _pack_ = 1
  1219. _fields_ = [
  1220. ('Header', EFI_DEVICE_PATH),
  1221. ('Protocol', EFI_GUID)
  1222. ]
  1223. class MEDIA_FW_VOL_FILEPATH_DEVICE_PATH(LittleEndianStructure):
  1224. _pack_ = 1
  1225. _fields_ = [
  1226. ('Header', EFI_DEVICE_PATH),
  1227. ('FvFileName', EFI_GUID)
  1228. ]
  1229. class MEDIA_FW_VOL_DEVICE_PATH(LittleEndianStructure):
  1230. _pack_ = 1
  1231. _fields_ = [
  1232. ('Header', EFI_DEVICE_PATH),
  1233. ('FvName', EFI_GUID)
  1234. ]
  1235. class MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH(LittleEndianStructure):
  1236. _pack_ = 1
  1237. _fields_ = [
  1238. ('Header', EFI_DEVICE_PATH),
  1239. ('Reserved', c_uint32),
  1240. ('StartingOffset', c_uint64),
  1241. ('EndingOffset', c_uint64)
  1242. ]
  1243. class MEDIA_RAM_DISK_DEVICE_PATH(LittleEndianStructure):
  1244. _pack_ = 1
  1245. _fields_ = [
  1246. ('Header', EFI_DEVICE_PATH),
  1247. ('StartingAddr', c_uint64),
  1248. ('EndingAddr', c_uint64),
  1249. ('TypeGuid', EFI_GUID),
  1250. ('Instance', c_uint16)
  1251. ]
  1252. class EfiDevicePath:
  1253. '''
  1254. Parse EFI Device Paths based on the edk2 C Structures defined above.
  1255. In the context of this class verbose means hexdump extra data.
  1256. Attributes
  1257. ??????
  1258. DevicePath : list
  1259. List of devixe path instances. Each instance is a list of nodes
  1260. for the given Device Path instance.
  1261. Methods
  1262. -----------
  1263. device_path_node(address)
  1264. return the Device Path ctype hdr, ctype, and any extra data in
  1265. the Device Path node. This is just a single Device Path node,
  1266. not the entire Device Path.
  1267. device_path_node_str(address)
  1268. return the device path node (not the entire Device Path) as a string
  1269. '''
  1270. DevicePath = []
  1271. device_path_dict = {
  1272. # ( Type, SubType ) : Device Path C typedef
  1273. # HARDWARE_DEVICE_PATH
  1274. (1, 1): PCI_DEVICE_PATH,
  1275. (1, 2): PCCARD_DEVICE_PATH,
  1276. (1, 3): MEMMAP_DEVICE_PATH,
  1277. (1, 4): VENDOR_DEVICE_PATH,
  1278. (1, 5): CONTROLLER_DEVICE_PATH,
  1279. (1, 6): BMC_DEVICE_PATH,
  1280. # ACPI_DEVICE_PATH
  1281. (2, 1): ACPI_HID_DEVICE_PATH,
  1282. (2, 2): ACPI_EXTENDED_HID_DEVICE_PATH,
  1283. (2, 3): ACPI_ADR_DEVICE_PATH,
  1284. (2, 4): ACPI_NVDIMM_DEVICE_PATH,
  1285. # MESSAGING_DEVICE_PATH
  1286. (3, 1): ATAPI_DEVICE_PATH,
  1287. (3, 2): SCSI_DEVICE_PATH,
  1288. (3, 3): FIBRECHANNEL_DEVICE_PATH,
  1289. (3, 4): F1394_DEVICE_PATH,
  1290. (3, 5): USB_DEVICE_PATH,
  1291. (3, 6): I2O_DEVICE_PATH,
  1292. (3, 9): INFINIBAND_DEVICE_PATH,
  1293. (3, 10): VENDOR_DEVICE_PATH,
  1294. (3, 11): MAC_ADDR_DEVICE_PATH,
  1295. (3, 12): IPv4_DEVICE_PATH,
  1296. (3, 13): IPv6_DEVICE_PATH,
  1297. (3, 14): UART_DEVICE_PATH,
  1298. (3, 15): USB_CLASS_DEVICE_PATH,
  1299. (3, 16): USB_WWID_DEVICE_PATH,
  1300. (3, 17): DEVICE_LOGICAL_UNIT_DEVICE_PATH,
  1301. (3, 18): SATA_DEVICE_PATH,
  1302. (3, 19): ISCSI_DEVICE_PATH,
  1303. (3, 20): VLAN_DEVICE_PATH,
  1304. (3, 21): FIBRECHANNELEX_DEVICE_PATH,
  1305. (3, 22): SASEX_DEVICE_PATH,
  1306. (3, 23): NVME_NAMESPACE_DEVICE_PATH,
  1307. (3, 24): DNS_DEVICE_PATH,
  1308. (3, 25): UFS_DEVICE_PATH,
  1309. (3, 26): SD_DEVICE_PATH,
  1310. (3, 27): BLUETOOTH_DEVICE_PATH,
  1311. (3, 28): WIFI_DEVICE_PATH,
  1312. (3, 29): EMMC_DEVICE_PATH,
  1313. (3, 30): BLUETOOTH_LE_DEVICE_PATH,
  1314. (3, 31): DNS_DEVICE_PATH,
  1315. (3, 32): NVDIMM_NAMESPACE_DEVICE_PATH,
  1316. (3, 33): REST_SERVICE_DEVICE_PATH,
  1317. (3, 34): REST_VENDOR_SERVICE_DEVICE_PATH,
  1318. # MEDIA_DEVICE_PATH
  1319. (4, 1): HARDDRIVE_DEVICE_PATH,
  1320. (4, 2): CDROM_DEVICE_PATH,
  1321. (4, 3): VENDOR_DEVICE_PATH,
  1322. (4, 4): EFI_DEVICE_PATH,
  1323. (4, 5): MEDIA_PROTOCOL_DEVICE_PATH,
  1324. (4, 6): MEDIA_FW_VOL_FILEPATH_DEVICE_PATH,
  1325. (4, 7): MEDIA_FW_VOL_DEVICE_PATH,
  1326. (4, 8): MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH,
  1327. (4, 9): MEDIA_RAM_DISK_DEVICE_PATH,
  1328. # BBS_DEVICE_PATH
  1329. (5, 1): BBS_BBS_DEVICE_PATH,
  1330. }
  1331. guid_override_dict = {
  1332. uuid.UUID('37499A9D-542F-4C89-A026-35DA142094E4'):
  1333. UART_FLOW_CONTROL_DEVICE_PATH,
  1334. uuid.UUID('D487DDB4-008B-11D9-AFDC-001083FFCA4D'):
  1335. SAS_DEVICE_PATH,
  1336. }
  1337. def __init__(self, file, ptr=None, verbose=False, count=64):
  1338. '''
  1339. Convert ptr into a list of Device Path nodes. If verbose also hexdump
  1340. extra data.
  1341. '''
  1342. self._file = file
  1343. self._verbose = verbose
  1344. if ptr is None:
  1345. return
  1346. try:
  1347. instance = []
  1348. for _ in range(count): # while True
  1349. hdr, _ = self._ctype_read_ex(EFI_DEVICE_PATH, ptr)
  1350. if hdr.Length < sizeof(EFI_DEVICE_PATH):
  1351. # Not a valid device path
  1352. break
  1353. if hdr.Type == 0x7F: # END_DEVICE_PATH_TYPE
  1354. self.DevicePath.append(instance)
  1355. if hdr.SubType == 0xFF: # END_ENTIRE_DEVICE_PATH_SUBTYPE
  1356. break
  1357. if hdr.SubType == 0x01: # END_INSTANCE_DEVICE_PATH_SUBTYPE
  1358. # start new device path instance
  1359. instance = []
  1360. type_str = self.device_path_dict.get(
  1361. (hdr.Type, hdr.SubType), EFI_DEVICE_PATH)
  1362. node, extra = self._ctype_read_ex(type_str, ptr, hdr.Length)
  1363. if 'VENDOR_DEVICE_PATH' in type(node).__name__:
  1364. guid_type = self.guid_override_dict.get(
  1365. GuidNames.to_uuid(node.Guid), None)
  1366. if guid_type:
  1367. # use the ctype associated with the GUID
  1368. node, extra = self._ctype_read_ex(
  1369. guid_type, ptr, hdr.Length)
  1370. instance.append((type(node).__name__, hdr.Type,
  1371. hdr.SubType, hdr.Length, node, extra))
  1372. ptr += hdr.Length
  1373. except ValueError:
  1374. pass
  1375. def __str__(self):
  1376. ''' '''
  1377. if not self.valid():
  1378. return '<class: EfiDevicePath>'
  1379. result = ""
  1380. for instance in self.DevicePath:
  1381. for (Name, Type, SubType, Length, cnode, extra) in instance:
  1382. result += f'{Name:s} {Type:2d}:{SubType:2d} Len: {Length:3d}\n'
  1383. result += ctype_to_str(cnode, ' ', ['Reserved'])
  1384. if self._verbose:
  1385. if extra is not None:
  1386. result += hexdump(extra, ' ')
  1387. result += '\n'
  1388. return result
  1389. def valid(self):
  1390. return True if self.DevicePath else False
  1391. def device_path_node(self, address):
  1392. try:
  1393. hdr, _ = self._ctype_read_ex(EFI_DEVICE_PATH, address)
  1394. if hdr.Length < sizeof(EFI_DEVICE_PATH):
  1395. return None, None, None
  1396. type_str = self.device_path_dict.get(
  1397. (hdr.Type, hdr.SubType), EFI_DEVICE_PATH)
  1398. cnode, extra = self._ctype_read_ex(type_str, address, hdr.Length)
  1399. return hdr, cnode, extra
  1400. except ValueError:
  1401. return None, None, None
  1402. def device_path_node_str(self, address, verbose=False):
  1403. hdr, cnode, extra = self.device_path_node(address)
  1404. if hdr is None:
  1405. return ''
  1406. cname = type(cnode).__name__
  1407. result = f'{cname:s} {hdr.Type:2d}:{hdr.SubType:2d} '
  1408. result += f'Len: 0x{hdr.Length:03x}\n'
  1409. result += ctype_to_str(cnode, ' ', ['Reserved'])
  1410. if verbose:
  1411. if extra is not None:
  1412. result += hexdump(extra, ' ')
  1413. return result
  1414. def _ctype_read_ex(self, ctype_struct, offset=0, rsize=None):
  1415. if offset != 0:
  1416. self._file.seek(offset)
  1417. type_size = sizeof(ctype_struct)
  1418. size = rsize if rsize else type_size
  1419. data = self._file.read(size)
  1420. if data is None:
  1421. return None, None
  1422. cdata = ctype_struct.from_buffer(bytearray(data))
  1423. if size > type_size:
  1424. return cdata, data[type_size:]
  1425. else:
  1426. return cdata, None
  1427. class EfiConfigurationTable:
  1428. '''
  1429. A class to abstract EFI Configuration Tables from gST->ConfigurationTable
  1430. and gST->NumberOfTableEntries. Pass in the gST pointer from EFI,
  1431. likely you need to look up this address after you have loaded symbols
  1432. Attributes
  1433. ??????
  1434. ConfigurationTableDict : dictionary
  1435. dictionary of EFI Configuration Table entries
  1436. Methods
  1437. -----------
  1438. GetConfigTable(uuid)
  1439. pass in VendorGuid and return VendorTable from EFI System Table
  1440. DebugImageInfo(table)
  1441. return tuple of load address and size of PE/COFF images
  1442. '''
  1443. ConfigurationTableDict = {}
  1444. def __init__(self, file, gST_addr=None):
  1445. self._file = file
  1446. if gST_addr is None:
  1447. # ToDo add code to search for gST via EFI_SYSTEM_TABLE_POINTER
  1448. return
  1449. gST = self._ctype_read(EFI_SYSTEM_TABLE, gST_addr)
  1450. self.read_efi_config_table(gST.NumberOfTableEntries,
  1451. gST.ConfigurationTable,
  1452. self._ctype_read)
  1453. @ classmethod
  1454. def __str__(cls):
  1455. '''return EFI_CONFIGURATION_TABLE entries as a string'''
  1456. result = ""
  1457. for key, value in cls.ConfigurationTableDict.items():
  1458. result += f'{GuidNames().to_name(key):>37s}: '
  1459. result += f'VendorTable = 0x{value:08x}\n'
  1460. return result
  1461. def _ctype_read(self, ctype_struct, offset=0):
  1462. '''ctype worker function to read data'''
  1463. if offset != 0:
  1464. self._file.seek(offset)
  1465. data = self._file.read(sizeof(ctype_struct))
  1466. return ctype_struct.from_buffer(bytearray(data))
  1467. @ classmethod
  1468. def read_efi_config_table(cls, table_cnt, table_ptr, ctype_read):
  1469. '''Create a dictionary of EFI Configuration table entries'''
  1470. EmptryTables = EFI_CONFIGURATION_TABLE * table_cnt
  1471. Tables = ctype_read(EmptryTables, table_ptr)
  1472. for i in range(table_cnt):
  1473. cls.ConfigurationTableDict[str(GuidNames.to_uuid(
  1474. Tables[i].VendorGuid)).upper()] = Tables[i].VendorTable
  1475. return cls.ConfigurationTableDict
  1476. def GetConfigTable(self, uuid):
  1477. ''' Return VendorTable for VendorGuid (uuid.UUID) or None'''
  1478. return self.ConfigurationTableDict.get(uuid.upper())
  1479. def DebugImageInfo(self, table=None):
  1480. '''
  1481. Walk the debug image info table to find the LoadedImage protocols
  1482. for all the loaded PE/COFF images and return a list of load address
  1483. and image size.
  1484. '''
  1485. ImageLoad = []
  1486. if table is None:
  1487. table = self.GetConfigTable('49152e77-1ada-4764-b7a2-7afefed95e8b')
  1488. DbgInfoHdr = self._ctype_read(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER, table)
  1489. NormalImageArray = EFI_DEBUG_IMAGE_INFO * DbgInfoHdr.TableSize
  1490. NormalImageArray = self._ctype_read(
  1491. NormalImageArray, DbgInfoHdr.EfiDebugImageInfoTable)
  1492. for i in range(DbgInfoHdr.TableSize):
  1493. ImageInfo = self._ctype_read(
  1494. EFI_DEBUG_IMAGE_INFO_NORMAL, NormalImageArray[i].NormalImage)
  1495. LoadedImage = self._ctype_read(
  1496. EFI_LOADED_IMAGE_PROTOCOL,
  1497. ImageInfo.LoadedImageProtocolInstance)
  1498. ImageLoad.append((LoadedImage.ImageBase, LoadedImage.ImageSize))
  1499. return ImageLoad
  1500. class PeTeImage:
  1501. '''
  1502. A class to abstract PE/COFF or TE image processing via passing in a
  1503. Python file like object. If you pass in an address the PE/COFF is parsed,
  1504. if you pass in NULL for an address then you get a class instance you can
  1505. use to search memory for a PE/COFF hader given a pc value.
  1506. Attributes
  1507. ??????
  1508. LoadAddress : int
  1509. Load address of the PE/COFF image
  1510. AddressOfEntryPoint : int
  1511. Address of the Entry point of the PE/COFF image
  1512. TextAddress : int
  1513. Start of the PE/COFF text section
  1514. DataAddress : int
  1515. Start of the PE/COFF data section
  1516. CodeViewPdb : str
  1517. File name of the symbols file
  1518. CodeViewUuid : uuid:UUID
  1519. GUID for "RSDS" Debug Directory entry, or Mach-O UUID for "MTOC"
  1520. Methods
  1521. -----------
  1522. pcToPeCoff(address, step, max_range, rom_range)
  1523. Given an address(pc) find the PE/COFF image it is in
  1524. sections_to_str()
  1525. return a string giving info for all the PE/COFF sections
  1526. '''
  1527. def __init__(self, file, address=0):
  1528. self._file = file
  1529. # book keeping, but public
  1530. self.PeHdr = None
  1531. self.TeHdr = None
  1532. self.Machine = None
  1533. self.Subsystem = None
  1534. self.CodeViewSig = None
  1535. self.e_lfanew = 0
  1536. self.NumberOfSections = 0
  1537. self.Sections = None
  1538. # Things debuggers may want to know
  1539. self.LoadAddress = 0 if address is None else address
  1540. self.EndLoadAddress = 0
  1541. self.AddressOfEntryPoint = 0
  1542. self.TextAddress = 0
  1543. self.DataAddress = 0
  1544. self.CodeViewPdb = None
  1545. self.CodeViewUuid = None
  1546. self.TeAdjust = 0
  1547. self.dir_name = {
  1548. 0: 'Export Table',
  1549. 1: 'Import Table',
  1550. 2: 'Resource Table',
  1551. 3: 'Exception Table',
  1552. 4: 'Certificate Table',
  1553. 5: 'Relocation Table',
  1554. 6: 'Debug',
  1555. 7: 'Architecture',
  1556. 8: 'Global Ptr',
  1557. 9: 'TLS Table',
  1558. 10: 'Load Config Table',
  1559. 11: 'Bound Import',
  1560. 12: 'IAT',
  1561. 13: 'Delay Import Descriptor',
  1562. 14: 'CLR Runtime Header',
  1563. 15: 'Reserved',
  1564. }
  1565. if address is not None:
  1566. if self.maybe():
  1567. self.parse()
  1568. def __str__(self):
  1569. if self.PeHdr is None and self.TeHdr is None:
  1570. # no PE/COFF header found
  1571. return "<class: PeTeImage>"
  1572. if self.CodeViewPdb:
  1573. pdb = f'{self.Machine}`{self.CodeViewPdb}'
  1574. else:
  1575. pdb = 'No Debug Info:'
  1576. if self.CodeViewUuid:
  1577. guid = f'{self.CodeViewUuid}:'
  1578. else:
  1579. guid = ''
  1580. slide = f'slide = {self.TeAdjust:d} ' if self.TeAdjust != 0 else ' '
  1581. res = guid + f'{pdb} load = 0x{self.LoadAddress:08x} ' + slide
  1582. return res
  1583. def _seek(self, offset):
  1584. """
  1585. seek() relative to start of PE/COFF (TE) image
  1586. """
  1587. self._file.seek(self.LoadAddress + offset)
  1588. def _read_offset(self, size, offset=None):
  1589. """
  1590. read() relative to start of PE/COFF (TE) image
  1591. if offset is not None then seek() before the read
  1592. """
  1593. if offset is not None:
  1594. self._seek(offset)
  1595. return self._file.read(size)
  1596. def _read_ctype(self, ctype_struct, offset=None):
  1597. data = self._read_offset(sizeof(ctype_struct), offset)
  1598. return ctype_struct.from_buffer(bytearray(data), 0)
  1599. def _unsigned(self, i):
  1600. """return a 32-bit unsigned int (UINT32) """
  1601. return int.from_bytes(i, byteorder='little', signed=False)
  1602. def pcToPeCoff(self,
  1603. address,
  1604. step=None,
  1605. max_range=None,
  1606. rom_range=[0xFE800000, 0xFFFFFFFF]):
  1607. """
  1608. Given an address search backwards for PE/COFF (TE) header
  1609. For DXE 4K is probably OK
  1610. For PEI you might have to search every 4 bytes.
  1611. """
  1612. if step is None:
  1613. step = 0x1000
  1614. if max_range is None:
  1615. max_range = 0x200000
  1616. if address in range(*rom_range):
  1617. # The XIP code in the ROM ends up 4 byte aligned.
  1618. step = 4
  1619. max_range = min(max_range, 0x100000)
  1620. # Align address to page boundary for memory image search.
  1621. address = address & ~(step-1)
  1622. # Search every step backward
  1623. offset_range = list(range(0, min(max_range, address), step))
  1624. for offset in offset_range:
  1625. if self.maybe(address - offset):
  1626. if self.parse():
  1627. return True
  1628. return False
  1629. def maybe(self, offset=None):
  1630. """Probe to see if this offset is likely a PE/COFF or TE file """
  1631. self.LoadAddress = 0
  1632. e_magic = self._read_offset(2, offset)
  1633. header_ok = e_magic == b'MZ' or e_magic == b'VZ'
  1634. if offset is not None and header_ok:
  1635. self.LoadAddress = offset
  1636. return header_ok
  1637. def parse(self):
  1638. """Parse PE/COFF (TE) debug directory entry """
  1639. DosHdr = self._read_ctype(EFI_IMAGE_DOS_HEADER, 0)
  1640. if DosHdr.e_magic == self._unsigned(b'VZ'):
  1641. # TE image
  1642. self.TeHdr = self._read_ctype(EFI_TE_IMAGE_HEADER, 0)
  1643. self.TeAdjust = sizeof(self.TeHdr) - self.TeHdr.StrippedSize
  1644. self.Machine = image_machine_dict.get(self.TeHdr.Machine, None)
  1645. self.Subsystem = self.TeHdr.Subsystem
  1646. self.AddressOfEntryPoint = self.TeHdr.AddressOfEntryPoint
  1647. debug_dir_size = self.TeHdr.DataDirectoryDebug.Size
  1648. debug_dir_offset = (self.TeAdjust +
  1649. self.TeHdr.DataDirectoryDebug.VirtualAddress)
  1650. else:
  1651. if DosHdr.e_magic == self._unsigned(b'MZ'):
  1652. self.e_lfanew = DosHdr.e_lfanew
  1653. else:
  1654. self.e_lfanew = 0
  1655. self.PeHdr = self._read_ctype(
  1656. EFI_IMAGE_NT_HEADERS64, self.e_lfanew)
  1657. if self.PeHdr.Signature != self._unsigned(b'PE\0\0'):
  1658. return False
  1659. if self.PeHdr.OptionalHeader.Magic == \
  1660. EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
  1661. self.PeHdr = self._read_ctype(
  1662. EFI_IMAGE_NT_HEADERS32, self.e_lfanew)
  1663. if self.PeHdr.OptionalHeader.NumberOfRvaAndSizes <= \
  1664. DIRECTORY_DEBUG:
  1665. return False
  1666. self.Machine = image_machine_dict.get(
  1667. self.PeHdr.FileHeader.Machine, None)
  1668. self.Subsystem = self.PeHdr.OptionalHeader.Subsystem
  1669. self.AddressOfEntryPoint = \
  1670. self.PeHdr.OptionalHeader.AddressOfEntryPoint
  1671. self.TeAdjust = 0
  1672. debug_dir_size = self.PeHdr.OptionalHeader.DataDirectory[
  1673. DIRECTORY_DEBUG].Size
  1674. debug_dir_offset = self.PeHdr.OptionalHeader.DataDirectory[
  1675. DIRECTORY_DEBUG].VirtualAddress
  1676. if self.Machine is None or self.Subsystem not in [0, 10, 11, 12]:
  1677. return False
  1678. self.AddressOfEntryPoint += self.LoadAddress
  1679. self.sections()
  1680. return self.processDebugDirEntry(debug_dir_offset, debug_dir_size)
  1681. def sections(self):
  1682. '''Parse the PE/COFF (TE) section table'''
  1683. if self.Sections is not None:
  1684. return
  1685. elif self.TeHdr is not None:
  1686. self.NumberOfSections = self.TeHdr.NumberOfSections
  1687. offset = sizeof(EFI_TE_IMAGE_HEADER)
  1688. elif self.PeHdr is not None:
  1689. self.NumberOfSections = self.PeHdr.FileHeader.NumberOfSections
  1690. offset = sizeof(c_uint32) + \
  1691. sizeof(EFI_IMAGE_FILE_HEADER)
  1692. offset += self.PeHdr.FileHeader.SizeOfOptionalHeader
  1693. offset += self.e_lfanew
  1694. else:
  1695. return
  1696. self.Sections = EFI_IMAGE_SECTION_HEADER * self.NumberOfSections
  1697. self.Sections = self._read_ctype(self.Sections, offset)
  1698. for i in range(self.NumberOfSections):
  1699. name = str(self.Sections[i].Name, 'ascii', 'ignore')
  1700. addr = self.Sections[i].VirtualAddress
  1701. addr += self.LoadAddress + self.TeAdjust
  1702. if name == '.text':
  1703. self.TextAddress = addr
  1704. elif name == '.data':
  1705. self.DataAddress = addr
  1706. end_addr = addr + self.Sections[i].VirtualSize - 1
  1707. if end_addr > self.EndLoadAddress:
  1708. self.EndLoadAddress = end_addr
  1709. def sections_to_str(self):
  1710. # return text summary of sections
  1711. # name virt addr (virt size) flags:Characteristics
  1712. result = ''
  1713. for i in range(self.NumberOfSections):
  1714. name = str(self.Sections[i].Name, 'ascii', 'ignore')
  1715. result += f'{name:8s} '
  1716. result += f'0x{self.Sections[i].VirtualAddress:08X} '
  1717. result += f'(0x{self.Sections[i].VirtualSize:05X}) '
  1718. result += f'flags:0x{self.Sections[i].Characteristics:08X}\n'
  1719. return result
  1720. def directory_to_str(self):
  1721. result = ''
  1722. if self.TeHdr:
  1723. debug_size = self.TeHdr.DataDirectoryDebug.Size
  1724. if debug_size > 0:
  1725. debug_offset = (self.TeAdjust
  1726. + self.TeHdr.DataDirectoryDebug.VirtualAddress)
  1727. result += f"Debug 0x{debug_offset:08X} 0x{debug_size}\n"
  1728. relocation_size = self.TeHdr.DataDirectoryBaseReloc.Size
  1729. if relocation_size > 0:
  1730. relocation_offset = (
  1731. self.TeAdjust
  1732. + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress)
  1733. result += f'Relocation 0x{relocation_offset:08X} '
  1734. result += f' 0x{relocation_size}\n'
  1735. elif self.PeHdr:
  1736. for i in range(self.PeHdr.OptionalHeader.NumberOfRvaAndSizes):
  1737. size = self.PeHdr.OptionalHeader.DataDirectory[i].Size
  1738. if size == 0:
  1739. continue
  1740. virt_addr = self.PeHdr.OptionalHeader.DataDirectory[
  1741. i].VirtualAddress
  1742. name = self.dir_name.get(i, '?')
  1743. result += f'{name:s} 0x{virt_addr:08X} 0x{size:X}\n'
  1744. return result
  1745. def processDebugDirEntry(self, virt_address, virt_size):
  1746. """Process PE/COFF Debug Directory Entry"""
  1747. if (virt_address == 0 or
  1748. virt_size < sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)):
  1749. return False
  1750. data = bytearray(self._read_offset(virt_size, virt_address))
  1751. for offset in range(0,
  1752. virt_size,
  1753. sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)):
  1754. DirectoryEntry = EFI_IMAGE_DEBUG_DIRECTORY_ENTRY.from_buffer(
  1755. data[offset:])
  1756. if DirectoryEntry.Type != 2:
  1757. continue
  1758. entry = self._read_offset(
  1759. DirectoryEntry.SizeOfData, DirectoryEntry.RVA + self.TeAdjust)
  1760. self.CodeViewSig = entry[:4]
  1761. if self.CodeViewSig == b'MTOC':
  1762. self.CodeViewUuid = uuid.UUID(bytes_le=entry[4:4+16])
  1763. PdbOffset = 20
  1764. elif self.CodeViewSig == b'RSDS':
  1765. self.CodeViewUuid = uuid.UUID(bytes_le=entry[4:4+16])
  1766. PdbOffset = 24
  1767. elif self.CodeViewSig == b'NB10':
  1768. PdbOffset = 16
  1769. else:
  1770. continue
  1771. # can't find documentation about Pdb string encoding?
  1772. # guessing utf-8 since that will match file systems in macOS
  1773. # and Linux Windows is UTF-16, or ANSI adjusted for local.
  1774. # We might need a different value for Windows here?
  1775. self.CodeViewPdb = entry[PdbOffset:].split(b'\x00')[
  1776. 0].decode('utf-8')
  1777. return True
  1778. return False
  1779. def main():
  1780. '''Process arguments as PE/COFF files'''
  1781. for fname in sys.argv[1:]:
  1782. with open(fname, 'rb') as f:
  1783. image = PeTeImage(f)
  1784. print(image)
  1785. res = f'EntryPoint = 0x{image.AddressOfEntryPoint:08x} '
  1786. res += f'TextAddress = 0x{image.TextAddress:08x} '
  1787. res += f'DataAddress = 0x{image.DataAddress:08x}'
  1788. print(res)
  1789. print(image.sections_to_str())
  1790. print('Data Directories:')
  1791. print(image.directory_to_str())
  1792. if __name__ == "__main__":
  1793. main()