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