diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 073e3bc2f3..80c5f63572 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -14699,13 +14699,16 @@ namespace BinaryNinja {
ExprId Flag(uint32_t flag, const ILSourceLocation& loc = ILSourceLocation());
ExprId FlagSSA(const SSAFlag& flag, const ILSourceLocation& loc = ILSourceLocation());
- /*! Sets the flag with index \c flag and size \c size to the constant integer value \c bit
+ /*! Reads the flag with index \c flag and positions it at bit \c bitIndex of a
+ \c size byte integer: the result is 1 << bitIndex if the flag is set
+ and \c 0 otherwise. Used to materialize flags into status registers
+ (e.g. x86 \c lahf , PowerPC \c mfcr ).
- \param size The size of the flag
+ \param size The size of the result in bytes
\param flag Flag index
- \param bitIndex Bit of the flag to set
+ \param bitIndex Bit position the flag value is shifted to
\param loc Optional IL Location this expression was added from.
- \return A constant expression of given value and size FLAG.reg = bit
+ \return The expression bool_to_int.size(FLAG.flag) << bitIndex
*/
ExprId FlagBit(size_t size, uint32_t flag, size_t bitIndex, const ILSourceLocation& loc = ILSourceLocation());
ExprId FlagBitSSA(
diff --git a/docs/guide/emulator.md b/docs/guide/emulator.md
new file mode 100644
index 0000000000..ed2a4964b4
--- /dev/null
+++ b/docs/guide/emulator.md
@@ -0,0 +1,353 @@
+# BNIL Emulator — Python API Guide
+
+The emulator plugin executes Binary Ninja's Low Level IL (LLIL) with full register,
+flag, and memory state. It is aimed at focused, snippet-level tasks — decrypting
+strings, resolving API hashes, evaluating a slice of a function — rather than
+full-program or whole-system emulation. See the
+[plugin README](https://github.com/Vector35/binaryninja-api/blob/dev/plugins/emulator/README.md)
+for the scope, accuracy notes, and the list of supported LLIL instructions.
+
+> **Experimental.** The API and behavior may change.
+
+Today the emulator works on **LLIL**; we intend to expand it to **MLIL** and **HLIL**
+as well.
+
+## Contents
+
+- [Getting the class](#getting-the-class)
+- [Creating an emulator](#creating-an-emulator)
+- [Setting the entry point](#setting-the-entry-point)
+- [Running and stepping](#running-and-stepping)
+- [Stop reasons](#stop-reasons)
+- [Registers, flags, and temporaries](#registers-flags-and-temporaries)
+- [Memory](#memory)
+- [Function arguments](#function-arguments)
+- [Breakpoints](#breakpoints)
+- [Hooks](#hooks)
+- [Built-in libc stubs](#built-in-libc-stubs)
+- [Call stack](#call-stack)
+- [State serialization](#state-serialization)
+- [Full example: cross-function emulation](#full-example-cross-function-emulation)
+- [API reference](#api-reference)
+
+## Getting the class
+
+The emulator is a normal importable class — there is no auto-injected console
+variable. In the Python console or a headless script:
+
+```python
+from binaryninja.emulator import LLILEmulator, ILEmulatorStopReason
+```
+
+In the interactive console, `bv` (current view) and `here` (current address) are
+already available as built-in magic variables, so the two lines below are all you
+need to get going:
+
+```python
+emu = LLILEmulator(bv)
+emu.set_entry_point(here)
+```
+
+If you use the emulator often and want `LLILEmulator` available without importing
+each session, add the import to `~/.binaryninja/startup.py`.
+
+## Creating an emulator
+
+`LLILEmulator` can be constructed three ways:
+
+```python
+# 1. For a whole view — resolve addresses to functions on demand (most common).
+emu = LLILEmulator(bv)
+
+# 2. For a specific LLIL function.
+emu = LLILEmulator(bv, il=func.llil)
+
+# 3. Wrap an existing core handle (advanced / internal).
+emu = LLILEmulator(bv, handle=raw_handle)
+```
+
+A view can have as many independent emulators as you like; each keeps its own
+registers, memory, and breakpoints.
+
+## Setting the entry point
+
+`set_entry_point` accepts two forms:
+
+```python
+# Address form: resolve an address to its function and start at its first LLIL
+# instruction. Returns False if the address is not inside an analyzed function.
+if not emu.set_entry_point(0x401000):
+ raise ValueError("address is not in an analyzed function")
+
+# IL form: start at a specific LLIL instruction index of a given LLIL function.
+emu.set_entry_point(func.llil, 5)
+```
+
+## Running and stepping
+
+```python
+emu.set_max_instructions(100000) # safety limit against runaway loops
+reason = emu.run() # run until a stop condition
+
+emu.step() # execute a single instruction
+emu.step_n(10) # execute up to 10 instructions
+emu.step_over() # step over a call (run through the callee)
+
+emu.request_stop() # ask a running emulator to stop (thread-safe)
+```
+
+Progress and position:
+
+```python
+emu.instructions_executed # count since the last reset
+emu.current_address # address of the current instruction
+emu.instruction_index # LLIL index within the current function (settable)
+```
+
+## Stop reasons
+
+`run`, `step`, `step_n`, and `step_over` all return an `ILEmulatorStopReason`, also
+available afterward via `emu.stop_reason` with a human-readable `emu.stop_message`:
+
+| Reason | Meaning |
+| --- | --- |
+| `ILEmulatorRunning` | Still running (not a terminal state) |
+| `ILEmulatorBreakpoint` | Hit a breakpoint |
+| `ILEmulatorInstructionLimit` | Reached `set_max_instructions` |
+| `ILEmulatorHalt` | Returned from the top-level function / halted normally |
+| `ILEmulatorError` | Internal error |
+| `ILEmulatorCallHook` | Stopped by a call hook |
+| `ILEmulatorSyscallHook` | Stopped by a syscall hook |
+| `ILEmulatorUndefinedBehavior` | Executed undefined behavior |
+| `ILEmulatorUnimplemented` | Hit an unimplemented LLIL instruction |
+| `ILEmulatorUserRequestedStop` | Stopped via `request_stop` |
+
+```python
+reason = emu.run()
+if reason != ILEmulatorStopReason.ILEmulatorHalt:
+ print(f"stopped early: {reason.name} — {emu.stop_message}")
+```
+
+## Registers, flags, and temporaries
+
+Registers accept either a name (`'rax'`) or a numeric register ID:
+
+```python
+emu.set_register('rsp', 0x7fff0000)
+rax = emu.get_register('rax')
+
+emu.regs # snapshot dict of every named register -> value
+
+emu.set_flag('z', 1) # flag by name or ID
+emu.get_flag('z')
+
+emu.set_temp_register(0, 0x1234) # LLIL temporary registers, by index
+emu.get_temp_register(0)
+```
+
+## Memory
+
+> **The emulator does not inherit memory from the BinaryView.** It starts with an
+> empty address space of its own. Execution works because the emulator runs on the
+> lifted LLIL, not by fetching bytes from its memory — but any data the code *reads*
+> (globals, `.rodata`, strings, tables, the stack) is **not** present unless you put
+> it there. Reading an address that holds data in the view returns zeroes in the
+> emulator. If you want the view's bytes, copy them in explicitly.
+
+Map regions before accessing them, then read and write raw bytes:
+
+```python
+emu.map_memory(0x1000, b'\x00' * 0x1000) # map with data
+emu.map_memory(0x2000, 0x1000) # map zero-filled
+emu.map_memory(0x3000, 0x1000, "stack") # map a named region
+
+emu.write_memory(0x1000, b'hello') # returns bytes written
+emu.read_memory(0x1000, 5) # -> b'hello'
+
+emu.get_mapped_regions() # [{'start':..., 'size':..., 'name':...}, ...]
+```
+
+### Copying BinaryView memory into the emulator
+
+To emulate code that reads existing program data, copy the relevant bytes from the
+view into the emulator at the same addresses. Copy whole segments:
+
+```python
+for seg in bv.segments:
+ data = bv.read(seg.start, seg.length) # bytes actually backed by the file
+ if data:
+ emu.map_memory(seg.start, data)
+```
+
+...or just the region you need (cheaper for large binaries):
+
+```python
+emu.map_memory(table_addr, bv.read(table_addr, table_size))
+```
+
+Alternatively, serve reads on demand with a memory-read hook that pulls from the view
+(see [Hooks](#hooks)):
+
+```python
+emu.set_memory_read_hook(
+ lambda emu, addr, size: int.from_bytes(bv.read(addr, size), 'little')
+ if bv.read(addr, size) else None)
+```
+
+## Function arguments
+
+Arguments are placed using the function's default calling convention:
+
+```python
+emu.set_argument(0, 0x1000) # a single argument by index
+emu.set_arguments([0x1000, 16, 42]) # several at once
+```
+
+## Breakpoints
+
+```python
+emu.add_breakpoint(0x401234) # stops *before* executing that address
+emu.remove_breakpoint(0x401234)
+emu.clear_breakpoints()
+```
+
+A breakpoint stops the emulator before the target instruction executes, so on stop
+`emu.current_address` equals the breakpoint address and its side effects have not yet
+occurred.
+
+## Hooks
+
+Hooks let embedding code intercept emulation. Pass a callable to install a hook and
+`None` to remove it. Exceptions raised inside a hook are swallowed and treated as
+"not handled".
+
+```python
+# CALL: return True if handled (advance past the call), False to let the emulator
+# try cross-function emulation.
+emu.set_call_hook(lambda emu, target: True) # skip all calls
+
+# SYSCALL: return True if handled, False to stop.
+emu.set_syscall_hook(lambda emu: True)
+
+# Memory read: return the value to use, or None to fall through to real memory.
+emu.set_memory_read_hook(lambda emu, addr, size: 0 if addr in mmio else None)
+
+# Memory write: return True if handled, False to let the write proceed.
+emu.set_memory_write_hook(lambda emu, addr, size, value: False)
+
+# Before each instruction: return True to continue, False to stop.
+emu.set_pre_instruction_hook(lambda emu, index: True)
+
+# INTRINSIC: return a list of (register_id, value) pairs if handled, else None.
+emu.set_intrinsic_hook(lambda emu, intrinsic, params: None)
+
+# stdout from emulated printf/puts/putchar; data is bytes.
+emu.set_stdout_callback(lambda emu, data: print(data.decode('latin1'), end=''))
+
+# stdin for emulated getchar/fgets/fread; return up to max_len bytes, b'' for EOF.
+emu.set_stdin_callback(lambda emu, max_len: b'')
+```
+
+The memory-read hook fires on **every** load, including implicit reads such as the
+stack pop performed by a `ret`, so filter by address when you only want to intercept
+specific regions.
+
+## Built-in libc stubs
+
+The emulator ships simple stubs for common libc functions so snippets that call
+`printf`, `malloc`, etc. can run without a real libc:
+
+```python
+emu.builtin_libc_stubs # bool, default True — enable the built-in stubs
+emu.log_libc_calls # bool, default True — log stub calls to the console
+emu.nop_unknown_externals # bool, default False — treat unknown external calls
+ # as no-ops returning 0 instead of stopping
+```
+
+## Call stack
+
+While stopped inside a called function:
+
+```python
+emu.call_stack_depth # number of nested calls
+emu.get_call_stack() # [{'function_address':..., 'return_address':...}, ...]
+```
+
+Frame 0 is the current function; later frames are its callers.
+
+## State serialization
+
+Emulator state (registers, flags, memory, call stack) can be snapshotted to JSON and
+restored — useful for save/restore points or reproducing a state across runs:
+
+```python
+snapshot = emu.save_state() # -> JSON string
+emu.load_state(snapshot) # restore, returns True on success
+
+emu.save_state_to_file("state.json")
+emu.load_state_from_file("state.json")
+
+emu.reset() # clear all state back to initial
+```
+
+## Full example: cross-function emulation
+
+Decrypt a string by emulating a decryption routine, letting the emulator run through
+the called functions and skipping anything it can't resolve:
+
+```python
+from binaryninja.emulator import LLILEmulator, ILEmulatorStopReason
+
+emu = LLILEmulator(bv)
+emu.nop_unknown_externals = True # don't stop on unresolved externals
+emu.set_max_instructions(1_000_000)
+
+# Give the routine a scratch output buffer and the encrypted input.
+emu.map_memory(0x100000, 0x1000, "out")
+emu.map_memory(0x101000, encrypted, "in")
+
+emu.set_entry_point(decrypt_func.start)
+emu.set_arguments([0x100000, 0x101000, len(encrypted)])
+
+reason = emu.run()
+if reason == ILEmulatorStopReason.ILEmulatorHalt:
+ print(emu.read_memory(0x100000, 0x100).split(b'\x00', 1)[0])
+else:
+ print(f"stopped: {reason.name} — {emu.stop_message}")
+```
+
+## API reference
+
+Everything is on the `LLILEmulator` class.
+
+**Construction:** `LLILEmulator(view, il=None, handle=None)`
+
+**Execution:** `run`, `step`, `step_n`, `step_over`, `request_stop`,
+`set_max_instructions`, `reset`
+
+**Entry / arguments:** `set_entry_point`, `set_argument`, `set_arguments`
+
+**State (properties):** `instruction_index`, `current_address`, `stop_reason`,
+`stop_message`, `instructions_executed`, `call_stack_depth`, `regs`
+
+**Registers / flags:** `get_register`, `set_register`, `get_temp_register`,
+`set_temp_register`, `get_flag`, `set_flag`
+
+**Memory:** `map_memory`, `read_memory`, `write_memory`, `get_mapped_regions`
+
+**Breakpoints:** `add_breakpoint`, `remove_breakpoint`, `clear_breakpoints`
+
+**Hooks:** `set_call_hook`, `set_syscall_hook`, `set_memory_read_hook`,
+`set_memory_write_hook`, `set_pre_instruction_hook`, `set_intrinsic_hook`,
+`set_stdout_callback`, `set_stdin_callback`
+
+**libc stubs (properties):** `builtin_libc_stubs`, `log_libc_calls`,
+`nop_unknown_externals`
+
+**Call stack:** `get_call_stack`
+
+**Serialization:** `save_state`, `load_state`, `save_state_to_file`,
+`load_state_from_file`
+
+For runnable, self-contained examples of every feature above, see the
+[test suite](https://github.com/Vector35/binaryninja-api/blob/dev/plugins/emulator/test/emulator_test.py).
diff --git a/plugins/emulator/.gitignore b/plugins/emulator/.gitignore
new file mode 100644
index 0000000000..9a91c9079e
--- /dev/null
+++ b/plugins/emulator/.gitignore
@@ -0,0 +1,32 @@
+# Build output
+/build
+/artifacts
+cmake-build-*/
+out/
+
+# Compiled objects / libraries
+*.o
+*.obj
+*.so
+*.dylib
+*.dll
+*.a
+*.lib
+*.pdb
+*.ilk
+
+# The known-answer-test kernels are checked in deliberately, so the suite does not
+# need a cross-compiler. Rebuild them with test/kat/build.sh.
+!/test/kat/prebuilt/*.o
+
+# Generated Python bindings
+/api/python/_emulatorcore.py
+/api/python/emulator_enums.py
+*/__pycache__/
+*.pyc
+
+# IDE / OS
+.idea/
+.vscode/
+.DS_Store
+/.claude/
diff --git a/plugins/emulator/CMakeLists.txt b/plugins/emulator/CMakeLists.txt
new file mode 100644
index 0000000000..033664bb77
--- /dev/null
+++ b/plugins/emulator/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(bnil-emulator)
+
+if(NOT BN_INTERNAL_BUILD)
+ # Out-of-tree build: locate the Binary Ninja API source. When this plugin is checked
+ # out inside the api repo (api/plugins/emulator), the api root is two levels up.
+ find_path(
+ BN_API_PATH
+ NAMES binaryninjaapi.h
+ HINTS ../.. binaryninjaapi $ENV{BN_API_PATH}
+ REQUIRED
+ )
+endif()
+
+add_subdirectory(core)
+add_subdirectory(api)
diff --git a/plugins/emulator/README.md b/plugins/emulator/README.md
new file mode 100644
index 0000000000..e35a0d71d4
--- /dev/null
+++ b/plugins/emulator/README.md
@@ -0,0 +1,96 @@
+# BNIL Emulator
+
+A standalone Binary Ninja plugin that emulates Binary Ninja's Low Level Intermediate
+Language (LLIL). It is structured like the [debugger](https://github.com/Vector35/debugger):
+the engine builds into its own plugin (`emulatorcore`) against the public Binary Ninja
+API, exposes a C ABI, and ships C++ (`emulatorapi`) and Python bindings.
+
+> **Experimental.** This plugin is experimental and under active development; its API and
+> behavior may change.
+
+The [Python API guide](../../docs/guide/emulator.md) walks through the `LLILEmulator`
+class with examples; runnable tests live in [`test/emulator_test.py`](test/emulator_test.py).
+
+## Scope & accuracy
+
+This plugin emulates the given BNIL instructions — it is **not** meant to match the accuracy
+of full CPU emulators like [Unicorn](https://www.unicorn-engine.org/) or
+[QEMU](https://www.qemu.org/). Because emulation runs on Binary Ninja's *lifted* IL rather
+than the raw machine instructions, the emulated state may deviate from the actual state of
+the program during real execution.
+
+In practice it is aimed at focused tasks — decrypting strings, resolving API hashes, and
+similar snippet-level emulation. It is **not** intended for full-program or whole-system
+emulation.
+
+## Layout
+
+- `core/` — the emulator engine (`ilemulator`, `llilemulator`) and plugin entry point,
+ built as the `emulatorcore` plugin. Exposes the emulator C ABI (`api/ffi.h`).
+- `api/` — C++ wrapper (`emulatorapi`, `BinaryNinja::LLILEmulator`) over the C ABI, plus
+ Python bindings under `api/python/`.
+
+## Building
+
+```sh
+export BN_API_PATH=/path/to/binaryninja-api
+cmake -S . -B build # -GNinja optional
+cmake --build build
+```
+
+The resulting `emulatorcore` plugin is written to `build/out/plugins/`.
+
+## Testing
+
+The Python test suite is self-contained — it runs headless against any Binary Ninja
+install that has the emulator plugin. There are two files:
+
+- [`test/emulator_test.py`](test/emulator_test.py) — end-to-end behaviour, assembling
+ tiny `BinaryView`s from raw machine code and emulating the lifted LLIL.
+- [`test/emulator_il_test.py`](test/emulator_il_test.py) — per-instruction coverage,
+ building LLIL directly so that every LLIL operation the emulator implements is
+ exercised in isolation by at least one test.
+
+```sh
+PYTHONPATH=/python python3 -m pytest test/
+# or a single file, without pytest:
+PYTHONPATH=/python python3 test/emulator_il_test.py
+```
+
+## Support status
+
+Only **LLIL** emulation is supported today. **MLIL and HLIL emulation are planned** for the
+future.
+
+Emulating an unsupported instruction stops the emulator with an `Unimplemented` stop reason.
+
+### Supported LLIL instructions
+
+- **Constants:** `CONST`, `CONST_PTR`, `EXTERN_PTR`, `FLOAT_CONST`
+- **Registers:** `REG`, `SET_REG`, `REG_SPLIT`, `SET_REG_SPLIT`, `LOW_PART`
+- **Memory:** `LOAD`, `STORE`, `PUSH`, `POP`
+- **Arithmetic:** `ADD`, `ADC`, `SUB`, `SBB`, `MUL`, `MULU_DP`, `MULS_DP`, `DIVU`, `DIVS`,
+ `DIVU_DP`, `DIVS_DP`, `MODU`, `MODS`, `MODU_DP`, `MODS_DP`, `NEG`, `ADD_OVERFLOW`
+- **Bitwise / shifts:** `AND`, `OR`, `XOR`, `NOT`, `LSL`, `LSR`, `ASR`, `ROL`, `ROR`, `RLC`,
+ `RRC`, `SX`, `ZX`, `LOW_PART`, `TEST_BIT`, `BOOL_TO_INT`
+- **Bit operations:** `BSWAP`, `POPCNT`, `CLZ`, `CTZ`, `RBIT`, `CLS`, `ABS`, `MINS`, `MAXS`,
+ `MINU`, `MAXU`
+- **Comparisons:** `CMP_E`, `CMP_NE`, `CMP_SLT`, `CMP_SLE`, `CMP_SGE`, `CMP_SGT`, `CMP_ULT`,
+ `CMP_ULE`, `CMP_UGE`, `CMP_UGT`
+- **Flags:** `FLAG`, `SET_FLAG`, `FLAG_BIT`, `FLAG_COND`, `FLAG_GROUP`
+- **Control flow:** `JUMP`, `JUMP_TO`, `GOTO`, `IF`, `CALL`, `CALL_STACK_ADJUST`, `TAILCALL`,
+ `RET`, `NORET`
+- **Other:** `NOP`
+
+### Not yet supported
+
+- **Floating point:** `FADD`, `FSUB`, `FMUL`, `FDIV`, `FSQRT`, `FABS`, `FNEG`, `FCMP_*`,
+ `FLOAT_CONV`, `FLOAT_TO_INT`, `INT_TO_FLOAT`, `ROUND_TO_INT`, `CEIL`, `FLOOR`, `FTRUNC`
+ (float *constants* are read, but float arithmetic is not evaluated)
+- **Register stacks (x87/FPU-style):** `REG_STACK_REL`, `SET_REG_STACK_REL`, `REG_STACK_PUSH`,
+ `REG_STACK_POP`, `REG_STACK_FREE_REG`, `REG_STACK_FREE_REL`
+- **System / hooks** (no built-in semantics — stop unless the embedding code registers a
+ hook): `SYSCALL`, `INTRINSIC`
+- **Halting / non-representable** (stop the emulator): `BP`, `TRAP`, `UNDEF`, `UNIMPL`,
+ `UNIMPL_MEM`
+- **Other:** `ASSERT`, `FORCE_VER`, `CALL_PARAM`
diff --git a/plugins/emulator/api/CMakeLists.txt b/plugins/emulator/api/CMakeLists.txt
new file mode 100644
index 0000000000..e262f54310
--- /dev/null
+++ b/plugins/emulator/api/CMakeLists.txt
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(emulatorapi)
+
+file(GLOB BN_EMULATOR_API_SOURCES CONFIGURE_DEPENDS *.cpp *.h)
+add_library(emulatorapi STATIC ${BN_EMULATOR_API_SOURCES})
+
+target_include_directories(emulatorapi
+ PUBLIC ${PROJECT_SOURCE_DIR})
+
+target_link_libraries(emulatorapi PUBLIC emulatorcore)
+
+set_target_properties(emulatorapi PROPERTIES
+ CXX_STANDARD 20
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/out)
+
+# Python bindings retarget (load the emulatorcore plugin dylib) is pending; wire the
+# subdirectory only once api/python/CMakeLists.txt exists.
+if (NOT DEMO AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/python/CMakeLists.txt)
+ add_subdirectory(python)
+endif()
diff --git a/plugins/emulator/api/emulatorapi.h b/plugins/emulator/api/emulatorapi.h
new file mode 100644
index 0000000000..c6f89b095d
--- /dev/null
+++ b/plugins/emulator/api/emulatorapi.h
@@ -0,0 +1,160 @@
+/*
+Copyright 2020-2026 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "vendor/intx/intx.hpp"
+#include "ffi.h"
+#include
+#include
+#include
+#include
+#include
+
+using namespace BinaryNinja;
+
+// The plugin's public C++ API lives in its own namespace (mirroring
+// BinaryNinjaDebuggerAPI) rather than in BinaryNinja, which is reserved for the core API.
+namespace BinaryNinjaEmulatorAPI
+{
+ /*!
+ \ingroup emulator
+ */
+ class LLILEmulator :
+ public CoreRefCountObject
+ {
+ // Stored hooks (prevent dangling captures)
+ std::function m_callHook;
+ std::function m_syscallHook;
+ std::function m_memoryReadHook;
+ std::function m_memoryWriteHook;
+ std::function m_preInstructionHook;
+ std::function&,
+ std::vector>&)> m_intrinsicHook;
+ std::function m_stdoutCallback;
+ std::function m_stdinCallback;
+
+ // Static C bridge callbacks
+ static bool CallHookCallback(void* ctxt, BNILEmulator* emu, uint64_t target);
+ static bool SyscallHookCallback(void* ctxt, BNILEmulator* emu);
+ static bool MemoryReadHookCallback(void* ctxt, BNILEmulator* emu,
+ uint64_t addr, size_t size, uint8_t* outBuf, size_t bufLen);
+ static bool MemoryWriteHookCallback(void* ctxt, BNILEmulator* emu,
+ uint64_t addr, size_t size, const uint8_t* buf, size_t bufLen);
+ static bool PreInstructionHookCallback(void* ctxt, BNILEmulator* emu, size_t instrIndex);
+ static bool IntrinsicHookCallback(void* ctxt, BNLLILEmulator* emu,
+ uint32_t intrinsic, const uint64_t* params, size_t paramCount,
+ uint64_t* outValues, uint32_t* outRegs, size_t maxCount, size_t* outCount);
+ static void StdoutCallbackBridge(void* ctxt, BNILEmulator* emu, const char* data, size_t len);
+ static size_t StdinCallbackBridge(void* ctxt, BNILEmulator* emu, void* buf, size_t maxLen);
+
+ public:
+ LLILEmulator(Ref view);
+ LLILEmulator(Ref il, Ref view);
+ LLILEmulator(BNLLILEmulator* emu);
+
+ bool SetEntryPoint(uint64_t addr);
+ void SetEntryPoint(Ref il, size_t instrIndex);
+
+ // Argument setup (uses default calling convention)
+ void SetArgument(size_t index, const intx::uint512& value);
+ void SetArguments(const std::vector& values);
+
+ // Execution
+ BNILEmulatorStopReason Run();
+ BNILEmulatorStopReason Step();
+ BNILEmulatorStopReason StepN(size_t n);
+ BNILEmulatorStopReason StepOver();
+ void RequestStop();
+
+ // State
+ size_t GetInstructionIndex() const;
+ void SetInstructionIndex(size_t index);
+ uint64_t GetCurrentAddress() const;
+ BNILEmulatorStopReason GetStopReason() const;
+ std::string GetStopMessage() const;
+
+ // Memory
+ size_t ReadMemory(void* dest, uint64_t addr, size_t len) const;
+ size_t WriteMemory(uint64_t addr, const void* src, size_t len);
+ void MapMemory(uint64_t addr, const void* data, size_t len, const std::string& name = "");
+ void MapMemory(uint64_t addr, size_t len, const std::string& name = "");
+
+ // Breakpoints (by address)
+ void AddBreakpoint(uint64_t addr);
+ void RemoveBreakpoint(uint64_t addr);
+ void ClearBreakpoints();
+
+ // Limits
+ void SetMaxInstructions(size_t max);
+ size_t GetInstructionsExecuted() const;
+
+ // Hooks
+ void SetCallHook(const std::function& hook);
+ void SetSyscallHook(const std::function& hook);
+ void SetMemoryReadHook(const std::function& hook);
+ void SetMemoryWriteHook(const std::function& hook);
+ void SetPreInstructionHook(const std::function& hook);
+ void SetIntrinsicHook(const std::function&, std::vector>&)>& hook);
+ void SetStdoutCallback(const std::function& cb);
+ void SetStdinCallback(const std::function& cb);
+
+ // Register / flag / temp access
+ intx::uint512 GetRegister(uint32_t reg) const;
+ void SetRegister(uint32_t reg, const intx::uint512& value);
+ intx::uint512 GetTempRegister(uint32_t index) const;
+ void SetTempRegister(uint32_t index, const intx::uint512& value);
+ std::unordered_map GetAllTempRegisters() const;
+ uint8_t GetFlag(uint32_t flag) const;
+ void SetFlag(uint32_t flag, uint8_t value);
+
+ // Cross-function state
+ size_t GetCallStackDepth() const;
+
+ struct CallStackEntry
+ {
+ uint64_t functionAddress;
+ uint64_t returnAddress;
+ };
+ std::vector GetCallStack() const;
+
+ // Memory regions
+ struct MappedRegion
+ {
+ uint64_t start;
+ uint64_t size;
+ std::string name;
+ };
+ std::vector GetMappedRegions() const;
+
+ // Built-in libc stub settings
+ void SetBuiltinLibcStubsEnabled(bool enabled);
+ bool IsBuiltinLibcStubsEnabled() const;
+ void SetLogLibcCalls(bool enabled);
+ bool IsLogLibcCalls() const;
+ void SetNopUnknownExternals(bool enabled);
+ bool IsNopUnknownExternals() const;
+
+ // Reset
+ void Reset();
+
+ // State serialization
+ std::string SaveState() const;
+ bool LoadState(const std::string& json);
+ };
+} // namespace BinaryNinjaEmulatorAPI
diff --git a/plugins/emulator/api/ffi.h b/plugins/emulator/api/ffi.h
new file mode 100644
index 0000000000..23951328b0
--- /dev/null
+++ b/plugins/emulator/api/ffi.h
@@ -0,0 +1,198 @@
+/*
+Copyright 2020-2026 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#pragma once
+
+// The Binary Ninja type parser (used to generate the Python bindings) defines
+// BN_TYPE_PARSER and supplies its own fixed-width integer types, so skip the system
+// headers in that mode to avoid depending on the parser's clang include search paths.
+#ifndef BN_TYPE_PARSER
+#ifdef __cplusplus
+#include
+#include
+#include
+#else
+#include
+#include
+#include
+#include
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __GNUC__
+ #ifdef EMULATOR_LIBRARY
+ #define EMULATOR_FFI_API __attribute__((visibility("default")))
+ #else // EMULATOR_LIBRARY
+ #define EMULATOR_FFI_API
+ #endif // EMULATOR_LIBRARY
+#else // __GNUC__
+ #ifdef _MSC_VER
+ #ifdef EMULATOR_LIBRARY
+ #define EMULATOR_FFI_API __declspec(dllexport)
+ #else // EMULATOR_LIBRARY
+ #define EMULATOR_FFI_API __declspec(dllimport)
+ #endif // EMULATOR_LIBRARY
+ #else // _MSC_VER
+ #define EMULATOR_FFI_API
+ #endif // _MSC_VER
+#endif // __GNUC__
+
+ // Opaque handles owned by this plugin
+ typedef struct BNILEmulator BNILEmulator;
+ typedef struct BNLLILEmulator BNLLILEmulator;
+
+ // Binary Ninja core handles used by the emulator API (defined by binaryninjacore.h)
+ typedef struct BNBinaryView BNBinaryView;
+ typedef struct BNLowLevelILFunction BNLowLevelILFunction;
+
+ enum BNILEmulatorStopReason
+ {
+ ILEmulatorRunning = 0,
+ ILEmulatorBreakpoint,
+ ILEmulatorInstructionLimit,
+ ILEmulatorHalt,
+ ILEmulatorError,
+ ILEmulatorCallHook,
+ ILEmulatorSyscallHook,
+ ILEmulatorUndefinedBehavior,
+ ILEmulatorUnimplemented,
+ ILEmulatorUserRequestedStop
+ };
+
+ struct BNEmulatorMemoryRegion
+ {
+ uint64_t start;
+ uint64_t size;
+ char* name;
+ };
+
+ struct BNEmulatorCallStackEntry
+ {
+ uint64_t functionAddress;
+ uint64_t returnAddress;
+ };
+
+ // IL Emulator — creation/lifecycle
+ EMULATOR_FFI_API BNLLILEmulator* BNCreateLLILEmulatorForView(BNBinaryView* view);
+ EMULATOR_FFI_API BNLLILEmulator* BNCreateLLILEmulator(BNLowLevelILFunction* il, BNBinaryView* view);
+ EMULATOR_FFI_API BNLLILEmulator* BNNewLLILEmulatorReference(BNLLILEmulator* emu);
+ EMULATOR_FFI_API void BNFreeLLILEmulator(BNLLILEmulator* emu);
+ EMULATOR_FFI_API BNILEmulator* BNLLILEmulatorGetBase(BNLLILEmulator* emu);
+ EMULATOR_FFI_API bool BNLLILEmulatorSetEntryPoint(BNLLILEmulator* emu, uint64_t addr);
+ EMULATOR_FFI_API void BNLLILEmulatorSetEntryPointForIL(BNLLILEmulator* emu,
+ BNLowLevelILFunction* il, size_t instrIndex);
+ EMULATOR_FFI_API void BNLLILEmulatorSetArgument(BNLLILEmulator* emu, size_t index, const uint8_t* buf, size_t bufLen);
+ EMULATOR_FFI_API void BNLLILEmulatorSetArguments(BNLLILEmulator* emu, const uint64_t* values, size_t count);
+
+ // IL Emulator — execution control (shared)
+ EMULATOR_FFI_API BNILEmulatorStopReason BNILEmulatorRun(BNILEmulator* emu);
+ EMULATOR_FFI_API BNILEmulatorStopReason BNILEmulatorStep(BNILEmulator* emu);
+ EMULATOR_FFI_API BNILEmulatorStopReason BNILEmulatorStepN(BNILEmulator* emu, size_t n);
+
+ // IL Emulator — state (shared)
+ EMULATOR_FFI_API size_t BNILEmulatorGetInstructionIndex(BNILEmulator* emu);
+ EMULATOR_FFI_API void BNILEmulatorSetInstructionIndex(BNILEmulator* emu, size_t index);
+ EMULATOR_FFI_API uint64_t BNILEmulatorGetCurrentAddress(BNILEmulator* emu);
+ EMULATOR_FFI_API BNILEmulatorStopReason BNILEmulatorGetStopReason(BNILEmulator* emu);
+ EMULATOR_FFI_API char* BNILEmulatorGetStopMessage(BNILEmulator* emu);
+
+ // IL Emulator — memory (shared)
+ EMULATOR_FFI_API size_t BNILEmulatorReadMemory(BNILEmulator* emu, void* dest, uint64_t addr, size_t len);
+ EMULATOR_FFI_API size_t BNILEmulatorWriteMemory(BNILEmulator* emu, uint64_t addr, const void* src, size_t len);
+ EMULATOR_FFI_API void BNILEmulatorMapMemory(BNILEmulator* emu, uint64_t addr, const void* data, size_t len);
+ EMULATOR_FFI_API void BNILEmulatorMapMemoryZero(BNILEmulator* emu, uint64_t addr, size_t len);
+ EMULATOR_FFI_API void BNILEmulatorMapMemoryNamed(BNILEmulator* emu, uint64_t addr, const void* data, size_t len, const char* name);
+ EMULATOR_FFI_API void BNILEmulatorMapMemoryZeroNamed(BNILEmulator* emu, uint64_t addr, size_t len, const char* name);
+
+ // IL Emulator — breakpoints (shared, by address)
+ EMULATOR_FFI_API void BNILEmulatorAddBreakpoint(BNILEmulator* emu, uint64_t addr);
+ EMULATOR_FFI_API void BNILEmulatorRemoveBreakpoint(BNILEmulator* emu, uint64_t addr);
+ EMULATOR_FFI_API void BNILEmulatorClearBreakpoints(BNILEmulator* emu);
+
+ // IL Emulator — limits (shared)
+ EMULATOR_FFI_API void BNILEmulatorSetMaxInstructions(BNILEmulator* emu, size_t max);
+ EMULATOR_FFI_API size_t BNILEmulatorGetInstructionsExecuted(BNILEmulator* emu);
+
+ // IL Emulator — hooks (shared)
+ EMULATOR_FFI_API void BNILEmulatorSetCallHook(BNILEmulator* emu, void* ctxt,
+ bool (*callback)(void* ctxt, BNILEmulator* emu, uint64_t target));
+ EMULATOR_FFI_API void BNILEmulatorSetSyscallHook(BNILEmulator* emu, void* ctxt,
+ bool (*callback)(void* ctxt, BNILEmulator* emu));
+ EMULATOR_FFI_API void BNILEmulatorSetMemoryReadHook(BNILEmulator* emu, void* ctxt,
+ bool (*callback)(void* ctxt, BNILEmulator* emu, uint64_t addr, size_t size, uint8_t* outBuf, size_t bufLen));
+ EMULATOR_FFI_API void BNILEmulatorSetMemoryWriteHook(BNILEmulator* emu, void* ctxt,
+ bool (*callback)(void* ctxt, BNILEmulator* emu, uint64_t addr, size_t size, const uint8_t* buf, size_t bufLen));
+ EMULATOR_FFI_API void BNILEmulatorSetPreInstructionHook(BNILEmulator* emu, void* ctxt,
+ bool (*callback)(void* ctxt, BNILEmulator* emu, size_t instrIndex));
+ EMULATOR_FFI_API void BNILEmulatorSetStdoutCallback(BNILEmulator* emu, void* ctxt,
+ void (*callback)(void* ctxt, BNILEmulator* emu, const char* data, size_t len));
+ // buf is a writable output buffer of maxLen bytes (typed void* so the generated bindings
+ // expose it as a writable pointer rather than an immutable string).
+ EMULATOR_FFI_API void BNILEmulatorSetStdinCallback(BNILEmulator* emu, void* ctxt,
+ size_t (*callback)(void* ctxt, BNILEmulator* emu, void* buf, size_t maxLen));
+ EMULATOR_FFI_API void BNILEmulatorRequestStop(BNILEmulator* emu);
+ EMULATOR_FFI_API void BNILEmulatorReset(BNILEmulator* emu);
+
+ // LLIL Emulator — register/flag access (byte-buffer API; values are 64-byte little-endian)
+ EMULATOR_FFI_API void BNLLILEmulatorGetRegister(BNLLILEmulator* emu, uint32_t reg, uint8_t* outBuf, size_t bufLen);
+ EMULATOR_FFI_API void BNLLILEmulatorSetRegister(BNLLILEmulator* emu, uint32_t reg, const uint8_t* buf, size_t bufLen);
+ EMULATOR_FFI_API void BNLLILEmulatorGetTempRegister(BNLLILEmulator* emu, uint32_t index, uint8_t* outBuf, size_t bufLen);
+ EMULATOR_FFI_API void BNLLILEmulatorSetTempRegister(BNLLILEmulator* emu, uint32_t index, const uint8_t* buf, size_t bufLen);
+ EMULATOR_FFI_API size_t BNLLILEmulatorGetAllTempRegisters(
+ BNLLILEmulator* emu, uint32_t* outIndices, uint8_t* outValues, size_t maxCount);
+ EMULATOR_FFI_API uint8_t BNLLILEmulatorGetFlag(BNLLILEmulator* emu, uint32_t flag);
+ EMULATOR_FFI_API void BNLLILEmulatorSetFlag(BNLLILEmulator* emu, uint32_t flag, uint8_t value);
+
+ // LLIL Emulator — intrinsic hook.
+ // The callback receives output buffers outValues/outRegs with capacity maxCount entries;
+ // it must write at most maxCount pairs and set *outCount to the number written.
+ EMULATOR_FFI_API void BNLLILEmulatorSetIntrinsicHook(BNLLILEmulator* emu, void* ctxt,
+ bool (*callback)(void* ctxt, BNLLILEmulator* emu, uint32_t intrinsic,
+ const uint64_t* params, size_t paramCount,
+ uint64_t* outValues, uint32_t* outRegs, size_t maxCount, size_t* outCount));
+
+ // LLIL Emulator — call stack
+ EMULATOR_FFI_API size_t BNLLILEmulatorGetCallStackDepth(BNLLILEmulator* emu);
+ EMULATOR_FFI_API BNEmulatorCallStackEntry* BNLLILEmulatorGetCallStack(BNLLILEmulator* emu, size_t* count);
+ EMULATOR_FFI_API void BNLLILEmulatorFreeCallStack(BNEmulatorCallStackEntry* entries);
+
+ // LLIL Emulator — stepping
+ EMULATOR_FFI_API BNILEmulatorStopReason BNLLILEmulatorStepOver(BNLLILEmulator* emu);
+
+ // IL Emulator — memory regions
+ EMULATOR_FFI_API BNEmulatorMemoryRegion* BNILEmulatorGetMappedRegions(BNILEmulator* emu, size_t* count);
+ EMULATOR_FFI_API void BNFreeEmulatorMemoryRegions(BNEmulatorMemoryRegion* regions, size_t count);
+
+ // LLIL Emulator — built-in libc stubs
+ EMULATOR_FFI_API void BNLLILEmulatorSetBuiltinLibcStubsEnabled(BNLLILEmulator* emu, bool enabled);
+ EMULATOR_FFI_API bool BNLLILEmulatorIsBuiltinLibcStubsEnabled(BNLLILEmulator* emu);
+ EMULATOR_FFI_API void BNLLILEmulatorSetLogLibcCalls(BNLLILEmulator* emu, bool enabled);
+ EMULATOR_FFI_API bool BNLLILEmulatorIsLogLibcCalls(BNLLILEmulator* emu);
+ EMULATOR_FFI_API void BNLLILEmulatorSetNopUnknownExternals(BNLLILEmulator* emu, bool enabled);
+ EMULATOR_FFI_API bool BNLLILEmulatorIsNopUnknownExternals(BNLLILEmulator* emu);
+
+ // LLIL Emulator — state serialization
+ EMULATOR_FFI_API char* BNLLILEmulatorSaveState(BNLLILEmulator* emu);
+ EMULATOR_FFI_API bool BNLLILEmulatorLoadState(BNLLILEmulator* emu, const char* json);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/plugins/emulator/api/ilemulator.cpp b/plugins/emulator/api/ilemulator.cpp
new file mode 100644
index 0000000000..d52ae5394f
--- /dev/null
+++ b/plugins/emulator/api/ilemulator.cpp
@@ -0,0 +1,555 @@
+// Copyright (c) 2015-2026 Vector 35 Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "binaryninjaapi.h"
+#include "ffi.h"
+#include "emulatorapi.h"
+
+using namespace BinaryNinja;
+using namespace BinaryNinjaEmulatorAPI;
+
+
+LLILEmulator::LLILEmulator(Ref view)
+{
+ m_object = BNCreateLLILEmulatorForView(view->GetObject());
+}
+
+
+LLILEmulator::LLILEmulator(Ref il, Ref view)
+{
+ m_object = BNCreateLLILEmulator(il->GetObject(), view->GetObject());
+}
+
+
+LLILEmulator::LLILEmulator(BNLLILEmulator* emu)
+{
+ m_object = emu;
+}
+
+
+bool LLILEmulator::SetEntryPoint(uint64_t addr)
+{
+ return BNLLILEmulatorSetEntryPoint(m_object, addr);
+}
+
+
+void LLILEmulator::SetEntryPoint(Ref il, size_t instrIndex)
+{
+ BNLLILEmulatorSetEntryPointForIL(m_object, il->GetObject(), instrIndex);
+}
+
+
+static void ApiUint512ToBytes(const intx::uint512& value, uint8_t* buf, size_t bufLen)
+{
+ intx::uint512 tmp = value;
+ size_t n = std::min(bufLen, (size_t)64);
+ for (size_t i = 0; i < n; i++)
+ {
+ buf[i] = static_cast(tmp);
+ tmp >>= 8;
+ }
+ for (size_t i = n; i < bufLen; i++)
+ buf[i] = 0;
+}
+
+
+static intx::uint512 ApiBytesToUint512(const uint8_t* buf, size_t bufLen)
+{
+ intx::uint512 result = 0;
+ size_t n = std::min(bufLen, (size_t)64);
+ for (size_t i = n; i > 0; i--)
+ result = (result << 8) | buf[i - 1];
+ return result;
+}
+
+
+void LLILEmulator::SetArgument(size_t index, const intx::uint512& value)
+{
+ uint8_t buf[64];
+ ApiUint512ToBytes(value, buf, sizeof(buf));
+ BNLLILEmulatorSetArgument(m_object, index, buf, sizeof(buf));
+}
+
+
+void LLILEmulator::SetArguments(const std::vector& values)
+{
+ BNLLILEmulatorSetArguments(m_object, values.data(), values.size());
+}
+
+
+// ─── Execution ───────────────────────────────────────────────────────────────
+
+BNILEmulatorStopReason LLILEmulator::Run()
+{
+ return BNILEmulatorRun(BNLLILEmulatorGetBase(m_object));
+}
+
+
+BNILEmulatorStopReason LLILEmulator::Step()
+{
+ return BNILEmulatorStep(BNLLILEmulatorGetBase(m_object));
+}
+
+
+BNILEmulatorStopReason LLILEmulator::StepN(size_t n)
+{
+ return BNILEmulatorStepN(BNLLILEmulatorGetBase(m_object), n);
+}
+
+
+BNILEmulatorStopReason LLILEmulator::StepOver()
+{
+ return BNLLILEmulatorStepOver(m_object);
+}
+
+
+void LLILEmulator::RequestStop()
+{
+ BNILEmulatorRequestStop(BNLLILEmulatorGetBase(m_object));
+}
+
+
+// ─── State ───────────────────────────────────────────────────────────────────
+
+size_t LLILEmulator::GetInstructionIndex() const
+{
+ return BNILEmulatorGetInstructionIndex(BNLLILEmulatorGetBase(m_object));
+}
+
+
+void LLILEmulator::SetInstructionIndex(size_t index)
+{
+ BNILEmulatorSetInstructionIndex(BNLLILEmulatorGetBase(m_object), index);
+}
+
+
+uint64_t LLILEmulator::GetCurrentAddress() const
+{
+ return BNILEmulatorGetCurrentAddress(BNLLILEmulatorGetBase(m_object));
+}
+
+
+BNILEmulatorStopReason LLILEmulator::GetStopReason() const
+{
+ return BNILEmulatorGetStopReason(BNLLILEmulatorGetBase(m_object));
+}
+
+
+std::string LLILEmulator::GetStopMessage() const
+{
+ char* msg = BNILEmulatorGetStopMessage(BNLLILEmulatorGetBase(m_object));
+ std::string result(msg);
+ BNFreeString(msg);
+ return result;
+}
+
+
+// ─── Memory ──────────────────────────────────────────────────────────────────
+
+size_t LLILEmulator::ReadMemory(void* dest, uint64_t addr, size_t len) const
+{
+ return BNILEmulatorReadMemory(BNLLILEmulatorGetBase(m_object), dest, addr, len);
+}
+
+
+size_t LLILEmulator::WriteMemory(uint64_t addr, const void* src, size_t len)
+{
+ return BNILEmulatorWriteMemory(BNLLILEmulatorGetBase(m_object), addr, src, len);
+}
+
+
+void LLILEmulator::MapMemory(uint64_t addr, const void* data, size_t len, const std::string& name)
+{
+ if (name.empty())
+ BNILEmulatorMapMemory(BNLLILEmulatorGetBase(m_object), addr, data, len);
+ else
+ BNILEmulatorMapMemoryNamed(BNLLILEmulatorGetBase(m_object), addr, data, len, name.c_str());
+}
+
+
+void LLILEmulator::MapMemory(uint64_t addr, size_t len, const std::string& name)
+{
+ if (name.empty())
+ BNILEmulatorMapMemoryZero(BNLLILEmulatorGetBase(m_object), addr, len);
+ else
+ BNILEmulatorMapMemoryZeroNamed(BNLLILEmulatorGetBase(m_object), addr, len, name.c_str());
+}
+
+
+// ─── Breakpoints ─────────────────────────────────────────────────────────────
+
+void LLILEmulator::AddBreakpoint(uint64_t addr)
+{
+ BNILEmulatorAddBreakpoint(BNLLILEmulatorGetBase(m_object), addr);
+}
+
+
+void LLILEmulator::RemoveBreakpoint(uint64_t addr)
+{
+ BNILEmulatorRemoveBreakpoint(BNLLILEmulatorGetBase(m_object), addr);
+}
+
+
+void LLILEmulator::ClearBreakpoints()
+{
+ BNILEmulatorClearBreakpoints(BNLLILEmulatorGetBase(m_object));
+}
+
+
+// ─── Limits ──────────────────────────────────────────────────────────────────
+
+void LLILEmulator::SetMaxInstructions(size_t max)
+{
+ BNILEmulatorSetMaxInstructions(BNLLILEmulatorGetBase(m_object), max);
+}
+
+
+size_t LLILEmulator::GetInstructionsExecuted() const
+{
+ return BNILEmulatorGetInstructionsExecuted(BNLLILEmulatorGetBase(m_object));
+}
+
+
+// ─── Hook bridge callbacks ───────────────────────────────────────────────────
+
+bool LLILEmulator::CallHookCallback(void* ctxt, BNILEmulator*, uint64_t target)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ return self->m_callHook(self, target);
+}
+
+
+bool LLILEmulator::SyscallHookCallback(void* ctxt, BNILEmulator*)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ return self->m_syscallHook(self);
+}
+
+
+bool LLILEmulator::MemoryReadHookCallback(
+ void* ctxt, BNILEmulator*, uint64_t addr, size_t size, uint8_t* outBuf, size_t bufLen)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ intx::uint512 value;
+ if (!self->m_memoryReadHook(self, addr, size, value))
+ return false;
+ ApiUint512ToBytes(value, outBuf, bufLen);
+ return true;
+}
+
+
+bool LLILEmulator::MemoryWriteHookCallback(
+ void* ctxt, BNILEmulator*, uint64_t addr, size_t size, const uint8_t* buf, size_t bufLen)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ intx::uint512 value = ApiBytesToUint512(buf, bufLen);
+ return self->m_memoryWriteHook(self, addr, size, value);
+}
+
+
+bool LLILEmulator::PreInstructionHookCallback(void* ctxt, BNILEmulator*, size_t instrIndex)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ return self->m_preInstructionHook(self, instrIndex);
+}
+
+
+bool LLILEmulator::IntrinsicHookCallback(void* ctxt, BNLLILEmulator*,
+ uint32_t intrinsic, const uint64_t* params, size_t paramCount,
+ uint64_t* outValues, uint32_t* outRegs, size_t maxCount, size_t* outCount)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ std::vector paramVec(params, params + paramCount);
+ std::vector> outputs;
+
+ bool result = self->m_intrinsicHook(self, intrinsic, paramVec, outputs);
+
+ // Never write past the caller-provided capacity, even if the user hook returns more
+ // outputs than the core allocated space for.
+ size_t count = std::min(outputs.size(), maxCount);
+ if (outCount)
+ *outCount = count;
+ for (size_t i = 0; i < count; i++)
+ {
+ if (outRegs)
+ outRegs[i] = outputs[i].first;
+ if (outValues)
+ outValues[i] = outputs[i].second;
+ }
+ return result;
+}
+
+
+void LLILEmulator::StdoutCallbackBridge(void* ctxt, BNILEmulator*, const char* data, size_t len)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ self->m_stdoutCallback(self, std::string(data, len));
+}
+
+
+size_t LLILEmulator::StdinCallbackBridge(void* ctxt, BNILEmulator*, void* buf, size_t maxLen)
+{
+ LLILEmulator* self = (LLILEmulator*)ctxt;
+ return self->m_stdinCallback(self, static_cast(buf), maxLen);
+}
+
+
+// ─── Hook setters ────────────────────────────────────────────────────────────
+
+void LLILEmulator::SetCallHook(const std::function& hook)
+{
+ m_callHook = hook;
+ BNILEmulatorSetCallHook(BNLLILEmulatorGetBase(m_object),
+ hook ? (void*)this : nullptr,
+ hook ? CallHookCallback : nullptr);
+}
+
+
+void LLILEmulator::SetSyscallHook(const std::function& hook)
+{
+ m_syscallHook = hook;
+ BNILEmulatorSetSyscallHook(BNLLILEmulatorGetBase(m_object),
+ hook ? (void*)this : nullptr,
+ hook ? SyscallHookCallback : nullptr);
+}
+
+
+void LLILEmulator::SetMemoryReadHook(
+ const std::function& hook)
+{
+ m_memoryReadHook = hook;
+ BNILEmulatorSetMemoryReadHook(BNLLILEmulatorGetBase(m_object),
+ hook ? (void*)this : nullptr,
+ hook ? MemoryReadHookCallback : nullptr);
+}
+
+
+void LLILEmulator::SetMemoryWriteHook(
+ const std::function& hook)
+{
+ m_memoryWriteHook = hook;
+ BNILEmulatorSetMemoryWriteHook(BNLLILEmulatorGetBase(m_object),
+ hook ? (void*)this : nullptr,
+ hook ? MemoryWriteHookCallback : nullptr);
+}
+
+
+void LLILEmulator::SetPreInstructionHook(const std::function& hook)
+{
+ m_preInstructionHook = hook;
+ BNILEmulatorSetPreInstructionHook(BNLLILEmulatorGetBase(m_object),
+ hook ? (void*)this : nullptr,
+ hook ? PreInstructionHookCallback : nullptr);
+}
+
+
+void LLILEmulator::SetIntrinsicHook(const std::function&, std::vector>&)>& hook)
+{
+ m_intrinsicHook = hook;
+ BNLLILEmulatorSetIntrinsicHook(m_object,
+ hook ? (void*)this : nullptr,
+ hook ? IntrinsicHookCallback : nullptr);
+}
+
+
+void LLILEmulator::SetStdoutCallback(const std::function& cb)
+{
+ m_stdoutCallback = cb;
+ BNILEmulatorSetStdoutCallback(BNLLILEmulatorGetBase(m_object),
+ cb ? (void*)this : nullptr,
+ cb ? StdoutCallbackBridge : nullptr);
+}
+
+
+void LLILEmulator::SetStdinCallback(const std::function& cb)
+{
+ m_stdinCallback = cb;
+ BNILEmulatorSetStdinCallback(BNLLILEmulatorGetBase(m_object),
+ cb ? (void*)this : nullptr,
+ cb ? StdinCallbackBridge : nullptr);
+}
+
+
+// ─── Register / flag / temp access ──────────────────────────────────────────
+
+intx::uint512 LLILEmulator::GetRegister(uint32_t reg) const
+{
+ uint8_t buf[64] = {};
+ BNLLILEmulatorGetRegister(m_object, reg, buf, sizeof(buf));
+ return ApiBytesToUint512(buf, sizeof(buf));
+}
+
+
+void LLILEmulator::SetRegister(uint32_t reg, const intx::uint512& value)
+{
+ uint8_t buf[64];
+ ApiUint512ToBytes(value, buf, sizeof(buf));
+ BNLLILEmulatorSetRegister(m_object, reg, buf, sizeof(buf));
+}
+
+
+intx::uint512 LLILEmulator::GetTempRegister(uint32_t index) const
+{
+ uint8_t buf[64] = {};
+ BNLLILEmulatorGetTempRegister(m_object, index, buf, sizeof(buf));
+ return ApiBytesToUint512(buf, sizeof(buf));
+}
+
+
+void LLILEmulator::SetTempRegister(uint32_t index, const intx::uint512& value)
+{
+ uint8_t buf[64];
+ ApiUint512ToBytes(value, buf, sizeof(buf));
+ BNLLILEmulatorSetTempRegister(m_object, index, buf, sizeof(buf));
+}
+
+
+std::unordered_map LLILEmulator::GetAllTempRegisters() const
+{
+ // Query count first, then fetch
+ size_t count = BNLLILEmulatorGetAllTempRegisters(m_object, nullptr, nullptr, 0);
+ std::unordered_map result;
+ if (count == 0)
+ return result;
+ std::vector indices(count);
+ std::vector values(count * 64);
+ count = BNLLILEmulatorGetAllTempRegisters(m_object, indices.data(), values.data(), count);
+ for (size_t i = 0; i < count; i++)
+ result[indices[i]] = ApiBytesToUint512(values.data() + i * 64, 64);
+ return result;
+}
+
+
+uint8_t LLILEmulator::GetFlag(uint32_t flag) const
+{
+ return BNLLILEmulatorGetFlag(m_object, flag);
+}
+
+
+void LLILEmulator::SetFlag(uint32_t flag, uint8_t value)
+{
+ BNLLILEmulatorSetFlag(m_object, flag, value);
+}
+
+
+// ─── Cross-function state ────────────────────────────────────────────────────
+
+size_t LLILEmulator::GetCallStackDepth() const
+{
+ return BNLLILEmulatorGetCallStackDepth(m_object);
+}
+
+
+std::vector LLILEmulator::GetCallStack() const
+{
+ size_t count = 0;
+ BNEmulatorCallStackEntry* entries = BNLLILEmulatorGetCallStack(m_object, &count);
+ std::vector result;
+ if (entries)
+ {
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back({entries[i].functionAddress, entries[i].returnAddress});
+ BNLLILEmulatorFreeCallStack(entries);
+ }
+ return result;
+}
+
+
+std::vector LLILEmulator::GetMappedRegions() const
+{
+ size_t count = 0;
+ BNEmulatorMemoryRegion* regions = BNILEmulatorGetMappedRegions(BNLLILEmulatorGetBase(m_object), &count);
+ std::vector result;
+ if (regions)
+ {
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.push_back({regions[i].start, regions[i].size, regions[i].name ? regions[i].name : ""});
+ BNFreeEmulatorMemoryRegions(regions, count);
+ }
+ return result;
+}
+
+
+// ─── Built-in libc stub settings ─────────────────────────────────────────────
+
+void LLILEmulator::SetBuiltinLibcStubsEnabled(bool enabled)
+{
+ BNLLILEmulatorSetBuiltinLibcStubsEnabled(m_object, enabled);
+}
+
+
+bool LLILEmulator::IsBuiltinLibcStubsEnabled() const
+{
+ return BNLLILEmulatorIsBuiltinLibcStubsEnabled(m_object);
+}
+
+
+void LLILEmulator::SetLogLibcCalls(bool enabled)
+{
+ BNLLILEmulatorSetLogLibcCalls(m_object, enabled);
+}
+
+
+bool LLILEmulator::IsLogLibcCalls() const
+{
+ return BNLLILEmulatorIsLogLibcCalls(m_object);
+}
+
+
+void LLILEmulator::SetNopUnknownExternals(bool enabled)
+{
+ BNLLILEmulatorSetNopUnknownExternals(m_object, enabled);
+}
+
+
+bool LLILEmulator::IsNopUnknownExternals() const
+{
+ return BNLLILEmulatorIsNopUnknownExternals(m_object);
+}
+
+
+// ─── Reset ───────────────────────────────────────────────────────────────────
+
+void LLILEmulator::Reset()
+{
+ BNILEmulatorReset(BNLLILEmulatorGetBase(m_object));
+}
+
+
+// ─── State serialization ─────────────────────────────────────────────────────
+
+std::string LLILEmulator::SaveState() const
+{
+ char* json = BNLLILEmulatorSaveState(m_object);
+ if (!json)
+ return {};
+ std::string result(json);
+ BNFreeString(json);
+ return result;
+}
+
+
+bool LLILEmulator::LoadState(const std::string& json)
+{
+ return BNLLILEmulatorLoadState(m_object, json.c_str());
+}
diff --git a/plugins/emulator/api/python/CMakeLists.txt b/plugins/emulator/api/python/CMakeLists.txt
new file mode 100644
index 0000000000..c83111ff61
--- /dev/null
+++ b/plugins/emulator/api/python/CMakeLists.txt
@@ -0,0 +1,52 @@
+cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
+
+project(emulator-python-api)
+
+if(BN_API_PATH)
+ include(${BN_API_PATH}/cmake/PythonBindings.cmake)
+else()
+ # api/plugins/emulator/api/python -> api root is four levels up
+ include(${PROJECT_SOURCE_DIR}/../../../../cmake/PythonBindings.cmake)
+endif()
+
+file(GLOB PYTHON_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/*.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_emulatorcore.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/emulator_enums.py)
+
+add_executable(emulator_generator
+ ${PROJECT_SOURCE_DIR}/generator.cpp)
+target_link_libraries(emulator_generator binaryninjaapi)
+
+set_target_properties(emulator_generator PROPERTIES
+ CXX_STANDARD 20
+ CXX_STANDARD_REQUIRED ON
+ BUILD_WITH_INSTALL_RPATH OFF
+ RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+if(BN_INTERNAL_BUILD)
+ set(PYTHON_OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja/emulator/)
+else()
+ set(PYTHON_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins/emulator/)
+endif()
+
+if(WIN32)
+ if (BN_INTERNAL_BUILD)
+ add_custom_command(TARGET emulator_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ else()
+ add_custom_command(TARGET emulator_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_INSTALL_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ endif()
+endif()
+
+generate_python_bindings(
+ TARGET_NAME emulator_generator_copy
+ DISPLAY_NAME "Emulator"
+ GENERATOR_TARGET emulator_generator
+ HEADER_FILE ${PROJECT_SOURCE_DIR}/../ffi.h
+ TEMPLATE_FILE ${PROJECT_SOURCE_DIR}/_emulatorcore_template.py
+ OUTPUT_DIRECTORY ${PYTHON_OUTPUT_DIRECTORY}
+ CORE_OUTPUT_FILE _emulatorcore.py
+ ENUMS_OUTPUT_FILE emulator_enums.py
+ PYTHON_SOURCES ${PYTHON_SOURCES}
+)
diff --git a/plugins/emulator/api/python/__init__.py b/plugins/emulator/api/python/__init__.py
new file mode 100644
index 0000000000..ee6d29d725
--- /dev/null
+++ b/plugins/emulator/api/python/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2020-2026 Vector 35 Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+from binaryninja.settings import Settings
+
+from binaryninja._binaryninjacore import BNGetUserPluginDirectory
+user_plugin_dir = os.path.realpath(BNGetUserPluginDirectory())
+current_path = os.path.realpath(__file__)
+
+# If BN_STANDALONE_EMULATOR is set, only initialize the python module when it is loaded from the user plugin dir
+if os.environ.get('BN_STANDALONE_EMULATOR'):
+ if current_path.startswith(user_plugin_dir):
+ from .ilemulator import *
+ from .emulator_enums import *
+else:
+ if Settings().get_bool('corePlugins.emulator') and (os.environ.get('BN_DISABLE_CORE_EMULATOR') is None):
+ from .ilemulator import *
+ from .emulator_enums import *
diff --git a/plugins/emulator/api/python/_emulatorcore_template.py b/plugins/emulator/api/python/_emulatorcore_template.py
new file mode 100644
index 0000000000..327c1521a0
--- /dev/null
+++ b/plugins/emulator/api/python/_emulatorcore_template.py
@@ -0,0 +1,58 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import emulator_enums
+# Emulator C ABI strings are allocated by the core allocator, so free them with BNFreeString.
+from binaryninja._binaryninjacore import BNFreeString
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+if os.environ.get('BN_STANDALONE_EMULATOR'):
+ from binaryninja._binaryninjacore import BNGetUserPluginDirectory
+ if core_platform == "Darwin":
+ _base_path = BNGetUserPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libemulatorcore.dylib"))
+
+ elif core_platform == "Linux":
+ _base_path = BNGetUserPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libemulatorcore.so"))
+
+ elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetUserPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "emulatorcore.dll"))
+ else:
+ raise Exception("OS not supported")
+else:
+ from binaryninja._binaryninjacore import BNGetBundledPluginDirectory
+ if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libemulatorcore.dylib"))
+
+ elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libemulatorcore.so"))
+
+ elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "emulatorcore.dll"))
+ else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
diff --git a/plugins/emulator/api/python/generator.cpp b/plugins/emulator/api/python/generator.cpp
new file mode 100644
index 0000000000..883b04dc7b
--- /dev/null
+++ b/plugins/emulator/api/python/generator.cpp
@@ -0,0 +1,614 @@
+/*
+Copyright 2020-2026 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+
+#include
+#include
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+map g_pythonKeywordReplacements = {
+ {"False", "False_"},
+ {"True", "True_"},
+ {"None", "None_"},
+ {"and", "and_"},
+ {"as", "as_"},
+ {"assert", "assert_"},
+ {"async", "async_"},
+ {"await", "await_"},
+ {"break", "break_"},
+ {"class", "class_"},
+ {"continue", "continue_"},
+ {"def", "def_"},
+ {"del", "del_"},
+ {"elif", "elif_"},
+ {"else", "else_"},
+ {"except", "except_"},
+ {"finally", "finally_"},
+ {"for", "for_"},
+ {"from", "from_"},
+ {"global", "global_"},
+ {"if", "if_"},
+ {"import", "import_"},
+ {"in", "in_"},
+ {"is", "is_"},
+ {"lambda", "lambda_"},
+ {"nonlocal", "nonlocal_"},
+ {"not", "not_"},
+ {"or", "or_"},
+ {"pass", "pass_"},
+ {"raise", "raise_"},
+ {"return", "return_"},
+ {"try", "try_"},
+ {"while", "while_"},
+ {"with", "with_"},
+ {"yield", "yield_"},
+};
+
+
+void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "ctypes.c_bool");
+ break;
+ case IntegerTypeClass:
+ switch (type->GetWidth())
+ {
+ case 1:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_byte");
+ else
+ fprintf(out, "ctypes.c_ubyte");
+ break;
+ case 2:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_short");
+ else
+ fprintf(out, "ctypes.c_ushort");
+ break;
+ case 4:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_int");
+ else
+ fprintf(out, "ctypes.c_uint");
+ break;
+ default:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_longlong");
+ else
+ fprintf(out, "ctypes.c_ulonglong");
+ break;
+ }
+ break;
+ case FloatTypeClass:
+ if (type->GetWidth() == 4)
+ fprintf(out, "ctypes.c_float");
+ else
+ fprintf(out, "ctypes.c_double");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass))
+ {
+ fprintf(out, "ctypes.c_void_p");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ if (isReturnType)
+ fprintf(out, "ctypes.POINTER(ctypes.c_byte)");
+ else
+ fprintf(out, "ctypes.c_char_p");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType().GetValue(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type.GetValue());
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType().GetValue());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType().GetValue());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+void OutputSwizzledType(FILE* out, Type* type)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "bool");
+ break;
+ case IntegerTypeClass:
+ fprintf(out, "int");
+ break;
+ case FloatTypeClass:
+ fprintf(out, "float");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (type->GetChildType()->GetClass() == VoidTypeClass)
+ {
+ fprintf(out, "Optional[ctypes.c_void_p]");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "Optional[str]");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType().GetValue(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type.GetValue());
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType().GetValue());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType().GetValue());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ if (argc < 5)
+ {
+ fprintf(stderr, "Usage: generator