bn is an agent-friendly Binary Ninja CLI.
It splits into two parts:
- a normal Python CLI that you can run from your shell
- a Binary Ninja GUI plugin that owns all Binary Ninja API access
The CLI talks to the GUI plugin over a local Unix socket. This avoids the headless binaryninja limitations in this environment and makes it easy to spill large results to files instead of truncating them in a tool harness.
Install the CLI on your PATH:
uv tool install -e .Install the Binary Ninja companion plugin:
bn plugin installThat links plugin/bn_agent_bridge into your Binary Ninja plugins directory.
If the plugin code changes, reload Binary Ninja Python plugins or restart Binary Ninja.
- The plugin creates one fixed bridge socket and one fixed registry file.
- The CLI discovers that bridge, connects to it, and forwards commands.
- Read commands return structured data.
- Large outputs can spill to artifacts with
--out, and some large stdout responses auto-spill to a temp directory. - Mutations support
--previewso you can inspect the effect before making a permanent change.
This version assumes one Binary Ninja/plugin instance per machine, which keeps discovery simple.
Open a binary or .bndb in Binary Ninja, then run:
bn doctor
bn target list
bn function list
bn decompile sub_401000If exactly one BinaryView is open, target-specific commands can omit --target entirely. If multiple targets are open, pass --target <selector> from bn target list.
Use bn target list to see available targets:
bn target listTargets can be selected with:
active- the
selectorfield frombn target list - the full
target_id - the BinaryView basename
- the full filename
- the view id
In normal use, prefer the selector field. For a single open database, this is usually just the .bndb basename:
bn decompile update_player_movement_flags --target SnailMail_unwrapped.exe.bndbEvery command supports:
--format json--format text--format md--format ndjson--out <path>
Examples:
bn function list --format ndjson
bn decompile sample_track_floor_height_at_position --out /tmp/floor.jsonIf --out is set, the command writes the rendered result to that path and prints a compact JSON envelope with the artifact path, size, hash, and summary.
Common read-only commands:
bn target list
bn target info
bn function list
bn function search attachment
bn function info end_track_attachment_follow_state
bn decompile end_track_attachment_follow_state
bn il end_track_attachment_follow_state
bn disasm end_track_attachment_follow_state
bn xrefs end_track_attachment_follow_state
bn xrefs field TrackRowCell.tile_type
bn comment get --address 0x401000
bn types --query Player
bn types show Player --format text
bn struct show Player --format text
bn strings --query follow
bn imports
bn dataOne local tooling caveat remains:
bn decompiledoes not always rewrite post-hoc struct-growth sites away from raw__offset(...)expressions, even after a manual analysis refresh.- For now,
bn types show Player,bn types show Game, andbn types show PathTemplateare the authoritative typed layouts.
Export a reusable function bundle:
bn bundle function end_track_attachment_follow_state --out /tmp/end_track_attachment_follow_state.jsonRun Python inside the Binary Ninja process:
bn py exec --code "print(hex(bv.entry_point)); result = {'functions': len(list(bv.functions))}"
bn py exec --stdin <<'PY'
print(hex(bv.entry_point))
result = {"functions": len(list(bv.functions))}
PYThe py exec environment includes:
bnbinaryninjabvresult
Stdout and result are both returned.
Mutations can omit --target when exactly one BinaryView is open. If multiple targets are open, pass an explicit selector.
Examples:
bn symbol rename sub_401000 player_update --preview
bn comment set --address 0x401000 "interesting branch" --preview
bn proto set sub_401000 "int __cdecl player_update(Player* self)" --preview
bn local rename sub_401000 var_14 speed --preview
bn local retype sub_401000 var_14 float --preview
bn types declare "typedef struct Player { int hp; } Player;" --preview
bn struct field set Player 0x308 movement_flag_selector uint32_t --preview
bn patch bytes 0x401000 "90 90" --previewPreview mode applies the change, refreshes analysis, captures affected decompile diffs, and then reverts the mutation.
For declaration and struct mutations, preview results also include affected_types with before/after layouts and a unified diff. If a field edit is already identical, the result is marked with changed: false and a No effective change detected message.
For the first few changed functions, affected_functions also includes short before_excerpt and after_excerpt snippets around the first changed HLIL lines.
bn batch apply accepts a JSON manifest:
{
"target": "SnailMail_unwrapped.exe.bndb",
"preview": true,
"ops": [
{
"op": "rename_symbol",
"kind": "function",
"identifier": "sub_401000",
"new_name": "player_update"
},
{
"op": "set_prototype",
"identifier": "player_update",
"prototype": "int __cdecl player_update(Player* self)"
}
]
}Apply it with:
bn batch apply manifest.jsonCheck bridge state:
bn doctorIf bn target list is empty:
- make sure Binary Ninja is open
- make sure a binary or
.bndbis open - make sure the plugin is installed with
bn plugin install - reload Binary Ninja plugins or restart Binary Ninja after plugin changes
If active is ambiguous, pass --target <selector> from bn target list.
Run tests with:
uv run pytestRun the CLI from the repo without installing it globally:
uv run bn --help