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    BNBinaryView, BNFunction, BNProject, BNRegisterPluginCommand,
37    BNRegisterPluginCommandForAddress, BNRegisterPluginCommandForFunction,
38    BNRegisterPluginCommandForProject, BNRegisterPluginCommandForRange,
39};
40
41use crate::binary_view::BinaryView;
42use crate::function::Function;
43use crate::project::Project;
44use crate::string::IntoCStr;
45use std::ops::Range;
46use std::os::raw::c_void;
47use std::ptr::NonNull;
48
49/// The trait required for generic commands.  See [register_command] for example usage.
50pub trait Command: 'static + Sync {
51    fn action(&self, view: &BinaryView);
52    fn valid(&self, view: &BinaryView) -> bool;
53}
54
55impl<T> Command for T
56where
57    T: 'static + Sync + Fn(&BinaryView),
58{
59    fn action(&self, view: &BinaryView) {
60        self(view);
61    }
62
63    fn valid(&self, _view: &BinaryView) -> bool {
64        true
65    }
66}
67
68/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
69///
70/// # Example
71/// ```no_run
72/// # use binaryninja::command::Command;
73/// # use binaryninja::binary_view::BinaryView;
74/// struct MyCommand;
75///
76/// impl Command for MyCommand {
77///     fn action(&self, view: &BinaryView) {
78///         // Your code here
79///     }
80///
81///     fn valid(&self, view: &BinaryView) -> bool {
82///         // Your code here
83///         true
84///     }
85/// }
86///
87/// # use binaryninja::command::register_command;
88/// #[no_mangle]
89/// pub extern "C" fn CorePluginInit() -> bool {
90///     register_command(
91///         "My Plugin Command",
92///         "A description of my command",
93///         MyCommand {},
94///     );
95///     true
96/// }
97/// ```
98pub fn register_command<C: Command>(name: &str, desc: &str, command: C) {
99    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView)
100    where
101        C: Command,
102    {
103        let cmd = unsafe { &*(ctxt as *const C) };
104        debug_assert!(!view.is_null());
105        let view = BinaryView { handle: view };
106        let _span = ffi_span!("Command::action", view);
107        cmd.action(&view);
108    }
109
110    extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
111    where
112        C: Command,
113    {
114        let cmd = unsafe { &*(ctxt as *const C) };
115        debug_assert!(!view.is_null());
116        let view = BinaryView { handle: view };
117        let _span = ffi_span!("Command::valid", view);
118        cmd.valid(&view)
119    }
120
121    let name = name.to_cstr();
122    let desc = desc.to_cstr();
123    let ctxt = Box::into_raw(Box::new(command));
124    unsafe {
125        BNRegisterPluginCommand(
126            name.as_ptr(),
127            desc.as_ptr(),
128            Some(cb_action::<C>),
129            Some(cb_valid::<C>),
130            ctxt as *mut _,
131        );
132    }
133}
134
135/// The trait required for address-associated commands.  See [register_command_for_address] for example usage.
136pub trait AddressCommand: 'static + Sync {
137    fn action(&self, view: &BinaryView, addr: u64);
138    fn valid(&self, view: &BinaryView, addr: u64) -> bool;
139}
140
141impl<T> AddressCommand for T
142where
143    T: 'static + Sync + Fn(&BinaryView, u64),
144{
145    fn action(&self, view: &BinaryView, addr: u64) {
146        self(view, addr);
147    }
148
149    fn valid(&self, _view: &BinaryView, _addr: u64) -> bool {
150        true
151    }
152}
153
154/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
155///
156/// # Example
157/// ```no_run
158/// # use binaryninja::command::AddressCommand;
159/// # use binaryninja::binary_view::BinaryView;
160/// struct MyCommand;
161///
162/// impl AddressCommand for MyCommand {
163///     fn action(&self, view: &BinaryView, addr: u64) {
164///         // Your code here
165///     }
166///
167///     fn valid(&self, view: &BinaryView, addr: u64) -> bool {
168///         // Your code here
169///         true
170///     }
171/// }
172///
173/// # use binaryninja::command::register_command_for_address;
174/// #[no_mangle]
175/// pub extern "C" fn CorePluginInit() -> bool {
176///     register_command_for_address(
177///         "My Plugin Command",
178///         "A description of my command",
179///         MyCommand {},
180///     );
181///     true
182/// }
183/// ```
184pub fn register_command_for_address<C: AddressCommand>(name: &str, desc: &str, command: C) {
185    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64)
186    where
187        C: AddressCommand,
188    {
189        let cmd = unsafe { &*(ctxt as *const C) };
190        debug_assert!(!view.is_null());
191        let view = BinaryView { handle: view };
192        let _span = ffi_span!("AddressCommand::action", view);
193        cmd.action(&view, addr);
194    }
195
196    extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) -> bool
197    where
198        C: AddressCommand,
199    {
200        let cmd = unsafe { &*(ctxt as *const C) };
201        debug_assert!(!view.is_null());
202        let view = BinaryView { handle: view };
203        let _span = ffi_span!("AddressCommand::valid", view);
204        cmd.valid(&view, addr)
205    }
206
207    let name = name.to_cstr();
208    let desc = desc.to_cstr();
209    let ctxt = Box::into_raw(Box::new(command));
210    unsafe {
211        BNRegisterPluginCommandForAddress(
212            name.as_ptr(),
213            desc.as_ptr(),
214            Some(cb_action::<C>),
215            Some(cb_valid::<C>),
216            ctxt as *mut _,
217        );
218    }
219}
220
221/// The trait required for range-associated commands.  See [register_command_for_range] for example usage.
222pub trait RangeCommand: 'static + Sync {
223    fn action(&self, view: &BinaryView, range: Range<u64>);
224    fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool;
225}
226
227impl<T> RangeCommand for T
228where
229    T: 'static + Sync + Fn(&BinaryView, Range<u64>),
230{
231    fn action(&self, view: &BinaryView, range: Range<u64>) {
232        self(view, range);
233    }
234
235    fn valid(&self, _view: &BinaryView, _range: Range<u64>) -> bool {
236        true
237    }
238}
239
240/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
241///
242/// # Example
243/// ```no_run
244/// # use std::ops::Range;
245/// # use binaryninja::command::RangeCommand;
246/// # use binaryninja::binary_view::BinaryView;
247/// struct MyCommand;
248///
249/// impl RangeCommand for MyCommand {
250///     fn action(&self, view: &BinaryView, range: Range<u64>) {
251///         // Your code here
252///     }
253///
254///     fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool {
255///         // Your code here
256///         true
257///     }
258/// }
259///
260/// # use binaryninja::command::register_command_for_range;
261/// #[no_mangle]
262/// pub extern "C" fn CorePluginInit() -> bool {
263///     register_command_for_range(
264///         "My Plugin Command",
265///         "A description of my command",
266///         MyCommand {},
267///     );
268///     true
269/// }
270/// ```
271pub fn register_command_for_range<C>(name: &str, desc: &str, command: C)
272where
273    C: RangeCommand,
274{
275    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64)
276    where
277        C: RangeCommand,
278    {
279        let cmd = unsafe { &*(ctxt as *const C) };
280        debug_assert!(!view.is_null());
281        let view = BinaryView { handle: view };
282        let _span = ffi_span!("RangeCommand::action", view);
283        cmd.action(&view, addr..addr.wrapping_add(len));
284    }
285
286    extern "C" fn cb_valid<C>(
287        ctxt: *mut c_void,
288        view: *mut BNBinaryView,
289        addr: u64,
290        len: u64,
291    ) -> bool
292    where
293        C: RangeCommand,
294    {
295        let cmd = unsafe { &*(ctxt as *const C) };
296        debug_assert!(!view.is_null());
297        let view = BinaryView { handle: view };
298        let _span = ffi_span!("RangeCommand::valid", view);
299        cmd.valid(&view, addr..addr.wrapping_add(len))
300    }
301
302    let name = name.to_cstr();
303    let desc = desc.to_cstr();
304    let ctxt = Box::into_raw(Box::new(command));
305    unsafe {
306        BNRegisterPluginCommandForRange(
307            name.as_ptr(),
308            desc.as_ptr(),
309            Some(cb_action::<C>),
310            Some(cb_valid::<C>),
311            ctxt as *mut _,
312        );
313    }
314}
315
316/// The trait required for function-associated commands.  See [register_command_for_function] for example usage.
317pub trait FunctionCommand: 'static + Sync {
318    fn action(&self, view: &BinaryView, func: &Function);
319    fn valid(&self, view: &BinaryView, func: &Function) -> bool;
320}
321
322impl<T> FunctionCommand for T
323where
324    T: 'static + Sync + Fn(&BinaryView, &Function),
325{
326    fn action(&self, view: &BinaryView, func: &Function) {
327        self(view, func);
328    }
329
330    fn valid(&self, _view: &BinaryView, _func: &Function) -> bool {
331        true
332    }
333}
334
335/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
336///
337/// # Example
338/// ```no_run
339/// # use binaryninja::command::FunctionCommand;
340/// # use binaryninja::binary_view::BinaryView;
341/// # use binaryninja::function::Function;
342/// # use binaryninja::command::register_command_for_function;
343/// struct MyCommand;
344///
345/// impl FunctionCommand for MyCommand {
346///     fn action(&self, view: &BinaryView, func: &Function) {
347///         // Your code here
348///     }
349///
350///     fn valid(&self, view: &BinaryView, func: &Function) -> bool {
351///         // Your code here
352///         true
353///     }
354/// }
355///
356/// #[no_mangle]
357/// pub extern "C" fn CorePluginInit() -> bool {
358///     register_command_for_function(
359///         "My Plugin Command",
360///         "A description of my command",
361///         MyCommand {},
362///     );
363///     true
364/// }
365/// ```
366pub fn register_command_for_function<C: FunctionCommand>(name: &str, desc: &str, command: C) {
367    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction)
368    where
369        C: FunctionCommand,
370    {
371        let cmd = unsafe { &*(ctxt as *const C) };
372        debug_assert!(!view.is_null());
373        let view = BinaryView { handle: view };
374        debug_assert!(!func.is_null());
375        let func = Function { handle: func };
376        let _span = ffi_span!("FunctionCommand::action", view);
377        cmd.action(&view, &func);
378    }
379
380    extern "C" fn cb_valid<C>(
381        ctxt: *mut c_void,
382        view: *mut BNBinaryView,
383        func: *mut BNFunction,
384    ) -> bool
385    where
386        C: FunctionCommand,
387    {
388        let cmd = unsafe { &*(ctxt as *const C) };
389        debug_assert!(!view.is_null());
390        let view = BinaryView { handle: view };
391        debug_assert!(!func.is_null());
392        let func = Function { handle: func };
393        let _span = ffi_span!("FunctionCommand::valid", view);
394        cmd.valid(&view, &func)
395    }
396
397    let name = name.to_cstr();
398    let desc = desc.to_cstr();
399    let ctxt = Box::into_raw(Box::new(command));
400    unsafe {
401        BNRegisterPluginCommandForFunction(
402            name.as_ptr(),
403            desc.as_ptr(),
404            Some(cb_action::<C>),
405            Some(cb_valid::<C>),
406            ctxt as *mut _,
407        );
408    }
409}
410
411pub trait ProjectCommand: 'static + Sync {
412    fn action(&self, project: &Project);
413    fn valid(&self, project: &Project) -> bool;
414}
415
416pub fn register_command_for_project<C: ProjectCommand>(name: &str, desc: &str, command: C) {
417    extern "C" fn cb_action<C>(ctxt: *mut c_void, project: *mut BNProject)
418    where
419        C: ProjectCommand,
420    {
421        let cmd = unsafe { &*(ctxt as *const C) };
422        let handle = NonNull::new(project).expect("project handle is null");
423        let project = Project { handle };
424        cmd.action(&project);
425    }
426
427    extern "C" fn cb_valid<C>(ctxt: *mut c_void, project: *mut BNProject) -> bool
428    where
429        C: ProjectCommand,
430    {
431        let cmd = unsafe { &*(ctxt as *const C) };
432        let handle = NonNull::new(project).expect("project handle is null");
433        let project = Project { handle };
434        cmd.valid(&project)
435    }
436
437    let name = name.to_cstr();
438    let desc = desc.to_cstr();
439    let ctxt = Box::into_raw(Box::new(command));
440    unsafe {
441        BNRegisterPluginCommandForProject(
442            name.as_ptr(),
443            desc.as_ptr(),
444            Some(cb_action::<C>),
445            Some(cb_valid::<C>),
446            ctxt as *mut _,
447        );
448    }
449}