summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h16
-rw-r--r--binaryninjacore.h8
-rw-r--r--plugin.cpp64
-rw-r--r--python/plugin.py90
-rw-r--r--rust/src/command.rs65
-rw-r--r--ui/action.h6
-rw-r--r--ui/projectbrowser.h1
7 files changed, 220 insertions, 30 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 5b435cf8..bab743d3 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -14757,6 +14757,7 @@ namespace BinaryNinja {
Ref<LowLevelILFunction> lowLevelILFunction;
Ref<MediumLevelILFunction> mediumLevelILFunction;
Ref<HighLevelILFunction> highLevelILFunction;
+ Ref<Project> project;
PluginCommandContext();
};
@@ -14833,6 +14834,12 @@ namespace BinaryNinja {
std::function<bool(BinaryView*, const HighLevelILInstruction&)> isValid;
};
+ struct RegisteredProjectCommand
+ {
+ std::function<void(Project*)> action;
+ std::function<bool(Project*)> isValid;
+ };
+
static void DefaultPluginCommandActionCallback(void* ctxt, BNBinaryView* view);
static void AddressPluginCommandActionCallback(void* ctxt, BNBinaryView* view, uint64_t addr);
static void RangePluginCommandActionCallback(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len);
@@ -14849,6 +14856,7 @@ namespace BinaryNinja {
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func);
static void HighLevelILInstructionPluginCommandActionCallback(
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr);
+ static void ProjectPluginCommandActionCallback(void* ctxt, BNProject* project);
static bool DefaultPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view);
static bool AddressPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view, uint64_t addr);
@@ -14866,6 +14874,7 @@ namespace BinaryNinja {
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func);
static bool HighLevelILInstructionPluginCommandIsValidCallback(
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr);
+ static bool ProjectPluginCommandIsValidCallback(void* ctxt, BNProject* project);
public:
PluginCommand(const BNPluginCommand& cmd);
@@ -15692,6 +15701,13 @@ namespace BinaryNinja {
const std::function<void(BinaryView* view, const HighLevelILInstruction& instr)>& action,
const std::function<bool(BinaryView* view, const HighLevelILInstruction& instr)>& isValid);
+ static void RegisterForProject(const std::string& name, const std::string& description,
+ const std::function<void(Project* project)>& action);
+
+ static void RegisterForProject(const std::string& name, const std::string& description,
+ const std::function<void(Project* project)>& action,
+ const std::function<bool(Project* project)>& isValid);
+
/*! Get the list of registered PluginCommands
\return The list of registered PluginCommands
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 48b767f8..deca98e5 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -2593,7 +2593,8 @@ extern "C"
MediumLevelILFunctionPluginCommand,
MediumLevelILInstructionPluginCommand,
HighLevelILFunctionPluginCommand,
- HighLevelILInstructionPluginCommand
+ HighLevelILInstructionPluginCommand,
+ ProjectPluginCommand
} BNPluginCommandType;
typedef struct BNPluginCommand
@@ -2615,6 +2616,7 @@ extern "C"
void (*highLevelILFunctionCommand)(void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func);
void (*highLevelILInstructionCommand)(
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr);
+ void (*projectCommand)(void* ctxt, BNProject* view);
bool (*defaultIsValid)(void* ctxt, BNBinaryView* view);
bool (*addressIsValid)(void* ctxt, BNBinaryView* view, uint64_t addr);
@@ -2628,6 +2630,7 @@ extern "C"
bool (*highLevelILFunctionIsValid)(void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func);
bool (*highLevelILInstructionIsValid)(
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr);
+ bool (*projectIsValid)(void* ctxt, BNProject* view);
} BNPluginCommand;
typedef struct BNCustomCallingConvention
@@ -6987,6 +6990,8 @@ extern "C"
BINARYNINJACOREAPI void BNRegisterPluginCommandForHighLevelILInstruction(const char* name, const char* description,
void (*action)(void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr),
bool (*isValid)(void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr), void* context);
+ BINARYNINJACOREAPI void BNRegisterPluginCommandForProject(const char* name, const char* description,
+ void (*action)(void* ctxt, BNProject* project), bool (*isValid)(void* ctxt, BNProject* project), void* context);
BINARYNINJACOREAPI BNPluginCommand* BNGetAllPluginCommands(size_t* count);
BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommands(BNBinaryView* view, size_t* count);
@@ -7008,6 +7013,7 @@ extern "C"
BNBinaryView* view, BNHighLevelILFunction* func, size_t* count);
BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommandsForHighLevelILInstruction(
BNBinaryView* view, BNHighLevelILFunction* func, size_t instr, size_t* count);
+ BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommandsForProject(BNProject* project, size_t* count);
BINARYNINJACOREAPI void BNFreePluginCommandList(BNPluginCommand* commands);
// Calling conventions
diff --git a/plugin.cpp b/plugin.cpp
index 8adaf9e3..a54f1f01 100644
--- a/plugin.cpp
+++ b/plugin.cpp
@@ -164,6 +164,14 @@ void PluginCommand::HighLevelILInstructionPluginCommandActionCallback(
}
+void PluginCommand::ProjectPluginCommandActionCallback(void *ctxt, BNProject* project)
+{
+ RegisteredProjectCommand* cmd = (RegisteredProjectCommand*)ctxt;
+ Ref<Project> projectObject = new Project(BNNewProjectReference(project));
+ return cmd->action(projectObject);
+}
+
+
bool PluginCommand::DefaultPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view)
{
RegisteredDefaultCommand* cmd = (RegisteredDefaultCommand*)ctxt;
@@ -260,6 +268,14 @@ bool PluginCommand::HighLevelILInstructionPluginCommandIsValidCallback(
}
+bool PluginCommand::ProjectPluginCommandIsValidCallback(void* ctxt, BNProject* project)
+{
+ RegisteredProjectCommand* cmd = (RegisteredProjectCommand*)ctxt;
+ Ref<Project> projectObject = new Project(BNNewProjectReference(project));
+ return cmd->isValid(projectObject);
+}
+
+
void PluginCommand::Register(
const string& name, const string& description, const function<void(BinaryView* view)>& action)
{
@@ -452,6 +468,22 @@ void PluginCommand::RegisterForHighLevelILInstruction(const string& name, const
HighLevelILInstructionPluginCommandActionCallback, HighLevelILInstructionPluginCommandIsValidCallback, cmd);
}
+void PluginCommand::RegisterForProject(const std::string &name, const std::string &description,
+ const std::function<void(Project *project)> &action)
+{
+ RegisterForProject(name, description, action, [](Project*) { return true; });
+}
+
+void PluginCommand::RegisterForProject(const std::string &name, const std::string &description,
+ const std::function<void(Project* project)> &action, const std::function<bool(Project* project)> &isValid)
+{
+ RegisteredProjectCommand* cmd = new RegisteredProjectCommand;
+ cmd->action = action;
+ cmd->isValid = isValid;
+ BNRegisterPluginCommandForProject(name.c_str(), description.c_str(), ProjectPluginCommandActionCallback,
+ ProjectPluginCommandIsValidCallback, cmd);
+}
+
vector<PluginCommand> PluginCommand::GetList()
{
@@ -481,40 +513,41 @@ vector<PluginCommand> PluginCommand::GetValidList(const PluginCommandContext& ct
bool PluginCommand::IsValid(const PluginCommandContext& ctxt) const
{
- if (!ctxt.binaryView)
- return false;
-
switch (m_command.type)
{
case DefaultPluginCommand:
+ if (!ctxt.binaryView)
+ return false;
if (!m_command.defaultIsValid)
return true;
return m_command.defaultIsValid(m_command.context, ctxt.binaryView->GetObject());
case AddressPluginCommand:
+ if (!ctxt.binaryView)
+ return false;
if (!m_command.addressIsValid)
return true;
return m_command.addressIsValid(m_command.context, ctxt.binaryView->GetObject(), ctxt.address);
case RangePluginCommand:
- if (ctxt.length == 0)
+ if (ctxt.length == 0 || !ctxt.binaryView)
return false;
if (!m_command.rangeIsValid)
return true;
return m_command.rangeIsValid(m_command.context, ctxt.binaryView->GetObject(), ctxt.address, ctxt.length);
case FunctionPluginCommand:
- if (!ctxt.function)
+ if (!ctxt.function || !ctxt.binaryView)
return false;
if (!m_command.functionIsValid)
return true;
return m_command.functionIsValid(m_command.context, ctxt.binaryView->GetObject(), ctxt.function->GetObject());
case LowLevelILFunctionPluginCommand:
- if (!ctxt.lowLevelILFunction)
+ if (!ctxt.lowLevelILFunction || !ctxt.binaryView)
return false;
if (!m_command.lowLevelILFunctionIsValid)
return true;
return m_command.lowLevelILFunctionIsValid(
m_command.context, ctxt.binaryView->GetObject(), ctxt.lowLevelILFunction->GetObject());
case LowLevelILInstructionPluginCommand:
- if (!ctxt.lowLevelILFunction)
+ if (!ctxt.lowLevelILFunction || !ctxt.binaryView)
return false;
if (ctxt.instrIndex == BN_INVALID_EXPR)
return false;
@@ -523,14 +556,14 @@ bool PluginCommand::IsValid(const PluginCommandContext& ctxt) const
return m_command.lowLevelILInstructionIsValid(
m_command.context, ctxt.binaryView->GetObject(), ctxt.lowLevelILFunction->GetObject(), ctxt.instrIndex);
case MediumLevelILFunctionPluginCommand:
- if (!ctxt.mediumLevelILFunction)
+ if (!ctxt.mediumLevelILFunction || !ctxt.binaryView)
return false;
if (!m_command.mediumLevelILFunctionIsValid)
return true;
return m_command.mediumLevelILFunctionIsValid(
m_command.context, ctxt.binaryView->GetObject(), ctxt.mediumLevelILFunction->GetObject());
case MediumLevelILInstructionPluginCommand:
- if (!ctxt.mediumLevelILFunction)
+ if (!ctxt.mediumLevelILFunction || !ctxt.binaryView)
return false;
if (ctxt.instrIndex == BN_INVALID_EXPR)
return false;
@@ -539,14 +572,14 @@ bool PluginCommand::IsValid(const PluginCommandContext& ctxt) const
return m_command.mediumLevelILInstructionIsValid(
m_command.context, ctxt.binaryView->GetObject(), ctxt.mediumLevelILFunction->GetObject(), ctxt.instrIndex);
case HighLevelILFunctionPluginCommand:
- if (!ctxt.highLevelILFunction)
+ if (!ctxt.highLevelILFunction || !ctxt.binaryView)
return false;
if (!m_command.highLevelILFunctionIsValid)
return true;
return m_command.highLevelILFunctionIsValid(
m_command.context, ctxt.binaryView->GetObject(), ctxt.highLevelILFunction->GetObject());
case HighLevelILInstructionPluginCommand:
- if (!ctxt.highLevelILFunction)
+ if (!ctxt.highLevelILFunction || !ctxt.binaryView)
return false;
if (ctxt.instrIndex == BN_INVALID_EXPR)
return false;
@@ -554,6 +587,12 @@ bool PluginCommand::IsValid(const PluginCommandContext& ctxt) const
return true;
return m_command.highLevelILInstructionIsValid(
m_command.context, ctxt.binaryView->GetObject(), ctxt.highLevelILFunction->GetObject(), ctxt.instrIndex);
+ case ProjectPluginCommand:
+ if (!m_command.projectIsValid)
+ return true;
+ if (!ctxt.project)
+ return false;
+ return m_command.projectIsValid(m_command.context, ctxt.project->GetObject());
default:
return false;
}
@@ -603,6 +642,9 @@ void PluginCommand::Execute(const PluginCommandContext& ctxt) const
m_command.highLevelILInstructionCommand(
m_command.context, ctxt.binaryView->GetObject(), ctxt.highLevelILFunction->GetObject(), ctxt.instrIndex);
break;
+ case ProjectPluginCommand:
+ m_command.projectCommand(m_command.context, ctxt.project->GetObject());
+ break;
default:
break;
}
diff --git a/python/plugin.py b/python/plugin.py
index 72f3385d..26fde9cb 100644
--- a/python/plugin.py
+++ b/python/plugin.py
@@ -18,23 +18,24 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
-import traceback
import ctypes
import threading
-
+import traceback
from typing import Optional, Callable
# Binary Ninja components
import binaryninja
+
from . import _binaryninjacore as core
-from .enums import PluginCommandType
-from . import filemetadata
from . import binaryview
+from . import filemetadata
from . import function
-from .log import log_error
+from . import highlevelil
from . import lowlevelil
from . import mediumlevelil
-from . import highlevelil
+from . import project
+from .enums import PluginCommandType
+from .log import log_error
class PluginCommandContext:
@@ -55,6 +56,7 @@ class PluginCommandContext:
self._length = 0
self._function = None
self._instruction = None
+ self._project = view.project
def __len__(self):
return self._length
@@ -99,6 +101,14 @@ class PluginCommandContext:
def instruction(self, value):
self._instruction = value
+ @property
+ def project(self):
+ return self._project
+
+ @function.setter
+ def project(self, value):
+ self._project = value
+
class _PluginCommandMetaClass(type):
def __iter__(self):
@@ -238,6 +248,14 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
log_error(traceback.format_exc())
@staticmethod
+ def _project_action(project, action):
+ try:
+ project_obj = project.Project(handle=core.BNNewProjectReference(project))
+ action(project_obj)
+ except:
+ log_error(traceback.format_exc())
+
+ @staticmethod
def _default_is_valid(view, is_valid):
try:
if is_valid is None:
@@ -380,6 +398,17 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
log_error(traceback.format_exc())
return False
+ @staticmethod
+ def _project_is_valid(project, is_valid):
+ try:
+ if is_valid is None:
+ return True
+ project_obj = project.Project(handle=core.BNNewProjectReference(project))
+ return is_valid(project_obj)
+ except:
+ log_error(traceback.format_exc())
+ return False
+
@classmethod
def register(
cls, name: str, description: str, action: Callable[['binaryview.BinaryView'], None],
@@ -756,7 +785,44 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
core.BNRegisterPluginCommandForHighLevelILInstruction(name, description, action_obj, is_valid_obj, None)
@classmethod
- def get_valid_list(cls, context):
+ def register_for_project(
+ cls, name: str, description: str, action: Callable[['project.Project'], None],
+ is_valid: Optional[Callable[['project.Project'], bool]] = None
+ ):
+ r"""
+ ``register_for_project`` Register a plugin to be called with a project argument
+
+ :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder)
+ :param str description: description of the plugin
+ :param callback action: function to call with the :class:`~project.Project` as an argument
+ :param callback is_valid: optional argument of a function passed a :class:`~project.Project` to determine whether the plugin should be enabled for that project
+ :rtype: None
+ :Example:
+
+ >>> def my_plugin(project: Project):
+ >>> log_info(f"My plugin was called on project: `{project}`")
+ >>> PluginCommand.register_for_project("My Plugin", "My plugin description (not used)", my_plugin)
+ True
+ >>> def is_valid(project: Project) -> bool:
+ >>> return False
+ >>> PluginCommand.register_for_project("My Plugin (With Valid Function)", "My plugin description (not used)", my_plugin, is_valid)
+ True
+
+ .. warning:: Calling ``register_for_project`` with the same function name will replace the existing function but will leak the memory of the original plugin.
+ """
+ binaryninja._init_plugins()
+ action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p,
+ ctypes.POINTER(core.BNProject
+ ))(lambda ctxt, project: cls._project_action(project, action))
+ is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p,
+ ctypes.POINTER(core.BNProject
+ ))(
+ lambda ctxt, project: cls._project_is_valid(project, is_valid))
+ cls._registered_commands.append((action_obj, is_valid_obj))
+ core.BNRegisterPluginCommandForProject(name, description, action_obj, is_valid_obj, None)
+
+ @classmethod
+ def get_valid_list(cls, context: PluginCommandContext):
"""Dict of registered plugins"""
commands = list(cls)
result = {}
@@ -765,7 +831,7 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
result[cmd.name] = cmd
return result
- def is_valid(self, context):
+ def is_valid(self, context: PluginCommandContext):
if context.view is None:
return False
if self._command.type == PluginCommandType.DefaultPluginCommand:
@@ -847,9 +913,13 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
self._command.context, context.view.handle, context.instruction.function.handle,
context.instruction.instr_index
)
+ elif self._command.type == PluginCommandType.ProjectPluginCommand:
+ if not self._command.projectIsValid:
+ return True
+ return self._command.projectIsValid(self._command.context, context.project.handle)
return False
- def execute(self, context):
+ def execute(self, context: PluginCommandContext):
r"""
``execute`` Execute a plugin. See the example in :class:`~PluginCommandContext`
@@ -895,6 +965,8 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
self._command.context, context.view.handle, context.instruction.function.handle,
context.instruction.instr_index
)
+ elif self._command.type == PluginCommandType.ProjectPluginCommand:
+ self._command.projectCommand(self._command.context, context.project.handle)
def __repr__(self):
return "<PluginCommand: %s>" % self._name
diff --git a/rust/src/command.rs b/rust/src/command.rs
index 46f43bbd..eecdf4a8 100644
--- a/rust/src/command.rs
+++ b/rust/src/command.rs
@@ -33,16 +33,18 @@
//! The return value of these functions should indicate whether they successfully initialized themselves.
use binaryninjacore_sys::{
- BNBinaryView, BNFunction, BNRegisterPluginCommand, BNRegisterPluginCommandForAddress,
- BNRegisterPluginCommandForFunction, BNRegisterPluginCommandForRange,
+ BNBinaryView, BNFunction, BNProject, BNRegisterPluginCommand,
+ BNRegisterPluginCommandForAddress, BNRegisterPluginCommandForFunction,
+ BNRegisterPluginCommandForProject, BNRegisterPluginCommandForRange,
};
-use std::ops::Range;
-use std::os::raw::c_void;
-
use crate::binary_view::BinaryView;
use crate::function::Function;
+use crate::project::Project;
use crate::string::IntoCStr;
+use std::ops::Range;
+use std::os::raw::c_void;
+use std::ptr::NonNull;
/// The trait required for generic commands. See [register_command] for example usage.
pub trait Command: 'static + Sync {
@@ -451,3 +453,56 @@ pub fn register_command_for_function<C: FunctionCommand>(name: &str, desc: &str,
);
}
}
+
+pub trait ProjectCommand: 'static + Sync {
+ fn action(&self, project: &Project);
+ fn valid(&self, project: &Project) -> bool;
+}
+
+pub fn register_command_for_project<C: ProjectCommand>(name: &str, desc: &str, command: C) {
+ extern "C" fn cb_action<C>(ctxt: *mut c_void, project: *mut BNProject)
+ where
+ C: ProjectCommand,
+ {
+ ffi_wrap!("Command::action", unsafe {
+ let cmd = &*(ctxt as *const C);
+
+ let handle = NonNull::new(project).expect("project handle is null");
+ let project = Project { handle };
+
+ cmd.action(&project);
+ })
+ }
+
+ extern "C" fn cb_valid<C>(ctxt: *mut c_void, project: *mut BNProject) -> bool
+ where
+ C: ProjectCommand,
+ {
+ ffi_wrap!("Command::valid", unsafe {
+ let cmd = &*(ctxt as *const C);
+
+ let handle = NonNull::new(project).expect("project handle is null");
+ let project = Project { handle };
+
+ cmd.valid(&project)
+ })
+ }
+
+ let name = name.to_cstr();
+ let desc = desc.to_cstr();
+
+ let name_ptr = name.as_ptr();
+ let desc_ptr = desc.as_ptr();
+
+ let ctxt = Box::into_raw(Box::new(command));
+
+ unsafe {
+ BNRegisterPluginCommandForProject(
+ name_ptr,
+ desc_ptr,
+ Some(cb_action::<C>),
+ Some(cb_valid::<C>),
+ ctxt as *mut _,
+ );
+ }
+}
diff --git a/ui/action.h b/ui/action.h
index c7357fcf..238121ec 100644
--- a/ui/action.h
+++ b/ui/action.h
@@ -353,10 +353,8 @@ class BINARYNINJAUIAPI Menu
void removeTransformActions();
void addPluginCommandActions(const QString& group);
- void addPluginCommandActions(const QString& prefix, const QString& group, uint8_t order = MENU_ORDER_NORMAL);
- void addPluginCommandSubmenuActions(const QString& submenu, const QString& group);
- void addPluginCommandSubmenuActions(
- const QString& submenu, const QString& prefix, const QString& group, uint8_t order = MENU_ORDER_NORMAL);
+ void addPluginCommandActions(const QString& prefix, const QString& group, uint8_t order = MENU_ORDER_NORMAL, const std::vector<BNPluginCommandType>& types = {});
+ void addPluginCommandSubmenuActions(const QString& submenu, const QString& group, const std::vector<BNPluginCommandType>& types);
void removePluginCommandActions();
void removePluginCommandSubmenuActions(const QString& submenu);
diff --git a/ui/projectbrowser.h b/ui/projectbrowser.h
index da6699e1..f4f9bd4c 100644
--- a/ui/projectbrowser.h
+++ b/ui/projectbrowser.h
@@ -323,6 +323,7 @@ class BINARYNINJAUIAPI ProjectBrowser: public QWidget, public UIContextNotificat
void restoreTreeState();
void initActions();
+ UIActionContext actionContext();
std::vector<ProjectFileRef> GetSelectedFilesRecursive();