Skip to main content

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 into deleted/.
  • FAT undelete (undelete/) - deletion only sets the directory entry's first name byte to 0xE5 and 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; recover simply extracts every file (including .mpy bytecode) 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:

Find some of my other projects below:

Metadata

Release files for picorescue 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for picorescue 0.2.0
File Size Uploaded
picorescue-0.2.0.tar.gz 24.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for picorescue 0.2.0
File Interpreter ABI Platform
picorescue-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 52.1 kB

Release files / picorescue-0.2.0.tar.gz

Download URL picorescue-0.2.0.tar.gz
Size 24.0 kB
Tags Source
SHA-256 checksum
How to use checksums
cadc7e8730bfb7dca89d871caba3923045482f35971f804cf355d0ff65f323c5
BLAKE2b-256 checksum
How to use checksums
4db52e6a022806b16f2ee0552f88d4d5f8809ae52f8bb4ded20ae7f2ac088c86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release files / picorescue-0.2.0-py3-none-any.whl

Download URL picorescue-0.2.0-py3-none-any.whl
Size 28.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a6ad4e8e04b5517e643bec4f6a9b8162e965133f02769ebd5d60af17523bb9bc
BLAKE2b-256 checksum
How to use checksums
67c8280e549f5c6696f642b8316e4938695497723fe69d75cae8ef8a77c7974a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page