Entry / main
0x004007cc · ARM64
For reverse engineers triaging unfamiliar targets
r2b gives the tools already in your lab one bounded first pass, ranks the addresses and artifacts worth opening, and keeps the evidence portable for a human or harness.
Deterministic by default · no target execution · no model call
$ r2b brief shallow-linux-arm64 --quick --no-save --json
Prioritized regions
0x004007cc · ARM64
strcpy
r2b decompile shallow-linux-arm64 0x4007cc --jsonCompiled and rerun from this checkout. Model calls: 0.
Keep the native tools and interfaces you trust. r2b detects the stack, dispatches only the requested stages, and records every missing prerequisite.
DOGFOOD HOST · ready: file, radare2, Capstone, Ghidra, binwalk, Frida, QEMU · absent: unblob, reported as a skip
THE LOOP
Wrap proven tools, preserve their output, and report missing analyzers without pretending every laptop is a full lab.
Select a small set of regions, imports, artifacts, and disagreements worth a seasoned practitioner’s attention.
Emit exact verification, extraction, or single-function decompilation commands. Humans read them; harnesses execute them.
BURDEN OF PROOF
r2brief is not a new disassembler, emulator, or oracle. It is useful only when orchestration, ranking, and a clean handoff save more time than they cost.
If you are already deep in one target inside r2, stay there.Do that when identity and headers answer the question. r2b earns its keep when you need the same bounded pass across formats, recorded tool gaps, ranked regions, and a versioned handoff.
format + arch + tool status + r2b.briefing.v1You should. radare2 is an analysis engine; r2b is a control and handoff layer. It chooses a small surface, keeps the evidence, and sends you back with an exact address—not a replacement UI.
r2b decompile … 0x4007cc --jsonCorrect. Static triage cannot prove runtime behavior. The default path avoids executing unknown targets; use emulation or instrumentation once the brief gives you a falsifiable candidate.
dynamic stages are explicit, never impliedNo model participates in classification, evidence collection, scoring, or handoff generation. Asking a model is a separate action over the completed brief.
models called: 0It is a deterministic, testable point table—not a confidence percentage. Every region includes why it ranked and the source snippet. Ignore the order and keep the evidence if you disagree.
score + why + source + addressunfamiliar corpora · batch baselines · uneven lab machines · analyst-to-agent handoffs · replayable evidence
NOT THE POINTreplacing r2 fluency · proving runtime behavior · generating a wall of prose · hiding missing tools
CHECKED OUTPUT
The useful unit is not a wall of generated prose. It is a finding tied to an address, artifact, and tool observation—with a concrete way to challenge it.
Claims are invitations to verify, not decorations.
{
"schema_version": "r2b.briefing.v1",
"subject": {
"format": "elf",
"arch": "arm64/64",
"function_count": 18,
"dangerous_imports": ["strcpy"]
},
"regions": [{
"rank": 1,
"title": "Entry / main",
"address": "0x4007cc",
"tool": "radare2"
}],
"handoff": {
"schema_version": "r2b.handoff.v1",
"next_argv": [
"r2b decompile ... 0x4007cc --json"
]
}
}FRESH DOGFOOD RUN
The same bundled AArch64 fixture was compiled, briefed, verified, and decompiled on this machine. One evidence chain—not three disconnected screenshots.
ELF · arm64/64 · 18 functions · 9 imports
01 Entry / main @ 0x4007cc 89
02 PLT / strcpy 86
The bounded pass identified the format, import surface, entry disassembly, and a concrete follow-up without executing the target.
{
"import": "strcpy",
"status": "dynamic",
"call_sites": [{
"function": "check_phrase",
"address": "0x004007a0",
"argument": "<unresolved>"
}]
}
The verifier recovered the AArch64 call site and caller, then refused to invent a value for the first argument.
bool check_phrase(char *param_1)
{
int iVar1;
char acStack_20[32];
strcpy(acStack_20,param_1);
iVar1 = strcmp(acStack_20,training_password);
return iVar1 == 0;
}
One-function decompilation at 0x40078c confirms a fixed-size destination and dynamic source. Caller ownership is still the next question.
DOGFOOD PAID OFF: this run exposed a missing AArch64 bl/blr verifier path. It is fixed and regression-covered. The fixture stays intentionally shallow so failures are obvious.
NO HIGHLIGHT REEL
These are the shallow fixture results from the current checkout—not a benchmark and not a hand-selected success case. Missing cross-compilers stay visible.
shallow-hostMach-O · arm64built + checkedlow · __strcpy_chk · 2 regionsshallow-linux-arm64ELF · arm64built + checkedmedium · strcpy · 2 regionsshallow-linux-arm32ELF · arm32skipped herecross-compiler unavailableshallow-windows-x86_64.exePE · x86_64skipped hereMinGW unavailablePE and ARM32 parsers also have deterministic format fixtures in the test suite; they are not presented here as locally compiled binaries.
ONE REAL AARCH64 INVESTIGATION
OpenWrt's small default web server sits in front of LuCI. A hash-pinned 66 KB build was downloaded, checked, and read—never executed or committed.
r2b grouped execl with socket acceptance, fork, and dynamic loading, then proposed caller verification. That turns 117 imports into one concrete question: why does a router's HTTP daemon launch a process?
The CGI dispatcher accepts configured interpreter suffixes or a CGI docroot. The child clears its environment, installs CGI variables, changes to the resolved root, and calls execl directly on the interpreter or script. Request data is piped to child stdin.
r2b verify … --import execl returned no verdicts on this stripped, sectionless build. The quick rank found the right subsystem; the current verifier did not recover the call sites. The case records that failure instead of rounding it into a win.
The useful audit is not “is execl dangerous?” It is whether configuration or a writable filesystem can influence the interpreter map, CGI docroot, resolved script path, or environment crossing into the child.
HONEST STRESS TEST
A Homebrew Node build made the failure mode obvious. The launcher looked trivial because the V8 implementation lived in a linked object. The real object completed, but generic import and string signals had the wrong base rate for useful prioritization.
2.6s · 1 region · mostly a loader
29.9s · 1,930 imports · 6 generic regions
No automatic next argv. Narrow by dependency, crash address, export, subsystem, or version diff. JIT code is absent from the static file and needs runtime capture.
SETUP
Bootstraps uv when needed and installs the selected Python environment into this checkout. No sudo. No system-Python mutation.
git clone https://github.com/sandbornm/r2brief.git
cd r2brief
./scripts/install.sh
./scripts/install.shsniffs memory + architectureR2B_FLAVOR=core ./scripts/install.shconstrained ARM / PiR2B_FLAVOR=lab ./scripts/install.shpractitioner workstationR2B_FLAVOR=full ./scripts/install.shheavy analyzers, capable hostOnce that succeeds, choose a mode:
Run a brief
uv run r2b brief ./sample.bin \
--quick --no-save --json
No extra service. The target is read, not executed, on the default path.
Keep evidence in-process
from r2b import analyze
report = analyze("./sample.bin")
for region in report.regions:
print(region.id, region.evidence)
Run it with uv run python. Typed regions; no implicit record or model call.
Optional interpretation
# Ollama must already be installed and running
ollama pull gemma3:4b
uv run r2b brief ./sample.bin \
--quick --ask --json
r2b does not install Ollama. Analysis completes before the model receives a scoped brief.
Bring your own planner
uv run r2b brief ./sample.bin \
--quick --no-save --json > brief.json
Point the harness at brief.json. No jq prerequisite; diagnostics stay on stderr.
COMPOSABLE BOUNDARIES
No framework inheritance maze. TOML bounds a run, a small protocol adds an analyzer, and each ranked region can carry its own evidence into a model call.
[analysis]
enable_ghidra = true
enable_angr = false
[extract]
enable = true
max_files = 80
max_bytes = 33554432
Pick depth and extraction limits without changing the engine.
class MyAnalyzer:
name = "my_tool"
def is_available(self) -> bool: ...
def quick_scan(self, binary, **ctx): ...
def deep_scan(self, binary, **ctx): ...
Unavailable prerequisites become explicit skips; adapters keep their native evidence.
report = analyze("./sample.bin")
region = report.regions[0]
print(region.evidence)
answer = region.ask(
"What supports this rank?",
config_path="config/local.toml",
)
The region already carries source, address, function, snippet, and why. The model receives only that scoped context.
WHERE IT SITS
A harness can own this loop natively. r2b is the reusable part when several harnesses or analysts should share the same sniffing, tool probes, bounded run policy, evidence shape, and next-command contract.
ELF · PE · Mach-O · firmware · embedded containers · raw blobs
radare2 · libmagic · Capstone · Ghidra · binwalk · unblob · angr · DWARF · Frida · GEF
OpenAI · Anthropic · xAI · Kimi · GLM · Ollama · exo
FAQ
Not in the default briefing path. Dynamic stages are explicit, optional, and belong in an isolation boundary appropriate to the target.
No. Classification, evidence collection, ranking, records, and handoffs are deterministic. A model is an optional interpreter of a completed brief.
Yes—only when the host declares the tools, supplies the executor, allowlists each tool, and sets a bounded round limit. Declaring a tool alone never executes it.
No. The CLI and Python API already provide stable, typed harness boundaries. Add a thin MCP adapter only when your host benefits from discovery or remote invocation; it is not a core dependency.
That is the point. Adapters share a small protocol, providers declare capabilities separately from transports, and JSON schemas make the handoff surface inspectable.
Treat the first pass as inventory. r2b now marks large/high-cardinality code targets broad and unscored, suppresses automatic next argv, and asks for a dependency, crash address, export, subsystem, or version diff. JIT-generated code requires runtime capture.
START SMALL
Start with a deterministic brief. Add deeper tools or a model only when the evidence earns the cost.