
IoT firmware identification and extraction
IoT firmware identification and extraction
moria identifies files and the structures embedded inside firmware and IoT images (filesystems, kernels, bootloaders, archives, keys) and unpacks most of what it finds, without root. It reports each finding with a byte offset, a type, and a confidence score, and it speaks clean JSON so scripts and LLM agents can drive it as easily as people can.
upx -d.openat + O_NOFOLLOW (no path-traversal or symlink escape), and decompression is bounded against bombs.-j) for tools, with offsets and confidence on every finding.cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
cmake --install build --prefix ~/.local # or /usr/local (needs sudo)
Build needs cmake, a C++20 compiler, and the zlib, liblzma, lz4, and zstd development libraries (the decompressors used by --extract). On Debian/Ubuntu: sudo apt install cmake g++ zlib1g-dev liblzma-dev liblz4-dev libzstd-dev. To build without one (a minimal or identify-only build), configure with -DMORIA_OPTIONAL_CODECS=ON and the missing codec is simply disabled.
The moria binary is self-contained: all signature sets are embedded at build time, so the binary works anywhere with nothing installed alongside it (cp build/moria ~/.local/bin is enough, and a downloaded release binary just runs). To use external signatures instead of the embedded ones (to test a new .toml without rebuilding, say), pass --sigs DIR or set $MORIA_SIGDIR.
Output is human-readable by default. Pass -j for JSON.
moria <file> # identify: a findings tree with offsets, types, and confidence
moria <dir> # scan a tree: a type summary plus the notable files
moria -j <file> # JSON, for tools and agents
moria -e <file> # extract to <file>.extracted/ (-C DIR to choose the output dir)
moria -c <file> # carve raw byte ranges to <file>.carved/ (no parsing)
moria -E <file> # entropy pass: flag unidentified / possibly-encrypted regions
moria --list <archive> # list tar/cpio/zip members without extracting
moria --broad <path> # also load the ~2.5k general file-type signatures
moria --help
-e unpacks recognized formats under <file>.extracted/, one directory per region (0x<offset>-<type>/), plus a manifest.json mapping offsets to paths. It recurses into nested containers automatically and rebuilds UBI images volume by volume. Guards (--depth, --max-files, --max-bytes, and a decompression-ratio cap) bound hostile input; a tripped guard stops that branch and still returns everything recovered.
Unpacked in-process, no external tools and no sudo:
signatures/ is the hand-written core: firmware filesystems, containers, kernels, and common formats, each with structural validation.signatures-firmware/ holds vendor firmware-container magics and loads by default.signatures-generated/ holds ~2.5k general file-type magics derived from file(1)'s magic database and loads only with --broad.To add a format, drop a .toml in signatures/. A small C++ validator is only needed for checks the declarative layer can't express, such as CRCs or cross-block pointers.
moria does structural identification, extraction, and carving. Secret/credential scanning, SBOM, CVE, and license analysis are a separate tool (mithril).
MIT, see LICENSE.
Some on-disk format handling is a clean reimplementation of the algorithms in other open-source projects, written independently against moria's own I/O layer (no source copied): the UCL/NRV2B decompressor and CTO unfilters from UPX/UCL (GPL-2.0, algorithms only), and the metadata-commit and CTZ skip-list layout of littlefs (BSD-3-Clause), and the page/object layout of SPIFFS (MIT).