RT-4D: реверс прошивки, русификация, кастомный UI, флешеры
- Полный RE стока V3.25 (Cortex-M4F) + FM100B: карта памяти, протокол, codeplug, UI-архитектура - Русификация: свой CP1251-шрифт + патч рендера, перевод меню и надписей, ребренд Ru-4D V3.25 - Блюпринт переделки UI + C-тулчейн (clang thumbv7em), доказан инъекцией - Готовые флешеры: WebSerial .html и Windows .exe со вшитой прошивкой - Дамп SPI рации, стоковая прошивка, инструменты сборки Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Этот коммит содержится в:
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Robust RT-4D application flasher (bootloader / 0x39 protocol).
|
||||
|
||||
Replicates the official DualTachyon flasher-cli handshake:
|
||||
1. 0xFF probe — spam 0xFF until the bootloader echoes 0xFF
|
||||
2. erase — two-part official-updater sequence (checksum seed 0x48)
|
||||
[0x39,0x33,0x05,0x10] then [0x39,0x33,0x05,0x55]
|
||||
3. write — [0x57, off>>8, off] + 1024B, checksum seed 0x48, ACK 0x06
|
||||
|
||||
Only the APPLICATION region is erased/written; the bootloader at
|
||||
0x08000000..0x08002800 is never touched, so a failed flash is always
|
||||
recoverable by reflashing the stock app image.
|
||||
|
||||
Usage: python flash_rt4d.py COM7 <firmware.bin>
|
||||
"""
|
||||
import sys, time, serial
|
||||
|
||||
SEED = 0x48
|
||||
ACK = 0x06
|
||||
|
||||
|
||||
def cksum(payload):
|
||||
s = SEED
|
||||
for b in payload:
|
||||
s = (s + b) & 0xFF
|
||||
return bytes(payload) + bytes([s])
|
||||
|
||||
|
||||
def handshake(p, timeout=5.0):
|
||||
p.reset_input_buffer()
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < timeout:
|
||||
b = p.read(1)
|
||||
if not b:
|
||||
p.write(bytes([0xFF]))
|
||||
elif b == b"\xff":
|
||||
return True
|
||||
# ignore any other stray byte and keep probing
|
||||
return False
|
||||
|
||||
|
||||
def erase(p):
|
||||
for trig in (0x10, 0x55):
|
||||
p.reset_input_buffer()
|
||||
p.write(cksum([0x39, 0x33, 0x05, trig]))
|
||||
# wait up to ~4s for ACK
|
||||
ok = False
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < 4.0:
|
||||
b = p.read(1)
|
||||
if b == bytes([ACK]):
|
||||
ok = True
|
||||
break
|
||||
if b: # unexpected byte
|
||||
pass
|
||||
if not ok:
|
||||
raise SystemExit(f"[E] erase phase 0x{trig:02X}: no ACK")
|
||||
print(f"[i] erase 0x{trig:02X}: ACK")
|
||||
|
||||
|
||||
def write_fw(p, fw):
|
||||
# pad up to a 1024 multiple with 0xFF (erased-flash value)
|
||||
pad = (-len(fw)) % 1024
|
||||
data = fw + b"\xff" * pad
|
||||
total = len(data)
|
||||
for off in range(0, total, 1024):
|
||||
chunk = data[off:off + 1024]
|
||||
payload = bytes([0x57, (off >> 8) & 0xFF, off & 0xFF]) + chunk
|
||||
p.reset_input_buffer()
|
||||
p.write(cksum(payload))
|
||||
# ACK
|
||||
ok = False
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < 2.0:
|
||||
b = p.read(1)
|
||||
if b == bytes([ACK]):
|
||||
ok = True
|
||||
break
|
||||
if not ok:
|
||||
raise SystemExit(f"\n[E] write at 0x{off:05X}: no ACK "
|
||||
f"(wrote {off}/{total})")
|
||||
pct = (off + len(chunk)) * 100 // total
|
||||
print(f"\r[i] writing 0x{off:05X}/{total:#07x} ({pct:3d}%)", end="", flush=True)
|
||||
print("\n[i] write complete")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
raise SystemExit(__doc__)
|
||||
port, path = sys.argv[1], sys.argv[2]
|
||||
fw = open(path, "rb").read()
|
||||
print(f"[i] firmware {path}: {len(fw)} bytes (0x{len(fw):X})")
|
||||
p = serial.Serial(port, 115200, timeout=0.3, write_timeout=3)
|
||||
try:
|
||||
print("[i] handshake (0xFF probe)...")
|
||||
if not handshake(p):
|
||||
raise SystemExit("[E] no bootloader (radio not in flash mode). "
|
||||
"Power off, hold * (or the flash-mode key) and power on.")
|
||||
print("[i] bootloader ready")
|
||||
print("[i] erasing application region...")
|
||||
erase(p)
|
||||
print("[i] flashing...")
|
||||
write_fw(p, fw)
|
||||
print("[i] DONE. Power-cycle the radio normally.")
|
||||
finally:
|
||||
p.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Ссылка в новой задаче
Block a user