diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/me_cleaner/README.md | 58 | ||||
-rwxr-xr-x | util/me_cleaner/me_cleaner.py | 474 |
2 files changed, 378 insertions, 154 deletions
diff --git a/util/me_cleaner/README.md b/util/me_cleaner/README.md index 0ca3d32bf9..72c15986df 100644 --- a/util/me_cleaner/README.md +++ b/util/me_cleaner/README.md @@ -1,26 +1,44 @@ -# ME cleaner +# me_cleaner -A cleaner for Intel ME/TXE images. +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 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 +be sure it is disabled is to remove its firmware from the flash chip. -This tools removes any unnecessary partition from an Intel ME/TXE firmware, reducing -its size and its ability to interact with the system. -It should work both with coreboot and with the factory firmware. +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 to reverse-engineer the ME firmware. -Currently this tool: - * Scans the FPT (partition table) and checks that everything is correct - * Removes any partition entry (except for FTPR) from FPT - * Removes any partition except for the fundamental one (FTPR) - * Removes the EFFS presence flag - * Corrects the FPT checksum - * Removes any non-essential LZMA or Huffman compressed module from the FTPR partition (pre-Skylake only) - * Relocates the remaining parts of the FTPR partition to the top of the ME region (pre-Skylake only) - * Checks the validity of the RSA signature of the FTPR partition +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 recovery mode. -Don't forget to power cycle your PC after flashing the modified ME/TXE image -(power off and power on, not just reboot). +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. -See the [current status](https://github.com/corna/me_cleaner/wiki/me_cleaner-status) -or [a more detailed description](https://github.com/corna/me_cleaner/wiki/How-does-it-work%3F) -of me_cleaner. +If you want to understand how me_cleaner works, you can read the ["How does it work?" page](https://github.com/corna/me_cleaner/wiki/How-does-it-work%3F). -Special thanks to Federico Amedeo Izzo for his help during the study of Intel ME. +If you want to apply me_cleaner on your platform I suggest you to read the +["How does it work?" page](https://github.com/corna/me_cleaner/wiki/How-does-it-work%3F) +and then follow the guide ["How to apply me_cleaner"](https://github.com/corna/me_cleaner/wiki/How-to-apply-me_cleaner). + +For pre-Skylake firmware (ME version < 11) this tool removes almost everything, +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. + +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 information about Intel ME and its firmware structure, +and Federico Amedeo Izzo, for its help during the study of Intel ME. diff --git a/util/me_cleaner/me_cleaner.py b/util/me_cleaner/me_cleaner.py index 8ca5498134..e8a89cc92c 100755 --- a/util/me_cleaner/me_cleaner.py +++ b/util/me_cleaner/me_cleaner.py @@ -25,14 +25,15 @@ 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): pass -class regionFile: +class RegionFile: def __init__(self, f, region_start, region_end): self.f = f self.region_start = region_start @@ -81,6 +82,13 @@ class regionFile: else: raise OutOfRegionException() + def save(self, filename, size): + self.f.seek(self.region_start) + copyf = open(filename, "w+b") + for i in range(0, size, 4096): + copyf.write(self.f.read(4096 if size - i >= 4096 else size - i)) + return copyf + def get_chunks_offsets(llut, me_start): chunk_count = unpack("<I", llut[0x04:0x08])[0] @@ -109,7 +117,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 @@ -211,6 +219,15 @@ def check_partition_signature(f, offset): return "{:#x}".format(decrypted_sig).endswith(sha256.hexdigest()) # FIXME +def print_check_partition_signature(f, offset): + if check_partition_signature(f, offset): + print("VALID") + else: + print("INVALID!!") + sys.exit("The FTPR partition signature is not valid. Is the input " + "ME/TXE image valid?") + + def relocate_partition(f, me_start, me_end, partition_header_offset, new_offset, mod_headers): @@ -226,7 +243,7 @@ def relocate_partition(f, me_start, me_end, partition_header_offset, llut_start = unpack("<I", mod_header[0x38:0x3C])[0] + old_offset break - if llut_start != 0: + if mod_headers and llut_start != 0: # Bytes 0x9:0xb of the LLUT (bytes 0x1:0x3 of the AddrBase) are added # to the SpiBase (bytes 0xc:0x10 of the LLUT) to compute the final # start of the LLUT. Since AddrBase is not modifiable, we can act only @@ -239,43 +256,45 @@ def relocate_partition(f, me_start, me_end, partition_header_offset, new_offset = ((new_offset + 0x1f) // 0x20) * 0x20 offset_diff = new_offset - old_offset - print("Relocating {} to {:#x} - {:#x}..." - .format(name, new_offset, new_offset + partition_size)) + print("Relocating {} from {:#x} - {:#x} to {:#x} - {:#x}..." + .format(name, old_offset, old_offset + partition_size, + new_offset, new_offset + partition_size)) print(" Adjusting FPT entry...") f.write_to(partition_header_offset + 0x8, pack("<I", new_offset - me_start)) - if llut_start != 0: - f.seek(llut_start) - if f.read(4) == b"LLUT": - print(" Adjusting LUT start offset...") - lut_offset = llut_start + offset_diff + 0x40 - \ - lut_start_corr - me_start - f.write_to(llut_start + 0x0c, pack("<I", lut_offset)) - - print(" Adjusting Huffman start offset...") - f.seek(llut_start + 0x14) - old_huff_offset = unpack("<I", f.read(4))[0] - f.write_to(llut_start + 0x14, - pack("<I", old_huff_offset + offset_diff)) - - print(" Adjusting chunks offsets...") - f.seek(llut_start + 0x4) - chunk_count = unpack("<I", f.read(4))[0] - f.seek(llut_start + 0x40) - chunks = bytearray(chunk_count * 4) - f.readinto(chunks) - for i in range(0, chunk_count * 4, 4): - if chunks[i + 3] != 0x80: - chunks[i:i + 3] = \ - pack("<I", unpack("<I", chunks[i:i + 3] + - b"\x00")[0] + offset_diff)[0:3] - f.write_to(llut_start + 0x40, chunks) + if mod_headers: + if llut_start != 0: + f.seek(llut_start) + if f.read(4) == b"LLUT": + print(" Adjusting LUT start offset...") + lut_offset = llut_start + offset_diff + 0x40 - \ + lut_start_corr - me_start + f.write_to(llut_start + 0x0c, pack("<I", lut_offset)) + + print(" Adjusting Huffman start offset...") + f.seek(llut_start + 0x14) + old_huff_offset = unpack("<I", f.read(4))[0] + f.write_to(llut_start + 0x14, + pack("<I", old_huff_offset + offset_diff)) + + print(" Adjusting chunks offsets...") + f.seek(llut_start + 0x4) + chunk_count = unpack("<I", f.read(4))[0] + f.seek(llut_start + 0x40) + chunks = bytearray(chunk_count * 4) + f.readinto(chunks) + for i in range(0, chunk_count * 4, 4): + if chunks[i + 3] != 0x80: + chunks[i:i + 3] = \ + pack("<I", unpack("<I", chunks[i:i + 3] + + b"\x00")[0] + offset_diff)[0:3] + f.write_to(llut_start + 0x40, chunks) + else: + sys.exit("Huffman modules present but no LLUT found!") else: - sys.exit("Huffman modules present but no LLUT found!") - else: - print(" No Huffman modules found") + print(" No Huffman modules found") print(" Moving data...") partition_size = min(partition_size, me_end - old_offset) @@ -284,66 +303,221 @@ def relocate_partition(f, me_start, me_end, partition_header_offset, return new_offset +def check_and_remove_modules(f, me_start, me_end, offset, min_offset, + relocate, keep_modules): + + f.seek(offset + 0x20) + num_modules = unpack("<I", f.read(4))[0] + f.seek(offset + 0x290) + data = f.read(0x84) + + module_header_size = 0 + if data[0x0:0x4] == b"$MME": + if data[0x60:0x64] == b"$MME" or num_modules == 1: + module_header_size = 0x60 + elif data[0x80:0x84] == b"$MME": + module_header_size = 0x80 + + if module_header_size != 0: + f.seek(offset + 0x290) + mod_headers = [f.read(module_header_size) + for i in range(0, num_modules)] + + if all(hdr.startswith(b"$MME") for hdr in mod_headers): + if args.keep_modules: + end_addr = offset + ftpr_lenght + else: + end_addr = remove_modules(f, mod_headers, + offset, me_end) + + if args.relocate: + new_offset = relocate_partition(f, me_start, me_end, + me_start + 0x30, + min_offset + me_start, + mod_headers) + end_addr += new_offset - offset + offset = new_offset + + return end_addr, offset + + 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") + + return -1, offset + + +def check_and_remove_modules_me11(f, me_start, me_end, partition_offset, + partition_lenght, min_offset, relocate, + keep_modules): + + comp_str = ("LZMA/uncomp.", "Huffman") + + if keep_modules: + end_data = partition_offset + partition_lenght + else: + end_data = 0 + + f.seek(partition_offset + 0x4) + module_count = unpack("<I", f.read(4))[0] + + modules = [] + modules.append(("end", partition_lenght, 0)) + + f.seek(partition_offset + 0x10) + for i in range(0, module_count): + data = f.read(0x18) + name = data[0x0:0xc].rstrip(b"\x00").decode("ascii") + offset_block = unpack("<I", data[0xc:0x10])[0] + offset = offset_block & 0x01ffffff + comp_type = (offset_block & 0x02000000) >> 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, min(end, me_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)) + + +def flreg_to_start_end(flreg): + return (flreg & 0x7fff) << 12, (flreg >> 4 & 0x7fff000 | 0xfff) + 1 + + +def start_end_to_flreg(start, end): + return (start & 0x7fff000) >> 12 | ((end - 1) & 0x7fff000) << 4 + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Tool to remove as much code " - "as possible from Intel ME/TXE firmwares") + "as possible from Intel ME/TXE firmware " + "images") parser.add_argument("file", help="ME/TXE image or full dump") parser.add_argument("-O", "--output", help="save the modified image in a " "separate file, instead of modifying the original " "file") parser.add_argument("-r", "--relocate", help="relocate the FTPR partition " - "to the top of the ME region", action="store_true") + "to the top of the ME region to save even more space", + action="store_true") parser.add_argument("-k", "--keep-modules", help="don't remove the FTPR " "modules, even when possible", action="store_true") parser.add_argument("-d", "--descriptor", help="remove the ME/TXE " "Read/Write permissions to the other regions on the " "flash from the Intel Flash Descriptor (requires a " "full dump)", action="store_true") + parser.add_argument("-t", "--truncate", help="truncate the empty part of " + "the firmware (requires a separated ME/TXE image or " + "--extract-me)", action="store_true") parser.add_argument("-c", "--check", help="verify the integrity of the " "fundamental parts of the firmware and exit", action="store_true") + parser.add_argument("-D", "--extract-descriptor", help="extract the " + "flash descriptor from a full dump; when used with " + "--truncate save a descriptor with adjusted regions " + "start and end") + parser.add_argument("-M", "--extract-me", help="extract the ME firmware " + "from a full dump; when used with --truncate save a " + "truncated ME/TXE image") + args = parser.parse_args() + if args.check: + if args.relocate: + sys.exit("-c and -r can't be used together") + elif args.truncate: + sys.exit("-c and -t can't be used together") + f = open(args.file, "rb" if args.check or args.output else "r+b") f.seek(0x10) magic = f.read(4) if magic == b"$FPT": print("ME/TXE image detected") - me_start = 0 - f.seek(0, 2) - me_end = f.tell() if args.descriptor: sys.exit("-d requires a full dump") + if args.extract_descriptor: + sys.exit("-D requires a full dump") + + if args.extract_me: + sys.exit("-M requires a full dump") + + me_start = 0 + f.seek(0, 2) + me_end = f.tell() + elif magic == b"\x5a\xa5\xf0\x0f": print("Full image detected") + + if args.truncate and not args.extract_me: + sys.exit("-t requires a separated ME/TXE image (or --extract-me)") + f.seek(0x14) flmap0, flmap1 = unpack("<II", f.read(8)) - nr = flmap0 >> 24 & 0x7 frba = flmap0 >> 12 & 0xff0 fmba = (flmap1 & 0xff) << 4 - if nr >= 2: - f.seek(frba) - flreg0, flreg1, flreg2 = unpack("<III", f.read(12)) - fd_start = (flreg0 & 0x1fff) << 12 - fd_end = flreg0 >> 4 & 0x1fff000 | 0xfff + 1 - me_start = (flreg2 & 0x1fff) << 12 - me_end = flreg2 >> 4 & 0x1fff000 | 0xfff + 1 - - if me_start >= me_end: - sys.exit("The ME/TXE region in this image has been disabled") - - f.seek(me_start + 0x10) - if f.read(4) != b"$FPT": - sys.exit("The ME/TXE region is corrupted or missing") - - print("The ME/TXE region goes from {:#x} to {:#x}" - .format(me_start, me_end)) - else: - sys.exit("This image does not contains a ME/TXE firmware NR = {})" - .format(nr)) + + f.seek(frba) + flreg = unpack("<III", f.read(12)) + + fd_start, fd_end = flreg_to_start_end(flreg[0]) + bios_start, bios_end = flreg_to_start_end(flreg[1]) + me_start, me_end = flreg_to_start_end(flreg[2]) + + if me_start >= me_end: + sys.exit("The ME/TXE region in this image has been disabled") + + f.seek(me_start + 0x10) + if f.read(4) != b"$FPT": + sys.exit("The ME/TXE region is corrupted or missing") + + print("The ME/TXE region goes from {:#x} to {:#x}" + .format(me_start, me_end)) else: sys.exit("Unknown image") @@ -378,8 +552,28 @@ if __name__ == "__main__": if f.read(4) == b"$CPD": me11 = True num_entries = unpack("<I", f.read(4))[0] - ftpr_mn2_offset = 0x10 + num_entries * 0x18 + + f.seek(ftpr_offset + 0x10) + ftpr_mn2_offset = -1 + + for i in range(0, num_entries): + data = f.read(0x18) + name = data[0x0:0xc].rstrip(b"\x00").decode("ascii") + offset = unpack("<I", data[0xc:0xf] + b"\x00")[0] + + if name == "FTPR.man": + ftpr_mn2_offset = offset + break + + if ftpr_mn2_offset >= 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 @@ -394,7 +588,10 @@ if __name__ == "__main__": shutil.copy(args.file, args.output) f = open(args.output, "r+b") - mef = regionFile(f, me_start, me_end) + mef = RegionFile(f, me_start, me_end) + + if args.descriptor or args.extract_descriptor: + fdf = RegionFile(f, fd_start, fd_end) print("Removing extra partitions...") mef.fill_range(me_start + 0x30, ftpr_offset, b"\xff") @@ -410,18 +607,15 @@ if __name__ == "__main__": flags &= ~(0x00000001) mef.write_to(me_start + 0x24, pack("<I", flags)) - if args.descriptor: - print("Removing ME/TXE R/W access to the other flash regions...") - fdf = regionFile(f, fd_start, fd_end) - fdf.write_to(fmba + 0x4, pack("<I", 0x04040000)) - if me11: mef.seek(me_start + 0x10) header = bytearray(mef.read(0x20)) + header[0x0b] = 0x00 else: mef.seek(me_start) header = bytearray(mef.read(0x30)) - checksum = (0x100 - (sum(header) - header[0x1b]) & 0xff) & 0xff + header[0x1b] = 0x00 + checksum = (0x100 - sum(header) & 0xff) & 0xff print("Correcting checksum (0x{:02x})...".format(checksum)) # The checksum is just the two's complement of the sum of the first @@ -430,76 +624,88 @@ 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("<I", mef.read(4))[0] - mef.seek(ftpr_offset + 0x290) - data = mef.read(0x84) - - module_header_size = 0 - if data[0x0:0x4] == b"$MME": - if data[0x60:0x64] == b"$MME" or num_modules == 1: - module_header_size = 0x60 - elif data[0x80:0x84] == b"$MME": - module_header_size = 0x80 - - if module_header_size != 0: - mef.seek(ftpr_offset + 0x290) - mod_headers = [mef.read(module_header_size) - for i in range(0, num_modules)] - - if all(hdr.startswith(b"$MME") for hdr in mod_headers): - if args.keep_modules: - end_addr = ftpr_offset + ftpr_lenght - else: - end_addr = remove_modules(mef, mod_headers, - ftpr_offset, me_end) - - if args.relocate: - new_ftpr_offset = relocate_partition(mef, - me_start, me_end, - me_start + 0x30, - min_ftpr_offset + me_start, - mod_headers) - end_addr += new_ftpr_offset - ftpr_offset - ftpr_offset = new_ftpr_offset - - 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)) - 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") + 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: + 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) + + if args.descriptor: + print("Removing ME/TXE R/W access to the other flash regions...") + fdf.write_to(fmba + 0x4, pack("<I", 0x04040000)) + + if args.extract_descriptor: + if args.truncate: + print("Extracting the descriptor to \"{}\"..." + .format(args.extract_descriptor)) + fdf_copy = fdf.save(args.extract_descriptor, fd_end - fd_start) + + if bios_start == me_end: + print("Modifying the regions of the extracted descriptor...") + print(" {:08x}:{:08x} me --> {:08x}:{:08x} me" + .format(me_start, me_end - 1, me_start, end_addr - 1)) + print(" {:08x}:{:08x} bios --> {:08x}:{:08x} bios" + .format(bios_start, bios_end - 1, end_addr, bios_end - 1)) + + flreg1 = start_end_to_flreg(end_addr, bios_end) + flreg2 = start_end_to_flreg(me_start, end_addr) + + fdf_copy.seek(frba + 0x4) + fdf_copy.write(pack("<II", flreg1, flreg2)) else: - print("Wrong FTPR partition tag ({}); skipping modules removal" - .format(tag)) + print("\nWARNING:\n The start address of the BIOS region " + "isn't equal to the end address of the ME\n region: if " + "you want to recover the space from the ME region you " + "have to\n manually modify the descriptor.\n") + + fdf_copy.close() else: - print("Modules removal in ME v11 or greater is not yet supported") + print("Extracting the descriptor to \"{}\"..." + .format(args.extract_descriptor)) + fdf.save(args.extract_descriptor, fd_end - fd_start).close() + + if args.extract_me: + if args.truncate: + print("Extracting and truncating the ME image to \"{}\"..." + .format(args.extract_me)) + mef_copy = mef.save(args.extract_me, end_addr - me_start) + else: + print("Extracting the ME image to \"{}\"..." + .format(args.extract_me)) + mef_copy = mef.save(args.extract_me, me_end - me_start) - sys.stdout.write("Checking FTPR RSA signature... ") - if check_partition_signature(f, ftpr_offset + ftpr_mn2_offset): - print("VALID") - else: - print("INVALID!!") - sys.exit("The FTPR partition signature is not valid. Is the input " - "ME/TXE image valid?") + sys.stdout.write("Checking the FTPR RSA signature of the extracted ME " + "image... ") + print_check_partition_signature(mef_copy, ftpr_offset + + ftpr_mn2_offset - me_start) + mef_copy.close() + + sys.stdout.write("Checking the FTPR RSA signature... ") + print_check_partition_signature(f, ftpr_offset + ftpr_mn2_offset) f.close() if not args.check: print("Done! Good luck!") + |