Skip to content

Latest commit

 

History

History
317 lines (253 loc) · 13.8 KB

File metadata and controls

317 lines (253 loc) · 13.8 KB

r2vsql — radare2 Virtual SQL

linux macos

Query a binary the way you query a database.

r2vsql is a radare2 core plugin that exposes the live analysis of the current session as SQL virtual tables. Functions, basic blocks, strings, cross-references, sections, imports, symbols, instructions, comments, registers and local variables all become queryable relations — no exporting, no indexing, no scripting. Just SQL, evaluated in real time against radare2's C API.

[0x00000000]> r2vsql SELECT name, hex(addr) AS addr, size, nbbs FROM funcs ORDER BY size DESC LIMIT 5
name                  addr   size nbbs
--------------------------------------
main                  0x4da0 8029 381
fcn.00007ed0          0x7ed0 402  30
sym._obstack_newchunk 0xdca0 384  15
sym._obstack_free     0xde80 136  13
entry.init0           0x6e10 60   5

The v in vsql stands for virtual: every query reads the live RCore state. Rename a function, analyze more code, patch a byte — the next query sees it immediately. Nothing is cached into a static database (the one exception is the explicit dump command, see below).

Inspiration

This plugin was vibecoded in a single prompt with these reference projects:

In the anouncement presentation r2sql was mentioned several times in the slides, but no public repo was made available, so i just spent few tokens to make it happen.

Why SQL?

SQL is the universal query language every AI agent and every reverse engineer already speaks. A single SELECT replaces dozens of lines of r2pipe glue or ad-hoc shell pipelines, and joins/aggregates/CTEs let you ask questions that are awkward to express with native commands:

-- functions that are never called (dead code candidates)
SELECT f.name, hex(f.addr) FROM funcs f
LEFT JOIN xrefs x ON x.to_ea = f.addr AND x.type = 'CALL'
WHERE x.to_ea IS NULL;

How it works

r2vsql links against the system SQLite library purely as a SQL engine. Each table is an eponymous virtual table whose rows are materialized on demand from radare2's C API (RAnalFunction, RFlag, RBin*, RAnalRef, the meta interval tree, …) — no r2pipe. Every table and scalar function reads data through the r_core C API. The only operations that go through a radare2 command are the three that have no stable C API — decompilation (decompile() via the pluggable cmd.pdc), assembly (assemble/patch_asm) and the disassembler-backed instruction search — and those use r_core_call_*, which dispatches the command without evaluating r2 command separators (;, @, |, backticks), so a SQL argument can never inject a side command. Single-key equality constraints (e.g. WHERE func_addr = 0x4da0) are pushed down so large tables only materialize the rows you ask for.

   SQL  ──▶  SQLite engine  ──▶  r2vsql virtual tables  ──▶  RCore (live)
                  ▲                                             │
                  └───────────────  results  ◀──────────────────┘

The HTTP server and threading use radare2's own native APIs (r_socket_http, r_th) — the only external dependency is SQLite, which is mandatory for SQL.

Feature comparison

The matrix below was produced by reading the source of all four projects (idasql, bnsql, ghidrasql and r2vsql), not from documentation — every tick is backed by an actual table, function, flag or build file.

Legend: ✅ supported · ◑ partial / indirect · — not present.

Data model (virtual tables)

Capability idasql bnsql ghidrasql r2vsql
Functions · basic blocks · instructions
Strings · xrefs · sections/segments
Imports · symbols/exports · entries
Comments · local variables · metadata
Registers
OO classes
Types · type members · enum values
Function signatures / prototypes
Relocations
Resolved syscalls (/as)
Calling-convention catalog (afcl)

SQL engine

Capability idasql bnsql ghidrasql r2vsql
Live virtual tables (no export/index step)
Custom scalar functions ✅ (34)
Predefined analysis views ✅ (9)
Query constraint pushdown

Write-back (mutation from SQL)

Capability idasql bnsql ghidrasql r2vsql
Rename functions / symbols
Set / clear comments
Apply / set types (parse_decls, set_var_type)
Patch bytes (patch_bytes)
Patch / search instructions (patch_asm, search_asm, assemble)

Decompiler & search

Capability idasql bnsql ghidrasql r2vsql
decompile() pseudocode function
Pluggable decompiler backend ✅ (cmd.pdc)
Decompiler AST tables (ctree/HLIL rows)
Byte / pattern search (search_bytes, search_first)
ROP gadget search (rop)
Entity name grep (entities_search)

Server

Capability idasql bnsql ghidrasql r2vsql
HTTP /query server
Background serving
Blocking / headless serving
MCP server
Optional auth token

Export, deployment & implementation

Capability idasql bnsql ghidrasql r2vsql
Whole-DB static SQL dump
MySQL-dialect output
Standalone CLI binary — (uses r2 host)
Runs inside the host tool
Language C++ C++ C++ C
Dependencies libxsql + httplib + fastmcp libxsql + httplib + fastmcp libxsql + httplib SQLite only
Build system CMake CMake CMake make + meson

What makes r2vsql distinctive

r2vsql matches the full core data model of all three originals — functions, blocks, instructions, strings, xrefs, sections, imports, symbols, comments, local variables, types, relocations and metadata are all live virtual tables — and goes further with registers, OO classes, linked-library, resolved-syscall (/as) and calling-convention catalog (afcl) tables that none of the others expose, plus ROP-gadget search (rop). It is also write-capable across the board: rename, comment, set calling convention (set_cc), parse_decls/set_var_type, and both byte- and instruction-level patching (patch_bytes, plus assemble/patch_asm/search_asm, which let you patch and hunt for assembled instructions — a capability the others, which only patch raw bytes, don't offer). For the decompiler it uses radare2's pluggable pdc mechanism, so a single e cmd.pdc=pdg (r2ghidra), =pdd (r2dec) or any other backend changes what decompile() returns — the competitors are hard-wired to their host's one decompiler.

Its standout structural advantage is the implementation itself: r2vsql is the only pure-C project and the only one whose sole third-party dependency is SQLite — its HTTP server and threading reuse radare2's own r_socket_http and r_th APIs instead of bundling cpp-httplib, fastmcp and libxsql — and the only one shipping both make and meson builds. Every command-backed helper (decompile, assemble, patch_asm, …) dispatches through r_core_call_*, which runs the target command without evaluating r2 command separators, so SQL arguments cannot inject side commands. For diffing it is one of just two projects (with idasql) to offer a whole-database static SQL dump, and the only one that emits a MySQL dialect; combined with the auto-generated indexes and views in the dump, that makes it purpose-built for offline analysis and diaphora-style binary comparison.

Where r2vsql is intentionally narrower: it exposes decompile() as a function but not the per-line/ctree AST tables (radare2 has no C API for a structured decompiler AST), it has no MCP server (neither does ghidrasql; idasql and bnsql ship one), and it runs as a radare2 plugin rather than a standalone binary (the r2 host is the CLI). And to be precise about the server: every one of these projects can serve SQL over HTTP headlessly — what is distinctive about r2vsql is that it exposes background (start) and blocking (listen) serving as two sibling commands inside a single r2 session, whereas the others split those across an in-UI/REPL async server and a standalone --http CLI.

Build & install

Requirements: radare2 (≥ 6.1) with development headers (pkg-config r_core), a C compiler, and SQLite development headers (pkg-config sqlite3; for example libsqlite3-dev on Debian/Ubuntu).

make

make                 # build core_r2vsql.<ext>
make user-install    # install into the per-user plugin dir (no root)
sudo make install    # install system-wide
make test            # run the test-suite

meson

meson setup build
meson compile -C build
meson install -C build

Verify it loaded:

r2 -qc 'Lc~r2vsql' /bin/ls

Quick start

r2 /bin/ls
[0x00000000]> aa                       # analyze (gives the tables data to read)
[0x00000000]> r2vsql SELECT count(*) FROM funcs
[0x00000000]> r2vsql SELECT * FROM strings WHERE content LIKE '%/proc/%'
[0x00000000]> r2vsql tables            # list tables and views
[0x00000000]> r2vsql schema            # print the full SQL schema

Command reference

r2vsql is the single entry command. The subcommand grammar matches the task spec: r2vsql [start|stop|status|listen|dump|help|version].

Command Description
r2vsql <sql> Run a SQL query and print the result as a table
r2vsql start [port] Start the HTTP SQL server in the background (non-blocking)
r2vsql listen [port] Start the SQL server in blocking mode (Ctrl-C to stop)
r2vsql stop Stop the background server
r2vsql status Show server status
r2vsql dump [file] [mysql] Export the whole analysis as a portable .sql script
r2vsql schema Print the SQL schema (tables and views)
r2vsql tables List available tables and views
r2vsql version Show version
r2vsql help / r2vsql? Show help

Configuration variables (set with e):

Variable Default Meaning
r2vsql.host 127.0.0.1 Bind address for the server
r2vsql.port 9000 Default server port
r2vsql.token (empty) Optional Authorization: Bearer <token> for the HTTP API

The server

Start it in the background and query it from any HTTP client or AI agent:

[0x0]> r2vsql start 9000
r2vsql HTTP server: http://127.0.0.1:9000

$ curl -s http://127.0.0.1:9000/query -d 'SELECT name, hex(addr) FROM funcs LIMIT 3'
{"success":true,"columns":["name","addr"],"rows":[["main","0x4da0"], ...],"row_count":3}

Open http://127.0.0.1:9000/ui in a browser for a zero-dependency query console — a SQL box (Ctrl/Cmd+Enter to run), one-click /status /health /tables /schema buttons, and pretty-printed JSON output, all served inline by the server.

Endpoints: GET /, /ui, /help, /status, /health, /tables, /schema, /dump[/sqlite] (the SQL export, same as r2vsql dump; default mysql), and POST /query. The server is read-only over HTTP — there is intentionally no remote shutdown; stop it from radare2 with r2vsql stop (or Ctrl-C for listen). See doc/server.md for the full API.

The dump exporter

dump is the only static path: it serializes the entire analysis into a portable SQL script (CREATE TABLE + INSERT + indexes + views) for import into sqlite or mysql. This is ideal for archiving, offline analysis and diaphora-style binary diffing.

[0x0]> r2vsql dump /tmp/a.sql           # sqlite dialect
[0x0]> r2vsql dump /tmp/a.mysql.sql mysql
$ sqlite3 a.db < /tmp/a.sql
$ sqlite3 a.db 'SELECT count(*) FROM funcs'

See doc/diffing.md for a two-binary diffing recipe.

Documentation

License

Mozilla Public License 2.0 (MPL-2.0). See LICENSE.