Writing Plugins¶
Writing Python Plugins¶
Creating the Plugin¶
First, take a look at someexample plugins, or some of thecommunity plugins to get a feel for different APIs you might be interested in. Of course, the fullAPI docs are online and available offline via theHelp/Open Python API Reference....
To start, we suggest you download thesample plugin as a template since it contains all the elements you're likely to need.
- Begin by editing the
plugin.jsonfile - Next, update the
LICENSE - For small scripts, you can include all the code inside
__init__.py, though we recommend for larger scripts that__init__.pyjust act as an initializer and calls into functions organized appropriately in other files. - If you have python dependencies, create arequirements.txt listing any python dependencies.
Submitting to the Plugin Manager¶
If your plugin was created as described above, there's only two steps to get it submitted to the plugin manager!
- First, create a release eithermanually or using ourrelease helper.
- Next, justfile an issue letting us know about your plugin.
For future releases all you need to do is increment the version and create a new release.
Using Your Own Plugin Repository¶
The simplest way to run your own plugin repository is to duplicate the structure ofhttps://github.com/vector35/community-plugins. Specifically, theplugins.json, aslisting.json is used along withgenerate_index.py to create that file.
Once you've created your test repository, use thepluginManager.unofficialName andpluginManager.unofficialUrl settings to add your third-party repository.
Theadd_repository API can also be used to add the repository, though itmay require manual creation of the repository folder.
Testing¶
It's useful to be able to reload your plugin during testing. On the Commercial or Ultimate editions of Binary Ninja, this is easily accomplished with a stand-alone headless install usingimport binaryninja afterinstalling the API. (install_api.py is included in each platform's respectiveinstallation folder)
Additionally, some plugin types like Architectures or BinaryViews are only loaded at launch and cannot be reloaded during a running session.
For other plugins, we recommend the following workflow from the scripting console which enables easy iteration and testing:
importpluginnameimportimportlibimportlib.reload(pluginname);pluginname.callbackmethod(bv)Then just[UP] [ENTER] to trigger a reload when the plugin has changed.
Writing plugins using other IDEs (tab completion)¶
Even though non-commercial licenses don't have headless automation, theinstall API script (which is included in the installation directory) allows you to add the binaryninja module to your python environment. Once you do that, you should get automatic completion in any editor that supports it even on non-commercial! Of course, on Commercial and Ultimate installations, the script is even more useful, allowing for headless scripts with your existing python interpreter.
Debugging using other IDEs¶
If you wish to debug your python scripts, there are a few methods specific to different IDEs:
Remote debugging with VSCode:¶
- Run
pip install --user debugpyin the Python interpreter you have selected in Binary Ninja Settings. - In VSCode, open the Run and Debug sidebar.
- Create a
launch.jsonfile if one does not already exist, or openlaunch.jsonif one does. - In
launch.json, select Add Configuration > Python > Remote Attach - Enter a host of
localhostand any port - Set the path mapping to be from
/to/(Windows:C:\\toC:\\) - Open Binary Ninja
- Use
connect_vscode_debugger(port=12345)in the Python Console, using whichever port you selected inlaunch.json. - In VSCode, start debugging. You should see the bottom toolbar change color, and the debugger should be attached.
Remote debugging with IntelliJ PyCharm¶
WARNING: Does not work on PyCharm Community, requires PyCharm Professional
- In PyCharm, add a Run Configuration for Python Debug Server. Give it a name and choose a port and host.
- Run the
pip installscript displayed in the Run Configuration using whichever python interpreter you have selected for Binary Ninja. - In PyCharm, start debugging. You should see "Waiting for process connection..." in the Debugger panel.
- Open Binary Ninja
- Use
connect_pycharm_debugger(port=12345)in the Python Console, using whichever port you selected in the Run Configuration. You should now see "Connected" in the PyCharm Debugger panel.
UI Plugins¶
Binary Ninja UI plugins should alwaysimport binaryninjaui before animport PySide6. Not only does this make sure the correct PySide6 is loaded (running the wrong version of PySide6 can result in crashing), but thisprevents plugins from running headlessly.
UI plugins can take many forms. Some, likeSnippets create their own UI elements and interact via UIActions. Others extend the UI via existing UI elements such asTriage,Kaitai,hellosidebar, orhelloglobalarea.
Many otherthird-party plugins also implement UI based examples, such asBNIL Graph using the FlowGraph APIs.
Unfortunately, due to a PySide documentation generation issue, the best and most reliable documentation on the UI system is not in the regularpython API docs, but in theC++ documentation which translates fairly cleanly to their python equivalent.
Writing Native Plugins¶
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.
Supported Toolchains¶
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 15 or Command Line Tools for macOS 14 (Apple Clang 15.0.0)
- Windows: VS 2022 Professional with C/C++ Native Tools package, v143 (14.34)
- Linux: GCC 11.4+
Additionally, Binary Ninja uses C++20 features, and requires a C++20 compatible compiler.
CMake Setup¶
Binary Ninja uses theCMake build system generator to compile native code, and providesconvenient 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 havecmake_minimum_required(VERSION3.24)# Name your pluginproject(TestPluginCXX)set(CMAKE_CXX_STANDARD20)# Unless you are writing a plugin that needs Qt's UI, specify thisset(HEADLESS1)Then you want to get the matching API repository for the version of Binary Ninja you have.This information is contained in a file namedapi_REVISION.txt that exists in the root install folder for Linux,theContents/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 repositoryand 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 binaryninjaapicd binaryninjaapi# Pick the revision from api_REVISION.txtgit checkout 6466fba3341b2ea7dbfceeeebbc6c0322a5d8514If you're not using git, you can clone the repository elsewhere:
git clone https://github.com/Vector35/binaryninja-api.git binaryninjaapicd binaryninjaapi# Pick the revision from api_REVISION.txtgit checkout 6466fba3341b2ea7dbfceeeebbc6c0322a5d8514Now 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 cloneto the HINTS list or set the BN_API_PATH environment variable to the location of your clone
find_path(BN_API_PATHNAMESbinaryninjaapi.h#ListofpathstosearchforthecloneoftheapiHINTS../..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 thebn_install_plugin helper to automatically set up your plugin to install to the Binary Ninja plugins directorywhen you usecmake install.
#Usewhicheversourcesandpluginnameyouwantadd_library(TestPluginSHAREDTestPlugin.cpp)#LinkwithBinaryNinjatarget_link_libraries(TestPluginPUBLICbinaryninjaapi)#Tell`cmake--install`tocopyyourplugintothepluginsdirectorybn_install_plugin(TestPlugin)From there you can write the rest of your plugin's CMake configuration, including any other dependencies oroptions that you want. When you want to run your plugin, you can usecmake --build andcmake --installto 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 pluginat runtime:
#include "binaryninjaapi.h"extern"C"{//TellsBinaryNinjawhichversionoftheAPIyoucompiledagainstBN_DECLARE_CORE_ABI_VERSION//Functionrunonpluginstartup,dosimpleinitializationhere(Settings,BinaryViewTypes,etc)BINARYNINJAPLUGINboolCorePluginInit(){returntrue;}//(Optional)FunctiontoaddotherplugindependenciesincaseyourpluginrequiresthemBINARYNINJAPLUGINvoidCorePluginDependencies(){//Forexample,ifyourequirethex86tobeloadedbeforeyourpluginAddRequiredPluginDependency("arch_x86");}}From there, you can implement your plugin functionality as you desire. I highly recommend looking at other plugins forAPI usage since the C++ API is less well-documented than the Python API. Usually the functions and classes are namedidentically, but you may find some outliers. Also, C++ has a way more difficult task of managing memory, since there isno garbage collector to handle it for you. Generally speaking, most API objects are reference-counted via theRef<T>class, and you should only ever handle Refs or bare pointers. When in doubt, feel free to ask onour Slack and both our team and helpful community can assist.
Automated GitHub CI¶
Managing build infrastructure to build cross-platform native plugins can be a headache even for stables, let alonetrying to track all dev releases. To help with that, we've published anexample plugin that includes GitHubactions to build on MacOS, Linux, and Windows. Combining this with something like aplugin loader can simplifyboth publishing and using native plugins.
UI Plugins¶
If you want to include a UI in your plugin, you can integrate with Binary Ninja's Qt-based UI by linking with Qt andbinaryninjaui.You will need to use the same version of Qt as Binary Ninja. We provide steps for building ithere,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:
#Removethisorsetto0#set(HEADLESS1)#IfyouareusingQtMOC(i.e.useQ_OBJECT/Q_SIGNALS/Q_SLOTS)set(CMAKE_AUTOMOCON)set(CMAKE_AUTORCCON)#LocateQtinstallationforlinkingfind_package(Qt6COMPONENTSCoreGuiWidgetsREQUIRED)#AddMOCStoyourbuildadd_library(TestPluginSHAREDlibrary.cpp${MOCS})#Linkagainstbothbinaryninjaapi/binaryninjauiandQt6target_link_libraries(TestPluginPUBLICbinaryninjaapibinaryninjauiQt6::CoreQt6::GuiQt6::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"{//TellsBinaryNinjawhichversionoftheAPIyoucompiledagainstBN_DECLARE_UI_ABI_VERSION//Functionrunonpluginstartup,dosimpleinitializationhere(ViewTypes,SidebarWidgetTypes,etc)BINARYNINJAPLUGINboolUIPluginInit(){returntrue;}//(Optional)Functiontoaddotherplugindependenciesincaseyourpluginrequiresthem//Historically,thesehaveneveractuallybeenusedBINARYNINJAPLUGINvoidUIPluginDependencies(){//Forexample,ifyourequiretriageviewtobeloadedbeforeyourpluginAddRequiredUIPluginDependency("triage");}}From there, you can implement whatever wacky Qt user interfaces you dream up. Be warned that the Binary Ninja UI API israther poorly documented and often missing helper functions for use by plugins. Feel free to ask for assistance andsuggestions, but know that it's very easy to run into memory bugs when working with Qt. I would recommend looking atthe source tothe debugger, as an example of the largest, best-maintained UIplugin 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, thereare no cookie-cutter solutions to this problem, but there is a general strategy:
- Expose a C API from your plugin
- Provide a set of Python bindings to that C API
- Load those Python bindings as a Python plugin in Binary Ninja
Again, I'm going to point outthe debugger as a fantastic example of how toimplement this. Generally speaking, you will either need to write both sides of the FFI in a similar way, or you maybe able to find a library that does that for you. Possiblylibffi, although therearen'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 canautomatically detect the plugin target and will compile and install correctly. Here are a few steps to finishsetup for building and live debugging your plugin:
- 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 - If you are writing a UI plugin, you will need to include the directory containing
qmaketo thePATHEnvironment Variable in your CMake Profile, e.g.:PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Users/user/Qt/6.8.2/clang_64/bin - 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.
- 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
- (Optionally) Add the
-eflag to the Program Arguments to get error logs printed to your console - (Optionally) Add the
-e -dflags 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. - (Optionally) Add the
-l /tmp/bn_out.txtflags 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. - (Optionally on macOS) Add the Environment Variables
MallocScribble=1andMallocPreScribble=1to 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.8.2\\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 theCMake: install task created above, so your code updates will bebuilt 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:
- (Optionally) Add the
"-e"flag to the launch configuration'sargsto get error logs printed to your console - (Optionally) Add the
"-e", "-d"flags to the launch configuration'sargsto get debug logs printed to your console. This may slow down Binary Ninja (and VSCode) due to the large volume of logs produced. - (Optionally) Add the
"-l", "/tmp/bn_out.txt"flags to the launch configuration'sargsso your logs also get printed to a text file when you inevitably fill up the Console buffer and want to see what happened. - (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 editorand 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 fully 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. Additionally, you can submit a plugin as "view_only" which helps with discoverability.
Examples¶
Several native plugin examples exist:
- Triage
- Debugger
- ObjectiveNinja
- BinExport (Used with BinDiff)
- Binliner
Contributing to Official Plugins¶
There are many official plugins released as open source. Python ones are included in theofficial plugin repository,native architectures are available on GitHub along with several others that are included with the default product such as thedebugger, theviews,platforms, and somerust plugins.
The first time you contribute, you'll be asked to sign aCLA automatically bycla-assistant. Further commits after the first should not require any changes.