1#![allow(clippy::missing_safety_doc)]
17#![allow(clippy::result_unit_err)]
18#![allow(clippy::type_complexity)]
19#![allow(clippy::too_many_arguments)]
20#![allow(clippy::needless_doctest_main)]
21#![doc(html_root_url = "https://dev-rust.binary.ninja/")]
22#![doc(html_favicon_url = "https://binary.ninja/icons/favicon-32x32.png")]
23#![doc(html_logo_url = "https://binary.ninja/icons/android-chrome-512x512.png")]
24#![doc(issue_tracker_base_url = "https://github.com/Vector35/binaryninja-api/issues/")]
25#![doc = include_str!("../README.md")]
26
27#[macro_use]
28mod ffi;
29
30pub mod architecture;
31pub mod background_task;
32pub mod base_detection;
33pub mod basic_block;
34pub mod binary_view;
35pub mod calling_convention;
36pub mod collaboration;
37pub mod command;
38pub mod component;
39pub mod confidence;
40pub mod custom_binary_view;
41pub mod data_buffer;
42pub mod data_notification;
43pub mod data_renderer;
44pub mod database;
45pub mod debuginfo;
46pub mod demangle;
47pub mod disassembly;
48pub mod download;
49pub mod enterprise;
50pub mod external_library;
51pub mod file_accessor;
52pub mod file_metadata;
53pub mod flowgraph;
54pub mod function;
55pub mod function_recognizer;
56pub mod headless;
57pub mod high_level_il;
58pub mod interaction;
59pub mod language_representation;
60pub mod line_formatter;
61pub mod linear_view;
62pub mod llvm;
63pub mod logger;
64pub mod low_level_il;
65pub mod main_thread;
66pub mod medium_level_il;
67pub mod metadata;
68pub mod platform;
69pub mod progress;
70pub mod project;
71pub mod qualified_name;
72pub mod rc;
73pub mod references;
74pub mod relocation;
75pub mod render_layer;
76pub mod repository;
77pub mod secrets_provider;
78pub mod section;
79pub mod segment;
80pub mod settings;
81pub mod string;
82pub mod symbol;
83pub mod tags;
84pub mod template_simplifier;
85pub mod tracing;
86pub mod types;
87pub mod update;
88pub mod variable;
89pub mod websocket;
90pub mod worker_thread;
91pub mod workflow;
92
93use crate::file_metadata::FileMetadata;
94use crate::function::Function;
95use crate::progress::{NoProgressCallback, ProgressCallback};
96use crate::string::raw_to_string;
97use binary_view::BinaryView;
98use binaryninjacore_sys::*;
99use metadata::Metadata;
100use metadata::MetadataType;
101use rc::Ref;
102use std::cmp;
103use std::collections::HashMap;
104use std::ffi::{c_char, c_void, CStr};
105use std::fmt::{Display, Formatter};
106use std::path::{Path, PathBuf};
107use string::BnString;
108use string::IntoCStr;
109use string::IntoJson;
110
111use crate::project::file::ProjectFile;
112pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption;
113pub use binaryninjacore_sys::BNEndianness as Endianness;
114pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence;
115
116pub const BN_FULL_CONFIDENCE: u8 = u8::MAX;
117pub const BN_INVALID_EXPR: usize = usize::MAX;
118
119pub fn load(file_path: impl AsRef<Path>) -> Option<Ref<BinaryView>> {
121 load_with_progress(file_path, NoProgressCallback)
122}
123
124pub fn load_with_progress<P: ProgressCallback>(
128 file_path: impl AsRef<Path>,
129 mut progress: P,
130) -> Option<Ref<BinaryView>> {
131 let file_path = file_path.as_ref().to_cstr();
132 let options = c"";
133 let handle = unsafe {
134 BNLoadFilename(
135 file_path.as_ptr() as *mut _,
136 true,
137 options.as_ptr() as *mut c_char,
138 Some(P::cb_progress_callback),
139 &mut progress as *mut P as *mut c_void,
140 )
141 };
142
143 if handle.is_null() {
144 None
145 } else {
146 Some(unsafe { BinaryView::ref_from_raw(handle) })
147 }
148}
149
150pub fn load_with_options<O>(
168 file_path: impl AsRef<Path>,
169 update_analysis_and_wait: bool,
170 options: Option<O>,
171) -> Option<Ref<BinaryView>>
172where
173 O: IntoJson,
174{
175 load_with_options_and_progress(
176 file_path,
177 update_analysis_and_wait,
178 options,
179 NoProgressCallback,
180 )
181}
182
183pub fn load_with_options_and_progress<O, P>(
187 file_path: impl AsRef<Path>,
188 update_analysis_and_wait: bool,
189 options: Option<O>,
190 mut progress: P,
191) -> Option<Ref<BinaryView>>
192where
193 O: IntoJson,
194 P: ProgressCallback,
195{
196 let file_path = file_path.as_ref().to_cstr();
197 let options_or_default = if let Some(opt) = options {
198 opt.get_json_string()
199 .ok()?
200 .to_cstr()
201 .to_bytes_with_nul()
202 .to_vec()
203 } else {
204 Metadata::new_of_type(MetadataType::KeyValueDataType)
205 .get_json_string()
206 .ok()?
207 .as_ref()
208 .to_vec()
209 };
210 let handle = unsafe {
211 BNLoadFilename(
212 file_path.as_ptr() as *mut _,
213 update_analysis_and_wait,
214 options_or_default.as_ptr() as *mut c_char,
215 Some(P::cb_progress_callback),
216 &mut progress as *mut P as *mut c_void,
217 )
218 };
219
220 if handle.is_null() {
221 None
222 } else {
223 Some(unsafe { BinaryView::ref_from_raw(handle) })
224 }
225}
226
227pub fn load_view<O>(
228 bv: &BinaryView,
229 update_analysis_and_wait: bool,
230 options: Option<O>,
231) -> Option<Ref<BinaryView>>
232where
233 O: IntoJson,
234{
235 load_view_with_progress(bv, update_analysis_and_wait, options, NoProgressCallback)
236}
237
238pub fn load_view_with_progress<O, P>(
240 bv: &BinaryView,
241 update_analysis_and_wait: bool,
242 options: Option<O>,
243 mut progress: P,
244) -> Option<Ref<BinaryView>>
245where
246 O: IntoJson,
247 P: ProgressCallback,
248{
249 let options_or_default = if let Some(opt) = options {
250 opt.get_json_string()
251 .ok()?
252 .to_cstr()
253 .to_bytes_with_nul()
254 .to_vec()
255 } else {
256 "{}".to_cstr().to_bytes_with_nul().to_vec()
257 };
258 let handle = unsafe {
259 BNLoadBinaryView(
260 bv.handle as *mut _,
261 update_analysis_and_wait,
262 options_or_default.as_ptr() as *mut c_char,
263 Some(P::cb_progress_callback),
264 &mut progress as *mut P as *mut c_void,
265 )
266 };
267
268 if handle.is_null() {
269 None
270 } else {
271 Some(unsafe { BinaryView::ref_from_raw(handle) })
272 }
273}
274
275pub fn load_project_file<O>(
276 file: &ProjectFile,
277 update_analysis_and_wait: bool,
278 options: Option<O>,
279) -> Option<Ref<BinaryView>>
280where
281 O: IntoJson,
282{
283 load_project_file_with_progress(file, update_analysis_and_wait, options, NoProgressCallback)
284}
285
286pub fn load_project_file_with_progress<O, P>(
288 file: &ProjectFile,
289 update_analysis_and_wait: bool,
290 options: Option<O>,
291 mut progress: P,
292) -> Option<Ref<BinaryView>>
293where
294 O: IntoJson,
295 P: ProgressCallback,
296{
297 let options_or_default = if let Some(opt) = options {
298 opt.get_json_string()
299 .ok()?
300 .to_cstr()
301 .to_bytes_with_nul()
302 .to_vec()
303 } else {
304 "{}".to_cstr().to_bytes_with_nul().to_vec()
305 };
306 let handle = unsafe {
307 BNLoadProjectFile(
308 file.handle.as_ptr(),
309 update_analysis_and_wait,
310 options_or_default.as_ptr() as *mut c_char,
311 Some(P::cb_progress_callback),
312 &mut progress as *mut P as *mut c_void,
313 )
314 };
315
316 if handle.is_null() {
317 None
318 } else {
319 Some(unsafe { BinaryView::ref_from_raw(handle) })
320 }
321}
322
323pub fn install_directory() -> PathBuf {
324 let install_dir_ptr: *mut c_char = unsafe { BNGetInstallDirectory() };
325 assert!(!install_dir_ptr.is_null());
326 let install_dir_str = unsafe { BnString::into_string(install_dir_ptr) };
327 PathBuf::from(install_dir_str)
328}
329
330pub fn bundled_plugin_directory() -> Result<PathBuf, ()> {
331 let s: *mut c_char = unsafe { BNGetBundledPluginDirectory() };
332 if s.is_null() {
333 return Err(());
334 }
335 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
336}
337
338pub fn set_bundled_plugin_directory(new_dir: impl AsRef<Path>) {
339 let new_dir = new_dir.as_ref().to_cstr();
340 unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr()) };
341}
342
343pub fn user_directory() -> PathBuf {
344 let user_dir_ptr: *mut c_char = unsafe { BNGetUserDirectory() };
345 assert!(!user_dir_ptr.is_null());
346 let user_dir_str = unsafe { BnString::into_string(user_dir_ptr) };
347 PathBuf::from(user_dir_str)
348}
349
350pub fn user_plugin_directory() -> Result<PathBuf, ()> {
351 let s: *mut c_char = unsafe { BNGetUserPluginDirectory() };
352 if s.is_null() {
353 return Err(());
354 }
355 let user_plugin_dir_str = unsafe { BnString::into_string(s) };
356 Ok(PathBuf::from(user_plugin_dir_str))
357}
358
359pub fn repositories_directory() -> Result<PathBuf, ()> {
360 let s: *mut c_char = unsafe { BNGetRepositoriesDirectory() };
361 if s.is_null() {
362 return Err(());
363 }
364 let repo_dir_str = unsafe { BnString::into_string(s) };
365 Ok(PathBuf::from(repo_dir_str))
366}
367
368pub fn settings_file_path() -> PathBuf {
369 let settings_file_name_ptr: *mut c_char = unsafe { BNGetSettingsFileName() };
370 assert!(!settings_file_name_ptr.is_null());
371 let settings_file_path_str = unsafe { BnString::into_string(settings_file_name_ptr) };
372 PathBuf::from(settings_file_path_str)
373}
374
375pub fn save_last_run() {
379 unsafe { BNSaveLastRun() };
380}
381
382pub fn path_relative_to_bundled_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
383 let path_raw = path.as_ref().to_cstr();
384 let s: *mut c_char = unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr()) };
385 if s.is_null() {
386 return Err(());
387 }
388 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
389}
390
391pub fn path_relative_to_user_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
392 let path_raw = path.as_ref().to_cstr();
393 let s: *mut c_char = unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr()) };
394 if s.is_null() {
395 return Err(());
396 }
397 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
398}
399
400pub fn path_relative_to_user_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
401 let path_raw = path.as_ref().to_cstr();
402 let s: *mut c_char = unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr()) };
403 if s.is_null() {
404 return Err(());
405 }
406 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
407}
408
409pub fn is_main_thread() -> bool {
413 unsafe { BNIsMainThread() }
414}
415
416pub fn memory_info() -> HashMap<String, u64> {
417 let mut count = 0;
418 let mut usage = HashMap::new();
419 unsafe {
420 let info_ptr = BNGetMemoryUsageInfo(&mut count);
421 let info_list = std::slice::from_raw_parts(info_ptr, count);
422 for info in info_list {
423 let info_name = CStr::from_ptr(info.name).to_str().unwrap().to_string();
424 usage.insert(info_name, info.value);
425 }
426 BNFreeMemoryUsageInfo(info_ptr, count);
427 }
428 usage
429}
430
431pub trait ObjectDestructor: 'static + Sync + Sized {
433 fn destruct_view(&self, _view: &BinaryView) {}
434 fn destruct_file_metadata(&self, _metadata: &FileMetadata) {}
435 fn destruct_function(&self, _func: &Function) {}
436
437 unsafe extern "C" fn cb_destruct_binary_view(ctxt: *mut c_void, view: *mut BNBinaryView) {
438 ffi_wrap!("ObjectDestructor::destruct_view", {
439 let view_type = &*(ctxt as *mut Self);
440 let view = BinaryView { handle: view };
441 view_type.destruct_view(&view);
442 })
443 }
444
445 unsafe extern "C" fn cb_destruct_file_metadata(ctxt: *mut c_void, file: *mut BNFileMetadata) {
446 ffi_wrap!("ObjectDestructor::destruct_file_metadata", {
447 let view_type = &*(ctxt as *mut Self);
448 let file = FileMetadata::from_raw(file);
449 view_type.destruct_file_metadata(&file);
450 })
451 }
452
453 unsafe extern "C" fn cb_destruct_function(ctxt: *mut c_void, func: *mut BNFunction) {
454 ffi_wrap!("ObjectDestructor::destruct_function", {
455 let view_type = &*(ctxt as *mut Self);
456 let func = Function { handle: func };
457 view_type.destruct_function(&func);
458 })
459 }
460
461 unsafe fn as_callbacks(&'static mut self) -> BNObjectDestructionCallbacks {
462 BNObjectDestructionCallbacks {
463 context: std::mem::transmute(&self),
464 destructBinaryView: Some(Self::cb_destruct_binary_view),
465 destructFileMetadata: Some(Self::cb_destruct_file_metadata),
466 destructFunction: Some(Self::cb_destruct_function),
467 }
468 }
469
470 fn register(&'static mut self) {
471 unsafe { BNRegisterObjectDestructionCallbacks(&mut self.as_callbacks()) };
472 }
473
474 fn unregister(&'static mut self) {
475 unsafe { BNUnregisterObjectDestructionCallbacks(&mut self.as_callbacks()) };
476 }
477}
478
479pub fn version() -> String {
480 unsafe { BnString::into_string(BNGetVersionString()) }
481}
482
483pub fn build_id() -> u32 {
484 unsafe { BNGetBuildId() }
485}
486
487#[derive(Clone, PartialEq, Eq, Hash, Debug)]
488pub struct VersionInfo {
489 pub major: u32,
490 pub minor: u32,
491 pub build: u32,
492 pub channel: String,
493}
494
495impl VersionInfo {
496 pub(crate) fn from_raw(value: &BNVersionInfo) -> Self {
497 Self {
498 major: value.major,
499 minor: value.minor,
500 build: value.build,
501 channel: raw_to_string(value.channel).unwrap_or_default(),
503 }
504 }
505
506 pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self {
507 let owned = Self::from_raw(&value);
508 Self::free_raw(value);
509 owned
510 }
511
512 pub(crate) fn into_owned_raw(value: &Self) -> BNVersionInfo {
513 BNVersionInfo {
514 major: value.major,
515 minor: value.minor,
516 build: value.build,
517 channel: value.channel.as_ptr() as *mut c_char,
518 }
519 }
520
521 pub(crate) fn free_raw(value: BNVersionInfo) {
522 unsafe { BnString::free_raw(value.channel) };
523 }
524}
525
526impl TryFrom<&str> for VersionInfo {
527 type Error = ();
528
529 fn try_from(value: &str) -> Result<Self, Self::Error> {
530 let string = value.to_cstr();
531 let result = unsafe { BNParseVersionString(string.as_ptr()) };
532 if result.build == 0 && result.channel.is_null() && result.major == 0 && result.minor == 0 {
533 return Err(());
534 }
535 Ok(Self::from_owned_raw(result))
536 }
537}
538
539impl PartialOrd for VersionInfo {
540 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
541 Some(self.cmp(other))
542 }
543}
544
545impl Ord for VersionInfo {
546 fn cmp(&self, other: &Self) -> cmp::Ordering {
547 if self == other {
548 return cmp::Ordering::Equal;
549 }
550 let bn_version_0 = VersionInfo::into_owned_raw(self);
551 let bn_version_1 = VersionInfo::into_owned_raw(other);
552 if unsafe { BNVersionLessThan(bn_version_0, bn_version_1) } {
553 cmp::Ordering::Less
554 } else {
555 cmp::Ordering::Greater
556 }
557 }
558}
559
560impl Display for VersionInfo {
561 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
562 if self.channel.is_empty() {
563 write!(f, "{}.{}.{}", self.major, self.minor, self.build)
564 } else {
565 write!(
566 f,
567 "{}.{}.{}-{}",
568 self.major, self.minor, self.build, self.channel
569 )
570 }
571 }
572}
573
574pub fn version_info() -> VersionInfo {
575 let info_raw = unsafe { BNGetVersionInfo() };
576 VersionInfo::from_owned_raw(info_raw)
577}
578
579pub fn serial_number() -> String {
580 unsafe { BnString::into_string(BNGetSerialNumber()) }
581}
582
583pub fn is_license_validated() -> bool {
584 unsafe { BNIsLicenseValidated() }
585}
586
587pub fn licensed_user_email() -> String {
588 unsafe { BnString::into_string(BNGetLicensedUserEmail()) }
589}
590
591pub fn license_path() -> PathBuf {
592 user_directory().join("license.dat")
593}
594
595pub fn license_count() -> i32 {
596 unsafe { BNGetLicenseCount() }
597}
598
599#[cfg(not(feature = "demo"))]
605pub fn set_license(license: Option<&str>) {
606 let license = license.unwrap_or_default().to_cstr();
607 unsafe { BNSetLicense(license.as_ptr()) }
608}
609
610#[cfg(feature = "demo")]
611pub fn set_license(_license: Option<&str>) {}
612
613pub fn product() -> String {
614 unsafe { BnString::into_string(BNGetProduct()) }
615}
616
617pub fn product_type() -> String {
618 unsafe { BnString::into_string(BNGetProductType()) }
619}
620
621pub fn license_expiration_time() -> std::time::SystemTime {
622 let m = std::time::Duration::from_secs(unsafe { BNGetLicenseExpirationTime() });
623 std::time::UNIX_EPOCH + m
624}
625
626pub fn is_ui_enabled() -> bool {
627 unsafe { BNIsUIEnabled() }
628}
629
630pub fn is_database(file: &Path) -> bool {
631 let filename = file.to_cstr();
632 unsafe { BNIsDatabase(filename.as_ptr()) }
633}
634
635pub fn plugin_abi_version() -> u32 {
636 BN_CURRENT_CORE_ABI_VERSION
637}
638
639pub fn plugin_abi_minimum_version() -> u32 {
640 BN_MINIMUM_CORE_ABI_VERSION
641}
642
643pub fn core_abi_version() -> u32 {
644 unsafe { BNGetCurrentCoreABIVersion() }
645}
646
647pub fn core_abi_minimum_version() -> u32 {
648 unsafe { BNGetMinimumCoreABIVersion() }
649}
650
651pub fn plugin_ui_abi_version() -> u32 {
652 BN_CURRENT_UI_ABI_VERSION
653}
654
655pub fn plugin_ui_abi_minimum_version() -> u32 {
656 BN_MINIMUM_UI_ABI_VERSION
657}
658
659pub fn add_required_plugin_dependency(name: &str) {
660 let raw_name = name.to_cstr();
661 unsafe { BNAddRequiredPluginDependency(raw_name.as_ptr()) };
662}
663
664pub fn add_optional_plugin_dependency(name: &str) {
665 let raw_name = name.to_cstr();
666 unsafe { BNAddOptionalPluginDependency(raw_name.as_ptr()) };
667}
668
669#[cfg(not(feature = "no_exports"))]
671#[no_mangle]
672#[allow(non_snake_case)]
673pub extern "C" fn CorePluginABIVersion() -> u32 {
674 plugin_abi_version()
675}
676
677#[cfg(not(feature = "no_exports"))]
679#[no_mangle]
680pub extern "C" fn UIPluginABIVersion() -> u32 {
681 plugin_ui_abi_version()
682}