Files
viktorиClaude Opus 4.8 bf55e9e53f Вариант A: загрузчик HKLFBOOT write-only (read флеша нет) — дамп через него недостижим
Загрузчик ловится/держится на Linux (catch link-check + SetBaudRate смена бода). Но read-команды
флеша нет (0x03/08/09/0a/0b не дают флеш; 0x08 = битовая карта). Сходится с DMR Upgrade Tool
(только erase/write). Дамп только физически (SPI-клипса). Но BK4819 найден -> TX-фикс без дампа.
tools/bk_module_dump.py, bk_bootloader_catch.py, bk_read_probe.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NikMGoqQWWk9wy2ww2vJAr
2026-07-24 20:09:31 +09:00

73 строки
3.6 KiB
Python

#!/usr/bin/env python3
"""Зонд чтения флеша Beken-загрузчика модуля: catch link-check -> один read4k(0) -> сырой ответ.
Фрейминг (из DMR Upgrade Tool SendEraseFlashReq): 01 E0 FC FF F4 <len_lo> <len_hi> <CMD> <addr:4 LE> ...
Erase=0x0F. Read4K пробуем CMD 0x09: 01 E0 FC FF F4 05 00 09 <addr:4 LE>. ТОЛЬКО ЧТЕНИЕ.
Процедура: запусти, затем зажми MENU и включи рацию. Тул поймает загрузчик и прочитает 1 сектор
по адресу 0 — покажет формат ответа (по нему построим полный дамп).
"""
import argparse, subprocess
PS = r'''
$p=New-Object System.IO.Ports.SerialPort "{port}",115200,"None",8,"one"
$p.ReadTimeout=1; $p.WriteTimeout=50; $p.ReadBufferSize=16384
try{{$p.Open()}}catch{{Write-Output "ERR:$($_.Exception.Message)";exit}}
$lc=[byte[]]@(1,224,252,1,0)
# read4k addr=0: 01 E0 FC FF F4 05 00 09 00 00 00 00
$rd=[byte[]]@(1,224,252,255,244,5,0,{cmd},0,0,0,0)
$sw=[Diagnostics.Stopwatch]::StartNew(); $state="catch"; $roll=""
while($sw.Elapsed.TotalSeconds -lt {sec}){{
if($state -eq "catch"){{
try{{$p.Write($lc,0,$lc.Length)}}catch{{}}
$n=$p.BytesToRead
if($n -gt 0){{
$b=New-Object byte[] $n;$p.Read($b,0,$n)|Out-Null
$hex=($b|ForEach-Object{{"{{0:x2}}"-f $_}}) -join ""
$roll=($roll+$hex); if($roll.Length -gt 400){{$roll=$roll.Substring($roll.Length-400)}}
if($hex -match "484b4c46"){{ Write-Output "BANNER" }}
if($roll -match "040e05"){{
Write-Output "CATCH"
# слить бэклог link-check ответов до тишины (~250мс без байт)
$q=[Diagnostics.Stopwatch]::StartNew(); $last=0
while($q.ElapsedMilliseconds -lt 400){{
$m=$p.BytesToRead
if($m -gt 0){{ $tb=New-Object byte[] $m;$p.Read($tb,0,$m)|Out-Null; $last=$q.ElapsedMilliseconds }}
}}
$p.DiscardInBuffer()
$p.Write($rd,0,$rd.Length) # read4k
$state="read"; $rsw=[Diagnostics.Stopwatch]::StartNew()
$acc=New-Object System.Collections.Generic.List[byte]
}}
}}
}} else {{
$n=$p.BytesToRead
if($n -gt 0){{ $b=New-Object byte[] $n;$p.Read($b,0,$n)|Out-Null; foreach($x in $b){{$acc.Add($x)}} }}
if($rsw.Elapsed.TotalMilliseconds -gt 2500){{
$hx=($acc.ToArray()|ForEach-Object{{"{{0:x2}}"-f $_}}) -join ""
Write-Output ("RDLEN:"+$acc.Count)
Write-Output ("RDHEAD:"+$hx.Substring(0,[Math]::Min(96,$hx.Length)))
if($hx.Length -gt 96){{ Write-Output ("RDTAIL:"+$hx.Substring($hx.Length-32)) }}
break
}}
}}
}}
$p.Close(); Write-Output "END"
'''
def main():
ap=argparse.ArgumentParser()
ap.add_argument("--port",default="COM4"); ap.add_argument("--sec",type=int,default=30)
ap.add_argument("--cmd",default="9",help="опкод чтения (9=read4k, 3=readsector)")
a=ap.parse_args()
print(f"[зонд чтения, CMD 0x{int(a.cmd):02x}, {a.port}, {a.sec}с] — ЗАЖМИ MENU и включи рацию\n")
r=subprocess.run(["powershell.exe","-NoProfile","-Command",
PS.format(port=a.port,sec=a.sec,cmd=int(a.cmd))],capture_output=True,text=True)
for line in r.stdout.replace("\r","").split("\n"):
line=line.strip()
if line.startswith(("ERR:","CATCH","RDLEN:","RDHEAD:","RDTAIL:")): print(" "+line)
print("\n(RDLEN ~4096+ и RDHEAD с данными = read4k работает. RDLEN 0/мало = опкод неверный, пробуй --cmd 3)")
if __name__=="__main__":
main()