dissection: real RM-1127 GPT33/RKH/P6236, ffu_parse.py mmap, edp 10 ELFs
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ffu_parse.py — разбор FFU v1 (Lumia): security header, manifest, GPT, store.
|
||||
Использование: ./tools/ffu_parse.py fw/dl/XXX.ffu [--gpt] [--manifest]
|
||||
WIP: только чтение, без проверки подписей/hashtable.
|
||||
"""
|
||||
import struct
|
||||
import sys
|
||||
import uuid
|
||||
import mmap
|
||||
|
||||
ACC = {0: "BEGIN", 1: "SEQ", 2: "END"}
|
||||
|
||||
|
||||
def guid_le(b):
|
||||
return str(uuid.UUID(bytes_le=b))
|
||||
|
||||
|
||||
def parse(path):
|
||||
raw = open(path, "rb")
|
||||
f = mmap.mmap(raw.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
sec = f.read(32)
|
||||
cb, sig, chunk_kb, alg, cat_sz, hash_sz = struct.unpack("<I12sIIII", sec)
|
||||
assert sig == b"SignedImage ", "not an FFU"
|
||||
chunk = chunk_kb * 1024
|
||||
off = 32 + cat_sz + hash_sz
|
||||
off += (chunk - off % chunk) % chunk
|
||||
f.seek(off)
|
||||
ih = f.read(24)
|
||||
mlen, csize = struct.unpack("<II", ih[16:24])
|
||||
man = f.read(mlen).decode("ascii", errors="replace")
|
||||
hdr_size = off + 24 + mlen
|
||||
hdr_size += (chunk - hdr_size % chunk) % chunk
|
||||
info = {
|
||||
"chunk": chunk, "catalog": cat_sz, "hashes": hash_sz // 32,
|
||||
"manifest_len": mlen, "header_size": hdr_size,
|
||||
}
|
||||
for tag in ["DevicePlatformId0", "OSVersion", "AntiTheftVersion",
|
||||
"SectorSize", "MinSectorCount"]:
|
||||
import re
|
||||
m = re.search(tag + r"\s*=\s*([^\r\n]+)", man)
|
||||
info[tag] = m.group(1).strip() if m else "?"
|
||||
# store header
|
||||
f.seek(hdr_size)
|
||||
sh = f.read(264)
|
||||
bs, wdc = struct.unpack("<I", sh[224:228])[0], struct.unpack("<I", sh[228:232])[0]
|
||||
info["block_size"] = bs
|
||||
descs = []
|
||||
for _ in range(wdc):
|
||||
loc_cnt, blk_cnt = struct.unpack("<II", f.read(8))
|
||||
locs = [struct.unpack("<II", f.read(8)) for _ in range(loc_cnt)]
|
||||
descs.append((blk_cnt, locs))
|
||||
info["write_descs"] = len(descs)
|
||||
info["payload_blocks"] = sum(b for b, _ in descs)
|
||||
# GPT lives somewhere in payload (sparse); find VALID header
|
||||
gpt_off = -1
|
||||
o = hdr_size
|
||||
while True:
|
||||
o = f.find(b"EFI PART", o)
|
||||
if o < 0:
|
||||
break
|
||||
hdr = bytes(f[o:o + 92])
|
||||
rev, hsz = struct.unpack("<II", hdr[8:16])
|
||||
nent0, esz0 = struct.unpack("<II", hdr[80:88])
|
||||
if rev == 0x10000 and hsz == 92 and nent0 == 128 and esz0 == 128:
|
||||
gpt_off = o
|
||||
break
|
||||
o += 8
|
||||
assert gpt_off > 0, "no valid GPT"
|
||||
assert gpt_off > 0, "no GPT"
|
||||
info["gpt_chunk"] = (gpt_off - hdr_size) // chunk
|
||||
hdr = bytes(f[gpt_off:gpt_off + 92])
|
||||
assert hdr[:8] == b"EFI PART", "no GPT"
|
||||
nent, esz = struct.unpack("<II", hdr[80:88])
|
||||
parts = []
|
||||
ent_off = gpt_off + 512 # entries start at next sector after header
|
||||
for _ in range(nent):
|
||||
e = bytes(f[ent_off:ent_off + esz])
|
||||
ent_off += esz
|
||||
t, u = e[:16], e[16:32]
|
||||
if t == b"\x00" * 16:
|
||||
continue
|
||||
lba0, lba1, attr = struct.unpack("<QQQ", e[32:56])
|
||||
name = e[56:128].decode("utf-16-le").rstrip("\x00")
|
||||
parts.append((name, lba0, lba1, guid_le(t)))
|
||||
f.close()
|
||||
return info, parts, man
|
||||
|
||||
|
||||
def main():
|
||||
path = sys.argv[1]
|
||||
info, parts, man = parse(path)
|
||||
for k, v in info.items():
|
||||
if k != "write_descs":
|
||||
print(f"{k}: {v}")
|
||||
if "--manifest" in sys.argv:
|
||||
print("---- manifest ----")
|
||||
print(man)
|
||||
if "--gpt" in sys.argv or True:
|
||||
print(f"---- GPT: {len(parts)} partitions ----")
|
||||
for name, a, b, t in parts:
|
||||
print(f"{name:16s} LBA {a:10d}..{b:<10d} size {(b-a+1)*512//1024:7d}K {t}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user