#!/usr/bin/env python3 """Зонд достижимости AMBE: при старте шлёт FM100B «enable» по диапазону команд, затем работает обычной рацией + снифер логирует всё, что модуль отдаёт. Идея: если какая-то из перебираемых команд включает выдачу голосового AMBE (модемный примитив ATC_RecordEn/RecvAmbe), то при входящем вызове в порт пойдёт поток ~27-байтовых кадров, которых в стоке нет. Диапазон сужаем бинарно, меняя LIST. python3 build_probe.py "0x40-0x6F" # первый прогон — весь диапазон python3 build_probe.py "0x50-0x5F" # сузить python3 tools/sniff.py 30 --raw # смотреть поток при вызове Безопасность: исключены 0x3E (прошивка модуля) и уже используемые команды. Основа rt4d_ru_batt.bin; откат — она же. """ import struct, sys from keystone import Ks, KS_ARCH_ARM, KS_MODE_THUMB BASE = 0x08002800 SRC = "stock-fw/rt4d_ru_batt.bin" OUT = "stock-fw/rt4d_probe.bin" ON_FRAME = 0x08006348 FRAME_CALL = 0x08018DD8 UI_HOOK = 0x08020806 TICK_FUNC = 0x0801DFD0 FM_SEND1 = 0x0801B044 # void fm100b_send1(cmd,b,sub,data, respbuf, timeout) TX_BYTE = 0x08018BFC # порт к ПК MAGIC_ADDR = 0x2000B000 MAGIC_VAL = 0x50524F42 # 'PROB' RESP_BUF = 0x2000B020 # scratch для respbuf fm100b_send1 USED = {0x3E, 0x42, 0x48, 0x49, 0x4C, 0x4D, 0x55, 0x57, 0x62, 0x05, 0x06, 0x07, 0x09, 0x0A, 0x25, 0x59, 0x60} EQ, NE, HS, LS = 0, 1, 2, 9 def parse_range(s): a, b = s.split("-") lo, hi = int(a, 16), int(b, 16) return [c for c in range(lo, hi + 1) if c not in USED] def thumb_bl(src, dst): off = (dst - (src + 4)) >> 1 s = (off >> 23) & 1; i1 = (off >> 22) & 1; i2 = (off >> 21) & 1 imm10 = (off >> 11) & 0x3FF; imm11 = off & 0x7FF j1 = (~i1 ^ s) & 1; j2 = (~i2 ^ s) & 1 return struct.pack(">1)&0x7FF)) else: out+=struct.pack(">1)&0xFF)) off+=self._sz(it) return bytes(out) def main(): rng = sys.argv[1] if len(sys.argv) > 1 else "0x40-0x6F" cand = parse_range(rng) print(f"диапазон {rng} -> {len(cand)} кандидатов: {' '.join('%02X'%c for c in cand)}") img = bytearray(open(SRC, "rb").read()) end = BASE + len(img); code_at = (end+3)&~3 img += b"\xff"*(code_at-end) ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB) def lo(a): return a & 0xFFFF def hi(a): return (a>>16)&0xFFFF # таблица кандидатов (байты) + завершающий 0x00 tbl = bytes(cand) + b"\x00" # ---- probe_once: разослать enable всем кандидатам ---- # табличка лежит сразу после кода; адрес вычислим после сборки — двухпроходно: # проще положить таблицу ПЕРЕД функцией и адресовать movw. tbl_at = code_at probe_at = tbl_at + len(tbl) if probe_at & 1: probe_at += 1 # выравнивание для кода pad = probe_at - (tbl_at + len(tbl)) p = Asm(ks, probe_at) p.asm(f""" push {{r4, r5, lr}} movw r4, #{lo(tbl_at)} movt r4, #{hi(tbl_at)} """) p.label("ploop") p.asm("ldrb r5, [r4]\ncmp r5, #0") p.b("pdone", EQ) # маркер в порт: F3 F3 cmd p.asm(f""" movs r0, #0xF3 bl #{TX_BYTE} movs r0, #0xF3 bl #{TX_BYTE} mov r0, r5 bl #{TX_BYTE} """) # fm100b_send1(cmd=r5, b=1, sub=1, data=1, respbuf, timeout=0x64) p.asm(f""" sub sp, #8 movw r0, #{lo(RESP_BUF)} movt r0, #{hi(RESP_BUF)} str r0, [sp] movs r0, #0x64 str r0, [sp, #4] mov r0, r5 movs r1, #1 movs r2, #1 movs r3, #1 bl #{FM_SEND1} add sp, #8 adds r4, #1 """) p.b("ploop") p.label("pdone") p.asm("pop {r4, r5, pc}") probe = p.build() # ---- tick_router: один раз вызвать probe_once ---- tick_at = probe_at + len(probe) r = Asm(ks, tick_at) r.asm(f""" push {{lr}} bl #{TICK_FUNC} movw r0, #{lo(MAGIC_ADDR)} movt r0, #{hi(MAGIC_ADDR)} ldr r1, [r0] movw r2, #{lo(MAGIC_VAL)} movt r2, #{hi(MAGIC_VAL)} cmp r1, r2 """) r.b("done", EQ) r.asm(f""" str r2, [r0] bl #{probe_at | 1} """) r.label("done") r.asm("pop {pc}") tick = r.build() # ---- ta_hook = снифер: F1 F2 len + кадр + passthrough ---- hook_at = tick_at + len(tick) h = Asm(ks, hook_at) h.asm(f""" push {{r4, r5, r6, lr}} mov r4, r0 ldrb r5, [r4, #6] ldrb r6, [r4, #7] orr.w r5, r6, r5, lsl #8 adds r5, #9 uxtb r5, r5 movs r0, #0xF1 bl #{TX_BYTE} movs r0, #0xF2 bl #{TX_BYTE} mov r0, r5 bl #{TX_BYTE} movs r6, #0 """) h.label("sloop") h.asm("cmp r6, r5") h.b("sdone", HS) h.asm(f""" ldrb r0, [r4, r6] bl #{TX_BYTE} adds r6, #1 """) h.b("sloop") h.label("sdone") h.asm(f""" mov r0, r4 bl #{ON_FRAME} pop {{r4, r5, r6, pc}} """) hook = h.build() code = tbl + b"\x00"*pad + probe + tick + hook img += code while len(img) % 4: img.append(0xFF) print(f"таблица 0x{tbl_at:08X}, probe 0x{probe_at:08X}, tick 0x{tick_at:08X}, hook 0x{hook_at:08X}") img[UI_HOOK-BASE:UI_HOOK-BASE+4] = thumb_bl(UI_HOOK, tick_at) img[FRAME_CALL-BASE:FRAME_CALL-BASE+4] = thumb_bl(FRAME_CALL, hook_at) open(OUT,"wb").write(bytes(img)) print(f"готово: {OUT}, {len(img)} Б") if __name__ == "__main__": main()