MORIA(1)

πŸ“¦ version 0.3.0, in nixpkgs unstable

πŸ’… MORIA(1) β€” User Commands β€” identify files and embedded firmware structures πŸ’…

okay bestie, welcome to the moria manual page. If you're doing firmware or IoT assessments, this is your new hyperfixation. No cap.

✨ NAME ✨

moria β€” identify files and embedded firmware structures. She's the friend who looks at a mysterious blob and goes, "bestie, I know her." πŸ’…

🌸 SYNOPSIS 🌸

moria [options] <file|dir>

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so moria maps what a file is and what known structures live inside it, with confidence scores and sourced metadata. It is identification only: it locates and names embedded formats (filesystems, containers, bootloaders, executables, compression, media) with byte offsets and confidence tiers. Iconic behavior, honestly.

She is identification-first; with --extract she also unpacks SquashFS, ext2/3/4, F2FS, JFFS2, UBIFS, romfs, YAFFS2, cramfs, FAT, exFAT, NTFS, HFS+, XFS, btrfs, EROFS, and ISO9660 filesystems, ZIP/tar/cpio/MS-CAB archives, CFBF compound files (Windows Installer .msi), U-Boot uImage/FIT containers, Windows CE XIP ROM images and registry hives, and RAE Systems/Honeywell RFP firmware packages (LZARI-decompressing their sections) internally (no sudo). That's a lot of unpacking, and she ate. Every other format is still identified and located by offset, ready for another tool such as binwalk.

Output is human-readable by default. A single file shows a findings tree (containers nest their contents) followed by a run-stats footer; a directory shows per-type counts and the notable findings grouped by directory, then the same footer. The one-line assessment is carried in the JSON output only. Pass -j/--json for a single JSON object, designed to be consumed by an LLM tool caller during firmware and IoT assessments. Tool-caller bestie behavior, fr fr.

πŸŽ€ ARGUMENTS πŸŽ€

  • <file|dir> β€” A file to identify, or a directory to scan recursively. That's the whole argument, periodt.

πŸ’– OPTIONS πŸ’–

🫢 Output

  • -j, --json β€” Emit JSON instead of the human-readable view (which is the default). Use this when a tool or model will parse the output.
  • -A, --all β€” Keep interior compressed streams. By default moria drops compression-category findings (xz/gzip/lzo nodes) that fall inside a filesystem/container region β€” the internal data nodes of a UBI/UBIFS or similar per-node flash filesystem, which the filesystem extractor already recovers as real files. This applies to both identification and --extract (so extraction does not emit hundreds of redundant single-node blobs). -A restores them in both. Very "don't talk to me or my redundant single-node blobs ever again" energy. πŸ’…
  • -E, --entropy β€” Entropy analysis (off by default). Adds the unidentified_regions list β€” byte ranges no finding claims, each with its Shannon entropy β€” and high-entropy "possible encryption" hints in the assessment. The analysis is region-aware: a filesystem's compressed interior (e.g. the xz data nodes inside a UBIFS volume) is not reported as an unidentified high-entropy region, so the hint fires only on genuinely unclaimed high-entropy data.
  • --list β€” List tar/cpio/zip/MS-CAB member names and sizes, or a Windows CE ROM's modules and files. Does not extract. Sometimes you just want the guest list, not the whole party. πŸ’…

🧚 Extract

  • -e, --extract β€” Unpack identified SquashFS, ext2/3/4, F2FS, JFFS2, UBIFS (incl. UBI), romfs, YAFFS2, cramfs, FAT, exFAT, NTFS, EROFS, and ISO9660 filesystems, ZIP/tar/cpio/MS-CAB archives, CFBF compound files such as Windows Installer .msi packages, U-Boot uImage/FIT containers, Android boot/sparse images, Windows CE XIP ROM images, Windows CE registry hives, and RAE Systems/Honeywell RFP firmware packages into file.extracted/ (one subdir per region, named 0x<offset>-<type>). Recurses into nested containers by default (a gzip-wrapped filesystem, a filesystem embedded in another), re-scanning each produced file and unpacking what it finds, bounded by --depth, --max-files, and --max-bytes. ext extraction is read-only and needs no sudo. Other formats are identification-only. Takes a single file, not a directory.
bestie, read this again: -e takes a single file, not a directory. Point it at a file, not a whole directory. πŸ’€
  • -C, --outdir dir β€” Write extraction output to dir instead of the default file.extracted/.
  • --depth N β€” Maximum extraction recursion depth (default 8). --depth 1 extracts one level only (no recursion).
  • --max-files N β€” Stop extraction after N files have been produced across the whole run (default 500000); the manifest is marked capped.
  • --max-bytes N β€” Stop extraction after N output bytes (default 4294967296 = 4 GiB); the manifest is marked capped. A per-extraction decompression-ratio guard (output/input over 1000:1 above 10 MiB) also stops descent into a likely decompression bomb.
not me warning you about a literal decompression bomb. stay safe out there. 😭
  • --unpack-executables β€” Also unpack installer/document containers (CFBF/MSI) that are found inside other extracted output. By default these are unpacked only when they are the root container because a single .msi expands to several hundred database-table streams and burying the rest of the findings under them is rarely what you want. She said "main character energy only for root containers," and honestly valid.

πŸͺ¦ Carve

  • -c, --carve β€” Dump the raw byte range of each finding, and the unidentified gaps between them, to file.carved/ as <offset>-<type>.bin files, WITHOUT parsing. This is the fallback when --extract cannot reconstruct a filesystem (vendor obfuscation, an unsupported or corrupt variant): the researcher still gets the raw bytes on disk to hand to another tool or inspect by hand. A manifest.json records each blob's offset, size, and type. Independent of --extract; when both are given, carve writes to a sibling file.carved/ directory. By default sub-kilobyte gaps (usually padding) are not carved; -A carves every gap. Obeys -C and --max-bytes. Takes a single file, not a directory.
when parsing said "I can't make it make sense," carving said "bet, raw bytes on disk." iconic fallback behavior. πŸ’…

πŸ”₯ Scan

  • --broad β€” Also match the ~2500 generated general-format signatures (file(1) Magdir, magic tier) on top of the firmware core set.

βš™οΈ Configuration

  • --sigs dir β€” Use a specific signatures directory instead of the bundled ones.
  • --threads N β€” Worker count for directory scans. Default: min(cores, 16).

πŸ‘‘ Information

  • -h, --help β€” Print help and exit.
  • --version β€” Print version and exit.

🌈 EXIT STATUS 🌈

  • 0 β€” Success.
  • 1 β€” Runtime error (unreadable file, no signatures loaded).
  • 2 β€” Usage error (unknown option, missing argument).

πŸ“ ENVIRONMENT πŸ“

  • MORIA_SIGDIR β€” Overrides the signatures directory (same effect as --sigs).
  • XDG_DATA_HOME β€” If set, moria looks for signatures under $XDG_DATA_HOME/moria (else ~/.local/share/moria) as part of its search path.
  • NO_COLOR β€” Disables bold section headers in --help output.

πŸ’œ FILES πŸ’œ

When no --sigs or MORIA_SIGDIR is given, signatures are searched for, in order, next to the binary (<exe>/../signatures and the install sibling <exe>/../share/moria/), then ~/.local/share/moria, then /usr/local/share/moria and /usr/share/moria. cmake --install populates <prefix>/share/moria/. The search path is giving "I know a spot." πŸ—ΊοΈ

πŸŽ€ EXAMPLES πŸŽ€

  • Identify one file (human-readable):
moria firmware.bin
  • Identify one file as JSON (for a tool or model):
moria -j firmware.bin
  • Unpack its filesystems:
moria -e firmware.bin
  • Carve raw byte ranges to disk without parsing:
moria -c firmware.bin
  • List archive contents without unpacking:
moria --list update.tar

πŸ¦‹ SEE ALSO πŸ¦‹

file(1), binwalk(1)

Full documentation: skills/moria/README.md


okay that's the whole vibe β€” this is moria 0.3.0, dated 2026-10-05, and she's literally iconic. πŸ’–βœ¨πŸ¦‹πŸ’…

πŸ“¦ where this page comes from