Вариант 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
Этот коммит содержится в:
@@ -1,61 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Поймать Beken-загрузчик модуля во время включения (через мост RT-4D, WSL/powershell).
|
||||
"""Поймать загрузчик модуля (HKLFBOOT) — ТАЙТОВЫЙ спам Connect (как DMR Upgrade Tool).
|
||||
|
||||
Формат флеша модуля = стандартный Beken (32+2 CRC), значит у него Beken UART-загрузчик,
|
||||
который слушает link-check КОРОТКОЕ окно после сброса. Этот тул одним процессом powershell
|
||||
в тайтовом цикле спамит link-check `01 E0 FC 01 00` и ловит ответ `04 0E 05 ...`.
|
||||
DMR Upgrade Tool: жмёшь Upgrade (спам Connect=01 E0 FC 01 00), затем MENU+включение —
|
||||
загрузчик ловит connect в окне до старта приложения. Наш powershell-цикл со Start-Sleep был
|
||||
слишком медленный и промахивался. Здесь — спам БЕЗ пауз (тысячи/сек), ловит короткое окно.
|
||||
|
||||
Использование: запусти тул, и в течение его работы ВЫКЛЮЧИ и включи рацию в режим
|
||||
«Обновление DMR чипа» (питание + MENU). Если поймаем ответ — загрузчик достижим через мост.
|
||||
|
||||
ТОЛЬКО ЧТЕНИЕ/handshake — ни erase, ни write не шлём.
|
||||
Процедура: запусти тул, затем зажми MENU и включи рацию. Успех = ответ загрузчика на link-check
|
||||
(04 0E 05 ...) и приложение (0x68 06 02) НЕ стартует. Только connect — ни erase, ни write.
|
||||
"""
|
||||
import argparse, subprocess, sys
|
||||
import argparse, subprocess
|
||||
|
||||
PS = r'''
|
||||
$port="{port}"; $sec={sec}
|
||||
$p = New-Object System.IO.Ports.SerialPort $port,115200,"None",8,"one"
|
||||
$p.ReadTimeout=20; $p.WriteTimeout=50
|
||||
$p = New-Object System.IO.Ports.SerialPort "{port}",115200,"None",8,"one"
|
||||
$p.ReadTimeout=1; $p.WriteTimeout=50; $p.ReadBufferSize=8192
|
||||
try {{ $p.Open() }} catch {{ Write-Output "ERR:$($_.Exception.Message)"; exit }}
|
||||
$lc=[byte[]]@(1,224,252,1,0) # 01 E0 FC 01 00
|
||||
$lc=[byte[]]@(1,224,252,1,0)
|
||||
$sw=[Diagnostics.Stopwatch]::StartNew()
|
||||
$hit=$false
|
||||
while($sw.Elapsed.TotalSeconds -lt $sec){{
|
||||
try {{ $p.Write($lc,0,$lc.Length) }} catch {{}}
|
||||
Start-Sleep -Milliseconds 8
|
||||
$acc=New-Object System.Collections.Generic.List[byte]
|
||||
$caught=$false
|
||||
while($sw.Elapsed.TotalSeconds -lt {sec}){{
|
||||
if(-not $caught){{ 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
|
||||
foreach($x in $b){{ $acc.Add($x) }}
|
||||
$hex=($b|ForEach-Object{{"{{0:x2}}"-f $_}}) -join ""
|
||||
if($hex -match "e0fc"){{ Write-Output ("HIT:"+$hex); $hit=$true; break }}
|
||||
else {{ Write-Output ("RX:"+$hex) }}
|
||||
Write-Output ("t"+[int]$sw.Elapsed.TotalSeconds+"|"+$hex)
|
||||
# ответ загрузчика на link-check: 04 0e 05 ...
|
||||
if($hex -match "040e05"){{ Write-Output "CAUGHT"; $caught=$true }}
|
||||
}}
|
||||
}}
|
||||
$p.Close()
|
||||
if(-not $hit){{ Write-Output "NOHIT" }}
|
||||
$p.Close(); Write-Output "END"
|
||||
'''
|
||||
|
||||
def main():
|
||||
ap=argparse.ArgumentParser()
|
||||
ap.add_argument("--port", default="COM4")
|
||||
ap.add_argument("--sec", type=int, default=25)
|
||||
ap.add_argument("--port", default="COM4"); ap.add_argument("--sec", type=int, default=30)
|
||||
a=ap.parse_args()
|
||||
print(f"[спам link-check на {a.port} @115200, {a.sec}с]")
|
||||
print(">>> СЕЙЧАС выключи рацию и включи в режим «Обновление DMR чипа» (питание+MENU) <<<\n")
|
||||
print(f"[ТАЙТОВЫЙ спам 01 e0 fc 01 00 на {a.port}, {a.sec}с]")
|
||||
print(">>> ЗАЖМИ MENU и включи рацию СЕЙЧАС (как при прошивке модуля) <<<\n")
|
||||
r=subprocess.run(["powershell.exe","-NoProfile","-Command",PS.format(port=a.port,sec=a.sec)],
|
||||
capture_output=True, text=True)
|
||||
banner=app=caught=False; n=0
|
||||
for line in r.stdout.replace("\r","").split("\n"):
|
||||
line=line.strip()
|
||||
if line.startswith("HIT:"):
|
||||
print(f"✅ ЗАГРУЗЧИК ОТВЕТИЛ: {line[4:]}")
|
||||
print(" -> Beken-загрузчик достижим через мост. Можно читать флеш.")
|
||||
elif line.startswith("RX:") and line[3:]:
|
||||
print(f" (эхо/данные: {line[3:][:60]})")
|
||||
elif line=="NOHIT":
|
||||
print("❌ Ответа загрузчика не поймано. Либо мост не пробрасывает загрузчик,")
|
||||
print(" либо окно/бод другие. Попробуй ещё раз или обсудим физ.доступ.")
|
||||
elif line.startswith("ERR:"):
|
||||
print(line)
|
||||
if line.startswith("ERR:"): print(line); return
|
||||
if line=="CAUGHT": caught=True; continue
|
||||
if "|" not in line: continue
|
||||
t,hexs=line.split("|",1); n+=1
|
||||
try: asc=bytes.fromhex(hexs).decode("latin1")
|
||||
except: asc=""
|
||||
asc="".join(c if 32<=ord(c)<127 else "." for c in asc)
|
||||
print(f" {t}: {asc[:48]} [{hexs[:40]}]")
|
||||
if "HKLF" in asc: banner=True
|
||||
if "680602" in hexs: app=True
|
||||
print()
|
||||
if caught: print("✅✅✅ ЗАГРУЗЧИК ПОЙМАН (ответ 04 0e 05)! Держим сессию — можно читать флеш.")
|
||||
elif banner and app: print("⚠️ Баннер+приложение стартовало. Окно ещё промахнули — повтори (спам уже без пауз).")
|
||||
elif banner: print("✅ Баннер был, приложение не стартовало — возможно, поймали. Проверю ответ.")
|
||||
elif not n: print("Тишина — не попал во включение? Порт COM4? Повтори.")
|
||||
else: print("Данные без баннера — перезагрузка вне окна. Повтори.")
|
||||
|
||||
if __name__=="__main__":
|
||||
main()
|
||||
|
||||
Ссылка в новой задаче
Block a user