dissection: real RM-1127 GPT33/RKH/P6236, ffu_parse.py mmap, edp 10 ELFs
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
# Разбор реальных файлов RM-1127 (zern, fw/dl/)
|
||||||
|
|
||||||
|
Файлы лежат на zern `~/lumia-emu/fw/dl/` (в гит не входят):
|
||||||
|
- `RM1127_059X5R5_orig.ffu` 1800142848 B, CRC32 `a41e0775` = VPL — ЦЕЛ
|
||||||
|
- `RM1085_donor.ffu` 2995126272 B (донор WPInternals)
|
||||||
|
- `MPRG8909_fh.ede` 146180 B ELF32 ARM entry `0x8008700` (идентичен на lumiadb и archive.org)
|
||||||
|
- `RM1127_fh.edp` 3530240 B Emergency Payload, 10 ELF внутри
|
||||||
|
- `0x000940e100420050.mbn` 292392 B generic firehose (Nokia 8110, то же семейство)
|
||||||
|
- `.vpl/.dcp/.nvi/.xml` companions из archive.org
|
||||||
|
|
||||||
|
## Ключи
|
||||||
|
- `RKH: 56BDC3746226E080146D4D30C8C8432657A5282F`
|
||||||
|
- `MSM_ID: 009600E1, OEM_ID: 0045, MODEL_ID: 0000, SW_ID: 1A`
|
||||||
|
- `PlatformID: Microsoft.MSM8909.P6236`, OS `8.15.13333.494`, AntiTheft 1.1
|
||||||
|
- FFU: `SignedImage, chunk 128K, SHA256, catalog 2501, hashes 13730`, header `0xA0000`
|
||||||
|
|
||||||
|
## .edp: 10 ELF (смещение → что)
|
||||||
|
```
|
||||||
|
0: 22016 SBL1 (SBL1 Start/End, pm_sbl_boot.c), entry 0x8006b80
|
||||||
|
1: 293376 RPM.BF.2.1.1 (Oct 23 2015)
|
||||||
|
2: 461312 TZ.BF.2.5 (QSEE)
|
||||||
|
3: 1006080 UEFI EDK2 (Dec 29 2015, PCD strings)
|
||||||
|
4: 2237440 WINSECAPP (qcom.tz.winsecapp)
|
||||||
|
5: 2660864 UEFISecApp (qcom.tz.uefisecapp)
|
||||||
|
6-9: модемные блобы (Oct 15 2015, без строк)
|
||||||
|
```
|
||||||
|
`.ede` — маленький Sahara-загрузчик (577 строк, в основном серты), сам Firehose-XML живет дальше по цепочке. В SBL1-блобе XML-команд не видно в открытом виде.
|
||||||
|
|
||||||
|
## Реальная GPT 550 (Final, 33 раздела!)
|
||||||
|
Отличается от шаблона 950: `SBL1 LBA 90112` (не 32768!), `QSEE+QHEE` вместо TZ, есть `BOOTMODE`, `TZAPPS 16M`, нет APDP/MSADP/DPO/LIMITS/HYP.
|
||||||
|
|
||||||
|
| Раздел | LBA | Размер |
|
||||||
|
|---|---|---|
|
||||||
|
| DPP | 4096..20479 | 8M |
|
||||||
|
| MODEM_FSG/FS1/FS2 | 20480/24576/28672 | 1.5M x3 |
|
||||||
|
| MODEM_FSC | 32768..32799 | 16K |
|
||||||
|
| DDR | 36864..36927 | 32K |
|
||||||
|
| LogFS | 40960..73727 | 16M |
|
||||||
|
| SSD | 73728..73759 | 16K |
|
||||||
|
| UEFI_BS_NV / UEFI_RT_NV | 77824 / 81920 | 256K x2 |
|
||||||
|
| BOOTMODE | 86016..86047 | 16K |
|
||||||
|
| SBL1 | 90112..91135 | 512K |
|
||||||
|
| DBI | 94208..94271 | 32K |
|
||||||
|
| UEFI | 98304..102399 | 2M |
|
||||||
|
| RPM | 102400..103423 | 512K |
|
||||||
|
| QSEE | 106496..108543 | 1M |
|
||||||
|
| QHEE | 110592..111615 | 512K |
|
||||||
|
| WINSECAPP | 114688..115711 | 512K |
|
||||||
|
| TZAPPS | 118784..151551 | 16M |
|
||||||
|
| BACKUP_SBL1/DBI/UEFI/RPM/QSEE/QHEE/WINSECAPP/TZAPPS | 151552..212991 | — |
|
||||||
|
| PLAT (pconf.bin!) | 212992..229375 | 8M |
|
||||||
|
| EFIESP | 229376..360447 | 64M |
|
||||||
|
| MMOS | 360448..372735 | 6M |
|
||||||
|
| SEC | 372736..372767 | 16K |
|
||||||
|
| MainOS | 376832..4089855 | 1813M NTFS |
|
||||||
|
| Data | 4096000..15150847 | 5397M NTFS |
|
||||||
|
|
||||||
|
MinSectorCount 15154944 = eMMC 7400M. Парсер: `tools/ffu_parse.py` (mmap, ищет валидный GPT по rev/nent).
|
||||||
@@ -13,5 +13,7 @@
|
|||||||
*.vpl
|
*.vpl
|
||||||
*.rar
|
*.rar
|
||||||
*.zip
|
*.zip
|
||||||
|
*.log
|
||||||
|
*.aria2
|
||||||
!*.md
|
!*.md
|
||||||
!*.txt
|
!*.txt
|
||||||
|
|||||||
@@ -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