3 # me_cleaner - Tool for partial deblobbing of Intel ME/TXE firmware images
4 # Copyright (C) 2016-2018 Nicola Corna <nicola@corna.info>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
17 from __future__
import division
, print_function
25 from struct
import pack
, unpack
28 min_ftpr_offset
= 0x400
30 unremovable_modules
= ("ROMP", "BUP")
31 unremovable_modules_me11
= ("rbe", "kernel", "syslib", "bup")
32 unremovable_partitions
= ("FTPR",)
35 "763e59ebe235e45a197a5b1a378dfa04": ("ME", ("6.x.x.x",)),
36 "3a98c847d609c253e145bd36512629cb": ("ME", ("6.0.50.x",)),
37 "0903fc25b0f6bed8c4ed724aca02124c": ("ME", ("7.x.x.x", "8.x.x.x")),
38 "2011ae6df87c40fba09e3f20459b1ce0": ("ME", ("9.0.x.x", "9.1.x.x")),
39 "e8427c5691cf8b56bc5cdd82746957ed": ("ME", ("9.5.x.x", "10.x.x.x")),
40 "986a78e481f185f7d54e4af06eb413f6": ("ME", ("11.x.x.x",)),
41 "bda0b6bb8ca0bf0cac55ac4c4d55e0f2": ("TXE", ("1.x.x.x",)),
42 "b726a2ab9cd59d4e62fe2bead7cf6997": ("TXE", ("1.x.x.x",)),
43 "0633d7f951a3e7968ae7460861be9cfb": ("TXE", ("2.x.x.x",)),
44 "1d0a36e9f5881540d8e4b382c6612ed8": ("TXE", ("3.x.x.x",)),
45 "be900fef868f770d266b1fc67e887e69": ("SPS", ("2.x.x.x",)),
46 "4622e3f2cb212a89c90a4de3336d88d2": ("SPS", ("3.x.x.x",)),
47 "31ef3d950eac99d18e187375c0764ca4": ("SPS", ("4.x.x.x",))
51 class OutOfRegionException(Exception):
56 def __init__(self
, f
, region_start
, region_end
):
58 self
.region_start
= region_start
59 self
.region_end
= region_end
62 if f
.tell() + n
<= self
.region_end
:
65 raise OutOfRegionException()
67 def readinto(self
, b
):
68 if f
.tell() + len(b
) <= self
.region_end
:
69 return self
.f
.readinto(b
)
71 raise OutOfRegionException()
73 def seek(self
, offset
):
74 if self
.region_start
+ offset
<= self
.region_end
:
75 return self
.f
.seek(self
.region_start
+ offset
)
77 raise OutOfRegionException()
79 def write_to(self
, offset
, data
):
80 if self
.region_start
+ offset
+ len(data
) <= self
.region_end
:
81 self
.f
.seek(self
.region_start
+ offset
)
82 return self
.f
.write(data
)
84 raise OutOfRegionException()
86 def fill_range(self
, start
, end
, fill
):
87 if self
.region_start
+ end
<= self
.region_end
:
90 self
.f
.seek(self
.region_start
+ start
)
91 self
.f
.writelines(itertools
.repeat(block
,
92 (end
- start
) // 4096))
93 self
.f
.write(block
[:(end
- start
) % 4096])
95 raise OutOfRegionException()
97 def fill_all(self
, fill
):
98 self
.fill_range(0, self
.region_end
- self
.region_start
, fill
)
100 def move_range(self
, offset_from
, size
, offset_to
, fill
):
101 if self
.region_start
+ offset_from
+ size
<= self
.region_end
and \
102 self
.region_start
+ offset_to
+ size
<= self
.region_end
:
103 for i
in range(0, size
, 4096):
104 self
.f
.seek(self
.region_start
+ offset_from
+ i
, 0)
105 block
= self
.f
.read(min(size
- i
, 4096))
106 self
.f
.seek(self
.region_start
+ offset_from
+ i
, 0)
107 self
.f
.write(fill
* len(block
))
108 self
.f
.seek(self
.region_start
+ offset_to
+ i
, 0)
111 raise OutOfRegionException()
113 def save(self
, filename
, size
):
114 if self
.region_start
+ size
<= self
.region_end
:
115 self
.f
.seek(self
.region_start
)
116 copyf
= open(filename
, "w+b")
117 for i
in range(0, size
, 4096):
118 copyf
.write(self
.f
.read(min(size
- i
, 4096)))
121 raise OutOfRegionException()
124 def get_chunks_offsets(llut
):
125 chunk_count
= unpack("<I", llut
[0x04:0x08])[0]
126 huffman_stream_end
= sum(unpack("<II", llut
[0x10:0x18]))
127 nonzero_offsets
= [huffman_stream_end
]
130 for i
in range(0, chunk_count
):
131 chunk
= llut
[0x40 + i
* 4:0x44 + i
* 4]
135 offset
= unpack("<I", chunk
[0:3] + b
"\x00")[0]
137 offsets
.append([offset
, 0])
139 nonzero_offsets
.append(offset
)
141 nonzero_offsets
.sort()
145 i
[1] = nonzero_offsets
[nonzero_offsets
.index(i
[0]) + 1]
150 def remove_modules(f
, mod_headers
, ftpr_offset
, me_end
):
151 comp_str
= ("uncomp.", "Huffman", "LZMA")
152 unremovable_huff_chunks
= []
158 for mod_header
in mod_headers
:
159 name
= mod_header
[0x04:0x14].rstrip(b
"\x00").decode("ascii")
160 offset
= unpack("<I", mod_header
[0x38:0x3C])[0] + ftpr_offset
161 size
= unpack("<I", mod_header
[0x40:0x44])[0]
162 flags
= unpack("<I", mod_header
[0x50:0x54])[0]
163 comp_type
= (flags
>> 4) & 7
165 print(" {:<16} ({:<7}, ".format(name
, comp_str
[comp_type
]), end
="")
167 if comp_type
== 0x00 or comp_type
== 0x02:
168 print("0x{:06x} - 0x{:06x} ): "
169 .format(offset
, offset
+ size
), end
="")
171 if name
in unremovable_modules
:
172 end_addr
= max(end_addr
, offset
+ size
)
173 print("NOT removed, essential")
175 end
= min(offset
+ size
, me_end
)
176 f
.fill_range(offset
, end
, b
"\xff")
179 elif comp_type
== 0x01:
180 if not chunks_offsets
:
186 chunk_count
= unpack("<I", llut
[0x4:0x8])[0]
187 base
= unpack("<I", llut
[0x8:0xc])[0] + 0x10000000
188 chunk_size
= unpack("<I", llut
[0x30:0x34])[0]
190 llut
+= f
.read(chunk_count
* 4)
191 chunks_offsets
= get_chunks_offsets(llut
)
193 sys
.exit("Huffman modules found, but LLUT is not present")
195 module_base
= unpack("<I", mod_header
[0x34:0x38])[0]
196 module_size
= unpack("<I", mod_header
[0x3c:0x40])[0]
197 first_chunk_num
= (module_base
- base
) // chunk_size
198 last_chunk_num
= first_chunk_num
+ module_size
// chunk_size
201 for chunk
in chunks_offsets
[first_chunk_num
:last_chunk_num
+ 1]:
202 huff_size
+= chunk
[1] - chunk
[0]
204 print("fragmented data, {:<9}): "
205 .format("~" + str(int(round(huff_size
/ 1024))) + " KiB"),
208 if name
in unremovable_modules
:
209 print("NOT removed, essential")
211 unremovable_huff_chunks
+= \
212 [x
for x
in chunks_offsets
[first_chunk_num
:
213 last_chunk_num
+ 1] if x
[0] != 0]
218 print("0x{:06x} - 0x{:06x}): unknown compression, skipping"
219 .format(offset
, offset
+ size
), end
="")
222 removable_huff_chunks
= []
224 for chunk
in chunks_offsets
:
225 if all(not(unremovable_chk
[0] <= chunk
[0] < unremovable_chk
[1] or
226 unremovable_chk
[0] < chunk
[1] <= unremovable_chk
[1])
227 for unremovable_chk
in unremovable_huff_chunks
):
228 removable_huff_chunks
.append(chunk
)
230 for removable_chunk
in removable_huff_chunks
:
231 if removable_chunk
[1] > removable_chunk
[0]:
232 end
= min(removable_chunk
[1], me_end
)
233 f
.fill_range(removable_chunk
[0], end
, b
"\xff")
235 end_addr
= max(end_addr
,
236 max(unremovable_huff_chunks
, key
=lambda x
: x
[1])[1])
241 def check_partition_signature(f
, offset
):
243 header
= f
.read(0x80)
244 modulus
= int(binascii
.hexlify(f
.read(0x100)[::-1]), 16)
245 public_exponent
= unpack("<I", f
.read(4))[0]
246 signature
= int(binascii
.hexlify(f
.read(0x100)[::-1]), 16)
248 header_len
= unpack("<I", header
[0x4:0x8])[0] * 4
249 manifest_len
= unpack("<I", header
[0x18:0x1c])[0] * 4
250 f
.seek(offset
+ header_len
)
252 sha256
= hashlib
.sha256()
253 sha256
.update(header
)
254 sha256
.update(f
.read(manifest_len
- header_len
))
256 decrypted_sig
= pow(signature
, public_exponent
, modulus
)
258 return "{:#x}".format(decrypted_sig
).endswith(sha256
.hexdigest()) # FIXME
261 def print_check_partition_signature(f
, offset
):
262 if check_partition_signature(f
, offset
):
266 sys
.exit("The FTPR partition signature is not valid. Is the input "
267 "ME/TXE image valid?")
270 def relocate_partition(f
, me_end
, partition_header_offset
,
271 new_offset
, mod_headers
):
273 f
.seek(partition_header_offset
)
274 name
= f
.read(4).rstrip(b
"\x00").decode("ascii")
275 f
.seek(partition_header_offset
+ 0x8)
276 old_offset
, partition_size
= unpack("<II", f
.read(0x8))
279 for mod_header
in mod_headers
:
280 if (unpack("<I", mod_header
[0x50:0x54])[0] >> 4) & 7 == 0x01:
281 llut_start
= unpack("<I", mod_header
[0x38:0x3C])[0] + old_offset
284 if mod_headers
and llut_start
!= 0:
285 # Bytes 0x9:0xb of the LLUT (bytes 0x1:0x3 of the AddrBase) are added
286 # to the SpiBase (bytes 0xc:0x10 of the LLUT) to compute the final
287 # start of the LLUT. Since AddrBase is not modifiable, we can act only
288 # on SpiBase and here we compute the minimum allowed new_offset.
289 f
.seek(llut_start
+ 0x9)
290 lut_start_corr
= unpack("<H", f
.read(2))[0]
291 new_offset
= max(new_offset
,
292 lut_start_corr
- llut_start
- 0x40 + old_offset
)
293 new_offset
= ((new_offset
+ 0x1f) // 0x20) * 0x20
295 offset_diff
= new_offset
- old_offset
296 print("Relocating {} from {:#x} - {:#x} to {:#x} - {:#x}..."
297 .format(name
, old_offset
, old_offset
+ partition_size
,
298 new_offset
, new_offset
+ partition_size
))
300 print(" Adjusting FPT entry...")
301 f
.write_to(partition_header_offset
+ 0x8,
302 pack("<I", new_offset
))
307 if f
.read(4) == b
"LLUT":
308 print(" Adjusting LUT start offset...")
309 lut_offset
= llut_start
+ offset_diff
+ 0x40 - lut_start_corr
310 f
.write_to(llut_start
+ 0x0c, pack("<I", lut_offset
))
312 print(" Adjusting Huffman start offset...")
313 f
.seek(llut_start
+ 0x14)
314 old_huff_offset
= unpack("<I", f
.read(4))[0]
315 f
.write_to(llut_start
+ 0x14,
316 pack("<I", old_huff_offset
+ offset_diff
))
318 print(" Adjusting chunks offsets...")
319 f
.seek(llut_start
+ 0x4)
320 chunk_count
= unpack("<I", f
.read(4))[0]
321 f
.seek(llut_start
+ 0x40)
322 chunks
= bytearray(chunk_count
* 4)
324 for i
in range(0, chunk_count
* 4, 4):
325 if chunks
[i
+ 3] != 0x80:
327 pack("<I", unpack("<I", chunks
[i
:i
+ 3] +
328 b
"\x00")[0] + offset_diff
)[0:3]
329 f
.write_to(llut_start
+ 0x40, chunks
)
331 sys
.exit("Huffman modules present but no LLUT found!")
333 print(" No Huffman modules found")
335 print(" Moving data...")
336 partition_size
= min(partition_size
, me_end
- old_offset
)
337 f
.move_range(old_offset
, partition_size
, new_offset
, b
"\xff")
342 def check_and_remove_modules(f
, me_end
, offset
, min_offset
,
343 relocate
, keep_modules
):
345 f
.seek(offset
+ 0x20)
346 num_modules
= unpack("<I", f
.read(4))[0]
347 f
.seek(offset
+ 0x290)
351 if data
[0x0:0x4] == b
"$MME":
352 if data
[0x60:0x64] == b
"$MME" or num_modules
== 1:
353 mod_header_size
= 0x60
354 elif data
[0x80:0x84] == b
"$MME":
355 mod_header_size
= 0x80
357 if mod_header_size
!= 0:
358 f
.seek(offset
+ 0x290)
359 data
= f
.read(mod_header_size
* num_modules
)
360 mod_headers
= [data
[i
* mod_header_size
:(i
+ 1) * mod_header_size
]
361 for i
in range(0, num_modules
)]
363 if all(hdr
.startswith(b
"$MME") for hdr
in mod_headers
):
364 if args
.keep_modules
:
365 end_addr
= offset
+ ftpr_length
367 end_addr
= remove_modules(f
, mod_headers
, offset
, me_end
)
370 new_offset
= relocate_partition(f
, me_end
, 0x30, min_offset
,
372 end_addr
+= new_offset
- offset
375 return end_addr
, offset
378 print("Found less modules than expected in the FTPR "
379 "partition; skipping modules removal")
381 print("Can't find the module header size; skipping "
387 def check_and_remove_modules_me11(f
, me_end
, partition_offset
,
388 partition_length
, min_offset
, relocate
,
391 comp_str
= ("LZMA/uncomp.", "Huffman")
394 end_data
= partition_offset
+ partition_length
398 f
.seek(partition_offset
+ 0x4)
399 module_count
= unpack("<I", f
.read(4))[0]
402 modules
.append(("end", partition_length
, 0))
404 f
.seek(partition_offset
+ 0x10)
405 for i
in range(0, module_count
):
407 name
= data
[0x0:0xc].rstrip(b
"\x00").decode("ascii")
408 offset_block
= unpack("<I", data
[0xc:0x10])[0]
409 offset
= offset_block
& 0x01ffffff
410 comp_type
= (offset_block
& 0x02000000) >> 25
412 modules
.append((name
, offset
, comp_type
))
414 modules
.sort(key
=lambda x
: x
[1])
416 for i
in range(0, module_count
):
418 offset
= partition_offset
+ modules
[i
][1]
419 end
= partition_offset
+ modules
[i
+ 1][1]
422 if name
.endswith(".man") or name
.endswith(".met"):
423 compression
= "uncompressed"
425 compression
= comp_str
[modules
[i
][2]]
427 print(" {:<12} ({:<12}, 0x{:06x} - 0x{:06x}): "
428 .format(name
, compression
, offset
, end
), end
="")
430 if name
.endswith(".man"):
431 print("NOT removed, partition manif.")
432 elif name
.endswith(".met"):
433 print("NOT removed, module metadata")
434 elif any(name
.startswith(m
) for m
in unremovable_modules_me11
):
435 print("NOT removed, essential")
438 f
.fill_range(offset
, min(end
, me_end
), b
"\xff")
442 end_data
= max(end_data
, end
)
445 new_offset
= relocate_partition(f
, me_end
, 0x30, min_offset
, [])
446 end_data
+= new_offset
- partition_offset
447 partition_offset
= new_offset
449 return end_data
, partition_offset
452 def check_mn2_tag(f
, offset
):
453 f
.seek(offset
+ 0x1c)
456 sys
.exit("Wrong FTPR manifest tag ({}), this image may be corrupted"
460 def flreg_to_start_end(flreg
):
461 return (flreg
& 0x7fff) << 12, (flreg
>> 4 & 0x7fff000 |
0xfff) + 1
464 def start_end_to_flreg(start
, end
):
465 return (start
& 0x7fff000) >> 12 |
((end
- 1) & 0x7fff000) << 4
468 if __name__
== "__main__":
469 parser
= argparse
.ArgumentParser(description
="Tool to remove as much code "
470 "as possible from Intel ME/TXE firmware "
472 softdis
= parser
.add_mutually_exclusive_group()
473 bw_list
= parser
.add_mutually_exclusive_group()
475 parser
.add_argument("-v", "--version", action
="version",
476 version
="%(prog)s 1.2")
478 parser
.add_argument("file", help="ME/TXE image or full dump")
479 parser
.add_argument("-O", "--output", metavar
='output_file', help="save "
480 "the modified image in a separate file, instead of "
481 "modifying the original file")
482 softdis
.add_argument("-S", "--soft-disable", help="in addition to the "
483 "usual operations on the ME/TXE firmware, set the "
484 "MeAltDisable bit or the HAP bit to ask Intel ME/TXE "
485 "to disable itself after the hardware initialization "
486 "(requires a full dump)", action
="store_true")
487 softdis
.add_argument("-s", "--soft-disable-only", help="instead of the "
488 "usual operations on the ME/TXE firmware, just set "
489 "the MeAltDisable bit or the HAP bit to ask Intel "
490 "ME/TXE to disable itself after the hardware "
491 "initialization (requires a full dump)",
493 parser
.add_argument("-r", "--relocate", help="relocate the FTPR partition "
494 "to the top of the ME region to save even more space",
496 parser
.add_argument("-t", "--truncate", help="truncate the empty part of "
497 "the firmware (requires a separated ME/TXE image or "
498 "--extract-me)", action
="store_true")
499 parser
.add_argument("-k", "--keep-modules", help="don't remove the FTPR "
500 "modules, even when possible", action
="store_true")
501 bw_list
.add_argument("-w", "--whitelist", metavar
="whitelist",
502 help="Comma separated list of additional partitions "
503 "to keep in the final image. This can be used to "
504 "specify the MFS partition for example, which stores "
505 "PCIe and clock settings.")
506 bw_list
.add_argument("-b", "--blacklist", metavar
="blacklist",
507 help="Comma separated list of partitions to remove "
508 "from the image. This option overrides the default "
510 parser
.add_argument("-d", "--descriptor", help="remove the ME/TXE "
511 "Read/Write permissions to the other regions on the "
512 "flash from the Intel Flash Descriptor (requires a "
513 "full dump)", action
="store_true")
514 parser
.add_argument("-D", "--extract-descriptor",
515 metavar
='output_descriptor', help="extract the flash "
516 "descriptor from a full dump; when used with "
517 "--truncate save a descriptor with adjusted regions "
519 parser
.add_argument("-M", "--extract-me", metavar
='output_me_image',
520 help="extract the ME firmware from a full dump; when "
521 "used with --truncate save a truncated ME/TXE image")
522 parser
.add_argument("-c", "--check", help="verify the integrity of the "
523 "fundamental parts of the firmware and exit",
526 args
= parser
.parse_args()
528 if args
.check
and (args
.soft_disable_only
or args
.soft_disable
or
529 args
.relocate
or args
.descriptor
or args
.truncate
or args
.output
):
530 sys
.exit("-c can't be used with -S, -s, -r, -d, -t or -O")
532 if args
.soft_disable_only
and (args
.relocate
or args
.truncate
):
533 sys
.exit("-s can't be used with -r or -t")
535 if (args
.whitelist
or args
.blacklist
) and args
.relocate
:
536 sys
.exit("Relocation is not yet supported with custom whitelist or "
539 f
= open(args
.file, "rb" if args
.check
or args
.output
else "r+b")
544 print("ME/TXE image detected")
546 if args
.descriptor
or args
.extract_descriptor
or args
.extract_me
or \
547 args
.soft_disable
or args
.soft_disable_only
:
548 sys
.exit("-d, -D, -M, -S and -s require a full dump")
553 mef
= RegionFile(f
, me_start
, me_end
)
555 elif magic
== b
"\x5a\xa5\xf0\x0f":
556 print("Full image detected")
558 if args
.truncate
and not args
.extract_me
:
559 sys
.exit("-t requires a separated ME/TXE image (or --extract-me)")
562 flmap0
, flmap1
= unpack("<II", f
.read(8))
563 frba
= flmap0
>> 12 & 0xff0
564 fmba
= (flmap1
& 0xff) << 4
565 fpsba
= flmap1
>> 12 & 0xff0
568 flreg
= unpack("<III", f
.read(12))
570 fd_start
, fd_end
= flreg_to_start_end(flreg
[0])
571 bios_start
, bios_end
= flreg_to_start_end(flreg
[1])
572 me_start
, me_end
= flreg_to_start_end(flreg
[2])
574 if me_start
>= me_end
:
575 sys
.exit("The ME/TXE region in this image has been disabled")
577 mef
= RegionFile(f
, me_start
, me_end
)
580 if mef
.read(4) != b
"$FPT":
581 sys
.exit("The ME/TXE region is corrupted or missing")
583 print("The ME/TXE region goes from {:#x} to {:#x}"
584 .format(me_start
, me_end
))
586 sys
.exit("Unknown image")
590 print("Found FPT header at {:#x}".format(mef
.region_start
+ 0x10))
593 entries
= unpack("<I", mef
.read(4))[0]
594 print("Found {} partition(s)".format(entries
))
597 partitions
= mef
.read(entries
* 0x20)
601 for i
in range(entries
):
602 if partitions
[i
* 0x20:(i
* 0x20) + 4] == b
"FTPR":
603 ftpr_header
= partitions
[i
* 0x20:(i
+ 1) * 0x20]
606 if ftpr_header
== b
"":
607 sys
.exit("FTPR header not found, this image doesn't seem to be valid")
609 ftpr_offset
, ftpr_length
= unpack("<II", ftpr_header
[0x08:0x10])
610 print("Found FTPR header: FTPR partition spans from {:#x} to {:#x}"
611 .format(ftpr_offset
, ftpr_offset
+ ftpr_length
))
613 mef
.seek(ftpr_offset
)
614 if mef
.read(4) == b
"$CPD":
616 num_entries
= unpack("<I", mef
.read(4))[0]
618 mef
.seek(ftpr_offset
+ 0x10)
621 for i
in range(0, num_entries
):
622 data
= mef
.read(0x18)
623 name
= data
[0x0:0xc].rstrip(b
"\x00").decode("ascii")
624 offset
= unpack("<I", data
[0xc:0xf] + b
"\x00")[0]
626 if name
== "FTPR.man":
627 ftpr_mn2_offset
= offset
630 if ftpr_mn2_offset
>= 0:
631 check_mn2_tag(mef
, ftpr_offset
+ ftpr_mn2_offset
)
632 print("Found FTPR manifest at {:#x}"
633 .format(ftpr_offset
+ ftpr_mn2_offset
))
635 sys
.exit("Can't find the manifest of the FTPR partition")
638 check_mn2_tag(mef
, ftpr_offset
)
642 mef
.seek(ftpr_offset
+ ftpr_mn2_offset
+ 0x24)
643 version
= unpack("<HHHH", mef
.read(0x08))
644 print("ME/TXE firmware version {}"
645 .format('.'.join(str(i
) for i
in version
)))
647 mef
.seek(ftpr_offset
+ ftpr_mn2_offset
+ 0x80)
648 pubkey_md5
= hashlib
.md5(mef
.read(0x104)).hexdigest()
650 if pubkey_md5
in pubkeys_md5
:
651 variant
, pubkey_versions
= pubkeys_md5
[pubkey_md5
]
652 print("Public key match: Intel {}, firmware versions {}"
653 .format(variant
, ", ".join(pubkey_versions
)))
659 print("WARNING Unknown public key {}\n"
660 " Assuming Intel {}\n"
661 " Please report this warning to the project's maintainer!"
662 .format(pubkey_md5
, variant
))
664 if not args
.check
and args
.output
:
666 shutil
.copy(args
.file, args
.output
)
667 f
= open(args
.output
, "r+b")
669 mef
= RegionFile(f
, me_start
, me_end
)
672 fdf
= RegionFile(f
, fd_start
, fd_end
)
676 pchstrp0
= unpack("<I", fdf
.read(4))[0]
677 print("The HAP bit is " +
678 ("SET" if pchstrp0
& 1 << 16 else "NOT SET"))
680 fdf
.seek(fpsba
+ 0x28)
681 pchstrp10
= unpack("<I", fdf
.read(4))[0]
682 print("The AltMeDisable bit is " +
683 ("SET" if pchstrp10
& 1 << 7 else "NOT SET"))
685 # ME 6 Ignition: wipe everything
687 if not args
.check
and not args
.soft_disable_only
and \
688 variant
== "ME" and version
[0] == 6:
689 mef
.seek(ftpr_offset
+ 0x20)
690 num_modules
= unpack("<I", mef
.read(4))[0]
691 mef
.seek(ftpr_offset
+ 0x290 + (num_modules
+ 1) * 0x60)
694 if data
[0x0:0x4] == b
"$SKU" and data
[0x8:0xc] == b
"\x00\x00\x00\x00":
695 print("ME 6 Ignition firmware detected, removing everything...")
696 mef
.fill_all(b
"\xff")
700 if not args
.soft_disable_only
and not me6_ignition
:
701 print("Reading partitions list...")
702 unremovable_part_fpt
= b
""
707 whitelist
+= unremovable_partitions
710 blacklist
= args
.blacklist
.split(",")
712 whitelist
+= args
.whitelist
.split(",")
714 for i
in range(entries
):
715 partition
= partitions
[i
* 0x20:(i
+ 1) * 0x20]
716 flags
= unpack("<I", partition
[0x1c:0x20])[0]
720 partition
[0x0:0x4].rstrip(b
"\x00").decode("ascii")
721 except UnicodeDecodeError:
724 part_start
, part_length
= unpack("<II", partition
[0x08:0x10])
726 # ME 6: the last partition has 0xffffffff as size
727 if variant
== "ME" and version
[0] == 6 and \
728 i
== entries
- 1 and part_length
== 0xffffffff:
729 part_length
= me_end
- me_start
- part_start
731 part_end
= part_start
+ part_length
733 if flags
& 0x7f == 2:
734 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
736 .format(part_name
, "NVRAM partition, no data",
738 elif part_start
== 0 or part_length
== 0 or part_end
> me_end
:
739 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
741 .format(part_name
, "no data here", part_length
))
743 print(" {:<4} (0x{:08x} - 0x{:09x}, 0x{:08x} total bytes): "
744 .format(part_name
, part_start
, part_end
, part_length
),
746 if part_name
in whitelist
or (blacklist
and
747 part_name
not in blacklist
):
748 unremovable_part_fpt
+= partition
749 if part_name
!= "FTPR":
750 extra_part_end
= max(extra_part_end
, part_end
)
753 mef
.fill_range(part_start
, part_end
, b
"\xff")
756 print("Removing partition entries in FPT...")
757 mef
.write_to(0x30, unremovable_part_fpt
)
759 pack("<I", len(unremovable_part_fpt
) // 0x20))
761 mef
.fill_range(0x30 + len(unremovable_part_fpt
),
762 0x30 + len(partitions
), b
"\xff")
764 if (not blacklist
and "EFFS" not in whitelist
) or \
766 print("Removing EFFS presence flag...")
768 flags
= unpack("<I", mef
.read(4))[0]
769 flags
&= ~
(0x00000001)
770 mef
.write_to(0x24, pack("<I", flags
))
774 header
= bytearray(mef
.read(0x20))
778 header
= bytearray(mef
.read(0x30))
780 checksum
= (0x100 - sum(header
) & 0xff) & 0xff
782 print("Correcting checksum (0x{:02x})...".format(checksum
))
783 # The checksum is just the two's complement of the sum of the first
784 # 0x30 bytes in ME < 11 or bytes 0x10:0x30 in ME >= 11 (except for
785 # 0x1b, the checksum itself). In other words, the sum of those
786 # bytes must be always 0x00.
787 mef
.write_to(0x1b, pack("B", checksum
))
789 print("Reading FTPR modules list...")
791 end_addr
, ftpr_offset
= \
792 check_and_remove_modules_me11(mef
, me_end
,
793 ftpr_offset
, ftpr_length
,
798 end_addr
, ftpr_offset
= \
799 check_and_remove_modules(mef
, me_end
, ftpr_offset
,
800 min_ftpr_offset
, args
.relocate
,
804 end_addr
= max(end_addr
, extra_part_end
)
805 end_addr
= (end_addr
// 0x1000 + 1) * 0x1000
806 end_addr
+= spared_blocks
* 0x1000
808 print("The ME minimum size should be {0} bytes "
809 "({0:#x} bytes)".format(end_addr
))
812 print("The ME region can be reduced up to:\n"
814 .format(me_start
, me_start
+ end_addr
- 1))
816 print("Truncating file at {:#x}...".format(end_addr
))
819 if args
.soft_disable
or args
.soft_disable_only
:
821 print("Setting the HAP bit in PCHSTRP0 to disable Intel ME...")
822 pchstrp0 |
= (1 << 16)
823 fdf
.write_to(fpsba
, pack("<I", pchstrp0
))
825 print("Setting the AltMeDisable bit in PCHSTRP10 to disable "
827 pchstrp10 |
= (1 << 7)
828 fdf
.write_to(fpsba
+ 0x28, pack("<I", pchstrp10
))
831 print("Removing ME/TXE R/W access to the other flash regions...")
836 flmstr2
= (unpack("<I", fdf
.read(4))[0] |
0x04040000) & 0x0404ffff
838 fdf
.write_to(fmba
+ 0x4, pack("<I", flmstr2
))
840 if args
.extract_descriptor
:
842 print("Extracting the descriptor to \"{}\"..."
843 .format(args
.extract_descriptor
))
844 fdf_copy
= fdf
.save(args
.extract_descriptor
, fd_end
- fd_start
)
846 if bios_start
== me_end
:
847 print("Modifying the regions of the extracted descriptor...")
848 print(" {:08x}:{:08x} me --> {:08x}:{:08x} me"
849 .format(me_start
, me_end
- 1,
850 me_start
, me_start
+ end_addr
- 1))
851 print(" {:08x}:{:08x} bios --> {:08x}:{:08x} bios"
852 .format(bios_start
, bios_end
- 1,
853 me_start
+ end_addr
, bios_end
- 1))
855 flreg1
= start_end_to_flreg(me_start
+ end_addr
, bios_end
)
856 flreg2
= start_end_to_flreg(me_start
, me_start
+ end_addr
)
858 fdf_copy
.seek(frba
+ 0x4)
859 fdf_copy
.write(pack("<II", flreg1
, flreg2
))
861 print("\nWARNING:\n The start address of the BIOS region "
862 "isn't equal to the end address of the ME\n region: if "
863 "you want to recover the space from the ME region you "
864 "have to\n manually modify the descriptor.\n")
866 print("Extracting the descriptor to \"{}\"..."
867 .format(args
.extract_descriptor
))
868 fdf_copy
= fdf
.save(args
.extract_descriptor
, fd_end
- fd_start
)
874 print("Extracting and truncating the ME image to \"{}\"..."
875 .format(args
.extract_me
))
876 mef_copy
= mef
.save(args
.extract_me
, end_addr
)
878 print("Extracting the ME image to \"{}\"..."
879 .format(args
.extract_me
))
880 mef_copy
= mef
.save(args
.extract_me
, me_end
- me_start
)
883 print("Checking the FTPR RSA signature of the extracted ME "
885 print_check_partition_signature(mef_copy
,
886 ftpr_offset
+ ftpr_mn2_offset
)
890 print("Checking the FTPR RSA signature... ", end
="")
891 print_check_partition_signature(mef
, ftpr_offset
+ ftpr_mn2_offset
)
896 print("Done! Good luck!")