Hotdry.

Article

吉他音箱固件逆向工程:从 JTAG 调试到自定义 DSP 效果

深入解析 Yamaha THR10c 吉他音箱固件逆向工程全过程,涵盖 JTAG 硬件调试、ARM7TDMI 固件分析、ELF 重链接技术,以及通过 MIDI SysEx 分发补丁的完整工程实践。

2026-06-04systems

现代数字吉他音箱内置复杂的 DSP 处理链,从音箱建模到箱体模拟,从混响到调制效果,这些功能都由固件中的算法实现。然而,厂商提供的功能往往无法满足所有使用场景 —— 比如无法单独关闭箱体模拟以连接真实吉他箱体,或者希望在插入耳机时仍然保持内部扬声器输出。当硬件本身具备能力但软件限制使用时,逆向工程成为解锁设备潜力的可行路径。

本文以 Yamaha THR10c 数字吉他音箱为例,系统介绍如何通过 JTAG 接口提取固件、分析 ARM7TDMI 架构代码、实施二进制补丁,并最终通过标准 MIDI 协议分发更新。

硬件接口识别与 JTAG 连接

逆向工程的第一步是找到与设备通信的物理接口。通过查阅服务手册的电路原理图,可以在主 PCB 上发现两个关键接口:UART 串口(CB4,2mm 间距)和 JTAG 调试口(CB3,1mm 间距 FFC 连接器)。UART 接口在启动时无输出,可能是未启用或需要特定触发条件。相比之下,JTAG 接口提供了更底层的硬件调试能力。

JTAG(Joint Test Action Group)是一种用于芯片测试和调试的串行接口,包含 TCK(时钟)、TMS(状态机控制)、TDI(数据输入)、TDO(数据输出)四个核心信号,以及可选的 TRST(复位)。对于 THR10c,其主控芯片 SSP2 的 JTAG ID 为 0x4F1F0F0F,与 NXP LPC 2xxx 系列一致,推测基于 ARM7TDMI-S 核心。

硬件连接使用 FTDI FT2232H Mini Module 作为 JTAG 适配器,该芯片提供双通道多功能接口,可同时支持 JTAG 和 UART。关键接线配置如下:AD0 接 TCK,AD1 接 TDI,AD2 接 TDO,AD3 接 TMS,AD4 接 TRST,AD5 接 SRST(系统复位)。注意 TRST 为低电平有效,需要通过 GPIO 拉高以启用 TAP 控制器。

OpenOCD 配置与内存转储

OpenOCD(Open On-Chip Debugger)是连接 JTAG 硬件与调试工具的关键软件。针对 FT2232H Mini Module 的自定义配置需设置 TRST 和 SRST 信号引脚:

adapter driver ftdi
ftdi device_desc "FT2232H MiniModule"
ftdi vid_pid 0x0403 0x6010
ftdi layout_init 0x0008 0x000b
ftdi layout_signal nTRST -data 0x0010 -oe 0x0010
ftdi layout_signal nSRST -data 0x0020 -oe 0x0020

目标配置文件需声明 TAP 控制器和处理器核心:

transport select jtag
jtag newtap ssp2 cpu -irlen 4 -expected-id 0x4F1F0F0F -ircapture 0x1 -irmask 0xF
target create ssp2.cpu arm7tdmi -chain-position ssp2.cpu -endian big
adapter speed 2000
reset_config trst_and_srst

注意 SSP2 使用大端模式(big-endian),这与 ARM7TDMI 默认的小端模式不同,需要在 GDB 中通过 set endian big 进行配置。

通过 GDB 连接到 OpenOCD 后(target extended-remote :3333),可以读取和写入内存、寄存器,甚至单步执行代码。为了分析固件,需要转储地址空间。THR10c 配备 2MiB Flash 芯片,从地址 0x2000000 开始。通过 GDB 命令 dump memory memory.bin 0 0x4000000 可以转储 64MiB 地址空间,耗时约 15 分钟。

固件结构与内存布局分析

将转储的内存加载到 Ghidra 进行分析,设置处理器为 ARM:BE:32:v4t(ARMv4 大端模式)。通过分析启动代码,可以识别出固件包含两个独立的程序镜像:

  • DTAb(Bootloader):位于 0x2000000,大小约 64KiB,负责固件更新和系统初始化
  • DTAm(Main Firmware):位于 0x2010000,大小约 1MiB,包含主要应用逻辑

DTAm 的启动流程包括:设置各 ARM 异常模式(IRQ、Abort、Undefined、FIQ、Supervisor)的栈指针,清零 RAM 区域(0x10000000x4000000 处的 SDRAM),然后复制数据段并跳转到主程序。

完整的内存布局如下:

地址 长度 用途
0x2000000 0x100 Bootloader 第一阶段
0x2000100 0xFF00 Bootloader (DTAb)
0x2010000 0x100000 主固件 (DTAm)
0x1008000 0x3AD0 RAM0 数据段
0x100BAD0 0xA49C RAM0 BSS 段
0x4000000 0x10100 RAM1 数据段
0x4010100 0x118B4 RAM1 BSS 段

ELF 重链接与补丁系统

为了在原始固件中注入自定义代码,需要将扁平的二进制固件转换为可重链接的 ELF 格式。首先通过汇编文件将固件分割为对应内存布局的段:

.macro fwdata addr:req,size:req
    .incbin "thr10_ver104c_20120803.bin",\addr-0x2010000,\size
.endm

.text
    fwdata 0x2010000,0x7645C

.section .data.ram1,"aw"
    fwdata 0x208645C,0x10100

链接器脚本定义了各段的加载地址和运行地址,同时预留了 .text.patch.data.patch.bss.patch 等自定义段,从 0x20A0000 开始存放新代码。

补丁系统通过 .patch.text 段实现,每个补丁符号对应一个需要覆盖的原始代码位置。宏定义简化了补丁编写:

.macro patch addr:req,body:vararg
    .section .patch.text,"xo",.text
    .org \addr-0x2010000
    patch\+:
    .ifnb \body
        \body
        endpatch
    .endif
.endm

补丁工具使用 libelf 库读取 .patch.* 段,将符号内容复制到对应原始段的位置,并调整重定位信息,最后移除补丁段。这使得可以通过简单的 Makefile 调用完成固件构建:make 即可生成包含自定义代码的完整固件镜像。

功能实现:箱体模拟旁路与强制扬声器输出

通过深入分析 Ghidra 反编译代码,可以理解固件的多线程架构:USB MIDI 处理、面板 I/O(LED、电位器、按键)分别运行在不同线程中,通过事件机制通信。DSP 核心独立于 ARM 主核运行,即使 ARM 核心被调试中断,吉他信号仍能通过 DSP 链处理。

内存映射 I/O 区域位于 0x7000000,通过对比按键按下前后的内存转储,可以定位按键状态寄存器(0x7001D60)和电位器 ADC 值(0x7001800)。类似方法也用于发现 UART 寄存器(0x7002000/0x7002001),波特率为 32000。

实现箱体模拟旁路功能需要拦截按键处理函数(0x2028CB8)和 DSP 命令发送函数。THR10c 使用组合按键触发特殊功能:长按 TAP 同时按下 PRESET1 切换箱体旁路,按下 PRESET2 切换强制扬声器模式。LED 指示灯复用调音器区域的左右箭头(旁路状态)和中心点(扬声器状态)进行状态显示。

DSP 命令通过 dsp_command 函数发送,支持多种命令类型:类型 0 用于编程 DSP 块(设置初始参数),类型 1 用于设置特定参数值。箱体模拟对应 DSP 块 2,参数 0 选择箱体类型(0=Flat,1=Brit Blues 2x12,以此类推)。在旁路模式下,需要拦截所有对块 2 的编程和参数设置命令,将箱体类型强制替换为 Flat。

分析还发现各箱体类型的增益补偿系数,Flat 模式的增益为 0dB,而其他模式有 -10dB 左右的衰减,这解释了为什么切换到 Flat 时音量会突然增大。通过将 Flat 模式的增益系数从 0 改为 -10,可以修正这一问题。

固件更新格式与分发

为了让其他用户无需硬件修改即可使用补丁,需要利用设备原有的固件更新机制。THR10c 支持通过 USB MIDI 接收 SysEx(System Exclusive)消息进行固件更新。

固件转储命令的 SysEx 格式为:F0 43 7D 50 44 54 41 31 52 4F 4D 52 02 F7,其中 43 是 Yamaha 的厂商 ID,7D 50 是设备类型和命令码,DTA1ROMR 是命令名,02 选择 Flash 区域。

固件更新数据通过 DTA1MAIN 消息传输,每条消息包含:前缀 F0 43 7D 40、剩余长度(7 位编码)、命令名、块编号、两个保留字节(固定 7F 7F)、以及编码后的固件数据。数据编码采用 MIDI 1.0 规范变体:每 7 个字节数据后跟第 8 个字节存储这 7 个字节的最高位。校验和为所有字节和的补码的低 7 位。

更新流程为:发送 DTA1ERASE 擦除固件区域,等待 16 秒,然后发送一系列 DTA1MAIN 消息传输固件数据(每条间隔 50ms),最后发送 DTA1CSUM 进行整体校验。

通过编写 bintomidmidtobin 工具,可以在原始二进制和 MIDI 文件格式之间转换。使用 Standard MIDI File 格式,设置 500 ticks/beat 和 120 BPM,使每个 tick 对应 1 毫秒,便于时间计算。

用户更新固件时,只需在开机时按住 TAP 按钮并按 5 次进入更新模式(LED 显示 "U"),然后通过 aplaymidi -p THR10 thr10.mid 播放 MIDI 文件即可完成更新。

工程实践要点

此类嵌入式固件逆向工程需要掌握的关键技能包括:

  1. 硬件调试接口:熟悉 JTAG 信号定义和 TAP 控制器状态机,能够阅读电路原理图识别调试端口
  2. 处理器架构:理解 ARM/Thumb 指令集切换机制(通过 BX 指令的最低位),掌握 ARM7TDMI 的异常模式和栈布局
  3. 固件分析工具:熟练使用 Ghidra 进行静态分析,编写脚本修复交叉引用(如处理 ARM/Thumb 切换辅助函数的调用关系)
  4. 链接与重定位:理解 ELF 格式、链接器脚本语法,能够将扁平二进制转换为可重链接格式
  5. 嵌入式通信协议:理解 MIDI SysEx 消息结构,掌握 7 位编码和校验和计算

风险方面,固件修改可能导致设备变砖(无法启动),因此在进行 Flash 写入前应确保能够通过 JTAG 恢复。建议保留原始固件备份,并在修改前验证补丁的正确性。

参考资料

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com