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
13 from struct
import pack
, unpack
16 min_ftpr_offset
= 0x400
18 unremovable_modules
= ("ROMP", "BUP")
19 unremovable_modules_me11
= ("rbe", "kernel", "syslib", "bup")
20 unremovable_partitions
= ("FTPR",)
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):
44 def __init__(self
, f
, region_start
, region_end
):
46 self
.region_start
= region_start
47 self
.region_end
= region_end
50 if f
.tell() + n
<= self
.region_end
:
53 raise OutOfRegionException()
55 def readinto(self
, b
):
56 if f
.tell() + len(b
) <= self
.region_end
:
57 return self
.f
.readinto(b
)
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
)
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
)
72 raise OutOfRegionException()
74 def fill_range(self
, start
, end
, fill
):
75 if self
.region_start
+ end
<= self
.region_end
:
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])
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)
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)))
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
]
118 for i
in range(0, chunk_count
):
119 chunk
= llut
[0x40 + i
* 4:0x44 + i
* 4]
123 offset
= unpack("<I", chunk
[0:3] + b
"\x00")[0]
125 offsets
.append([offset
, 0])
127 nonzero_offsets
.append(offset
)
129 nonzero_offsets
.sort()
133 i
[1] = nonzero_offsets
[nonzero_offsets
.index(i
[0]) + 1]
138 def remove_modules(f
, mod_headers
, ftpr_offset
, me_end
):
139 comp_str
= ("uncomp.", "Huffman", "LZMA")
140 unremovable_huff_chunks
= []
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")
163 end
= min(offset
+ size
, me_end
)
164 f
.fill_range(offset
, end
, b
"\xff")
167 elif comp_type
== 0x01:
168 if not chunks_offsets
:
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
)
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
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"),
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]
206 print("0x{:06x} - 0x{:06x}): unknown compression, skipping"
207 .format(offset
, offset
+ size
), end
="")
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])
229 def check_partition_signature(f
, 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
):
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))
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
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
))
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)
312 for i
in range(0, chunk_count
* 4, 4):
313 if chunks
[i
+ 3] != 0x80:
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
)
319 sys
.exit("Huffman modules present but no LLUT found!")
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")
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)
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
355 end_addr
= remove_modules(f
, mod_headers
, offset
, me_end
)
358 new_offset
= relocate_partition(f
, me_end
, 0x30, min_offset
,
360 end_addr
+= new_offset
- offset
363 return end_addr
, offset
366 print("Found less modules than expected in the FTPR "
367 "partition; skipping modules removal")
369 print("Can't find the module header size; skipping "
375 def check_and_remove_modules_me11(f
, me_end
, partition_offset
,
376 partition_length
, min_offset
, relocate
,
379 comp_str
= ("LZMA/uncomp.", "Huffman")
382 end_data
= partition_offset
+ partition_length
386 f
.seek(partition_offset
+ 0x4)
387 module_count
= unpack("<I", f
.read(4))[0]
390 modules
.append(("end", partition_length
, 0))
392 f
.seek(partition_offset
+ 0x10)
393 for i
in range(0, module_count
):
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
):
406 offset
= partition_offset
+ modules
[i
][1]
407 end
= partition_offset
+ modules
[i
+ 1][1]
410 if name
.endswith(".man") or name
.endswith(".met"):
411 compression
= "uncompressed"
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")
426 f
.fill_range(offset
, min(end
, me_end
), b
"\xff")
430 end_data
= max(end_data
, end
)
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)
444 sys
.exit("Wrong FTPR manifest tag ({}), this image may be corrupted"
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 "
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)",
481 parser
.add_argument("-r", "--relocate", help="relocate the FTPR partition "
482 "to the top of the ME region to save even more space",
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 "
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 "
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",
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 "
527 f
= open(args
.file, "rb" if args
.check
or args
.output
else "r+b")
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")
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)")
550 flmap0
, flmap1
= unpack("<II", f
.read(8))
551 frba
= flmap0
>> 12 & 0xff0
552 fmba
= (flmap1
& 0xff) << 4
553 fpsba
= flmap1
>> 12 & 0xff0
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
)
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
))
574 sys
.exit("Unknown image")
578 print("Found FPT header at {:#x}".format(mef
.region_start
+ 0x10))
581 entries
= unpack("<I", mef
.read(4))[0]
582 print("Found {} partition(s)".format(entries
))
585 partitions
= mef
.read(entries
* 0x20)
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]
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":
604 num_entries
= unpack("<I", mef
.read(4))[0]
606 mef
.seek(ftpr_offset
+ 0x10)
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
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
))
623 sys
.exit("Can't find the manifest of the FTPR partition")
626 check_mn2_tag(mef
, ftpr_offset
)
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
)))
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
:
654 shutil
.copy(args
.file, args
.output
)
655 f
= open(args
.output
, "r+b")
657 mef
= RegionFile(f
, me_start
, me_end
)
660 fdf
= RegionFile(f
, fd_start
, fd_end
)
664 pchstrp0
= unpack("<I", fdf
.read(4))[0]
665 print("The HAP bit is " +
666 ("SET" if pchstrp0
& 1 << 16 else "NOT SET"))
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
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)
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")
688 if not args
.soft_disable_only
and not me6_ignition
:
689 print("Reading partitions list...")
690 unremovable_part_fpt
= b
""
695 whitelist
+= unremovable_partitions
698 blacklist
= args
.blacklist
.split(",")
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]
708 partition
[0x0:0x4].rstrip(b
"\x00").decode("ascii")
709 except UnicodeDecodeError:
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 "
724 .format(part_name
, "NVRAM partition, no data",
726 elif part_start
== 0 or part_length
== 0 or part_end
> me_end
:
727 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
729 .format(part_name
, "no data here", part_length
))
731 print(" {:<4} (0x{:08x} - 0x{:09x}, 0x{:08x} total bytes): "
732 .format(part_name
, part_start
, part_end
, part_length
),
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
)
741 mef
.fill_range(part_start
, part_end
, b
"\xff")
744 print("Removing partition entries in FPT...")
745 mef
.write_to(0x30, unremovable_part_fpt
)
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 \
754 print("Removing EFFS presence flag...")
756 flags
= unpack("<I", mef
.read(4))[0]
757 flags
&= ~
(0x00000001)
758 mef
.write_to(0x24, pack("<I", flags
))
762 header
= bytearray(mef
.read(0x20))
766 header
= bytearray(mef
.read(0x30))
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...")
779 end_addr
, ftpr_offset
= \
780 check_and_remove_modules_me11(mef
, me_end
,
781 ftpr_offset
, ftpr_length
,
786 end_addr
, ftpr_offset
= \
787 check_and_remove_modules(mef
, me_end
, ftpr_offset
,
788 min_ftpr_offset
, args
.relocate
,
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
))
800 print("The ME region can be reduced up to:\n"
802 .format(me_start
, me_start
+ end_addr
- 1))
804 print("Truncating file at {:#x}...".format(end_addr
))
807 if args
.soft_disable
or args
.soft_disable_only
:
809 print("Setting the HAP bit in PCHSTRP0 to disable Intel ME...")
810 pchstrp0 |
= (1 << 16)
811 fdf
.write_to(fpsba
, pack("<I", pchstrp0
))
813 print("Setting the AltMeDisable bit in PCHSTRP10 to disable "
815 pchstrp10 |
= (1 << 7)
816 fdf
.write_to(fpsba
+ 0x28, pack("<I", pchstrp10
))
819 print("Removing ME/TXE R/W access to the other flash regions...")
824 flmstr2
= (unpack("<I", fdf
.read(4))[0] |
0x04040000) & 0x0404ffff
826 fdf
.write_to(fmba
+ 0x4, pack("<I", flmstr2
))
828 if args
.extract_descriptor
:
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
))
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")
854 print("Extracting the descriptor to \"{}\"..."
855 .format(args
.extract_descriptor
))
856 fdf_copy
= fdf
.save(args
.extract_descriptor
, fd_end
- fd_start
)
862 print("Extracting and truncating the ME image to \"{}\"..."
863 .format(args
.extract_me
))
864 mef_copy
= mef
.save(args
.extract_me
, end_addr
)
866 print("Extracting the ME image to \"{}\"..."
867 .format(args
.extract_me
))
868 mef_copy
= mef
.save(args
.extract_me
, me_end
- me_start
)
871 print("Checking the FTPR RSA signature of the extracted ME "
873 print_check_partition_signature(mef_copy
,
874 ftpr_offset
+ ftpr_mn2_offset
)
878 print("Checking the FTPR RSA signature... ", end
="")
879 print_check_partition_signature(mef
, ftpr_offset
+ ftpr_mn2_offset
)
884 print("Done! Good luck!")