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

96 строки
3.5 KiB
Python

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))