diff --git a/tools/ffu_extract.py b/tools/ffu_extract.py new file mode 100644 index 0000000..52aa0b8 --- /dev/null +++ b/tools/ffu_extract.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""ffu_extract.py — извлечение разделов из FFU v1 payload в файлы/eMMC-образ. +Модель (проверена на RM1127 orig): +- payload data chunk k лежит в файле по смещению DATA0 + k*131072, + DATA0 = первая граница чанка после заголовков (здесь 0xE0000). +- WriteDescriptor entries по порядку; entries с method-0 потребляют + чанки данных (holes method-2 пропускаются); disk chunk = LBA//256. +Использование: + ./tools/ffu_extract.py RM1127.ffu out-dir/ [LBA0 LBA1 ...] + без LBA — извлекает SBL1/DBI/UEFI/RPM/QSEE/QHEE/WINSECAPP/TZAPPS/PLAT/EFIESP + ./tools/ffu_extract.py RM1127.ffu --emmc-user out-user.bin +""" +import os +import struct +import sys + +CHUNK = 131072 + + +def load(ffu): + f = open(ffu, "rb") + sec = f.read(32) + _, _, chunk_kb, _, cat, hsh = struct.unpack(" datachunk + dc = 0 + for _ in range(wcnt): + f.seek(pos) + a, _ = struct.unpack(" (lba0, секторы) из tools/ffu_parse.py по RM1127 orig +PARTS = { + "SBL1": (90112, 1024), "DBI": (94208, 64), "UEFI": (98304, 4096), + "RPM": (102400, 1024), "QSEE": (106496, 2048), "QHEE": (110592, 1024), + "WINSECAPP": (114688, 1024), "TZAPPS": (118784, 32768), + "PLAT": (212992, 16384), "EFIESP": (229376, 131072), + "DPP": (4096, 16384), "MODEM_FSG": (20480, 6144), + "SSD": (73728, 32), "UEFI_BS_NV": (77824, 512), + "UEFI_RT_NV": (81920, 512), "BOOTMODE": (86016, 32), + "SEC": (372736, 32), +} + + +def chunk_of(f, data0, disk, dchunk): + if dchunk not in disk: + return None + f.seek(data0 + disk[dchunk] * CHUNK) + return f.read(CHUNK) + + +def main(): + ffu, out = sys.argv[1], sys.argv[2] + f, data0, disk = load(ffu) + print("data0:", hex(data0), "mapped chunks:", len(disk)) + if out == "--emmc-user": + dest = sys.argv[3] + with open(dest, "wb") as o: + o.truncate(15154944 * 512) + for name, (lba, n) in PARTS.items(): + for s in range(0, n, 256): + c = chunk_of(f, data0, disk, (lba + s) // 256) + if c is None: + continue + o.seek((lba + s) * 512) + o.write(c[:min(n - s, 256) * 512]) + print("wrote", dest) + return + os.makedirs(out, exist_ok=True) + names = sys.argv[3:] or list(PARTS) + for name in names: + lba, n = PARTS[name] + with open(os.path.join(out, name + ".bin"), "wb") as o: + for s in range(0, n, 256): + c = chunk_of(f, data0, disk, (lba + s) // 256) + o.write(c[:min(n - s, 256) * 512] if c else b"\x00" * 512) + print("wrote", name) + + +if __name__ == "__main__": + main()