Skip to content

Latest commit

 

History

History
220 lines (157 loc) · 7.53 KB

File metadata and controls

220 lines (157 loc) · 7.53 KB

FPBInject

English | 中文

License: MIT Platform Platform Ask DeepWiki CI

ARM Cortex-M 运行时代码注入工具。通过串口连接,在不重新烧录、不需要调试器的情况下,替换正在运行的 MCU 上的任意函数。

FPBInject 利用 Flash Patch and Breakpoint(FPB)硬件单元拦截函数调用,将执行重定向到 RAM 中的自定义代码,原始 Flash 内容保持不变。

FPBInject Workbench

传统方式 vs FPBInject

gantt
    title 迭代周期对比(典型 STM32 项目)
    dateFormat  s
    axisFormat  %Ss

    section 传统方式
    修改代码           : a1, 0, 5s
    编译链接           : a2, after a1, 15s
    擦除 Flash         : a3, after a2, 3s
    烧录写入           : a4, after a3, 5s
    MCU 重启           : a5, after a4, 2s
    复现问题           : a6, after a5, 5s

    section FPBInject
    修改代码           : b1, 0, 5s
    编译注入           : b2, after b1, 1s
    复现问题           : b3, after b2, 5s
Loading

传统方式每次迭代都要经历编译、擦除、烧录、重启,才能复现问题。FPBInject 让 MCU 全程不停机:保存补丁,不到一秒即可生效,真正实现“不熄火修车”。

工作原理

flowchart LR
    A["caller()<br/>调用 foo()"] -->|"FPB 拦截<br/>foo 的地址"| B["跳板代码<br/>Flash 中"]
    B -->|"跳转到 RAM"| C["你的代码<br/>RAM 中"]
Loading

FPB 单元匹配目标函数地址,通过 Flash 中的跳板代码将执行重定向到 RAM 中的替换函数。全程由硬件完成,调用路径零软件开销。

工作台

FPBInject 自带浏览器工作台,支持完整工作流:浏览符号、查看反汇编、编写补丁、一键注入。

符号搜索与反汇编

搜索固件符号表,点击函数查看反汇编或反编译源码。

反汇编视图

手动注入

用 C 语言编写替换函数,点击注入。工作台自动编译、上传、打补丁,通常不到一秒完成。

注入视图

自动注入

在工作台中指定源码目录并开启文件监控。在需要替换的函数前添加 /* FPB_INJECT */ 标记,保存文件即可 — 工作台自动检测变更、重新编译并注入。

自动注入 - 编辑器

自动注入 - 工作台

文件传输(可选)

FPBInject 还支持通过串口进行文件传输 — 浏览、上传、下载设备文件系统中的文件。支持拖拽上传(文件和文件夹)、CRC 校验和传输进度显示。

文件系统后端:POSIX(NuttX VFS、Linux)、FatFS、标准 C 库(stdio),或通过 fl_fs_ops_t 接口自定义实现。

文件传输

快速开始

1. 编译与烧录固件

git clone https://github.com/FASTSHIFT/FPBInject.git
cd FPBInject

cmake -B build -DAPP_SELECT=3 -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi-gcc.cmake
cmake --build build

st-flash write build/FPBInject.bin 0x08000000

2. 启动工作台

cd Tools/WebServer
pip install -r ../requirements.txt
python main.py

浏览器打开 http://127.0.0.1:5500,连接串口,加载 ELF 文件,即可开始注入。

3. 或使用 CLI

所有命令输出 JSON,适合脚本和 AI 代理集成。

# 搜索函数
python fpb_cli.py search firmware.elf "gpio"

# 查看反汇编
python fpb_cli.py disasm firmware.elf digitalWrite

# 注入补丁
python fpb_cli.py --port /dev/ttyACM0 --elf firmware.elf \
    --compile-commands build/compile_commands.json \
    inject digitalWrite patch.c

完整命令参考见 CLI 文档

编写补丁

创建带 /* FPB_INJECT */ 标记的 C 文件,函数签名必须与原始函数一致。

#include <Arduino.h>

/* FPB_INJECT */
__attribute__((section(".fpb.text"), used))
void digitalWrite(uint8_t pin, uint8_t value) {
    printf("Patched: pin=%d val=%d\n", pin, value);
    value ? digitalWrite_HIGH(pin)
          : digitalWrite_LOW(pin);
}

如需在注入代码中调用原始函数,需要两步配合:用函数指针直接指向原始地址(绕过 FPB 重定向),同时在调用前后用 fpb_enable_patch 临时禁用补丁。直接按函数名调用仍会被 FPB 拦截,导致无限递归。

/* 定义指向原始地址的函数指针(| 1 设置 Thumb bit) */
typedef void (*digitalWrite_fn_t)(uint8_t, uint8_t);
static digitalWrite_fn_t const ORIG_DIGITALWRITE = (digitalWrite_fn_t)(0x08001234 | 1);

/* FPB_INJECT */
__attribute__((section(".fpb.text"), used))
void digitalWrite(uint8_t pin, uint8_t value) {
    printf("Patched: pin=%d val=%d\n", pin, value);

    /* 禁用补丁 -> 通过指针调用原始函数 -> 恢复补丁 */
    fpb_enable_patch(0, false);
    ORIG_DIGITALWRITE(pin, value);
    fpb_enable_patch(0, true);
}

工作台在已知原始函数地址时会自动生成上述模板。

支持的硬件

特性 规格
架构 ARMv7-M, ARMv8-M
已测试 MCU STM32F103C8T6
补丁槽位 6 个(FPB v1)或 8 个(FPB v2)
补丁模式 Trampoline / Direct(ARMv7-M REMAP)、DebugMonitor(ARMv8-M BKPT)
RTOS 支持 裸机、NuttX
连接方式 串口(USB 转 UART 或 USB CDC)
CMake 构建选项
选项 默认值 说明
APP_SELECT 1 应用选择(3 = func_loader)
FL_ALLOC_MODE STATIC 内存分配方式:STATIC 或 LIBC
FPB_NO_DEBUGMON OFF 禁用 DebugMonitor 模式
项目结构
FPBInject/
├── Source/                 # FPB 驱动、跳板代码、DebugMonitor
├── App/
│   ├── func_loader/        # 串口协议、内存分配器、FPB 控制
│   ├── inject/             # 注入辅助代码
│   └── tests/              # 固件单元测试(宿主机运行,支持覆盖率)
├── Project/                # 平台 HAL(STM32F10x、Arduino API)
├── Tools/
│   └── WebServer/          # 工作台(Flask 后端 + JS 前端)& CLI
└── Docs/                   # 架构文档、CLI 参考、WebServer 指南

文档

文档 说明
架构 FPB 内部原理、补丁模式、内存布局、协议
CLI 参考 所有 CLI 命令及示例、JSON 输出格式
WebServer 指南 工作台安装与使用

许可证

MIT

参考资料