REA accepts Binwalk 3.1.x for region inspection and Unblob 26.6.x for explicit extraction. The report parsers are verified with Binwalk 3.1.0 and Unblob 26.6.4. Another build on those lines is accepted, the observed version is reported, and a limitation records that the parser was not verified against that build. This first firmware milestone is verified on Linux x64. No device, root privilege, mount, emulation or execution of extracted programs is needed. Arbitrary firmware bytes do not pass native executable admission; select a returned ELF separately for supported Ghidra analysis.
Provide existing tools using absolute executable paths, scoped to your shell:
export REA_BINWALK_COMMAND=/absolute/path/to/binwalk
export REA_UNBLOB_COMMAND=/absolute/path/to/unblobREA does not install these tools, their dependencies or system packages. It
checks the tool's --version banner when executing a request. Binwalk must
print binwalk <version> and Unblob must print the version alone. Inspection and
extraction have independent prerequisites; one does not require the other.
Both require util-linux prlimit, normally /usr/bin/prlimit; override its
absolute path with REA_FIRMWARE_PRLIMIT_COMMAND if needed.
Unblob needs the external extractor for the selected format on its inherited
PATH: for example, debugfs for ext2/ext3/ext4. Its default Landlock sandbox
and skip rules stay enabled. Sandbox startup failures remain actionable errors;
REA does not retry with --no-sandbox. Missing extractors reported in a valid
analysis report retain partial results and dependency names inline.
Upstream sources are preserved unmodified as optional pinned submodules:
git submodule update --init third_party/binwalk third_party/unblobNormal REA builds and npm packages do not include or build these sources or engine binaries. See upstream provenance.
rea inspect-firmware-regions /path/to/firmware.bin
rea extract-firmware /path/to/firmware.bin /path/to/new-output
rea extract-firmware /path/to/firmware.bin /path/to/selected-output \
--offset 64 --length 4096 --max-depth 3The corresponding MCP tools accept the same intent:
{
"name": "inspect_firmware_regions",
"arguments": { "path": "/path/to/firmware.bin" }
}{
"name": "extract_firmware",
"arguments": {
"path": "/path/to/firmware.bin",
"output_directory": "/path/to/new-output",
"range": { "offset": 64, "length": 4096 },
"max_depth": 3
}
}Both return Evidence with the original path and SHA-256, raw producer reports, actual executable path/digest, version and limitations. Binwalk signature UUIDs and Unblob blob IDs are producer-local identities; compare content hashes and source ranges across runs. A matching version does not attest an installed build's source revision, so that field remains unknown.
- Region offsets refer to bytes in the original firmware.
reported_sizepreserves Binwalk's output;size_basis=provider_reported_validation_unknownreflects that Binwalk can replace unknown lengths with the next signature or EOF before serialization. Neither a signature nor its confidence byte proves a valid partition, encryption or runtime behavior. - Extraction returns regular file paths, independently verified SHA-256 values,
producer hashes when available, chunks, diagnostics and task derivations.
Unblob's task report is unordered; REA resolves relationships by paths and
blob IDs. Logical references use
$input,$output, or$output/<relative-path>so a real file named$inputstays distinct from the original input. Chunk offsets belong to the task's input file. Only chunks in the selected input have a knownroot_file_range, adjusted by the selected offset. - Decompressed children have unknown original byte ranges and runtime addresses. Task lineage does not establish a byte-for-byte mapping through decompression.
partialmarks unknown/encrypted chunks, reported extraction problems, unpublished links/special files or reached depth limits.completemeans the engine finished its configured extraction without those reported conditions; default skip rules and format coverage still apply. It does not establish complete firmware reconstruction.- The destination must be absent, absolute, with an existing parent. Publication uses exclusive, symlink-resistant writes and durable digest readback. Existing destinations are never overwritten. This is not an atomic directory rename: a new tree can be visible while files are written. Failed uncommitted output is rolled back. Symlinks and special files are recorded without publication; empty directories, ownership and executable permission bits are not reproduced.
Select a verified native child for the existing tools:
{
"name": "open_binary",
"arguments": {
"path": "/path/from/extraction/usr/bin/program",
"provider_id": "ghidra"
}
}Then use analyze_function, inspect_native_api or the other native primitives.
Native architecture and executable validation still apply. This milestone does
not add MIPS/PPC/RISC-V decompilation, raw flash load profiles, bootloader/device
tree semantics, vulnerability scanning, emulation or hardware runtime capture.
- Nonempty regular firmware inputs: at most 128 MiB, copied and hashed into a private workspace before analysis. No in-place source modification.
- One queued heavy operation per provider instance; Binwalk one thread and Unblob one worker. No automatic parallel extraction engines.
- 120-second deadline; inherited 1 GiB per-process address-space limit,
120 CPU seconds and zero core-dump allowance through
prlimit. - JSON reports: at most 8 MiB; oversized reports fail without a truncated success. Diagnostic output is bounded with exact captured byte counts.
- Default staging budget: 256 MiB and 10,000 entries, configurable using
max_output_bytes/max_output_files(CLI--max-output-bytes/--max-output-files). Depth defaults to 3, allowed range 1–10.
Staging budgets are polled and rechecked after exit; short-lived overshoot is possible. The inherited file-size limit bounds each file, not total storage; address-space limits are per process, not an aggregate memory quota. Worker count is not CPU affinity for every external extractor. These limits do not claim cgroup/container containment. Cancellation cleans the owned process group before removing its workspace; uncertain cleanup retains resources and blocks further operations in that provider instance.
Generate small public, source-derived fixtures into ignored _reference/:
npm run fixtures:firmware
npm run verify:firmwareFixture generation requires existing Python 3 and a host C compiler (cc);
override with REA_FIRMWARE_PYTHON / REA_FIRMWARE_CC. The independent oracle
records gzip offset/length and extracted file hashes. Real verification checks
CLI/MCP, full-file and selected-range extraction, unknown chunks, depth limits,
content parity and unchanged source bytes. No binary fixture is committed.
Additional lanes are explicit, so the base lane stays small:
# Existing mke2fs and debugfs required; put debugfs on the scoped PATH.
REA_FIRMWARE_VERIFY_EXT4=1 npm run fixtures:firmware
REA_FIRMWARE_VERIFY_EXT4=1 npm run verify:firmware
# Existing Ghidra 12.1.x and its declared JDK required.
REA_FIRMWARE_VERIFY_GHIDRA=1 npm run verify:firmwareREA_FIRMWARE_MKE2FS_COMMAND selects the optional ext4 fixture producer.
REA_FIRMWARE_FIXTURE_ROOT selects generated data storage.
REA_FIRMWARE_TEST_ENTRYPOINT can select an isolated installed npm package's
scripts/rea.mjs to verify the packaged CLI/MCP workflow. Real coverage here is
generated gzip/USTAR/ext4 with a Linux x64 ELF child; SquashFS, UBIFS, proprietary
firmware, other hosts and architectures remain unverified.