-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add IDA artifact change watchers and add example plugin (#13)
* Add IDA change watchers and add example plugin * Make the readme better * Fix an initialization bug
- Loading branch information
Showing
15 changed files
with
672 additions
and
483 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# LibBS Examples | ||
This directory contains a series of example uses of LibBS in both plugins and as scripting library utilities. | ||
When used as a plugin, LibBS requires a bit more setup to both init the UI components and start the artifact | ||
watching backend. | ||
|
||
## Plugins | ||
### change_watcher_plugins | ||
This plugin shows off a few things: | ||
1. Passing a generic function to be called on Artifact changes | ||
2. Initing a context menu in any decompiler | ||
3. Generally setting up a plugin as a package with its own installer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Example BS Change Watcher Plugin | ||
The example plugin to show of LibBS for watching artifact changes. | ||
|
||
## Install | ||
``` | ||
pip3 install -e . && python3 -m bs_change_watcher --install | ||
``` | ||
|
||
## Usage | ||
Open the decompiler: | ||
1. If you are in Ghidra, use the menu to start the BS backend first | ||
2. Right click on any function and select the `ArtifactChangeWatcher` and start the change watcher backend | ||
3. Change any stack variable (as an example), you should see a printout that it was changed |
89 changes: 89 additions & 0 deletions
89
examples/change_watcher_plugin/bs_change_watcher/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
from pathlib import Path | ||
|
||
from libbs.plugin_installer import LibBSPluginInstaller | ||
|
||
|
||
def create_plugin(*args, **kwargs): | ||
""" | ||
This is the entry point that all decompilers will call in various ways. To remain agnostic, | ||
always pass the args and kwargs to the ui_init_args and ui_init_kwargs of DecompilerInterface, inited | ||
through the discover api. | ||
""" | ||
|
||
from libbs.api import DecompilerInterface | ||
from libbs.data import ( | ||
FunctionHeader, StackVariable, Enum, Struct, GlobalVariable, Comment | ||
) | ||
|
||
generic_printer = lambda *x, **y: print(f"Changed {x}{y}") | ||
callback_handlers = { | ||
typ: [generic_printer] for typ in (FunctionHeader, StackVariable, Enum, Struct, GlobalVariable, Comment,) | ||
} | ||
deci = DecompilerInterface.discover_interface( | ||
plugin_name="ArtifactChangeWatcher", | ||
init_plugin=True, | ||
artifact_write_callbacks=callback_handlers, | ||
ui_init_args=args, | ||
ui_init_kwargs=kwargs | ||
) | ||
|
||
# register a ctx_menu_item late since we want the callback to be inside the deci | ||
deci.register_ctx_menu_item( | ||
"StartArtifactChangeWatcher", | ||
"Start watching artifact changes", | ||
lambda: deci.start_artifact_watchers(), | ||
category="ArtifactChangeWatcher" | ||
) | ||
|
||
# return a plugin so the decompiler sets up the UI | ||
return deci.gui_plugin | ||
|
||
|
||
class BSChangeWatcherInstaller(LibBSPluginInstaller): | ||
""" | ||
This acts as a simple installer for the plugin | ||
""" | ||
|
||
def __init__(self): | ||
super().__init__() | ||
self.pkg_path = self.find_pkg_files("bs_change_watcher") | ||
|
||
def _copy_plugin_to_path(self, path): | ||
src = self.pkg_path / "bs_change_watcher_plugin.py" | ||
dst = Path(path) / "bs_change_watcher_plugin.py" | ||
self.link_or_copy(src, dst, symlink=True) | ||
|
||
def display_prologue(self): | ||
print("Now installing BSChangeWatcher plugin...") | ||
|
||
def install_ida(self, path=None, interactive=True): | ||
path = super().install_ida(path=path, interactive=interactive) | ||
if not path: | ||
return | ||
|
||
self._copy_plugin_to_path(path) | ||
return path | ||
|
||
def install_ghidra(self, path=None, interactive=True): | ||
path = super().install_ghidra(path=path, interactive=interactive) | ||
if not path: | ||
return | ||
|
||
self._copy_plugin_to_path(path) | ||
return path | ||
|
||
def install_binja(self, path=None, interactive=True): | ||
path = super().install_binja(path=path, interactive=interactive) | ||
if not path: | ||
return | ||
|
||
self._copy_plugin_to_path(path) | ||
return path | ||
|
||
def install_angr(self, path=None, interactive=True): | ||
path = super().install_angr(path=path, interactive=interactive) | ||
if not path: | ||
return | ||
|
||
self._copy_plugin_to_path(path) | ||
return path |
26 changes: 26 additions & 0 deletions
26
examples/change_watcher_plugin/bs_change_watcher/__main__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import argparse | ||
|
||
from . import BSChangeWatcherInstaller, create_plugin | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description="An example CLI for the example change watcher plugin") | ||
parser.add_argument( | ||
"-i", "--install", action="store_true", help="Install plugin into your decompilers" | ||
) | ||
parser.add_argument( | ||
"-s", "--server", help="Run a a headless server for the watcher plugin", choices=["ghidra"] | ||
) | ||
args = parser.parse_args() | ||
|
||
if args.install: | ||
BSChangeWatcherInstaller().install() | ||
elif args.server: | ||
if args.server != "ghidra": | ||
raise NotImplementedError("Only Ghidra is supported for now") | ||
|
||
create_plugin(force_decompiler="ghidra") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
47 changes: 47 additions & 0 deletions
47
examples/change_watcher_plugin/bs_change_watcher/bs_change_watcher_plugin.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# An example LibBS plugin that will print when every artifact is changed inside the decompiler | ||
# @author BinSync | ||
# @category BinSync | ||
# @menupath Tools.ArtifactChangeWatcher.Start the BS backed for watcher | ||
|
||
library_command = "bs_change_watcher --run" | ||
def create_plugin(*args, **kwargs): | ||
from bs_change_watcher import create_plugin as _create_plugin | ||
return _create_plugin(*args, **kwargs) | ||
|
||
|
||
# ============================================================================= | ||
# LibBS generic plugin loader (don't touch) | ||
# ============================================================================= | ||
|
||
import sys | ||
# Python 2 has special requirements for Ghidra, which forces us to use a different entry point | ||
# and scope for defining plugin entry points | ||
if sys.version[0] == "2": | ||
# Do Ghidra Py2 entry point | ||
import subprocess | ||
from libbs_vendored.ghidra_bridge_server import GhidraBridgeServer | ||
full_command = "python3 -m " + library_command | ||
|
||
GhidraBridgeServer.run_server(background=True) | ||
process = subprocess.Popen(full_command.split(" ")) | ||
if process.poll() is not None: | ||
raise RuntimeError( | ||
"Failed to run the Python3 backed. It's likely Python3 is not in your Path inside Ghidra.") | ||
else: | ||
# Try plugin discovery for other decompilers | ||
try: | ||
import idaapi | ||
has_ida = True | ||
except ImportError: | ||
has_ida = False | ||
|
||
if not has_ida: | ||
create_plugin() | ||
|
||
|
||
def PLUGIN_ENTRY(*args, **kwargs): | ||
""" | ||
This is the entry point for IDA to load the plugin. | ||
""" | ||
print("[+] Loading callback_watcher plugin (1/2)") | ||
return create_plugin(*args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[metadata] | ||
name = bs_change_watcher | ||
version = 0.0.0 | ||
url = https://github.com/binsync/libbs/tree/main/examples/change_watcher_plugin | ||
classifiers = | ||
License :: OSI Approved :: BSD License | ||
Programming Language :: Python :: 3 | ||
Programming Language :: Python :: 3.8 | ||
license = BSD 2 Clause | ||
description = An example plugin using LibBS to watch and report changes to artifacts | ||
long_description = file: README.md | ||
long_description_content_type = text/markdown | ||
|
||
[options] | ||
install_requires = | ||
libbs | ||
|
||
python_requires = >= 3.8 | ||
include_package_data = True | ||
packages = find: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from setuptools import setup | ||
setup() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from libbs.api import DecompilerInterface | ||
|
||
deci = DecompilerInterface.discover_interface() | ||
for function in deci.functions: | ||
if function.header.type == "void *": | ||
function.header.type = "long long" | ||
|
||
deci.functions[function.addr] = function |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# REPLACE_ME: with the description of the plugin you want displayed in Ghidra, and update below items | ||
# @author YourNameHere | ||
# @category YourCategoryHere | ||
# @menupath Tools.MyPlugin.Replace me with short desc shown in Tools>MyPlugin menu | ||
|
||
# REPLACE_ME: replace the command to run your plugin from Ghidra Python2 side | ||
library_command = "my_library_name --run" | ||
|
||
|
||
def create_plugin(*args, **kwargs): | ||
# REPLACE_ME this import with an import of your plugin's create_plugin function | ||
from my_library_name import create_plugin as _create_plugin | ||
return _create_plugin(*args, **kwargs) | ||
|
||
# ============================================================================= | ||
# LibBS generic plugin loader (don't touch things below this) | ||
# ============================================================================= | ||
|
||
import sys | ||
# Python 2 has special requirements for Ghidra, which forces us to use a different entry point | ||
# and scope for defining plugin entry points | ||
if sys.version[0] == "2": | ||
# Do Ghidra Py2 entry point | ||
import subprocess | ||
from libbs_vendored.ghidra_bridge_server import GhidraBridgeServer | ||
full_command = "python3 -m " + library_command | ||
|
||
GhidraBridgeServer.run_server(background=True) | ||
process = subprocess.Popen(full_command.split(" ")) | ||
if process.poll() is not None: | ||
raise RuntimeError( | ||
"Failed to run the Python3 backed. It's likely Python3 is not in your Path inside Ghidra.") | ||
else: | ||
# Try plugin discovery for other decompilers | ||
try: | ||
import idaapi | ||
has_ida = True | ||
except ImportError: | ||
has_ida = False | ||
|
||
if not has_ida: | ||
create_plugin() | ||
|
||
|
||
def PLUGIN_ENTRY(*args, **kwargs): | ||
""" | ||
This is the entry point for IDA to load the plugin. | ||
""" | ||
return create_plugin(*args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "0.6.11" | ||
__version__ = "0.7.0" |
Oops, something went wrong.