binaryninja/
command.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Provides commands for registering plugins and plugin actions.
16//!
17//! All plugins need to provide one of the following functions for Binary Ninja to call:
18//!
19//! ```no_run
20//! pub extern "C" fn CorePluginInit() -> bool {
21//!     todo!();
22//! }
23//! ```
24//!
25//! ```no_run
26//! pub extern "C" fn UIPluginInit() -> bool {
27//!     todo!();
28//! }
29//! ```
30//!
31//! Both of these functions can call any of the following registration functions, though `CorePluginInit` is called during Binary Ninja core initialization, and `UIPluginInit` is called during Binary Ninja UI initialization.
32//!
33//! The return value of these functions should indicate whether they successfully initialized themselves.
34
35use binaryninjacore_sys::*;
36
37use crate::binary_view::BinaryView;
38use crate::function::Function;
39use crate::project::Project;
40use crate::string::IntoCStr;
41use std::ops::Range;
42use std::os::raw::c_void;
43use std::ptr::NonNull;
44
45pub trait GlobalCommand: 'static + Sync {
46    fn action(&self);
47    fn valid(&self) -> bool;
48}
49
50pub fn register_global_command<C: GlobalCommand>(name: &str, desc: &str, command: C) {
51    extern "C" fn cb_action<C>(ctxt: *mut c_void)
52    where
53        C: GlobalCommand,
54    {
55        let cmd = unsafe { &*(ctxt as *const C) };
56        cmd.action();
57    }
58
59    extern "C" fn cb_valid<C>(ctxt: *mut c_void) -> bool
60    where
61        C: GlobalCommand,
62    {
63        let cmd = unsafe { &*(ctxt as *const C) };
64        cmd.valid()
65    }
66
67    let name = name.to_cstr();
68    let desc = desc.to_cstr();
69    let ctxt = Box::into_raw(Box::new(command));
70    unsafe {
71        BNRegisterPluginCommandGlobal(
72            name.as_ptr(),
73            desc.as_ptr(),
74            Some(cb_action::<C>),
75            Some(cb_valid::<C>),
76            ctxt as *mut _,
77        );
78    }
79}
80
81/// The trait required for generic commands.  See [register_command] for example usage.
82pub trait Command: 'static + Sync {
83    fn action(&self, view: &BinaryView);
84    fn valid(&self, view: &BinaryView) -> bool;
85}
86
87impl<T> Command for T
88where
89    T: 'static + Sync + Fn(&BinaryView),
90{
91    fn action(&self, view: &BinaryView) {
92        self(view);
93    }
94
95    fn valid(&self, _view: &BinaryView) -> bool {
96        true
97    }
98}
99
100/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
101///
102/// # Example
103/// ```no_run
104/// # use binaryninja::command::Command;
105/// # use binaryninja::binary_view::BinaryView;
106/// struct MyCommand;
107///
108/// impl Command for MyCommand {
109///     fn action(&self, view: &BinaryView) {
110///         // Your code here
111///     }
112///
113///     fn valid(&self, view: &BinaryView) -> bool {
114///         // Your code here
115///         true
116///     }
117/// }
118///
119/// # use binaryninja::command::register_command;
120/// #[no_mangle]
121/// pub extern "C" fn CorePluginInit() -> bool {
122///     register_command(
123///         "My Plugin Command",
124///         "A description of my command",
125///         MyCommand {},
126///     );
127///     true
128/// }
129/// ```
130pub fn register_command<C: Command>(name: &str, desc: &str, command: C) {
131    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView)
132    where
133        C: Command,
134    {
135        let cmd = unsafe { &*(ctxt as *const C) };
136        debug_assert!(!view.is_null());
137        let view = BinaryView { handle: view };
138        let _span = ffi_span!("Command::action", view);
139        cmd.action(&view);
140    }
141
142    extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
143    where
144        C: Command,
145    {
146        let cmd = unsafe { &*(ctxt as *const C) };
147        debug_assert!(!view.is_null());
148        let view = BinaryView { handle: view };
149        let _span = ffi_span!("Command::valid", view);
150        cmd.valid(&view)
151    }
152
153    let name = name.to_cstr();
154    let desc = desc.to_cstr();
155    let ctxt = Box::into_raw(Box::new(command));
156    unsafe {
157        BNRegisterPluginCommand(
158            name.as_ptr(),
159            desc.as_ptr(),
160            Some(cb_action::<C>),
161            Some(cb_valid::<C>),
162            ctxt as *mut _,
163        );
164    }
165}
166
167/// The trait required for address-associated commands.  See [register_command_for_address] for example usage.
168pub trait AddressCommand: 'static + Sync {
169    fn action(&self, view: &BinaryView, addr: u64);
170    fn valid(&self, view: &BinaryView, addr: u64) -> bool;
171}
172
173impl<T> AddressCommand for T
174where
175    T: 'static + Sync + Fn(&BinaryView, u64),
176{
177    fn action(&self, view: &BinaryView, addr: u64) {
178        self(view, addr);
179    }
180
181    fn valid(&self, _view: &BinaryView, _addr: u64) -> bool {
182        true
183    }
184}
185
186/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
187///
188/// # Example
189/// ```no_run
190/// # use binaryninja::command::AddressCommand;
191/// # use binaryninja::binary_view::BinaryView;
192/// struct MyCommand;
193///
194/// impl AddressCommand for MyCommand {
195///     fn action(&self, view: &BinaryView, addr: u64) {
196///         // Your code here
197///     }
198///
199///     fn valid(&self, view: &BinaryView, addr: u64) -> bool {
200///         // Your code here
201///         true
202///     }
203/// }
204///
205/// # use binaryninja::command::register_command_for_address;
206/// #[no_mangle]
207/// pub extern "C" fn CorePluginInit() -> bool {
208///     register_command_for_address(
209///         "My Plugin Command",
210///         "A description of my command",
211///         MyCommand {},
212///     );
213///     true
214/// }
215/// ```
216pub fn register_command_for_address<C: AddressCommand>(name: &str, desc: &str, command: C) {
217    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64)
218    where
219        C: AddressCommand,
220    {
221        let cmd = unsafe { &*(ctxt as *const C) };
222        debug_assert!(!view.is_null());
223        let view = BinaryView { handle: view };
224        let _span = ffi_span!("AddressCommand::action", view);
225        cmd.action(&view, addr);
226    }
227
228    extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) -> bool
229    where
230        C: AddressCommand,
231    {
232        let cmd = unsafe { &*(ctxt as *const C) };
233        debug_assert!(!view.is_null());
234        let view = BinaryView { handle: view };
235        let _span = ffi_span!("AddressCommand::valid", view);
236        cmd.valid(&view, addr)
237    }
238
239    let name = name.to_cstr();
240    let desc = desc.to_cstr();
241    let ctxt = Box::into_raw(Box::new(command));
242    unsafe {
243        BNRegisterPluginCommandForAddress(
244            name.as_ptr(),
245            desc.as_ptr(),
246            Some(cb_action::<C>),
247            Some(cb_valid::<C>),
248            ctxt as *mut _,
249        );
250    }
251}
252
253/// The trait required for range-associated commands.  See [register_command_for_range] for example usage.
254pub trait RangeCommand: 'static + Sync {
255    fn action(&self, view: &BinaryView, range: Range<u64>);
256    fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool;
257}
258
259impl<T> RangeCommand for T
260where
261    T: 'static + Sync + Fn(&BinaryView, Range<u64>),
262{
263    fn action(&self, view: &BinaryView, range: Range<u64>) {
264        self(view, range);
265    }
266
267    fn valid(&self, _view: &BinaryView, _range: Range<u64>) -> bool {
268        true
269    }
270}
271
272/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
273///
274/// # Example
275/// ```no_run
276/// # use std::ops::Range;
277/// # use binaryninja::command::RangeCommand;
278/// # use binaryninja::binary_view::BinaryView;
279/// struct MyCommand;
280///
281/// impl RangeCommand for MyCommand {
282///     fn action(&self, view: &BinaryView, range: Range<u64>) {
283///         // Your code here
284///     }
285///
286///     fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool {
287///         // Your code here
288///         true
289///     }
290/// }
291///
292/// # use binaryninja::command::register_command_for_range;
293/// #[no_mangle]
294/// pub extern "C" fn CorePluginInit() -> bool {
295///     register_command_for_range(
296///         "My Plugin Command",
297///         "A description of my command",
298///         MyCommand {},
299///     );
300///     true
301/// }
302/// ```
303pub fn register_command_for_range<C>(name: &str, desc: &str, command: C)
304where
305    C: RangeCommand,
306{
307    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64)
308    where
309        C: RangeCommand,
310    {
311        let cmd = unsafe { &*(ctxt as *const C) };
312        debug_assert!(!view.is_null());
313        let view = BinaryView { handle: view };
314        let _span = ffi_span!("RangeCommand::action", view);
315        cmd.action(&view, addr..addr.wrapping_add(len));
316    }
317
318    extern "C" fn cb_valid<C>(
319        ctxt: *mut c_void,
320        view: *mut BNBinaryView,
321        addr: u64,
322        len: u64,
323    ) -> bool
324    where
325        C: RangeCommand,
326    {
327        let cmd = unsafe { &*(ctxt as *const C) };
328        debug_assert!(!view.is_null());
329        let view = BinaryView { handle: view };
330        let _span = ffi_span!("RangeCommand::valid", view);
331        cmd.valid(&view, addr..addr.wrapping_add(len))
332    }
333
334    let name = name.to_cstr();
335    let desc = desc.to_cstr();
336    let ctxt = Box::into_raw(Box::new(command));
337    unsafe {
338        BNRegisterPluginCommandForRange(
339            name.as_ptr(),
340            desc.as_ptr(),
341            Some(cb_action::<C>),
342            Some(cb_valid::<C>),
343            ctxt as *mut _,
344        );
345    }
346}
347
348/// The trait required for function-associated commands.  See [register_command_for_function] for example usage.
349pub trait FunctionCommand: 'static + Sync {
350    fn action(&self, view: &BinaryView, func: &Function);
351    fn valid(&self, view: &BinaryView, func: &Function) -> bool;
352}
353
354impl<T> FunctionCommand for T
355where
356    T: 'static + Sync + Fn(&BinaryView, &Function),
357{
358    fn action(&self, view: &BinaryView, func: &Function) {
359        self(view, func);
360    }
361
362    fn valid(&self, _view: &BinaryView, _func: &Function) -> bool {
363        true
364    }
365}
366
367/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
368///
369/// # Example
370/// ```no_run
371/// # use binaryninja::command::FunctionCommand;
372/// # use binaryninja::binary_view::BinaryView;
373/// # use binaryninja::function::Function;
374/// # use binaryninja::command::register_command_for_function;
375/// struct MyCommand;
376///
377/// impl FunctionCommand for MyCommand {
378///     fn action(&self, view: &BinaryView, func: &Function) {
379///         // Your code here
380///     }
381///
382///     fn valid(&self, view: &BinaryView, func: &Function) -> bool {
383///         // Your code here
384///         true
385///     }
386/// }
387///
388/// #[no_mangle]
389/// pub extern "C" fn CorePluginInit() -> bool {
390///     register_command_for_function(
391///         "My Plugin Command",
392///         "A description of my command",
393///         MyCommand {},
394///     );
395///     true
396/// }
397/// ```
398pub fn register_command_for_function<C: FunctionCommand>(name: &str, desc: &str, command: C) {
399    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction)
400    where
401        C: FunctionCommand,
402    {
403        let cmd = unsafe { &*(ctxt as *const C) };
404        debug_assert!(!view.is_null());
405        let view = BinaryView { handle: view };
406        debug_assert!(!func.is_null());
407        let func = Function { handle: func };
408        let _span = ffi_span!("FunctionCommand::action", view);
409        cmd.action(&view, &func);
410    }
411
412    extern "C" fn cb_valid<C>(
413        ctxt: *mut c_void,
414        view: *mut BNBinaryView,
415        func: *mut BNFunction,
416    ) -> bool
417    where
418        C: FunctionCommand,
419    {
420        let cmd = unsafe { &*(ctxt as *const C) };
421        debug_assert!(!view.is_null());
422        let view = BinaryView { handle: view };
423        debug_assert!(!func.is_null());
424        let func = Function { handle: func };
425        let _span = ffi_span!("FunctionCommand::valid", view);
426        cmd.valid(&view, &func)
427    }
428
429    let name = name.to_cstr();
430    let desc = desc.to_cstr();
431    let ctxt = Box::into_raw(Box::new(command));
432    unsafe {
433        BNRegisterPluginCommandForFunction(
434            name.as_ptr(),
435            desc.as_ptr(),
436            Some(cb_action::<C>),
437            Some(cb_valid::<C>),
438            ctxt as *mut _,
439        );
440    }
441}
442
443pub trait ProjectCommand: 'static + Sync {
444    fn action(&self, project: &Project);
445    fn valid(&self, project: &Project) -> bool;
446}
447
448pub fn register_command_for_project<C: ProjectCommand>(name: &str, desc: &str, command: C) {
449    extern "C" fn cb_action<C>(ctxt: *mut c_void, project: *mut BNProject)
450    where
451        C: ProjectCommand,
452    {
453        let cmd = unsafe { &*(ctxt as *const C) };
454        let handle = NonNull::new(project).expect("project handle is null");
455        let project = Project { handle };
456        cmd.action(&project);
457    }
458
459    extern "C" fn cb_valid<C>(ctxt: *mut c_void, project: *mut BNProject) -> bool
460    where
461        C: ProjectCommand,
462    {
463        let cmd = unsafe { &*(ctxt as *const C) };
464        let handle = NonNull::new(project).expect("project handle is null");
465        let project = Project { handle };
466        cmd.valid(&project)
467    }
468
469    let name = name.to_cstr();
470    let desc = desc.to_cstr();
471    let ctxt = Box::into_raw(Box::new(command));
472    unsafe {
473        BNRegisterPluginCommandForProject(
474            name.as_ptr(),
475            desc.as_ptr(),
476            Some(cb_action::<C>),
477            Some(cb_valid::<C>),
478            ctxt as *mut _,
479        );
480    }
481}