From f4e3d1401efa79a3d40190c8a86293b601a0e87e Mon Sep 17 00:00:00 2001 From: Nicola Corna Date: Fri, 16 Jun 2017 13:08:56 +0200 Subject: [PATCH] Add full support for Skylake (ME 11) and following This commit has been widely tested on an MSI H110M (Sunrise Point) with an i3-6300T, on two different ME firmware: * 11.6.1.1142 CON (2.0 MB, no AMT) * 11.6.1.1142 COR (7.1 MB, AMT) In particular: * The only fundamental FTPR modules seems to be rbe, kernel, syslib and bup. Incidentally, on CON images, these modules are the only ones Huffman-compressed. Removing any of these modules inhibits the correct powering on of the PC. * Now that the Huffman modules are not mixed together in a single Huffman stream, removing them is trivial and can be done in the same way as the LZMA/uncompressed modules. * For the same reason, as there isn't a LLUT header anymore, the partitions can be freely moved without any change in the content of the partition, thus the relocation option has been added. * The truncation information has been adjusted, like in the older ME versions. However, the correct functioning of a PC with a truncated ME region hasn't been tested yet. With this commit me_cleaner is able to remove the majority of the FTPR modules, going from the original code size of 2.0 MB (no AMT) or 6-7 MB (AMT) to ~300 kB of compressed code. --- README.md | 16 +-- me_cleaner.py | 300 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 212 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index d16d520..a8bee6d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Intel ME is a coprocessor integrated in all post-2006 Intel boards, for which this [Libreboot page](https://libreboot.org/faq.html#intelme) has an excellent description. The main component of Intel ME is Intel AMT, and I suggest you to read [this Wikipedia page](https://en.wikipedia.org/wiki/Intel_Active_Management_Technology) -for more informations about it. In short, Intel ME is an unremovable environment +for more information about it. In short, Intel ME is an irremovable environment with an obscure signed proprietary firmware, with full network and memory access, which poses a serious security threat. Even when disabled from the BIOS settings, Intel ME is active: the only way to @@ -12,12 +12,12 @@ be sure it is disabled is to remove its firmware from the flash chip. Before Nehalem (ME version 6, 2008/2009) the ME firmware could be removed completely from the flash chip by setting a couple of bits inside the flash -descriptor, without the need of reverse-engineer the ME firmware. +descriptor, without the need to reverse-engineer the ME firmware. Starting from Nehalem the Intel ME firmware can't be removed anymore: without a valid firmware the PC shuts off forcefully after 30 minutes. This project is an attempt to remove as much code as possible from such firmware without falling -into the 30 minutes window mode. +into the 30 minutes recovery mode. me_cleaner currently works on most architectures, see [me_cleaner status](https://github.com/corna/me_cleaner/wiki/me_cleaner-status) (or [its discussion](https://github.com/corna/me_cleaner/issues/3)) for more info about them. me_cleaner works also on the TXE and SPS firmware. @@ -33,10 +33,12 @@ leaving only the two fundamental modules needed for the correct boot, ROMP and BUP. The code size is reduced from 1.5 MB (non-AMT firmware) or 5 MB (AMT firmware) to ~90 kB of compressed code. -For Skylake and the later architectures (ME version >= 11), since the internal -structure of the partitions is not yet known, the FTPR partition is left intact. -The code size is reduced from 1.5 MB/5 MB to ~650 kB of compressed code. +Starting from Skylake (ME version >= 11) the ME subsystem and the firmware +structure have changed, requiring substantial changes in me_cleaner. +The fundamental modules required for the correct boot are now four (rbe, kernel, +syslib and bup) and the minimum code size is ~300 kB of compressed code (from +the 2 MB of the non-AMT firmware and the 7 MB of the AMT one). This project is based on the work of the community; in particular I thank Igor -Skochinsky, for the core informations about Intel ME and its firmware structure, +Skochinsky, for the core information about Intel ME and its firmware structure, and Federico Amedeo Izzo, for its help during the study of Intel ME. diff --git a/me_cleaner.py b/me_cleaner.py index 6c0a6d5..7c8eed3 100755 --- a/me_cleaner.py +++ b/me_cleaner.py @@ -25,7 +25,8 @@ from struct import pack, unpack min_ftpr_offset = 0x400 spared_blocks = 4 -unremovable_modules = ("BUP", "ROMP") +unremovable_modules = ("ROMP", "BUP") +unremovable_modules_me11 = ("rbe", "kernel", "syslib", "bup") class OutOfRegionException(Exception): @@ -109,7 +110,7 @@ def get_chunks_offsets(llut, me_start): def remove_modules(f, mod_headers, ftpr_offset, me_end): - comp_str = ("Uncomp.", "Huffman", "LZMA") + comp_str = ("uncomp.", "Huffman", "LZMA") unremovable_huff_chunks = [] chunks_offsets = [] base = 0 @@ -226,7 +227,7 @@ def relocate_partition(f, me_start, me_end, partition_header_offset, llut_start = unpack("> 25 + + modules.append((name, offset, comp_type)) + + modules.sort(key=lambda x: x[1]) + + for i in range(0, module_count): + name = modules[i][0] + offset = partition_offset + modules[i][1] + end = partition_offset + modules[i + 1][1] + removed = False + + if name.endswith(".man") or name.endswith(".met"): + compression = "uncompressed" + else: + compression = comp_str[modules[i][2]] + + sys.stdout.write(" {:<12} ({:<12}, 0x{:06x} - 0x{:06x}): " + .format(name, compression, offset, end)) + + if name.endswith(".man"): + print("NOT removed, partition manif.") + elif name.endswith(".met"): + print("NOT removed, module metadata") + elif any(name.startswith(m) for m in unremovable_modules_me11): + print("NOT removed, essential") + else: + removed = True + f.fill_range(offset, end, b"\xff") + print("removed") + + if not removed: + end_data = max(end_data, end) + + if relocate: + new_offset = relocate_partition(f, me_start, me_end, me_start + 0x30, + min_offset + me_start, []) + end_data += new_offset - partition_offset + partition_offset = new_offset + + return end_data, partition_offset + + +def check_mn2_tag(f, offset): + f.seek(offset + 0x1c) + tag = f.read(4) + if tag != b"$MN2": + sys.exit("Wrong FTPR manifest tag ({}), this image may be corrupted" + .format(tag)) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Tool to remove as much code " "as possible from Intel ME/TXE firmwares") @@ -381,8 +505,28 @@ if __name__ == "__main__": if f.read(4) == b"$CPD": me11 = True num_entries = unpack("= 0: + check_mn2_tag(f, ftpr_offset + ftpr_mn2_offset) + print("Found FTPR manifest at {:#x}" + .format(ftpr_offset + ftpr_mn2_offset)) + else: + sys.exit("Can't find the manifest of the FTPR partition") + else: + check_mn2_tag(f, ftpr_offset) me11 = False ftpr_mn2_offset = 0 @@ -435,71 +579,32 @@ if __name__ == "__main__": # must be always 0x00. mef.write_to(me_start + 0x1b, pack("B", checksum)) - if not me11: - print("Reading FTPR modules list...") - mef.seek(ftpr_offset + 0x1c) - tag = mef.read(4) - - if tag == b"$MN2": - mef.seek(ftpr_offset + 0x20) - num_modules = unpack(" 0: - print("The ME region can be reduced up to:\n" - " {:08x}:{:08x} me" - .format(me_start, end_addr - 1)) - elif args.truncate: - print("Truncating file at {:#x}..." - .format(end_addr)) - f.truncate(end_addr) - - else: - print("Found less modules than expected in the FTPR " - "partition; skipping modules removal") - else: - print("Can't find the module header size; skipping " - "modules removal") - else: - print("Wrong FTPR partition tag ({}); skipping modules removal" - .format(tag)) + print("Reading FTPR modules list...") + if me11: + end_addr, ftpr_offset = \ + check_and_remove_modules_me11(mef, me_start, me_end, + ftpr_offset, ftpr_lenght, + min_ftpr_offset, args.relocate, + args.keep_modules) else: - print("Modules removal in ME v11 or greater is not yet supported") + end_addr, ftpr_offset = \ + check_and_remove_modules(mef, me_start, me_end, ftpr_offset, + min_ftpr_offset, args.relocate, + args.keep_modules) + + if end_addr > 0: + end_addr = (end_addr // 0x1000 + 1) * 0x1000 + end_addr += spared_blocks * 0x1000 + + print("The ME minimum size should be {0} bytes " + "({0:#x} bytes)".format(end_addr - me_start)) + + if me_start > 0: + print("The ME region can be reduced up to:\n" + " {:08x}:{:08x} me".format(me_start, end_addr - 1)) + elif args.truncate: + print("Truncating file at {:#x}...".format(end_addr)) + f.truncate(end_addr) sys.stdout.write("Checking FTPR RSA signature... ") if check_partition_signature(f, ftpr_offset + ftpr_mn2_offset): @@ -513,3 +618,4 @@ if __name__ == "__main__": if not args.check: print("Done! Good luck!") +