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,88 @@
|
||||
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
|
||||
Ссылка в новой задаче
Block a user