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

80 строки
3.0 KiB
Python

import struct, collections
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)
print("### 0x010000 vs channels 0x004000 (compare first 0x1000) ###")
same=sum(1 for i in range(0x1000) if data[0x010000+i]==data[0x004000+i])
print(f"match to channels@0x4000: {same}/{0x1000} = {100*same/0x1000:.1f}%")
# channel region 0x004000 first records
print("channels@0x004000 head:")
print(hd(0x004000,96))
print("region@0x010000 head:")
print(hd(0x010000,96))
print("\n### 0x03E000 DMRhub context: this looks like a contact/radioid record ###")
print(hd(0x03e000,32))
# Is contacts region at 0x05c000? compare structure - here name at offset 4
print("contacts region @0x05e000 (occupied):")
print(hd(0x05e000,128))
print("\n### 0x0D0000 Key records - 48 byte stride. Compare vs dmr_keys 0x082000 ###")
print("dmr_keys @0x082000:")
print(hd(0x082000,160))
print("encrypt-in-region @0x0D0000 rec0/rec1 detail:")
print(hd(0x0d0000,48))
# check stride: 'Key N' every 0x30
for i in range(5):
o=0x0d0000+i*0x30
print(f"rec{i} @{o:08x}: b0={data[o]:02x} b1={data[o+1]:02x} name={data[o+2:o+16]!r} tail={data[o+16:o+24].hex()}")
print("\n### how many Key records populated in 0x0D0000 (+24KB) ###")
cnt=0; last=None
for i in range(24576//0x30):
o=0x0d0000+i*0x30
if data[o]!=0xff and data[o]!=0x00:
cnt+=1; last=i
print(f"populated key records: {cnt}, last idx {last}")
print("\n### 0x126000 record analysis (32-byte stride) ###")
for i in range(8):
o=0x126000+i*32
r=data[o:o+32]
print(f"rec{i} @{o:08x}: {r.hex()}")
# decode fields: bytes 14,15,16 = 19 0a 0e -> 0x19=25,0x0a=10,0x0e=14 => date 2025-10-14
print("byte14-19 as date candidates (YY MM DD HH MM SS):")
for i in range(8):
o=0x126000+i*32
r=data[o:o+32]
print(f" rec{i}: {r[14]:02d}-{r[15]:02d}-{r[16]:02d} {r[17]:02d}:{r[18]:02d}:{r[19]:02d} b1(type)={r[1]} dmr_le={struct.unpack('<I',r[6:10])[0]} field10={struct.unpack('<I',r[10:14])[0]}")
# count records
cnt=sum(1 for i in range(20480//32) if data[0x126000+i*32]!=0x00 or any(b!=0 for b in data[0x126000+i*32:0x126000+i*32+16]))
print(f"nonzero-ish records among {20480//32}")
# call_log region is 0x088000; occupied run was 0x092000. compare
print("\ncall_log region occupied @0x092000:")
print(hd(0x092000,128))
print("\n### 0x14C000 GBK decode sample ###")
seg=data[0x14c000:0x14c000+256]
try:
print(seg.decode('gbk',errors='replace')[:120])
except Exception as e:
print("err",e)
print("\n### 0x164000 pinyin table structure ###")
# each entry seems 6 bytes ascii pinyin. period ratio peaked at 6
print(hd(0x164000,96))
# Check: is this indexed by GBK char? Look further in
print(hd(0x170000,96))
print("region 0x164000 size 200KB -> 200KB/6 ~ 34000 entries")