drivers/wifi: Remove unnecessary data structure copy
[coreboot2.git] / util / me_cleaner / me_cleaner.py
blobfae5e567322f4d6ff8d50e5c1252304e45ef5173
1 #!/usr/bin/env python
2 # me_cleaner - Tool for partial deblobbing of Intel ME/TXE firmware images
3 # SPDX-License-Identifier: GPL-3.0-or-later
5 from __future__ import division, print_function
7 import argparse
8 import binascii
9 import hashlib
10 import itertools
11 import shutil
12 import sys
13 from struct import pack, unpack
16 min_ftpr_offset = 0x400
17 spared_blocks = 4
18 unremovable_modules = ("ROMP", "BUP")
19 unremovable_modules_me11 = ("rbe", "kernel", "syslib", "bup")
20 unremovable_partitions = ("FTPR",)
22 pubkeys_md5 = {
23 "763e59ebe235e45a197a5b1a378dfa04": ("ME", ("6.x.x.x",)),
24 "3a98c847d609c253e145bd36512629cb": ("ME", ("6.0.50.x",)),
25 "0903fc25b0f6bed8c4ed724aca02124c": ("ME", ("7.x.x.x", "8.x.x.x")),
26 "2011ae6df87c40fba09e3f20459b1ce0": ("ME", ("9.0.x.x", "9.1.x.x")),
27 "e8427c5691cf8b56bc5cdd82746957ed": ("ME", ("9.5.x.x", "10.x.x.x")),
28 "986a78e481f185f7d54e4af06eb413f6": ("ME", ("11.x.x.x",)),
29 "bda0b6bb8ca0bf0cac55ac4c4d55e0f2": ("TXE", ("1.x.x.x",)),
30 "b726a2ab9cd59d4e62fe2bead7cf6997": ("TXE", ("1.x.x.x",)),
31 "0633d7f951a3e7968ae7460861be9cfb": ("TXE", ("2.x.x.x",)),
32 "1d0a36e9f5881540d8e4b382c6612ed8": ("TXE", ("3.x.x.x",)),
33 "be900fef868f770d266b1fc67e887e69": ("SPS", ("2.x.x.x",)),
34 "4622e3f2cb212a89c90a4de3336d88d2": ("SPS", ("3.x.x.x",)),
35 "31ef3d950eac99d18e187375c0764ca4": ("SPS", ("4.x.x.x",))
39 class OutOfRegionException(Exception):
40 pass
43 class RegionFile:
44 def __init__(self, f, region_start, region_end):
45 self.f = f
46 self.region_start = region_start
47 self.region_end = region_end
49 def read(self, n):
50 if f.tell() + n <= self.region_end:
51 return self.f.read(n)
52 else:
53 raise OutOfRegionException()
55 def readinto(self, b):
56 if f.tell() + len(b) <= self.region_end:
57 return self.f.readinto(b)
58 else:
59 raise OutOfRegionException()
61 def seek(self, offset):
62 if self.region_start + offset <= self.region_end:
63 return self.f.seek(self.region_start + offset)
64 else:
65 raise OutOfRegionException()
67 def write_to(self, offset, data):
68 if self.region_start + offset + len(data) <= self.region_end:
69 self.f.seek(self.region_start + offset)
70 return self.f.write(data)
71 else:
72 raise OutOfRegionException()
74 def fill_range(self, start, end, fill):
75 if self.region_start + end <= self.region_end:
76 if start < end:
77 block = fill * 4096
78 self.f.seek(self.region_start + start)
79 self.f.writelines(itertools.repeat(block,
80 (end - start) // 4096))
81 self.f.write(block[:(end - start) % 4096])
82 else:
83 raise OutOfRegionException()
85 def fill_all(self, fill):
86 self.fill_range(0, self.region_end - self.region_start, fill)
88 def move_range(self, offset_from, size, offset_to, fill):
89 if self.region_start + offset_from + size <= self.region_end and \
90 self.region_start + offset_to + size <= self.region_end:
91 for i in range(0, size, 4096):
92 self.f.seek(self.region_start + offset_from + i, 0)
93 block = self.f.read(min(size - i, 4096))
94 self.f.seek(self.region_start + offset_from + i, 0)
95 self.f.write(fill * len(block))
96 self.f.seek(self.region_start + offset_to + i, 0)
97 self.f.write(block)
98 else:
99 raise OutOfRegionException()
101 def save(self, filename, size):
102 if self.region_start + size <= self.region_end:
103 self.f.seek(self.region_start)
104 copyf = open(filename, "w+b")
105 for i in range(0, size, 4096):
106 copyf.write(self.f.read(min(size - i, 4096)))
107 return copyf
108 else:
109 raise OutOfRegionException()
112 def get_chunks_offsets(llut):
113 chunk_count = unpack("<I", llut[0x04:0x08])[0]
114 huffman_stream_end = sum(unpack("<II", llut[0x10:0x18]))
115 nonzero_offsets = [huffman_stream_end]
116 offsets = []
118 for i in range(0, chunk_count):
119 chunk = llut[0x40 + i * 4:0x44 + i * 4]
120 offset = 0
122 if chunk[3] != 0x80:
123 offset = unpack("<I", chunk[0:3] + b"\x00")[0]
125 offsets.append([offset, 0])
126 if offset != 0:
127 nonzero_offsets.append(offset)
129 nonzero_offsets.sort()
131 for i in offsets:
132 if i[0] != 0:
133 i[1] = nonzero_offsets[nonzero_offsets.index(i[0]) + 1]
135 return offsets
138 def remove_modules(f, mod_headers, ftpr_offset, me_end):
139 comp_str = ("uncomp.", "Huffman", "LZMA")
140 unremovable_huff_chunks = []
141 chunks_offsets = []
142 base = 0
143 chunk_size = 0
144 end_addr = 0
146 for mod_header in mod_headers:
147 name = mod_header[0x04:0x14].rstrip(b"\x00").decode("ascii")
148 offset = unpack("<I", mod_header[0x38:0x3C])[0] + ftpr_offset
149 size = unpack("<I", mod_header[0x40:0x44])[0]
150 flags = unpack("<I", mod_header[0x50:0x54])[0]
151 comp_type = (flags >> 4) & 7
153 print(" {:<16} ({:<7}, ".format(name, comp_str[comp_type]), end="")
155 if comp_type == 0x00 or comp_type == 0x02:
156 print("0x{:06x} - 0x{:06x} ): "
157 .format(offset, offset + size), end="")
159 if name in unremovable_modules:
160 end_addr = max(end_addr, offset + size)
161 print("NOT removed, essential")
162 else:
163 end = min(offset + size, me_end)
164 f.fill_range(offset, end, b"\xff")
165 print("removed")
167 elif comp_type == 0x01:
168 if not chunks_offsets:
169 f.seek(offset)
170 llut = f.read(4)
171 if llut == b"LLUT":
172 llut += f.read(0x3c)
174 chunk_count = unpack("<I", llut[0x4:0x8])[0]
175 base = unpack("<I", llut[0x8:0xc])[0] + 0x10000000
176 chunk_size = unpack("<I", llut[0x30:0x34])[0]
178 llut += f.read(chunk_count * 4)
179 chunks_offsets = get_chunks_offsets(llut)
180 else:
181 sys.exit("Huffman modules found, but LLUT is not present")
183 module_base = unpack("<I", mod_header[0x34:0x38])[0]
184 module_size = unpack("<I", mod_header[0x3c:0x40])[0]
185 first_chunk_num = (module_base - base) // chunk_size
186 last_chunk_num = first_chunk_num + module_size // chunk_size
187 huff_size = 0
189 for chunk in chunks_offsets[first_chunk_num:last_chunk_num + 1]:
190 huff_size += chunk[1] - chunk[0]
192 print("fragmented data, {:<9}): "
193 .format("~" + str(int(round(huff_size / 1024))) + " KiB"),
194 end="")
196 if name in unremovable_modules:
197 print("NOT removed, essential")
199 unremovable_huff_chunks += \
200 [x for x in chunks_offsets[first_chunk_num:
201 last_chunk_num + 1] if x[0] != 0]
202 else:
203 print("removed")
205 else:
206 print("0x{:06x} - 0x{:06x}): unknown compression, skipping"
207 .format(offset, offset + size), end="")
209 if chunks_offsets:
210 removable_huff_chunks = []
212 for chunk in chunks_offsets:
213 if all(not(unremovable_chk[0] <= chunk[0] < unremovable_chk[1] or
214 unremovable_chk[0] < chunk[1] <= unremovable_chk[1])
215 for unremovable_chk in unremovable_huff_chunks):
216 removable_huff_chunks.append(chunk)
218 for removable_chunk in removable_huff_chunks:
219 if removable_chunk[1] > removable_chunk[0]:
220 end = min(removable_chunk[1], me_end)
221 f.fill_range(removable_chunk[0], end, b"\xff")
223 end_addr = max(end_addr,
224 max(unremovable_huff_chunks, key=lambda x: x[1])[1])
226 return end_addr
229 def check_partition_signature(f, offset):
230 f.seek(offset)
231 header = f.read(0x80)
232 modulus = int(binascii.hexlify(f.read(0x100)[::-1]), 16)
233 public_exponent = unpack("<I", f.read(4))[0]
234 signature = int(binascii.hexlify(f.read(0x100)[::-1]), 16)
236 header_len = unpack("<I", header[0x4:0x8])[0] * 4
237 manifest_len = unpack("<I", header[0x18:0x1c])[0] * 4
238 f.seek(offset + header_len)
240 sha256 = hashlib.sha256()
241 sha256.update(header)
242 sha256.update(f.read(manifest_len - header_len))
244 decrypted_sig = pow(signature, public_exponent, modulus)
246 return "{:#x}".format(decrypted_sig).endswith(sha256.hexdigest()) # FIXME
249 def print_check_partition_signature(f, offset):
250 if check_partition_signature(f, offset):
251 print("VALID")
252 else:
253 print("INVALID!!")
254 sys.exit("The FTPR partition signature is not valid. Is the input "
255 "ME/TXE image valid?")
258 def relocate_partition(f, me_end, partition_header_offset,
259 new_offset, mod_headers):
261 f.seek(partition_header_offset)
262 name = f.read(4).rstrip(b"\x00").decode("ascii")
263 f.seek(partition_header_offset + 0x8)
264 old_offset, partition_size = unpack("<II", f.read(0x8))
266 llut_start = 0
267 for mod_header in mod_headers:
268 if (unpack("<I", mod_header[0x50:0x54])[0] >> 4) & 7 == 0x01:
269 llut_start = unpack("<I", mod_header[0x38:0x3C])[0] + old_offset
270 break
272 if mod_headers and llut_start != 0:
273 # Bytes 0x9:0xb of the LLUT (bytes 0x1:0x3 of the AddrBase) are added
274 # to the SpiBase (bytes 0xc:0x10 of the LLUT) to compute the final
275 # start of the LLUT. Since AddrBase is not modifiable, we can act only
276 # on SpiBase and here we compute the minimum allowed new_offset.
277 f.seek(llut_start + 0x9)
278 lut_start_corr = unpack("<H", f.read(2))[0]
279 new_offset = max(new_offset,
280 lut_start_corr - llut_start - 0x40 + old_offset)
281 new_offset = ((new_offset + 0x1f) // 0x20) * 0x20
283 offset_diff = new_offset - old_offset
284 print("Relocating {} from {:#x} - {:#x} to {:#x} - {:#x}..."
285 .format(name, old_offset, old_offset + partition_size,
286 new_offset, new_offset + partition_size))
288 print(" Adjusting FPT entry...")
289 f.write_to(partition_header_offset + 0x8,
290 pack("<I", new_offset))
292 if mod_headers:
293 if llut_start != 0:
294 f.seek(llut_start)
295 if f.read(4) == b"LLUT":
296 print(" Adjusting LUT start offset...")
297 lut_offset = llut_start + offset_diff + 0x40 - lut_start_corr
298 f.write_to(llut_start + 0x0c, pack("<I", lut_offset))
300 print(" Adjusting Huffman start offset...")
301 f.seek(llut_start + 0x14)
302 old_huff_offset = unpack("<I", f.read(4))[0]
303 f.write_to(llut_start + 0x14,
304 pack("<I", old_huff_offset + offset_diff))
306 print(" Adjusting chunks offsets...")
307 f.seek(llut_start + 0x4)
308 chunk_count = unpack("<I", f.read(4))[0]
309 f.seek(llut_start + 0x40)
310 chunks = bytearray(chunk_count * 4)
311 f.readinto(chunks)
312 for i in range(0, chunk_count * 4, 4):
313 if chunks[i + 3] != 0x80:
314 chunks[i:i + 3] = \
315 pack("<I", unpack("<I", chunks[i:i + 3] +
316 b"\x00")[0] + offset_diff)[0:3]
317 f.write_to(llut_start + 0x40, chunks)
318 else:
319 sys.exit("Huffman modules present but no LLUT found!")
320 else:
321 print(" No Huffman modules found")
323 print(" Moving data...")
324 partition_size = min(partition_size, me_end - old_offset)
325 f.move_range(old_offset, partition_size, new_offset, b"\xff")
327 return new_offset
330 def check_and_remove_modules(f, me_end, offset, min_offset,
331 relocate, keep_modules):
333 f.seek(offset + 0x20)
334 num_modules = unpack("<I", f.read(4))[0]
335 f.seek(offset + 0x290)
336 data = f.read(0x84)
338 mod_header_size = 0
339 if data[0x0:0x4] == b"$MME":
340 if data[0x60:0x64] == b"$MME" or num_modules == 1:
341 mod_header_size = 0x60
342 elif data[0x80:0x84] == b"$MME":
343 mod_header_size = 0x80
345 if mod_header_size != 0:
346 f.seek(offset + 0x290)
347 data = f.read(mod_header_size * num_modules)
348 mod_headers = [data[i * mod_header_size:(i + 1) * mod_header_size]
349 for i in range(0, num_modules)]
351 if all(hdr.startswith(b"$MME") for hdr in mod_headers):
352 if args.keep_modules:
353 end_addr = offset + ftpr_length
354 else:
355 end_addr = remove_modules(f, mod_headers, offset, me_end)
357 if args.relocate:
358 new_offset = relocate_partition(f, me_end, 0x30, min_offset,
359 mod_headers)
360 end_addr += new_offset - offset
361 offset = new_offset
363 return end_addr, offset
365 else:
366 print("Found less modules than expected in the FTPR "
367 "partition; skipping modules removal")
368 else:
369 print("Can't find the module header size; skipping "
370 "modules removal")
372 return -1, offset
375 def check_and_remove_modules_me11(f, me_end, partition_offset,
376 partition_length, min_offset, relocate,
377 keep_modules):
379 comp_str = ("LZMA/uncomp.", "Huffman")
381 if keep_modules:
382 end_data = partition_offset + partition_length
383 else:
384 end_data = 0
386 f.seek(partition_offset + 0x4)
387 module_count = unpack("<I", f.read(4))[0]
389 modules = []
390 modules.append(("end", partition_length, 0))
392 f.seek(partition_offset + 0x10)
393 for i in range(0, module_count):
394 data = f.read(0x18)
395 name = data[0x0:0xc].rstrip(b"\x00").decode("ascii")
396 offset_block = unpack("<I", data[0xc:0x10])[0]
397 offset = offset_block & 0x01ffffff
398 comp_type = (offset_block & 0x02000000) >> 25
400 modules.append((name, offset, comp_type))
402 modules.sort(key=lambda x: x[1])
404 for i in range(0, module_count):
405 name = modules[i][0]
406 offset = partition_offset + modules[i][1]
407 end = partition_offset + modules[i + 1][1]
408 removed = False
410 if name.endswith(".man") or name.endswith(".met"):
411 compression = "uncompressed"
412 else:
413 compression = comp_str[modules[i][2]]
415 print(" {:<12} ({:<12}, 0x{:06x} - 0x{:06x}): "
416 .format(name, compression, offset, end), end="")
418 if name.endswith(".man"):
419 print("NOT removed, partition manif.")
420 elif name.endswith(".met"):
421 print("NOT removed, module metadata")
422 elif any(name.startswith(m) for m in unremovable_modules_me11):
423 print("NOT removed, essential")
424 else:
425 removed = True
426 f.fill_range(offset, min(end, me_end), b"\xff")
427 print("removed")
429 if not removed:
430 end_data = max(end_data, end)
432 if relocate:
433 new_offset = relocate_partition(f, me_end, 0x30, min_offset, [])
434 end_data += new_offset - partition_offset
435 partition_offset = new_offset
437 return end_data, partition_offset
440 def check_mn2_tag(f, offset):
441 f.seek(offset + 0x1c)
442 tag = f.read(4)
443 if tag != b"$MN2":
444 sys.exit("Wrong FTPR manifest tag ({}), this image may be corrupted"
445 .format(tag))
448 def flreg_to_start_end(flreg):
449 return (flreg & 0x7fff) << 12, (flreg >> 4 & 0x7fff000 | 0xfff) + 1
452 def start_end_to_flreg(start, end):
453 return (start & 0x7fff000) >> 12 | ((end - 1) & 0x7fff000) << 4
456 if __name__ == "__main__":
457 parser = argparse.ArgumentParser(description="Tool to remove as much code "
458 "as possible from Intel ME/TXE firmware "
459 "images")
460 softdis = parser.add_mutually_exclusive_group()
461 bw_list = parser.add_mutually_exclusive_group()
463 parser.add_argument("-v", "--version", action="version",
464 version="%(prog)s 1.2")
466 parser.add_argument("file", help="ME/TXE image or full dump")
467 parser.add_argument("-O", "--output", metavar='output_file', help="save "
468 "the modified image in a separate file, instead of "
469 "modifying the original file")
470 softdis.add_argument("-S", "--soft-disable", help="in addition to the "
471 "usual operations on the ME/TXE firmware, set the "
472 "MeAltDisable bit or the HAP bit to ask Intel ME/TXE "
473 "to disable itself after the hardware initialization "
474 "(requires a full dump)", action="store_true")
475 softdis.add_argument("-s", "--soft-disable-only", help="instead of the "
476 "usual operations on the ME/TXE firmware, just set "
477 "the MeAltDisable bit or the HAP bit to ask Intel "
478 "ME/TXE to disable itself after the hardware "
479 "initialization (requires a full dump)",
480 action="store_true")
481 parser.add_argument("-r", "--relocate", help="relocate the FTPR partition "
482 "to the top of the ME region to save even more space",
483 action="store_true")
484 parser.add_argument("-t", "--truncate", help="truncate the empty part of "
485 "the firmware (requires a separated ME/TXE image or "
486 "--extract-me)", action="store_true")
487 parser.add_argument("-k", "--keep-modules", help="don't remove the FTPR "
488 "modules, even when possible", action="store_true")
489 bw_list.add_argument("-w", "--whitelist", metavar="whitelist",
490 help="Comma separated list of additional partitions "
491 "to keep in the final image. This can be used to "
492 "specify the MFS partition for example, which stores "
493 "PCIe and clock settings.")
494 bw_list.add_argument("-b", "--blacklist", metavar="blacklist",
495 help="Comma separated list of partitions to remove "
496 "from the image. This option overrides the default "
497 "removal list.")
498 parser.add_argument("-d", "--descriptor", help="remove the ME/TXE "
499 "Read/Write permissions to the other regions on the "
500 "flash from the Intel Flash Descriptor (requires a "
501 "full dump)", action="store_true")
502 parser.add_argument("-D", "--extract-descriptor",
503 metavar='output_descriptor', help="extract the flash "
504 "descriptor from a full dump; when used with "
505 "--truncate save a descriptor with adjusted regions "
506 "start and end")
507 parser.add_argument("-M", "--extract-me", metavar='output_me_image',
508 help="extract the ME firmware from a full dump; when "
509 "used with --truncate save a truncated ME/TXE image")
510 parser.add_argument("-c", "--check", help="verify the integrity of the "
511 "fundamental parts of the firmware and exit",
512 action="store_true")
514 args = parser.parse_args()
516 if args.check and (args.soft_disable_only or args.soft_disable or
517 args.relocate or args.descriptor or args.truncate or args.output):
518 sys.exit("-c can't be used with -S, -s, -r, -d, -t or -O")
520 if args.soft_disable_only and (args.relocate or args.truncate):
521 sys.exit("-s can't be used with -r or -t")
523 if (args.whitelist or args.blacklist) and args.relocate:
524 sys.exit("Relocation is not yet supported with custom whitelist or "
525 "blacklist")
527 f = open(args.file, "rb" if args.check or args.output else "r+b")
528 f.seek(0x10)
529 magic = f.read(4)
531 if magic == b"$FPT":
532 print("ME/TXE image detected")
534 if args.descriptor or args.extract_descriptor or args.extract_me or \
535 args.soft_disable or args.soft_disable_only:
536 sys.exit("-d, -D, -M, -S and -s require a full dump")
538 f.seek(0, 2)
539 me_start = 0
540 me_end = f.tell()
541 mef = RegionFile(f, me_start, me_end)
543 elif magic == b"\x5a\xa5\xf0\x0f":
544 print("Full image detected")
546 if args.truncate and not args.extract_me:
547 sys.exit("-t requires a separated ME/TXE image (or --extract-me)")
549 f.seek(0x14)
550 flmap0, flmap1 = unpack("<II", f.read(8))
551 frba = flmap0 >> 12 & 0xff0
552 fmba = (flmap1 & 0xff) << 4
553 fpsba = flmap1 >> 12 & 0xff0
555 f.seek(frba)
556 flreg = unpack("<III", f.read(12))
558 fd_start, fd_end = flreg_to_start_end(flreg[0])
559 bios_start, bios_end = flreg_to_start_end(flreg[1])
560 me_start, me_end = flreg_to_start_end(flreg[2])
562 if me_start >= me_end:
563 sys.exit("The ME/TXE region in this image has been disabled")
565 mef = RegionFile(f, me_start, me_end)
567 mef.seek(0x10)
568 if mef.read(4) != b"$FPT":
569 sys.exit("The ME/TXE region is corrupted or missing")
571 print("The ME/TXE region goes from {:#x} to {:#x}"
572 .format(me_start, me_end))
573 else:
574 sys.exit("Unknown image")
576 end_addr = me_end
578 print("Found FPT header at {:#x}".format(mef.region_start + 0x10))
580 mef.seek(0x14)
581 entries = unpack("<I", mef.read(4))[0]
582 print("Found {} partition(s)".format(entries))
584 mef.seek(0x30)
585 partitions = mef.read(entries * 0x20)
587 ftpr_header = b""
589 for i in range(entries):
590 if partitions[i * 0x20:(i * 0x20) + 4] == b"FTPR":
591 ftpr_header = partitions[i * 0x20:(i + 1) * 0x20]
592 break
594 if ftpr_header == b"":
595 sys.exit("FTPR header not found, this image doesn't seem to be valid")
597 ftpr_offset, ftpr_length = unpack("<II", ftpr_header[0x08:0x10])
598 print("Found FTPR header: FTPR partition spans from {:#x} to {:#x}"
599 .format(ftpr_offset, ftpr_offset + ftpr_length))
601 mef.seek(ftpr_offset)
602 if mef.read(4) == b"$CPD":
603 me11 = True
604 num_entries = unpack("<I", mef.read(4))[0]
606 mef.seek(ftpr_offset + 0x10)
607 ftpr_mn2_offset = -1
609 for i in range(0, num_entries):
610 data = mef.read(0x18)
611 name = data[0x0:0xc].rstrip(b"\x00").decode("ascii")
612 offset = unpack("<I", data[0xc:0xf] + b"\x00")[0]
614 if name == "FTPR.man":
615 ftpr_mn2_offset = offset
616 break
618 if ftpr_mn2_offset >= 0:
619 check_mn2_tag(mef, ftpr_offset + ftpr_mn2_offset)
620 print("Found FTPR manifest at {:#x}"
621 .format(ftpr_offset + ftpr_mn2_offset))
622 else:
623 sys.exit("Can't find the manifest of the FTPR partition")
625 else:
626 check_mn2_tag(mef, ftpr_offset)
627 me11 = False
628 ftpr_mn2_offset = 0
630 mef.seek(ftpr_offset + ftpr_mn2_offset + 0x24)
631 version = unpack("<HHHH", mef.read(0x08))
632 print("ME/TXE firmware version {}"
633 .format('.'.join(str(i) for i in version)))
635 mef.seek(ftpr_offset + ftpr_mn2_offset + 0x80)
636 pubkey_md5 = hashlib.md5(mef.read(0x104)).hexdigest()
638 if pubkey_md5 in pubkeys_md5:
639 variant, pubkey_versions = pubkeys_md5[pubkey_md5]
640 print("Public key match: Intel {}, firmware versions {}"
641 .format(variant, ", ".join(pubkey_versions)))
642 else:
643 if version[0] >= 6:
644 variant = "ME"
645 else:
646 variant = "TXE"
647 print("WARNING Unknown public key {}\n"
648 " Assuming Intel {}\n"
649 " Please report this warning to the project's maintainer!"
650 .format(pubkey_md5, variant))
652 if not args.check and args.output:
653 f.close()
654 shutil.copy(args.file, args.output)
655 f = open(args.output, "r+b")
657 mef = RegionFile(f, me_start, me_end)
659 if me_start > 0:
660 fdf = RegionFile(f, fd_start, fd_end)
662 if me11:
663 fdf.seek(fpsba)
664 pchstrp0 = unpack("<I", fdf.read(4))[0]
665 print("The HAP bit is " +
666 ("SET" if pchstrp0 & 1 << 16 else "NOT SET"))
667 else:
668 fdf.seek(fpsba + 0x28)
669 pchstrp10 = unpack("<I", fdf.read(4))[0]
670 print("The AltMeDisable bit is " +
671 ("SET" if pchstrp10 & 1 << 7 else "NOT SET"))
673 # ME 6 Ignition: wipe everything
674 me6_ignition = False
675 if not args.check and not args.soft_disable_only and \
676 variant == "ME" and version[0] == 6:
677 mef.seek(ftpr_offset + 0x20)
678 num_modules = unpack("<I", mef.read(4))[0]
679 mef.seek(ftpr_offset + 0x290 + (num_modules + 1) * 0x60)
680 data = mef.read(0xc)
682 if data[0x0:0x4] == b"$SKU" and data[0x8:0xc] == b"\x00\x00\x00\x00":
683 print("ME 6 Ignition firmware detected, removing everything...")
684 mef.fill_all(b"\xff")
685 me6_ignition = True
687 if not args.check:
688 if not args.soft_disable_only and not me6_ignition:
689 print("Reading partitions list...")
690 unremovable_part_fpt = b""
691 extra_part_end = 0
692 whitelist = []
693 blacklist = []
695 whitelist += unremovable_partitions
697 if args.blacklist:
698 blacklist = args.blacklist.split(",")
699 elif args.whitelist:
700 whitelist += args.whitelist.split(",")
702 for i in range(entries):
703 partition = partitions[i * 0x20:(i + 1) * 0x20]
704 flags = unpack("<I", partition[0x1c:0x20])[0]
706 try:
707 part_name = \
708 partition[0x0:0x4].rstrip(b"\x00").decode("ascii")
709 except UnicodeDecodeError:
710 part_name = "????"
712 part_start, part_length = unpack("<II", partition[0x08:0x10])
714 # ME 6: the last partition has 0xffffffff as size
715 if variant == "ME" and version[0] == 6 and \
716 i == entries - 1 and part_length == 0xffffffff:
717 part_length = me_end - me_start - part_start
719 part_end = part_start + part_length
721 if flags & 0x7f == 2:
722 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
723 "remove"
724 .format(part_name, "NVRAM partition, no data",
725 part_length))
726 elif part_start == 0 or part_length == 0 or part_end > me_end:
727 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
728 "remove"
729 .format(part_name, "no data here", part_length))
730 else:
731 print(" {:<4} (0x{:08x} - 0x{:09x}, 0x{:08x} total bytes): "
732 .format(part_name, part_start, part_end, part_length),
733 end="")
734 if part_name in whitelist or (blacklist and
735 part_name not in blacklist):
736 unremovable_part_fpt += partition
737 if part_name != "FTPR":
738 extra_part_end = max(extra_part_end, part_end)
739 print("NOT removed")
740 else:
741 mef.fill_range(part_start, part_end, b"\xff")
742 print("removed")
744 print("Removing partition entries in FPT...")
745 mef.write_to(0x30, unremovable_part_fpt)
746 mef.write_to(0x14,
747 pack("<I", len(unremovable_part_fpt) // 0x20))
749 mef.fill_range(0x30 + len(unremovable_part_fpt),
750 0x30 + len(partitions), b"\xff")
752 if (not blacklist and "EFFS" not in whitelist) or \
753 "EFFS" in blacklist:
754 print("Removing EFFS presence flag...")
755 mef.seek(0x24)
756 flags = unpack("<I", mef.read(4))[0]
757 flags &= ~(0x00000001)
758 mef.write_to(0x24, pack("<I", flags))
760 if me11:
761 mef.seek(0x10)
762 header = bytearray(mef.read(0x20))
763 header[0x0b] = 0x00
764 else:
765 mef.seek(0)
766 header = bytearray(mef.read(0x30))
767 header[0x1b] = 0x00
768 checksum = (0x100 - sum(header) & 0xff) & 0xff
770 print("Correcting checksum (0x{:02x})...".format(checksum))
771 # The checksum is just the two's complement of the sum of the first
772 # 0x30 bytes in ME < 11 or bytes 0x10:0x30 in ME >= 11 (except for
773 # 0x1b, the checksum itself). In other words, the sum of those
774 # bytes must be always 0x00.
775 mef.write_to(0x1b, pack("B", checksum))
777 print("Reading FTPR modules list...")
778 if me11:
779 end_addr, ftpr_offset = \
780 check_and_remove_modules_me11(mef, me_end,
781 ftpr_offset, ftpr_length,
782 min_ftpr_offset,
783 args.relocate,
784 args.keep_modules)
785 else:
786 end_addr, ftpr_offset = \
787 check_and_remove_modules(mef, me_end, ftpr_offset,
788 min_ftpr_offset, args.relocate,
789 args.keep_modules)
791 if end_addr > 0:
792 end_addr = max(end_addr, extra_part_end)
793 end_addr = (end_addr // 0x1000 + 1) * 0x1000
794 end_addr += spared_blocks * 0x1000
796 print("The ME minimum size should be {0} bytes "
797 "({0:#x} bytes)".format(end_addr))
799 if me_start > 0:
800 print("The ME region can be reduced up to:\n"
801 " {:08x}:{:08x} me"
802 .format(me_start, me_start + end_addr - 1))
803 elif args.truncate:
804 print("Truncating file at {:#x}...".format(end_addr))
805 f.truncate(end_addr)
807 if args.soft_disable or args.soft_disable_only:
808 if me11:
809 print("Setting the HAP bit in PCHSTRP0 to disable Intel ME...")
810 pchstrp0 |= (1 << 16)
811 fdf.write_to(fpsba, pack("<I", pchstrp0))
812 else:
813 print("Setting the AltMeDisable bit in PCHSTRP10 to disable "
814 "Intel ME...")
815 pchstrp10 |= (1 << 7)
816 fdf.write_to(fpsba + 0x28, pack("<I", pchstrp10))
818 if args.descriptor:
819 print("Removing ME/TXE R/W access to the other flash regions...")
820 if me11:
821 flmstr2 = 0x00400500
822 else:
823 fdf.seek(fmba + 0x4)
824 flmstr2 = (unpack("<I", fdf.read(4))[0] | 0x04040000) & 0x0404ffff
826 fdf.write_to(fmba + 0x4, pack("<I", flmstr2))
828 if args.extract_descriptor:
829 if args.truncate:
830 print("Extracting the descriptor to \"{}\"..."
831 .format(args.extract_descriptor))
832 fdf_copy = fdf.save(args.extract_descriptor, fd_end - fd_start)
834 if bios_start == me_end:
835 print("Modifying the regions of the extracted descriptor...")
836 print(" {:08x}:{:08x} me --> {:08x}:{:08x} me"
837 .format(me_start, me_end - 1,
838 me_start, me_start + end_addr - 1))
839 print(" {:08x}:{:08x} bios --> {:08x}:{:08x} bios"
840 .format(bios_start, bios_end - 1,
841 me_start + end_addr, bios_end - 1))
843 flreg1 = start_end_to_flreg(me_start + end_addr, bios_end)
844 flreg2 = start_end_to_flreg(me_start, me_start + end_addr)
846 fdf_copy.seek(frba + 0x4)
847 fdf_copy.write(pack("<II", flreg1, flreg2))
848 else:
849 print("\nWARNING:\n The start address of the BIOS region "
850 "isn't equal to the end address of the ME\n region: if "
851 "you want to recover the space from the ME region you "
852 "have to\n manually modify the descriptor.\n")
853 else:
854 print("Extracting the descriptor to \"{}\"..."
855 .format(args.extract_descriptor))
856 fdf_copy = fdf.save(args.extract_descriptor, fd_end - fd_start)
858 fdf_copy.close()
860 if args.extract_me:
861 if args.truncate:
862 print("Extracting and truncating the ME image to \"{}\"..."
863 .format(args.extract_me))
864 mef_copy = mef.save(args.extract_me, end_addr)
865 else:
866 print("Extracting the ME image to \"{}\"..."
867 .format(args.extract_me))
868 mef_copy = mef.save(args.extract_me, me_end - me_start)
870 if not me6_ignition:
871 print("Checking the FTPR RSA signature of the extracted ME "
872 "image... ", end="")
873 print_check_partition_signature(mef_copy,
874 ftpr_offset + ftpr_mn2_offset)
875 mef_copy.close()
877 if not me6_ignition:
878 print("Checking the FTPR RSA signature... ", end="")
879 print_check_partition_signature(mef, ftpr_offset + ftpr_mn2_offset)
881 f.close()
883 if not args.check:
884 print("Done! Good luck!")