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