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")]
26#![doc(html_logo_url = "/brand/logo-vertical-dark.svg")]
27#![doc(issue_tracker_base_url = "https://github.com/Vector35/binaryninja-api/issues/")]
28#![doc = include_str!("../README.md")]
29
30#[macro_use]
31mod ffi;
32
33pub mod architecture;
34pub mod background_task;
35pub mod base_detection;
36pub mod basic_block;
37pub mod binary_view;
38pub mod calling_convention;
39pub mod collaboration;
40pub mod command;
41pub mod component;
42pub mod confidence;
43pub mod data_buffer;
44pub mod data_notification;
45pub mod data_renderer;
46pub mod database;
47pub mod debuginfo;
48pub mod demangle;
49pub mod disassembly;
50pub mod download;
51pub mod enterprise;
52pub mod external_library;
53pub mod file_accessor;
54pub mod file_metadata;
55pub mod flowgraph;
56pub mod function;
57pub mod function_recognizer;
58pub mod headless;
59pub mod high_level_il;
60pub mod interaction;
61pub mod language_representation;
62pub mod line_formatter;
63pub mod linear_view;
64pub mod llvm;
65pub mod logger;
66pub mod low_level_il;
67pub mod main_thread;
68pub mod medium_level_il;
69pub mod metadata;
70pub mod object_destructor;
71pub mod platform;
72pub mod progress;
73pub mod project;
74pub mod qualified_name;
75pub mod rc;
76pub mod references;
77pub mod relocation;
78pub mod render_layer;
79pub mod repository;
80pub mod secrets_provider;
81pub mod section;
82pub mod segment;
83pub mod settings;
84pub mod string;
85pub mod string_detection;
86pub mod symbol;
87pub mod tags;
88pub mod tracing;
89pub mod transform;
90pub mod types;
91pub mod update;
92pub mod variable;
93pub mod websocket;
94pub mod worker_thread;
95pub mod workflow;
96
97use crate::progress::{NoProgressCallback, ProgressCallback};
98use crate::string::raw_to_string;
99use binary_view::BinaryView;
100use binaryninjacore_sys::*;
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 "{}".to_cstr().to_bytes_with_nul().to_vec()
205 };
206 let handle = unsafe {
207 BNLoadFilename(
208 file_path.as_ptr() as *mut _,
209 update_analysis_and_wait,
210 options_or_default.as_ptr() as *mut c_char,
211 Some(P::cb_progress_callback),
212 &mut progress as *mut P as *mut c_void,
213 )
214 };
215
216 if handle.is_null() {
217 None
218 } else {
219 Some(unsafe { BinaryView::ref_from_raw(handle) })
220 }
221}
222
223pub fn load_view<O>(
224 bv: &BinaryView,
225 update_analysis_and_wait: bool,
226 options: Option<O>,
227) -> Option<Ref<BinaryView>>
228where
229 O: IntoJson,
230{
231 load_view_with_progress(bv, update_analysis_and_wait, options, NoProgressCallback)
232}
233
234pub fn load_view_with_progress<O, P>(
236 bv: &BinaryView,
237 update_analysis_and_wait: bool,
238 options: Option<O>,
239 mut progress: P,
240) -> Option<Ref<BinaryView>>
241where
242 O: IntoJson,
243 P: ProgressCallback,
244{
245 let options_or_default = if let Some(opt) = options {
246 opt.get_json_string()
247 .ok()?
248 .to_cstr()
249 .to_bytes_with_nul()
250 .to_vec()
251 } else {
252 "{}".to_cstr().to_bytes_with_nul().to_vec()
253 };
254 let handle = unsafe {
255 BNLoadBinaryView(
256 bv.handle as *mut _,
257 update_analysis_and_wait,
258 options_or_default.as_ptr() as *mut c_char,
259 Some(P::cb_progress_callback),
260 &mut progress as *mut P as *mut c_void,
261 )
262 };
263
264 if handle.is_null() {
265 None
266 } else {
267 Some(unsafe { BinaryView::ref_from_raw(handle) })
268 }
269}
270
271pub fn load_project_file<O>(
272 file: &ProjectFile,
273 update_analysis_and_wait: bool,
274 options: Option<O>,
275) -> Option<Ref<BinaryView>>
276where
277 O: IntoJson,
278{
279 load_project_file_with_progress(file, update_analysis_and_wait, options, NoProgressCallback)
280}
281
282pub fn load_project_file_with_progress<O, P>(
284 file: &ProjectFile,
285 update_analysis_and_wait: bool,
286 options: Option<O>,
287 mut progress: P,
288) -> Option<Ref<BinaryView>>
289where
290 O: IntoJson,
291 P: ProgressCallback,
292{
293 let options_or_default = if let Some(opt) = options {
294 opt.get_json_string()
295 .ok()?
296 .to_cstr()
297 .to_bytes_with_nul()
298 .to_vec()
299 } else {
300 "{}".to_cstr().to_bytes_with_nul().to_vec()
301 };
302 let handle = unsafe {
303 BNLoadProjectFile(
304 file.handle.as_ptr(),
305 update_analysis_and_wait,
306 options_or_default.as_ptr() as *mut c_char,
307 Some(P::cb_progress_callback),
308 &mut progress as *mut P as *mut c_void,
309 )
310 };
311
312 if handle.is_null() {
313 None
314 } else {
315 Some(unsafe { BinaryView::ref_from_raw(handle) })
316 }
317}
318
319pub fn install_directory() -> PathBuf {
320 let install_dir_ptr: *mut c_char = unsafe { BNGetInstallDirectory() };
321 assert!(!install_dir_ptr.is_null());
322 let install_dir_str = unsafe { BnString::into_string(install_dir_ptr) };
323 PathBuf::from(install_dir_str)
324}
325
326pub fn bundled_plugin_directory() -> Result<PathBuf, ()> {
327 let s: *mut c_char = unsafe { BNGetBundledPluginDirectory() };
328 if s.is_null() {
329 return Err(());
330 }
331 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
332}
333
334pub fn set_bundled_plugin_directory(new_dir: impl AsRef<Path>) {
335 let new_dir = new_dir.as_ref().to_cstr();
336 unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr()) };
337}
338
339pub fn user_directory() -> PathBuf {
340 let user_dir_ptr: *mut c_char = unsafe { BNGetUserDirectory() };
341 assert!(!user_dir_ptr.is_null());
342 let user_dir_str = unsafe { BnString::into_string(user_dir_ptr) };
343 PathBuf::from(user_dir_str)
344}
345
346pub fn user_plugin_directory() -> Result<PathBuf, ()> {
347 let s: *mut c_char = unsafe { BNGetUserPluginDirectory() };
348 if s.is_null() {
349 return Err(());
350 }
351 let user_plugin_dir_str = unsafe { BnString::into_string(s) };
352 Ok(PathBuf::from(user_plugin_dir_str))
353}
354
355pub fn repositories_directory() -> Result<PathBuf, ()> {
356 let s: *mut c_char = unsafe { BNGetRepositoriesDirectory() };
357 if s.is_null() {
358 return Err(());
359 }
360 let repo_dir_str = unsafe { BnString::into_string(s) };
361 Ok(PathBuf::from(repo_dir_str))
362}
363
364pub fn settings_file_path() -> PathBuf {
365 let settings_file_name_ptr: *mut c_char = unsafe { BNGetSettingsFileName() };
366 assert!(!settings_file_name_ptr.is_null());
367 let settings_file_path_str = unsafe { BnString::into_string(settings_file_name_ptr) };
368 PathBuf::from(settings_file_path_str)
369}
370
371pub fn save_last_run() {
375 unsafe { BNSaveLastRun() };
376}
377
378pub fn path_relative_to_bundled_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
379 let path_raw = path.as_ref().to_cstr();
380 let s: *mut c_char = unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr()) };
381 if s.is_null() {
382 return Err(());
383 }
384 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
385}
386
387pub fn path_relative_to_user_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
388 let path_raw = path.as_ref().to_cstr();
389 let s: *mut c_char = unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr()) };
390 if s.is_null() {
391 return Err(());
392 }
393 Ok(PathBuf::from(unsafe { BnString::into_string(s) }))
394}
395
396pub fn path_relative_to_user_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
397 let path_raw = path.as_ref().to_cstr();
398 let s: *mut c_char = unsafe { BNGetPathRelativeToUserDirectory(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 is_main_thread() -> bool {
409 unsafe { BNIsMainThread() }
410}
411
412pub fn memory_info() -> HashMap<String, u64> {
413 let mut count = 0;
414 let mut usage = HashMap::new();
415 unsafe {
416 let info_ptr = BNGetMemoryUsageInfo(&mut count);
417 let info_list = std::slice::from_raw_parts(info_ptr, count);
418 for info in info_list {
419 let info_name = CStr::from_ptr(info.name).to_str().unwrap().to_string();
420 usage.insert(info_name, info.value);
421 }
422 BNFreeMemoryUsageInfo(info_ptr, count);
423 }
424 usage
425}
426
427pub fn version() -> String {
428 unsafe { BnString::into_string(BNGetVersionString()) }
429}
430
431pub fn build_id() -> u32 {
432 unsafe { BNGetBuildId() }
433}
434
435#[derive(Clone, PartialEq, Eq, Hash, Debug)]
436pub struct VersionInfo {
437 pub major: u32,
438 pub minor: u32,
439 pub build: u32,
440 pub channel: String,
441}
442
443impl VersionInfo {
444 pub(crate) fn from_raw(value: &BNVersionInfo) -> Self {
445 Self {
446 major: value.major,
447 minor: value.minor,
448 build: value.build,
449 channel: raw_to_string(value.channel).unwrap_or_default(),
451 }
452 }
453
454 pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self {
455 let owned = Self::from_raw(&value);
456 Self::free_raw(value);
457 owned
458 }
459
460 pub(crate) fn into_owned_raw(value: &Self) -> BNVersionInfo {
461 BNVersionInfo {
462 major: value.major,
463 minor: value.minor,
464 build: value.build,
465 channel: value.channel.as_ptr() as *mut c_char,
466 }
467 }
468
469 pub(crate) fn free_raw(value: BNVersionInfo) {
470 unsafe { BnString::free_raw(value.channel) };
471 }
472}
473
474impl TryFrom<&str> for VersionInfo {
475 type Error = ();
476
477 fn try_from(value: &str) -> Result<Self, Self::Error> {
478 let string = value.to_cstr();
479 let result = unsafe { BNParseVersionString(string.as_ptr()) };
480 if result.build == 0 && result.channel.is_null() && result.major == 0 && result.minor == 0 {
481 return Err(());
482 }
483 Ok(Self::from_owned_raw(result))
484 }
485}
486
487impl PartialOrd for VersionInfo {
488 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
489 Some(self.cmp(other))
490 }
491}
492
493impl Ord for VersionInfo {
494 fn cmp(&self, other: &Self) -> cmp::Ordering {
495 if self == other {
496 return cmp::Ordering::Equal;
497 }
498 let bn_version_0 = VersionInfo::into_owned_raw(self);
499 let bn_version_1 = VersionInfo::into_owned_raw(other);
500 if unsafe { BNVersionLessThan(bn_version_0, bn_version_1) } {
501 cmp::Ordering::Less
502 } else {
503 cmp::Ordering::Greater
504 }
505 }
506}
507
508impl Display for VersionInfo {
509 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
510 if self.channel.is_empty() {
511 write!(f, "{}.{}.{}", self.major, self.minor, self.build)
512 } else {
513 write!(
514 f,
515 "{}.{}.{}-{}",
516 self.major, self.minor, self.build, self.channel
517 )
518 }
519 }
520}
521
522pub fn version_info() -> VersionInfo {
523 let info_raw = unsafe { BNGetVersionInfo() };
524 VersionInfo::from_owned_raw(info_raw)
525}
526
527pub fn serial_number() -> String {
528 unsafe { BnString::into_string(BNGetSerialNumber()) }
529}
530
531pub fn is_license_validated() -> bool {
532 unsafe { BNIsLicenseValidated() }
533}
534
535pub fn licensed_user_email() -> String {
536 unsafe { BnString::into_string(BNGetLicensedUserEmail()) }
537}
538
539pub fn license_path() -> PathBuf {
540 user_directory().join("license.dat")
541}
542
543pub fn license_count() -> i32 {
544 unsafe { BNGetLicenseCount() }
545}
546
547#[derive(Clone, Debug, Eq, PartialEq)]
548pub struct LicenseAddon {
549 pub id: String,
550 pub license_serial: String,
551 pub product: String,
552 pub created_timestamp: u64,
553 pub expiration_timestamp: u64,
554 pub signature: String,
555}
556
557pub fn license_addons() -> Vec<LicenseAddon> {
558 let mut count = 0;
559 let addons = unsafe { BNGetLicenseAddons(&mut count) };
560 if addons.is_null() {
561 return Vec::new();
562 }
563
564 let result = unsafe { std::slice::from_raw_parts(addons, count) }
565 .iter()
566 .map(|addon| LicenseAddon {
567 id: unsafe { CStr::from_ptr(addon.id).to_string_lossy().into_owned() },
568 license_serial: unsafe {
569 CStr::from_ptr(addon.licenseSerial)
570 .to_string_lossy()
571 .into_owned()
572 },
573 product: unsafe { CStr::from_ptr(addon.product).to_string_lossy().into_owned() },
574 created_timestamp: addon.createdTimestamp,
575 expiration_timestamp: addon.expirationTimestamp,
576 signature: unsafe {
577 CStr::from_ptr(addon.signature)
578 .to_string_lossy()
579 .into_owned()
580 },
581 })
582 .collect();
583 unsafe { BNFreeLicenseAddons(addons, count) };
584 result
585}
586
587#[cfg(not(feature = "demo"))]
593pub fn set_license(license: Option<&str>) {
594 let license = license.unwrap_or_default().to_cstr();
595 unsafe { BNSetLicense(license.as_ptr()) }
596}
597
598#[cfg(feature = "demo")]
599pub fn set_license(_license: Option<&str>) {}
600
601pub fn product() -> String {
602 unsafe { BnString::into_string(BNGetProduct()) }
603}
604
605pub fn product_type() -> String {
606 unsafe { BnString::into_string(BNGetProductType()) }
607}
608
609pub fn license_expiration_time() -> std::time::SystemTime {
610 let m = std::time::Duration::from_secs(unsafe { BNGetLicenseExpirationTime() });
611 std::time::UNIX_EPOCH + m
612}
613
614pub fn is_ui_enabled() -> bool {
615 unsafe { BNIsUIEnabled() }
616}
617
618pub fn is_database(file: &Path) -> bool {
619 let filename = file.to_cstr();
620 unsafe { BNIsDatabase(filename.as_ptr()) }
621}
622
623pub fn plugin_abi_version() -> u32 {
624 BN_CURRENT_CORE_ABI_VERSION
625}
626
627pub fn plugin_abi_minimum_version() -> u32 {
628 BN_MINIMUM_CORE_ABI_VERSION
629}
630
631pub fn core_abi_version() -> u32 {
632 unsafe { BNGetCurrentCoreABIVersion() }
633}
634
635pub fn core_abi_minimum_version() -> u32 {
636 unsafe { BNGetMinimumCoreABIVersion() }
637}
638
639pub fn plugin_ui_abi_version() -> u32 {
640 BN_CURRENT_UI_ABI_VERSION
641}
642
643pub fn plugin_ui_abi_minimum_version() -> u32 {
644 BN_MINIMUM_UI_ABI_VERSION
645}
646
647pub fn add_required_plugin_dependency(name: &str) {
648 let raw_name = name.to_cstr();
649 unsafe { BNAddRequiredPluginDependency(raw_name.as_ptr()) };
650}
651
652pub fn add_optional_plugin_dependency(name: &str) {
653 let raw_name = name.to_cstr();
654 unsafe { BNAddOptionalPluginDependency(raw_name.as_ptr()) };
655}
656
657#[cfg(not(feature = "no_exports"))]
659#[no_mangle]
660#[allow(non_snake_case)]
661pub extern "C" fn CorePluginABIVersion() -> u32 {
662 plugin_abi_version()
663}
664
665#[cfg(not(feature = "no_exports"))]
667#[no_mangle]
668pub extern "C" fn UIPluginABIVersion() -> u32 {
669 plugin_ui_abi_version()
670}