mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
feat(native): inspect offline ELF layout through pwntools (#1037)
* feat(native): inspect offline ELF layout through pwntools * fix(build): prepare offline ELF verification on clean runners * fix(native): preserve snapshot acquisition cancellation * fix(native): handle sectionless ELF and retain decoder diagnostics * test(native): verify malformed symbol table entry sizes * fix(native): respect inherited limits and unused segment fields * fix(native): preserve packed relocations and decoder launch constraints * fix(native): validate symbol spans and preserve resource failures * fix(native): preserve diagnostic completeness and memory failures * fix(native): guard bridge imports against allocation failures * fix(native): validate relocation symbol and section references * fix(native): retain diagnostics through compounded cleanup failures * fix(native): preserve unresolved relocation section references * fix(native): bound linkage evidence and preserve CPU signal diagnostics * fix(native): preserve file-size resource failure evidence * fix(native): retain opaque linkage names and late cancellation output * test(native): make CPU-limit fixture robust across runners * fix(native): verify resource failure branches before interpreting exits * fix(native): validate declared ELF string references * fix(native): validate section-name table before decoding * fix(native): enforce canonical ELF name-table escapes
This commit is contained in:
1 parent
8d5c88f17c
commit
4fc6565b0f
59 files changed
+5068
-45
No files matched your search
@@ -0,0 +1,131 @@
|
||||
# Offline binary diagnostics
|
||||
|
||||
`inspect_binary_layout` / `inspect-binary-layout` answers what an explicit
|
||||
file contains: section/segment layout, original symbols/relocations, static
|
||||
linkage names and mitigation indicators. It works without an active Hopper,
|
||||
Ghidra or IDA target. CTF is one possible use; crash triage, compatibility and
|
||||
ordinary binary inspection use the same modular capability.
|
||||
|
||||
## Bring your own engine
|
||||
|
||||
Initial real verification covers **Linux x64, ELF64 x86-64 little-endian
|
||||
EXEC/DYN/REL**. Provide an absolute Python executable whose environment already
|
||||
contains unchanged pwntools 4.15.0, pyelftools 0.33 and Unicorn 2.1.2:
|
||||
|
||||
```sh
|
||||
REA_PWNTOOLS_PYTHON=/absolute/isolated-env/bin/python \
|
||||
rea inspect-binary-layout ./selected.elf --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_binary_layout",
|
||||
"arguments": { "path": "/artifacts/selected.elf" }
|
||||
}
|
||||
```
|
||||
|
||||
REA never installs Python, packages or GDB, changes a user init file, launches
|
||||
the selected object as a host process, or requests runtime library resolution. The Python process
|
||||
uses isolated mode and an owned cache. Exact upstream profiles are recorded in
|
||||
[upstream provenance](../third_party/pwntools/README.md).
|
||||
|
||||
## Interpreting results
|
||||
|
||||
- Artifact path, SHA-256 and size identify original selected bytes. All parsing
|
||||
reads an owned stable snapshot; the original is unchanged.
|
||||
- Addresses, offsets, lengths and flags are hexadecimal strings. A linked
|
||||
address is not a runtime address. Runtime load base remains null. A zero
|
||||
EXEC/DYN entry means absent; a relocatable entry is not applicable.
|
||||
- Each symbol retains its table and entry index. Its reported value can mean
|
||||
undefined, alignment, absolute value, no address, unknown section index,
|
||||
section offset, TLS offset or linked virtual address. Duplicate names remain
|
||||
separate entries. SHN_XINDEX remains unresolved in the selected upstream
|
||||
representation; its external index table is not silently treated as resolved.
|
||||
- Relocatable objects have section-relative relocations and no executable entry
|
||||
claim. Signed REL/RELA relocation addends are decimal strings. Packed RELR
|
||||
tables retain full original bytes and upstream-decoded offsets as derived
|
||||
evidence, with per-offset packed-word locations and implicit addends unknown.
|
||||
Overall relocation inventory completeness remains unknown.
|
||||
A relocatable SHN_UNDEF target or inactive SHT_NULL header remains an explicit
|
||||
unknown with the original reported section index and offset.
|
||||
- REL/RELA symbol references retain their original table and entry indices.
|
||||
Positive indices resolve through a validated symbol table; symbol index zero
|
||||
means a zero symbol value without a table lookup, including when no table is
|
||||
linked. Malformed references fail with the affected section and symbol index.
|
||||
- Section-name and symbol tables require declared SHT_STRTAB links.
|
||||
SHN_UNDEF explicitly means no section names: display is empty, raw name and
|
||||
location unknown, with the original name offsets retained. Extended section-name
|
||||
indices resolve through section zero and undergo the same validation.
|
||||
- Symbol tables require a declared SHT_STRTAB link. Name offsets and terminators
|
||||
must stay inside the declared string table; malformed references fail before
|
||||
unrelated bytes can become names. Dynamic dependencies use DT_STRTAB/DT_STRSZ
|
||||
with a unique file-backed mapping; ambiguous or unbacked mappings are
|
||||
explicitly unsupported.
|
||||
- Name display strings use UTF-8 replacement for opaque bytes, including
|
||||
sectionless dependency names and interpreter paths. Raw name
|
||||
bytes and string-table ranges retain observed identity where resolvable;
|
||||
ranges include the terminating NUL and base64 bytes exclude it.
|
||||
- NOBITS and NULL sections provide no file bytes. Reported file-backed ranges
|
||||
are checked against snapshot size again at the public boundary. PT_NULL payload
|
||||
fields are unused: reported numbers remain, file backing is none and interpreted
|
||||
permissions are null. Segments with zero file size also have no file bytes.
|
||||
- Sectionless dynamic images still report dependency names with raw bytes and
|
||||
file locations where uniquely mapped. Missing symbol/relocation section tables
|
||||
do not establish that those runtime tables are absent.
|
||||
Upstream RELRO/canary heuristics rely on sections and can be incomplete in
|
||||
this case; their reported candidates retain an explicit coverage limitation.
|
||||
- DT_NEEDED/PT_INTERP are reported names, not resolved runtime paths. GOT/PLT
|
||||
maps are derived convenience views and may collapse aliases. PLT inference
|
||||
can emulate selected instructions in Unicorn; its outputs are static candidates.
|
||||
Exec syscall tracing observes host process launches. Map completeness
|
||||
remains unknown and original upstream warnings are returned inline.
|
||||
- Canary, PIE, NX, executable-stack and RELRO are static inferences. NX and
|
||||
executable-stack are separate upstream indicators: on x86-64 an executable
|
||||
stack can accompany an unknown (`null`) NX indicator. ET_DYN can be a shared
|
||||
library, and an absent canary symbol does not prove every function unprotected.
|
||||
|
||||
Complete results have a 32 MiB input, 64 MiB reply, 1 MiB combined diagnostics
|
||||
and 30-second owned command deadline. The reply budget applies to the decoder
|
||||
record; the CLI/MCP Evidence envelope adds copies and encoding overhead. The
|
||||
pinned MCP SDK defaults to a 10 MiB receive buffer. Large complete results need
|
||||
a caller-configured `StdioClientTransport({ maxBufferSize: ... })`; the large
|
||||
extended-index verification uses 256 MiB and an explicit five-minute request
|
||||
timeout in its separate opt-in MCP lane. REA does not silently truncate the result. Python lowers resource soft limits to at most 3 GiB virtual address space,
|
||||
30 CPU seconds and 64 MiB file output. Inherited tighter soft/hard limits are
|
||||
retained, and the effective values are returned in the result limitations. Virtual address space is not RSS;
|
||||
Unicorn needs a large virtual map. Limits fail with no partial success, and
|
||||
owned process/root cleanup completes independently of cancellation.
|
||||
|
||||
Core files, GDB session/state/control, optional pwndbg enrichment and instruction
|
||||
inspection are separate increments tracked by #969. This increment makes no
|
||||
real support claim for those features or other operating systems/architectures.
|
||||
|
||||
Typed decoder failures retain bounded `captured_output` (stdout, stderr and an
|
||||
explicit truncation flag) alongside the original failure category and reason.
|
||||
Successful `diagnostics` also exposes the supervisor's `truncated` flag. Output
|
||||
beyond the complete diagnostic budget is rejected, including when the decoder
|
||||
writes a valid reply; cleanup and late cancellation reuse the observed flag.
|
||||
Memory allocation failures use `resource_constraint`, with effective resource
|
||||
limits (or an explicit unknown) and memory-specific recovery guidance. A
|
||||
MemoryError alone does not establish the exact failed allocation or that a
|
||||
particular budget was exhausted. Invalid undersized symbol entries are rejected
|
||||
before REA reports source ranges for them.
|
||||
The bridge reserves 1 MiB to report allocation failures. If even error reporting
|
||||
or serialization fails with MemoryError, its reserved exit status preserves the
|
||||
resource classification with unknown effective limits.
|
||||
Reserved exits require a matching private marker written by the bridge's actual
|
||||
failure branch. Bare launcher exits or missing/unwritable markers retain process
|
||||
diagnostics with the resource cause unverified.
|
||||
Signal termination alone is never classified as observed memory exhaustion.
|
||||
The small bootstrap also covers catchable MemoryError during adapter imports,
|
||||
compilation and initialization. Python interpreter startup failures before the
|
||||
bootstrap runs retain their observed process diagnostics without guessed causes.
|
||||
SIGXCPU retains a CPU resource diagnostic and CPU-specific recovery advice.
|
||||
Observed EFBIG writes or SIGXFSZ termination retain a file-size resource
|
||||
diagnostic and file-size-specific recovery advice. If a tight file limit also
|
||||
prevents writing the limit record, effective values remain unknown.
|
||||
The bridge records actual limits in owned storage before analysis; missing or
|
||||
malformed limit reports remain unknown with their read failure preserved. A
|
||||
received signal alone does not establish its exact cause. Dynamic tags require
|
||||
a complete DT_NULL inside PT_DYNAMIC; interpreter names and ranges end at the
|
||||
first NUL, with any padding still represented by the original segment range.
|
||||
Reference in new issue
Block a user