Files

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.