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