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 = "/brand/favicon-32x32.png")]
23#![doc(html_logo_url = "/brand/logo-vertical-dark.svg")]
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 data_buffer;
41pub mod data_notification;
42pub mod data_renderer;
43pub mod database;
44pub mod debuginfo;
45pub mod demangle;
46pub mod disassembly;
47pub mod download;
48pub mod enterprise;
49pub mod external_library;
50pub mod file_accessor;
51pub mod file_metadata;
52pub mod flowgraph;
53pub mod function;
54pub mod function_recognizer;
55pub mod headless;
56pub mod high_level_il;
57pub mod interaction;
58pub mod language_representation;
59pub mod line_formatter;
60pub mod linear_view;
61pub mod llvm;
62pub mod logger;
63pub mod low_level_il;
64pub mod main_thread;
65pub mod medium_level_il;
66pub mod metadata;
67pub mod object_destructor;
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 similarity;
82pub mod string;
83pub mod string_detection;
84pub mod symbol;
85pub mod tags;
86pub mod tracing;
87pub mod transform;
88pub mod types;
89pub mod update;
90pub mod variable;
91pub mod websocket;
92pub mod worker_thread;
93pub mod workflow;
94
95use crate::progress::{NoProgressCallback, ProgressCallback};
96use crate::string::raw_to_string;
97use binary_view::BinaryView;
98use binaryninjacore_sys::*;
99use rc::Ref;
100use std::cmp;
101use std::collections::HashMap;
102use std::ffi::{c_char, c_void, CStr};
103use std::fmt::{Display, Formatter};
104use std::path::{Path, PathBuf};
105use string::BnString;
106use string::IntoCStr;
107use string::IntoJson;
108
109use crate::project::file::ProjectFile;
110pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption;
111pub use binaryninjacore_sys::BNEndianness as Endianness;
112pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence;
113
114pub const BN_FULL_CONFIDENCE: u8 = u8::MAX;
115pub const BN_INVALID_EXPR: usize = usize::MAX;
116
117pub fn fuzzy_match_single(target: &str, query: &str) -> Option<usize> {
120 let target = target.to_cstr();
121 let query = query.to_cstr();
122 let score = unsafe { BNFuzzyMatchSingle(target.as_ptr(), query.as_ptr()) };
123 (score != 0).then_some(score)
124}
125
126pub fn fuzzy_match_contextual(target: &str, query: &str) -> Option<usize> {
131 let target = target.to_cstr();
132 let query = query.to_cstr();
133 let score = unsafe { BNFuzzyMatchContextual(target.as_ptr(), query.as_ptr()) };
134 (score != 0).then_some(score)
135}
136
137pub fn load(file_path: impl AsRef<Path>) -> Option<Ref<BinaryView>> {
139 load_with_progress(file_path, NoProgressCallback)
140}
141
142pub fn load_with_progress<P: ProgressCallback>(
146 file_path: impl AsRef<Path>,
147 mut progress: P,
148) -> Option<Ref<BinaryView>> {
149 let file_path = file_path.as_ref().to_cstr();
150 let options = c"";
151 let handle = unsafe {
152 BNLoadFilename(
153 file_path.as_ptr() as *mut _,
154 true,
155 options.as_ptr() as *mut c_char,
156 Some(P::cb_progress_callback),
157 &mut progress as *mut P as *mut c_void,
158 )
159 };
160
161 if handle.is_null() {
162 None
163 } else {
164 Some(unsafe { BinaryView::ref_from_raw(handle) })
165 }
166}
167
168pub fn load_with_options<O>(
186 file_path: impl AsRef<Path>,
187 update_analysis_and_wait: bool,
188 options: Option<O>,
189) -> Option<Ref<BinaryView>>
190where
191 O: IntoJson,
192{
193 load_with_options_and_progress(
194 file_path,
195 update_analysis_and_wait,
196 options,
197 NoProgressCallback,
198 )
199}
200
201pub fn load_with_options_and_progress<O, P>(
205 file_path: impl AsRef<Path>,
206 update_analysis_and_wait: bool,
207 options: Option<O>,
208 mut progress: P,
209) -> Option<Ref<BinaryView>>
210where
211 O: IntoJson,
212 P: ProgressCallback,
213{
214 let file_path = file_path.as_ref().to_cstr();
215 let options_or_default = if let Some(opt) = options {
216 opt.get_json_string()
217 .ok()?
218 .to_cstr()
219 .to_bytes_with_nul()
220 .to_vec()
221 } else {
222 "{}".to_cstr().to_bytes_with_nul().to_vec()
223 };
224 let handle = unsafe {
225 BNLoadFilename(
226 file_path.as_ptr() as *mut _,
227 update_analysis_and_wait,
228 options_or_default.as_ptr() as *mut c_char,
229 Some(P::cb_progress_callback),
230 &mut progress as *mut P as *mut c_void,
231 )
232 };
233
234 if handle.is_null() {
235 None
236 } else {
237 Some(unsafe { BinaryView::ref_from_raw(handle) })
238 }
239}
240
241pub fn load_view<O>(
242 bv: &BinaryView,
243 update_analysis_and_wait: bool,
244 options: Option<O>,
245) -> Option<Ref<BinaryView>>
246where
247 O: IntoJson,
248{
249 load_view_with_progress(bv, update_analysis_and_wait, options, NoProgressCallback)
250}
251
252pub fn load_view_with_progress<O, P>(
254 bv: &BinaryView,
255 update_analysis_and_wait: bool,
256 options: Option<O>,
257 mut progress: P,
258) -> Option<Ref<BinaryView>>
259where
260 O: IntoJson,
261 P: ProgressCallback,
262{
263 let options_or_default = if let Some(opt) = options {
264 opt.get_json_string()
265 .ok()?
266 .to_cstr()
267 .to_bytes_with_nul()
268 .to_vec()
269 } else {
270 "{}".to_cstr().to_bytes_with_nul().to_vec()
271 };
272 let handle = unsafe {
273 BNLoadBinaryView(
274 bv.handle as *mut _,
275 update_analysis_and_wait,
276 options_or_default.as_ptr() as *mut c_char,
277 Some(P::cb_progress_callback),
278 &mut progress as *mut P as *mut c_void,
279 )
280 };
281
282 if handle.is_null() {
283 None
284 } else {
285 Some(unsafe { BinaryView::ref_from_raw(handle) })
286 }
287}
288
289pub fn load_project_file<O>(
290 file: &ProjectFile,
291 update_analysis_and_wait: bool,
292 options: Option<O>,
293) -> Option<Ref<BinaryView>>
294where
295 O: IntoJson,
296{
297 load_project_file_with_progress(file, update_analysis_and_wait, options, NoProgressCallback)
298}
299
300pub fn load_project_file_with_progress<O, P>(
302 file: &ProjectFile,
303 update_analysis_and_wait: bool,
304 options: Option<O>,
305 mut progress: P,
306) -> Option<Ref<BinaryView>>
307where
308 O: IntoJson,
309 P: ProgressCallback,
310{
311 let options_or_default = if let Some(opt) = options {
312 opt.get_json_string()
313 .ok()?
314 .to_cstr()
315 .to_bytes_with_nul()
316 .to_vec()
317 } else {
318 "{}".to_cstr().to_bytes_with_nul().to_vec()
319 };
320 let handle = unsafe {
321 BNLoadProjectFile(
322 file.handle.as_ptr(),
323 update_analysis_and_wait,
324 options_or_default.as_ptr() as *mut c_char,
325 Some(P::cb_progress_callback),
326 &mut progress as *mut P as *mut c_void,
327 )
328 };
329
330 if handle.is_null() {
331 None
332 } else {
333 Some(unsafe { BinaryView::ref_from_raw(handle) })
334 }
335}
336
337pub fn install_directory() -> PathBuf {
338 let install_dir_ptr: *mut c_char = unsafe { BNGetInstallDirectory() };
339 assert!(!install_dir_ptr.is_null());
340 let install_dir_str = unsafe { BnString::into_string(install_dir_ptr) };
341 PathBuf::from(install_dir_str)
342}
343
344pub fn bundled_plugin_directory() -> Result<PathBuf, ()> {
345 let s: *mut c_char = unsafe { BNGetBundledPluginDirectory() };
346 if s.is_null() {
347 return Err(());
348 }
349 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
350}
351
352pub fn set_bundled_plugin_directory(new_dir: impl AsRef<Path>) {
353 let new_dir = new_dir.as_ref().to_cstr();
354 unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr()) };
355}
356
357pub fn user_directory() -> PathBuf {
358 let user_dir_ptr: *mut c_char = unsafe { BNGetUserDirectory() };
359 assert!(!user_dir_ptr.is_null());
360 let user_dir_str = unsafe { BnString::into_string(user_dir_ptr) };
361 PathBuf::from(user_dir_str)
362}
363
364pub fn user_plugin_directory() -> Result<PathBuf, ()> {
365 let s: *mut c_char = unsafe { BNGetUserPluginDirectory() };
366 if s.is_null() {
367 return Err(());
368 }
369 let user_plugin_dir_str = unsafe { BnString::into_string(s) };
370 Ok(PathBuf::from(user_plugin_dir_str))
371}
372
373pub fn repositories_directory() -> Result<PathBuf, ()> {
374 let s: *mut c_char = unsafe { BNGetRepositoriesDirectory() };
375 if s.is_null() {
376 return Err(());
377 }
378 let repo_dir_str = unsafe { BnString::into_string(s) };
379 Ok(PathBuf::from(repo_dir_str))
380}
381
382pub fn settings_file_path() -> PathBuf {
383 let settings_file_name_ptr: *mut c_char = unsafe { BNGetSettingsFileName() };
384 assert!(!settings_file_name_ptr.is_null());
385 let settings_file_path_str = unsafe { BnString::into_string(settings_file_name_ptr) };
386 PathBuf::from(settings_file_path_str)
387}
388
389pub fn save_last_run() {
393 unsafe { BNSaveLastRun() };
394}
395
396pub fn path_relative_to_bundled_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
397 let path_raw = path.as_ref().to_cstr();
398 let s: *mut c_char = unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr()) };
399 if s.is_null() {
400 return Err(());
401 }
402 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
403}
404
405pub fn path_relative_to_user_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
406 let path_raw = path.as_ref().to_cstr();
407 let s: *mut c_char = unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr()) };
408 if s.is_null() {
409 return Err(());
410 }
411 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
412}
413
414pub fn path_relative_to_user_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
415 let path_raw = path.as_ref().to_cstr();
416 let s: *mut c_char = unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr()) };
417 if s.is_null() {
418 return Err(());
419 }
420 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
421}
422
423pub fn is_main_thread() -> bool {
427 unsafe { BNIsMainThread() }
428}
429
430pub fn memory_info() -> HashMap<String, u64> {
431 let mut count = 0;
432 let mut usage = HashMap::new();
433 unsafe {
434 let info_ptr = BNGetMemoryUsageInfo(&mut count);
435 let info_list = std::slice::from_raw_parts(info_ptr, count);
436 for info in info_list {
437 let info_name = CStr::from_ptr(info.name).to_str().unwrap().to_string();
438 usage.insert(info_name, info.value);
439 }
440 BNFreeMemoryUsageInfo(info_ptr, count);
441 }
442 usage
443}
444
445pub fn version() -> String {
446 unsafe { BnString::into_string(BNGetVersionString()) }
447}
448
449pub fn build_id() -> u32 {
450 unsafe { BNGetBuildId() }
451}
452
453#[derive(Clone, PartialEq, Eq, Hash, Debug)]
454pub struct VersionInfo {
455 pub major: u32,
456 pub minor: u32,
457 pub build: u32,
458 pub channel: String,
459}
460
461impl VersionInfo {
462 pub(crate) fn from_raw(value: &BNVersionInfo) -> Self {
463 Self {
464 major: value.major,
465 minor: value.minor,
466 build: value.build,
467 channel: raw_to_string(value.channel).unwrap_or_default(),
469 }
470 }
471
472 pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self {
473 let owned = Self::from_raw(&value);
474 Self::free_raw(value);
475 owned
476 }
477
478 pub(crate) fn into_owned_raw(value: &Self) -> BNVersionInfo {
479 BNVersionInfo {
480 major: value.major,
481 minor: value.minor,
482 build: value.build,
483 channel: value.channel.as_ptr() as *mut c_char,
484 }
485 }
486
487 pub(crate) fn free_raw(value: BNVersionInfo) {
488 unsafe { BnString::free_raw(value.channel) };
489 }
490}
491
492impl TryFrom<&str> for VersionInfo {
493 type Error = ();
494
495 fn try_from(value: &str) -> Result<Self, Self::Error> {
496 let string = value.to_cstr();
497 let result = unsafe { BNParseVersionString(string.as_ptr()) };
498 if result.build == 0 && result.channel.is_null() && result.major == 0 && result.minor == 0 {
499 return Err(());
500 }
501 Ok(Self::from_owned_raw(result))
502 }
503}
504
505impl PartialOrd for VersionInfo {
506 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
507 Some(self.cmp(other))
508 }
509}
510
511impl Ord for VersionInfo {
512 fn cmp(&self, other: &Self) -> cmp::Ordering {
513 if self == other {
514 return cmp::Ordering::Equal;
515 }
516 let bn_version_0 = VersionInfo::into_owned_raw(self);
517 let bn_version_1 = VersionInfo::into_owned_raw(other);
518 if unsafe { BNVersionLessThan(bn_version_0, bn_version_1) } {
519 cmp::Ordering::Less
520 } else {
521 cmp::Ordering::Greater
522 }
523 }
524}
525
526impl Display for VersionInfo {
527 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
528 if self.channel.is_empty() {
529 write!(f, "{}.{}.{}", self.major, self.minor, self.build)
530 } else {
531 write!(
532 f,
533 "{}.{}.{}-{}",
534 self.major, self.minor, self.build, self.channel
535 )
536 }
537 }
538}
539
540pub fn version_info() -> VersionInfo {
541 let info_raw = unsafe { BNGetVersionInfo() };
542 VersionInfo::from_owned_raw(info_raw)
543}
544
545pub fn serial_number() -> String {
546 unsafe { BnString::into_string(BNGetSerialNumber()) }
547}
548
549pub fn is_license_validated() -> bool {
550 unsafe { BNIsLicenseValidated() }
551}
552
553pub fn licensed_user_email() -> String {
554 unsafe { BnString::into_string(BNGetLicensedUserEmail()) }
555}
556
557pub fn license_path() -> PathBuf {
558 user_directory().join("license.dat")
559}
560
561pub fn license_count() -> i32 {
562 unsafe { BNGetLicenseCount() }
563}
564
565#[derive(Clone, Debug, Eq, PartialEq)]
566pub struct LicenseAddon {
567 pub id: String,
568 pub license_serial: String,
569 pub product: String,
570 pub created: String,
571 pub created_timestamp: u64,
572 pub expiration: String,
573 pub expiration_timestamp: u64,
574 pub signature: String,
575}
576
577pub fn license_addons() -> Vec<LicenseAddon> {
578 let mut count = 0;
579 let addons = unsafe { BNGetLicenseAddons(&mut count) };
580 if addons.is_null() {
581 return Vec::new();
582 }
583
584 let result = unsafe { std::slice::from_raw_parts(addons, count) }
585 .iter()
586 .map(|addon| LicenseAddon {
587 id: unsafe { CStr::from_ptr(addon.id).to_string_lossy().into_owned() },
588 license_serial: unsafe {
589 CStr::from_ptr(addon.licenseSerial)
590 .to_string_lossy()
591 .into_owned()
592 },
593 product: unsafe { CStr::from_ptr(addon.product).to_string_lossy().into_owned() },
594 created: unsafe { CStr::from_ptr(addon.created).to_string_lossy().into_owned() },
595 created_timestamp: addon.createdTimestamp,
596 expiration: unsafe {
597 CStr::from_ptr(addon.expiration)
598 .to_string_lossy()
599 .into_owned()
600 },
601 expiration_timestamp: addon.expirationTimestamp,
602 signature: unsafe {
603 CStr::from_ptr(addon.signature)
604 .to_string_lossy()
605 .into_owned()
606 },
607 })
608 .collect();
609 unsafe { BNFreeLicenseAddons(addons, count) };
610 result
611}
612
613#[cfg(not(feature = "demo"))]
619pub fn set_license(license: Option<&str>) {
620 let license = license.unwrap_or_default().to_cstr();
621 unsafe { BNSetLicense(license.as_ptr()) }
622}
623
624#[cfg(feature = "demo")]
625pub fn set_license(_license: Option<&str>) {}
626
627pub fn product() -> String {
628 unsafe { BnString::into_string(BNGetProduct()) }
629}
630
631pub fn product_type() -> String {
632 unsafe { BnString::into_string(BNGetProductType()) }
633}
634
635pub fn license_expiration_time() -> std::time::SystemTime {
636 let m = std::time::Duration::from_secs(unsafe { BNGetLicenseExpirationTime() });
637 std::time::UNIX_EPOCH + m
638}
639
640pub fn is_ui_enabled() -> bool {
641 unsafe { BNIsUIEnabled() }
642}
643
644pub fn is_database(file: &Path) -> bool {
645 let filename = file.to_cstr();
646 unsafe { BNIsDatabase(filename.as_ptr()) }
647}
648
649pub fn plugin_abi_version() -> u32 {
650 BN_CURRENT_CORE_ABI_VERSION
651}
652
653pub fn plugin_abi_minimum_version() -> u32 {
654 BN_MINIMUM_CORE_ABI_VERSION
655}
656
657pub fn core_abi_version() -> u32 {
658 unsafe { BNGetCurrentCoreABIVersion() }
659}
660
661pub fn core_abi_minimum_version() -> u32 {
662 unsafe { BNGetMinimumCoreABIVersion() }
663}
664
665pub fn plugin_ui_abi_version() -> u32 {
666 BN_CURRENT_UI_ABI_VERSION
667}
668
669pub fn plugin_ui_abi_minimum_version() -> u32 {
670 BN_MINIMUM_UI_ABI_VERSION
671}
672
673pub fn add_required_plugin_dependency(name: &str) {
674 let raw_name = name.to_cstr();
675 unsafe { BNAddRequiredPluginDependency(raw_name.as_ptr()) };
676}
677
678pub fn add_optional_plugin_dependency(name: &str) {
679 let raw_name = name.to_cstr();
680 unsafe { BNAddOptionalPluginDependency(raw_name.as_ptr()) };
681}
682
683#[cfg(not(feature = "no_exports"))]
685#[no_mangle]
686#[allow(non_snake_case)]
687pub extern "C" fn CorePluginABIVersion() -> u32 {
688 plugin_abi_version()
689}
690
691#[cfg(not(feature = "no_exports"))]
693#[no_mangle]
694pub extern "C" fn UIPluginABIVersion() -> u32 {
695 plugin_ui_abi_version()
696}