me_cleaner.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/python3
  2. # Copyright (C) 2016 Nicola Corna <nicola@corna.info>
  3. #
  4. # me_cleaner is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # me_cleaner is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with me_cleaner. If not, see <http://www.gnu.org/licenses/>.
  16. #
  17. import sys
  18. import itertools
  19. from struct import *
  20. def fill_range(f, start, end, fill):
  21. block = fill * 4096
  22. f.seek(start, 0)
  23. f.writelines(itertools.repeat(block, (end - start) // 4096))
  24. f.write(fill[:(end - start) % 4096])
  25. def remove_module(f, mod_header, ftpr_offset, lzma_start, lzma_end):
  26. name = mod_header[0x04:0x14].decode("ascii")
  27. start = unpack("<I", mod_header[0x38:0x3C])[0]
  28. size = unpack("<I", mod_header[0x40:0x44])[0]
  29. flags = unpack("<I", mod_header[0x50:0x54])[0]
  30. comp_type = (flags >> 4) & 7
  31. if comp_type == 0x00 or comp_type == 0x02:
  32. if start >= lzma_start and start + size <= lzma_end:
  33. fill_range(f, ftpr_offset + start, ftpr_offset + start + size,
  34. b"\xFF")
  35. print(" {:<16}: removed (0x{:0X} - 0x{:0X})"
  36. .format(name, ftpr_offset + start,
  37. ftpr_offset + start + size))
  38. else:
  39. print(" {:<16}: is outside the LZMA region (module 0x{:0X} - "
  40. "0x{:0X}, LZMA 0x{:0X} - 0x{:0X}), skipping"
  41. .format(name, start, start+size, lzma_start, lzma_end))
  42. elif comp_type == 0x01:
  43. print(" {:<16}: removal of Huffman modules is not supported yet, "
  44. "skipping".format(name))
  45. else:
  46. print(" {:<16}: unknown compression, skipping".format(name))
  47. if len(sys.argv) != 2 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
  48. print("Usage: me_cleaner.py me_image.bin")
  49. else:
  50. with open(sys.argv[1], "r+b") as f:
  51. f.seek(0x10, 0)
  52. fpt_header = f.read(4)
  53. if fpt_header == b"$FPT":
  54. print("Found FPT header at 0x10")
  55. entries = unpack("<I", f.read(4))[0]
  56. print("Found {} partition(s)".format(entries))
  57. f.seek(0x02, 1)
  58. header_len = unpack("<B", f.read(1))[0]
  59. f.seek(0x30, 0)
  60. partitions = f.read(entries * 0x20)
  61. ftpr_header = b""
  62. ftpr_offset = 0
  63. ftpr_lenght = 0
  64. for i in range(entries):
  65. if partitions[i * 0x20:(i * 0x20) + 4] == b"FTPR":
  66. ftpr_header = partitions[i * 0x20:(i + 1) * 0x20]
  67. break
  68. if ftpr_header != b"":
  69. ftpr_offset, ftpr_lenght = unpack("<II", ftpr_header[0x08:0x10])
  70. print("Found FTPR header: FTPR partition spans from "
  71. "0x{:02x} to 0x{:02x}".format(ftpr_offset,
  72. ftpr_offset + ftpr_lenght))
  73. print("Removing extra partitions...")
  74. fill_range(f, 0x30, ftpr_offset, b"\xFF")
  75. f.seek(0, 2)
  76. fill_range(f, ftpr_offset + ftpr_lenght, f.tell(), b"\xFF")
  77. print("Removing extra partition entries in FPT...")
  78. f.seek(0x30, 0)
  79. f.write(ftpr_header)
  80. f.seek(0x14, 0)
  81. f.write(pack("<I", 1))
  82. print("Removing EFFS presence flag...")
  83. f.seek(0x24, 0)
  84. flags = unpack("<I", f.read(4))[0]
  85. flags &= ~(0x00000001)
  86. f.seek(0x24, 0)
  87. f.write(pack("<I", flags))
  88. print("Reading FTPR modules list...")
  89. f.seek(ftpr_offset + 0x1c, 0)
  90. if f.read(4) == b"$MN2":
  91. f.seek(ftpr_offset + 0x20, 0)
  92. num_modules = unpack("<I", f.read(4))[0]
  93. f.seek(ftpr_offset + 0x290, 0)
  94. mod_headers = [f.read(0x60) for i in range(0, num_modules)]
  95. if any(mod_h.startswith(b"$MME") for mod_h in mod_headers):
  96. f.seek(ftpr_offset + 0x18, 0)
  97. size = unpack("<I", f.read(4))[0]
  98. llut_start = ftpr_offset + (size * 4 + 0x3f) & ~0x3f
  99. f.seek(llut_start + 0x10, 0)
  100. huff_start, huff_size = unpack("<II", f.read(8))
  101. lzma_start = huff_start + huff_size - ftpr_offset
  102. f.seek(llut_start, 0)
  103. if f.read(4) == b"LLUT":
  104. for mod_header in mod_headers:
  105. remove_module(f, mod_header, ftpr_offset,
  106. lzma_start, ftpr_lenght)
  107. else:
  108. print("Can't find the LLUT region in the FTPR "
  109. "partition")
  110. else:
  111. print("Can't find the $MN2 modules in the FTPR "
  112. "partition")
  113. else:
  114. print("Wrong FTPR partition tag ({})")
  115. print("Correcting checksum...")
  116. # The checksum is just the two's complement of the sum of the
  117. # first 0x30 bytes (except for 0x1b, the checksum itself). In
  118. # other words, the sum of the first 0x30 bytes must be always
  119. # 0x00.
  120. f.seek(0x00, 0)
  121. checksum_bytes = f.read(0x30)
  122. f.seek(0x1b, 0)
  123. f.write(pack("B", (0x100 -
  124. (sum(checksum_bytes) - checksum_bytes[0x1b]) &
  125. 0xff) & 0xff))
  126. print("Done! Good luck!")
  127. else:
  128. print("FTPR header not found, this image doesn't seem to be "
  129. "valid")
  130. else:
  131. print("{} is not a valid Intel ME image (FPT not found)"
  132. .format(sys.argv[1]))