Files
qianbian/analysis/firmware/find_cmds.py

56 lines
2.0 KiB
Python

#!/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()