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