picorescue
Find, inspect and recover data from Raspberry Pi Pico (RP2040 / RP2350) flash dumps. Built for Pimoroni-style images that pair a MicroPython firmware with LittleFS, FAT and (on RP2350) ROMFS partitions, where the rescue target is usually a handful of accidentally-deleted Python scripts.
It reads the bi_decl binary info region (the same BlockDevice declarations
parsed by py_decl) to locate
partitions, then reads and recovers data from them. It also signature-scans the
whole dump and merges the results, so it finds filesystems that are missing from
bi_decl or nested inside a declared device - e.g. the dir2uf2 --fs-reserve
hybrid where a LittleFS sits in the tail of a FAT block device.
Install / run
Uses uv:
uv sync
uv run picorescue --help
or, from pip:
uv pip install picorescue
uv run picorescue --help
Accepts a raw flash dump (.bin, offset 0 = flash base 0x10000000) or a .uf2
(reassembled to the correct flash addresses, gaps filled with 0xFF).
A full-flash dump is ideal. You can grab one with picotool save -a flash.bin
or over SWD with OpenOCD.
Commands
picorescue info DUMP # bi_decl info + partitions, with used storage
picorescue partitions DUMP # just the partition table
picorescue debug DUMP # report overlapping filesystems & other errors
picorescue ls DUMP [-p NAME] [-v] # list live files (-v adds file count + used size)
picorescue extract DUMP OUTDIR # extract live (non-deleted) files
picorescue recover DUMP OUTDIR # recover deleted / orphaned data
recover options: -p/--partition NAME, --no-carve, --whole-dump (carve the
entire image, not just known partitions), --min-score (carver threshold).
Supported filesystems: LittleFS (list, extract, deleted-file recovery),
FAT12/16/32 (list, extract, 0xE5 undelete), and MicroPython ROMFS
(list, extract; read-only, with .mpy bytecode surfaced verbatim).
Examples
File names below are illustrative; the layout and figures match real tool output.
Inspect a dump - firmware info, discovered partitions, and estimated usage:
$ picorescue info flash.bin
Loaded flash.bin: 16.00MB @ base 0x10000000
ProgramName: MicroPython
ProgramVersion: v1.25.0
PicoBoard: pico2
SDKVersion: 2.1.1
BinaryEndAddress: 0x100d5138
Partitions (3):
ROMFS 0x10200000 1.00MB romfs [read] (bi_decl) used ~ 409.7KB ( 40%)
MicroPython 0x10300000 13.00MB fat [read,write] (bi_decl) used ~ 1.33MB ( 11%)
12.02MB FAT volume within the 13.00MB MicroPython bi_decl region
MicroPython:littlefs@0x10f00000 0x10f00000 1.00MB littlefs [-] (scan) used ~ 100.0KB ( 10%)
List a filesystem's live files, with a summary line (-v):
$ picorescue ls flash.bin -p MicroPython:littlefs@0x10f00000 -v
# MicroPython:littlefs@0x10f00000 (littlefs) @ 0x10f00000
6212 /main.py
512 /lib/display.py
78 /state/clock.json
307200 /state/screenshot.txt
... (12 files)
-- 12 file(s), used ~100.0KB (10% of 1.00MB)
Recover deleted and orphaned data into ./rescued/ (writes a MANIFEST.json):
$ picorescue recover flash.bin ./rescued
# MicroPython (fat) @ 0x10300000 - 13.00MB
FAT12: undeleted 4 file(s)
carved 21 Python candidate(s)
# MicroPython:littlefs@0x10f00000 (littlefs) @ 0x10f00000 - 1.00MB
metadata: 12 inline file(s) recovered (1 not in live FS -> likely deleted); 3 name-only hint(s)
carved 0 Python candidate(s)
Done. 37 item(s) -> ./rescued (see MANIFEST.json)
$ ls ./rescued/MicroPython/undelete/
config.py main.py notes.txt _ecret.py
Check a dump for structural problems (exits non-zero when errors are found):
$ picorescue debug flash.bin
Loaded flash.bin: 16.00MB @ base 0x10000000
Discovered 3 partition(s).
[ERROR] Overlap: 'MicroPython' (FAT volume 0x10300000-0x10f04000) and 'MicroPython:littlefs@0x10f00000' (LittleFS 0x10f00000-0x11000000) overlap by 16.0KB
1 issue(s): 1 error(s), 0 warning(s), 0 note(s).
How recovery works
recover writes into OUTDIR/<partition>/ and a top-level MANIFEST.json
describing every recovered item and the method used.
- LittleFS metadata (
deleted/,metadata/) - LittleFS is a copy-on-write, log-structured filesystem. Deleting or overwriting a file leaves the old commit (its name and, for small files, its inline content) in the metadata block until that block is erased and compacted. picorescue threads the XOR-delta tag log of every metadata block and pulls out names + inline data across all commits, not just the live view. Entries whose name is absent from the mounted filesystem are flagged as likely-deleted and sorted intodeleted/. - FAT undelete (
undelete/) - deletion only sets the directory entry's first name byte to0xE5and frees the cluster chain; the starting cluster and size remain, so contiguous files undelete cleanly. Verify integrity of anything recovered this way - fragmented files may be partial. - ROMFS (
files/) - ROMFS is immutable, so there are no deletions to hunt;recoversimply extracts every file (including.mpybytecode) alongside the carve pass. - Carving (
carved/) - filesystem-agnostic. Scans raw blocks for printable text runs and scores them for "Python-ness" (import/def/class, indented multi-line structure, assignments). Catches scripts whose directory entry or metadata is gone but whose content still lingers in flash. Carved content is de-duplicated against live files.
Recovered scripts are best-effort: always eyeball them. Carved fragments may have a stray leading byte or be truncated at a block boundary.
Layout
src/picorescue/
bidecl.py vendored py_decl bi_decl parser (BlockDevice discovery)
dump.py load .bin/.uf2 -> addressed flash image; partition discovery
lfs.py LittleFS mount/list/extract + metadata-log recovery
fat.py FAT12/16/32 read + 0xE5 undelete
romfs.py MicroPython ROMFS read (list/extract)
carve.py Python-source carver
cli.py click CLI
Support Me
I work on Pico shinies by day, occasionally cranking out balmy tools to make my job easier and sharing them with the world.
If they help you too, great! If you want to throw me a bone for my troubles, see below:
- Ko-Fi - https://ko-fi.com/gadgetoid
- GitHub - https://github.com/sponsors/Gadgetoid
- Patreon - https://www.patreon.com/c/gadgetoid
- PayPal - https://www.paypal.com/paypalme/gadgetoid
Find some of my other projects below:
- dir2uf2 - Pack a directory and append it to a MicroPython uf2 - https://github.com/gadgetoid/dir2uf2
- py_decl - Python code to read Pico's binary declaration format - https://github.com/gadgetoid/py_decl
- Pico pinouts - https://pico.pinout.xyz
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file picorescue-0.2.0.tar.gz.
File metadata
- Download URL: picorescue-0.2.0.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cadc7e8730bfb7dca89d871caba3923045482f35971f804cf355d0ff65f323c5
|
|
| MD5 |
5037158a654d9fa926b519c71213a86d
|
|
| BLAKE2b-256 |
4db52e6a022806b16f2ee0552f88d4d5f8809ae52f8bb4ded20ae7f2ac088c86
|
File details
Details for the file picorescue-0.2.0-py3-none-any.whl.
File metadata
- Download URL: picorescue-0.2.0-py3-none-any.whl
- Upload date:
- Size: 28.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6ad4e8e04b5517e643bec4f6a9b8162e965133f02769ebd5d60af17523bb9bc
|
|
| MD5 |
60fc5022460845e83ef60b919edb3b3e
|
|
| BLAKE2b-256 |
67c8280e549f5c6696f642b8316e4938695497723fe69d75cae8ef8a77c7974a
|