# Binary hardening Use `inspect_binary_hardening` to read compile-time and link-time hardening from an explicit local ELF, PE, or Mach-O file. No disassembler, pwntools install, or target execution is required. Universal Mach-O files return one image per slice. ```sh rea inspect-binary-hardening /absolute/path/to/program --json rea inspect-binary-hardening /absolute/path/to/program --max-file-bytes 1048576 --json ``` MCP accepts `{ "path": "/absolute/path/to/program" }`. CLI paths may be relative to the current directory; MCP paths must be absolute. Each image has one row per property: `property`, `state`, `confidence`, `basis`, `location`, `note`, and `next_probe`. States are `enabled`, `disabled`, `partial`, `unknown`, and `not_applicable`. `confidence` is `observed` for a single header or import, and `derived` when the state combines several fields, such as ELF PIE or full RELRO. An image's `architecture` is its CPU family, or null when REA has no family for it. `machine` and `slice.architecture` keep the exact identity, such as `arm64e`. ELF rows cover PIE (`ET_DYN` with `PT_INTERP` or `DF_1_PIE`), NX (`PT_GNU_STACK`; a missing header is `unknown` and the architecture default is only noted), RELRO (`PT_GNU_RELRO` plus `BIND_NOW`), stack protector and FORTIFY undefined dynamic imports, CET and AArch64 BTI/PAC GNU property notes, `DT_RPATH`, `DT_RUNPATH`, and whether a `SHT_SYMTAB` section is present. Only an undefined dynamic import counts. Imports come from the table `DT_SYMTAB` declares. A `SHT_DYNSYM` section only supplies the symbol count when `DT_HASH` and `DT_GNU_HASH` leave it open, and only if it names the same symbol and string tables. Without that count the import rows are `unknown` and give the reason. A missing `__stack_chk_fail` or fortify import is `unknown`. That absence does not establish that the protection was left out of a static or stripped build. A present import is a sign that some code uses the protection, not proof that all code does. NX, RELRO, `DT_RPATH`, and `DT_RUNPATH` are `not_applicable` when `e_type` is neither `ET_EXEC` nor `ET_DYN`, and `unknown` for a linked image with no readable program headers. PE rows read `DllCharacteristics` for dynamic base, high-entropy VA, NX compatibility, force integrity, no SEH, and guard CF. `cfg_instrumented` is the load-config `GuardFlags` linker claim when `Size` covers that field. An unreadable load configuration, including a TLS failure in that parser, leaves only `cfg_instrumented` `unknown`. Authenticode and debug rows report certificate and debug directory presence, not signature validity or debug content. A directory whose address and size disagree is `unknown`. Mach-O rows read `MH_PIE`, `MH_ALLOW_STACK_EXECUTION`, `MH_NO_HEAP_EXECUTION`, an `__RESTRICT` segment, `LC_CODE_SIGNATURE` presence, encryption from the existing encryption reader, the arm64e subtype, and a stack-protector import. `hardened_runtime` is `unknown` with `next_probe` `inspect_signature`. CodeDirectory flags stay with that tool. The report states presence of build-time features. It does not state exploitability. `inspect_binary_layout` is unchanged. The default read budget is 64 MiB (`max_file_bytes`, ceiling 512 MiB). A changed file is refused. A malformed header, a Mach-O segment or encryption command that fails admission, or a program, section, or segment payload declared outside the file returns no partial report, except that one bad slice in a universal Mach-O leaves the other slices. A symbol table or symbol name over its budget leaves the import rows `unknown` and keeps the header rows. A GNU property note over its budget, or a compressed note section that may hold one, leaves the CET and BTI/PAC rows `unknown` and keeps the header rows. A dynamic-table budget still fails the ELF report. The image is never executed.