{"id":50508646,"url":"https://github.com/banteg/bn","last_synced_at":"2026-06-02T18:03:28.892Z","repository":{"id":343216153,"uuid":"1176249067","full_name":"banteg/bn","owner":"banteg","description":"binary ninja cli for coding agents","archived":false,"fork":false,"pushed_at":"2026-05-06T07:08:04.000Z","size":156,"stargazers_count":166,"open_issues_count":0,"forks_count":12,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-06T09:16:18.086Z","etag":null,"topics":["agents","binary-ninja","codex","reverse-engineering"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/banteg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-08T20:16:53.000Z","updated_at":"2026-05-06T07:08:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/banteg/bn","commit_stats":null,"previous_names":["banteg/bn"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/banteg/bn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banteg%2Fbn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banteg%2Fbn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banteg%2Fbn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banteg%2Fbn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/banteg","download_url":"https://codeload.github.com/banteg/bn/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banteg%2Fbn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33833277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","binary-ninja","codex","reverse-engineering"],"created_at":"2026-06-02T18:03:27.955Z","updated_at":"2026-06-02T18:03:28.886Z","avatar_url":"https://github.com/banteg.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bn\n\n`bn` is a coding agent-first CLI for Binary Ninja. It gives a shell session or tool-calling agent stable commands, structured output, and access to the same live Binary Ninja database you already have open in the GUI.\n\n## Headline Features\n\n- Query live Binary Ninja state from the shell: targets, functions, callsites, decompile text, IL, disassembly, xrefs, types, strings, imports, and reusable bundles.\n- Execute Python inside the Binary Ninja process instead of maintaining a separate headless workflow.\n- Apply mutations with `--preview`, capture decompile diffs, and verify the live post-state before reporting success.\n- Emit structured `json` or `ndjson` output, auto-spill large results to files, and return token counts so agents can budget context intelligently.\n\n## Install\n\nRecommended setup: install the CLI, the Binary Ninja companion plugin, and the bundled Codex skill.\n\nInstall the CLI on your PATH:\n\n```bash\nuv tool install -e .\n```\n\nInstall the Binary Ninja companion plugin:\n\n```bash\nbn plugin install\n```\n\nThat links [`plugin/bn_agent_bridge`](/Users/banteg/dev/banteg/bn/plugin/bn_agent_bridge) into your Binary Ninja plugins directory.\n\nInstall the bundled Codex skill:\n\n```bash\nbn skill install\n```\n\nThat symlinks [`skills/bn`](/Users/banteg/dev/banteg/bn/skills/bn) into `$CODEX_HOME/skills/bn` by default. Use `--mode copy` if you want a standalone copy instead. Restart Codex to pick up a new or renamed skill.\n\nIf the plugin code changes, reload Binary Ninja Python plugins or restart Binary Ninja.\n\n## How It Works\n\n- `bn` has two parts:\n  - a normal Python CLI that you can run from your shell or agent tool harness\n  - a Binary Ninja GUI plugin that owns all Binary Ninja API access\n- The plugin creates one fixed bridge socket and one fixed registry file.\n- The CLI discovers that bridge, connects to it, and forwards commands into the live GUI session.\n- Because the Binary Ninja side runs as a GUI plugin, it works with a personal license and does not require a commercial headless license.\n\n## Quick Start\n\nOpen a binary or `.bndb` in Binary Ninja, then run:\n\n```bash\nbn doctor\nbn target list\nbn refresh\nbn function list\nbn decompile sub_401000\n```\n\nIf exactly one BinaryView is open, target-specific commands can omit `--target` entirely. If multiple targets are open, pass `--target \u003cselector\u003e` from `bn target list`.\n\n## Target Selection\n\nUse `bn target list` to see available targets:\n\n```bash\nbn target list\n```\n\nTargets can be selected with:\n\n- the `selector` field from `bn target list`\n- the full `target_id`\n- the BinaryView basename\n- the full filename\n- the view id\n- `active` when you explicitly want the GUI-selected target\n\nIn normal use, prefer the `selector` field. For a single open database, this is usually just the `.bndb` basename:\n\n```bash\nbn decompile update_player_movement_flags --target SnailMail_unwrapped.exe.bndb\n```\n\nOmitting `--target` only works when exactly one target is open. If multiple targets are open, the CLI rejects the command instead of silently falling back to `active`.\n\n## Output Behavior\n\nEvery command supports:\n\n- `--format json`\n- `--format text`\n- `--format ndjson`\n- `--out \u003cpath\u003e`\n\nInteractive read commands default to `text`. Mutation, setup, and export commands default to `json`.\nAdd `--format json` when you need stable fields for automation or piping into structured tooling.\n\nExamples:\n\n```bash\nbn function list --format ndjson\nbn function list --min-address 0x401000 --max-address 0x40ffff\nbn function search --regex 'attach|detach'\nbn decompile sample_track_floor_height_at_position --out /tmp/floor.json\n```\n\nIf `--out` is set, the command writes the rendered result to that path and prints a compact JSON envelope with the artifact path, byte size, token count, tokenizer, hash, and summary. Agents can use that envelope to decide whether to read the full artifact, keep a summary, or defer loading it into context.\n\nThe only exception is `bn bundle function`, which writes the bundle artifact from inside the bridge and prints the envelope back to the CLI.\n\n`bn function list` and `bn function search` return the full matching set for the selected target or address range. Large results auto-spill to an artifact instead of forcing manual pagination. Spill is token-based and currently triggers above 10,000 tokens. When that happens, stdout stays empty and stderr carries the spill metadata as plain text, including the artifact path and size counts.\n\n## Extraction Commands\n\nCommon read-only commands:\n\n```bash\nbn target list\nbn target info\n\nbn function list\nbn function list --min-address 0x401000 --max-address 0x40ffff\nbn function search attachment\nbn function search --regex 'attach|detach|follow'\nbn function info end_track_attachment_follow_state\nbn callsites crt_rand --within bonus_pick_random_type\nbn callsites crt_rand --within-file /tmp/rng-functions.txt --format ndjson\nbn proto get end_track_attachment_follow_state\nbn local list end_track_attachment_follow_state\nbn refresh\n\nbn decompile end_track_attachment_follow_state\nbn il end_track_attachment_follow_state\nbn disasm end_track_attachment_follow_state\nbn xrefs end_track_attachment_follow_state\nbn xrefs field TrackRowCell.tile_type\nbn comment get --address 0x401000\n\nbn types --query Player\nbn types show Player\nbn struct show Player\nbn types declare --file /path/to/win32_min.h --preview\nbn strings --query follow\nbn imports\n```\n\n`bn function search` stays case-insensitive substring matching by default. Add `--regex` when you need regular expressions. `bn function list` and `bn function search` both accept `--min-address` and `--max-address` to filter by function start address.\n\n`bn callsites` is the direct-call lane for exact return-address recovery. It reports both the native `call_addr` and the post-call `caller_static`, where `caller_static = call_addr + instruction_length`. Scope it with `--within \u003cfunction\u003e` or `--within-file \u003cpath\u003e`; the file format is one function identifier per non-empty line, with `#` comments ignored.\n\nEach callsite row also includes:\n\n- `call_index`: zero-based ordinal for matching callsites in the containing function, ordered by `call_addr`\n- `within_query`: the original unresolved scope token from `--within` or `--within-file`\n- `hlil_statement`: the smallest recoverable HLIL expression or statement containing the call, or `null` when Binary Ninja only exposes a coarse enclosing region\n- `pre_branch_condition`: the nearest enclosing pre-call HLIL condition when it can be recovered confidently, otherwise `null`\n\n`hlil_statement` is intentionally local-or-null. If the best available HLIL mapping expands to a broad whole-function or multi-statement blob, `bn callsites` suppresses it instead of returning noisy context.\n\n## Bundles And Python\n\n`bn decompile` is the HLIL-text convenience lane. It is useful for quick function reading, but typed layouts remain authoritative in `bn types show` and `bn struct show`.\n\nExport a reusable function bundle:\n\n```bash\nbn bundle function end_track_attachment_follow_state --out /tmp/end_track_attachment_follow_state.json\n```\n\nRun Python inside the Binary Ninja process for one-off inspection and BN-native scripting:\n\n```bash\nbn py exec --code \"print(hex(bv.entry_point)); result = {'functions': len(list(bv.functions))}\"\n\nbn py exec --stdin \u003c\u003c'PY'\nprint(hex(bv.entry_point))\nresult = {\"functions\": len(list(bv.functions))}\nPY\n```\n\nUse `--stdin` or `--script` for multiline Python snippets. Use `--code` for true one-liners only.\n\n```bash\nbn py exec --stdin \u003c\u003c'PY'\nout = []\nfor f in bv.functions:\n    if 0x416000 \u003c= f.start \u003c 0x41C000:\n        out.append((f.start, f.symbol.short_name))\nout.sort()\nprint(\"\\n\".join(f\"{addr:#x} {name}\" for addr, name in out))\nPY\n```\n\nUse a quoted heredoc for multiline Python snippets.\n\nWhen you need counts from BN iterators such as `f.hlil.instructions`, materialize them explicitly with `list(...)` or consume them with `sum(1 for ...)` instead of assuming sequence semantics.\n\nThe `py exec` environment includes:\n\n- `bn`\n- `binaryninja`\n- `bv`\n- `result`\n\nStdout and `result` are both returned. If `result` is not JSON-serializable, `bn` returns `repr(result)` and includes a warning instead of silently stringifying the whole response.\n\n## Mutation Commands\n\nMutations follow the same target-selection rules as other target-specific commands.\n\nExamples:\n\n```bash\nbn symbol rename sub_401000 player_update --preview\nbn comment set --address 0x401000 \"interesting branch\" --preview\nbn comment get --address 0x401000\nbn proto get sub_401000\nbn proto set sub_401000 \"int __cdecl player_update(Player* self)\" --preview\nbn local list sub_401000\nbn local rename sub_401000 0x401000:local:StackVariableSourceType:-20:2:12345 speed --preview\nbn local retype sub_401000 0x401000:local:StackVariableSourceType:-20:2:12345 float --preview\nbn types declare \"typedef struct Player { int hp; } Player;\" --preview\nbn struct field set Player 0x308 movement_flag_selector uint32_t --preview\n```\n\nPreview mode applies the change, refreshes analysis, captures affected decompile diffs, and then reverts the mutation.\n\nNon-preview writes only report success after reading the live BN session back and verifying that the requested post-state actually landed. If verification fails, the CLI returns a nonzero exit code and reverts the whole mutation or batch.\n\nAfter any live type or prototype mutation, do an explicit readback:\n\n```bash\nbn proto get sub_401000\nbn struct show Player\nbn types show Player\nbn decompile sub_401000\n```\n\nFor 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.\n\nFor the first few changed functions, `affected_functions` also includes short `before_excerpt` and `after_excerpt` snippets around the first changed HLIL lines.\n\nMutation results now distinguish:\n\n- `verified`\n- `noop`\n- `unsupported`\n- `verification_failed`\n\nWhen verification fails, JSON output also includes `requested` and `observed` state for the failed op.\n\n`bn types declare` now uses Binary Ninja's source parser when available. When you pass `--file`, the CLI also forwards the source path so relative includes resolve the same way they would during header import in the GUI.\n\nIf a declaration only parses functions or extern variables and introduces no named types to persist, `types declare` returns a verified no-op instead of failing with `No named types found in declaration`.\n\n`bn local list` and `bn function info` return stable `local_id` values for parameters and locals. Prefer those IDs for `bn local rename`, `bn local retype`, and batch manifests; legacy name-based targeting still works for compatibility.\n\n## Batch Manifests\n\n`bn batch apply` accepts a JSON manifest:\n\n```json\n{\n  \"target\": \"SnailMail_unwrapped.exe.bndb\",\n  \"preview\": true,\n  \"ops\": [\n    {\n      \"op\": \"rename_symbol\",\n      \"kind\": \"function\",\n      \"identifier\": \"sub_401000\",\n      \"new_name\": \"player_update\"\n    },\n    {\n      \"op\": \"set_prototype\",\n      \"identifier\": \"player_update\",\n      \"prototype\": \"int __cdecl player_update(Player* self)\"\n    }\n  ]\n}\n```\n\nApply it with:\n\n```bash\nbn batch apply manifest.json\n```\n\nBatch apply verifies the live session by default. If any op fails to apply or fails post-state verification, the entire batch is reverted.\n\n## Troubleshooting\n\nCheck bridge state:\n\n```bash\nbn doctor\n```\n\nIf `bn target list` is empty:\n\n- make sure Binary Ninja is open\n- make sure a binary or `.bndb` is open\n- make sure the plugin is installed with `bn plugin install`\n- reload Binary Ninja plugins or restart Binary Ninja after plugin changes\n\nIf `bn doctor` sees a bridge registry but reports `Operation not permitted` under Codex,\nthe Codex sandbox is blocking the Unix socket that connects to the live Binary Ninja GUI\nprocess. Let Codex run `bn` outside the sandbox by adding this rule to\n`~/.codex/rules/default.rules`:\n\n```text\nprefix_rule(pattern=[\"bn\"], decision=\"allow\")\n```\n\nRestart Codex or reload rules after editing the file. This is only needed for Codex\nsandboxed runs; normal shells can use `bn` without that rule.\n\nIf multiple targets are open, omitted `--target` is rejected. Pass `--target \u003cselector\u003e` from `bn target list`, or use `--target active` only when you intentionally want the GUI-selected target.\n\nIf decompile text still looks stale after a type change, run:\n\n```bash\nbn refresh\n```\n\nThat forces an analysis refresh, but it still may not fully eliminate Binary Ninja's stale `__offset(...)` presentation in every case.\n\n## Development\n\nRun tests with:\n\n```bash\nuv run pytest\n```\n\nRun the CLI from the repo without installing it globally:\n\n```bash\nuv run bn --help\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbanteg%2Fbn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbanteg%2Fbn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbanteg%2Fbn/lists"}