Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

FPBInject

English | 中文

License: MIT Platform Platform Ask DeepWiki CI

Runtime code injection for ARM Cortex-M. Replace any function on a running MCU through a serial connection — no reflashing, no debugger, no downtime.

FPBInject uses the Flash Patch and Breakpoint (FPB) hardware unit to intercept function calls and redirect them to your custom code in RAM, while the original Flash stays untouched.

FPBInject Workbench

Traditional vs FPBInject

gantt
    title Iteration cycle comparison (typical STM32 project)
    dateFormat  s
    axisFormat  %Ss

    section Traditional
    Edit code          : a1, 0, 5s
    Compile & link     : a2, after a1, 15s
    Erase flash        : a3, after a2, 3s
    Flash write        : a4, after a3, 5s
    MCU reboot         : a5, after a4, 2s
    Reproduce issue    : a6, after a5, 5s

    section FPBInject
    Edit code          : b1, 0, 5s
    Compile & inject   : b2, after b1, 1s
    Reproduce issue    : b3, after b2, 5s

The traditional cycle touches flash on every iteration — compile, erase, write, reboot, then finally reproduce the issue. With FPBInject, the MCU never stops: save your patch, it's live in under a second. No pit stop required.

How It Works

flowchart LR
    A["caller()<br/>calls foo()"] -->|"FPB intercepts<br/>foo's address"| B["Trampoline<br/>in Flash"]
    B -->|"Jump to RAM"| C["Your Code<br/>in RAM"]

The FPB unit matches the target function's address, redirects execution through a trampoline in Flash, which jumps to your replacement function in RAM. All handled by hardware — zero software overhead on the call path.

Workbench

FPBInject ships with a browser-based workbench for the full workflow: browse symbols, read disassembly, write patches, and inject — all from one interface.

Symbol Search & Disassembly

Search the firmware's symbol table, click a function to view its disassembly or decompiled source.

Disassembly View

Manual Inject

Write your replacement function in C, then hit inject. The workbench compiles, uploads, and patches — typically under a second.

Inject View

Auto Inject

Point the workbench at your source directory and enable file watching. Add /* FPB_INJECT */ before any function you want to patch, then just save the file — the workbench detects the change, recompiles, and re-injects automatically.

Auto Inject - Editor

Auto Inject - Workbench

File Transfer (Optional)

FPBInject also supports file transfer over serial — browse, upload, and download files on the device's filesystem. Supports drag-and-drop (files and folders), CRC verification, and progress tracking.

Filesystem backends: POSIX (NuttX VFS, Linux), FatFS, standard C library (stdio), or custom implementations via the fl_fs_ops_t interface.

File Transfer

Quick Start

1. Build & Flash Firmware

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. Install the Host Tools

Install from PyPI — this provides the fpbinject-server (workbench) and fpbinject (CLI) commands, plus the Python SDK:

pip install fpbinject
Or run from source (no install)
cd Tools/WebServer
pip install -r ../requirements.txt
python main.py           # workbench
python fpb_cli.py --help # CLI

3. Start the Workbench

fpbinject-server

Open http://127.0.0.1:5500 in your browser, connect to the serial port, load your ELF file, and start patching.

4. Or Use the CLI

All commands output JSON, designed for scripting and AI agent integration.

# Search for functions
fpbinject search firmware.elf "gpio"

# View disassembly
fpbinject disasm firmware.elf digitalWrite

# Inject a patch
fpbinject --port /dev/ttyACM0 --elf firmware.elf \
    --compile-commands build/compile_commands.json \
    inject digitalWrite patch.c

See the CLI Guide for the full command reference.

5. Or Use the Python SDK

Drive everything the CLI can do from Python:

from fpbinject import Client

# Auto-discover a running WebServer on the LAN (mDNS)
client = Client.discover(token="...")
client.serial_send("help\r\n")
print(client.serial_read()["raw_data"])
client.inject("digitalWrite", "patch.c", elf="firmware.elf")

# Or analyze an ELF offline — no device needed
off = Client.offline()
print(off.signature("firmware.elf", "digitalWrite"))

See the SDK Guide for the full API.

Writing Patches

Create a C file with the /* FPB_INJECT */ marker. The function signature must match the original.

#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);
}

To call the original function from injected code, you need two things: a function pointer pointing directly at the original address (bypassing the FPB redirect), and temporarily disabling the patch around the call. Direct calls by name will still be intercepted by FPB and cause infinite recursion.

/* Define a function pointer to the original address (| 1 sets the 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);

    /* Disable patch -> call original via pointer -> re-enable */
    fpb_enable_patch(0, false);
    ORIG_DIGITALWRITE(pin, value);
    fpb_enable_patch(0, true);
}

The workbench generates this pattern automatically when the original function address is known.

Supported Hardware

Feature Spec
Architecture ARMv7-M, ARMv8-M
Tested MCU STM32F103C8T6
Patch Slots 6 (FPB v1) or 8 (FPB v2)
Patch Modes Trampoline / Direct (ARMv7-M REMAP), DebugMonitor (ARMv8-M BKPT)
RTOS Support Bare-metal, NuttX
Connection Serial (USB-to-UART or USB CDC)
CMake Build Options
Option Default Description
APP_SELECT 1 Application selection (3 = func_loader)
FL_ALLOC_MODE STATIC Memory allocation: STATIC or LIBC
FPB_NO_DEBUGMON OFF Disable DebugMonitor mode
Project Structure
FPBInject/
├── Source/                 # FPB driver, trampoline, DebugMonitor
├── App/
│   ├── func_loader/        # Serial protocol, memory allocator, FPB control
│   ├── inject/             # Injection helpers
│   └── tests/              # Firmware unit tests (host-based, with coverage)
├── Project/                # Platform HAL (STM32F10x, Arduino API)
├── Tools/
│   └── WebServer/          # Workbench (Flask backend + JS frontend) & CLI
└── Docs/                   # Architecture, CLI reference, WebServer guide

Documentation

Document Description
Architecture FPB internals, patch modes, memory layout, protocol
CLI Reference All CLI commands with examples and JSON output format
SDK Guide pip install fpbinject — Python Client API reference
WebServer Guide Workbench setup and usage

License

MIT

References

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fpbinject-1.6.9a1.tar.gz (376.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fpbinject-1.6.9a1-py3-none-any.whl (425.2 kB view details)

Uploaded Python 3

File details

Details for the file fpbinject-1.6.9a1.tar.gz.

File metadata

  • Download URL: fpbinject-1.6.9a1.tar.gz
  • Upload date:
  • Size: 376.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for fpbinject-1.6.9a1.tar.gz
Algorithm Hash digest
SHA256 00f49137fc2e33c6c49485273eaa38478bec6a70e8b8d2bd82f17bd1a1613ada
MD5 199bd6af858383be2df793e989bf2a0b
BLAKE2b-256 3a75474666e162e28265718e214ba2a8ccd587050959f4088915c9a6bf3f05a3

See more details on using hashes here.

File details

Details for the file fpbinject-1.6.9a1-py3-none-any.whl.

File metadata

  • Download URL: fpbinject-1.6.9a1-py3-none-any.whl
  • Upload date:
  • Size: 425.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for fpbinject-1.6.9a1-py3-none-any.whl
Algorithm Hash digest
SHA256 9161bd25514a5df87e549ea656074b4cbc471f7e206896b9ff3d4e35787cb601
MD5 254cfec02e318eb268922d836eca68e7
BLAKE2b-256 53090b34428c5f8039ff0f424cb569fa5137de63a499f2860552977076b2127b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page