When to use
Pick this skill when the user wants to incrementally rebuild
meaning in a binary: turn sub_401000 into aes_init, write what
each function does, and persist that across sessions so the next pass
starts from a richer baseline.
For one-off analysis or open-ended triage, use the analysis skill.
For the mechanics of writing comments/flags, use annotations.
Required: write + project
Persistence is required. Always:
r2xsql -w --project NAME -s <binary> -i
so every comment/flag you add gets saved to the project on exit.
The re-source loop
- Anchor — pick an unambiguous data leaf:
- a unique string (
'license check failed') - a unique import (
CryptDecryptif it appears once) - a known constant (PE magic, RC4 sbox, …)
- a unique string (
- Walk back —
xrefsto find the first caller, then the caller's caller, … up to a function that's identifiable from its own surface (a public DLL export, an exception handler, a top-level dispatcher). - Name and comment — every named anchor becomes a
UPDATE flags SET name = …andUPDATE comments SET text = …. - Repeat with the next un-named anchor.
Walked example
-- (1) the anchor: a unique error string
SELECT addr FROM strings WHERE content = 'license check failed';
-- → addr = 0x412ab0
-- (2) one hop back: who references the string? from_func is the owning
-- function's ADDRESS, which is exactly what step (3) renames.
SELECT from_addr, from_func FROM xrefs WHERE to_addr = 0x412ab0;
-- → from_func = 0x401a10 (sub_401a10), from_addr = 0x401b34
-- (3) annotate that function
UPDATE flags SET name = 'license_fail_log' WHERE addr = 0x401a10;
UPDATE comments SET text = 'logs license-check failure' WHERE addr = 0x401a10;
UPDATE comments SET text = 'msg = "license check failed"' WHERE addr = 0x401b34;
-- (4) two hops back: who calls license_fail_log?
SELECT cf.name FROM xrefs x
JOIN funcs f ON f.addr = x.to_addr
JOIN funcs cf ON cf.addr = x.from_func
WHERE f.name = 'license_fail_log' AND x.type = 'CALL';
-- → license_check_v2
-- (5) annotate, repeat
UPDATE comments SET text = 'top-level license check; calls license_fail_log on failure'
WHERE addr = (SELECT addr FROM funcs WHERE name = 'license_check_v2');
Multi-pass workflow
Each pass over the binary makes the next pass cheaper:
- Pass 1: name 20-50 leaf functions from strings/imports.
- Pass 2: use the named functions as your new anchors — every
xrefswalk lands on a meaningful name instead ofsub_*. - Pass 3: start naming dispatchers and state machines based on what their callees do.
Persist with Ps (the backend does this automatically when
-w --project NAME was passed). Reopen with --project NAME to
skip re-analysis and keep building.
Type reconstruction
types and types_members surface r2's type database — useful when
the binary uses well-known struct shapes (Windows headers, Linux
kernel structs). Apply types to memory with tl <TYPENAME> @ <addr>
through the r2js skill (a raw r2 command, not a SQL write).
Caveats
- Renames stick only if you opened with
-w; otherwise they vanish on exit and the next pass starts fromsub_*again. - The
xrefstable only sees what the analysis found. Runaaaa(with -AA) for deeper static analysis before serious re-sourcing. - Anchors degrade — a string like
'error'is too common to follow. Pick unique anchors first; the rare ones give straight chains.