binaryninja/
command.rs

1// Copyright 2021-2025 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        ffi_wrap!("Command::action", unsafe {
104            let cmd = &*(ctxt as *const C);
105
106            debug_assert!(!view.is_null());
107            let view = BinaryView { handle: view };
108
109            cmd.action(&view);
110        })
111    }
112
113    extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
114    where
115        C: Command,
116    {
117        ffi_wrap!("Command::valid", unsafe {
118            let cmd = &*(ctxt as *const C);
119
120            debug_assert!(!view.is_null());
121            let view = BinaryView { handle: view };
122
123            cmd.valid(&view)
124        })
125    }
126
127    let name = name.to_cstr();
128    let desc = desc.to_cstr();
129
130    let name_ptr = name.as_ptr();
131    let desc_ptr = desc.as_ptr();
132
133    let ctxt = Box::into_raw(Box::new(command));
134
135    unsafe {
136        BNRegisterPluginCommand(
137            name_ptr,
138            desc_ptr,
139            Some(cb_action::<C>),
140            Some(cb_valid::<C>),
141            ctxt as *mut _,
142        );
143    }
144}
145
146/// The trait required for address-associated commands.  See [register_command_for_address] for example usage.
147pub trait AddressCommand: 'static + Sync {
148    fn action(&self, view: &BinaryView, addr: u64);
149    fn valid(&self, view: &BinaryView, addr: u64) -> bool;
150}
151
152impl<T> AddressCommand for T
153where
154    T: 'static + Sync + Fn(&BinaryView, u64),
155{
156    fn action(&self, view: &BinaryView, addr: u64) {
157        self(view, addr);
158    }
159
160    fn valid(&self, _view: &BinaryView, _addr: u64) -> bool {
161        true
162    }
163}
164
165/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
166///
167/// # Example
168/// ```no_run
169/// # use binaryninja::command::AddressCommand;
170/// # use binaryninja::binary_view::BinaryView;
171/// struct MyCommand;
172///
173/// impl AddressCommand for MyCommand {
174///     fn action(&self, view: &BinaryView, addr: u64) {
175///         // Your code here
176///     }
177///
178///     fn valid(&self, view: &BinaryView, addr: u64) -> bool {
179///         // Your code here
180///         true
181///     }
182/// }
183///
184/// # use binaryninja::command::register_command_for_address;
185/// #[no_mangle]
186/// pub extern "C" fn CorePluginInit() -> bool {
187///     register_command_for_address(
188///         "My Plugin Command",
189///         "A description of my command",
190///         MyCommand {},
191///     );
192///     true
193/// }
194/// ```
195pub fn register_command_for_address<C: AddressCommand>(name: &str, desc: &str, command: C) {
196    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64)
197    where
198        C: AddressCommand,
199    {
200        ffi_wrap!("AddressCommand::action", unsafe {
201            let cmd = &*(ctxt as *const C);
202
203            debug_assert!(!view.is_null());
204            let view = BinaryView { handle: view };
205
206            cmd.action(&view, addr);
207        })
208    }
209
210    extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) -> bool
211    where
212        C: AddressCommand,
213    {
214        ffi_wrap!("AddressCommand::valid", unsafe {
215            let cmd = &*(ctxt as *const C);
216
217            debug_assert!(!view.is_null());
218            let view = BinaryView { handle: view };
219
220            cmd.valid(&view, addr)
221        })
222    }
223
224    let name = name.to_cstr();
225    let desc = desc.to_cstr();
226
227    let name_ptr = name.as_ptr();
228    let desc_ptr = desc.as_ptr();
229
230    let ctxt = Box::into_raw(Box::new(command));
231
232    unsafe {
233        BNRegisterPluginCommandForAddress(
234            name_ptr,
235            desc_ptr,
236            Some(cb_action::<C>),
237            Some(cb_valid::<C>),
238            ctxt as *mut _,
239        );
240    }
241}
242
243/// The trait required for range-associated commands.  See [register_command_for_range] for example usage.
244pub trait RangeCommand: 'static + Sync {
245    fn action(&self, view: &BinaryView, range: Range<u64>);
246    fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool;
247}
248
249impl<T> RangeCommand for T
250where
251    T: 'static + Sync + Fn(&BinaryView, Range<u64>),
252{
253    fn action(&self, view: &BinaryView, range: Range<u64>) {
254        self(view, range);
255    }
256
257    fn valid(&self, _view: &BinaryView, _range: Range<u64>) -> bool {
258        true
259    }
260}
261
262/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
263///
264/// # Example
265/// ```no_run
266/// # use std::ops::Range;
267/// # use binaryninja::command::RangeCommand;
268/// # use binaryninja::binary_view::BinaryView;
269/// struct MyCommand;
270///
271/// impl RangeCommand for MyCommand {
272///     fn action(&self, view: &BinaryView, range: Range<u64>) {
273///         // Your code here
274///     }
275///
276///     fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool {
277///         // Your code here
278///         true
279///     }
280/// }
281///
282/// # use binaryninja::command::register_command_for_range;
283/// #[no_mangle]
284/// pub extern "C" fn CorePluginInit() -> bool {
285///     register_command_for_range(
286///         "My Plugin Command",
287///         "A description of my command",
288///         MyCommand {},
289///     );
290///     true
291/// }
292/// ```
293pub fn register_command_for_range<C>(name: &str, desc: &str, command: C)
294where
295    C: RangeCommand,
296{
297    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64)
298    where
299        C: RangeCommand,
300    {
301        ffi_wrap!("RangeCommand::action", unsafe {
302            let cmd = &*(ctxt as *const C);
303
304            debug_assert!(!view.is_null());
305            let view = BinaryView { handle: view };
306
307            cmd.action(&view, addr..addr.wrapping_add(len));
308        })
309    }
310
311    extern "C" fn cb_valid<C>(
312        ctxt: *mut c_void,
313        view: *mut BNBinaryView,
314        addr: u64,
315        len: u64,
316    ) -> bool
317    where
318        C: RangeCommand,
319    {
320        ffi_wrap!("RangeCommand::valid", unsafe {
321            let cmd = &*(ctxt as *const C);
322
323            debug_assert!(!view.is_null());
324            let view = BinaryView { handle: view };
325
326            cmd.valid(&view, addr..addr.wrapping_add(len))
327        })
328    }
329
330    let name = name.to_cstr();
331    let desc = desc.to_cstr();
332
333    let name_ptr = name.as_ptr();
334    let desc_ptr = desc.as_ptr();
335
336    let ctxt = Box::into_raw(Box::new(command));
337
338    unsafe {
339        BNRegisterPluginCommandForRange(
340            name_ptr,
341            desc_ptr,
342            Some(cb_action::<C>),
343            Some(cb_valid::<C>),
344            ctxt as *mut _,
345        );
346    }
347}
348
349/// The trait required for function-associated commands.  See [register_command_for_function] for example usage.
350pub trait FunctionCommand: 'static + Sync {
351    fn action(&self, view: &BinaryView, func: &Function);
352    fn valid(&self, view: &BinaryView, func: &Function) -> bool;
353}
354
355impl<T> FunctionCommand for T
356where
357    T: 'static + Sync + Fn(&BinaryView, &Function),
358{
359    fn action(&self, view: &BinaryView, func: &Function) {
360        self(view, func);
361    }
362
363    fn valid(&self, _view: &BinaryView, _func: &Function) -> bool {
364        true
365    }
366}
367
368/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
369///
370/// # Example
371/// ```no_run
372/// # use binaryninja::command::FunctionCommand;
373/// # use binaryninja::binary_view::BinaryView;
374/// # use binaryninja::function::Function;
375/// # use binaryninja::command::register_command_for_function;
376/// struct MyCommand;
377///
378/// impl FunctionCommand for MyCommand {
379///     fn action(&self, view: &BinaryView, func: &Function) {
380///         // Your code here
381///     }
382///
383///     fn valid(&self, view: &BinaryView, func: &Function) -> bool {
384///         // Your code here
385///         true
386///     }
387/// }
388///
389/// #[no_mangle]
390/// pub extern "C" fn CorePluginInit() -> bool {
391///     register_command_for_function(
392///         "My Plugin Command",
393///         "A description of my command",
394///         MyCommand {},
395///     );
396///     true
397/// }
398/// ```
399pub fn register_command_for_function<C: FunctionCommand>(name: &str, desc: &str, command: C) {
400    extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction)
401    where
402        C: FunctionCommand,
403    {
404        ffi_wrap!("FunctionCommand::action", unsafe {
405            let cmd = &*(ctxt as *const C);
406
407            debug_assert!(!view.is_null());
408            let view = BinaryView { handle: view };
409
410            debug_assert!(!func.is_null());
411            let func = Function { handle: func };
412
413            cmd.action(&view, &func);
414        })
415    }
416
417    extern "C" fn cb_valid<C>(
418        ctxt: *mut c_void,
419        view: *mut BNBinaryView,
420        func: *mut BNFunction,
421    ) -> bool
422    where
423        C: FunctionCommand,
424    {
425        ffi_wrap!("FunctionCommand::valid", unsafe {
426            let cmd = &*(ctxt as *const C);
427
428            debug_assert!(!view.is_null());
429            let view = BinaryView { handle: view };
430
431            debug_assert!(!func.is_null());
432            let func = Function { handle: func };
433
434            cmd.valid(&view, &func)
435        })
436    }
437
438    let name = name.to_cstr();
439    let desc = desc.to_cstr();
440
441    let name_ptr = name.as_ptr();
442    let desc_ptr = desc.as_ptr();
443
444    let ctxt = Box::into_raw(Box::new(command));
445
446    unsafe {
447        BNRegisterPluginCommandForFunction(
448            name_ptr,
449            desc_ptr,
450            Some(cb_action::<C>),
451            Some(cb_valid::<C>),
452            ctxt as *mut _,
453        );
454    }
455}
456
457pub trait ProjectCommand: 'static + Sync {
458    fn action(&self, project: &Project);
459    fn valid(&self, project: &Project) -> bool;
460}
461
462pub fn register_command_for_project<C: ProjectCommand>(name: &str, desc: &str, command: C) {
463    extern "C" fn cb_action<C>(ctxt: *mut c_void, project: *mut BNProject)
464    where
465        C: ProjectCommand,
466    {
467        ffi_wrap!("Command::action", unsafe {
468            let cmd = &*(ctxt as *const C);
469
470            let handle = NonNull::new(project).expect("project handle is null");
471            let project = Project { handle };
472
473            cmd.action(&project);
474        })
475    }
476
477    extern "C" fn cb_valid<C>(ctxt: *mut c_void, project: *mut BNProject) -> bool
478    where
479        C: ProjectCommand,
480    {
481        ffi_wrap!("Command::valid", unsafe {
482            let cmd = &*(ctxt as *const C);
483
484            let handle = NonNull::new(project).expect("project handle is null");
485            let project = Project { handle };
486
487            cmd.valid(&project)
488        })
489    }
490
491    let name = name.to_cstr();
492    let desc = desc.to_cstr();
493
494    let name_ptr = name.as_ptr();
495    let desc_ptr = desc.as_ptr();
496
497    let ctxt = Box::into_raw(Box::new(command));
498
499    unsafe {
500        BNRegisterPluginCommandForProject(
501            name_ptr,
502            desc_ptr,
503            Some(cb_action::<C>),
504            Some(cb_valid::<C>),
505            ctxt as *mut _,
506        );
507    }
508}