Files

69 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Linear-sweep Thumb disassembly of DA14585 firmware.
Code region = file[0x40:], mapped base 0x07FC0000.
- 16KB-aligned linear sweep; on decode failure emit .word and resync (+2).
- Function prologue detection: PUSH {...,lr} (encoding 0xB5xx).
Output: analysis/firmware/fw.asm (addr: bytes mnemonic [;;FUNC markers])
"""
from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB, CS_MODE_LITTLE_ENDIAN
IMG = "app/firmware-PP_da14585_4.2_CH.img"
OUT = "analysis/firmware/fw.asm"
BASE = 0x07FC0000
HDR = 0x40
def main():
data = open(IMG, "rb").read()[HDR:]
md = Cs(CS_ARCH_ARM, CS_MODE_THUMB | CS_MODE_LITTLE_ENDIAN)
md.detail = False
out = open(OUT, "w")
out.write(f"; DA14585 firmware linear sweep, base 0x{BASE:08x}, {len(data)} bytes\n")
off = 0
n_ins = 0
n_bad = 0
funcs = []
while off < len(data):
chunk = data[off:off+0x2000]
addr = BASE + off
consumed = 0
for ins in md.disasm(chunk, addr):
# skip if disassembler resync'd mid-chunk oddly
if ins.address < addr + consumed:
continue
# fill any gap (undecodable bytes) with .word
gap = ins.address - (addr + consumed)
while gap > 0:
g = addr + consumed
raw = data[off+consumed:off+consumed+2]
if len(raw) == 2:
w = raw[0] | raw[1] << 8
out.write(f"0x{g:08x}: {w:04x} .word 0x{w:04x}\n")
else:
out.write(f"0x{g:08x}: {raw.hex():<9s} .byte {raw.hex()}\n")
consumed += 2
gap -= 2
n_bad += 1
b = ins.bytes
# prologue: PUSH {...,lr} => 0xB5xx ; also push {lr} alone? (B500)
mark = ""
if len(b) == 2 and b[1] == 0xB5:
mark = " ;;FUNC"
funcs.append(ins.address)
out.write(f"0x{ins.address:08x}: {b.hex():<9s} {ins.mnemonic} {ins.op_str}{mark}\n")
consumed = ins.address + ins.size - addr
n_ins += 1
off += consumed if consumed else 2
if consumed == 0:
n_bad += 1
out.write(f"; instructions={n_ins} badwords={n_bad} functions={len(funcs)}\n")
out.close()
print(f"instructions={n_ins} badwords={n_bad} prologues={len(funcs)}")
with open("analysis/firmware/funcs.txt", "w") as f:
for a in funcs:
f.write(f"0x{a:08x}\n")
if __name__ == "__main__":
main()