#!/usr/bin/env python3 """Снять CRC16 из сырого дампа Beken-флеша (FM100B/HF6853 модуль RT-4D). Формат Beken (bootloader/app партиции): каждые 32 байта данных сопровождаются 2 байтами CRC16 -> физические блоки по 34 байта. Сырой дамп/образ (FM100B_*.bin) содержит эти CRC, что «рвёт» строки и код. Этот тул выкидывает 2 CRC-байта из каждого 34-байтного блока -> чистая прошивка для реверса. Подтверждено на FM100B_V1.2.0.32: непечатные пары строго через 34 байта; после снятия строки («RF Firmware», «check failed», «beken378\\intercom») становятся целыми. python3 bk_deinterleave.py FM100B_raw.bin FM100B_clean.bin python3 bk_deinterleave.py --crc FM100B_raw.bin # только проверить период CRC Совместимо по формату с ltchiptool/bk7231tools (они делают то же при чтении флеша). """ import sys, argparse BLK = 34 # 32 данных + 2 CRC16 DATA = 32 def deinterleave(raw: bytes, phase: int = 0) -> bytes: out = bytearray(raw[:phase]) body = raw[phase:] for i in range(0, len(body), BLK): out += body[i:i+DATA] return bytes(out) def find_crc_period(raw: bytes, start=0, length=0x400): """вернуть наиболее частый шаг между непечатными байтами (ожидаем 34)""" from collections import Counter poss = [i for i in range(start, min(start+length, len(raw))) if not (32 <= raw[i] < 127 or raw[i] in (0, 10, 13))] steps = Counter(b-a for a, b in zip(poss, poss[1:])) return steps.most_common(5) def main(): ap = argparse.ArgumentParser() ap.add_argument("infile") ap.add_argument("outfile", nargs="?") ap.add_argument("--phase", type=int, default=0, help="смещение начала блоков (обычно 0)") ap.add_argument("--crc", action="store_true", help="только показать период CRC") a = ap.parse_args() raw = open(a.infile, "rb").read() if a.crc or not a.outfile: # поискать текстовую область для оценки периода for s in (0x15c000, 0x100000, 0x80000, 0): if s < len(raw): p = find_crc_period(raw, s) if p and p[0][0] == BLK: print(f"CRC-период {BLK} подтверждён @0x{s:x}: {p}") break else: print("период 34 не найден — возможно, образ уже чистый или другой формат") if not a.outfile: return clean = deinterleave(raw, a.phase) open(a.outfile, "wb").write(clean) print(f"{a.infile} ({len(raw)}) -> {a.outfile} ({len(clean)}), снято {len(raw)-len(clean)} CRC-байт") if __name__ == "__main__": main()