feat(ppclock): protocol.py 协议编解码 23 测试全绿(BCD语义经JS证据校正)

This commit is contained in:
agent committed 2026-07-24 10:43:33 +00:00
1 parent 190182fb2e
commit 6f5b0c60be
11 files changed
+32843

No files matched your search

+68
View File
@@ -0,0 +1,68 @@
#!/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()
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Scan firmware for command-dispatch CMP-immediate and branch-switch patterns."""
from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB, CS_MODE_LITTLE_ENDIAN
import struct, collections
IMG = "app/firmware-PP_da14585_4.2_CH.img"
BASE = 0x07FC0000
HDR = 0x40
CODE_END = 0x119e4 # code region size
CMDS = {0xdd:"time", 0xe2:"commit", 0x03:"img_blk_full", 0x04:"img_blk_small",
0x01:"refresh", 0xAA:"refresh_small", 0xEF:"activate", 0xE6:"lut_cal",
0xFB:"sleep_time", 0xEA:"slot", 0xFA:"countdown_off", 0xE1:"mode",
0xE3:"invert", 0xE4:"font", 0xE5:"rotate", 0x23:"bw_3color", 0x14:"12_24h",
0x15:"clk_color", 0x99:"cal_text", 0x00:"refresh_step1", 0xE9:"carousel"}
def main():
data = open(IMG, "rb").read()[HDR:HDR+CODE_END]
md = Cs(CS_ARCH_ARM, CS_MODE_THUMB | CS_MODE_LITTLE_ENDIAN)
# collect all instructions via same sweep
insns = []
off = 0
while off < len(data):
chunk = data[off:off+0x2000]
addr = BASE + off
consumed = 0
for ins in md.disasm(chunk, addr):
if ins.address < addr + consumed:
continue
gap = ins.address - (addr + consumed)
if gap:
consumed += (gap // 2) * 2
insns.append(ins)
consumed = ins.address + ins.size - addr
off += consumed if consumed else 2
# CMP Rn, #imm with imm in CMDS
hits = collections.defaultdict(list)
for ins in insns:
if ins.mnemonic == "cmp":
parts = ins.op_str.split(",")
if len(parts) == 2 and parts[1].strip().startswith("#"):
try:
v = int(parts[1].strip()[1:], 0)
except ValueError:
continue
if v in CMDS:
hits[v].append((ins.address, ins.op_str))
for v in sorted(hits):
print(f"--- CMP #0x{v:02x} ({CMDS[v]}): {len(hits[v])} hits ---")
for a, ops in hits[v]:
print(f" 0x{a:08x}: cmp {ops}")
if __name__ == "__main__":
main()
+273
View File
@@ -0,0 +1,273 @@
0x07fc0574
0x07fc05ec
0x07fc0634
0x07fc06ac
0x07fc0734
0x07fc07ea
0x07fc081a
0x07fc082e
0x07fc08bc
0x07fc0a24
0x07fc0b18
0x07fc0b3e
0x07fc0c34
0x07fc0c7a
0x07fc0d9c
0x07fc0dea
0x07fc0e58
0x07fc0f6c
0x07fc11d8
0x07fc1504
0x07fc154a
0x07fc15b8
0x07fc16ea
0x07fc1754
0x07fc17e6
0x07fc1a56
0x07fc1ab8
0x07fc1b0c
0x07fc1b7c
0x07fc1bcc
0x07fc1c54
0x07fc1cc6
0x07fc1d28
0x07fc1e8c
0x07fc1f5c
0x07fc204c
0x07fc207c
0x07fc209e
0x07fc20c4
0x07fc20de
0x07fc2184
0x07fc21ac
0x07fc21c8
0x07fc221c
0x07fc238c
0x07fc23d0
0x07fc2574
0x07fc2628
0x07fc2788
0x07fc27c4
0x07fc2b3c
0x07fc2c48
0x07fc2cac
0x07fc2da4
0x07fc2dfc
0x07fc3098
0x07fc3154
0x07fc3190
0x07fc31b4
0x07fc32bc
0x07fc3344
0x07fc336c
0x07fc33a8
0x07fc33d0
0x07fc3404
0x07fc3440
0x07fc359c
0x07fc3614
0x07fc361e
0x07fc3628
0x07fc3632
0x07fc363c
0x07fc3646
0x07fc3658
0x07fc36c8
0x07fc3710
0x07fc37a8
0x07fc3854
0x07fc3920
0x07fc3a38
0x07fc3c54
0x07fc3d20
0x07fc3e44
0x07fc48f4
0x07fc4988
0x07fc4bf8
0x07fc4c38
0x07fc4cdc
0x07fc4d50
0x07fc4e20
0x07fc4f90
0x07fc50c4
0x07fc5104
0x07fc519c
0x07fc51d6
0x07fc522c
0x07fc52f4
0x07fc5354
0x07fc53e8
0x07fc546c
0x07fc54bc
0x07fc5504
0x07fc554c
0x07fc5598
0x07fc55b8
0x07fc55e0
0x07fc5678
0x07fc56b0
0x07fc570c
0x07fc5744
0x07fc5764
0x07fc5798
0x07fc57ec
0x07fc582c
0x07fc587c
0x07fc58e0
0x07fc5974
0x07fc5a2c
0x07fc5a6c
0x07fc5a8c
0x07fc5ac4
0x07fc5adc
0x07fc5afc
0x07fc5b0c
0x07fc5b7c
0x07fc5bb0
0x07fc5bd0
0x07fc5c4c
0x07fc5ca4
0x07fc5dd4
0x07fc5df0
0x07fc5e0c
0x07fc5ec0
0x07fc5edc
0x07fc5f5c
0x07fc6078
0x07fc60d8
0x07fc6240
0x07fc62a8
0x07fc62f8
0x07fc631c
0x07fc6374
0x07fc641c
0x07fc6434
0x07fc64bc
0x07fc6540
0x07fc6590
0x07fc65d0
0x07fc6634
0x07fc6680
0x07fc66cc
0x07fc66f4
0x07fc67f8
0x07fc6814
0x07fc683c
0x07fc6aac
0x07fc6b24
0x07fc6b9c
0x07fc6c1c
0x07fc6c40
0x07fc6cb8
0x07fc6cda
0x07fc6ce2
0x07fc6cec
0x07fc6e98
0x07fc6fa0
0x07fc6fc8
0x07fc70a0
0x07fc715c
0x07fc71e0
0x07fc7268
0x07fc7290
0x07fc7374
0x07fc7430
0x07fc7528
0x07fc7618
0x07fc7768
0x07fc778c
0x07fc78b0
0x07fc793c
0x07fc79d0
0x07fc79f8
0x07fc7a58
0x07fc7b54
0x07fc7b94
0x07fc7c6c
0x07fc7cc4
0x07fc7d18
0x07fc7d2c
0x07fc7d3c
0x07fc7e1c
0x07fc7ee4
0x07fc8010
0x07fc80c0
0x07fc826c
0x07fc8340
0x07fc852c
0x07fc8598
0x07fc85e8
0x07fc8610
0x07fc8648
0x07fc9784
0x07fc97a0
0x07fc97cc
0x07fc9b24
0x07fc9ba0
0x07fc9bc4
0x07fc9bcc
0x07fc9bd4
0x07fca22c
0x07fca2dc
0x07fca39c
0x07fca3cc
0x07fca41c
0x07fca518
0x07fca588
0x07fca5b4
0x07fca5f8
0x07fca620
0x07fca648
0x07fca6e4
0x07fca718
0x07fca814
0x07fca880
0x07fca974
0x07fcaa68
0x07fcaad8
0x07fcaae8
0x07fcaafc
0x07fcab74
0x07fcaba2
0x07fcabb4
0x07fcabf4
0x07fcac54
0x07fcac68
0x07fcac80
0x07fcac9c
0x07fcacc4
0x07fcad74
0x07fcadac
0x07fcae24
0x07fcae4c
0x07fcae8c
0x07fcaea4
0x07fcaeda
0x07fcaeec
0x07fcaf20
0x07fcb3b8
0x07fcb3f0
0x07fcb6b8
0x07fcb6f8
0x07fcb824
0x07fcb848
0x07fcb86c
0x07fcb8ec
0x07fcb93c
0x07fcb9bc
0x07fcbb10
0x07fcbc64
0x07fcbde4
0x07fcc1c6
0x07fcc238
0x07fcc660
0x07fcc680
0x07fcc770
0x07fced48
0x07fcf5f2
0x07fd00fa
0x07fd06ec
0x07fd0a84
0x07fd0f2c
0x07fd0f46
0x07fd0ffc
File diff suppressed because it is too large. Load diff