binary-ninja-reverse · git:20260924.a8d2b81 · 2026-09-24 · sha256 be37a30c3c049977
binary-ninja-reverse git:20260924.a8d2b81A
Immutable. This exact content never changes and is served at /api/v1/blob/be37a30c3c049977.
--- name: binary-ninja-reverse description: Reverse engineering with Binary Ninja — HLIL/MLIL/LLIL intermediate languages, cross-references, type recovery, and headless batch analysis. Use for Binja-specific decompilation, IL-level analysis, and BN API scripting. --- # Binary Ninja Reverse Engineering Binary Ninja's strength is its **multi-level intermediate language (IL)** view. Where IDA gives one pseudocode view, BN exposes LLIL, MLIL and HLIL separately — which makes it the better tool for understanding optimizer behaviour, dataflow, and what a compiler actually did to your code. ## ⚠️ Edition gate — read this first Check the edition before planning any work: ```bash "/c/Program Files/Vector35/BinaryNinja/binaryninja.exe" --version # -> "Binary Ninja 6.0.10601 free" <-- free edition ``` | Capability | Free | Personal | Commercial / Ultimate | |---|---|---|---| | GUI analysis | yes | yes | yes | | **Python API** | no | yes | yes | | **Headless (`-p`) batch** | limited | yes | yes | | HLIL / decompiler | yes | yes | yes | | Debugger plugins | yes | yes | yes | | Automation / scripting | **no** | yes | yes | **On a Free licence there is no `binaryninja` Python module and no headless entry point.** Verify before writing any script that imports it: ```bash python -c "import binaryninja; print(binaryninja.core_version())" # ModuleNotFoundError: No module named 'binaryninja' <-- expected on Free find "/c/Program Files/Vector35/BinaryNinja" -maxdepth 2 -iname '*headless*' # no results <-- expected on Free ``` If both fail, this skill is **GUI-only**: do the analysis by hand in the BN window and record the result. Do not fabricate scripted output. Report the licence gap to the user so they can decide to upgrade. A known-good install layout for the paid editions: ```bash "/c/Program Files/Vector35/BinaryNinja/python/binaryninja_api" # API package "/c/Program Files/Vector35/BinaryNinja/binaryninja" # headless driver "/c/Program Files/Vector35/BinaryNinja/scripts/quick_analysis.py" # batch entry ``` ## IL levels — what each one answers Pick the level that matches the question. Going too high loses the detail; going too low drowns you in noise. | Level | Form | Use it for | |---|---|---| | **HLIL** | near-C pseudocode | fastest read of intent; algorithm recovery | | **MLIL** | typed SSA-ish IR | dataflow, where a value came from, optimiser output | | **MLIL SSA** | explicit def-use | taint-style tracing, constant propagation | | **LLIL** | register/flag level | what the machine actually executes; CET, stack layout | | **Disassembly** | raw mnemonics | architecture quirks, alignment, AVX/NEON specifics | ### HLIL for intent Read HLIL first. It inlines helpers and rebuilds loops, so a crypto routine or a length check becomes readable C-like structure. ### MLIL for provenance When you need to know *where* a value came from: 1. Select the variable in HLIL, press the IL-level hotkey to drop to MLIL. 2. Right-click the definition -> **Show all cross-references**. 3. Walk `MLIL_VAR_SSA` / `MLIL_SET_VAR_SSA` to trace the chain. This is the fast path for "is this size field attacker-controlled?" — follow the SSA chain back and see whether it bottoms out in a read from a network buffer. ### LLIL for the machine truth Use LLIL when the decompiler is lying to you (it happens with hand-written asm, obfuscated code, and packed regions). LLIL cannot hide a stack pivot or a manual `jmp` into a gadget. ## Cross-references `Ctrl+Shift+X`-equivalent — **Show cross-references** on any symbol or address. For exploit work the useful views are: - **Code xrefs** to a dangerous call (`memcpy`, `system`, `strcpy`) — shows every call site and the argument setup at each one. - **Data xrefs** to a global — reveals which code writes a config value or a function pointer table. Writable function-pointer tables plus an overflow nearby is a classic control-flow hijack. - **String xrefs** — fastest way into error paths and feature flags. ## Type recovery BN's type propagation is its second strength. When a struct is wrong: 1. Open the **Types** view, define or import the struct. 2. Apply it to the variable (`Y` in HLIL). 3. Wrong field offsets usually mean the struct is mis-sized — check `size_t` vs `int` members on 64-bit, and check whether the compiler inserted padding. Importing real headers beats guessing: ``` File -> Load Header File -> <windows.h | your SDK header> ``` Then re-run analysis (**Actions -> Reanalyze**, or `Ctrl+P` -> reanalyze) so the type library is applied to call sites. ## Common workflows **"What does this function do?"** 1. HLIL, read the structure. 2. Rename variables as you understand them (`N`). 3. Set the function's type signature so call sites become readable. 4. Reanalyze. Callers now show typed arguments. **"Where is this buffer written?"** 1. Find the allocation (`malloc` xrefs) or the stack slot in LLIL. 2. Data xrefs on the address -> every writer. 3. For each writer compare the copy length against the allocation size. That is the overflow, or the proof there isn't one. **"Is this binary packed?"** High entropy section, few cross-references, tiny import table, and an entry point that does nothing but unpack = packed. Dump at the OEP and re-analyze the dump. **"Patch it"** BN writes patches back into the file: right-click an instruction -> **Patch**. For a persistent change, patch bytes, then `File -> Save`. Prefer patching the branch condition over NOP-ing a whole check — it keeps the diff readable. ## Comparisons to IDA Use BN when: you need IL levels, SSA dataflow, or type propagation across a large binary. Use IDA when: you need the widest decompiler coverage of exotic architectures, mature idapython automation, or the largest plugin ecosystem. For headless/automated reverse engineering prefer IDA (`idat64.exe -A -S script.py`). If BN is on a paid licence, the equivalent is `binaryninja -p` with a script. ## Pitfalls - **Free edition has no API.** Confirmed above; do not attempt to script it. - **Analysis cache.** Re-running analysis after a type change does not always refresh HLIL. Close and reopen the view. - **`-p` still loads debugger plugins** on some builds. If startup hangs, use `-p` plus `--help` first to confirm the flag is honoured. - **Do not trust the decompiler on obfuscated code.** Verify at LLIL, and verify again in a debugger before writing an exploit around it. - **Licence check first, always.** Reporting a scripted result from a Free install is a fabricated result.