Files
viktorиClaude Opus 4.8 ae36c3b729 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>
2026-07-08 15:47:22 +09:00

89 строки
3.0 KiB
Python

import re
from collections import Counter
data = open(r"C:/Users/vikto/Documents/Claude/rt-4d/radio-spi-dump.bin","rb").read()
REGIONS = [
("calibration",0x000000,0x001000),("main_settings",0x002000,0x001000),
("channels",0x004000,0x00C000),("zones",0x01C000,0x020000),
("contacts",0x05C000,0x010000),("groups",0x07C000,0x003000),
("dmr_keys",0x082000,0x003000),("call_log",0x088000,0x00C000),
("default_sms",0x094000,0x001000),("msg_drafts",0x095000,0x010000),
("msg_inbox",0x0A5000,0x010000),("msg_outbox",0x0B5000,0x010000),
("schedules",0x0C6000,0x008000),("dtmf_names",0x0C7000,0x000100),
("fm_settings",0x0D6000,0x001000),
# extended discovered
("key_aliases(0xD0000)",0x0D0000,0x006000),
("cjk_codepoint_table",0x14C000,0x00B000),
]
def region_of(a):
for n,s,sz in REGIONS:
if s<=a<s+sz: return n
return "UNMAPPED"
def is_word(s):
"""Genuine human string: has a >=3 alpha run and is not a punctuation/char-ramp."""
s=s.strip()
if len(s)<2: return False
if not re.search(r"[A-Za-z]{2,}", s) and not re.search(r"[A-Za-z][a-z]", s):
# allow pure digit/label like "TG6"
if not re.search(r"[A-Za-z]", s): return False
# reject monotone char ramps / repeats
if re.fullmatch(r"(.)\1*", s): return False
letters=[c for c in s if c.isalpha()]
if letters:
# reject alphabet ramps like KLMNOP / HIJK (consecutive ordinals dominating)
seq=sum(1 for a,b in zip(letters,letters[1:]) if abs(ord(a)-ord(b))<=1)
if seq/max(1,len(letters)-1) > 0.8 and len(set(letters))>3:
return False
if len(set(s))<=2 and len(s)>=4: return False
return True
found=[]
# ASCII
run=None
for i,b in enumerate(data):
if 0x20<=b<=0x7E:
run=i if run is None else run
else:
if run is not None:
s=data[run:i].decode('latin-1')
if len(s)>=3 and is_word(s): found.append((run,'ascii',region_of(run),s))
run=None
# UTF16LE
i=0;n=len(data)
while i<n-1:
if 0x20<=data[i]<=0x7E and data[i+1]==0:
j=i;ch=[]
while j<n-1 and data[j+1]==0 and 0x20<=data[j]<=0x7E:
ch.append(chr(data[j])); j+=2
s=''.join(ch)
if len(s)>=3 and is_word(s): found.append((i,'utf16',region_of(i),s))
i=max(j,i+2)
else: i+=1
# dedup identical (addr,text)
seen=set();uniq=[]
for r in found:
k=(r[0],r[3])
if k in seen: continue
seen.add(k); uniq.append(r)
print("TOTAL genuine human-readable strings:", len(uniq))
print("By region:", dict(Counter(r[2] for r in uniq)))
print("By encoding:", dict(Counter(r[1] for r in uniq)))
print()
# collapse Key-N and dump unique text values per region
byreg={}
for a,e,reg,t in uniq: byreg.setdefault(reg,[]).append((a,t))
for reg in byreg:
vals=byreg[reg]
print(f"--- {reg} ({len(vals)} strings) ---")
# show distinct text (first 30)
shown=0
for a,t in vals:
print(f" 0x{a:06X} {t!r}")
shown+=1
if shown>=30:
print(f" ... (+{len(vals)-30} more)")
break