9.7 KiB
Using r2xsql with radare2 and an agent
The plugin supplies SQL over a live radare2 core. The skills teach an agent how to choose tables, query and annotate that core, and manage its lifetime. The user can ask in natural language in either tandem or headless mode.
Installation
Install radare2, then install a matching r2xsql plugin:
r2pm -U
r2pm -bi r2xsql
Release v0.0.1 targets radare2 6.2.6. For a source build on Linux/macOS,
use r2pm -ci r2xsql or make user-install. Windows source builds use
cmake --build build --config Release --target user-install after
configuring CMake.
Installed plugins load automatically on normal startup. make user-install
does the same for a local build. Manual installation uses the directory
reported by r2 -NN -H R2_USER_PLUGINS. Restart a host after installation.
The plugin's radare2 ABI must match the engine loading it; iaito can use a
different engine from the standalone r2 on PATH. Binary installation with
r2pm -bi r2xsql requires an available asset matching the host and platform.
See building and packaging.
For a build that is not installed:
r2 -i ./core_r2xsql.so -A ./target
Use the actual .so, .dylib or .dll file. -i also accepts native r2
command scripts. Inside an existing console, load a plugin with
L ./core_r2xsql.so; L r2xsql is not a package-name loader.
-N skips startup scripts while retaining plugins; -NN disables automatic
plugin loading too. For a deterministic local-build check, use -NN -i FILE.
Verify r2xsql in Lcj, then try sql? and sqlj SELECT 1.
Install the r2xsql skills
in the coding agent's harness. Skills and the binary plugin are separate
installations. The harness selects the r2xsql entry skill and routes to
connection and domain instructions; explicit skill selection can be used
when the harness does not select it automatically.
Tandem mode
Tandem shares an existing analysis in an interactive r2 CLI, its visual/panels
text interface, or iaito. For a fresh
binary run r2 -A ./target, or open it in iaito and let analysis finish.
For a saved project run r2 -p PROJECT without -A.
At the r2 prompt or iaito's View > Console, verify the plugin with Lcj,
then start background HTTP:
e http.bind=127.0.0.1
e http.sandbox=false
e http.port=9090
=h&
Choose a free port. http.port selects it; =h& starts the background
server and leaves the console/UI available. It is an alias for & =h,
using r2's background-task facility. =h serves in the foreground instead.
Start HTTP at the prompt before entering the text interface.
Wait for open http://127.0.0.1:9090/, then tell the agent:
Use r2xsql at http://127.0.0.1:9090 on the binary already open here. Show me the largest functions.
The agent checks the host, plugin and binary identity, authors SQL and reuses
the endpoint on later questions. It should not open another UI or rerun
analysis merely to attach. Renames and comments affect the human's analysis
too. Take turns with the agent when running analysis-changing native commands.
Use =h- at the interactive console to stop background HTTP while keeping
the human's host open.
For several hosts, select a distinct port per process and provide a mapping: application = http://127.0.0.1:9090, library = http://127.0.0.1:9091. The agent verifies each target and labels findings by source. SQL executes inside one host; comparisons across hosts combine results in the client.
Fully headless
With the plugin installed, an agent can start the native r2 process without a human console or GUI. For a fresh binary, POSIX shell:
r2 -N -q -A -e scr.html=false -e http.bind=127.0.0.1 \
-e http.sandbox=false -e http.port=9090 -c '=h' ./target \
>r2.stdout.log 2>r2.stderr.log &
r2_pid=$!
Windows PowerShell:
$engine = Get-Command r2,radare2 -CommandType Application -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $engine) { throw 'Install radare2 or supply its executable location.' }
$target = (Resolve-Path -LiteralPath '.\target.exe').Path
$port = 9090 # choose a free port
$logDir = Join-Path $env:TEMP ('r2-session-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $logDir | Out-Null
$arguments = '-N -q -A -e scr.html=false -e http.bind=127.0.0.1 -e http.sandbox=false -e http.port={0} -c "=h" "{1}"' -f $port,$target
$process = Start-Process -FilePath $engine.Source -ArgumentList $arguments -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logDir 'stdout.log') `
-RedirectStandardError (Join-Path $logDir 'stderr.log') -PassThru
$endpoint = 'http://127.0.0.1:{0}/cmd/' -f $port
These launch radare2 directly. No Python session/query wrapper or build
orchestrator is needed. -A runs analysis once; foreground =h keeps the
background OS process alive. Queries use that same core until shutdown.
For an uninstalled plugin, substitute -NN -i PLUGIN_FILE for -N.
Wait for the server banner on stderr, then verify the endpoint with curl. A bind failure is not readiness. Retain the PID, endpoint, binary/project and log locations; subsequent prompts reuse this session.
HTTP queries
For radare2 6.2.4, POST the complete r2 command to /cmd/:
curl -d '?V' http://127.0.0.1:9090/cmd/
curl -d 'Lcj' http://127.0.0.1:9090/cmd/
curl -d '"sqlj SELECT * FROM binary"' http://127.0.0.1:9090/cmd/
curl -d '"sqlj SELECT name,size FROM funcs WHERE size > 100 LIMIT 5"' \
http://127.0.0.1:9090/cmd/
In PowerShell use curl.exe to avoid the shell alias:
$command = '"sqlj SELECT name,size FROM funcs ORDER BY size DESC LIMIT 10"'
curl.exe --silent --show-error --max-time 30 -d $command $endpoint
The body is an r2 command. The double quotes are part of that body and
protect SQL operators such as > and | from r2's parser. Use sqlj
for the JSON envelope; sql,:json is a table format with different NULL and
empty-string behavior. Inspect top-level and per-statement success/error
fields; HTTP 200 alone does not establish success. See SQL access.
Native commands such as pdf and Ps travel over the same endpoint.
The command-prefix-plus-body form is implemented in development radare2 and is planned for 6.2.6:
curl -d 'SELECT name,size FROM funcs ORDER BY size DESC LIMIT 5' \
http://127.0.0.1:9090/cmd/sqlj
Here the URL selects sqlj and the body contains only SQL. This is a
version-dependent alternative, not a 6.2.4 example. Use the full-command
form until the host is known to support it. An agent should verify the JSON
response to a harmless query before choosing this form for mutations.
The combined command still passes through r2's parser. For SQL containing
operators such as > or |, keep the full double-quoted command form above.
Saving and reopening
Analysis lives in memory until saved. Ask the agent to save the project beside
the binary, or select another location. The save operation is native radare2;
there is no SQL save_database() in r2xsql.
curl -d 'Ps /absolute/path/target.exe.r2' http://127.0.0.1:9090/cmd/
PowerShell, using the absolute target path from the launch:
$project = ($target + '.r2').Replace('\','/')
curl.exe --silent --show-error --max-time 30 -d ('Ps ' + $project) $endpoint
Get-Item -LiteralPath $project
Use forward slashes in native r2 commands on Windows. Verify the actual saved
artifact, host log and requested annotations; a refused save can return an
empty HTTP 200. Keep the original binary and any companion .r2.d directory.
Saving analysis does not patch the executable or shut down the host.
Reopen the saved project with -p, without a separate target or reanalysis:
r2 -N -q -p /absolute/path/target.exe.r2 \
-e scr.html=false -e http.bind=127.0.0.1 \
-e http.sandbox=false -e http.port=9090 -c '=h' \
>r2.stdout.log 2>r2.stderr.log &
The project stores the binary path. On Windows replace -A and the target
in the launch arguments with -p "PROJECT"; keep the same -e options
and -c "=h". Verify binary identity and saved annotations after reopening,
rather than checking only function counts. Preserve the checkpoint and logs
if verification fails.
Projects and command scripts
Native projects support binary serialization and script compatibility; do not
infer their format from the filename alone. Ps PROJECT and -p PROJECT
are the normal save/reopen interface. A bare project name uses dir.projects;
an absolute filename selects a sidecar. Pl lists named projects, not every
sidecar elsewhere.
Where available, prj? documents the binary-container operations, including
prj save FILE and prj open FILE. Select the matching native loader for
an existing container; a standalone container is not interchangeable with a
named -p project on every host. Explicit command-script exports use PS
or PS* and can be interpreted with -i SCRIPT; they are not a reason to
describe all projects as scripts. Check P? and prj? on the chosen host.
Shutdown and session ownership
On a save-and-shutdown request, save and verify first, then stop an agent-owned foreground server:
curl 'http://127.0.0.1:9090/cmd/=h--'
=h-- is intercepted in the GET path; putting it in a POST body does not
stop that accept loop. With the headless -q launch and no later commands,
r2 exits when serving ends. Verify the intended PID and listener stopped.
Do not close the user's tandem UI; stop its background server at the console
only when requested. Saving alone leaves the host available for follow-ups.
The server provides local command access, including writes when
http.sandbox=false. Bind to 127.0.0.1 and do not expose it as a public
service.