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,51 @@
|
||||
import struct, 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()
|
||||
|
||||
# 0x126000 decode as call-log. Fields observed:
|
||||
# b0=00, b1=type(1=priv/grp,2=?), b2=01, b3=00, b4-5=?, b6-9=dmr_id LE, b10-13=counter LE,
|
||||
# b14-19 = YY MM DD HH MM SS, rest 0
|
||||
print("### 0x126000 decoded call-log entries ###")
|
||||
for i in range(16):
|
||||
o=0x126000+i*32
|
||||
r=data[o:o+32]
|
||||
if r[14]==0 and all(b==0 for b in r[:14]): break
|
||||
b1=r[1]; b4=struct.unpack('<H',r[4:6])[0]
|
||||
dmr=struct.unpack('<I',r[6:10])[0]
|
||||
cnt=struct.unpack('<I',r[10:14])[0]
|
||||
date=f"20{r[14]:02d}-{r[15]:02d}-{r[16]:02d} {r[17]:02d}:{r[18]:02d}:{r[19]:02d}"
|
||||
print(f" #{i:2d} type={b1} b4={b4:04x} dmr_id={dmr}(0x{dmr:x}) seq={cnt} {date}")
|
||||
# region schedules is at 0xC6000. call_log at 0x88000 region. 0x126000 is unrelated location.
|
||||
# Occupancy said call_log used @0x092000 (that was actually contacts-looking / All Call).
|
||||
# Let's see total valid + gap pattern
|
||||
print("\ntotal 32-byte records with a nonzero timestamp year 0x19:")
|
||||
n=sum(1 for i in range(20480//32) if data[0x126000+i*32+14]==0x19)
|
||||
print(n)
|
||||
|
||||
# Confirm the 0x100000 bitmap: render 1bpp. It has 5-row-tall column data (values <=0x1f then 0x3c,0x38 etc)
|
||||
print("\n### 0x100000 bitmap render (bytes as vertical 8px columns, LSB=top) ###")
|
||||
seg=data[0x100000:0x100000+0x300]
|
||||
# print 8 rows
|
||||
for row in range(8):
|
||||
line=''
|
||||
for col in range(0x190,0x2f8):
|
||||
b=data[0x100000+col]
|
||||
line+='#' if (b>>row)&1 else ' '
|
||||
print(line.rstrip())
|
||||
|
||||
# 0x03E000 twin: what is at 0x01E000 exactly and how does it relate to zones?
|
||||
print("\n### 0x01E000 (inside zones region) full ###")
|
||||
def hd(off,n):
|
||||
out=[]
|
||||
for i in range(0,n,16):
|
||||
c=data[off+i:off+i+16]
|
||||
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'{off+i:08x} {h:<47} {a}')
|
||||
return '\n'.join(out)
|
||||
print(hd(0x01e000,48))
|
||||
# Is 0x01E000 the tail of the last zone slot? zones start 0x01C000, 512B each. 0x01E000 = slot 16.
|
||||
print(f"0x01E000 is zone slot #{(0x01E000-0x01C000)//512}")
|
||||
# The DTCN A/B marker in beta is at page+0xFF8. 0x01dffc had ff ff ff ff before DMRhub.
|
||||
# So 0x01E000 likely a settings/identity page, twinned at 0x03E000.
|
||||
Ссылка в новой задаче
Block a user