blob: fae5e567322f4d6ff8d50e5c1252304e45ef5173 [file] [log] [blame]
Yegor Timoshenkoc2e49412018-10-07 01:58:27 +00001#!/usr/bin/env python
Nicola Corna8882ac52018-03-31 16:25:03 +02002# me_cleaner - Tool for partial deblobbing of Intel ME/TXE firmware images
Patrick Georgi55189c92020-05-10 20:09:31 +02003# SPDX-License-Identifier: GPL-3.0-or-later
Nicola Corna9bcc0022017-01-23 15:28:24 +01004
Nicola Corna8882ac52018-03-31 16:25:03 +02005from __future__ import division, print_function
6
7import argparse
Nicola Corna9bcc0022017-01-23 15:28:24 +01008import binascii
9import hashlib
Nicola Corna8882ac52018-03-31 16:25:03 +020010import itertools
Nicola Cornae38f8592017-02-22 10:16:32 +010011import shutil
Nicola Corna8882ac52018-03-31 16:25:03 +020012import sys
Nicola Corna9bcc0022017-01-23 15:28:24 +010013from struct import pack, unpack
14
15
Nicola Cornae38f8592017-02-22 10:16:32 +010016min_ftpr_offset = 0x400
17spared_blocks = 4
Nicola Corna98f30342017-08-08 21:24:49 +020018unremovable_modules = ("ROMP", "BUP")
19unremovable_modules_me11 = ("rbe", "kernel", "syslib", "bup")
Nicola Corna8882ac52018-03-31 16:25:03 +020020unremovable_partitions = ("FTPR",)
21
22pubkeys_md5 = {
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",))
36}
Nicola Corna9bcc0022017-01-23 15:28:24 +010037
38
Nicola Cornae38f8592017-02-22 10:16:32 +010039class OutOfRegionException(Exception):
40 pass
41
42
Nicola Corna98f30342017-08-08 21:24:49 +020043class RegionFile:
Nicola Cornae38f8592017-02-22 10:16:32 +010044 def __init__(self, f, region_start, region_end):
45 self.f = f
46 self.region_start = region_start
47 self.region_end = region_end
48
49 def read(self, n):
Nicola Corna8882ac52018-03-31 16:25:03 +020050 if f.tell() + n <= self.region_end:
51 return self.f.read(n)
52 else:
53 raise OutOfRegionException()
Nicola Cornae38f8592017-02-22 10:16:32 +010054
55 def readinto(self, b):
Nicola Corna8882ac52018-03-31 16:25:03 +020056 if f.tell() + len(b) <= self.region_end:
57 return self.f.readinto(b)
58 else:
59 raise OutOfRegionException()
Nicola Cornae38f8592017-02-22 10:16:32 +010060
61 def seek(self, offset):
Nicola Corna8882ac52018-03-31 16:25:03 +020062 if self.region_start + offset <= self.region_end:
63 return self.f.seek(self.region_start + offset)
64 else:
65 raise OutOfRegionException()
Nicola Cornae38f8592017-02-22 10:16:32 +010066
67 def write_to(self, offset, data):
Nicola Corna8882ac52018-03-31 16:25:03 +020068 if self.region_start + offset + len(data) <= self.region_end:
69 self.f.seek(self.region_start + offset)
Nicola Cornae38f8592017-02-22 10:16:32 +010070 return self.f.write(data)
71 else:
72 raise OutOfRegionException()
73
74 def fill_range(self, start, end, fill):
Nicola Corna8882ac52018-03-31 16:25:03 +020075 if self.region_start + end <= self.region_end:
Nicola Cornae38f8592017-02-22 10:16:32 +010076 if start < end:
77 block = fill * 4096
Nicola Corna8882ac52018-03-31 16:25:03 +020078 self.f.seek(self.region_start + start)
Nicola Cornae38f8592017-02-22 10:16:32 +010079 self.f.writelines(itertools.repeat(block,
80 (end - start) // 4096))
81 self.f.write(block[:(end - start) % 4096])
82 else:
83 raise OutOfRegionException()
84
Nicola Corna8882ac52018-03-31 16:25:03 +020085 def fill_all(self, fill):
86 self.fill_range(0, self.region_end - self.region_start, fill)
87
Nicola Cornae38f8592017-02-22 10:16:32 +010088 def move_range(self, offset_from, size, offset_to, fill):
Nicola Corna8882ac52018-03-31 16:25:03 +020089 if self.region_start + offset_from + size <= self.region_end and \
90 self.region_start + offset_to + size <= self.region_end:
Nicola Cornae38f8592017-02-22 10:16:32 +010091 for i in range(0, size, 4096):
Nicola Corna8882ac52018-03-31 16:25:03 +020092 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)
Nicola Cornae38f8592017-02-22 10:16:32 +010095 self.f.write(fill * len(block))
Nicola Corna8882ac52018-03-31 16:25:03 +020096 self.f.seek(self.region_start + offset_to + i, 0)
Nicola Cornae38f8592017-02-22 10:16:32 +010097 self.f.write(block)
98 else:
99 raise OutOfRegionException()
100
Nicola Corna98f30342017-08-08 21:24:49 +0200101 def save(self, filename, size):
Nicola Corna8882ac52018-03-31 16:25:03 +0200102 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)))
107 return copyf
108 else:
109 raise OutOfRegionException()
Nicola Corna98f30342017-08-08 21:24:49 +0200110
Nicola Cornae38f8592017-02-22 10:16:32 +0100111
Nicola Corna8882ac52018-03-31 16:25:03 +0200112def get_chunks_offsets(llut):
Nicola Corna9bcc0022017-01-23 15:28:24 +0100113 chunk_count = unpack("<I", llut[0x04:0x08])[0]
Nicola Corna8882ac52018-03-31 16:25:03 +0200114 huffman_stream_end = sum(unpack("<II", llut[0x10:0x18]))
Nicola Corna9bcc0022017-01-23 15:28:24 +0100115 nonzero_offsets = [huffman_stream_end]
116 offsets = []
117
118 for i in range(0, chunk_count):
119 chunk = llut[0x40 + i * 4:0x44 + i * 4]
120 offset = 0
121
122 if chunk[3] != 0x80:
Nicola Corna8882ac52018-03-31 16:25:03 +0200123 offset = unpack("<I", chunk[0:3] + b"\x00")[0]
Nicola Corna9bcc0022017-01-23 15:28:24 +0100124
125 offsets.append([offset, 0])
126 if offset != 0:
127 nonzero_offsets.append(offset)
128
129 nonzero_offsets.sort()
130
131 for i in offsets:
132 if i[0] != 0:
133 i[1] = nonzero_offsets[nonzero_offsets.index(i[0]) + 1]
134
135 return offsets
136
137
Nicola Cornae38f8592017-02-22 10:16:32 +0100138def remove_modules(f, mod_headers, ftpr_offset, me_end):
Nicola Corna98f30342017-08-08 21:24:49 +0200139 comp_str = ("uncomp.", "Huffman", "LZMA")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100140 unremovable_huff_chunks = []
141 chunks_offsets = []
142 base = 0
143 chunk_size = 0
Nicola Cornae38f8592017-02-22 10:16:32 +0100144 end_addr = 0
Nicola Corna9bcc0022017-01-23 15:28:24 +0100145
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
152
Nicola Corna8882ac52018-03-31 16:25:03 +0200153 print(" {:<16} ({:<7}, ".format(name, comp_str[comp_type]), end="")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100154
155 if comp_type == 0x00 or comp_type == 0x02:
Nicola Corna8882ac52018-03-31 16:25:03 +0200156 print("0x{:06x} - 0x{:06x} ): "
157 .format(offset, offset + size), end="")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100158
159 if name in unremovable_modules:
Nicola Cornae38f8592017-02-22 10:16:32 +0100160 end_addr = max(end_addr, offset + size)
Nicola Corna9bcc0022017-01-23 15:28:24 +0100161 print("NOT removed, essential")
162 else:
Nicola Cornae38f8592017-02-22 10:16:32 +0100163 end = min(offset + size, me_end)
164 f.fill_range(offset, end, b"\xff")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100165 print("removed")
166
167 elif comp_type == 0x01:
Nicola Corna9bcc0022017-01-23 15:28:24 +0100168 if not chunks_offsets:
169 f.seek(offset)
170 llut = f.read(4)
171 if llut == b"LLUT":
172 llut += f.read(0x3c)
173
174 chunk_count = unpack("<I", llut[0x4:0x8])[0]
175 base = unpack("<I", llut[0x8:0xc])[0] + 0x10000000
Nicola Corna9bcc0022017-01-23 15:28:24 +0100176 chunk_size = unpack("<I", llut[0x30:0x34])[0]
177
Nicola Cornae38f8592017-02-22 10:16:32 +0100178 llut += f.read(chunk_count * 4)
Nicola Corna8882ac52018-03-31 16:25:03 +0200179 chunks_offsets = get_chunks_offsets(llut)
Nicola Corna9bcc0022017-01-23 15:28:24 +0100180 else:
181 sys.exit("Huffman modules found, but LLUT is not present")
182
Nicola Corna8882ac52018-03-31 16:25:03 +0200183 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
187 huff_size = 0
188
189 for chunk in chunks_offsets[first_chunk_num:last_chunk_num + 1]:
190 huff_size += chunk[1] - chunk[0]
191
192 print("fragmented data, {:<9}): "
193 .format("~" + str(int(round(huff_size / 1024))) + " KiB"),
194 end="")
195
Nicola Corna9bcc0022017-01-23 15:28:24 +0100196 if name in unremovable_modules:
197 print("NOT removed, essential")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100198
199 unremovable_huff_chunks += \
200 [x for x in chunks_offsets[first_chunk_num:
201 last_chunk_num + 1] if x[0] != 0]
202 else:
203 print("removed")
204
205 else:
Nicola Corna8882ac52018-03-31 16:25:03 +0200206 print("0x{:06x} - 0x{:06x}): unknown compression, skipping"
207 .format(offset, offset + size), end="")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100208
209 if chunks_offsets:
210 removable_huff_chunks = []
211
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)
217
218 for removable_chunk in removable_huff_chunks:
219 if removable_chunk[1] > removable_chunk[0]:
Nicola Cornae38f8592017-02-22 10:16:32 +0100220 end = min(removable_chunk[1], me_end)
221 f.fill_range(removable_chunk[0], end, b"\xff")
222
223 end_addr = max(end_addr,
224 max(unremovable_huff_chunks, key=lambda x: x[1])[1])
225
226 return end_addr
Nicola Corna9bcc0022017-01-23 15:28:24 +0100227
228
229def check_partition_signature(f, offset):
230 f.seek(offset)
231 header = f.read(0x80)
232 modulus = int(binascii.hexlify(f.read(0x100)[::-1]), 16)
Nicola Cornae38f8592017-02-22 10:16:32 +0100233 public_exponent = unpack("<I", f.read(4))[0]
Nicola Corna9bcc0022017-01-23 15:28:24 +0100234 signature = int(binascii.hexlify(f.read(0x100)[::-1]), 16)
235
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)
239
240 sha256 = hashlib.sha256()
241 sha256.update(header)
242 sha256.update(f.read(manifest_len - header_len))
243
244 decrypted_sig = pow(signature, public_exponent, modulus)
245
246 return "{:#x}".format(decrypted_sig).endswith(sha256.hexdigest()) # FIXME
247
248
Nicola Corna98f30342017-08-08 21:24:49 +0200249def print_check_partition_signature(f, offset):
250 if check_partition_signature(f, offset):
251 print("VALID")
252 else:
253 print("INVALID!!")
254 sys.exit("The FTPR partition signature is not valid. Is the input "
255 "ME/TXE image valid?")
256
257
Nicola Corna8882ac52018-03-31 16:25:03 +0200258def relocate_partition(f, me_end, partition_header_offset,
Nicola Cornae38f8592017-02-22 10:16:32 +0100259 new_offset, mod_headers):
Nicola Corna9bcc0022017-01-23 15:28:24 +0100260
Nicola Cornae38f8592017-02-22 10:16:32 +0100261 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))
Nicola Corna9bcc0022017-01-23 15:28:24 +0100265
Nicola Cornae38f8592017-02-22 10:16:32 +0100266 llut_start = 0
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
270 break
Nicola Corna9bcc0022017-01-23 15:28:24 +0100271
Nicola Corna98f30342017-08-08 21:24:49 +0200272 if mod_headers and llut_start != 0:
Nicola Cornae38f8592017-02-22 10:16:32 +0100273 # 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,
Nicola Corna8882ac52018-03-31 16:25:03 +0200280 lut_start_corr - llut_start - 0x40 + old_offset)
Nicola Cornae38f8592017-02-22 10:16:32 +0100281 new_offset = ((new_offset + 0x1f) // 0x20) * 0x20
Nicola Corna9bcc0022017-01-23 15:28:24 +0100282
Nicola Cornae38f8592017-02-22 10:16:32 +0100283 offset_diff = new_offset - old_offset
Nicola Corna98f30342017-08-08 21:24:49 +0200284 print("Relocating {} from {:#x} - {:#x} to {:#x} - {:#x}..."
285 .format(name, old_offset, old_offset + partition_size,
286 new_offset, new_offset + partition_size))
Nicola Corna9bcc0022017-01-23 15:28:24 +0100287
Nicola Cornae38f8592017-02-22 10:16:32 +0100288 print(" Adjusting FPT entry...")
289 f.write_to(partition_header_offset + 0x8,
Nicola Corna8882ac52018-03-31 16:25:03 +0200290 pack("<I", new_offset))
Nicola Cornae38f8592017-02-22 10:16:32 +0100291
Nicola Corna98f30342017-08-08 21:24:49 +0200292 if mod_headers:
293 if llut_start != 0:
294 f.seek(llut_start)
295 if f.read(4) == b"LLUT":
296 print(" Adjusting LUT start offset...")
Nicola Corna8882ac52018-03-31 16:25:03 +0200297 lut_offset = llut_start + offset_diff + 0x40 - lut_start_corr
Nicola Corna98f30342017-08-08 21:24:49 +0200298 f.write_to(llut_start + 0x0c, pack("<I", lut_offset))
Nicola Cornae38f8592017-02-22 10:16:32 +0100299
Nicola Corna98f30342017-08-08 21:24:49 +0200300 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))
Nicola Cornae38f8592017-02-22 10:16:32 +0100305
Nicola Corna98f30342017-08-08 21:24:49 +0200306 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)
311 f.readinto(chunks)
312 for i in range(0, chunk_count * 4, 4):
313 if chunks[i + 3] != 0x80:
314 chunks[i:i + 3] = \
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)
318 else:
319 sys.exit("Huffman modules present but no LLUT found!")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100320 else:
Nicola Corna98f30342017-08-08 21:24:49 +0200321 print(" No Huffman modules found")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100322
Nicola Cornae38f8592017-02-22 10:16:32 +0100323 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")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100326
Nicola Cornae38f8592017-02-22 10:16:32 +0100327 return new_offset
Nicola Corna9bcc0022017-01-23 15:28:24 +0100328
Nicola Corna9bcc0022017-01-23 15:28:24 +0100329
Nicola Corna8882ac52018-03-31 16:25:03 +0200330def check_and_remove_modules(f, me_end, offset, min_offset,
Nicola Corna98f30342017-08-08 21:24:49 +0200331 relocate, keep_modules):
332
333 f.seek(offset + 0x20)
334 num_modules = unpack("<I", f.read(4))[0]
335 f.seek(offset + 0x290)
336 data = f.read(0x84)
337
Nicola Corna8882ac52018-03-31 16:25:03 +0200338 mod_header_size = 0
Nicola Corna98f30342017-08-08 21:24:49 +0200339 if data[0x0:0x4] == b"$MME":
340 if data[0x60:0x64] == b"$MME" or num_modules == 1:
Nicola Corna8882ac52018-03-31 16:25:03 +0200341 mod_header_size = 0x60
Nicola Corna98f30342017-08-08 21:24:49 +0200342 elif data[0x80:0x84] == b"$MME":
Nicola Corna8882ac52018-03-31 16:25:03 +0200343 mod_header_size = 0x80
Nicola Corna98f30342017-08-08 21:24:49 +0200344
Nicola Corna8882ac52018-03-31 16:25:03 +0200345 if mod_header_size != 0:
Nicola Corna98f30342017-08-08 21:24:49 +0200346 f.seek(offset + 0x290)
Nicola Corna8882ac52018-03-31 16:25:03 +0200347 data = f.read(mod_header_size * num_modules)
348 mod_headers = [data[i * mod_header_size:(i + 1) * mod_header_size]
Nicola Corna98f30342017-08-08 21:24:49 +0200349 for i in range(0, num_modules)]
350
351 if all(hdr.startswith(b"$MME") for hdr in mod_headers):
352 if args.keep_modules:
Nicola Corna8882ac52018-03-31 16:25:03 +0200353 end_addr = offset + ftpr_length
Nicola Corna98f30342017-08-08 21:24:49 +0200354 else:
Nicola Corna8882ac52018-03-31 16:25:03 +0200355 end_addr = remove_modules(f, mod_headers, offset, me_end)
Nicola Corna98f30342017-08-08 21:24:49 +0200356
357 if args.relocate:
Nicola Corna8882ac52018-03-31 16:25:03 +0200358 new_offset = relocate_partition(f, me_end, 0x30, min_offset,
Nicola Corna98f30342017-08-08 21:24:49 +0200359 mod_headers)
360 end_addr += new_offset - offset
361 offset = new_offset
362
363 return end_addr, offset
364
365 else:
366 print("Found less modules than expected in the FTPR "
367 "partition; skipping modules removal")
368 else:
369 print("Can't find the module header size; skipping "
370 "modules removal")
371
372 return -1, offset
373
374
Nicola Corna8882ac52018-03-31 16:25:03 +0200375def check_and_remove_modules_me11(f, me_end, partition_offset,
376 partition_length, min_offset, relocate,
Nicola Corna98f30342017-08-08 21:24:49 +0200377 keep_modules):
378
379 comp_str = ("LZMA/uncomp.", "Huffman")
380
381 if keep_modules:
Nicola Corna8882ac52018-03-31 16:25:03 +0200382 end_data = partition_offset + partition_length
Nicola Corna98f30342017-08-08 21:24:49 +0200383 else:
384 end_data = 0
385
386 f.seek(partition_offset + 0x4)
387 module_count = unpack("<I", f.read(4))[0]
388
389 modules = []
Nicola Corna8882ac52018-03-31 16:25:03 +0200390 modules.append(("end", partition_length, 0))
Nicola Corna98f30342017-08-08 21:24:49 +0200391
392 f.seek(partition_offset + 0x10)
393 for i in range(0, module_count):
394 data = f.read(0x18)
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
399
400 modules.append((name, offset, comp_type))
401
402 modules.sort(key=lambda x: x[1])
403
404 for i in range(0, module_count):
405 name = modules[i][0]
406 offset = partition_offset + modules[i][1]
407 end = partition_offset + modules[i + 1][1]
408 removed = False
409
410 if name.endswith(".man") or name.endswith(".met"):
411 compression = "uncompressed"
412 else:
413 compression = comp_str[modules[i][2]]
414
Nicola Corna8882ac52018-03-31 16:25:03 +0200415 print(" {:<12} ({:<12}, 0x{:06x} - 0x{:06x}): "
416 .format(name, compression, offset, end), end="")
Nicola Corna98f30342017-08-08 21:24:49 +0200417
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")
424 else:
425 removed = True
426 f.fill_range(offset, min(end, me_end), b"\xff")
427 print("removed")
428
429 if not removed:
430 end_data = max(end_data, end)
431
432 if relocate:
Nicola Corna8882ac52018-03-31 16:25:03 +0200433 new_offset = relocate_partition(f, me_end, 0x30, min_offset, [])
Nicola Corna98f30342017-08-08 21:24:49 +0200434 end_data += new_offset - partition_offset
435 partition_offset = new_offset
436
437 return end_data, partition_offset
438
439
440def check_mn2_tag(f, offset):
441 f.seek(offset + 0x1c)
442 tag = f.read(4)
443 if tag != b"$MN2":
444 sys.exit("Wrong FTPR manifest tag ({}), this image may be corrupted"
445 .format(tag))
446
447
448def flreg_to_start_end(flreg):
449 return (flreg & 0x7fff) << 12, (flreg >> 4 & 0x7fff000 | 0xfff) + 1
450
451
452def start_end_to_flreg(start, end):
453 return (start & 0x7fff000) >> 12 | ((end - 1) & 0x7fff000) << 4
454
455
Nicola Cornae38f8592017-02-22 10:16:32 +0100456if __name__ == "__main__":
457 parser = argparse.ArgumentParser(description="Tool to remove as much code "
Nicola Corna98f30342017-08-08 21:24:49 +0200458 "as possible from Intel ME/TXE firmware "
459 "images")
Nicola Corna8882ac52018-03-31 16:25:03 +0200460 softdis = parser.add_mutually_exclusive_group()
461 bw_list = parser.add_mutually_exclusive_group()
462
463 parser.add_argument("-v", "--version", action="version",
464 version="%(prog)s 1.2")
465
Nicola Cornae38f8592017-02-22 10:16:32 +0100466 parser.add_argument("file", help="ME/TXE image or full dump")
Nicola Corna8882ac52018-03-31 16:25:03 +0200467 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)",
480 action="store_true")
Nicola Cornae38f8592017-02-22 10:16:32 +0100481 parser.add_argument("-r", "--relocate", help="relocate the FTPR partition "
Nicola Corna98f30342017-08-08 21:24:49 +0200482 "to the top of the ME region to save even more space",
483 action="store_true")
Nicola Corna8882ac52018-03-31 16:25:03 +0200484 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")
Nicola Cornae38f8592017-02-22 10:16:32 +0100487 parser.add_argument("-k", "--keep-modules", help="don't remove the FTPR "
488 "modules, even when possible", action="store_true")
Nicola Corna8882ac52018-03-31 16:25:03 +0200489 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 "
497 "removal list.")
Nicola Cornae38f8592017-02-22 10:16:32 +0100498 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")
Nicola Corna8882ac52018-03-31 16:25:03 +0200502 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 "
506 "start and end")
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")
Nicola Cornae38f8592017-02-22 10:16:32 +0100510 parser.add_argument("-c", "--check", help="verify the integrity of the "
511 "fundamental parts of the firmware and exit",
512 action="store_true")
Nicola Corna98f30342017-08-08 21:24:49 +0200513
Nicola Cornae38f8592017-02-22 10:16:32 +0100514 args = parser.parse_args()
Nicola Corna9bcc0022017-01-23 15:28:24 +0100515
Nicola Corna8882ac52018-03-31 16:25:03 +0200516 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")
519
520 if args.soft_disable_only and (args.relocate or args.truncate):
521 sys.exit("-s can't be used with -r or -t")
522
523 if (args.whitelist or args.blacklist) and args.relocate:
524 sys.exit("Relocation is not yet supported with custom whitelist or "
525 "blacklist")
Nicola Corna98f30342017-08-08 21:24:49 +0200526
Nicola Cornae38f8592017-02-22 10:16:32 +0100527 f = open(args.file, "rb" if args.check or args.output else "r+b")
528 f.seek(0x10)
529 magic = f.read(4)
Nicola Corna9bcc0022017-01-23 15:28:24 +0100530
Nicola Cornae38f8592017-02-22 10:16:32 +0100531 if magic == b"$FPT":
532 print("ME/TXE image detected")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100533
Nicola Corna8882ac52018-03-31 16:25:03 +0200534 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")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100537
Nicola Corna98f30342017-08-08 21:24:49 +0200538 f.seek(0, 2)
Nicola Corna8882ac52018-03-31 16:25:03 +0200539 me_start = 0
Nicola Corna98f30342017-08-08 21:24:49 +0200540 me_end = f.tell()
Nicola Corna8882ac52018-03-31 16:25:03 +0200541 mef = RegionFile(f, me_start, me_end)
Nicola Corna98f30342017-08-08 21:24:49 +0200542
Nicola Cornae38f8592017-02-22 10:16:32 +0100543 elif magic == b"\x5a\xa5\xf0\x0f":
544 print("Full image detected")
Nicola Corna98f30342017-08-08 21:24:49 +0200545
546 if args.truncate and not args.extract_me:
547 sys.exit("-t requires a separated ME/TXE image (or --extract-me)")
548
Nicola Cornae38f8592017-02-22 10:16:32 +0100549 f.seek(0x14)
550 flmap0, flmap1 = unpack("<II", f.read(8))
Nicola Cornae38f8592017-02-22 10:16:32 +0100551 frba = flmap0 >> 12 & 0xff0
552 fmba = (flmap1 & 0xff) << 4
Nicola Corna8882ac52018-03-31 16:25:03 +0200553 fpsba = flmap1 >> 12 & 0xff0
Nicola Cornae38f8592017-02-22 10:16:32 +0100554
Nicola Corna98f30342017-08-08 21:24:49 +0200555 f.seek(frba)
556 flreg = unpack("<III", f.read(12))
Nicola Cornae38f8592017-02-22 10:16:32 +0100557
Nicola Corna98f30342017-08-08 21:24:49 +0200558 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])
Nicola Cornae38f8592017-02-22 10:16:32 +0100561
Nicola Corna98f30342017-08-08 21:24:49 +0200562 if me_start >= me_end:
563 sys.exit("The ME/TXE region in this image has been disabled")
564
Nicola Corna8882ac52018-03-31 16:25:03 +0200565 mef = RegionFile(f, me_start, me_end)
566
567 mef.seek(0x10)
568 if mef.read(4) != b"$FPT":
Nicola Corna98f30342017-08-08 21:24:49 +0200569 sys.exit("The ME/TXE region is corrupted or missing")
570
571 print("The ME/TXE region goes from {:#x} to {:#x}"
572 .format(me_start, me_end))
Nicola Cornae38f8592017-02-22 10:16:32 +0100573 else:
574 sys.exit("Unknown image")
575
Nicola Corna8882ac52018-03-31 16:25:03 +0200576 end_addr = me_end
Nicola Cornae38f8592017-02-22 10:16:32 +0100577
Nicola Corna8882ac52018-03-31 16:25:03 +0200578 print("Found FPT header at {:#x}".format(mef.region_start + 0x10))
579
580 mef.seek(0x14)
581 entries = unpack("<I", mef.read(4))[0]
Nicola Cornae38f8592017-02-22 10:16:32 +0100582 print("Found {} partition(s)".format(entries))
583
Nicola Corna8882ac52018-03-31 16:25:03 +0200584 mef.seek(0x30)
585 partitions = mef.read(entries * 0x20)
Nicola Cornae38f8592017-02-22 10:16:32 +0100586
587 ftpr_header = b""
588
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]
592 break
593
594 if ftpr_header == b"":
595 sys.exit("FTPR header not found, this image doesn't seem to be valid")
596
Nicola Corna8882ac52018-03-31 16:25:03 +0200597 ftpr_offset, ftpr_length = unpack("<II", ftpr_header[0x08:0x10])
Nicola Cornae38f8592017-02-22 10:16:32 +0100598 print("Found FTPR header: FTPR partition spans from {:#x} to {:#x}"
Nicola Corna8882ac52018-03-31 16:25:03 +0200599 .format(ftpr_offset, ftpr_offset + ftpr_length))
Nicola Cornae38f8592017-02-22 10:16:32 +0100600
Nicola Corna8882ac52018-03-31 16:25:03 +0200601 mef.seek(ftpr_offset)
602 if mef.read(4) == b"$CPD":
Nicola Cornae38f8592017-02-22 10:16:32 +0100603 me11 = True
Nicola Corna8882ac52018-03-31 16:25:03 +0200604 num_entries = unpack("<I", mef.read(4))[0]
Nicola Corna98f30342017-08-08 21:24:49 +0200605
Nicola Corna8882ac52018-03-31 16:25:03 +0200606 mef.seek(ftpr_offset + 0x10)
Nicola Corna98f30342017-08-08 21:24:49 +0200607 ftpr_mn2_offset = -1
608
609 for i in range(0, num_entries):
Nicola Corna8882ac52018-03-31 16:25:03 +0200610 data = mef.read(0x18)
Nicola Corna98f30342017-08-08 21:24:49 +0200611 name = data[0x0:0xc].rstrip(b"\x00").decode("ascii")
612 offset = unpack("<I", data[0xc:0xf] + b"\x00")[0]
613
614 if name == "FTPR.man":
615 ftpr_mn2_offset = offset
616 break
617
618 if ftpr_mn2_offset >= 0:
Nicola Corna8882ac52018-03-31 16:25:03 +0200619 check_mn2_tag(mef, ftpr_offset + ftpr_mn2_offset)
Nicola Corna98f30342017-08-08 21:24:49 +0200620 print("Found FTPR manifest at {:#x}"
621 .format(ftpr_offset + ftpr_mn2_offset))
622 else:
623 sys.exit("Can't find the manifest of the FTPR partition")
624
Nicola Cornae38f8592017-02-22 10:16:32 +0100625 else:
Nicola Corna8882ac52018-03-31 16:25:03 +0200626 check_mn2_tag(mef, ftpr_offset)
Nicola Cornae38f8592017-02-22 10:16:32 +0100627 me11 = False
628 ftpr_mn2_offset = 0
629
Nicola Corna8882ac52018-03-31 16:25:03 +0200630 mef.seek(ftpr_offset + ftpr_mn2_offset + 0x24)
631 version = unpack("<HHHH", mef.read(0x08))
Nicola Cornae38f8592017-02-22 10:16:32 +0100632 print("ME/TXE firmware version {}"
633 .format('.'.join(str(i) for i in version)))
634
Nicola Corna8882ac52018-03-31 16:25:03 +0200635 mef.seek(ftpr_offset + ftpr_mn2_offset + 0x80)
636 pubkey_md5 = hashlib.md5(mef.read(0x104)).hexdigest()
637
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)))
642 else:
643 if version[0] >= 6:
644 variant = "ME"
645 else:
646 variant = "TXE"
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))
651
652 if not args.check and args.output:
653 f.close()
654 shutil.copy(args.file, args.output)
655 f = open(args.output, "r+b")
656
657 mef = RegionFile(f, me_start, me_end)
658
659 if me_start > 0:
660 fdf = RegionFile(f, fd_start, fd_end)
661
662 if me11:
663 fdf.seek(fpsba)
664 pchstrp0 = unpack("<I", fdf.read(4))[0]
665 print("The HAP bit is " +
666 ("SET" if pchstrp0 & 1 << 16 else "NOT SET"))
667 else:
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"))
672
673 # ME 6 Ignition: wipe everything
674 me6_ignition = False
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)
680 data = mef.read(0xc)
681
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")
685 me6_ignition = True
686
Nicola Cornae38f8592017-02-22 10:16:32 +0100687 if not args.check:
Nicola Corna8882ac52018-03-31 16:25:03 +0200688 if not args.soft_disable_only and not me6_ignition:
689 print("Reading partitions list...")
690 unremovable_part_fpt = b""
691 extra_part_end = 0
692 whitelist = []
693 blacklist = []
Nicola Cornae38f8592017-02-22 10:16:32 +0100694
Nicola Corna8882ac52018-03-31 16:25:03 +0200695 whitelist += unremovable_partitions
Nicola Corna98f30342017-08-08 21:24:49 +0200696
Nicola Corna8882ac52018-03-31 16:25:03 +0200697 if args.blacklist:
698 blacklist = args.blacklist.split(",")
699 elif args.whitelist:
700 whitelist += args.whitelist.split(",")
Nicola Cornae38f8592017-02-22 10:16:32 +0100701
Nicola Corna8882ac52018-03-31 16:25:03 +0200702 for i in range(entries):
703 partition = partitions[i * 0x20:(i + 1) * 0x20]
704 flags = unpack("<I", partition[0x1c:0x20])[0]
Nicola Corna9bcc0022017-01-23 15:28:24 +0100705
Nicola Corna8882ac52018-03-31 16:25:03 +0200706 try:
707 part_name = \
708 partition[0x0:0x4].rstrip(b"\x00").decode("ascii")
709 except UnicodeDecodeError:
710 part_name = "????"
Nicola Corna9bcc0022017-01-23 15:28:24 +0100711
Nicola Corna8882ac52018-03-31 16:25:03 +0200712 part_start, part_length = unpack("<II", partition[0x08:0x10])
Nicola Corna9bcc0022017-01-23 15:28:24 +0100713
Nicola Corna8882ac52018-03-31 16:25:03 +0200714 # 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
Nicola Corna9bcc0022017-01-23 15:28:24 +0100718
Nicola Corna8882ac52018-03-31 16:25:03 +0200719 part_end = part_start + part_length
Nicola Corna9bcc0022017-01-23 15:28:24 +0100720
Nicola Corna8882ac52018-03-31 16:25:03 +0200721 if flags & 0x7f == 2:
722 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
723 "remove"
724 .format(part_name, "NVRAM partition, no data",
725 part_length))
726 elif part_start == 0 or part_length == 0 or part_end > me_end:
727 print(" {:<4} ({:^24}, 0x{:08x} total bytes): nothing to "
728 "remove"
729 .format(part_name, "no data here", part_length))
730 else:
731 print(" {:<4} (0x{:08x} - 0x{:09x}, 0x{:08x} total bytes): "
732 .format(part_name, part_start, part_end, part_length),
733 end="")
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)
739 print("NOT removed")
740 else:
741 mef.fill_range(part_start, part_end, b"\xff")
742 print("removed")
Nicola Corna9bcc0022017-01-23 15:28:24 +0100743
Nicola Corna8882ac52018-03-31 16:25:03 +0200744 print("Removing partition entries in FPT...")
745 mef.write_to(0x30, unremovable_part_fpt)
746 mef.write_to(0x14,
747 pack("<I", len(unremovable_part_fpt) // 0x20))
Nicola Corna98f30342017-08-08 21:24:49 +0200748
Nicola Corna8882ac52018-03-31 16:25:03 +0200749 mef.fill_range(0x30 + len(unremovable_part_fpt),
750 0x30 + len(partitions), b"\xff")
Nicola Corna98f30342017-08-08 21:24:49 +0200751
Nicola Corna8882ac52018-03-31 16:25:03 +0200752 if (not blacklist and "EFFS" not in whitelist) or \
753 "EFFS" in blacklist:
754 print("Removing EFFS presence flag...")
755 mef.seek(0x24)
756 flags = unpack("<I", mef.read(4))[0]
757 flags &= ~(0x00000001)
758 mef.write_to(0x24, pack("<I", flags))
759
760 if me11:
761 mef.seek(0x10)
762 header = bytearray(mef.read(0x20))
763 header[0x0b] = 0x00
764 else:
765 mef.seek(0)
766 header = bytearray(mef.read(0x30))
767 header[0x1b] = 0x00
768 checksum = (0x100 - sum(header) & 0xff) & 0xff
769
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))
776
777 print("Reading FTPR modules list...")
778 if me11:
779 end_addr, ftpr_offset = \
780 check_and_remove_modules_me11(mef, me_end,
781 ftpr_offset, ftpr_length,
782 min_ftpr_offset,
783 args.relocate,
784 args.keep_modules)
785 else:
786 end_addr, ftpr_offset = \
787 check_and_remove_modules(mef, me_end, ftpr_offset,
788 min_ftpr_offset, args.relocate,
789 args.keep_modules)
790
791 if end_addr > 0:
792 end_addr = max(end_addr, extra_part_end)
793 end_addr = (end_addr // 0x1000 + 1) * 0x1000
794 end_addr += spared_blocks * 0x1000
795
796 print("The ME minimum size should be {0} bytes "
797 "({0:#x} bytes)".format(end_addr))
798
799 if me_start > 0:
800 print("The ME region can be reduced up to:\n"
801 " {:08x}:{:08x} me"
802 .format(me_start, me_start + end_addr - 1))
803 elif args.truncate:
804 print("Truncating file at {:#x}...".format(end_addr))
805 f.truncate(end_addr)
806
807 if args.soft_disable or args.soft_disable_only:
808 if me11:
809 print("Setting the HAP bit in PCHSTRP0 to disable Intel ME...")
810 pchstrp0 |= (1 << 16)
811 fdf.write_to(fpsba, pack("<I", pchstrp0))
812 else:
813 print("Setting the AltMeDisable bit in PCHSTRP10 to disable "
814 "Intel ME...")
815 pchstrp10 |= (1 << 7)
816 fdf.write_to(fpsba + 0x28, pack("<I", pchstrp10))
Nicola Corna98f30342017-08-08 21:24:49 +0200817
818 if args.descriptor:
819 print("Removing ME/TXE R/W access to the other flash regions...")
Nicola Corna8882ac52018-03-31 16:25:03 +0200820 if me11:
821 flmstr2 = 0x00400500
822 else:
823 fdf.seek(fmba + 0x4)
824 flmstr2 = (unpack("<I", fdf.read(4))[0] | 0x04040000) & 0x0404ffff
825
826 fdf.write_to(fmba + 0x4, pack("<I", flmstr2))
Nicola Corna98f30342017-08-08 21:24:49 +0200827
828 if args.extract_descriptor:
829 if args.truncate:
830 print("Extracting the descriptor to \"{}\"..."
831 .format(args.extract_descriptor))
832 fdf_copy = fdf.save(args.extract_descriptor, fd_end - fd_start)
833
834 if bios_start == me_end:
835 print("Modifying the regions of the extracted descriptor...")
836 print(" {:08x}:{:08x} me --> {:08x}:{:08x} me"
Nicola Corna8882ac52018-03-31 16:25:03 +0200837 .format(me_start, me_end - 1,
838 me_start, me_start + end_addr - 1))
Nicola Corna98f30342017-08-08 21:24:49 +0200839 print(" {:08x}:{:08x} bios --> {:08x}:{:08x} bios"
Nicola Corna8882ac52018-03-31 16:25:03 +0200840 .format(bios_start, bios_end - 1,
841 me_start + end_addr, bios_end - 1))
Nicola Corna98f30342017-08-08 21:24:49 +0200842
Nicola Corna8882ac52018-03-31 16:25:03 +0200843 flreg1 = start_end_to_flreg(me_start + end_addr, bios_end)
844 flreg2 = start_end_to_flreg(me_start, me_start + end_addr)
Nicola Corna98f30342017-08-08 21:24:49 +0200845
846 fdf_copy.seek(frba + 0x4)
847 fdf_copy.write(pack("<II", flreg1, flreg2))
848 else:
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")
Nicola Corna98f30342017-08-08 21:24:49 +0200853 else:
854 print("Extracting the descriptor to \"{}\"..."
855 .format(args.extract_descriptor))
Nicola Corna8882ac52018-03-31 16:25:03 +0200856 fdf_copy = fdf.save(args.extract_descriptor, fd_end - fd_start)
857
858 fdf_copy.close()
Nicola Corna98f30342017-08-08 21:24:49 +0200859
860 if args.extract_me:
861 if args.truncate:
862 print("Extracting and truncating the ME image to \"{}\"..."
863 .format(args.extract_me))
Nicola Corna8882ac52018-03-31 16:25:03 +0200864 mef_copy = mef.save(args.extract_me, end_addr)
Nicola Corna98f30342017-08-08 21:24:49 +0200865 else:
866 print("Extracting the ME image to \"{}\"..."
867 .format(args.extract_me))
868 mef_copy = mef.save(args.extract_me, me_end - me_start)
869
Nicola Corna8882ac52018-03-31 16:25:03 +0200870 if not me6_ignition:
871 print("Checking the FTPR RSA signature of the extracted ME "
872 "image... ", end="")
873 print_check_partition_signature(mef_copy,
874 ftpr_offset + ftpr_mn2_offset)
Nicola Corna98f30342017-08-08 21:24:49 +0200875 mef_copy.close()
876
Nicola Corna8882ac52018-03-31 16:25:03 +0200877 if not me6_ignition:
878 print("Checking the FTPR RSA signature... ", end="")
879 print_check_partition_signature(mef, ftpr_offset + ftpr_mn2_offset)
Nicola Corna9bcc0022017-01-23 15:28:24 +0100880
Nicola Cornae38f8592017-02-22 10:16:32 +0100881 f.close()
882
883 if not args.check:
Nicola Corna9bcc0022017-01-23 15:28:24 +0100884 print("Done! Good luck!")