tools: ffu_extract.py (WriteDescriptor->LBA map)
This commit is contained in:
@@ -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("<I12sIIII", sec)
|
||||||
|
assert chunk_kb * 1024 == CHUNK
|
||||||
|
off = 32 + cat + hsh
|
||||||
|
off += (CHUNK - off % CHUNK) % CHUNK
|
||||||
|
f.seek(off)
|
||||||
|
ih = f.read(24)
|
||||||
|
mlen = struct.unpack("<I", ih[16:20])[0]
|
||||||
|
man = f.read(mlen)
|
||||||
|
st = off + 24 + mlen
|
||||||
|
f.seek(st + 204)
|
||||||
|
vals = struct.unpack("<11I", f.read(44))
|
||||||
|
wcnt = vals[1]
|
||||||
|
pos = st + 248
|
||||||
|
disk = {} # diskchunk -> datachunk
|
||||||
|
dc = 0
|
||||||
|
for _ in range(wcnt):
|
||||||
|
f.seek(pos)
|
||||||
|
a, _ = struct.unpack("<II", f.read(8))
|
||||||
|
locs = [struct.unpack("<II", f.read(8)) for _ in range(a)]
|
||||||
|
pos += 8 + 8 * a
|
||||||
|
for m, idx in locs:
|
||||||
|
if m == 0:
|
||||||
|
disk[idx] = dc
|
||||||
|
dc += 1
|
||||||
|
data0 = st + 248 + vals[2]
|
||||||
|
data0 += (CHUNK - data0 % CHUNK) % CHUNK
|
||||||
|
return f, data0, disk
|
||||||
|
|
||||||
|
|
||||||
|
# имя -> (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()
|
||||||
Reference in New Issue
Block a user