| name | annotations |
| description | Edit radare2 annotations — comments, flag/function names, local variables, bookmarks — via r2xsql. Use when asked to add comments, rename functions/flags, rename or retype locals, or persist analysis state. |
| allowed-tools | ["Bash","Read"] |
When to use
Pick this skill any time the task is a mutation: adding/editing/
removing a comment, renaming a function, deleting a flag, persisting
session state. For read-only annotation lookups, use data or
disassembly.
Required: write mode + project
Mutations only land in r2's session memory. To persist them
across reopens you need both:
-w (or --write) when opening the session.
--project NAME so r2xsql can call Ps NAME on shutdown.
r2xsql -w --project demo -s ./malware.exe -q "
UPDATE comments SET text = 'crypto init' WHERE addr = 0x401000;
UPDATE flags SET name = 'aes_init' WHERE addr = 0x401000;
"
r2xsql --project demo -s ./malware.exe -q "
SELECT text FROM comments WHERE addr = 0x401000;
"
Without --project, mutations are still visible in the current
session but vanish on exit.
Writable tables
| table | INSERT → r2 cmd | UPDATE → r2 cmd | DELETE → r2 cmd |
|---|
funcs | af @ <addr> (+afn) | afn <name> @ <addr> (rename) / afs <sig> @ <addr> (set prototype) / afc <cc> @ <addr> (set calling convention) | af- <addr> |
locals | — | afvn <new> <old> @ <fn> (rename) / afvt <name> <type> @ <fn> (retype) | — |
comments | CCu base64:<b64> @ <addr> | CCu base64:<b64> @ <addr> | CC- @ <addr> |
flags | f <name> @ <addr> | fr <old> <new> (rename, any flagspace) | f- @ <addr> |
bookmarks | f <name> @ <addr> | fr <old> <new> | f- @ <addr> |
io_maps | om $d <vaddr> <size> <paddr> <perm> [name] | — | om-<map_id> |
types | td "<kind> <name> {};" (empty struct/union/enum shell only; use r2xsql_type_define for a full declaration) | — | t- <name> |
projects | — (use r2xsql_project_save) | — | P- <name> |
- Rename functions:
UPDATE funcs SET name='…' WHERE addr=… (issues afn).
flags also renames (UPDATE flags SET name='…' WHERE addr=…), always via
fr <old> <new> — radare2's generic in-place rename, regardless of which
flagspace the flag lives in. (This does NOT also drive afn: that command
only renames a flag already parked in the functions flagspace, and
otherwise creates a second, duplicate flag instead — to also rename a
function's own analysis-tracked name, use the funcs table.)
- Set a function prototype:
UPDATE funcs SET prototype='int parse(hdr_t *h, int len)' WHERE addr=… applies a C signature via afs (set-signature). A trailing ; is
stripped (r2's command separator); an empty/NULL prototype is rejected (r2 has no
clear-signature primitive). The read comes from aflj's signature field.
- Set a function's calling convention:
UPDATE funcs SET calltype='ms' WHERE addr=… issues afc <cc> (set-calling-convention). cc must be one of
the names SELECT name FROM calling_conventions reports for this session's
(arch,bits) — checked before the command runs, because afc itself gives no
usable signal on a rejected name: its textual output is empty on both a
successful set and an unknown convention. The read comes from aflj's
calltype field; the write is confirmed by reading afij back afterward.
- Every
funcs write (rename, set-signature, set-calltype, undefine, define) is
address-scoped, so
on the in-process (libr) backend each one also uses the same structural
command-dispatch path comments writes use — never evaluating r2's own command
separators at all, as a second, independent layer beneath the name/signature
validation above. The pipe backend is unaffected: it always uses the plain command
form.
flags' INSERT (f) and DELETE (f-) use the same structural dispatch (address-
scoped, like funcs/comments). Its rename does not issue fr at all on the
in-process (r2xsql-full) flavor — it calls radare2's rename function directly
(no address needed for that call either), measured substantially faster on a
bulk-rename workload with identical results. The pipe-only flavor still issues
, seeked to the flag's own address (a harmless anchor never consults).
Persistence functions
SELECT r2xsql_project_save('triage1');
SELECT name FROM projects;
SELECT r2xsql_project_open('triage1');
SELECT r2xsql_type_define('struct hdr { int magic; int size; }');
Common operations
UPDATE funcs SET name = 'aes_decrypt' WHERE addr = 0x401000;
UPDATE locals SET name = 'key_len' WHERE func_addr = 0x401000 AND name = 'var_20h';
UPDATE locals SET type = 'int' WHERE func_addr = 0x401000 AND name = 'key_len';
INSERT INTO comments(addr, text) VALUES (0x401000, 'crypto init');
UPDATE comments SET text = 'crypto init v2' WHERE addr = 0x401000;
DELETE FROM comments WHERE addr = 0x401000;
INSERT INTO flags(addr, name) VALUES (0x401000, 'aes_sbox');
DELETE flags addr ;
bookmarks (addr, name) (, );
bookmarks name addr ;
bookmarks addr ;
Bulk operations
UPDATE / DELETE happily take any SQL predicate, so bulk operations
just work:
UPDATE comments SET text = 'TODO: review init logic'
WHERE addr IN (SELECT addr FROM funcs WHERE name LIKE '%init%');
DELETE FROM comments
WHERE addr IN (
SELECT addr FROM funcs
WHERE addr BETWEEN 0x401000 AND 0x402000
);
Caveats
comments, flags, and bookmarks support INSERT. Use INSERT
when adding a new address/name pair; UPDATE only affects rows that
already match the predicate.
- Changes are sequenced — r2xsql issues one r2 command per affected
row. For thousands of rows this is slow; chunk the predicate.