summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h77
-rw-r--r--binaryninjacore.h8
-rw-r--r--plugin.cpp38
-rw-r--r--plugins/bntl_utils/src/command/create.rs8
-rw-r--r--plugins/bntl_utils/src/command/diff.rs9
-rw-r--r--plugins/bntl_utils/src/command/dump.rs10
-rw-r--r--plugins/bntl_utils/src/command/validate.rs9
-rw-r--r--plugins/bntl_utils/src/lib.rs8
-rw-r--r--plugins/warp/src/plugin.rs5
-rw-r--r--plugins/warp/src/plugin/commit.rs9
-rw-r--r--plugins/warp/src/plugin/create.rs15
-rw-r--r--python/plugin.py74
-rw-r--r--rust/src/command.rs42
13 files changed, 259 insertions, 53 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index a7a64f4c..08c03c30 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -16502,6 +16502,12 @@ namespace BinaryNinja {
{
BNPluginCommand m_command;
+ struct RegisteredGlobalCommand
+ {
+ std::function<void()> action;
+ std::function<bool()> isValid;
+ };
+
struct RegisteredDefaultCommand
{
std::function<void(BinaryView*)> action;
@@ -16568,6 +16574,7 @@ namespace BinaryNinja {
std::function<bool(Project*)> isValid;
};
+ static void GlobalPluginCommandActionCallback(void* ctxt);
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);
@@ -16586,6 +16593,7 @@ namespace BinaryNinja {
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr);
static void ProjectPluginCommandActionCallback(void* ctxt, BNProject* project);
+ static bool GlobalPluginCommandIsValidCallback(void* ctxt);
static bool DefaultPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view);
static bool AddressPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view, uint64_t addr);
static bool RangePluginCommandIsValidCallback(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len);
@@ -16611,9 +16619,74 @@ namespace BinaryNinja {
PluginCommand& operator=(const PluginCommand& cmd);
+ /*! Register a command.
+
+ This will appear in the top menu.
+
+ \code{.cpp}
+
+ // Registering a command using a lambda expression
+ PluginCommand::RegisterGlobal("MyPlugin\\MyAction", "Perform an action", []() { });
+
+ // Registering a command using a standard static function
+ // This also works with functions in the global namespace, e.g. "void myCommand()"
+ void MyPlugin::MyCommand()
+ {
+ // Perform an action
+ }
+
+ PluginCommand::Register("MyPlugin\\MySecondAction", "Perform an action", MyPlugin::MyCommand);
+ \endcode
+
+ \param name
+ \parblock
+ Name of the command to register. This will appear in the top menu.
+
+ You can register submenus to an item by separating names with a \c "\\". The base (farthest right) name will
+ be the item which upon being clicked will perform the action.
+ \endparblock
+ \param description Description of the command
+ \param action Action to perform
+ */
+ static void RegisterGlobal(const std::string& name, const std::string& description, const std::function<void()>& action);
+
+ /*! Register a command globally, with a validity check.
+
+ This will appear in the top menu.
+
+ \code{.cpp}
+
+ // Registering a command using lambda expressions
+ PluginCommand::Register("MyPlugin\\MyAction", "Perform an action", [](){ }, [](){ });
+
+ // Registering a command using a standard static function, and a lambda for the isValid check
+ // This also works with functions in the global namespace, e.g. "void myCommand()"
+ void MyPlugin::MyCommand(BinaryView* view)
+ {
+ // Perform an action
+ }
+
+ PluginCommand::Register("MyPlugin\\MySecondAction", "Perform an action", MyPlugin::MyCommand,
+ [](){ return true; });
+ \endcode
+
+ \param name
+ \parblock
+ Name of the command to register. This will appear in the top menu and the context menu.
+
+ You can register submenus to an item by separating names with a \c "\\". The base (farthest right) name will
+ be the item which upon being clicked will perform the action.
+ \endparblock
+ \param description Description of the command
+ \param action Action to perform
+ \param isValid Function that returns whether the command is allowed to be performed.
+ */
+ static void RegisterGlobal(const std::string& name, const std::string& description,
+ const std::function<void()>& action, const std::function<bool()>& isValid);
+
/*! Register a command for a given BinaryView.
- This will appear in the top menu and the right-click context menu.
+ This will appear in the top menu.
\code{.cpp}
@@ -16649,7 +16722,7 @@ namespace BinaryNinja {
/*! Register a command for a given BinaryView, with a validity check.
- This will appear in the top menu and the right-click context menu.
+ This will appear in the top menu.
\code{.cpp}
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 94456c2f..439f87c0 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -2784,7 +2784,8 @@ extern "C"
MediumLevelILInstructionPluginCommand,
HighLevelILFunctionPluginCommand,
HighLevelILInstructionPluginCommand,
- ProjectPluginCommand
+ ProjectPluginCommand,
+ GlobalPluginCommand
};
typedef struct BNPluginCommand
@@ -2794,6 +2795,7 @@ extern "C"
BNPluginCommandType type;
void* context;
+ void (*globalCommand)(void* ctxt);
void (*defaultCommand)(void* ctxt, BNBinaryView* view);
void (*addressCommand)(void* ctxt, BNBinaryView* view, uint64_t addr);
void (*rangeCommand)(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len);
@@ -2808,6 +2810,7 @@ extern "C"
void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr);
void (*projectCommand)(void* ctxt, BNProject* view);
+ bool (*globalIsValid)(void* ctxt);
bool (*defaultIsValid)(void* ctxt, BNBinaryView* view);
bool (*addressIsValid)(void* ctxt, BNBinaryView* view, uint64_t addr);
bool (*rangeIsValid)(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len);
@@ -7426,6 +7429,8 @@ extern "C"
BINARYNINJACOREAPI void BNInstallPendingUpdate(char** errors);
// Plugin commands
+ BINARYNINJACOREAPI void BNRegisterPluginCommandGlobal(const char* name, const char* description,
+ void (*action)(void* ctxt), bool (*isValid)(void* ctxt), void* context);
BINARYNINJACOREAPI void BNRegisterPluginCommand(const char* name, const char* description,
void (*action)(void* ctxt, BNBinaryView* view), bool (*isValid)(void* ctxt, BNBinaryView* view), void* context);
BINARYNINJACOREAPI void BNRegisterPluginCommandForAddress(const char* name, const char* description,
@@ -7460,6 +7465,7 @@ extern "C"
void (*action)(void* ctxt, BNProject* project), bool (*isValid)(void* ctxt, BNProject* project), void* context);
BINARYNINJACOREAPI BNPluginCommand* BNGetAllPluginCommands(size_t* count);
+ BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommandsGlobal(size_t* count);
BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommands(BNBinaryView* view, size_t* count);
BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommandsForAddress(
BNBinaryView* view, uint64_t addr, size_t* count);
diff --git a/plugin.cpp b/plugin.cpp
index 28c2b466..81a280c4 100644
--- a/plugin.cpp
+++ b/plugin.cpp
@@ -68,6 +68,13 @@ PluginCommand& PluginCommand::operator=(const PluginCommand& cmd)
}
+void PluginCommand::GlobalPluginCommandActionCallback(void* ctxt)
+{
+ RegisteredGlobalCommand* cmd = (RegisteredGlobalCommand*)ctxt;
+ cmd->action();
+}
+
+
void PluginCommand::DefaultPluginCommandActionCallback(void* ctxt, BNBinaryView* view)
{
RegisteredDefaultCommand* cmd = (RegisteredDefaultCommand*)ctxt;
@@ -172,6 +179,13 @@ void PluginCommand::ProjectPluginCommandActionCallback(void *ctxt, BNProject* pr
}
+bool PluginCommand::GlobalPluginCommandIsValidCallback(void* ctxt)
+{
+ RegisteredGlobalCommand* cmd = (RegisteredGlobalCommand*)ctxt;
+ return cmd->isValid();
+}
+
+
bool PluginCommand::DefaultPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view)
{
RegisteredDefaultCommand* cmd = (RegisteredDefaultCommand*)ctxt;
@@ -276,6 +290,23 @@ bool PluginCommand::ProjectPluginCommandIsValidCallback(void* ctxt, BNProject* p
}
+void PluginCommand::RegisterGlobal(const string& name, const string& description, const function<void()>& action)
+{
+ RegisterGlobal(name, description, action, []() { return true; });
+}
+
+
+void PluginCommand::RegisterGlobal(const string& name, const string& description,
+ const function<void()>& action, const function<bool()>& isValid)
+{
+ RegisteredGlobalCommand* cmd = new RegisteredGlobalCommand;
+ cmd->action = action;
+ cmd->isValid = isValid;
+ BNRegisterPluginCommandGlobal(name.c_str(), description.c_str(), GlobalPluginCommandActionCallback,
+ GlobalPluginCommandIsValidCallback, cmd);
+}
+
+
void PluginCommand::Register(
const string& name, const string& description, const function<void(BinaryView* view)>& action)
{
@@ -515,6 +546,10 @@ bool PluginCommand::IsValid(const PluginCommandContext& ctxt) const
{
switch (m_command.type)
{
+ case GlobalPluginCommand:
+ if (!m_command.globalIsValid)
+ return true;
+ return m_command.globalIsValid(m_command.context);
case DefaultPluginCommand:
if (!ctxt.binaryView)
return false;
@@ -606,6 +641,9 @@ void PluginCommand::Execute(const PluginCommandContext& ctxt) const
switch (m_command.type)
{
+ case GlobalPluginCommand:
+ m_command.globalCommand(m_command.context);
+ break;
case DefaultPluginCommand:
m_command.defaultCommand(m_command.context, ctxt.binaryView->GetObject());
break;
diff --git a/plugins/bntl_utils/src/command/create.rs b/plugins/bntl_utils/src/command/create.rs
index 5fa8c2be..96d0eb60 100644
--- a/plugins/bntl_utils/src/command/create.rs
+++ b/plugins/bntl_utils/src/command/create.rs
@@ -3,7 +3,7 @@ use crate::process::{new_processing_state_background_thread, TypeLibProcessor};
use crate::validate::TypeLibValidater;
use binaryninja::background_task::BackgroundTask;
use binaryninja::binary_view::{BinaryView, BinaryViewExt};
-use binaryninja::command::{Command, ProjectCommand};
+use binaryninja::command::{Command, GlobalCommand, ProjectCommand};
use binaryninja::interaction::{Form, FormInputField, MessageBoxButtonSet, MessageBoxIcon};
use binaryninja::platform::Platform;
use binaryninja::project::Project;
@@ -188,14 +188,14 @@ impl CreateFromDirectory {
}
}
-impl Command for CreateFromDirectory {
- fn action(&self, _view: &BinaryView) {
+impl GlobalCommand for CreateFromDirectory {
+ fn action(&self) {
thread::spawn(move || {
CreateFromDirectory::execute();
});
}
- fn valid(&self, _view: &BinaryView) -> bool {
+ fn valid(&self) -> bool {
true
}
}
diff --git a/plugins/bntl_utils/src/command/diff.rs b/plugins/bntl_utils/src/command/diff.rs
index 48501433..6cc0fe0a 100644
--- a/plugins/bntl_utils/src/command/diff.rs
+++ b/plugins/bntl_utils/src/command/diff.rs
@@ -2,8 +2,7 @@ use crate::command::OutputDirectoryField;
use crate::diff::TILDiff;
use crate::helper::path_to_type_libraries;
use binaryninja::background_task::BackgroundTask;
-use binaryninja::binary_view::BinaryView;
-use binaryninja::command::Command;
+use binaryninja::command::GlobalCommand;
use binaryninja::interaction::{Form, FormInputField};
use std::path::PathBuf;
use std::thread;
@@ -95,14 +94,14 @@ impl Diff {
}
}
-impl Command for Diff {
- fn action(&self, _view: &BinaryView) {
+impl GlobalCommand for Diff {
+ fn action(&self) {
thread::spawn(move || {
Diff::execute();
});
}
- fn valid(&self, _view: &BinaryView) -> bool {
+ fn valid(&self) -> bool {
true
}
}
diff --git a/plugins/bntl_utils/src/command/dump.rs b/plugins/bntl_utils/src/command/dump.rs
index 96c3b264..7b48900d 100644
--- a/plugins/bntl_utils/src/command/dump.rs
+++ b/plugins/bntl_utils/src/command/dump.rs
@@ -2,8 +2,7 @@ use crate::command::{InputDirectoryField, OutputDirectoryField};
use crate::dump::TILDump;
use crate::helper::path_to_type_libraries;
use binaryninja::background_task::BackgroundTask;
-use binaryninja::binary_view::BinaryView;
-use binaryninja::command::Command;
+use binaryninja::command::GlobalCommand;
use binaryninja::interaction::Form;
pub struct Dump;
@@ -48,15 +47,14 @@ impl Dump {
}
}
-impl Command for Dump {
- // TODO: We need a command type that does not require a binary view.
- fn action(&self, _view: &BinaryView) {
+impl GlobalCommand for Dump {
+ fn action(&self) {
std::thread::spawn(move || {
Dump::execute();
});
}
- fn valid(&self, _view: &BinaryView) -> bool {
+ fn valid(&self) -> bool {
true
}
}
diff --git a/plugins/bntl_utils/src/command/validate.rs b/plugins/bntl_utils/src/command/validate.rs
index b95699e0..921309fd 100644
--- a/plugins/bntl_utils/src/command/validate.rs
+++ b/plugins/bntl_utils/src/command/validate.rs
@@ -1,8 +1,7 @@
use crate::command::{InputDirectoryField, OutputDirectoryField};
use crate::helper::path_to_type_libraries;
use crate::validate::TypeLibValidater;
-use binaryninja::binary_view::BinaryView;
-use binaryninja::command::Command;
+use binaryninja::command::GlobalCommand;
use binaryninja::interaction::Form;
use binaryninja::platform::Platform;
@@ -68,14 +67,14 @@ impl Validate {
}
}
-impl Command for Validate {
- fn action(&self, _view: &BinaryView) {
+impl GlobalCommand for Validate {
+ fn action(&self) {
std::thread::spawn(move || {
Validate::execute();
});
}
- fn valid(&self, _view: &BinaryView) -> bool {
+ fn valid(&self) -> bool {
true
}
}
diff --git a/plugins/bntl_utils/src/lib.rs b/plugins/bntl_utils/src/lib.rs
index aad6cdcf..fb96746a 100644
--- a/plugins/bntl_utils/src/lib.rs
+++ b/plugins/bntl_utils/src/lib.rs
@@ -35,25 +35,25 @@ fn plugin_init() -> Result<(), ()> {
command::create::CreateFromProject {},
);
- binaryninja::command::register_command(
+ binaryninja::command::register_global_command(
"BNTL\\Create\\From Directory",
"Create .bntl files from the given directory",
command::create::CreateFromDirectory {},
);
- binaryninja::command::register_command(
+ binaryninja::command::register_global_command(
"BNTL\\Diff",
"Diff two .bntl files and output the difference to a file",
command::diff::Diff {},
);
- binaryninja::command::register_command(
+ binaryninja::command::register_global_command(
"BNTL\\Dump To Header",
"Dump a .bntl file to a header file",
command::dump::Dump {},
);
- binaryninja::command::register_command(
+ binaryninja::command::register_global_command(
"BNTL\\Validate",
"Validate a .bntl file and report the issues",
command::validate::Validate {},
diff --git a/plugins/warp/src/plugin.rs b/plugins/warp/src/plugin.rs
index cb527fff..d163205b 100644
--- a/plugins/warp/src/plugin.rs
+++ b/plugins/warp/src/plugin.rs
@@ -11,6 +11,7 @@ use crate::{core_signature_dir, user_signature_dir};
use binaryninja::background_task::BackgroundTask;
use binaryninja::command::{
register_command, register_command_for_function, register_command_for_project,
+ register_global_command,
};
use binaryninja::is_ui_enabled;
use binaryninja::settings::{QueryOptions, Settings};
@@ -192,7 +193,7 @@ fn plugin_init() -> bool {
load::LoadSignatureFile {},
);
- register_command(
+ register_global_command(
"WARP\\Commit File",
"Commit file to a source",
commit::CommitFile {},
@@ -234,7 +235,7 @@ fn plugin_init() -> bool {
create::CreateFromCurrentView {},
);
- register_command(
+ register_global_command(
"WARP\\Create\\From File(s)",
"Creates a signature file containing all selected functions",
create::CreateFromFiles {},
diff --git a/plugins/warp/src/plugin/commit.rs b/plugins/warp/src/plugin/commit.rs
index d98a356a..4e8a9fee 100644
--- a/plugins/warp/src/plugin/commit.rs
+++ b/plugins/warp/src/plugin/commit.rs
@@ -3,8 +3,7 @@
use crate::cache::container::cached_containers;
use crate::container::{SourceId, SourcePath};
use crate::plugin::create::OpenFileField;
-use binaryninja::binary_view::BinaryView;
-use binaryninja::command::Command;
+use binaryninja::command::GlobalCommand;
use binaryninja::interaction::{Form, FormInputField};
use warp::chunk::ChunkKind;
use warp::WarpFile;
@@ -137,14 +136,14 @@ impl CommitFile {
}
}
-impl Command for CommitFile {
- fn action(&self, _view: &BinaryView) {
+impl GlobalCommand for CommitFile {
+ fn action(&self) {
std::thread::spawn(move || {
Self::execute();
});
}
- fn valid(&self, _view: &BinaryView) -> bool {
+ fn valid(&self) -> bool {
true
}
}
diff --git a/plugins/warp/src/plugin/create.rs b/plugins/warp/src/plugin/create.rs
index 2a748819..5f5527a7 100644
--- a/plugins/warp/src/plugin/create.rs
+++ b/plugins/warp/src/plugin/create.rs
@@ -6,7 +6,8 @@ use crate::report::{ReportGenerator, ReportKindField};
use crate::{user_signature_dir, INCLUDE_TAG_NAME};
use binaryninja::background_task::BackgroundTask;
use binaryninja::binary_view::{BinaryView, BinaryViewExt};
-use binaryninja::command::Command;
+use binaryninja::command::{Command, GlobalCommand};
+use binaryninja::file_metadata::FileMetadata;
use binaryninja::interaction::form::{Form, FormInputField};
use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon};
use binaryninja::rc::Ref;
@@ -248,15 +249,17 @@ impl Command for CreateFromCurrentView {
pub struct CreateFromFiles;
-impl Command for CreateFromFiles {
- fn action(&self, view: &BinaryView) {
- let view = view.to_owned();
+impl GlobalCommand for CreateFromFiles {
+ fn action(&self) {
+ let empty_file_metadata = FileMetadata::new();
+ let empty_bv = BinaryView::from_data(&empty_file_metadata, &[]);
thread::spawn(move || {
- CreateFromCurrentView::execute(view, true);
+ CreateFromCurrentView::execute(empty_bv.to_owned(), true);
+ empty_bv.file().close();
});
}
- fn valid(&self, _view: &BinaryView) -> bool {
+ fn valid(&self) -> bool {
true
}
}
diff --git a/python/plugin.py b/python/plugin.py
index 0284d882..988f7c41 100644
--- a/python/plugin.py
+++ b/python/plugin.py
@@ -55,7 +55,8 @@ class PluginCommandContext:
self._length = 0
self._function = None
self._instruction = None
- self._project = view.project
+ if view is not None:
+ self._project = view.project
def __len__(self):
return self._length
@@ -140,6 +141,14 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
self._type = PluginCommandType(cmd.type)
@staticmethod
+ def _global_action(action):
+ try:
+ action()
+ except:
+ log_error_for_exception("Unhandled Python exception in PluginCommand._global_action")
+
+
+ @staticmethod
def _default_action(view, action):
try:
file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(view))
@@ -255,6 +264,16 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
log_error_for_exception("Unhandled Python exception in PluginCommand._project_action")
@staticmethod
+ def _global_is_valid(is_valid):
+ try:
+ if is_valid is None:
+ return True
+ return is_valid()
+ except:
+ log_error_for_exception("Unhandled Python exception in PluginCommand._global_is_valid")
+ return False
+
+ @staticmethod
def _default_is_valid(view, is_valid):
try:
if is_valid is None:
@@ -409,6 +428,38 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
return False
@classmethod
+ def register_global(
+ cls, name: str, description: str, action: Callable[[], None],
+ is_valid: Optional[Callable[[], bool]] = None
+ ):
+ r"""
+ ``register_global`` Register a command globally
+
+ :param str name: name of the command (use 'Folder\\Name' to have the menu item nested in a folder)
+ :param str description: description of the command
+ :param callback action: function to call
+ :param callback is_valid: optional argument of a function to determine whether the command should be enabled
+ :rtype: None
+ :Example:
+
+ >>> def my_command():
+ >>> log_info(f"My command was called on bv")
+ >>> PluginCommand.register_global("My Command", "My command description (not used)", my_command)
+ True
+ >>> def is_valid() -> bool:
+ >>> return False
+ >>> PluginCommand.register_global("My Command (With Valid Function)", "My command description (not used)", my_plugin, is_valid)
+ True
+
+ .. warning:: Calling ``register_global`` 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)(lambda ctxt: cls._global_action(action))
+ is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p)(lambda ctxt: cls._global_is_valid(is_valid))
+ cls._registered_commands.append((action_obj, is_valid_obj))
+ core.BNRegisterPluginCommandGlobal(name, description, action_obj, is_valid_obj, None)
+
+ @classmethod
def register(
cls, name: str, description: str, action: Callable[['binaryview.BinaryView'], None],
is_valid: Optional[Callable[['binaryview.BinaryView'], bool]] = None
@@ -831,6 +882,17 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
return result
def is_valid(self, context: PluginCommandContext):
+ if self._command.type == PluginCommandType.ProjectPluginCommand:
+ if context.project is None:
+ return False
+ if not self._command.projectIsValid:
+ return True
+ return self._command.projectIsValid(self._command.context, context.project.handle)
+ elif self._command.type == PluginCommandType.GlobalPluginCommand:
+ if not self._command.globalIsValid:
+ return True
+ return self._command.globalIsValid(self._command.context)
+
if context.view is None:
return False
if self._command.type == PluginCommandType.DefaultPluginCommand:
@@ -912,12 +974,6 @@ 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 context.project is None:
- return False
- if not self._command.projectIsValid:
- return True
- return self._command.projectIsValid(self._command.context, context.project.handle)
return False
def execute(self, context: PluginCommandContext):
@@ -933,7 +989,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass):
"""
if not self.is_valid(context):
return
- if self._command.type == PluginCommandType.DefaultPluginCommand:
+ if self._command.type == PluginCommandType.GlobalPluginCommand:
+ self._command.globalCommand(self._command.context)
+ elif self._command.type == PluginCommandType.DefaultPluginCommand:
self._command.defaultCommand(self._command.context, context.view.handle)
elif self._command.type == PluginCommandType.AddressPluginCommand:
self._command.addressCommand(self._command.context, context.view.handle, context.address)
diff --git a/rust/src/command.rs b/rust/src/command.rs
index 9f585633..ac9870fa 100644
--- a/rust/src/command.rs
+++ b/rust/src/command.rs
@@ -32,11 +32,7 @@
//!
//! The return value of these functions should indicate whether they successfully initialized themselves.
-use binaryninjacore_sys::{
- BNBinaryView, BNFunction, BNProject, BNRegisterPluginCommand,
- BNRegisterPluginCommandForAddress, BNRegisterPluginCommandForFunction,
- BNRegisterPluginCommandForProject, BNRegisterPluginCommandForRange,
-};
+use binaryninjacore_sys::*;
use crate::binary_view::BinaryView;
use crate::function::Function;
@@ -46,6 +42,42 @@ use std::ops::Range;
use std::os::raw::c_void;
use std::ptr::NonNull;
+pub trait GlobalCommand: 'static + Sync {
+ fn action(&self);
+ fn valid(&self) -> bool;
+}
+
+pub fn register_global_command<C: GlobalCommand>(name: &str, desc: &str, command: C) {
+ extern "C" fn cb_action<C>(ctxt: *mut c_void)
+ where
+ C: GlobalCommand,
+ {
+ let cmd = unsafe { &*(ctxt as *const C) };
+ cmd.action();
+ }
+
+ extern "C" fn cb_valid<C>(ctxt: *mut c_void) -> bool
+ where
+ C: GlobalCommand,
+ {
+ let cmd = unsafe { &*(ctxt as *const C) };
+ cmd.valid()
+ }
+
+ let name = name.to_cstr();
+ let desc = desc.to_cstr();
+ let ctxt = Box::into_raw(Box::new(command));
+ unsafe {
+ BNRegisterPluginCommandGlobal(
+ name.as_ptr(),
+ desc.as_ptr(),
+ Some(cb_action::<C>),
+ Some(cb_valid::<C>),
+ ctxt as *mut _,
+ );
+ }
+}
+
/// The trait required for generic commands. See [register_command] for example usage.
pub trait Command: 'static + Sync {
fn action(&self, view: &BinaryView);