fpbinject 1.6.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fpbinject-1.6.8/LICENSE +21 -0
- fpbinject-1.6.8/PKG-INFO +255 -0
- fpbinject-1.6.8/README.md +220 -0
- fpbinject-1.6.8/Tools/WebServer/__init__.py +10 -0
- fpbinject-1.6.8/Tools/WebServer/__main__.py +9 -0
- fpbinject-1.6.8/Tools/WebServer/app/__init__.py +41 -0
- fpbinject-1.6.8/Tools/WebServer/app/auto_ban.py +365 -0
- fpbinject-1.6.8/Tools/WebServer/app/middleware.py +155 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/__init__.py +42 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/connection.py +402 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/files.py +280 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/fpb.py +758 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/logs.py +308 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/patch.py +233 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/symbols.py +1521 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/transfer.py +830 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/watch.py +174 -0
- fpbinject-1.6.8/Tools/WebServer/app/routes/watch_expr.py +237 -0
- fpbinject-1.6.8/Tools/WebServer/app/utils/__init__.py +0 -0
- fpbinject-1.6.8/Tools/WebServer/app/utils/sse.py +80 -0
- fpbinject-1.6.8/Tools/WebServer/cli/__init__.py +12 -0
- fpbinject-1.6.8/Tools/WebServer/cli/connection_plan.py +67 -0
- fpbinject-1.6.8/Tools/WebServer/cli/discover.py +295 -0
- fpbinject-1.6.8/Tools/WebServer/cli/fpb_cli.py +2242 -0
- fpbinject-1.6.8/Tools/WebServer/cli/handle_cache.py +128 -0
- fpbinject-1.6.8/Tools/WebServer/cli/server_proxy.py +553 -0
- fpbinject-1.6.8/Tools/WebServer/core/__init__.py +2 -0
- fpbinject-1.6.8/Tools/WebServer/core/compile_commands.py +426 -0
- fpbinject-1.6.8/Tools/WebServer/core/compiler.py +738 -0
- fpbinject-1.6.8/Tools/WebServer/core/config_schema.py +500 -0
- fpbinject-1.6.8/Tools/WebServer/core/elf_utils.py +779 -0
- fpbinject-1.6.8/Tools/WebServer/core/file_transfer.py +699 -0
- fpbinject-1.6.8/Tools/WebServer/core/gdb_bridge.py +469 -0
- fpbinject-1.6.8/Tools/WebServer/core/gdb_json_print.py +115 -0
- fpbinject-1.6.8/Tools/WebServer/core/gdb_manager.py +359 -0
- fpbinject-1.6.8/Tools/WebServer/core/gdb_session.py +1281 -0
- fpbinject-1.6.8/Tools/WebServer/core/patch_generator.py +493 -0
- fpbinject-1.6.8/Tools/WebServer/core/serial_protocol.py +1182 -0
- fpbinject-1.6.8/Tools/WebServer/core/state.py +390 -0
- fpbinject-1.6.8/Tools/WebServer/core/watch_evaluator.py +290 -0
- fpbinject-1.6.8/Tools/WebServer/fpb_cli.py +42 -0
- fpbinject-1.6.8/Tools/WebServer/fpb_inject.py +837 -0
- fpbinject-1.6.8/Tools/WebServer/main.py +573 -0
- fpbinject-1.6.8/Tools/WebServer/routes.py +59 -0
- fpbinject-1.6.8/Tools/WebServer/services/__init__.py +16 -0
- fpbinject-1.6.8/Tools/WebServer/services/config_file_watcher.py +0 -0
- fpbinject-1.6.8/Tools/WebServer/services/device_worker.py +306 -0
- fpbinject-1.6.8/Tools/WebServer/services/file_watcher.py +314 -0
- fpbinject-1.6.8/Tools/WebServer/services/file_watcher_manager.py +504 -0
- fpbinject-1.6.8/Tools/WebServer/services/log_recorder.py +124 -0
- fpbinject-1.6.8/Tools/WebServer/services/mdns_advertiser.py +224 -0
- fpbinject-1.6.8/Tools/WebServer/services/timer.py +129 -0
- fpbinject-1.6.8/Tools/WebServer/services/virtual_serial.py +266 -0
- fpbinject-1.6.8/Tools/WebServer/static/css/style.css +1192 -0
- fpbinject-1.6.8/Tools/WebServer/static/css/tutorial.css +397 -0
- fpbinject-1.6.8/Tools/WebServer/static/css/workbench.css +3168 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/app.js +65 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/config-schema.js +727 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/connection.js +423 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/i18n.js +175 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/logs.js +200 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/slots.js +277 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/sse.js +81 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/state.js +225 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/terminal.js +342 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/theme.js +90 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/core/version.js +13 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/autoinject.js +549 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/config.js +602 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/editor.js +499 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/elfwatcher.js +163 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/filebrowser.js +187 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/fpb.js +554 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/inline-edit.js +228 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/patch.js +639 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/quick-commands.js +1779 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/symbols.js +1265 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/transfer.js +2478 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/tutorial.js +1163 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/features/watch.js +815 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/lib/beautify.min.js +4915 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/lib/i18next.min.js +2632 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/locales/en.js +708 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/locales/zh-CN.js +665 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/locales/zh-TW.js +667 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/ui/sash.js +185 -0
- fpbinject-1.6.8/Tools/WebServer/static/js/ui/sidebar.js +268 -0
- fpbinject-1.6.8/Tools/WebServer/templates/base.html +52 -0
- fpbinject-1.6.8/Tools/WebServer/templates/index.html +6 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/activitybar.html +67 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/editor.html +70 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/modals.html +28 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/scripts.html +110 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/sidebar.html +248 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/sidebar_config.html +15 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/sidebar_device.html +110 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/sidebar_quick_commands.html +269 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/sidebar_transfer.html +145 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/statusbar.html +30 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/terminal.html +38 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/titlebar.html +30 -0
- fpbinject-1.6.8/Tools/WebServer/templates/partials/tutorial.html +34 -0
- fpbinject-1.6.8/Tools/WebServer/utils/__init__.py +13 -0
- fpbinject-1.6.8/Tools/WebServer/utils/crc.py +299 -0
- fpbinject-1.6.8/Tools/WebServer/utils/helpers.py +108 -0
- fpbinject-1.6.8/Tools/WebServer/utils/net.py +146 -0
- fpbinject-1.6.8/Tools/WebServer/utils/port_lock.py +169 -0
- fpbinject-1.6.8/Tools/WebServer/utils/serial.py +344 -0
- fpbinject-1.6.8/Tools/WebServer/utils/toolchain.py +48 -0
- fpbinject-1.6.8/Tools/WebServer/version.py +13 -0
- fpbinject-1.6.8/fpbinject.egg-info/PKG-INFO +255 -0
- fpbinject-1.6.8/fpbinject.egg-info/SOURCES.txt +116 -0
- fpbinject-1.6.8/fpbinject.egg-info/dependency_links.txt +1 -0
- fpbinject-1.6.8/fpbinject.egg-info/entry_points.txt +3 -0
- fpbinject-1.6.8/fpbinject.egg-info/requires.txt +14 -0
- fpbinject-1.6.8/fpbinject.egg-info/top_level.txt +1 -0
- fpbinject-1.6.8/pyproject.toml +83 -0
- fpbinject-1.6.8/setup.cfg +4 -0
fpbinject-1.6.8/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VIFEX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fpbinject-1.6.8/PKG-INFO
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fpbinject
|
|
3
|
+
Version: 1.6.8
|
|
4
|
+
Summary: Runtime code injection for ARM Cortex-M via the FPB (Flash Patch and Breakpoint) hardware unit
|
|
5
|
+
Author-email: VIFEX <vifextech@foxmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/FASTSHIFT/FPBInject
|
|
8
|
+
Project-URL: Repository, https://github.com/FASTSHIFT/FPBInject
|
|
9
|
+
Keywords: arm,cortex-m,fpb,firmware,code-injection,debugging
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: Flask
|
|
22
|
+
Requires-Dist: Flask-Cors
|
|
23
|
+
Requires-Dist: pyserial
|
|
24
|
+
Requires-Dist: pygdbmi
|
|
25
|
+
Requires-Dist: watchdog
|
|
26
|
+
Requires-Dist: tree_sitter
|
|
27
|
+
Requires-Dist: zeroconf>=0.131
|
|
28
|
+
Requires-Dist: platformdirs
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest; extra == "dev"
|
|
31
|
+
Requires-Dist: coverage; extra == "dev"
|
|
32
|
+
Requires-Dist: black; extra == "dev"
|
|
33
|
+
Requires-Dist: flake8; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# FPBInject
|
|
37
|
+
|
|
38
|
+
**English** | [中文](README_zh.md)
|
|
39
|
+
|
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
|
41
|
+
[](https://www.st.com/en/microcontrollers-microprocessors/stm32f103.html)
|
|
42
|
+
[](https://github.com/apache/nuttx)
|
|
43
|
+
[](https://deepwiki.com/FASTSHIFT/FPBInject)
|
|
44
|
+
[](https://github.com/FASTSHIFT/FPBInject/actions/workflows/ci.yml)
|
|
45
|
+
|
|
46
|
+
Runtime code injection for ARM Cortex-M. Replace any function on a running MCU through a serial connection — no reflashing, no debugger, no downtime.
|
|
47
|
+
|
|
48
|
+
FPBInject uses the [Flash Patch and Breakpoint (FPB)](https://developer.arm.com/documentation/ddi0337/h/debug/about-the-flash-patch-and-breakpoint-unit--fpb-) hardware unit to intercept function calls and redirect them to your custom code in RAM, while the original Flash stays untouched.
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
## Traditional vs FPBInject
|
|
53
|
+
|
|
54
|
+
```mermaid
|
|
55
|
+
gantt
|
|
56
|
+
title Iteration cycle comparison (typical STM32 project)
|
|
57
|
+
dateFormat s
|
|
58
|
+
axisFormat %Ss
|
|
59
|
+
|
|
60
|
+
section Traditional
|
|
61
|
+
Edit code : a1, 0, 5s
|
|
62
|
+
Compile & link : a2, after a1, 15s
|
|
63
|
+
Erase flash : a3, after a2, 3s
|
|
64
|
+
Flash write : a4, after a3, 5s
|
|
65
|
+
MCU reboot : a5, after a4, 2s
|
|
66
|
+
Reproduce issue : a6, after a5, 5s
|
|
67
|
+
|
|
68
|
+
section FPBInject
|
|
69
|
+
Edit code : b1, 0, 5s
|
|
70
|
+
Compile & inject : b2, after b1, 1s
|
|
71
|
+
Reproduce issue : b3, after b2, 5s
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
## How It Works
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
flowchart LR
|
|
80
|
+
A["caller()<br/>calls foo()"] -->|"FPB intercepts<br/>foo's address"| B["Trampoline<br/>in Flash"]
|
|
81
|
+
B -->|"Jump to RAM"| C["Your Code<br/>in RAM"]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
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.
|
|
85
|
+
|
|
86
|
+
## Workbench
|
|
87
|
+
|
|
88
|
+
FPBInject ships with a browser-based workbench for the full workflow: browse symbols, read disassembly, write patches, and inject — all from one interface.
|
|
89
|
+
|
|
90
|
+
### Symbol Search & Disassembly
|
|
91
|
+
|
|
92
|
+
Search the firmware's symbol table, click a function to view its disassembly or decompiled source.
|
|
93
|
+
|
|
94
|
+

|
|
95
|
+
|
|
96
|
+
### Manual Inject
|
|
97
|
+
|
|
98
|
+
Write your replacement function in C, then hit inject. The workbench compiles, uploads, and patches — typically under a second.
|
|
99
|
+
|
|
100
|
+

|
|
101
|
+
|
|
102
|
+
### Auto Inject
|
|
103
|
+
|
|
104
|
+
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.
|
|
105
|
+
|
|
106
|
+

|
|
107
|
+
|
|
108
|
+

|
|
109
|
+
|
|
110
|
+
## File Transfer (Optional)
|
|
111
|
+
|
|
112
|
+
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.
|
|
113
|
+
|
|
114
|
+
Filesystem backends: POSIX (NuttX VFS, Linux), FatFS, standard C library (stdio), or custom implementations via the `fl_fs_ops_t` interface.
|
|
115
|
+
|
|
116
|
+

|
|
117
|
+
|
|
118
|
+
## Quick Start
|
|
119
|
+
|
|
120
|
+
### 1. Build & Flash Firmware
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git clone https://github.com/FASTSHIFT/FPBInject.git
|
|
124
|
+
cd FPBInject
|
|
125
|
+
|
|
126
|
+
cmake -B build -DAPP_SELECT=3 -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi-gcc.cmake
|
|
127
|
+
cmake --build build
|
|
128
|
+
|
|
129
|
+
st-flash write build/FPBInject.bin 0x08000000
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. Start the Workbench
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
cd Tools/WebServer
|
|
136
|
+
pip install -r ../requirements.txt
|
|
137
|
+
python main.py
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Open `http://127.0.0.1:5500` in your browser, connect to the serial port, load your ELF file, and start patching.
|
|
141
|
+
|
|
142
|
+
### 3. Or Use the CLI
|
|
143
|
+
|
|
144
|
+
All commands output JSON, designed for scripting and AI agent integration.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Search for functions
|
|
148
|
+
python fpb_cli.py search firmware.elf "gpio"
|
|
149
|
+
|
|
150
|
+
# View disassembly
|
|
151
|
+
python fpb_cli.py disasm firmware.elf digitalWrite
|
|
152
|
+
|
|
153
|
+
# Inject a patch
|
|
154
|
+
python fpb_cli.py --port /dev/ttyACM0 --elf firmware.elf \
|
|
155
|
+
--compile-commands build/compile_commands.json \
|
|
156
|
+
inject digitalWrite patch.c
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
See the [CLI Guide](Docs/CLI.md) for the full command reference.
|
|
160
|
+
|
|
161
|
+
## Writing Patches
|
|
162
|
+
|
|
163
|
+
Create a C file with the `/* FPB_INJECT */` marker. The function signature must match the original.
|
|
164
|
+
|
|
165
|
+
```c
|
|
166
|
+
#include <Arduino.h>
|
|
167
|
+
|
|
168
|
+
/* FPB_INJECT */
|
|
169
|
+
__attribute__((section(".fpb.text"), used))
|
|
170
|
+
void digitalWrite(uint8_t pin, uint8_t value) {
|
|
171
|
+
printf("Patched: pin=%d val=%d\n", pin, value);
|
|
172
|
+
value ? digitalWrite_HIGH(pin)
|
|
173
|
+
: digitalWrite_LOW(pin);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
> 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.
|
|
178
|
+
>
|
|
179
|
+
> ```c
|
|
180
|
+
> /* Define a function pointer to the original address (| 1 sets the Thumb bit) */
|
|
181
|
+
> typedef void (*digitalWrite_fn_t)(uint8_t, uint8_t);
|
|
182
|
+
> static digitalWrite_fn_t const ORIG_DIGITALWRITE = (digitalWrite_fn_t)(0x08001234 | 1);
|
|
183
|
+
>
|
|
184
|
+
> /* FPB_INJECT */
|
|
185
|
+
> __attribute__((section(".fpb.text"), used))
|
|
186
|
+
> void digitalWrite(uint8_t pin, uint8_t value) {
|
|
187
|
+
> printf("Patched: pin=%d val=%d\n", pin, value);
|
|
188
|
+
>
|
|
189
|
+
> /* Disable patch -> call original via pointer -> re-enable */
|
|
190
|
+
> fpb_enable_patch(0, false);
|
|
191
|
+
> ORIG_DIGITALWRITE(pin, value);
|
|
192
|
+
> fpb_enable_patch(0, true);
|
|
193
|
+
> }
|
|
194
|
+
> ```
|
|
195
|
+
>
|
|
196
|
+
> The workbench generates this pattern automatically when the original function address is known.
|
|
197
|
+
|
|
198
|
+
## Supported Hardware
|
|
199
|
+
|
|
200
|
+
| Feature | Spec |
|
|
201
|
+
|---------|------|
|
|
202
|
+
| Architecture | ARMv7-M, ARMv8-M |
|
|
203
|
+
| Tested MCU | STM32F103C8T6 |
|
|
204
|
+
| Patch Slots | 6 (FPB v1) or 8 (FPB v2) |
|
|
205
|
+
| Patch Modes | Trampoline / Direct (ARMv7-M REMAP), DebugMonitor (ARMv8-M BKPT) |
|
|
206
|
+
| RTOS Support | Bare-metal, NuttX |
|
|
207
|
+
| Connection | Serial (USB-to-UART or USB CDC) |
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
<details>
|
|
211
|
+
<summary>CMake Build Options</summary>
|
|
212
|
+
|
|
213
|
+
| Option | Default | Description |
|
|
214
|
+
|--------|---------|-------------|
|
|
215
|
+
| `APP_SELECT` | 1 | Application selection (3 = func_loader) |
|
|
216
|
+
| `FL_ALLOC_MODE` | STATIC | Memory allocation: STATIC or LIBC |
|
|
217
|
+
| `FPB_NO_DEBUGMON` | OFF | Disable DebugMonitor mode |
|
|
218
|
+
|
|
219
|
+
</details>
|
|
220
|
+
|
|
221
|
+
<details>
|
|
222
|
+
<summary>Project Structure</summary>
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
FPBInject/
|
|
226
|
+
├── Source/ # FPB driver, trampoline, DebugMonitor
|
|
227
|
+
├── App/
|
|
228
|
+
│ ├── func_loader/ # Serial protocol, memory allocator, FPB control
|
|
229
|
+
│ ├── inject/ # Injection helpers
|
|
230
|
+
│ └── tests/ # Firmware unit tests (host-based, with coverage)
|
|
231
|
+
├── Project/ # Platform HAL (STM32F10x, Arduino API)
|
|
232
|
+
├── Tools/
|
|
233
|
+
│ └── WebServer/ # Workbench (Flask backend + JS frontend) & CLI
|
|
234
|
+
└── Docs/ # Architecture, CLI reference, WebServer guide
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
</details>
|
|
238
|
+
|
|
239
|
+
## Documentation
|
|
240
|
+
|
|
241
|
+
| Document | Description |
|
|
242
|
+
|----------|-------------|
|
|
243
|
+
| [Architecture](Docs/Architecture.md) | FPB internals, patch modes, memory layout, protocol |
|
|
244
|
+
| [CLI Reference](Docs/CLI.md) | All CLI commands with examples and JSON output format |
|
|
245
|
+
| [WebServer Guide](Docs/WebServer.md) | Workbench setup and usage |
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
[MIT](LICENSE)
|
|
250
|
+
|
|
251
|
+
## References
|
|
252
|
+
|
|
253
|
+
- [ARM Cortex-M3 Technical Reference Manual](https://developer.arm.com/documentation/ddi0337)
|
|
254
|
+
- [ARMv7-M Architecture Reference Manual](https://developer.arm.com/documentation/ddi0403)
|
|
255
|
+
- [STM32F103 Reference Manual](https://www.st.com/resource/en/reference_manual/rm0008.pdf)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# FPBInject
|
|
2
|
+
|
|
3
|
+
**English** | [中文](README_zh.md)
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.st.com/en/microcontrollers-microprocessors/stm32f103.html)
|
|
7
|
+
[](https://github.com/apache/nuttx)
|
|
8
|
+
[](https://deepwiki.com/FASTSHIFT/FPBInject)
|
|
9
|
+
[](https://github.com/FASTSHIFT/FPBInject/actions/workflows/ci.yml)
|
|
10
|
+
|
|
11
|
+
Runtime code injection for ARM Cortex-M. Replace any function on a running MCU through a serial connection — no reflashing, no debugger, no downtime.
|
|
12
|
+
|
|
13
|
+
FPBInject uses the [Flash Patch and Breakpoint (FPB)](https://developer.arm.com/documentation/ddi0337/h/debug/about-the-flash-patch-and-breakpoint-unit--fpb-) hardware unit to intercept function calls and redirect them to your custom code in RAM, while the original Flash stays untouched.
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## Traditional vs FPBInject
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
gantt
|
|
21
|
+
title Iteration cycle comparison (typical STM32 project)
|
|
22
|
+
dateFormat s
|
|
23
|
+
axisFormat %Ss
|
|
24
|
+
|
|
25
|
+
section Traditional
|
|
26
|
+
Edit code : a1, 0, 5s
|
|
27
|
+
Compile & link : a2, after a1, 15s
|
|
28
|
+
Erase flash : a3, after a2, 3s
|
|
29
|
+
Flash write : a4, after a3, 5s
|
|
30
|
+
MCU reboot : a5, after a4, 2s
|
|
31
|
+
Reproduce issue : a6, after a5, 5s
|
|
32
|
+
|
|
33
|
+
section FPBInject
|
|
34
|
+
Edit code : b1, 0, 5s
|
|
35
|
+
Compile & inject : b2, after b1, 1s
|
|
36
|
+
Reproduce issue : b3, after b2, 5s
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
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.
|
|
40
|
+
|
|
41
|
+
## How It Works
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart LR
|
|
45
|
+
A["caller()<br/>calls foo()"] -->|"FPB intercepts<br/>foo's address"| B["Trampoline<br/>in Flash"]
|
|
46
|
+
B -->|"Jump to RAM"| C["Your Code<br/>in RAM"]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
## Workbench
|
|
52
|
+
|
|
53
|
+
FPBInject ships with a browser-based workbench for the full workflow: browse symbols, read disassembly, write patches, and inject — all from one interface.
|
|
54
|
+
|
|
55
|
+
### Symbol Search & Disassembly
|
|
56
|
+
|
|
57
|
+
Search the firmware's symbol table, click a function to view its disassembly or decompiled source.
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
### Manual Inject
|
|
62
|
+
|
|
63
|
+
Write your replacement function in C, then hit inject. The workbench compiles, uploads, and patches — typically under a second.
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
### Auto Inject
|
|
68
|
+
|
|
69
|
+
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.
|
|
70
|
+
|
|
71
|
+

|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
## File Transfer (Optional)
|
|
76
|
+
|
|
77
|
+
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.
|
|
78
|
+
|
|
79
|
+
Filesystem backends: POSIX (NuttX VFS, Linux), FatFS, standard C library (stdio), or custom implementations via the `fl_fs_ops_t` interface.
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
### 1. Build & Flash Firmware
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git clone https://github.com/FASTSHIFT/FPBInject.git
|
|
89
|
+
cd FPBInject
|
|
90
|
+
|
|
91
|
+
cmake -B build -DAPP_SELECT=3 -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi-gcc.cmake
|
|
92
|
+
cmake --build build
|
|
93
|
+
|
|
94
|
+
st-flash write build/FPBInject.bin 0x08000000
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 2. Start the Workbench
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cd Tools/WebServer
|
|
101
|
+
pip install -r ../requirements.txt
|
|
102
|
+
python main.py
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Open `http://127.0.0.1:5500` in your browser, connect to the serial port, load your ELF file, and start patching.
|
|
106
|
+
|
|
107
|
+
### 3. Or Use the CLI
|
|
108
|
+
|
|
109
|
+
All commands output JSON, designed for scripting and AI agent integration.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Search for functions
|
|
113
|
+
python fpb_cli.py search firmware.elf "gpio"
|
|
114
|
+
|
|
115
|
+
# View disassembly
|
|
116
|
+
python fpb_cli.py disasm firmware.elf digitalWrite
|
|
117
|
+
|
|
118
|
+
# Inject a patch
|
|
119
|
+
python fpb_cli.py --port /dev/ttyACM0 --elf firmware.elf \
|
|
120
|
+
--compile-commands build/compile_commands.json \
|
|
121
|
+
inject digitalWrite patch.c
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
See the [CLI Guide](Docs/CLI.md) for the full command reference.
|
|
125
|
+
|
|
126
|
+
## Writing Patches
|
|
127
|
+
|
|
128
|
+
Create a C file with the `/* FPB_INJECT */` marker. The function signature must match the original.
|
|
129
|
+
|
|
130
|
+
```c
|
|
131
|
+
#include <Arduino.h>
|
|
132
|
+
|
|
133
|
+
/* FPB_INJECT */
|
|
134
|
+
__attribute__((section(".fpb.text"), used))
|
|
135
|
+
void digitalWrite(uint8_t pin, uint8_t value) {
|
|
136
|
+
printf("Patched: pin=%d val=%d\n", pin, value);
|
|
137
|
+
value ? digitalWrite_HIGH(pin)
|
|
138
|
+
: digitalWrite_LOW(pin);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> 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.
|
|
143
|
+
>
|
|
144
|
+
> ```c
|
|
145
|
+
> /* Define a function pointer to the original address (| 1 sets the Thumb bit) */
|
|
146
|
+
> typedef void (*digitalWrite_fn_t)(uint8_t, uint8_t);
|
|
147
|
+
> static digitalWrite_fn_t const ORIG_DIGITALWRITE = (digitalWrite_fn_t)(0x08001234 | 1);
|
|
148
|
+
>
|
|
149
|
+
> /* FPB_INJECT */
|
|
150
|
+
> __attribute__((section(".fpb.text"), used))
|
|
151
|
+
> void digitalWrite(uint8_t pin, uint8_t value) {
|
|
152
|
+
> printf("Patched: pin=%d val=%d\n", pin, value);
|
|
153
|
+
>
|
|
154
|
+
> /* Disable patch -> call original via pointer -> re-enable */
|
|
155
|
+
> fpb_enable_patch(0, false);
|
|
156
|
+
> ORIG_DIGITALWRITE(pin, value);
|
|
157
|
+
> fpb_enable_patch(0, true);
|
|
158
|
+
> }
|
|
159
|
+
> ```
|
|
160
|
+
>
|
|
161
|
+
> The workbench generates this pattern automatically when the original function address is known.
|
|
162
|
+
|
|
163
|
+
## Supported Hardware
|
|
164
|
+
|
|
165
|
+
| Feature | Spec |
|
|
166
|
+
|---------|------|
|
|
167
|
+
| Architecture | ARMv7-M, ARMv8-M |
|
|
168
|
+
| Tested MCU | STM32F103C8T6 |
|
|
169
|
+
| Patch Slots | 6 (FPB v1) or 8 (FPB v2) |
|
|
170
|
+
| Patch Modes | Trampoline / Direct (ARMv7-M REMAP), DebugMonitor (ARMv8-M BKPT) |
|
|
171
|
+
| RTOS Support | Bare-metal, NuttX |
|
|
172
|
+
| Connection | Serial (USB-to-UART or USB CDC) |
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
<details>
|
|
176
|
+
<summary>CMake Build Options</summary>
|
|
177
|
+
|
|
178
|
+
| Option | Default | Description |
|
|
179
|
+
|--------|---------|-------------|
|
|
180
|
+
| `APP_SELECT` | 1 | Application selection (3 = func_loader) |
|
|
181
|
+
| `FL_ALLOC_MODE` | STATIC | Memory allocation: STATIC or LIBC |
|
|
182
|
+
| `FPB_NO_DEBUGMON` | OFF | Disable DebugMonitor mode |
|
|
183
|
+
|
|
184
|
+
</details>
|
|
185
|
+
|
|
186
|
+
<details>
|
|
187
|
+
<summary>Project Structure</summary>
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
FPBInject/
|
|
191
|
+
├── Source/ # FPB driver, trampoline, DebugMonitor
|
|
192
|
+
├── App/
|
|
193
|
+
│ ├── func_loader/ # Serial protocol, memory allocator, FPB control
|
|
194
|
+
│ ├── inject/ # Injection helpers
|
|
195
|
+
│ └── tests/ # Firmware unit tests (host-based, with coverage)
|
|
196
|
+
├── Project/ # Platform HAL (STM32F10x, Arduino API)
|
|
197
|
+
├── Tools/
|
|
198
|
+
│ └── WebServer/ # Workbench (Flask backend + JS frontend) & CLI
|
|
199
|
+
└── Docs/ # Architecture, CLI reference, WebServer guide
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
</details>
|
|
203
|
+
|
|
204
|
+
## Documentation
|
|
205
|
+
|
|
206
|
+
| Document | Description |
|
|
207
|
+
|----------|-------------|
|
|
208
|
+
| [Architecture](Docs/Architecture.md) | FPB internals, patch modes, memory layout, protocol |
|
|
209
|
+
| [CLI Reference](Docs/CLI.md) | All CLI commands with examples and JSON output format |
|
|
210
|
+
| [WebServer Guide](Docs/WebServer.md) | Workbench setup and usage |
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
[MIT](LICENSE)
|
|
215
|
+
|
|
216
|
+
## References
|
|
217
|
+
|
|
218
|
+
- [ARM Cortex-M3 Technical Reference Manual](https://developer.arm.com/documentation/ddi0337)
|
|
219
|
+
- [ARMv7-M Architecture Reference Manual](https://developer.arm.com/documentation/ddi0403)
|
|
220
|
+
- [STM32F103 Reference Manual](https://www.st.com/resource/en/reference_manual/rm0008.pdf)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""FPBInject — runtime code injection for ARM Cortex-M via the FPB unit.
|
|
2
|
+
|
|
3
|
+
This package bundles the WebServer, CLI, and supporting modules. The physical
|
|
4
|
+
layout lives under ``Tools/WebServer/`` and is mapped to the import name
|
|
5
|
+
``fpbinject`` via setuptools ``package-dir`` (see pyproject.toml).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from fpbinject.version import __version__
|
|
9
|
+
|
|
10
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# MIT License
|
|
4
|
+
# Copyright (c) 2025 - 2026 _VIFEXTech
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
FPBInject WebServer Flask Application Package.
|
|
8
|
+
|
|
9
|
+
This package contains the Flask application factory and route blueprints.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
from flask import Flask
|
|
15
|
+
from flask_cors import CORS
|
|
16
|
+
|
|
17
|
+
# Locate the package root (holds templates/ and static/) via importlib.resources
|
|
18
|
+
# so it resolves correctly whether run from source or an installed wheel.
|
|
19
|
+
try:
|
|
20
|
+
from importlib.resources import files as _res_files
|
|
21
|
+
|
|
22
|
+
WEBSERVER_DIR = str(_res_files("fpbinject"))
|
|
23
|
+
except Exception: # pragma: no cover - fallback for odd layouts
|
|
24
|
+
WEBSERVER_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_app():
|
|
28
|
+
"""Create and configure the Flask application."""
|
|
29
|
+
app = Flask(
|
|
30
|
+
"fpbinject",
|
|
31
|
+
template_folder=os.path.join(WEBSERVER_DIR, "templates"),
|
|
32
|
+
static_folder=os.path.join(WEBSERVER_DIR, "static"),
|
|
33
|
+
)
|
|
34
|
+
CORS(app)
|
|
35
|
+
|
|
36
|
+
# Import and register routes
|
|
37
|
+
from fpbinject.routes import register_routes
|
|
38
|
+
|
|
39
|
+
register_routes(app)
|
|
40
|
+
|
|
41
|
+
return app
|