Binary composition & provenance¶
Where the SBOM answers what packages are installed, --binaries
<dir> answers what the binaries are actually made of. It walks a directory,
parses every ELF file it finds, and reports — per binary and aggregated over the
whole tree — architecture, libc, shared-library dependencies, the toolchain that
built it, and its exploit-mitigation posture.
It is opt-in (it reads file contents, so it is not part of a default run) and
entirely path-based: point it at / on a live host, or at a mounted image
under --root to analyze a firmware rootfs offline. The parser is
dependency-free (no libelf) and fully bounds-checked, so it is safe to run
against untrusted images — a malformed or truncated file is reported as “not
ELF” rather than crashing the scan.
delta --binaries /usr/bin
delta --root /mnt/image --binaries / --json
Per binary¶
For each ELF, Delta records:
architecture / class / endianness — from the ELF header (
e_machine, ELF32/64, LE/BE). A mix of architectures in one image is itself notable.libc flavor — derived from the program interpreter (
PT_INTERP):glibc(ld-linux*),musl(ld-musl*),uclibc, or the raw loader name. This distinguishes an embedded musl/uClibc rootfs that/etc/os-releasewould not reveal.dependencies — the
DT_NEEDEDshared libraries the binary pulls in, plusDT_SONAMEandRUNPATH/RPATH.toolchain — the producer string(s) from the
.commentsection (GCC: (…) 12.2.0,clang version …) and the.note.gnu.build-id.linkage — dynamic, static, or static-PIE.
exploit mitigations — NX (non-executable stack), PIE, RELRO (partial/full), stack canary,
_FORTIFY_SOURCE, and whether the binary is stripped.
In --json these appear under binaries.items:
{ "path": "/bin/ls", "arch": "aarch64", "class": 64, "endian": "LE",
"pie": true, "static": false, "libc": "glibc",
"compiler": "GCC: (Ubuntu 13.2.0) 13.2.0",
"build_id": "14aaed12…", "nx": true, "relro": "full",
"canary": true, "fortify": true, "stripped": true,
"needed": ["libc.so.6", "libselinux.so.1"] }
Distribution summary¶
The interesting signal is usually the aggregate over the whole image, emitted as
binaries.summary:
"summary": {
"total": 235,
"architectures": ["aarch64"],
"libc": ["glibc"],
"compilers": ["GCC: (Ubuntu 13.2.0) 13.2.0"],
"hardening": { "nx": 235, "pie": 234, "relro": 235, "full_relro": 234,
"canary": 232, "fortify": 228, "static": 0, "stripped": 235 }
}
so at a glance you see: a single-architecture glibc image, one toolchain, and a hardening-coverage count per mitigation (how many of the 235 binaries carry NX, PIE, full RELRO, a canary, fortify). A stray second architecture, an unexpected compiler, or a dip in mitigation coverage all stand out.
Hardening policy¶
The composition above is descriptive; a norm.binaries block makes the
exploit-mitigation posture enforceable. Each require_* demands that
mitigation on every scanned executable and emits a hardening violation
where it is missing:
{ "norm": { "binaries": {
"require_nx": true, "require_pie": true, "require_relro": true,
"require_canary": true, "require_fortify": false } } }
delta --binaries /usr/bin --cfg example/config_binaries.json --json
Shared libraries are exempt from require_pie (they are always position-
independent) but are still held to the memory-protection requirements. Like the
other policy checks, this is inert unless the binaries block is present
(see Configuration), so an unconfigured --binaries run only reports,
never fails.
Notes¶
Static binaries are typically stripped, so canary/fortify (detected from the dynamic symbol names) are not visible for them and read as absent.
Non-ELF files (scripts, data, PE/Mach-O) are silently skipped.