Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | # me_cleaner - Tool for partial deblobbing of Intel ME/TXE firmware images |
| 4 | # Copyright (C) 2016, 2017 Nicola Corna <nicola@corna.info> |
| 5 | # |
| 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. |
| 10 | # |
| 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. |
| 15 | # |
| 16 | |
| 17 | import sys |
| 18 | import itertools |
| 19 | import binascii |
| 20 | import hashlib |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 21 | import argparse |
| 22 | import shutil |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 23 | from struct import pack, unpack |
| 24 | |
| 25 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 26 | min_ftpr_offset = 0x400 |
| 27 | spared_blocks = 4 |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 28 | unremovable_modules = ("BUP", "ROMP") |
| 29 | |
| 30 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 31 | class OutOfRegionException(Exception): |
| 32 | pass |
| 33 | |
| 34 | |
| 35 | class regionFile: |
| 36 | def __init__(self, f, region_start, region_end): |
| 37 | self.f = f |
| 38 | self.region_start = region_start |
| 39 | self.region_end = region_end |
| 40 | |
| 41 | def read(self, n): |
| 42 | return self.f.read(n) |
| 43 | |
| 44 | def readinto(self, b): |
| 45 | return self.f.readinto(b) |
| 46 | |
| 47 | def seek(self, offset): |
| 48 | return self.f.seek(offset) |
| 49 | |
| 50 | def write_to(self, offset, data): |
| 51 | if offset >= self.region_start and \ |
| 52 | offset + len(data) <= self.region_end: |
| 53 | self.f.seek(offset) |
| 54 | return self.f.write(data) |
| 55 | else: |
| 56 | raise OutOfRegionException() |
| 57 | |
| 58 | def fill_range(self, start, end, fill): |
| 59 | if start >= self.region_start and end <= self.region_end: |
| 60 | if start < end: |
| 61 | block = fill * 4096 |
| 62 | self.f.seek(start) |
| 63 | self.f.writelines(itertools.repeat(block, |
| 64 | (end - start) // 4096)) |
| 65 | self.f.write(block[:(end - start) % 4096]) |
| 66 | else: |
| 67 | raise OutOfRegionException() |
| 68 | |
| 69 | def move_range(self, offset_from, size, offset_to, fill): |
| 70 | if offset_from >= self.region_start and \ |
| 71 | offset_from + size <= self.region_end and \ |
| 72 | offset_to >= self.region_start and \ |
| 73 | offset_to + size <= self.region_end: |
| 74 | for i in range(0, size, 4096): |
| 75 | self.f.seek(offset_from + i, 0) |
| 76 | block = self.f.read(4096 if size - i >= 4096 else size - i) |
| 77 | self.f.seek(offset_from + i, 0) |
| 78 | self.f.write(fill * len(block)) |
| 79 | self.f.seek(offset_to + i, 0) |
| 80 | self.f.write(block) |
| 81 | else: |
| 82 | raise OutOfRegionException() |
| 83 | |
| 84 | |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 85 | def get_chunks_offsets(llut, me_start): |
| 86 | chunk_count = unpack("<I", llut[0x04:0x08])[0] |
| 87 | huffman_stream_end = sum(unpack("<II", llut[0x10:0x18])) + me_start |
| 88 | nonzero_offsets = [huffman_stream_end] |
| 89 | offsets = [] |
| 90 | |
| 91 | for i in range(0, chunk_count): |
| 92 | chunk = llut[0x40 + i * 4:0x44 + i * 4] |
| 93 | offset = 0 |
| 94 | |
| 95 | if chunk[3] != 0x80: |
| 96 | offset = unpack("<I", chunk[0:3] + b"\x00")[0] + me_start |
| 97 | |
| 98 | offsets.append([offset, 0]) |
| 99 | if offset != 0: |
| 100 | nonzero_offsets.append(offset) |
| 101 | |
| 102 | nonzero_offsets.sort() |
| 103 | |
| 104 | for i in offsets: |
| 105 | if i[0] != 0: |
| 106 | i[1] = nonzero_offsets[nonzero_offsets.index(i[0]) + 1] |
| 107 | |
| 108 | return offsets |
| 109 | |
| 110 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 111 | def remove_modules(f, mod_headers, ftpr_offset, me_end): |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 112 | comp_str = ("Uncomp.", "Huffman", "LZMA") |
| 113 | unremovable_huff_chunks = [] |
| 114 | chunks_offsets = [] |
| 115 | base = 0 |
| 116 | chunk_size = 0 |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 117 | end_addr = 0 |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 118 | |
| 119 | for mod_header in mod_headers: |
| 120 | name = mod_header[0x04:0x14].rstrip(b"\x00").decode("ascii") |
| 121 | offset = unpack("<I", mod_header[0x38:0x3C])[0] + ftpr_offset |
| 122 | size = unpack("<I", mod_header[0x40:0x44])[0] |
| 123 | flags = unpack("<I", mod_header[0x50:0x54])[0] |
| 124 | comp_type = (flags >> 4) & 7 |
| 125 | |
| 126 | sys.stdout.write(" {:<16} ({:<7}, ".format(name, comp_str[comp_type])) |
| 127 | |
| 128 | if comp_type == 0x00 or comp_type == 0x02: |
| 129 | sys.stdout.write("0x{:06x} - 0x{:06x}): " |
| 130 | .format(offset, offset + size)) |
| 131 | |
| 132 | if name in unremovable_modules: |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 133 | end_addr = max(end_addr, offset + size) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 134 | print("NOT removed, essential") |
| 135 | else: |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 136 | end = min(offset + size, me_end) |
| 137 | f.fill_range(offset, end, b"\xff") |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 138 | print("removed") |
| 139 | |
| 140 | elif comp_type == 0x01: |
| 141 | sys.stdout.write("fragmented data ): ") |
| 142 | if not chunks_offsets: |
| 143 | f.seek(offset) |
| 144 | llut = f.read(4) |
| 145 | if llut == b"LLUT": |
| 146 | llut += f.read(0x3c) |
| 147 | |
| 148 | chunk_count = unpack("<I", llut[0x4:0x8])[0] |
| 149 | base = unpack("<I", llut[0x8:0xc])[0] + 0x10000000 |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 150 | chunk_size = unpack("<I", llut[0x30:0x34])[0] |
| 151 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 152 | llut += f.read(chunk_count * 4) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 153 | chunks_offsets = get_chunks_offsets(llut, me_start) |
| 154 | else: |
| 155 | sys.exit("Huffman modules found, but LLUT is not present") |
| 156 | |
| 157 | if name in unremovable_modules: |
| 158 | print("NOT removed, essential") |
| 159 | module_base = unpack("<I", mod_header[0x34:0x38])[0] |
| 160 | module_size = unpack("<I", mod_header[0x3c:0x40])[0] |
| 161 | first_chunk_num = (module_base - base) // chunk_size |
| 162 | last_chunk_num = first_chunk_num + module_size // chunk_size |
| 163 | |
| 164 | unremovable_huff_chunks += \ |
| 165 | [x for x in chunks_offsets[first_chunk_num: |
| 166 | last_chunk_num + 1] if x[0] != 0] |
| 167 | else: |
| 168 | print("removed") |
| 169 | |
| 170 | else: |
| 171 | sys.stdout.write("0x{:06x} - 0x{:06x}): unknown compression, " |
| 172 | "skipping".format(offset, offset + size)) |
| 173 | |
| 174 | if chunks_offsets: |
| 175 | removable_huff_chunks = [] |
| 176 | |
| 177 | for chunk in chunks_offsets: |
| 178 | if all(not(unremovable_chk[0] <= chunk[0] < unremovable_chk[1] or |
| 179 | unremovable_chk[0] < chunk[1] <= unremovable_chk[1]) |
| 180 | for unremovable_chk in unremovable_huff_chunks): |
| 181 | removable_huff_chunks.append(chunk) |
| 182 | |
| 183 | for removable_chunk in removable_huff_chunks: |
| 184 | if removable_chunk[1] > removable_chunk[0]: |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 185 | end = min(removable_chunk[1], me_end) |
| 186 | f.fill_range(removable_chunk[0], end, b"\xff") |
| 187 | |
| 188 | end_addr = max(end_addr, |
| 189 | max(unremovable_huff_chunks, key=lambda x: x[1])[1]) |
| 190 | |
| 191 | return end_addr |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 192 | |
| 193 | |
| 194 | def check_partition_signature(f, offset): |
| 195 | f.seek(offset) |
| 196 | header = f.read(0x80) |
| 197 | modulus = int(binascii.hexlify(f.read(0x100)[::-1]), 16) |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 198 | public_exponent = unpack("<I", f.read(4))[0] |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 199 | signature = int(binascii.hexlify(f.read(0x100)[::-1]), 16) |
| 200 | |
| 201 | header_len = unpack("<I", header[0x4:0x8])[0] * 4 |
| 202 | manifest_len = unpack("<I", header[0x18:0x1c])[0] * 4 |
| 203 | f.seek(offset + header_len) |
| 204 | |
| 205 | sha256 = hashlib.sha256() |
| 206 | sha256.update(header) |
| 207 | sha256.update(f.read(manifest_len - header_len)) |
| 208 | |
| 209 | decrypted_sig = pow(signature, public_exponent, modulus) |
| 210 | |
| 211 | return "{:#x}".format(decrypted_sig).endswith(sha256.hexdigest()) # FIXME |
| 212 | |
| 213 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 214 | def relocate_partition(f, me_start, me_end, partition_header_offset, |
| 215 | new_offset, mod_headers): |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 216 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 217 | f.seek(partition_header_offset) |
| 218 | name = f.read(4).rstrip(b"\x00").decode("ascii") |
| 219 | f.seek(partition_header_offset + 0x8) |
| 220 | old_offset, partition_size = unpack("<II", f.read(0x8)) |
| 221 | old_offset += me_start |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 222 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 223 | llut_start = 0 |
| 224 | for mod_header in mod_headers: |
| 225 | if (unpack("<I", mod_header[0x50:0x54])[0] >> 4) & 7 == 0x01: |
| 226 | llut_start = unpack("<I", mod_header[0x38:0x3C])[0] + old_offset |
| 227 | break |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 228 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 229 | if llut_start != 0: |
| 230 | # Bytes 0x9:0xb of the LLUT (bytes 0x1:0x3 of the AddrBase) are added |
| 231 | # to the SpiBase (bytes 0xc:0x10 of the LLUT) to compute the final |
| 232 | # start of the LLUT. Since AddrBase is not modifiable, we can act only |
| 233 | # on SpiBase and here we compute the minimum allowed new_offset. |
| 234 | f.seek(llut_start + 0x9) |
| 235 | lut_start_corr = unpack("<H", f.read(2))[0] |
| 236 | new_offset = max(new_offset, |
| 237 | lut_start_corr + me_start - llut_start - 0x40 + |
| 238 | old_offset) |
| 239 | new_offset = ((new_offset + 0x1f) // 0x20) * 0x20 |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 240 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 241 | offset_diff = new_offset - old_offset |
| 242 | print("Relocating {} to {:#x} - {:#x}..." |
| 243 | .format(name, new_offset, new_offset + partition_size)) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 244 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 245 | print(" Adjusting FPT entry...") |
| 246 | f.write_to(partition_header_offset + 0x8, |
| 247 | pack("<I", new_offset - me_start)) |
| 248 | |
| 249 | if llut_start != 0: |
| 250 | f.seek(llut_start) |
| 251 | if f.read(4) == b"LLUT": |
| 252 | print(" Adjusting LUT start offset...") |
| 253 | lut_offset = llut_start + offset_diff + 0x40 - \ |
| 254 | lut_start_corr - me_start |
| 255 | f.write_to(llut_start + 0x0c, pack("<I", lut_offset)) |
| 256 | |
| 257 | print(" Adjusting Huffman start offset...") |
| 258 | f.seek(llut_start + 0x14) |
| 259 | old_huff_offset = unpack("<I", f.read(4))[0] |
| 260 | f.write_to(llut_start + 0x14, |
| 261 | pack("<I", old_huff_offset + offset_diff)) |
| 262 | |
| 263 | print(" Adjusting chunks offsets...") |
| 264 | f.seek(llut_start + 0x4) |
| 265 | chunk_count = unpack("<I", f.read(4))[0] |
| 266 | f.seek(llut_start + 0x40) |
| 267 | chunks = bytearray(chunk_count * 4) |
| 268 | f.readinto(chunks) |
| 269 | for i in range(0, chunk_count * 4, 4): |
| 270 | if chunks[i + 3] != 0x80: |
| 271 | chunks[i:i + 3] = \ |
| 272 | pack("<I", unpack("<I", chunks[i:i + 3] + |
| 273 | b"\x00")[0] + offset_diff)[0:3] |
| 274 | f.write_to(llut_start + 0x40, chunks) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 275 | else: |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 276 | sys.exit("Huffman modules present but no LLUT found!") |
| 277 | else: |
| 278 | print(" No Huffman modules found") |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 279 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 280 | print(" Moving data...") |
| 281 | partition_size = min(partition_size, me_end - old_offset) |
| 282 | f.move_range(old_offset, partition_size, new_offset, b"\xff") |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 283 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 284 | return new_offset |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 285 | |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 286 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 287 | if __name__ == "__main__": |
| 288 | parser = argparse.ArgumentParser(description="Tool to remove as much code " |
| 289 | "as possible from Intel ME/TXE firmwares") |
| 290 | parser.add_argument("file", help="ME/TXE image or full dump") |
| 291 | parser.add_argument("-O", "--output", help="save the modified image in a " |
| 292 | "separate file, instead of modifying the original " |
| 293 | "file") |
| 294 | parser.add_argument("-r", "--relocate", help="relocate the FTPR partition " |
| 295 | "to the top of the ME region", action="store_true") |
| 296 | parser.add_argument("-k", "--keep-modules", help="don't remove the FTPR " |
| 297 | "modules, even when possible", action="store_true") |
| 298 | parser.add_argument("-d", "--descriptor", help="remove the ME/TXE " |
| 299 | "Read/Write permissions to the other regions on the " |
| 300 | "flash from the Intel Flash Descriptor (requires a " |
| 301 | "full dump)", action="store_true") |
| 302 | parser.add_argument("-c", "--check", help="verify the integrity of the " |
| 303 | "fundamental parts of the firmware and exit", |
| 304 | action="store_true") |
| 305 | args = parser.parse_args() |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 306 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 307 | f = open(args.file, "rb" if args.check or args.output else "r+b") |
| 308 | f.seek(0x10) |
| 309 | magic = f.read(4) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 310 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 311 | if magic == b"$FPT": |
| 312 | print("ME/TXE image detected") |
| 313 | me_start = 0 |
| 314 | f.seek(0, 2) |
| 315 | me_end = f.tell() |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 316 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 317 | if args.descriptor: |
| 318 | sys.exit("-d requires a full dump") |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 319 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 320 | elif magic == b"\x5a\xa5\xf0\x0f": |
| 321 | print("Full image detected") |
| 322 | f.seek(0x14) |
| 323 | flmap0, flmap1 = unpack("<II", f.read(8)) |
| 324 | nr = flmap0 >> 24 & 0x7 |
| 325 | frba = flmap0 >> 12 & 0xff0 |
| 326 | fmba = (flmap1 & 0xff) << 4 |
| 327 | if nr >= 2: |
| 328 | f.seek(frba) |
| 329 | flreg0, flreg1, flreg2 = unpack("<III", f.read(12)) |
| 330 | fd_start = (flreg0 & 0x1fff) << 12 |
| 331 | fd_end = flreg0 >> 4 & 0x1fff000 | 0xfff + 1 |
| 332 | me_start = (flreg2 & 0x1fff) << 12 |
| 333 | me_end = flreg2 >> 4 & 0x1fff000 | 0xfff + 1 |
| 334 | |
| 335 | if me_start >= me_end: |
| 336 | sys.exit("The ME/TXE region in this image has been disabled") |
| 337 | |
| 338 | f.seek(me_start + 0x10) |
| 339 | if f.read(4) != b"$FPT": |
| 340 | sys.exit("The ME/TXE region is corrupted or missing") |
| 341 | |
| 342 | print("The ME/TXE region goes from {:#x} to {:#x}" |
| 343 | .format(me_start, me_end)) |
| 344 | else: |
| 345 | sys.exit("This image does not contains a ME/TXE firmware NR = {})" |
| 346 | .format(nr)) |
| 347 | else: |
| 348 | sys.exit("Unknown image") |
| 349 | |
| 350 | print("Found FPT header at {:#x}".format(me_start + 0x10)) |
| 351 | |
| 352 | f.seek(me_start + 0x14) |
| 353 | entries = unpack("<I", f.read(4))[0] |
| 354 | print("Found {} partition(s)".format(entries)) |
| 355 | |
| 356 | f.seek(me_start + 0x14) |
| 357 | header_len = unpack("B", f.read(1))[0] |
| 358 | |
| 359 | f.seek(me_start + 0x30) |
| 360 | partitions = f.read(entries * 0x20) |
| 361 | |
| 362 | ftpr_header = b"" |
| 363 | |
| 364 | for i in range(entries): |
| 365 | if partitions[i * 0x20:(i * 0x20) + 4] == b"FTPR": |
| 366 | ftpr_header = partitions[i * 0x20:(i + 1) * 0x20] |
| 367 | break |
| 368 | |
| 369 | if ftpr_header == b"": |
| 370 | sys.exit("FTPR header not found, this image doesn't seem to be valid") |
| 371 | |
| 372 | ftpr_offset, ftpr_lenght = unpack("<II", ftpr_header[0x08:0x10]) |
| 373 | ftpr_offset += me_start |
| 374 | print("Found FTPR header: FTPR partition spans from {:#x} to {:#x}" |
| 375 | .format(ftpr_offset, ftpr_offset + ftpr_lenght)) |
| 376 | |
| 377 | f.seek(ftpr_offset) |
| 378 | if f.read(4) == b"$CPD": |
| 379 | me11 = True |
| 380 | num_entries = unpack("<I", f.read(4))[0] |
| 381 | ftpr_mn2_offset = 0x10 + num_entries * 0x18 |
| 382 | else: |
| 383 | me11 = False |
| 384 | ftpr_mn2_offset = 0 |
| 385 | |
| 386 | f.seek(ftpr_offset + ftpr_mn2_offset + 0x24) |
| 387 | version = unpack("<HHHH", f.read(0x08)) |
| 388 | print("ME/TXE firmware version {}" |
| 389 | .format('.'.join(str(i) for i in version))) |
| 390 | |
| 391 | if not args.check: |
| 392 | if args.output: |
| 393 | f.close() |
| 394 | shutil.copy(args.file, args.output) |
| 395 | f = open(args.output, "r+b") |
| 396 | |
| 397 | mef = regionFile(f, me_start, me_end) |
| 398 | |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 399 | print("Removing extra partitions...") |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 400 | mef.fill_range(me_start + 0x30, ftpr_offset, b"\xff") |
| 401 | mef.fill_range(ftpr_offset + ftpr_lenght, me_end, b"\xff") |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 402 | |
| 403 | print("Removing extra partition entries in FPT...") |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 404 | mef.write_to(me_start + 0x30, ftpr_header) |
| 405 | mef.write_to(me_start + 0x14, pack("<I", 1)) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 406 | |
| 407 | print("Removing EFFS presence flag...") |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 408 | mef.seek(me_start + 0x24) |
| 409 | flags = unpack("<I", mef.read(4))[0] |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 410 | flags &= ~(0x00000001) |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 411 | mef.write_to(me_start + 0x24, pack("<I", flags)) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 412 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 413 | if args.descriptor: |
| 414 | print("Removing ME/TXE R/W access to the other flash regions...") |
| 415 | fdf = regionFile(f, fd_start, fd_end) |
| 416 | fdf.write_to(fmba + 0x4, pack("<I", 0x04040000)) |
| 417 | |
| 418 | if me11: |
| 419 | mef.seek(me_start + 0x10) |
| 420 | header = bytearray(mef.read(0x20)) |
| 421 | else: |
| 422 | mef.seek(me_start) |
| 423 | header = bytearray(mef.read(0x30)) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 424 | checksum = (0x100 - (sum(header) - header[0x1b]) & 0xff) & 0xff |
| 425 | |
| 426 | print("Correcting checksum (0x{:02x})...".format(checksum)) |
| 427 | # The checksum is just the two's complement of the sum of the first |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 428 | # 0x30 bytes in ME < 11 or bytes 0x10:0x30 in ME >= 11 (except for |
| 429 | # 0x1b, the checksum itself). In other words, the sum of those bytes |
| 430 | # must be always 0x00. |
| 431 | mef.write_to(me_start + 0x1b, pack("B", checksum)) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 432 | |
| 433 | if not me11: |
| 434 | print("Reading FTPR modules list...") |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 435 | mef.seek(ftpr_offset + 0x1c) |
| 436 | tag = mef.read(4) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 437 | |
| 438 | if tag == b"$MN2": |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 439 | mef.seek(ftpr_offset + 0x20) |
| 440 | num_modules = unpack("<I", mef.read(4))[0] |
| 441 | mef.seek(ftpr_offset + 0x290) |
| 442 | data = mef.read(0x84) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 443 | |
| 444 | module_header_size = 0 |
| 445 | if data[0x0:0x4] == b"$MME": |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 446 | if data[0x60:0x64] == b"$MME" or num_modules == 1: |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 447 | module_header_size = 0x60 |
| 448 | elif data[0x80:0x84] == b"$MME": |
| 449 | module_header_size = 0x80 |
| 450 | |
| 451 | if module_header_size != 0: |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 452 | mef.seek(ftpr_offset + 0x290) |
| 453 | mod_headers = [mef.read(module_header_size) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 454 | for i in range(0, num_modules)] |
| 455 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 456 | if all(hdr.startswith(b"$MME") for hdr in mod_headers): |
| 457 | if args.keep_modules: |
| 458 | end_addr = ftpr_offset + ftpr_lenght |
| 459 | else: |
| 460 | end_addr = remove_modules(mef, mod_headers, |
| 461 | ftpr_offset, me_end) |
| 462 | |
| 463 | if args.relocate: |
| 464 | new_ftpr_offset = relocate_partition(mef, |
| 465 | me_start, me_end, |
| 466 | me_start + 0x30, |
| 467 | min_ftpr_offset + me_start, |
| 468 | mod_headers) |
| 469 | end_addr += new_ftpr_offset - ftpr_offset |
| 470 | ftpr_offset = new_ftpr_offset |
| 471 | |
| 472 | end_addr = (end_addr // 0x1000 + 1) * 0x1000 |
| 473 | end_addr += spared_blocks * 0x1000 |
| 474 | |
| 475 | print("The ME minimum size should be {0} bytes " |
| 476 | "({0:#x} bytes)".format(end_addr - me_start)) |
| 477 | |
| 478 | if me_start > 0: |
| 479 | print("The ME region can be reduced up to:\n" |
| 480 | " {:08x}:{:08x} me" |
| 481 | .format(me_start, end_addr - 1)) |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 482 | else: |
| 483 | print("Found less modules than expected in the FTPR " |
| 484 | "partition; skipping modules removal") |
| 485 | else: |
| 486 | print("Can't find the module header size; skipping " |
| 487 | "modules removal") |
| 488 | else: |
| 489 | print("Wrong FTPR partition tag ({}); skipping modules removal" |
| 490 | .format(tag)) |
| 491 | else: |
| 492 | print("Modules removal in ME v11 or greater is not yet supported") |
| 493 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 494 | sys.stdout.write("Checking FTPR RSA signature... ") |
| 495 | if check_partition_signature(f, ftpr_offset + ftpr_mn2_offset): |
| 496 | print("VALID") |
| 497 | else: |
| 498 | print("INVALID!!") |
| 499 | sys.exit("The FTPR partition signature is not valid. Is the input " |
| 500 | "ME/TXE image valid?") |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 501 | |
Nicola Corna | e38f859 | 2017-02-22 10:16:32 +0100 | [diff] [blame] | 502 | f.close() |
| 503 | |
| 504 | if not args.check: |
Nicola Corna | 9bcc002 | 2017-01-23 15:28:24 +0100 | [diff] [blame] | 505 | print("Done! Good luck!") |