- Полный 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>
89 строки
3.0 KiB
Python
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
|