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