#!/usr/bin/env python3
"""
JLink Patch: 绕过 0x81 前置检查#2（队列非空检查）

问题分析：
- 0x81 处理函数在 0x1A376 调用 0x19FEC 检查某个队列是否非空
- 如果队列为空，0x19FEC 返回非0，0x1A37C 的 bne.n 跳转到 0x1A462 丢弃 0x81
- 在没有 CAN 环境时，该队列可能为空，导致 0x81 被静默丢弃

Patch 位置：0x0001A37C
原指令：d171  (bne.n 0x1a462)  —— 如果队列空则丢弃 0x81
Patch：  e7ff  (b.n 0x1a37e)    —— 无条件继续执行

同时 patch 0x1A466 的 cbz（deepseek 已分析过）：
原指令：b360  (cbz r0, 0x1a4c2)
Patch： e7ff  (b.n 0x1a468)    —— 无条件走正常分支

用法：
    python3 jlink_patch_0x81_queue_check.py
    # 然后在车机上运行 ./mcu_upgrade_bug_repro
"""
import pexpect
import sys
import time

JLINK_EXE = "/Applications/SEGGER/JLink_V942/JLinkExe"

PATCHES = [
    {
        "addr": 0x0001A37C,
        "orig": 0xD171,      # bne.n 0x1a462
        "patch": 0xE7FF,     # b.n 0x1a37e
        "desc": "0x81 前置检查#2: 队列非空检查 -> 无条件通过"
    },
    {
        "addr": 0x0001A466,
        "orig": 0xB360,      # cbz r0, 0x1a4c2
        "patch": 0xE7FF,     # b.n 0x1a468
        "desc": "0x81 状态检查: cbz -> 无条件走正常分支"
    },
]

def send_cmd(child, cmd, timeout=10):
    child.sendline(cmd)
    child.expect(r'J-Link>', timeout=timeout)
    return child.before

def main():
    print("[*] 启动 JLinkExe...")
    child = pexpect.spawn(JLINK_EXE, encoding='utf-8', timeout=30)
    child.logfile_read = sys.stdout

    child.expect(r'J-Link>', timeout=15)
    send_cmd(child, "device HC32A4A8xI")
    send_cmd(child, "si SWD")
    send_cmd(child, "speed 4000")
    child.sendline("connect")
    idx = child.expect([r'Cortex-M4 identified', r'J-Link>'], timeout=30)
    if idx == 0:
        child.expect(r'J-Link>', timeout=10)
    print("\n[+] 目标已连接")

    send_cmd(child, "h")
    print("[+] 已 halt")

    for p in PATCHES:
        addr = p["addr"]
        # 读取原值
        orig = send_cmd(child, f"mem16 0x{addr:08X},1")
        print(f"[+] 0x{addr:08X} 原值: {orig.strip()}")

        # Patch
        send_cmd(child, f"w2 0x{addr:08X}, 0x{p['patch']:04X}")
        patched = send_cmd(child, f"mem16 0x{addr:08X},1")
        print(f"[+] 0x{addr:08X} Patch 后: {patched.strip()} ({p['desc']})")

    # 运行目标
    send_cmd(child, "g")
    print("\n[*] MCU 已恢复运行，Patch 生效")
    print("[*] 请在车机上运行: ./mcu_upgrade_bug_repro")
    print("[*] 观察 0x81 是否回复 UPDATE_LEN + IND_AQUIRE...")
    print("[*] 等待 30 秒后自动恢复机器码...")

    time.sleep(30)

    send_cmd(child, "h")
    print("\n[*] 恢复原始机器码...")
    for p in PATCHES:
        addr = p["addr"]
        send_cmd(child, f"w2 0x{addr:08X}, 0x{p['orig']:04X}")
        restored = send_cmd(child, f"mem16 0x{addr:08X},1")
        print(f"[+] 0x{addr:08X} 恢复后: {restored.strip()}")

    send_cmd(child, "g", timeout=3)
    child.sendline("q")
    try:
        child.expect(pexpect.EOF, timeout=5)
    except:
        child.terminate(force=True)
    print("[+] 完成")

if __name__ == "__main__":
    main()
