cardrecovery - read-only file carver for memory card images =========================================================== cardrecovery recovers photos and documents from a raw memory card image whose filesystem is gone. It does this by FILE CARVING: it scans the raw bytes of the image for file signatures and then walks each format's internal structure to find where the file ends. It never reads a directory entry, a FAT, an inode or any other filesystem metadata - there may not be any left to read. It is strictly read-only with respect to the image. The image file is opened read-only and every read is bounds-checked. The only thing cardrecovery ever writes is the files it carves into the directory you pass to --out, plus a manifest.txt in that same directory. COMMANDS -------- cardrecovery scan [--json] [--max-carve ] cardrecovery recover --out [--types ] [--min-size ] [--max-carve ] [--apply] [--json] cardrecovery verify [--json] scan Scans the raw image and reports every file that could be recovered: type, byte offset, end offset, length, status (complete / truncated / invalid / nested) and a confidence rating. recover Carves the found files out into --out with sequential names (000001.jpg, 000002.png, ...). It is a DRY RUN by default and prints exactly what it would write; pass --apply to actually write the files. With --apply it also prints and records the SHA-256 of every carved file. verify Fully decodes every JPEG, PNG and GIF in a directory (using Go's image/jpeg, image/png and image/gif decoders) and reports which ones genuinely survived and at what dimensions. A carve that looked plausible but is actually damaged fails here. Exit status 2 if any file failed to decode. OPTIONS ------- --json Machine-readable JSON instead of text. --out Destination directory for recover (required). --types Comma-separated types to carve: jpeg,png,gif,pdf,zip. "jpg" is accepted as an alias for "jpeg". --min-size Skip carves smaller than this. Accepts a plain byte count or a K/M/G suffix (4096, 8K, 1M). --max-carve Cap for a header whose footer is never found (default 16M). --apply Actually write the carved files (recover only). -h, --help, help Show usage. EXIT STATUS 0 success 1 usage error, or the image or directory could not be read 2 verify only: at least one recovered file failed to decode FORMATS AND HOW THE END OF EACH FILE IS FOUND --------------------------------------------- JPEG Header FF D8 FF. cardrecovery walks the marker segments from SOI: every APPn / DQT / DHT / SOF segment is skipped by its declared length, and entropy-coded data after SOS is scanned with 0xFF00 byte stuffing and 0xFFD0-0xFFD7 restart markers handled correctly, until the FF D9 EOI. Dimensions come from the SOF segment. PNG Header 89 50 4E 47 0D 0A 1A 0A. The chunk chain is walked to the IEND chunk, and the CRC32 of every chunk is recomputed and compared. A file whose chunks all check out is reported with high confidence; a bad CRC downgrades it to medium. Dimensions come from IHDR. GIF Header GIF87a or GIF89a. The logical screen descriptor, global colour table, extension blocks, image descriptors, local colour tables and LZW sub-block chains are walked to the 0x3B trailer. PDF Header %PDF-. The last %%EOF before the next %PDF- header is taken as the end, which keeps incrementally-updated PDFs intact without swallowing the document that follows. Confidence is high when a startxref is also present. ZIP Header PK 03 04. The end-of-central-directory record is located and validated against the central directory offset and size it declares; when they agree the carve is high confidence. This also covers the ZIP-based container formats (DOCX, XLSX, ODT, ...). HOW THE HARD CASES ARE HANDLED ------------------------------ Header with no matching footer The carve is capped at --max-carve (default 16 MiB) or at the end of the image, whichever comes first, and it is reported with status "truncated" and confidence "low". A truncated carve is additionally cut short at the offset of the next signature found in the image, so one footerless header cannot swallow the files that follow it. Overlapping / nested signatures A JPEG usually contains a second complete JPEG: the EXIF thumbnail inside its APP1 segment. A stored ZIP entry contains the whole signature of the file it holds. Because each format walk determines the true end of the outer file, any signature that falls inside an already-carved extent is reported with status "nested" and is NOT carved as a separate file. The scan output tells you which file each nested signature was found inside, and the summary line reports how many were suppressed. The outer file is always the one carved - if you want the thumbnail, extract it from the recovered photo's EXIF. Very large images The header scan streams the image in 1 MiB chunks and carries the last 64 bytes of each chunk into the next one, so a signature that straddles a chunk boundary is still found - a real and easy-to-miss bug class. Memory use does not grow with image size. A 200 MB image scans in well under a second. False positives Random data occasionally contains a valid-looking 3-byte JPEG signature. When the structural walk shows the bytes cannot be that format at all (a PNG signature not followed by a 13-byte IHDR, a JPEG signature not followed by a marker, and so on) the hit is reported with status "invalid" and is not carved. WHAT IS IMPLEMENTED ------------------- * Signature scanning of a raw image file for JPEG, PNG, GIF, PDF and ZIP. * Structural end-of-file detection for all five formats, as described above, rather than a naive search for the first footer byte pair. * Chunked streaming scan with an overlapping window, so image size is not bounded by memory and boundary-straddling signatures are found. * Carving to an output directory with sequential names, a dry-run default, --types and --min-size filters, and a SHA-256 for every file written. * Full-decode verification of recovered JPEG/PNG/GIF with reported dimensions. * JSON output for all three commands. * Strictly read-only treatment of the image being scanned. WHAT IS NOT IMPLEMENTED ----------------------- * NO FILESYSTEM PARSING. That is the entire point of this tool: it is for cards whose FAT, exFAT or directory structure is already destroyed. If your filesystem is intact, an ordinary undelete tool will do a better job than carving, because it can recover names, dates and fragmented files. * FRAGMENTED FILES CANNOT BE RECOVERED. Carving assumes a file occupies one contiguous run of bytes. If the card wrote a photo in two pieces with other data in between, the carve will contain the wrong bytes in the middle. The verify command is how you find out that this happened - the file will fail to decode, or will decode with visible corruption. * ORIGINAL FILENAMES ARE UNRECOVERABLE. Filenames live in the directory structure, not in the file data. Carved files get sequential names. * ORIGINAL TIMESTAMPS ARE UNRECOVERABLE for the same reason. The mtime on a carved file is simply when it was carved. (Some JPEGs carry an EXIF DateTimeOriginal inside the file data; cardrecovery does not currently extract it.) * IT READS IMAGE FILES, NOT RAW DEVICES. Give it a file such as one produced by "dd if=/dev/sdX of=card.img bs=4M" or by ddrescue. It deliberately refuses anything that is not a regular file, because reading a block device directly requires elevated privileges and carries a real risk of pointing the tool at the wrong disk. * No RAW camera formats (CR2, NEF, ARW, DNG), no HEIC/AVIF, no TIFF, no MP4 or MOV video, no audio formats. * No repair of damaged files. A truncated carve is written out as-is and reported as truncated; nothing tries to reconstruct the missing tail. * No deduplication, no thumbnail extraction, no image preview. ROADMAP ------- * Raw device access (--device /dev/sdX) with an explicit confirmation step and a read-only open, for people who cannot afford the disk space for a full image. * FAT and exFAT directory recovery: when enough of the directory structure survives, recover real filenames, timestamps and cluster chains, and fall back to carving only for what the directory cannot explain. * Fragmentation handling: cluster-aware carving that can follow a file across a gap, validated by attempting a decode of each candidate reassembly. * RAW camera formats (CR2, NEF, ARW, DNG) and HEIC, plus MP4/MOV carving via the box structure. EXAMPLES -------- cardrecovery scan card.img cardrecovery scan card.img --json cardrecovery recover card.img --out ./rescued cardrecovery recover card.img --out ./rescued --types jpeg,png --min-size 8K --apply cardrecovery verify ./rescued cardrecovery verify ./rescued --json BUILDING -------- Go 1.24 or newer. Standard library only, no third-party dependencies. GOPROXY=off go build -o cardrecovery . Prebuilt binaries are in dist/: dist/cardrecovery-linux-amd64 dist/cardrecovery-darwin-amd64 dist/cardrecovery-darwin-arm64 dist/cardrecovery-windows-amd64.exe