summaryrefslogtreecommitdiff
path: root/docs/dev/plugins.md
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2023-04-27 20:10:27 -0400
committerGlenn Smith <glenn@vector35.com>2023-05-01 14:26:11 -0400
commit7191658282315521220137779383046212337768 (patch)
tree9f60961a3966f0b099284142438163be0a3c15cc /docs/dev/plugins.md
parent5d3e98685591b2521f8dc3151d9a3c36095401b6 (diff)
Add documentation for writing native plugins
Diffstat (limited to 'docs/dev/plugins.md')
-rw-r--r--docs/dev/plugins.md270
1 files changed, 267 insertions, 3 deletions
diff --git a/docs/dev/plugins.md b/docs/dev/plugins.md
index 7907cd3b..53263ccc 100644
--- a/docs/dev/plugins.md
+++ b/docs/dev/plugins.md
@@ -76,16 +76,280 @@ If you wish to debug your python scripts, there are a few methods specific to di
# Writing Native Plugins
-Unfortunately, native plugins are not supported in the plugin manager at this time. It's possible to work-around this limitation by pre-building for all three platforms a native plugin and using a python plugin that acts as a loader for the native plugin, but no such examples exist.
+Writing native plugins allows for higher performance code and lower level access to the Binary Ninja API, but comes with a couple more hurdles than Python.
+Notably, native plugins are built against a specific version of the API, cannot be hot-reloaded, and require more sophisticated build setups.
-## Matching API versions
+## Supported Toolchains
-One issue with building a native plugin is that you'll want your plugin to be able to match the API of a given dev build. This information is contained in a file named `api_REVISION.txt` that exists in the root install folder for Linux, the `Contents/Resources` sub-folder on MacOS, and the root installation folder on Windows.
+When building native plugins for Binary Ninja, the following toolchains and dependencies are required, based on host OS.
+Older versions may work but are not supported.
+
+- macOS: Xcode 13+
+- Windows: VS 2019 Professional with C/C++ Native Tools package
+- Linux: GCC 9.4+
+
+Additionally, Binary Ninja uses C++17 features, and requires a C++17 compatible compiler.
+
+## CMake Setup
+
+Binary Ninja uses the [CMake](https://cmake.org/) build system generator to compile native code, and provides
+convenient helper scripts for those making plugins. As of writing, CMake 3.13 or greater is required,
+although it is recommended to use the latest version.
+
+## Project Setup
+
+The first things to specify in your CMake file are a couple boilerplate options for building C++:
+
+ # Pick whatever version you have
+ cmake_minimum_required(VERSION 3.24)
+
+ # Name your plugin
+ project(TestPlugin CXX)
+
+ set(CMAKE_CXX_STANDARD 17)
+
+ # Unless you are writing a plugin that needs Qt's UI, specify this
+ set(HEADLESS 1)
+
+Then you want to get the matching API repository for the version of Binary Ninja you have.
+This information is contained in a file named `api_REVISION.txt` that exists in the root install folder for Linux,
+the `Contents/Resources` sub-folder on macOS, and the root installation folder on Windows.
+
+Once you know which revision to use, you can clone a copy of the binaryninja-api repository
+and reference it directly in your plugin. If you're using git, this can be accomplished easily using a submodule:
+
+ git submodule add https://github.com/Vector35/binaryninja-api.git binaryninjaapi
+ cd binaryninjaapi
+ # Pick the revision from api_REVISION.txt
+ git checkout 6466fba3341b2ea7dbfceeeebbc6c0322a5d8514
+
+If you're not using git, you can clone the repository elsewhere:
+
+ git clone https://github.com/Vector35/binaryninja-api.git binaryninjaapi
+ cd binaryninjaapi
+ # Pick the revision from api_REVISION.txt
+ git checkout 6466fba3341b2ea7dbfceeeebbc6c0322a5d8514
+
+Now that you have the correct copy of the api, you need to point CMake at it and include it for use.
+Include something like the following in your CMake script and either add the path of your clone
+to the HINTS list or set the BN_API_PATH environment variable to the location of your clone
+
+ find_path(
+ BN_API_PATH
+ NAMES binaryninjaapi.h
+ # List of paths to search for the clone of the api
+ HINTS ../.. binaryninjaapi $ENV{BN_API_PATH}
+ REQUIRED
+ )
+ add_subdirectory(${BN_API_PATH} api)
+
+Be sure to create a shared library and link against the Binary Ninja api. Also, you can use the
+`bn_install_plugin` helper to automatically set up your plugin to install to the Binary Ninja plugins directory
+when you use `cmake install`.
+
+ # Use whichever sources and plugin name you want
+ add_library(TestPlugin SHARED TestPlugin.cpp)
+
+ # Link with Binary Ninja
+ target_link_libraries(TestPlugin PUBLIC binaryninjaapi)
+
+ # Tell `cmake --install` to copy your plugin to the plugins directory
+ bn_install_plugin(TestPlugin)
+
+From there you can write the rest of your plugin's CMake configuration, including any other dependencies or
+options that you want. When you want to run your plugin, you can use `cmake --build` and `cmake --install`
+to compile and copy your plugin to your Binary Ninja plugins directory, or set up an IDE to do that for you.
+You could also copy the plugin manually if you are using a different plugins directory location.
+
+In the source code of your plugin, you will need to export some functions that Binary Ninja uses to load your plugin
+at runtime:
+
+ #include "binaryninjaapi.h"
+ extern "C" {
+ // Tells Binary Ninja which version of the API you compiled against
+ BN_DECLARE_CORE_ABI_VERSION
+
+ // Function run on plugin startup, do simple initialization here (Settings, BinaryViewTypes, etc)
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ return true;
+ }
+
+ // (Optional) Function to add other plugin dependencies in case your plugin requires them
+ BINARYNINJAPLUGIN void CorePluginDependencies()
+ {
+ // For example, if you require the x86 to be loaded before your plugin
+ AddRequiredPluginDependency("arch_x86");
+ }
+ }
+
+From there, you can implement your plugin functionality as you desire. I highly recommend looking at other plugins for
+API usage since the C++ API is less well-documented than the Python API. Usually the functions and classes are named
+identically, but you may find some outliers. Also, C++ has a way more difficult task of managing memory, since there is
+no garbage collector to handle it for you. Generally speaking, most API objects are reference-counted via the `Ref<T>`
+class, and you should only ever handle Refs or bare pointers. When in doubt, feel free to ask on
+[our Slack](https://binaryninja.slack.com/) and both our team and helpful community can assist.
+
+### UI Plugins
+
+If you want to include UI in your plugin, you can integrate with Binary Ninja's Qt-based UI by linking with Qt and `binaryninjaui`.
+You will need to use the same version of Qt as Binary Ninja. We provide steps for building it [here](../about/open-source.md#building-qt),
+or you can attempt to use a system-provided copy if you use Linux and like to live dangerously.
+Building it is a bit of a process, but should provide you with a working installation. Once you have a Qt build,
+you can amend your CMake file to make a UI plugin. You will need the following CMake:
+
+ # Remove this or set to 0
+ # set(HEADLESS 1)
+
+ # If you are using Qt MOC (i.e. use Q_OBJECT/Q_SIGNALS/Q_SLOTS)
+ set(CMAKE_AUTOMOC ON)
+ set(CMAKE_AUTORCC ON)
+
+ # Locate Qt installation for linking
+ find_package(Qt6 COMPONENTS Core Gui Widgets REQUIRED)
+
+ # Add MOCS to your build
+ add_library(TestPlugin SHARED library.cpp ${MOCS})
+
+ # Link against both binaryninjaapi/binaryninjaui and Qt6
+ target_link_libraries(TestPlugin PUBLIC binaryninjaapi binaryninjaui Qt6::Core Qt6::Gui Qt6::Widgets)
+
+Then, in your plugin code, instead of using the exported functions for a core plugin, use the ones for a UI plugin:
+
+ #include "binaryninjaapi.h"
+ #include "uitypes.h"
+ #include "uicontext.h"
+
+ extern "C" {
+ // Tells Binary Ninja which version of the API you compiled against
+ BN_DECLARE_UI_ABI_VERSION
+
+ // Function run on plugin startup, do simple initialization here (ViewTypes, SidebarWidgetTypes, etc)
+ BINARYNINJAPLUGIN bool UIPluginInit()
+ {
+ return true;
+ }
+
+ // (Optional) Function to add other plugin dependencies in case your plugin requires them
+ // Historically, these have never actually been used
+ BINARYNINJAPLUGIN void UIPluginDependencies()
+ {
+ // For example, if you require triage view to be loaded before your plugin
+ AddRequiredUIPluginDependency("triage");
+ }
+ }
+
+From there, you can implement whatever wacky Qt user interfaces you dream up. Be warned that the Binary Ninja UI API is
+rather poorly documented and often missing helper functions for use by plugins. Feel free to ask for assistance and
+suggestions, but know that it's very easy to run into memory bugs when working with Qt. I would recommend looking at
+the source to [the debugger](https://github.com/vector35/debugger), as an example of the largest, best-maintained UI
+plugin for Binary Ninja.
+
+### Python Integration
+
+If you want your C++ plugin to also support a Python API, you will have a lot of work to do. Generally speaking, there
+are no cookie-cutter solutions to this problem, but there is a general strategy:
+
+1. Expose a C API from your plugin
+2. Provide a set of Python bindings to that C API
+3. Load those Python bindings as a Python plugin in Binary Ninja
+
+Again, I'm going to point out [the debugger](https://github.com/vector35/debugger) as a fantastic example of how to
+implement this. Generally speaking, you will either need to write both sides of the FFI in a similar way, or you may
+be able to find a library that does that for you. Possibly [libffi](https://sourceware.org/libffi/), although there
+aren't any examples of using it for Binary Ninja specifically. If you manage to get something working, let us know!
+We would love to see more complex plugins with extensible behavior!
+
+## IDE Setup
+
+### CLion
+
+CLion is generally pretty good at handling CMake projects. Given the above CMake configuration, it can
+automatically detect the plugin target and will compile and install correctly. Here are a a few steps to finish
+setup for building and live debugging your plugin:
+
+1. If you installed Binary Ninja somewhere other than the default, add an environment variable in your CMake Profile pointing at the installation, e.g.: `BN_INSTALL_DIR=/Applications/Binary Ninja.app`
+2. If you are writing a UI plugin, you will need to include the directory containing `qmake` to the `PATH` Environment Variable in your CMake Profile, e.g.: `PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Users/user/Qt/6.4.3/clang_64/bin`
+3. In your Run Configuration's Before Launch steps, add an Install step. This will copy the updated version of your plugin before starting, so you don't have to run Install manually.
+4. Set the Executable of your Run Configuration to point to the Binary Ninja executable. This allows you to compile your plugin and start Binary Ninja automatically.
+ i. On macOS, you will need the full path to /Applications/Binary Ninja.app/Contents/MacOS/binaryninja
+5. (Optionally) Add the `-e` flag to the Program Arguments to get error logs printed to your console
+6. (Optionally) Add the `-e -d` flags to the Program Arguments to get debug logs printed to your console. This may slow down Binary Ninja (and CLion) due to the large volume of logs produced.
+7. (Optionally) Add the `-l /tmp/bn_out.txt` flags to the Program Arguments so your logs also get printed to a text file when you inevitably fill up the Console buffer in CLion and want to see what happened.
+8. (Optionally on macOS) Add the Environment Variables `MallocScribble=1` and `MallocPreScribble=1` to make memory errors easier to spot.
+
+### Visual Studio Code
+
+VSCode takes a bit of configuration to set up, but can build and debug plugins efficiently once ready.
+You can install the C/C++ extension, the CMake extension, and the CMake Tools extension.
+You need to set up a task in `.vscode/tasks.json` to build and install your plugin. Something like this:
+
+ // tasks.json
+ {
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "cmake",
+ "label": "CMake: install",
+ "command": "install",
+ "problemMatcher": [],
+ "detail": "CMake template install task",
+ "options": {
+ "environment": {
+ // You will need this if your Binary Ninja installation is not in the default location
+ "BN_INSTALL_DIR": "C:\\Users\\User\\AppData\\Local\\Vector35\\BinaryNinja",
+ // You will need this if you are writing a UI plugin
+ "PATH": "C:\\Users\\User\\Qt\\6.4.3\\msvc2019_64\\bin"
+ }
+ }
+ }
+ ]
+ }
+
+You will also want to set up a launch task in `.vscode/launch.json` to launch Binary Ninja in a debugger,
+so you can debug your plugin.
+Be sure to set `"preLaunchTask"` to use the `CMake: install` task created above, so your code updates will be
+built and installed automatically before you start debugging.
+
+ // launch.json
+ {
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(Windows) Launch",
+ "type": "cppvsdbg",
+ "request": "launch",
+ "program": "C:\\Users\\User\\AppData\\Local\\Vector35\\BinaryNinja\\binaryninja.exe",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "C:\\Users\\User\\AppData\\Local\\Vector35\\BinaryNinja",
+ "environment": [],
+ "console": "externalTerminal",
+ "preLaunchTask": "CMake: install"
+ }
+ ]
+ }
+
+There are a few other options you can use to assist in debugging:
+
+1. (Optionally) Add the `"-e"` flag to the launch configuration's `args` to get error logs printed to your console
+2. (Optionally) Add the `"-e", "-d"` flags to the launch configuration's `args` to get debug logs printed to your console. This may slow down Binary Ninja (and VSCode) due to the large volume of logs produced.
+3. (Optionally) Add the `"-l", "/tmp/bn_out.txt"` flags to the launch configuration's `args` so your logs also get printed to a text file when you inevitably fill up the Console buffer and want to see what happened.
+4. (Optionally on macOS) Add the environment variables `{ "name": "MallocScribble", "value": "1" }` and `{ "name": "MallocPreScribble", "value": "1" }` to make memory errors easier to spot.
+
+As a footnote, it should be noted that most of the team at Vector 35 use VSCode as a bare text editor
+and use command-line lldb or gdb to debug their code. Shout-outs to people trying to get this working in Vim.
+
+## Submitting to the plugin manager
+
+While native plugins are not supported in the plugin manager at this time, it's possible to work around this limitation by pre-building a native plugin for all three platforms and using a python plugin that acts as a loader for the native plugin. There aren't any good examples of automated workflows for this, but [binexport](https://github.com/google/binexport) has supposedly got it working. But given that the people you're distributing your plugin to may have a different version of the API, you will likely want to just distribute the source code and build setup, and have them build against whatever version of Binary Ninja they have installed.
## Examples
Several native plugin examples exist:
+ - [Triage](https://github.com/Vector35/binaryninja-api/tree/dev/examples/triage)
+ - [Debugger](https://github.com/Vector35/debugger)
- [ObjectiveNinja](https://github.com/jonpalmisc/ObjectiveNinja)
- [BinExport](https://github.com/google/binexport#binary-ninja) (Used with BinDiff)
- [Binliner](https://github.com/holmesmr/binliner)