55 lines
1.8 KiB
Python
55 lines
1.8 KiB
Python
"""DA14585 SUOTA 镜像解析/重打包(tools 交付物)。
|
||
格式(证据:app/ 样本 + CRC 反推验证):
|
||
[0:2] magic 'pQ' = 0x7051(LE)
|
||
[2:4] flags = 0x01AA
|
||
[4:8] body 长度(= 文件长 - 64)
|
||
[8:12] CRC32(body)(zlib 多项式,LE)
|
||
[12:64] 0xFF 填充
|
||
[64:] 镜像体,起始为 Cortex-M 向量表(SP, Reset, ...),加载基址 0x07FC0000
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import struct
|
||
import zlib
|
||
|
||
MAGIC_BYTES = b"pQ" # 文件头两字节 70 51
|
||
MAGIC = 0x7051
|
||
FLAGS_DEFAULT = 0x01AA
|
||
HEADER_SIZE = 64
|
||
LOAD_BASE = 0x07FC0000
|
||
|
||
|
||
def parse_image(data: bytes) -> dict:
|
||
if len(data) < HEADER_SIZE:
|
||
raise ValueError("文件小于 64 字节头")
|
||
if data[0:2] != MAGIC_BYTES:
|
||
raise ValueError(f"magic 错误: {data[0:2].hex()}(期望 {MAGIC_BYTES.hex()}='pQ')")
|
||
flags, body_size, crc = struct.unpack_from("<HII", data, 2)
|
||
body = data[HEADER_SIZE:HEADER_SIZE + body_size]
|
||
sp, reset = struct.unpack_from("<II", body, 0)
|
||
return {
|
||
"magic": MAGIC,
|
||
"flags": flags,
|
||
"body_size": body_size,
|
||
"crc_stored": crc,
|
||
"crc_valid": (zlib.crc32(body) & 0xFFFFFFFF) == crc,
|
||
"initial_sp": sp,
|
||
"reset_handler": reset,
|
||
"load_base": LOAD_BASE,
|
||
"file_size": len(data),
|
||
}
|
||
|
||
|
||
def unpack_body(data: bytes) -> bytes:
|
||
info = parse_image(data)
|
||
return data[HEADER_SIZE:HEADER_SIZE + info["body_size"]]
|
||
|
||
|
||
def pack_image(body: bytes, flags: int = FLAGS_DEFAULT) -> bytes:
|
||
header = MAGIC_BYTES + struct.pack("<HII", flags, len(body),
|
||
zlib.crc32(body) & 0xFFFFFFFF)
|
||
# 填充:黄金样本 0x20 处为 0x00(含义未知,保留以字节级一致),其余 0xFF
|
||
pad = bytearray(b"\xFF" * (HEADER_SIZE - len(header)))
|
||
pad[0x20 - len(header)] = 0x00
|
||
return header + bytes(pad) + body
|