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,95 @@
|
||||
import struct, collections, sys, io
|
||||
sys.stdout=io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
DUMP=r"C:/Users/vikto/Documents/Claude/rt-4d/radio-spi-dump.bin"
|
||||
data=open(DUMP,'rb').read()
|
||||
|
||||
def hd(off,n=128,base=None):
|
||||
base=off if base is None else base
|
||||
out=[]
|
||||
for i in range(0,n,16):
|
||||
c=data[off+i:off+i+16]
|
||||
if not c:break
|
||||
h=' '.join(f'{b:02x}' for b in c)
|
||||
a=''.join(chr(b) if 32<=b<127 else '.' for b in c)
|
||||
out.append(f'{base+i:08x} {h:<47} {a}')
|
||||
return '\n'.join(out)
|
||||
|
||||
# 0x14C000: GBK char table. Decode as GBK 2-byte
|
||||
print("### 0x14C000 GBK char list (first 40 chars) ###")
|
||||
seg=data[0x14c000:0x14c000+80]
|
||||
chars=[]
|
||||
i=0
|
||||
while i < len(seg)-1:
|
||||
b=seg[i]
|
||||
if b>=0x81:
|
||||
try:
|
||||
ch=bytes([seg[i],seg[i+1]]).decode('gbk')
|
||||
chars.append(ch); i+=2; continue
|
||||
except: pass
|
||||
i+=1
|
||||
print(''.join(chars))
|
||||
# Size and how it ends
|
||||
end=0x14c000+0xB000
|
||||
print("tail of 0x14C000 region:")
|
||||
print(hd(end-64,64))
|
||||
|
||||
# Correlate: 0x14C000 is a GBK char table (34KB?) and 0x164000 pinyin (200KB).
|
||||
# The pinyin region: are entries fixed 6 bytes each -> index maps to char in char table?
|
||||
print("\n### relationship: char count vs pinyin count ###")
|
||||
# count 2-byte gbk chars in 0x14C000..0x157000 (44KB used, then zeros)
|
||||
charseg=data[0x14c000:0x157000]
|
||||
nchars=0
|
||||
i=0
|
||||
while i<len(charseg)-1:
|
||||
if charseg[i]>=0x81 and charseg[i+1]!=0x00:
|
||||
nchars+=1; i+=2
|
||||
else:
|
||||
i+=1
|
||||
print(f"approx gbk chars in 0x14C000-0x157000 (44KB): {nchars}")
|
||||
# pinyin at 0x164000, 6 bytes each, over 200KB
|
||||
print(f"pinyin entries if 6-byte: {0x32000//6}")
|
||||
print(f"pinyin entries if 8-byte: {0x32000//8}")
|
||||
|
||||
# Check pinyin stride precisely: look for the pattern of fixed-width fields
|
||||
print("\n### pinyin stride check: line up 'kao','shang','xia' ###")
|
||||
# they appeared as 6-char fields: 'kao ','shang ','xia '
|
||||
for w in range(4,10):
|
||||
# test if every field is exactly w and space padded
|
||||
ok=True
|
||||
for k in range(20):
|
||||
f=data[0x164000+k*w:0x164000+k*w+w]
|
||||
# must be ascii letters+spaces
|
||||
if not all(32<=b<127 for b in f): ok=False;break
|
||||
if ok:
|
||||
fields=[data[0x164000+k*w:0x164000+k*w+w] for k in range(12)]
|
||||
print(f"w={w}: {[f.decode() for f in fields]}")
|
||||
|
||||
# 0x100000 region: mostly zeros with some ascii. dump non-zero area
|
||||
print("\n### 0x100000 non-zero content ###")
|
||||
for i in range(0,0x1000,16):
|
||||
c=data[0x100000+i:0x100000+i+16]
|
||||
if any(b!=0 for b in c):
|
||||
h=' '.join(f'{b:02x}' for b in c)
|
||||
a=''.join(chr(b) if 32<=b<127 else '.' for b in c)
|
||||
print(f'{0x100000+i:08x} {h:<47} {a}')
|
||||
|
||||
# 0x03E000 DMRhub: bytes 0-3 = 00 00 00 00 then name. This resembles radio identity/owner?
|
||||
# main_settings radio_name at cfg+76. Check cfg region for DMRhub
|
||||
print("\n### search whole dump for 'DMRhub' ###")
|
||||
o=0
|
||||
while True:
|
||||
o=data.find(b'DMRhub',o)
|
||||
if o<0:break
|
||||
print(f" @{o:08x}: {hd(o-8,32,o-8)}")
|
||||
o+=1
|
||||
|
||||
# 0x0D0000 keys: is this the 'groups'/grouplist backup? name 'Key N', b0=index, tail has index at +20
|
||||
# Compare with grouplist format (b1==0x01 enabled, name @0x02). Here b0=idx,b1=00,name@2.
|
||||
# Actually looks like encryption keys OR group lists. Check groups region 0x07C000
|
||||
print("\n### groups region 0x07C000 ###")
|
||||
print(hd(0x07c000,96))
|
||||
# and encrypt region in .4rdmf is separate. Let's see full first key record 48 bytes across
|
||||
print("\n### 0x0D0000 full stride verify: is it 0x30? show rec 100 ###")
|
||||
print(hd(0x0d0000+100*0x30,48))
|
||||
print("last populated:")
|
||||
print(hd(0x0d0000+509*0x30,48))
|
||||
Ссылка в новой задаче
Block a user