diff --git a/Cargo.lock b/Cargo.lock index ed74c1416c..efe2bfe067 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,6 +377,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + [[package]] name = "cast" version = "0.3.0" @@ -1527,9 +1533,9 @@ dependencies = [ [[package]] name = "minidump" -version = "0.23.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24e8d3964f4a60ee45309dc76cfc3023588b0b62fcb79baa3e9134d4298c7ac" +checksum = "a902ca21d9772a66d3d1b050b3436dcadb192694be01409e0219902dcf4bc1e8" dependencies = [ "debugid", "encoding_rs", @@ -1537,9 +1543,10 @@ dependencies = [ "minidump-common", "num-traits", "procfs-core", + "prost", "range-map", "scroll 0.12.0", - "thiserror 1.0.69", + "thiserror 2.0.18", "time", "tracing", "uuid", @@ -1547,9 +1554,9 @@ dependencies = [ [[package]] name = "minidump-common" -version = "0.23.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c464c28d59ae40fd48a1da695e338910f19f71e1e585dd37261d81eaa3adbee" +checksum = "2e16d10087ae9e375bad7a40e8ef5504bc08e808ccc6019067ff9de42a84570f" dependencies = [ "bitflags 2.9.1", "debugid", @@ -1567,6 +1574,7 @@ dependencies = [ "binaryninja", "binaryninjacore-sys", "minidump", + "object 0.39.1", "tracing", ] @@ -1819,6 +1827,17 @@ dependencies = [ "ruzstd 0.7.3", ] +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "flate2", + "memchr", + "ruzstd 0.8.2", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -2045,6 +2064,29 @@ dependencies = [ "hex", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.40" @@ -2337,7 +2379,7 @@ checksum = "58c4eb8a81997cf040a091d1f7e1938aeab6749d3a0dfa73af43cdc32393483d" dependencies = [ "byteorder", "derive_more", - "twox-hash", + "twox-hash 1.6.3", ] [[package]] @@ -2346,7 +2388,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" dependencies = [ - "twox-hash", + "twox-hash 1.6.3", +] + +[[package]] +name = "ruzstd" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01" +dependencies = [ + "twox-hash 2.1.2", ] [[package]] @@ -2915,6 +2966,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "twox-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" + [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/arch/msp430/src/lib.rs b/arch/msp430/src/lib.rs index 892e617dc5..0655cc853f 100644 --- a/arch/msp430/src/lib.rs +++ b/arch/msp430/src/lib.rs @@ -1,9 +1,6 @@ use binaryninja::{ - add_optional_plugin_dependency, - architecture::ArchitectureExt, - calling_convention, - custom_binary_view::{BinaryViewType, BinaryViewTypeExt}, - Endianness, + add_optional_plugin_dependency, architecture::ArchitectureExt, binary_view::BinaryViewType, + calling_convention, Endianness, }; mod architecture; @@ -45,7 +42,7 @@ pub extern "C" fn CorePluginInit() -> bool { arch.set_default_calling_convention(&default); - if let Ok(bvt) = BinaryViewType::by_name("ELF") { + if let Some(bvt) = BinaryViewType::by_name("ELF") { bvt.register_arch(105, Endianness::LittleEndian, arch); } diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index 2bc0427c30..3537a082f0 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -13,9 +13,8 @@ use binaryninja::{ ImplicitRegisterExtend, InstructionInfo, Register as Reg, RegisterInfo, UnusedFlag, UnusedRegisterStack, }, - binary_view::{BinaryView, BinaryViewExt}, + binary_view::{BinaryView, BinaryViewType}, calling_convention::{register_calling_convention, CallingConvention, ConventionBuilder}, - custom_binary_view::{BinaryViewType, BinaryViewTypeExt}, disassembly::{InstructionTextToken, InstructionTextTokenKind}, function::Function, function_recognizer::FunctionRecognizer, @@ -3101,7 +3100,7 @@ pub extern "C" fn CorePluginInit() -> bool { ); arch64.set_default_calling_convention(&cc64); - if let Ok(bvt) = BinaryViewType::by_name("ELF") { + if let Some(bvt) = BinaryViewType::by_name("ELF") { bvt.register_arch( (1 << 16) | 243, binaryninja::Endianness::LittleEndian, diff --git a/plugins/bntl_utils/src/command/create.rs b/plugins/bntl_utils/src/command/create.rs index 96d0eb6068..8fc37159e6 100644 --- a/plugins/bntl_utils/src/command/create.rs +++ b/plugins/bntl_utils/src/command/create.rs @@ -2,7 +2,7 @@ use crate::command::{InputDirectoryField, OutputDirectoryField}; use crate::process::{new_processing_state_background_thread, TypeLibProcessor}; use crate::validate::TypeLibValidater; use binaryninja::background_task::BackgroundTask; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::command::{Command, GlobalCommand, ProjectCommand}; use binaryninja::interaction::{Form, FormInputField, MessageBoxButtonSet, MessageBoxIcon}; use binaryninja::platform::Platform; diff --git a/plugins/bntl_utils/src/dump.rs b/plugins/bntl_utils/src/dump.rs index c2e9249844..d86500c017 100644 --- a/plugins/bntl_utils/src/dump.rs +++ b/plugins/bntl_utils/src/dump.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::file_metadata::FileMetadata; use binaryninja::metadata::{Metadata, MetadataType}; use binaryninja::platform::Platform; diff --git a/plugins/bntl_utils/src/process.rs b/plugins/bntl_utils/src/process.rs index 3f4aec696d..5c9651a473 100644 --- a/plugins/bntl_utils/src/process.rs +++ b/plugins/bntl_utils/src/process.rs @@ -20,8 +20,7 @@ use crate::schema::BntlSchema; use crate::tbd::{parse_tbd_info, TbdArchitecture}; use crate::winmd::WindowsMetadataImporter; use binaryninja::background_task::BackgroundTask; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; -use binaryninja::custom_binary_view::BinaryViewType; +use binaryninja::binary_view::{BinaryView, BinaryViewType}; use binaryninja::file_metadata::FileMetadata; use binaryninja::metadata::Metadata; use binaryninja::platform::Platform; diff --git a/plugins/dwarf/dwarf_export/src/lib.rs b/plugins/dwarf/dwarf_export/src/lib.rs index fac7b2bb99..90a92faacc 100644 --- a/plugins/dwarf/dwarf_export/src/lib.rs +++ b/plugins/dwarf/dwarf_export/src/lib.rs @@ -2,7 +2,7 @@ mod edit_distance; use binaryninja::interaction::form::{Form, FormInputField}; use binaryninja::{ - binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, + binary_view::{BinaryView, BinaryViewBase}, command::{register_command, Command}, confidence::Conf, rc::Ref, diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index fbf7e7dfad..6c1d78a8ac 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -19,7 +19,7 @@ use crate::{ }; use binaryninja::{ - binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, + binary_view::{BinaryView, BinaryViewBase}, debuginfo::{DebugFunctionInfo, DebugInfo}, platform::Platform, rc::*, diff --git a/plugins/dwarf/dwarf_import/src/helpers.rs b/plugins/dwarf/dwarf_import/src/helpers.rs index 541fdc4249..0a8a2cbdad 100644 --- a/plugins/dwarf/dwarf_import/src/helpers.rs +++ b/plugins/dwarf/dwarf_import/src/helpers.rs @@ -19,7 +19,7 @@ use crate::{DebugInfoBuilderContext, ReaderType}; use binaryninja::binary_view::BinaryViewBase; use binaryninja::Endianness; use binaryninja::{ - binary_view::{BinaryView, BinaryViewExt}, + binary_view::BinaryView, download::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, settings::Settings, }; diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs index 1ed3ce198e..a9e4bbd80b 100644 --- a/plugins/dwarf/dwarf_import/src/lib.rs +++ b/plugins/dwarf/dwarf_import/src/lib.rs @@ -30,7 +30,7 @@ use crate::types::parse_variable; use binaryninja::binary_view::BinaryViewBase; use binaryninja::{ - binary_view::{BinaryView, BinaryViewExt}, + binary_view::BinaryView, debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}, settings::Settings, template_simplifier::simplify_str_to_str, diff --git a/plugins/dwarf/dwarfdump/src/lib.rs b/plugins/dwarf/dwarfdump/src/lib.rs index a235de4f83..aafe1f1834 100644 --- a/plugins/dwarf/dwarfdump/src/lib.rs +++ b/plugins/dwarf/dwarfdump/src/lib.rs @@ -14,7 +14,7 @@ use binaryninja::{ architecture::BranchType, - binary_view::{BinaryView, BinaryViewExt}, + binary_view::BinaryView, command::{register_command, Command}, disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, flowgraph::{EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption}, diff --git a/plugins/dwarf/shared/src/lib.rs b/plugins/dwarf/shared/src/lib.rs index a9dc8b7ced..d453193845 100644 --- a/plugins/dwarf/shared/src/lib.rs +++ b/plugins/dwarf/shared/src/lib.rs @@ -16,7 +16,7 @@ use gimli::{EndianRcSlice, Endianity, RunTimeEndian, SectionId}; use object::{Object, ObjectSection}; use binaryninja::{ - binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, + binary_view::{BinaryView, BinaryViewBase}, settings::Settings, Endianness, }; diff --git a/plugins/idb_import/src/commands/load_file.rs b/plugins/idb_import/src/commands/load_file.rs index 67416e613e..ef773c6a70 100644 --- a/plugins/idb_import/src/commands/load_file.rs +++ b/plugins/idb_import/src/commands/load_file.rs @@ -1,7 +1,7 @@ use crate::commands::LoadFileField; use crate::mapper::IDBMapper; use crate::parse::IDBFileParser; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::command::Command; use binaryninja::interaction::Form; use std::fs::File; diff --git a/plugins/idb_import/src/mapper.rs b/plugins/idb_import/src/mapper.rs index 36c980dade..530fef1f06 100644 --- a/plugins/idb_import/src/mapper.rs +++ b/plugins/idb_import/src/mapper.rs @@ -6,7 +6,7 @@ use crate::parse::{ }; use crate::translate::TILTranslator; use binaryninja::architecture::Architecture; -use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::{BinaryView, BinaryViewBase}; use binaryninja::qualified_name::QualifiedName; use binaryninja::rc::Ref; use binaryninja::section::{SectionBuilder, Semantics}; diff --git a/plugins/idb_import/src/types.rs b/plugins/idb_import/src/types.rs index 933fec126f..cbd37f66c8 100644 --- a/plugins/idb_import/src/types.rs +++ b/plugins/idb_import/src/types.rs @@ -3,7 +3,7 @@ use std::num::{NonZeroU16, NonZeroU8}; use anyhow::{anyhow, Result}; use binaryninja::architecture::{Architecture, ArchitectureExt, CoreArchitecture}; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::{BinaryView}; use binaryninja::calling_convention::CoreCallingConvention; use binaryninja::confidence::Conf; use binaryninja::rc::Ref; diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs index d6678a17cf..c3bbdf5ed0 100644 --- a/plugins/pdb-ng/src/lib.rs +++ b/plugins/pdb-ng/src/lib.rs @@ -23,7 +23,7 @@ use std::{env, fs}; use anyhow::{anyhow, Result}; use pdb::PDB; -use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::{BinaryView, BinaryViewBase}; use binaryninja::debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}; use binaryninja::download::{DownloadInstanceInputOutputCallbacks, DownloadProvider}; use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet}; @@ -275,23 +275,14 @@ fn search_sym_store( fn parse_pdb_info(view: &BinaryView) -> Option { match view.get_metadata::("DEBUG_INFO_TYPE") { - Some(Ok(0x53445352 /* 'SDSR' */)) => {} + Some(0x53445352 /* 'SDSR' */) => {} _ => return None, } // This is stored in the BV by the PE loader - let file_path = match view.get_metadata::("PDB_FILENAME") { - Some(Ok(md)) => md, - _ => return None, - }; - let mut guid = match view.get_metadata::>("PDB_GUID") { - Some(Ok(md)) => md, - _ => return None, - }; - let age = match view.get_metadata::("PDB_AGE") { - Some(Ok(md)) => md as u32, - _ => return None, - }; + let file_path = view.get_metadata::("PDB_FILENAME")?; + let mut guid = view.get_metadata::>("PDB_GUID")?; + let age = view.get_metadata::("PDB_AGE")? as u32; if guid.len() != 16 { return None; diff --git a/plugins/pdb-ng/src/parser.rs b/plugins/pdb-ng/src/parser.rs index 9db635e8d7..8c33fe6469 100644 --- a/plugins/pdb-ng/src/parser.rs +++ b/plugins/pdb-ng/src/parser.rs @@ -23,7 +23,7 @@ use pdb::*; use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol}; use crate::type_parser::ParsedType; use binaryninja::architecture::{Architecture, CoreArchitecture}; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::calling_convention::CoreCallingConvention; use binaryninja::confidence::{Conf, MIN_CONFIDENCE}; use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo}; diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs index 026a421aff..bccb93f120 100644 --- a/plugins/pdb-ng/src/type_parser.rs +++ b/plugins/pdb-ng/src/type_parser.rs @@ -19,7 +19,6 @@ use crate::struct_grouper::group_structure; use crate::PDBParserInstance; use anyhow::{anyhow, Result}; use binaryninja::architecture::Architecture; -use binaryninja::binary_view::BinaryViewExt; use binaryninja::calling_convention::CoreCallingConvention; use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use binaryninja::platform::Platform; diff --git a/plugins/svd/src/lib.rs b/plugins/svd/src/lib.rs index 1eb84a5f1c..ea37b783d1 100644 --- a/plugins/svd/src/lib.rs +++ b/plugins/svd/src/lib.rs @@ -3,7 +3,7 @@ pub mod settings; use crate::mapper::DeviceMapper; use crate::settings::LoadSettings; -use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::{BinaryView, BinaryViewBase}; use binaryninja::command::Command; use binaryninja::interaction::{Form, FormInputField}; use binaryninja::workflow::{activity, Activity, AnalysisContext, Workflow}; diff --git a/plugins/svd/src/mapper.rs b/plugins/svd/src/mapper.rs index 1ba7f61c34..4ec02b9e4c 100644 --- a/plugins/svd/src/mapper.rs +++ b/plugins/svd/src/mapper.rs @@ -1,5 +1,5 @@ use crate::settings::LoadSettings; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use binaryninja::data_buffer::DataBuffer; use binaryninja::rc::Ref; diff --git a/plugins/svd/tests/mapper.rs b/plugins/svd/tests/mapper.rs index 8af8c2618e..845bfd613b 100644 --- a/plugins/svd/tests/mapper.rs +++ b/plugins/svd/tests/mapper.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::binary_view::{BinaryView, BinaryViewBase}; use binaryninja::file_metadata::FileMetadata; use binaryninja::headless::Session; diff --git a/plugins/warp/benches/convert.rs b/plugins/warp/benches/convert.rs index e0acdc15e5..d1bf9dd694 100644 --- a/plugins/warp/benches/convert.rs +++ b/plugins/warp/benches/convert.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use criterion::{criterion_group, criterion_main, Criterion}; use std::path::PathBuf; diff --git a/plugins/warp/benches/function.rs b/plugins/warp/benches/function.rs index 299ed1eb2c..a9cfd26a54 100644 --- a/plugins/warp/benches/function.rs +++ b/plugins/warp/benches/function.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use criterion::{criterion_group, criterion_main, Criterion}; use rayon::prelude::*; diff --git a/plugins/warp/benches/guid.rs b/plugins/warp/benches/guid.rs index eddf2f0616..f8c7f4b5cc 100644 --- a/plugins/warp/benches/guid.rs +++ b/plugins/warp/benches/guid.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use criterion::{criterion_group, criterion_main, Criterion}; use std::path::PathBuf; diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index d9a5a8d1a0..5b3469c1c1 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -7,7 +7,7 @@ pub use function::*; pub use guid::*; pub use type_reference::*; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::function::Function as BNFunction; use binaryninja::object_destructor::{register_object_destructor, ObjectDestructor}; use binaryninja::rc::Guard; diff --git a/plugins/warp/src/cache/function.rs b/plugins/warp/src/cache/function.rs index 79b9e86ae6..d7eae3affb 100644 --- a/plugins/warp/src/cache/function.rs +++ b/plugins/warp/src/cache/function.rs @@ -1,5 +1,4 @@ use crate::convert::{comment_to_bn_comment, to_bn_symbol_at_address}; -use binaryninja::binary_view::BinaryViewExt; use binaryninja::function::{Function as BNFunction, FunctionUpdateType}; use binaryninja::symbol::SymbolType; use warp::signature::function::Function; diff --git a/plugins/warp/src/cache/guid.rs b/plugins/warp/src/cache/guid.rs index ede54013a7..3b3e4a319c 100644 --- a/plugins/warp/src/cache/guid.rs +++ b/plugins/warp/src/cache/guid.rs @@ -1,7 +1,6 @@ use crate::cache::FunctionID; use crate::convert::from_bn_symbol; use crate::function_guid; -use binaryninja::binary_view::BinaryViewExt; use binaryninja::function::Function as BNFunction; use binaryninja::low_level_il::function::{FunctionMutability, LowLevelILFunction, NonSSA}; use binaryninja::rc::Ref as BNRef; diff --git a/plugins/warp/src/convert/symbol.rs b/plugins/warp/src/convert/symbol.rs index 0107cff58d..5d794d30f8 100644 --- a/plugins/warp/src/convert/symbol.rs +++ b/plugins/warp/src/convert/symbol.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::Symbol as BNSymbol; use binaryninja::symbol::SymbolType as BNSymbolType; diff --git a/plugins/warp/src/convert/types.rs b/plugins/warp/src/convert/types.rs index abfa3bc97c..6969ac677a 100644 --- a/plugins/warp/src/convert/types.rs +++ b/plugins/warp/src/convert/types.rs @@ -490,7 +490,6 @@ pub fn to_bn_type(arch: Option, ty: &Type) -> BNRef #[cfg(test)] mod tests { use super::*; - use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use std::path::PathBuf; use warp::r#type::guid::TypeGUID; diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index e0aaeed346..11f85566ed 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -4,7 +4,7 @@ use binaryninja::architecture::{ Architecture, ImplicitRegisterExtend, Register as BNRegister, RegisterInfo, }; use binaryninja::basic_block::BasicBlock as BNBasicBlock; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::{Function as BNFunction, NativeBlock}; use binaryninja::low_level_il::expression::{ diff --git a/plugins/warp/src/matcher.rs b/plugins/warp/src/matcher.rs index f96128e5a5..69bfd4b6aa 100644 --- a/plugins/warp/src/matcher.rs +++ b/plugins/warp/src/matcher.rs @@ -2,7 +2,7 @@ use crate::cache::cached_constraints; use crate::container::{Container, SourceId}; use crate::convert::to_bn_type; use binaryninja::architecture::Architecture as BNArchitecture; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::function::Function as BNFunction; use binaryninja::settings::{QueryOptions, Settings as BNSettings}; use serde_json::json; diff --git a/plugins/warp/src/plugin/load.rs b/plugins/warp/src/plugin/load.rs index 2be473d8d9..9ec6eaed16 100644 --- a/plugins/warp/src/plugin/load.rs +++ b/plugins/warp/src/plugin/load.rs @@ -3,7 +3,7 @@ use crate::container::disk::{DiskContainer, DiskContainerSource}; use crate::container::{ContainerError, SourcePath}; use crate::convert::platform_to_target; use crate::plugin::workflow::run_matcher; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::command::Command; use binaryninja::interaction::{ show_message_box, Form, FormInputField, MessageBoxButtonResult, MessageBoxButtonSet, diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs index c1728b5e51..c2915717cd 100644 --- a/plugins/warp/src/plugin/workflow.rs +++ b/plugins/warp/src/plugin/workflow.rs @@ -10,7 +10,7 @@ use crate::plugin::settings::PluginSettings; use crate::{get_warp_ignore_tag_type, get_warp_tag_type, relocatable_regions, IGNORE_TAG_NAME}; use binaryninja::architecture::RegisterId; use binaryninja::background_task::BackgroundTask; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::command::Command; use binaryninja::function::Function as BNFunction; use binaryninja::rc::Ref as BNRef; diff --git a/plugins/warp/src/processor.rs b/plugins/warp/src/processor.rs index 32310d3c25..666f8df32b 100644 --- a/plugins/warp/src/processor.rs +++ b/plugins/warp/src/processor.rs @@ -19,7 +19,7 @@ use thiserror::Error; use walkdir::WalkDir; use binaryninja::background_task::BackgroundTask; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::function::Function as BNFunction; use binaryninja::project::file::ProjectFile; use binaryninja::project::Project; diff --git a/plugins/warp/tests/determinism.rs b/plugins/warp/tests/determinism.rs index 5100b43608..50361562a9 100644 --- a/plugins/warp/tests/determinism.rs +++ b/plugins/warp/tests/determinism.rs @@ -1,5 +1,4 @@ //! This tests to make sure the function GUIDs are stable. -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use std::collections::BTreeMap; use std::path::PathBuf; diff --git a/plugins/warp/tests/matcher.rs b/plugins/warp/tests/matcher.rs index d2d7a96013..5703419bd4 100644 --- a/plugins/warp/tests/matcher.rs +++ b/plugins/warp/tests/matcher.rs @@ -1,5 +1,5 @@ use binaryninja::architecture::CoreArchitecture; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::file_metadata::FileMetadata; use binaryninja::function::Function as BNFunction; use binaryninja::headless::Session; diff --git a/plugins/workflow_objc/src/activities/alloc_init.rs b/plugins/workflow_objc/src/activities/alloc_init.rs index 5c8bf0645c..953e07386c 100644 --- a/plugins/workflow_objc/src/activities/alloc_init.rs +++ b/plugins/workflow_objc/src/activities/alloc_init.rs @@ -1,8 +1,5 @@ use binaryninja::{ - binary_view::{BinaryView, BinaryViewExt as _}, - medium_level_il::MediumLevelILLiftedInstruction, - rc::Ref, - types::Type, + binary_view::BinaryView, medium_level_il::MediumLevelILLiftedInstruction, rc::Ref, types::Type, workflow::AnalysisContext, }; use bstr::ByteSlice; diff --git a/plugins/workflow_objc/src/activities/objc_msg_send_calls.rs b/plugins/workflow_objc/src/activities/objc_msg_send_calls.rs index 157bb19c12..41fc38c065 100644 --- a/plugins/workflow_objc/src/activities/objc_msg_send_calls.rs +++ b/plugins/workflow_objc/src/activities/objc_msg_send_calls.rs @@ -1,5 +1,5 @@ use binaryninja::{ - binary_view::{BinaryView, BinaryViewExt as _}, + binary_view::BinaryView, function::Function, low_level_il::{ expression::{ExpressionHandler as _, LowLevelILExpressionKind}, diff --git a/plugins/workflow_objc/src/activities/objc_msg_send_calls/adjust_call_type.rs b/plugins/workflow_objc/src/activities/objc_msg_send_calls/adjust_call_type.rs index 07e83ba7d6..4d22476a48 100644 --- a/plugins/workflow_objc/src/activities/objc_msg_send_calls/adjust_call_type.rs +++ b/plugins/workflow_objc/src/activities/objc_msg_send_calls/adjust_call_type.rs @@ -1,6 +1,6 @@ use binaryninja::{ architecture::CoreRegister, - binary_view::{BinaryView, BinaryViewBase as _, BinaryViewExt}, + binary_view::{BinaryView, BinaryViewBase as _}, confidence::Conf, function::Function, low_level_il::{ diff --git a/plugins/workflow_objc/src/activities/remove_memory_management.rs b/plugins/workflow_objc/src/activities/remove_memory_management.rs index cd613ee2bf..a6389d227a 100644 --- a/plugins/workflow_objc/src/activities/remove_memory_management.rs +++ b/plugins/workflow_objc/src/activities/remove_memory_management.rs @@ -1,6 +1,6 @@ use binaryninja::{ architecture::{Architecture as _, CoreRegister, Register as _, RegisterInfo as _}, - binary_view::{BinaryView, BinaryViewExt as _}, + binary_view::BinaryView, low_level_il::{ expression::{ExpressionHandler, LowLevelILExpressionKind}, function::{LowLevelILFunction, Mutable, NonSSA}, diff --git a/plugins/workflow_objc/src/activities/super_init.rs b/plugins/workflow_objc/src/activities/super_init.rs index 7b6bb3f71c..533fe9e6a7 100644 --- a/plugins/workflow_objc/src/activities/super_init.rs +++ b/plugins/workflow_objc/src/activities/super_init.rs @@ -1,5 +1,5 @@ use binaryninja::{ - binary_view::{BinaryView, BinaryViewBase as _, BinaryViewExt as _}, + binary_view::{BinaryView, BinaryViewBase as _}, medium_level_il::{ operation::{Constant, LiftedSetVarSsa, LiftedSetVarSsaField, Var, VarSsa}, MediumLevelILLiftedInstruction, MediumLevelILLiftedInstructionKind, diff --git a/plugins/workflow_objc/src/activities/util.rs b/plugins/workflow_objc/src/activities/util.rs index 3ac7df7c1d..d9acf29ff5 100644 --- a/plugins/workflow_objc/src/activities/util.rs +++ b/plugins/workflow_objc/src/activities/util.rs @@ -1,5 +1,5 @@ use binaryninja::{ - binary_view::{BinaryView, BinaryViewExt as _}, + binary_view::BinaryView, confidence::Conf, function::Function, medium_level_il::{ diff --git a/plugins/workflow_objc/src/metadata/global_state.rs b/plugins/workflow_objc/src/metadata/global_state.rs index 6c128e22b5..a36931403e 100644 --- a/plugins/workflow_objc/src/metadata/global_state.rs +++ b/plugins/workflow_objc/src/metadata/global_state.rs @@ -1,7 +1,7 @@ use binaryninja::file_metadata::SessionId; use binaryninja::object_destructor::register_object_destructor; use binaryninja::{ - binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, + binary_view::{BinaryView, BinaryViewBase}, file_metadata::FileMetadata, metadata::Metadata, object_destructor::ObjectDestructor, @@ -155,10 +155,7 @@ impl AnalysisInfo { } fn load_selector_impls(&self, bv: &BinaryView) -> Option { - let Some(Ok(meta)) = bv.get_metadata::>>("Objective-C") - else { - return None; - }; + let meta = bv.get_metadata::>>("Objective-C")?; let version_meta = meta.get("version")?; if version_meta.get_unsigned_integer()? != 1 { tracing::error!( diff --git a/plugins/workflow_objc/src/metadata/selector.rs b/plugins/workflow_objc/src/metadata/selector.rs index de2e883ee7..aa51d8e902 100644 --- a/plugins/workflow_objc/src/metadata/selector.rs +++ b/plugins/workflow_objc/src/metadata/selector.rs @@ -1,5 +1,5 @@ use crate::Error; -use binaryninja::binary_view::{BinaryView, BinaryViewBase as _, BinaryViewExt}; +use binaryninja::binary_view::{BinaryView, BinaryViewBase as _}; pub struct Selector { pub name: String, diff --git a/rust/README.md b/rust/README.md index 55dccdff65..bed6065e94 100644 --- a/rust/README.md +++ b/rust/README.md @@ -21,7 +21,6 @@ If you are worried about breaking changes, avoid modules with warnings about ins ```rust use binaryninja::headless::Session; -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; fn main() { let headless_session = Session::new().expect("Failed to initialize session"); @@ -30,7 +29,6 @@ fn main() { .expect("Couldn't open `/bin/cat`"); println!("File: `{}`", bv.file()); - println!("File size: `{:#x}`", bv.len()); println!("Function count: {}", bv.functions().len()); for func in &bv.functions() { diff --git a/rust/examples/bndb_to_type_library.rs b/rust/examples/bndb_to_type_library.rs index 2cfaf83ef3..b733dde025 100644 --- a/rust/examples/bndb_to_type_library.rs +++ b/rust/examples/bndb_to_type_library.rs @@ -1,6 +1,5 @@ // Usage: cargo run --example bndb_to_type_library -use binaryninja::binary_view::BinaryViewExt; use binaryninja::tracing::TracingLogListener; use binaryninja::types::{QualifiedName, TypeLibrary}; use tracing_indicatif::span_ext::IndicatifSpanExt; diff --git a/rust/examples/decompile.rs b/rust/examples/decompile.rs index 155de81cbf..30465a05fe 100644 --- a/rust/examples/decompile.rs +++ b/rust/examples/decompile.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::{BinaryView, BinaryViewBase}; use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings}; use binaryninja::function::Function; use binaryninja::linear_view::LinearViewObject; diff --git a/rust/examples/disassemble.rs b/rust/examples/disassemble.rs index df33aadec4..245f24d7d6 100644 --- a/rust/examples/disassemble.rs +++ b/rust/examples/disassemble.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::BinaryViewBase; use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings, DisassemblyTextRenderer}; use binaryninja::function::Function; use binaryninja::tracing::TracingLogListener; diff --git a/rust/examples/flowgraph.rs b/rust/examples/flowgraph.rs index cb03234e73..30307c3ffa 100644 --- a/rust/examples/flowgraph.rs +++ b/rust/examples/flowgraph.rs @@ -9,7 +9,7 @@ use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, Mess use binaryninja::tracing::TracingLogListener; use binaryninja::{ architecture::BranchType, - binary_view::{BinaryView, BinaryViewExt}, + binary_view::BinaryView, disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, flowgraph::{EdgePenStyle, FlowGraph, ThemeColor}, }; diff --git a/rust/examples/high_level_il.rs b/rust/examples/high_level_il.rs index 57b735e963..28081a762a 100644 --- a/rust/examples/high_level_il.rs +++ b/rust/examples/high_level_il.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::BinaryViewBase; use binaryninja::tracing::TracingLogListener; fn main() { diff --git a/rust/examples/medium_level_il.rs b/rust/examples/medium_level_il.rs index e2c0edee5f..4e9cac8375 100644 --- a/rust/examples/medium_level_il.rs +++ b/rust/examples/medium_level_il.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::BinaryViewBase; use binaryninja::tracing::TracingLogListener; fn main() { diff --git a/rust/examples/simple.rs b/rust/examples/simple.rs index c01ef74e58..5f063edec4 100644 --- a/rust/examples/simple.rs +++ b/rust/examples/simple.rs @@ -1,5 +1,5 @@ use binaryninja::architecture::Architecture; -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::BinaryViewBase; use binaryninja::tracing::TracingLogListener; fn main() { diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs index bfbbbdb250..a827b0a2c0 100644 --- a/rust/examples/workflow.rs +++ b/rust/examples/workflow.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::low_level_il::expression::{ExpressionHandler, LowLevelILExpressionKind}; use binaryninja::low_level_il::instruction::InstructionHandler; use binaryninja::low_level_il::VisitorAction; diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index b65367a2dd..6025f1e8d6 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -13,6 +13,8 @@ // limitations under the License. //! A view on binary data and queryable interface of a binary files analysis. +//! +//! The main analysis object is [`BinaryView`], and custom implementations can be implemented with [`CustomBinaryView`]. use binaryninjacore_sys::*; @@ -57,11 +59,11 @@ use crate::workflow::Workflow; use crate::{Endianness, BN_FULL_CONFIDENCE}; use std::collections::{BTreeMap, HashMap}; use std::ffi::{c_char, c_void, CString}; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Display, Formatter}; +use std::mem::MaybeUninit; use std::ops::Range; use std::path::{Path, PathBuf}; use std::ptr::NonNull; -use std::{result, slice}; pub mod memory_map; pub mod reader; @@ -72,15 +74,377 @@ pub use memory_map::{MemoryMap, MemoryRegionInfo, ResolvedRange}; pub use reader::BinaryReader; pub use writer::BinaryWriter; -pub type Result = result::Result; pub type BinaryViewEventType = BNBinaryViewEventType; pub type AnalysisState = BNAnalysisState; pub type ModificationStatus = BNModificationStatus; pub type StringType = BNStringType; pub type FindFlag = BNFindFlag; +/// Registers a new binary view type. +pub fn register_binary_view_type(view_type: T) -> (&'static T, BinaryViewType) +where + T: CustomBinaryViewType, +{ + let name = T::NAME.to_cstr(); + let long_name = T::LONG_NAME.to_cstr(); + let leaked_type = Box::leak(Box::new(view_type)); + + let result = unsafe { + BNRegisterBinaryViewType( + name.as_ref().as_ptr() as *const _, + long_name.as_ref().as_ptr() as *const _, + &mut BNCustomBinaryViewType { + context: leaked_type as *mut _ as *mut std::os::raw::c_void, + create: Some(cb_create::), + parse: Some(cb_parse::), + isValidForData: Some(cb_valid::), + isDeprecated: Some(cb_deprecated::), + isForceLoadable: Some(cb_force_loadable::), + getLoadSettingsForData: Some(cb_load_settings::), + hasNoInitialContent: Some(cb_has_no_initial_content::), + }, + ) + }; + + assert!( + !result.is_null(), + "BNRegisterBinaryViewType always returns a non-null handle" + ); + let core_view_type = unsafe { BinaryViewType::from_raw(result) }; + (leaked_type, core_view_type) +} + +/// Interface for creating custom binary views of a given type, analogous to [`BinaryViewType`]. +pub trait CustomBinaryViewType: 'static + Sync { + /// The associated [`BinaryViewBase`] for which this type creates with [`CustomBinaryViewType::create_binary_view`]. + type CustomBinaryView: CustomBinaryView; + + /// The name of the binary view type. + const NAME: &'static str; + + /// The longer name of the binary view type, defaults to [`CustomBinaryViewType::NAME`]. + const LONG_NAME: &'static str = Self::NAME; + + /// Is this [`CustomBinaryViewType`] deprecated and should not be used? + /// + /// We specify this such that the view type may still be used by existing databases, but not + /// newly created views. + const DEPRECATED: bool = false; + + /// Is this [`CustomBinaryViewType`] able to be loaded forcefully? + /// + /// If so, it will be shown in the drop-down when a user opens a file with options. + const FORCE_LOADABLE: bool = false; + + /// Do instances of this [`CustomBinaryViewType`] start with no loaded content? + /// + /// When true, the view has no meaningful default state: the user must make a + /// selection (e.g. load images from a shared cache) before any content exists. + /// + /// Callers can use this to suppress restoring the previously saved view state for + /// files not being loaded from a database, since a saved layout would reference + /// content that isn't available on reopening. + const HAS_NO_INITIAL_CONTENT: bool = false; + + /// Constructs the custom binary view instance. + fn create_binary_view(&self, data: &BinaryView) -> Result; + + /// Constructs the custom binary view instance to be used for configuration. + /// + /// This is the path that is used when opening a binary with "Open With Options", and is what populates + /// the sections and segments of the dialog along with settings like the image base (start) address. + /// + /// The default implementation for this will construct a new instance identical to that of [`CustomBinaryViewType::create_binary_view`]. + /// + /// Overriding this is encouraged as you can skip actually applying data to the view such as functions, + /// symbols, and other data not required for configuration, especially because this binary view is created + /// only temporarily and will be discarded after configuration is complete. + fn create_binary_view_for_parse( + &self, + data: &BinaryView, + ) -> Result { + self.create_binary_view(data) + } + + /// Is this [`BinaryViewType`] valid for the given the raw [`BinaryView`]? + /// + /// Typical implementations will read the magic bytes (e.g. 'MZ'), this is a performance-sensitive + /// path so prefer inexpensive checks rather than comprehensive ones. + fn is_valid_for(&self, data: &BinaryView) -> bool; + + fn load_settings_for_data(&self, _data: &BinaryView) -> Ref { + Settings::new() + } +} + +/// A [`BinaryViewType`] acts as a factory for [`BinaryView`] objects. +/// +/// Each file format will have its own type, such as PE, ELF, or Mach-O. +/// +/// Custom view types can be implemented using [`CustomBinaryViewType`]. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BinaryViewType { + pub handle: *mut BNBinaryViewType, +} + +impl BinaryViewType { + pub(crate) unsafe fn from_raw(handle: *mut BNBinaryViewType) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + + pub fn list_all() -> Array { + unsafe { + let mut count: usize = 0; + let types = BNGetBinaryViewTypes(&mut count as *mut _); + Array::new(types, count, ()) + } + } + + /// Enumerates all view types and checks to see if the given raw [`BinaryView`] is valid, + /// returning only those that are. + pub fn valid_types_for_data(data: &BinaryView) -> Array { + unsafe { + let mut count: usize = 0; + let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _); + Array::new(types, count, ()) + } + } + + /// Looks up a binary view type by its name (_not_ the long name). + pub fn by_name(name: &str) -> Option { + let bytes = name.to_cstr(); + let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; + if handle.is_null() { + None + } else { + Some(unsafe { BinaryViewType::from_raw(handle) }) + } + } + + /// The given name for the binary view type. + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetBinaryViewTypeName(self.handle)) } + } + + /// The given long name for the binary view type. + pub fn long_name(&self) -> String { + unsafe { BnString::into_string(BNGetBinaryViewTypeLongName(self.handle)) } + } + + /// Register an architecture for selection via the `id` and `endianness`. + /// + /// If you need to peak at the [`BinaryView`] to determine the architecture, use [`BinaryViewType::register_platform_recognizer`] + /// instead of this. + pub fn register_arch(&self, id: u32, endianness: Endianness, arch: &A) { + unsafe { + BNRegisterArchitectureForViewType(self.handle, id, endianness, arch.as_ref().handle); + } + } + + /// Register a platform for selection via the `id`. + /// + /// If you need to peak at the [`BinaryView`] to determine the platform, use [`BinaryViewType::register_platform_recognizer`] + /// instead of this. + pub fn register_platform(&self, id: u32, plat: &Platform) { + let arch = plat.arch(); + unsafe { + BNRegisterPlatformForViewType(self.handle, id, arch.handle, plat.handle); + } + } + + /// Expanded identification of [`Platform`] for [`BinaryViewType`]'s. Supersedes [`BinaryViewType::register_arch`] + /// and [`BinaryViewType::register_platform`], as these have certain edge cases (overloaded elf families, for example) + /// that can't be represented. + /// + /// The callback returns a [`Platform`] object or `None` (failure), and most recently added callbacks are called first + /// to allow plugins to override any default behaviors. When a callback returns a platform, architecture will be + /// derived from the identified platform. + /// + /// The [`BinaryView`] is the *parent* view (usually 'Raw') that the [`BinaryView`] is being created for. This + /// means that generally speaking, the callbacks need to be aware of the underlying file format. However, the + /// [`BinaryView`] implementation may have created data variables in the 'Raw' view by the time the callback is invoked. + /// Behavior regarding when this callback is invoked and what has been made available in the [`BinaryView`] passed as an + /// argument to the callback is up to the discretion of the [`BinaryView`] implementation. + /// + /// The `id` ind `endian` arguments are used as a filter to determine which registered [`Platform`] recognizer callbacks + /// are invoked. + /// + /// Support for this API tentatively requires explicit support in the [`BinaryView`] implementation. + pub fn register_platform_recognizer(&self, id: u32, endian: Endianness, recognizer: R) + where + R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, + { + #[repr(C)] + struct PlatformRecognizerHandlerContext + where + R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, + { + recognizer: R, + } + + extern "C" fn cb_recognize_low_level_il( + ctxt: *mut std::os::raw::c_void, + bv: *mut BNBinaryView, + metadata: *mut BNMetadata, + ) -> *mut BNPlatform + where + R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, + { + let context = unsafe { &*(ctxt as *mut PlatformRecognizerHandlerContext) }; + let bv = unsafe { BinaryView::from_raw(bv).to_owned() }; + let metadata = unsafe { Metadata::from_raw(metadata).to_owned() }; + match (context.recognizer)(&bv, &metadata) { + Some(plat) => unsafe { Ref::into_raw(plat).handle }, + None => std::ptr::null_mut(), + } + } + + let recognizer = PlatformRecognizerHandlerContext { recognizer }; + let raw = Box::into_raw(Box::new(recognizer)); + unsafe { + BNRegisterPlatformRecognizerForViewType( + self.handle, + id as u64, + endian, + Some(cb_recognize_low_level_il::), + raw as *mut std::os::raw::c_void, + ) + } + } + + /// Creates a new instance of the binary view for this given type, constructed with `data` as + /// the parent view. + /// + /// This will also call the initialization routine for the view, after calling this you should + /// be able to use the view as normal and ready to start analysis with [`BinaryView::update_analysis`]. + pub fn create(&self, data: &BinaryView) -> Result, ()> { + let handle = unsafe { BNCreateBinaryViewOfType(self.handle, data.handle) }; + if handle.is_null() { + // TODO: Proper Result, possibly introduce BNSetError to populate. + return Err(()); + } + unsafe { Ok(BinaryView::ref_from_raw(handle)) } + } + + /// Creates a new instance of the binary view for parsing, this is a "specialize" version of the + /// regular [`BinaryViewType::create`] and is expected to be used when you only want to have the + /// view parsed and populated with information required for configuration, like with open with options. + pub fn parse(&self, data: &BinaryView) -> Result, ()> { + let handle = unsafe { BNParseBinaryViewOfType(self.handle, data.handle) }; + if handle.is_null() { + // TODO: Proper Result, possibly introduce BNSetError to populate. + return Err(()); + } + unsafe { Ok(BinaryView::ref_from_raw(handle)) } + } + + /// Is this [`BinaryViewType`] valid for the given the raw [`BinaryView`]? + /// + /// Typical implementations will read the magic bytes (e.g. 'MZ'), this is a performance-sensitive + /// path so prefer inexpensive checks rather than comprehensive ones. + pub fn is_valid_for(&self, data: &BinaryView) -> bool { + unsafe { BNIsBinaryViewTypeValidForData(self.handle, data.handle) } + } + + /// Is this [`BinaryViewType`] deprecated and should not be used? + /// + /// We specify this such that the view type may still be used by existing databases, but not + /// newly created views. + pub fn is_deprecated(&self) -> bool { + unsafe { BNIsBinaryViewTypeDeprecated(self.handle) } + } + + /// Is this [`BinaryViewType`] able to be loaded forcefully? + /// + /// If so, it will be shown in the drop-down when a user opens a file with options. + pub fn is_force_loadable(&self) -> bool { + unsafe { BNIsBinaryViewTypeForceLoadable(self.handle) } + } + + /// Do instances of this [`BinaryViewType`] start with no loaded content? + /// + /// When true, the view has no meaningful default state: the user must make a + /// selection (e.g. load images from a shared cache) before any content exists. + /// + /// Callers can use this to suppress restoring the previously saved view state for + /// files not being loaded from a database, since a saved layout would reference + /// content that isn't available on reopening. + pub fn has_no_initial_content(&self) -> bool { + unsafe { BNBinaryViewTypeHasNoInitialContent(self.handle) } + } + + pub fn load_settings_for_data(&self, data: &BinaryView) -> Option> { + let settings_handle = + unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) }; + + if settings_handle.is_null() { + None + } else { + unsafe { Some(Settings::ref_from_raw(settings_handle)) } + } + } +} + +impl Debug for BinaryViewType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BinaryViewType") + .field("name", &self.name()) + .field("long_name", &self.long_name()) + .finish() + } +} + +impl CoreArrayProvider for BinaryViewType { + type Raw = *mut BNBinaryViewType; + type Context = (); + type Wrapped<'a> = Guard<'a, BinaryViewType>; +} + +unsafe impl CoreArrayProviderInner for BinaryViewType { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeBinaryViewTypeList(raw); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(BinaryViewType::from_raw(*raw), &()) + } +} + +unsafe impl Send for BinaryViewType {} +unsafe impl Sync for BinaryViewType {} + +/// Implemented for custom views, responsible for setting up the view state once the binary is open. +pub trait CustomBinaryView: BinaryViewBase { + /// Initializes the opened binary view state. + /// + /// Use this to populate the [`BinaryView`] with sections, segments, and other view data. + /// + /// NOTE: You must add **at-least** one segment to the view, otherwise calls to [`BinaryViewType::create`] + /// will fail. + /// + /// NOTE: This will be called on every subsequent open of a database, any view data applied here + /// should be expected to be regenerated on every open. + fn initialize(&mut self, view: &BinaryView) -> bool; + + /// Called after deserialization of the current database snapshot has completed and all the + /// view data inside that snapshot has been applied to the view (like sections and segments). + /// + /// Useful if you need to regenerate temporary data based on the view state. + fn on_after_snapshot_data_applied(&mut self) {} +} + +/// Wrapper around `C` when being passed to the custom view constructor so that we have the core +/// view available to [`CustomBinaryView::initialize`], called from [`cb_init`]. +struct CustomBinaryViewContext { + // This is not ref-counted because we do not want to impact the lifetime of the core view, the lifetime + // of which is already bound to the lifetime of the custom view (to be freed when the custom view is freed). + core_view: MaybeUninit, + view: C, +} + #[allow(clippy::len_without_is_empty)] -pub trait BinaryViewBase: AsRef { +pub trait BinaryViewBase { fn read(&self, _buf: &mut [u8], _offset: u64) -> usize { 0 } @@ -100,10 +464,7 @@ pub trait BinaryViewBase: AsRef { /// Check if the offset is valid for the current view. fn offset_valid(&self, offset: u64) -> bool { let mut buf = [0u8; 1]; - - // don't use self.read so that if segments were used we - // check against those as well - self.as_ref().read(&mut buf[..], offset) == buf.len() + self.read(&mut buf[..], offset) == buf.len() } /// Check if the offset is readable for the current view. @@ -129,8 +490,7 @@ pub trait BinaryViewBase: AsRef { /// Get the next valid offset after the provided `offset`, useful if you need to iterate over all /// readable offsets in the view. fn next_valid_offset_after(&self, offset: u64) -> u64 { - let start = self.as_ref().start(); - + let start = self.start(); if offset < start { start } else { @@ -158,18 +518,20 @@ pub trait BinaryViewBase: AsRef { } fn relocatable(&self) -> bool { - true + false + } + + fn entry_point(&self) -> u64 { + 0 } - fn entry_point(&self) -> u64; fn default_endianness(&self) -> Endianness; + fn address_size(&self) -> usize; + // TODO: Needs to take file accessor? fn save(&self) -> bool { - self.as_ref() - .parent_view() - .map(|view| view.save()) - .unwrap_or(false) + false } } @@ -241,33 +603,213 @@ impl From for AnalysisProgress { } } -pub trait BinaryViewExt: BinaryViewBase { - fn file(&self) -> Ref { +/// Represents the "whole view" of the binary and its analysis. +/// +/// Analysis information: +/// +/// - [`BinaryView::functions`] +/// - [`BinaryView::data_variables`] +/// - [`BinaryView::strings`] +/// +/// Annotation information: +/// +/// - [`BinaryView::symbols`] +/// - [`BinaryView::tags_all_scopes`] +/// - [`BinaryView::comments`] +/// +/// Data representation and binary information: +/// +/// - [`BinaryView::types`] +/// - [`BinaryView::segments`] +/// - [`BinaryView::sections`] +/// +/// # Cleaning up +/// +/// [`BinaryView`] has a cyclic relationship with the associated [`FileMetadata`], each holds a strong +/// reference to one another, so to properly clean up/free the [`BinaryView`], you must manually close the +/// file using [`FileMetadata::close`], this is not fixable in the general case, until [`FileMetadata`] +/// has only a weak reference to the [`BinaryView`]. +#[derive(PartialEq, Eq, Hash)] +pub struct BinaryView { + pub handle: *mut BNBinaryView, +} + +impl BinaryView { + pub unsafe fn from_raw(handle: *mut BNBinaryView) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref { + debug_assert!(!handle.is_null()); + Ref::new(Self { handle }) + } + + /// Create a core instance of the [`CustomBinaryView`]. + pub fn from_custom( + view_type_name: &str, + file: &FileMetadata, + parent_view: &BinaryView, + view: C, + ) -> Result, ()> { + let type_name = view_type_name.to_cstr(); + // We need to pass the core BinaryView when initializing the custom view state with [`CustomBinaryView::initialize`], + // and to do that we need to store the returned core view handle after creating the custom view. + let custom_context = CustomBinaryViewContext { + core_view: MaybeUninit::uninit(), + view, + }; + // We leak to be freed in `cb_free_object`. + let leaked_view = Box::leak(Box::new(custom_context)); + let handle = unsafe { + BNCreateCustomBinaryView( + type_name.as_ptr(), + file.handle, + parent_view.handle, + &mut BNCustomBinaryView { + context: leaked_view as *mut CustomBinaryViewContext as *mut _, + init: Some(cb_init::), + freeObject: Some(cb_free_object::), + externalRefTaken: None, + externalRefReleased: None, + read: Some(cb_read::), + write: Some(cb_write::), + insert: Some(cb_insert::), + remove: Some(cb_remove::), + getModification: Some(cb_modification::), + isValidOffset: Some(cb_offset_valid::), + isOffsetReadable: Some(cb_offset_readable::), + isOffsetWritable: Some(cb_offset_writable::), + isOffsetExecutable: Some(cb_offset_executable::), + isOffsetBackedByFile: Some(cb_offset_backed_by_file::), + getNextValidOffset: Some(cb_next_valid_offset::), + getStart: Some(cb_start::), + getLength: Some(cb_length::), + getEntryPoint: Some(cb_entry_point::), + isExecutable: Some(cb_executable::), + getDefaultEndianness: Some(cb_endianness::), + isRelocatable: Some(cb_relocatable::), + getAddressSize: Some(cb_address_size::), + save: Some(cb_save::), + onAfterSnapshotDataApplied: Some(cb_on_after_snapshot_data_applied::), + }, + ) + }; + if handle.is_null() { + // TODO: Free here? + return Err(()); + } + leaked_view.core_view = unsafe { MaybeUninit::new(BinaryView::from_raw(handle)) }; + unsafe { Ok(Ref::new(Self { handle })) } + } + + /// Construct the raw binary view from the given metadata. + /// + /// Before calling this, make sure you have a valid file path set for the [`FileMetadata`]. It is + /// required that the [`FileMetadata::file_path`] exist in the local filesystem. + pub fn from_metadata(meta: &FileMetadata) -> Result, ()> { + if !meta.file_path().exists() { + return Err(()); + } + let file = meta.file_path().to_cstr(); + let handle = + unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) }; + if handle.is_null() { + return Err(()); + } + unsafe { Ok(Ref::new(Self { handle })) } + } + + /// Construct the raw binary view from the given `file_path` and metadata. + /// + /// This will implicitly set the metadata file path and then construct the view. If the metadata + /// already has the desired file path, use [`BinaryView::from_metadata`] instead. + pub fn from_path(meta: &FileMetadata, file_path: impl AsRef) -> Result, ()> { + meta.set_file_path(file_path.as_ref()); + Self::from_metadata(meta) + } + + // TODO: Provide an API that manages the lifetime of the accessor and the view. + /// Construct the raw binary view from the given `accessor` and metadata. + /// + /// It is the responsibility of the caller to keep the accessor alive for the lifetime of the view; + /// because of this, we mark the function as unsafe. + pub unsafe fn from_accessor( + meta: &FileMetadata, + accessor: &mut FileAccessor, + ) -> Result, ()> { + let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut accessor.raw) }; + if handle.is_null() { + return Err(()); + } + unsafe { Ok(Ref::new(Self { handle })) } + } + + /// Construct the raw binary view from the given `data` and metadata. + /// + /// The data will be copied into the view, so the caller does not need to keep the data alive. + pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Ref { + let handle = unsafe { + BNCreateBinaryDataViewFromData(meta.handle, data.as_ptr() as *mut _, data.len()) + }; + assert!( + !handle.is_null(), + "BNCreateBinaryDataViewFromData should always succeed" + ); + unsafe { Ref::new(Self { handle }) } + } + + /// Save the original binary file to the provided `file_path` along with any modifications. + /// + /// WARNING: Currently, there is a possibility to deadlock if the analysis has queued up a main thread action + /// that tries to take the [`FileMetadata`] lock of the current view and is executed while we + /// are executing in this function. + /// + /// To avoid the above issue, use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there + /// are no queued up main thread actions. + pub fn save_to_path(&self, file_path: impl AsRef) -> bool { + let file = file_path.as_ref().to_cstr(); + unsafe { BNSaveToFilename(self.handle, file.as_ptr() as *mut _) } + } + + /// Save the original binary file to the provided [`FileAccessor`] along with any modifications. + /// + /// WARNING: Currently, there is a possibility to deadlock if the analysis has queued up a main thread action + /// that tries to take the [`FileMetadata`] lock of the current view and is executed while we + /// are executing in this function. + /// + /// To avoid the above issue, use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there + /// are no queued up main thread actions. + pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool { + unsafe { BNSaveToFile(self.handle, &mut file.raw) } + } + + pub fn file(&self) -> Ref { unsafe { - let raw = BNGetFileForView(self.as_ref().handle); + let raw = BNGetFileForView(self.handle); FileMetadata::ref_from_raw(raw) } } - fn parent_view(&self) -> Option> { - let raw_view_ptr = unsafe { BNGetParentView(self.as_ref().handle) }; + pub fn parent_view(&self) -> Option> { + let raw_view_ptr = unsafe { BNGetParentView(self.handle) }; match raw_view_ptr.is_null() { false => Some(unsafe { BinaryView::ref_from_raw(raw_view_ptr) }), true => None, } } - fn raw_view(&self) -> Option> { + pub fn raw_view(&self) -> Option> { self.file().view_of_type("Raw") } - fn view_type(&self) -> String { - let ptr: *mut c_char = unsafe { BNGetViewType(self.as_ref().handle) }; + pub fn view_type(&self) -> String { + let ptr: *mut c_char = unsafe { BNGetViewType(self.handle) }; unsafe { BnString::into_string(ptr) } } /// Reads up to `len` bytes from address `offset` - fn read_vec(&self, offset: u64, len: usize) -> Vec { + pub fn read_vec(&self, offset: u64, len: usize) -> Vec { let mut ret = vec![0; len]; let size = self.read(&mut ret, offset); ret.truncate(size); @@ -275,7 +817,7 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Appends up to `len` bytes from address `offset` into `dest` - fn read_into_vec(&self, dest: &mut Vec, offset: u64, len: usize) -> usize { + pub fn read_into_vec(&self, dest: &mut Vec, offset: u64, len: usize) -> usize { let starting_len = dest.len(); dest.resize(starting_len + len, 0); let read_size = self.read(&mut dest[starting_len..], offset); @@ -284,7 +826,7 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Reads up to `len` bytes from the address `offset` returning a `CString` if available. - fn read_c_string_at(&self, offset: u64, len: usize) -> Option { + pub fn read_c_string_at(&self, offset: u64, len: usize) -> Option { let mut buf = vec![0; len]; let size = self.read(&mut buf, offset); let string = CString::new(buf[..size].to_vec()).ok()?; @@ -292,7 +834,7 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Reads up to `len` bytes from the address `offset` returning a `String` if available. - fn read_utf8_string_at(&self, offset: u64, len: usize) -> Option { + pub fn read_utf8_string_at(&self, offset: u64, len: usize) -> Option { let mut buf = vec![0; len]; let size = self.read(&mut buf, offset); let string = String::from_utf8(buf[..size].to_vec()).ok()?; @@ -302,14 +844,18 @@ pub trait BinaryViewExt: BinaryViewBase { /// Search the view using the query options. /// /// In the `on_match` callback return `false` to stop searching. - fn search bool>(&self, query: &SearchQuery, on_match: C) -> bool { + pub fn search bool>( + &self, + query: &SearchQuery, + on_match: C, + ) -> bool { self.search_with_progress(query, on_match, NoProgressCallback) } /// Search the view using the query options. /// /// In the `on_match` callback return `false` to stop searching. - fn search_with_progress bool>( + pub fn search_with_progress bool>( &self, query: &SearchQuery, mut on_match: C, @@ -328,7 +874,7 @@ pub trait BinaryViewExt: BinaryViewBase { let query = query.to_json().to_cstr(); unsafe { BNSearch( - self.as_ref().handle, + self.handle, query.as_ptr(), &mut progress as *mut P as *mut c_void, Some(P::cb_progress_callback), @@ -338,7 +884,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn find_next_data(&self, start: u64, end: u64, data: &DataBuffer) -> Option { + pub fn find_next_data(&self, start: u64, end: u64, data: &DataBuffer) -> Option { self.find_next_data_with_opts( start, end, @@ -351,7 +897,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// # Warning /// /// This function is likely to be changed to take in a "query" structure. Or deprecated entirely. - fn find_next_data_with_opts( + pub fn find_next_data_with_opts( &self, start: u64, end: u64, @@ -362,7 +908,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut result: u64 = 0; let found = unsafe { BNFindNextDataWithProgress( - self.as_ref().handle, + self.handle, start, end, data.as_raw(), @@ -380,7 +926,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn find_next_constant( + pub fn find_next_constant( &self, start: u64, end: u64, @@ -402,7 +948,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// # Warning /// /// This function is likely to be changed to take in a "query" structure. - fn find_next_constant_with_opts( + pub fn find_next_constant_with_opts( &self, start: u64, end: u64, @@ -415,7 +961,7 @@ pub trait BinaryViewExt: BinaryViewBase { let raw_view_type = FunctionViewType::into_raw(view_type); let found = unsafe { BNFindNextConstantWithProgress( - self.as_ref().handle, + self.handle, start, end, constant, @@ -435,7 +981,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn find_next_text( + pub fn find_next_text( &self, start: u64, end: u64, @@ -458,7 +1004,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// # Warning /// /// This function is likely to be changed to take in a "query" structure. - fn find_next_text_with_opts( + pub fn find_next_text_with_opts( &self, start: u64, end: u64, @@ -473,7 +1019,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut result: u64 = 0; let found = unsafe { BNFindNextTextWithProgress( - self.as_ref().handle, + self.handle, start, end, text.as_ptr(), @@ -494,75 +1040,75 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn notify_data_written(&self, offset: u64, len: usize) { + pub fn notify_data_written(&self, offset: u64, len: usize) { unsafe { - BNNotifyDataWritten(self.as_ref().handle, offset, len); + BNNotifyDataWritten(self.handle, offset, len); } } - fn notify_data_inserted(&self, offset: u64, len: usize) { + pub fn notify_data_inserted(&self, offset: u64, len: usize) { unsafe { - BNNotifyDataInserted(self.as_ref().handle, offset, len); + BNNotifyDataInserted(self.handle, offset, len); } } - fn notify_data_removed(&self, offset: u64, len: usize) { + pub fn notify_data_removed(&self, offset: u64, len: usize) { unsafe { - BNNotifyDataRemoved(self.as_ref().handle, offset, len as u64); + BNNotifyDataRemoved(self.handle, offset, len as u64); } } /// Consults the [`Section`]'s current [`crate::section::Semantics`] to determine if the /// offset has code semantics. - fn offset_has_code_semantics(&self, offset: u64) -> bool { - unsafe { BNIsOffsetCodeSemantics(self.as_ref().handle, offset) } + pub fn offset_has_code_semantics(&self, offset: u64) -> bool { + unsafe { BNIsOffsetCodeSemantics(self.handle, offset) } } /// Check if the offset is within a [`Section`] with [`crate::section::Semantics::External`]. - fn offset_has_extern_semantics(&self, offset: u64) -> bool { - unsafe { BNIsOffsetExternSemantics(self.as_ref().handle, offset) } + pub fn offset_has_extern_semantics(&self, offset: u64) -> bool { + unsafe { BNIsOffsetExternSemantics(self.handle, offset) } } /// Consults the [`Section`]'s current [`crate::section::Semantics`] to determine if the /// offset has writable semantics. - fn offset_has_writable_semantics(&self, offset: u64) -> bool { - unsafe { BNIsOffsetWritableSemantics(self.as_ref().handle, offset) } + pub fn offset_has_writable_semantics(&self, offset: u64) -> bool { + unsafe { BNIsOffsetWritableSemantics(self.handle, offset) } } /// Consults the [`Section`]'s current [`crate::section::Semantics`] to determine if the /// offset has read only semantics. - fn offset_has_read_only_semantics(&self, offset: u64) -> bool { - unsafe { BNIsOffsetReadOnlySemantics(self.as_ref().handle, offset) } + pub fn offset_has_read_only_semantics(&self, offset: u64) -> bool { + unsafe { BNIsOffsetReadOnlySemantics(self.handle, offset) } } - fn image_base(&self) -> u64 { - unsafe { BNGetImageBase(self.as_ref().handle) } + pub fn image_base(&self) -> u64 { + unsafe { BNGetImageBase(self.handle) } } - fn original_image_base(&self) -> u64 { - unsafe { BNGetOriginalImageBase(self.as_ref().handle) } + pub fn original_image_base(&self) -> u64 { + unsafe { BNGetOriginalImageBase(self.handle) } } - fn set_original_image_base(&self, image_base: u64) { - unsafe { BNSetOriginalImageBase(self.as_ref().handle, image_base) } + pub fn set_original_image_base(&self, image_base: u64) { + unsafe { BNSetOriginalImageBase(self.handle, image_base) } } /// The highest address in the view. - fn end(&self) -> u64 { - unsafe { BNGetEndOffset(self.as_ref().handle) } + pub fn end(&self) -> u64 { + unsafe { BNGetEndOffset(self.handle) } } - fn add_analysis_option(&self, name: &str) { + pub fn add_analysis_option(&self, name: &str) { let name = name.to_cstr(); - unsafe { BNAddAnalysisOption(self.as_ref().handle, name.as_ptr()) } + unsafe { BNAddAnalysisOption(self.handle, name.as_ptr()) } } - fn has_initial_analysis(&self) -> bool { - unsafe { BNHasInitialAnalysis(self.as_ref().handle) } + pub fn has_initial_analysis(&self) -> bool { + unsafe { BNHasInitialAnalysis(self.handle) } } - fn set_analysis_hold(&self, enable: bool) { - unsafe { BNSetAnalysisHold(self.as_ref().handle, enable) } + pub fn set_analysis_hold(&self, enable: bool) { + unsafe { BNSetAnalysisHold(self.handle, enable) } } /// Runs the analysis pipeline, analyzing any data that has been marked for updates. @@ -571,11 +1117,11 @@ pub trait BinaryViewExt: BinaryViewBase { /// - [`Function::mark_updates_required`] /// - [`Function::mark_caller_updates_required`] /// - /// NOTE: This is a **non-blocking** call, use [`BinaryViewExt::update_analysis_and_wait`] if you + /// NOTE: This is a **non-blocking** call, use [`BinaryView::update_analysis_and_wait`] if you /// require analysis to have completed before moving on. - fn update_analysis(&self) { + pub fn update_analysis(&self) { unsafe { - BNUpdateAnalysis(self.as_ref().handle); + BNUpdateAnalysis(self.handle); } } @@ -585,47 +1131,47 @@ pub trait BinaryViewExt: BinaryViewBase { /// - [`Function::mark_updates_required`] /// - [`Function::mark_caller_updates_required`] /// - /// NOTE: This is a **blocking** call, use [`BinaryViewExt::update_analysis`] if you do not + /// NOTE: This is a **blocking** call, use [`BinaryView::update_analysis`] if you do not /// need to wait for the analysis update to finish. - fn update_analysis_and_wait(&self) { + pub fn update_analysis_and_wait(&self) { unsafe { - BNUpdateAnalysisAndWait(self.as_ref().handle); + BNUpdateAnalysisAndWait(self.handle); } } /// Causes **all** functions to be reanalyzed. /// - /// Use [`BinaryViewExt::update_analysis`] or [`BinaryViewExt::update_analysis_and_wait`] instead + /// Use [`BinaryView::update_analysis`] or [`BinaryView::update_analysis_and_wait`] instead /// if you want to incrementally update analysis. /// /// NOTE: This function does not wait for the analysis to finish. - fn reanalyze(&self) { + pub fn reanalyze(&self) { unsafe { - BNReanalyzeAllFunctions(self.as_ref().handle); + BNReanalyzeAllFunctions(self.handle); } } - fn abort_analysis(&self) { - unsafe { BNAbortAnalysis(self.as_ref().handle) } + pub fn abort_analysis(&self) { + unsafe { BNAbortAnalysis(self.handle) } } - fn analysis_is_aborted(&self) -> bool { - unsafe { BNAnalysisIsAborted(self.as_ref().handle) } + pub fn analysis_is_aborted(&self) -> bool { + unsafe { BNAnalysisIsAborted(self.handle) } } - fn workflow(&self) -> Ref { + pub fn workflow(&self) -> Ref { unsafe { - let raw_ptr = BNGetWorkflowForBinaryView(self.as_ref().handle); + let raw_ptr = BNGetWorkflowForBinaryView(self.handle); let nonnull = NonNull::new(raw_ptr).expect("All views must have a workflow"); Workflow::ref_from_raw(nonnull) } } - fn analysis_info(&self) -> AnalysisInfo { - let info_ptr = unsafe { BNGetAnalysisInfo(self.as_ref().handle) }; + pub fn analysis_info(&self) -> AnalysisInfo { + let info_ptr = unsafe { BNGetAnalysisInfo(self.handle) }; assert!(!info_ptr.is_null()); let info = unsafe { *info_ptr }; - let active_infos = unsafe { slice::from_raw_parts(info.activeInfo, info.count) }; + let active_infos = unsafe { std::slice::from_raw_parts(info.activeInfo, info.count) }; let mut active_info_list = vec![]; for active_info in active_infos { @@ -648,14 +1194,14 @@ pub trait BinaryViewExt: BinaryViewBase { result } - fn analysis_progress(&self) -> AnalysisProgress { - let progress_raw = unsafe { BNGetAnalysisProgress(self.as_ref().handle) }; + pub fn analysis_progress(&self) -> AnalysisProgress { + let progress_raw = unsafe { BNGetAnalysisProgress(self.handle) }; AnalysisProgress::from(progress_raw) } - fn default_arch(&self) -> Option { + pub fn default_arch(&self) -> Option { unsafe { - let raw = BNGetDefaultArchitecture(self.as_ref().handle); + let raw = BNGetDefaultArchitecture(self.handle); if raw.is_null() { return None; @@ -665,15 +1211,15 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn set_default_arch(&self, arch: &A) { + pub fn set_default_arch(&self, arch: &A) { unsafe { - BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().handle); + BNSetDefaultArchitecture(self.handle, arch.as_ref().handle); } } - fn default_platform(&self) -> Option> { + pub fn default_platform(&self) -> Option> { unsafe { - let raw = BNGetDefaultPlatform(self.as_ref().handle); + let raw = BNGetDefaultPlatform(self.handle); if raw.is_null() { return None; @@ -683,22 +1229,22 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn set_default_platform(&self, plat: &Platform) { + pub fn set_default_platform(&self, plat: &Platform) { unsafe { - BNSetDefaultPlatform(self.as_ref().handle, plat.handle); + BNSetDefaultPlatform(self.handle, plat.handle); } } - fn base_address_detection(&self) -> Option { + pub fn base_address_detection(&self) -> Option { unsafe { - let handle = BNCreateBaseAddressDetection(self.as_ref().handle); + let handle = BNCreateBaseAddressDetection(self.handle); NonNull::new(handle).map(|base| BaseAddressDetection::from_raw(base)) } } - fn instruction_len(&self, arch: &A, addr: u64) -> Option { + pub fn instruction_len(&self, arch: &A, addr: u64) -> Option { unsafe { - let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().handle, addr); + let size = BNGetInstructionLength(self.handle, arch.as_ref().handle, addr); if size > 0 { Some(size) @@ -708,10 +1254,9 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbol_by_address(&self, addr: u64) -> Option> { + pub fn symbol_by_address(&self, addr: u64) -> Option> { unsafe { - let raw_sym_ptr = - BNGetSymbolByAddress(self.as_ref().handle, addr, std::ptr::null_mut()); + let raw_sym_ptr = BNGetSymbolByAddress(self.handle, addr, std::ptr::null_mut()); match raw_sym_ptr.is_null() { false => Some(Symbol::ref_from_raw(raw_sym_ptr)), true => None, @@ -719,15 +1264,12 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbol_by_raw_name(&self, raw_name: impl IntoCStr) -> Option> { + pub fn symbol_by_raw_name(&self, raw_name: impl IntoCStr) -> Option> { let raw_name = raw_name.to_cstr(); unsafe { - let raw_sym_ptr = BNGetSymbolByRawName( - self.as_ref().handle, - raw_name.as_ptr(), - std::ptr::null_mut(), - ); + let raw_sym_ptr = + BNGetSymbolByRawName(self.handle, raw_name.as_ptr(), std::ptr::null_mut()); match raw_sym_ptr.is_null() { false => Some(Symbol::ref_from_raw(raw_sym_ptr)), true => None, @@ -735,22 +1277,22 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbols(&self) -> Array { + pub fn symbols(&self) -> Array { unsafe { let mut count = 0; - let handles = BNGetSymbols(self.as_ref().handle, &mut count, std::ptr::null_mut()); + let handles = BNGetSymbols(self.handle, &mut count, std::ptr::null_mut()); Array::new(handles, count, ()) } } - fn symbols_by_name(&self, name: impl IntoCStr) -> Array { + pub fn symbols_by_name(&self, name: impl IntoCStr) -> Array { let raw_name = name.to_cstr(); unsafe { let mut count = 0; let handles = BNGetSymbolsByName( - self.as_ref().handle, + self.handle, raw_name.as_ptr(), &mut count, std::ptr::null_mut(), @@ -760,12 +1302,12 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbols_in_range(&self, range: Range) -> Array { + pub fn symbols_in_range(&self, range: Range) -> Array { unsafe { let mut count = 0; let len = range.end.wrapping_sub(range.start); let handles = BNGetSymbolsInRange( - self.as_ref().handle, + self.handle, range.start, len, &mut count, @@ -776,26 +1318,22 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbols_of_type(&self, ty: SymbolType) -> Array { + pub fn symbols_of_type(&self, ty: SymbolType) -> Array { unsafe { let mut count = 0; - let handles = BNGetSymbolsOfType( - self.as_ref().handle, - ty.into(), - &mut count, - std::ptr::null_mut(), - ); + let handles = + BNGetSymbolsOfType(self.handle, ty.into(), &mut count, std::ptr::null_mut()); Array::new(handles, count, ()) } } - fn symbols_of_type_in_range(&self, ty: SymbolType, range: Range) -> Array { + pub fn symbols_of_type_in_range(&self, ty: SymbolType, range: Range) -> Array { unsafe { let mut count = 0; let len = range.end.wrapping_sub(range.start); let handles = BNGetSymbolsOfTypeInRange( - self.as_ref().handle, + self.handle, ty.into(), range.start, len, @@ -807,18 +1345,21 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn define_auto_symbol(&self, sym: &Symbol) { + pub fn define_auto_symbol(&self, sym: &Symbol) { unsafe { - BNDefineAutoSymbol(self.as_ref().handle, sym.handle); + BNDefineAutoSymbol(self.handle, sym.handle); } } - fn define_auto_symbol_with_type<'a, T: Into>>( + /// Defines the symbol as well as the analysis object associated with the given symbol type, such as + /// the data variable for a [`SymbolType::Data`], or the function for a [`SymbolType::Function`]. + /// Returns the symbol, as it was applied to the binary view. + pub fn define_auto_symbol_with_type<'a, T: Into>>( &self, sym: &Symbol, plat: &Platform, ty: T, - ) -> Result> { + ) -> Ref { let mut type_with_conf = BNTypeWithConfidence { type_: if let Some(t) = ty.into() { t.handle @@ -830,50 +1371,50 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let raw_sym = BNDefineAutoSymbolAndVariableOrFunction( - self.as_ref().handle, + self.handle, plat.handle, sym.handle, &mut type_with_conf, ); - - if raw_sym.is_null() { - return Err(()); - } - - Ok(Symbol::ref_from_raw(raw_sym)) + // We should always get the symbol back as it is defined. + debug_assert!( + !raw_sym.is_null(), + "BNDefineAutoSymbolAndVariableOrFunction should not return null" + ); + Symbol::ref_from_raw(raw_sym) } } - fn undefine_auto_symbol(&self, sym: &Symbol) { + pub fn undefine_auto_symbol(&self, sym: &Symbol) { unsafe { - BNUndefineAutoSymbol(self.as_ref().handle, sym.handle); + BNUndefineAutoSymbol(self.handle, sym.handle); } } - fn define_user_symbol(&self, sym: &Symbol) { + pub fn define_user_symbol(&self, sym: &Symbol) { unsafe { - BNDefineUserSymbol(self.as_ref().handle, sym.handle); + BNDefineUserSymbol(self.handle, sym.handle); } } - fn undefine_user_symbol(&self, sym: &Symbol) { + pub fn undefine_user_symbol(&self, sym: &Symbol) { unsafe { - BNUndefineUserSymbol(self.as_ref().handle, sym.handle); + BNUndefineUserSymbol(self.handle, sym.handle); } } - fn data_variables(&self) -> Array { + pub fn data_variables(&self) -> Array { unsafe { let mut count = 0; - let vars = BNGetDataVariables(self.as_ref().handle, &mut count); + let vars = BNGetDataVariables(self.handle, &mut count); Array::new(vars, count, ()) } } - fn data_variable_at_address(&self, addr: u64) -> Option { + pub fn data_variable_at_address(&self, addr: u64) -> Option { let mut dv = BNDataVariable::default(); unsafe { - if BNGetDataVariableAtAddress(self.as_ref().handle, addr, &mut dv) { + if BNGetDataVariableAtAddress(self.handle, addr, &mut dv) { Some(DataVariable::from_owned_raw(dv)) } else { None @@ -881,34 +1422,34 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn define_auto_data_var<'a, T: Into>>(&self, addr: u64, ty: T) { + pub fn define_auto_data_var<'a, T: Into>>(&self, addr: u64, ty: T) { let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { - BNDefineDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty); + BNDefineDataVariable(self.handle, addr, &mut owned_raw_ty); } } - /// You likely would also like to call [`BinaryViewExt::define_user_symbol`] to bind this data variable with a name - fn define_user_data_var<'a, T: Into>>(&self, addr: u64, ty: T) { + /// You likely would also like to call [`BinaryView::define_user_symbol`] to bind this data variable with a name + pub fn define_user_data_var<'a, T: Into>>(&self, addr: u64, ty: T) { let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { - BNDefineUserDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty); + BNDefineUserDataVariable(self.handle, addr, &mut owned_raw_ty); } } - fn undefine_auto_data_var(&self, addr: u64, blacklist: Option) { + pub fn undefine_auto_data_var(&self, addr: u64, blacklist: Option) { unsafe { - BNUndefineDataVariable(self.as_ref().handle, addr, blacklist.unwrap_or(true)); + BNUndefineDataVariable(self.handle, addr, blacklist.unwrap_or(true)); } } - fn undefine_user_data_var(&self, addr: u64) { + pub fn undefine_user_data_var(&self, addr: u64) { unsafe { - BNUndefineUserDataVariable(self.as_ref().handle, addr); + BNUndefineUserDataVariable(self.handle, addr); } } - fn define_auto_type>( + pub fn define_auto_type>( &self, name: T, source: &str, @@ -919,13 +1460,13 @@ pub trait BinaryViewExt: BinaryViewBase { let name_handle = unsafe { let id_str = BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name); - BNDefineAnalysisType(self.as_ref().handle, id_str, &mut raw_name, type_obj.handle) + BNDefineAnalysisType(self.handle, id_str, &mut raw_name, type_obj.handle) }; QualifiedName::free_raw(raw_name); QualifiedName::from_owned_raw(name_handle) } - fn define_auto_type_with_id>( + pub fn define_auto_type_with_id>( &self, name: T, id: &str, @@ -935,7 +1476,7 @@ pub trait BinaryViewExt: BinaryViewBase { let id_str = id.to_cstr(); let result_raw_name = unsafe { BNDefineAnalysisType( - self.as_ref().handle, + self.handle, id_str.as_ref().as_ptr() as *const _, &mut raw_name, type_obj.handle, @@ -945,13 +1486,16 @@ pub trait BinaryViewExt: BinaryViewBase { QualifiedName::from_owned_raw(result_raw_name) } - fn define_user_type>(&self, name: T, type_obj: &Type) { + pub fn define_user_type>(&self, name: T, type_obj: &Type) { let mut raw_name = QualifiedName::into_raw(name.into()); - unsafe { BNDefineUserAnalysisType(self.as_ref().handle, &mut raw_name, type_obj.handle) } + unsafe { BNDefineUserAnalysisType(self.handle, &mut raw_name, type_obj.handle) } QualifiedName::free_raw(raw_name); } - fn define_auto_types(&self, names_sources_and_types: T) -> HashMap + pub fn define_auto_types( + &self, + names_sources_and_types: T, + ) -> HashMap where T: Iterator, I: Into, @@ -959,7 +1503,7 @@ pub trait BinaryViewExt: BinaryViewBase { self.define_auto_types_with_progress(names_sources_and_types, NoProgressCallback) } - fn define_auto_types_with_progress( + pub fn define_auto_types_with_progress( &self, names_sources_and_types: T, mut progress: P, @@ -978,7 +1522,7 @@ pub trait BinaryViewExt: BinaryViewBase { let result_count = unsafe { BNDefineAnalysisTypes( - self.as_ref().handle, + self.handle, types.as_mut_ptr(), types.len(), Some(P::cb_progress_callback), @@ -1001,7 +1545,7 @@ pub trait BinaryViewExt: BinaryViewBase { .collect() } - fn define_user_types(&self, names_and_types: T) + pub fn define_user_types(&self, names_and_types: T) where T: Iterator, I: Into, @@ -1009,7 +1553,7 @@ pub trait BinaryViewExt: BinaryViewBase { self.define_user_types_with_progress(names_and_types, NoProgressCallback); } - fn define_user_types_with_progress(&self, names_and_types: T, mut progress: P) + pub fn define_user_types_with_progress(&self, names_and_types: T, mut progress: P) where T: Iterator, I: Into, @@ -1022,7 +1566,7 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNDefineUserAnalysisTypes( - self.as_ref().handle, + self.handle, types.as_mut_ptr(), types.len(), Some(P::cb_progress_callback), @@ -1035,39 +1579,39 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn undefine_auto_type(&self, id: &str) { + pub fn undefine_auto_type(&self, id: &str) { let id_str = id.to_cstr(); unsafe { - BNUndefineAnalysisType(self.as_ref().handle, id_str.as_ref().as_ptr() as *const _); + BNUndefineAnalysisType(self.handle, id_str.as_ref().as_ptr() as *const _); } } - fn undefine_user_type>(&self, name: T) { + pub fn undefine_user_type>(&self, name: T) { let mut raw_name = QualifiedName::into_raw(name.into()); - unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut raw_name) } + unsafe { BNUndefineUserAnalysisType(self.handle, &mut raw_name) } QualifiedName::free_raw(raw_name); } - fn types(&self) -> Array { + pub fn types(&self) -> Array { unsafe { let mut count = 0usize; - let types = BNGetAnalysisTypeList(self.as_ref().handle, &mut count); + let types = BNGetAnalysisTypeList(self.handle, &mut count); Array::new(types, count, ()) } } - fn dependency_sorted_types(&self) -> Array { + pub fn dependency_sorted_types(&self) -> Array { unsafe { let mut count = 0usize; - let types = BNGetAnalysisDependencySortedTypeList(self.as_ref().handle, &mut count); + let types = BNGetAnalysisDependencySortedTypeList(self.handle, &mut count); Array::new(types, count, ()) } } - fn type_by_name>(&self, name: T) -> Option> { + pub fn type_by_name>(&self, name: T) -> Option> { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { - let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut raw_name); + let type_handle = BNGetAnalysisTypeByName(self.handle, &mut raw_name); QualifiedName::free_raw(raw_name); if type_handle.is_null() { return None; @@ -1076,9 +1620,9 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_by_ref(&self, ref_: &NamedTypeReference) -> Option> { + pub fn type_by_ref(&self, ref_: &NamedTypeReference) -> Option> { unsafe { - let type_handle = BNGetAnalysisTypeByRef(self.as_ref().handle, ref_.handle); + let type_handle = BNGetAnalysisTypeByRef(self.handle, ref_.handle); if type_handle.is_null() { return None; } @@ -1086,10 +1630,10 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_by_id(&self, id: &str) -> Option> { + pub fn type_by_id(&self, id: &str) -> Option> { let id_str = id.to_cstr(); unsafe { - let type_handle = BNGetAnalysisTypeById(self.as_ref().handle, id_str.as_ptr()); + let type_handle = BNGetAnalysisTypeById(self.handle, id_str.as_ptr()); if type_handle.is_null() { return None; } @@ -1097,10 +1641,10 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_name_by_id(&self, id: &str) -> Option { + pub fn type_name_by_id(&self, id: &str) -> Option { let id_str = id.to_cstr(); unsafe { - let name_handle = BNGetAnalysisTypeNameById(self.as_ref().handle, id_str.as_ptr()); + let name_handle = BNGetAnalysisTypeNameById(self.handle, id_str.as_ptr()); let name = QualifiedName::from_owned_raw(name_handle); // The core will return an empty qualified name if no type name was found. match name.items.is_empty() { @@ -1110,10 +1654,10 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_id_by_name>(&self, name: T) -> Option { + pub fn type_id_by_name>(&self, name: T) -> Option { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { - let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut raw_name); + let id_cstr = BNGetAnalysisTypeId(self.handle, &mut raw_name); QualifiedName::free_raw(raw_name); let id = BnString::into_string(id_cstr); match id.is_empty() { @@ -1123,24 +1667,24 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn is_type_auto_defined>(&self, name: T) -> bool { + pub fn is_type_auto_defined>(&self, name: T) -> bool { let mut raw_name = QualifiedName::into_raw(name.into()); - let result = unsafe { BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut raw_name) }; + let result = unsafe { BNIsAnalysisTypeAutoDefined(self.handle, &mut raw_name) }; QualifiedName::free_raw(raw_name); result } - fn segments(&self) -> Array { + pub fn segments(&self) -> Array { unsafe { let mut count = 0; - let raw_segments = BNGetSegments(self.as_ref().handle, &mut count); + let raw_segments = BNGetSegments(self.handle, &mut count); Array::new(raw_segments, count, ()) } } - fn segment_at(&self, addr: u64) -> Option> { + pub fn segment_at(&self, addr: u64) -> Option> { unsafe { - let raw_seg = BNGetSegmentAt(self.as_ref().handle, addr); + let raw_seg = BNGetSegmentAt(self.handle, addr); match raw_seg.is_null() { false => Some(Segment::ref_from_raw(raw_seg)), true => None, @@ -1150,24 +1694,24 @@ pub trait BinaryViewExt: BinaryViewBase { /// Adds a segment to the view. /// - /// NOTE: Consider using [BinaryViewExt::begin_bulk_add_segments] and [BinaryViewExt::end_bulk_add_segments] + /// NOTE: Consider using [BinaryView::begin_bulk_add_segments] and [BinaryView::end_bulk_add_segments] /// if you plan on adding a number of segments all at once, to avoid unnecessary MemoryMap updates. - fn add_segment(&self, segment: SegmentBuilder) { + pub fn add_segment(&self, segment: SegmentBuilder) { segment.create(self.as_ref()); } // TODO: Replace with BulkModify guard. /// Start adding segments in bulk. Useful for adding large numbers of segments. /// - /// After calling this any call to [BinaryViewExt::add_segment] will be uncommitted until a call to - /// [BinaryViewExt::end_bulk_add_segments] + /// After calling this any call to [BinaryView::add_segment] will be uncommitted until a call to + /// [BinaryView::end_bulk_add_segments] /// - /// If you wish to discard the uncommitted segments you can call [BinaryViewExt::cancel_bulk_add_segments]. + /// If you wish to discard the uncommitted segments you can call [BinaryView::cancel_bulk_add_segments]. /// - /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or - /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommitted. - fn begin_bulk_add_segments(&self) { - unsafe { BNBeginBulkAddSegments(self.as_ref().handle) } + /// NOTE: This **must** be paired with a later call to [BinaryView::end_bulk_add_segments] or + /// [BinaryView::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommitted. + pub fn begin_bulk_add_segments(&self) { + unsafe { BNBeginBulkAddSegments(self.handle) } } // TODO: Replace with BulkModify guard. @@ -1175,8 +1719,8 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// NOTE: This **must** be paired with a prior call to [Self::begin_bulk_add_segments], otherwise this /// does nothing and segments are added individually. - fn end_bulk_add_segments(&self) { - unsafe { BNEndBulkAddSegments(self.as_ref().handle) } + pub fn end_bulk_add_segments(&self) { + unsafe { BNEndBulkAddSegments(self.handle) } } // TODO: Replace with BulkModify guard. @@ -1186,35 +1730,35 @@ pub trait BinaryViewExt: BinaryViewBase { /// and [Self::end_bulk_add_segments], where the latter will commit the segments /// which have been added since [Self::begin_bulk_add_segments], this function /// will discard them so that they do not get added to the view. - fn cancel_bulk_add_segments(&self) { - unsafe { BNCancelBulkAddSegments(self.as_ref().handle) } + pub fn cancel_bulk_add_segments(&self) { + unsafe { BNCancelBulkAddSegments(self.handle) } } - fn add_section(&self, section: SectionBuilder) { + pub fn add_section(&self, section: SectionBuilder) { section.create(self.as_ref()); } - fn remove_auto_section(&self, name: impl IntoCStr) { + pub fn remove_auto_section(&self, name: impl IntoCStr) { let raw_name = name.to_cstr(); let raw_name_ptr = raw_name.as_ptr(); unsafe { - BNRemoveAutoSection(self.as_ref().handle, raw_name_ptr); + BNRemoveAutoSection(self.handle, raw_name_ptr); } } - fn remove_user_section(&self, name: impl IntoCStr) { + pub fn remove_user_section(&self, name: impl IntoCStr) { let raw_name = name.to_cstr(); let raw_name_ptr = raw_name.as_ptr(); unsafe { - BNRemoveUserSection(self.as_ref().handle, raw_name_ptr); + BNRemoveUserSection(self.handle, raw_name_ptr); } } - fn section_by_name(&self, name: impl IntoCStr) -> Option> { + pub fn section_by_name(&self, name: impl IntoCStr) -> Option> { unsafe { let raw_name = name.to_cstr(); let name_ptr = raw_name.as_ptr(); - let raw_section_ptr = BNGetSectionByName(self.as_ref().handle, name_ptr); + let raw_section_ptr = BNGetSectionByName(self.handle, name_ptr); match raw_section_ptr.is_null() { false => Some(Section::ref_from_raw(raw_section_ptr)), true => None, @@ -1222,42 +1766,42 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn sections(&self) -> Array
{ + pub fn sections(&self) -> Array
{ unsafe { let mut count = 0; - let sections = BNGetSections(self.as_ref().handle, &mut count); + let sections = BNGetSections(self.handle, &mut count); Array::new(sections, count, ()) } } - fn sections_at(&self, addr: u64) -> Array
{ + pub fn sections_at(&self, addr: u64) -> Array
{ unsafe { let mut count = 0; - let sections = BNGetSectionsAt(self.as_ref().handle, addr, &mut count); + let sections = BNGetSectionsAt(self.handle, addr, &mut count); Array::new(sections, count, ()) } } - fn memory_map(&self) -> MemoryMap { + pub fn memory_map(&self) -> MemoryMap { MemoryMap::new(self.as_ref().to_owned()) } /// Add an auto function at the given `address` with the views default platform. /// - /// Use [`BinaryViewExt::add_auto_function_with_platform`] if you wish to specify a platform. + /// Use [`BinaryView::add_auto_function_with_platform`] if you wish to specify a platform. /// /// NOTE: The default platform **must** be set for this view! - fn add_auto_function(&self, address: u64) -> Option> { + pub fn add_auto_function(&self, address: u64) -> Option> { let platform = self.default_platform()?; self.add_auto_function_with_platform(address, &platform) } /// Add an auto function at the given `address` with the `platform`. /// - /// Use [`BinaryViewExt::add_auto_function_ext`] if you wish to specify a function type. + /// Use [`BinaryView::add_auto_function_ext`] if you wish to specify a function type. /// /// NOTE: If the view's default platform is not set, this will set it to `platform`. - fn add_auto_function_with_platform( + pub fn add_auto_function_with_platform( &self, address: u64, platform: &Platform, @@ -1268,7 +1812,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// Add an auto function at the given `address` with the `platform` and function type. /// /// NOTE: If the view's default platform is not set, this will set it to `platform`. - fn add_auto_function_ext( + pub fn add_auto_function_ext( &self, address: u64, platform: &Platform, @@ -1280,13 +1824,8 @@ pub trait BinaryViewExt: BinaryViewBase { None => std::ptr::null_mut(), }; - let handle = BNAddFunctionForAnalysis( - self.as_ref().handle, - platform.handle, - address, - true, - func_type, - ); + let handle = + BNAddFunctionForAnalysis(self.handle, platform.handle, address, true, func_type); if handle.is_null() { return None; @@ -1300,21 +1839,21 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// Pass `true` for `update_refs` to update all references of the function. /// - /// NOTE: Unlike [`BinaryViewExt::remove_user_function`], this will NOT prohibit the function from - /// being re-added in the future, use [`BinaryViewExt::remove_user_function`] to blacklist the + /// NOTE: Unlike [`BinaryView::remove_user_function`], this will NOT prohibit the function from + /// being re-added in the future, use [`BinaryView::remove_user_function`] to blacklist the /// function from being automatically created. - fn remove_auto_function(&self, func: &Function, update_refs: bool) { + pub fn remove_auto_function(&self, func: &Function, update_refs: bool) { unsafe { - BNRemoveAnalysisFunction(self.as_ref().handle, func.handle, update_refs); + BNRemoveAnalysisFunction(self.handle, func.handle, update_refs); } } /// Add a user function at the given `address` with the views default platform. /// - /// Use [`BinaryViewExt::add_user_function_with_platform`] if you wish to specify a platform. + /// Use [`BinaryView::add_user_function_with_platform`] if you wish to specify a platform. /// /// NOTE: The default platform **must** be set for this view! - fn add_user_function(&self, addr: u64) -> Option> { + pub fn add_user_function(&self, addr: u64) -> Option> { let platform = self.default_platform()?; self.add_user_function_with_platform(addr, &platform) } @@ -1322,13 +1861,13 @@ pub trait BinaryViewExt: BinaryViewBase { /// Add an auto function at the given `address` with the `platform`. /// /// NOTE: If the view's default platform is not set, this will set it to `platform`. - fn add_user_function_with_platform( + pub fn add_user_function_with_platform( &self, addr: u64, platform: &Platform, ) -> Option> { unsafe { - let func = BNCreateUserFunction(self.as_ref().handle, platform.handle, addr); + let func = BNCreateUserFunction(self.handle, platform.handle, addr); if func.is_null() { return None; } @@ -1338,19 +1877,19 @@ pub trait BinaryViewExt: BinaryViewBase { /// Removes the function from the view and blacklists it from being created automatically. /// - /// NOTE: If you call [`BinaryViewExt::add_user_function`], it will override the blacklist. - fn remove_user_function(&self, func: &Function) { - unsafe { BNRemoveUserFunction(self.as_ref().handle, func.handle) } + /// NOTE: If you call [`BinaryView::add_user_function`], it will override the blacklist. + pub fn remove_user_function(&self, func: &Function) { + unsafe { BNRemoveUserFunction(self.handle, func.handle) } } - fn has_functions(&self) -> bool { - unsafe { BNHasFunctions(self.as_ref().handle) } + pub fn has_functions(&self) -> bool { + unsafe { BNHasFunctions(self.handle) } } /// Add an entry point at the given `address` with the view's default platform. /// /// NOTE: The default platform **must** be set for this view! - fn add_entry_point(&self, addr: u64) { + pub fn add_entry_point(&self, addr: u64) { if let Some(platform) = self.default_platform() { self.add_entry_point_with_platform(addr, &platform); } @@ -1359,15 +1898,15 @@ pub trait BinaryViewExt: BinaryViewBase { /// Add an entry point at the given `address` with the `platform`. /// /// NOTE: If the view's default platform is not set, this will set it to `platform`. - fn add_entry_point_with_platform(&self, addr: u64, platform: &Platform) { + pub fn add_entry_point_with_platform(&self, addr: u64, platform: &Platform) { unsafe { - BNAddEntryPointForAnalysis(self.as_ref().handle, platform.handle, addr); + BNAddEntryPointForAnalysis(self.handle, platform.handle, addr); } } - fn entry_point_function(&self) -> Option> { + pub fn entry_point_function(&self) -> Option> { unsafe { - let raw_func_ptr = BNGetAnalysisEntryPoint(self.as_ref().handle); + let raw_func_ptr = BNGetAnalysisEntryPoint(self.handle); match raw_func_ptr.is_null() { false => Some(Function::ref_from_raw(raw_func_ptr)), true => None, @@ -1380,41 +1919,39 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// We see `entry_functions` as good starting points for analysis, these functions normally don't /// have internal references. Exported functions in a dll/so file are not included. - fn entry_point_functions(&self) -> Array { + pub fn entry_point_functions(&self) -> Array { unsafe { let mut count = 0; - let functions = BNGetAllEntryFunctions(self.as_ref().handle, &mut count); + let functions = BNGetAllEntryFunctions(self.handle, &mut count); Array::new(functions, count, ()) } } - fn functions(&self) -> Array { + pub fn functions(&self) -> Array { unsafe { let mut count = 0; - let functions = BNGetAnalysisFunctionList(self.as_ref().handle, &mut count); + let functions = BNGetAnalysisFunctionList(self.handle, &mut count); Array::new(functions, count, ()) } } /// List of functions *starting* at `addr` - fn functions_at(&self, addr: u64) -> Array { + pub fn functions_at(&self, addr: u64) -> Array { unsafe { let mut count = 0; - let functions = - BNGetAnalysisFunctionsForAddress(self.as_ref().handle, addr, &mut count); + let functions = BNGetAnalysisFunctionsForAddress(self.handle, addr, &mut count); Array::new(functions, count, ()) } } /// List of functions containing `addr` - fn functions_containing(&self, addr: u64) -> Array { + pub fn functions_containing(&self, addr: u64) -> Array { unsafe { let mut count = 0; - let functions = - BNGetAnalysisFunctionsContainingAddress(self.as_ref().handle, addr, &mut count); + let functions = BNGetAnalysisFunctionsContainingAddress(self.handle, addr, &mut count); Array::new(functions, count, ()) } @@ -1429,7 +1966,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// # Params /// - `name`: Name that the function should have /// - `plat`: Optional platform that the function should be defined for. Defaults to all platforms if `None` passed. - fn functions_by_name( + pub fn functions_by_name( &self, name: impl IntoCStr, plat: Option<&Platform>, @@ -1459,9 +1996,9 @@ pub trait BinaryViewExt: BinaryViewBase { functions } - fn function_at(&self, platform: &Platform, addr: u64) -> Option> { + pub fn function_at(&self, platform: &Platform, addr: u64) -> Option> { unsafe { - let raw_func_ptr = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr); + let raw_func_ptr = BNGetAnalysisFunction(self.handle, platform.handle, addr); match raw_func_ptr.is_null() { false => Some(Function::ref_from_raw(raw_func_ptr)), true => None, @@ -1469,42 +2006,42 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn function_start_before(&self, addr: u64) -> u64 { - unsafe { BNGetPreviousFunctionStartBeforeAddress(self.as_ref().handle, addr) } + pub fn function_start_before(&self, addr: u64) -> u64 { + unsafe { BNGetPreviousFunctionStartBeforeAddress(self.handle, addr) } } - fn function_start_after(&self, addr: u64) -> u64 { - unsafe { BNGetNextFunctionStartAfterAddress(self.as_ref().handle, addr) } + pub fn function_start_after(&self, addr: u64) -> u64 { + unsafe { BNGetNextFunctionStartAfterAddress(self.handle, addr) } } - fn basic_blocks_containing(&self, addr: u64) -> Array> { + pub fn basic_blocks_containing(&self, addr: u64) -> Array> { unsafe { let mut count = 0; - let blocks = BNGetBasicBlocksForAddress(self.as_ref().handle, addr, &mut count); + let blocks = BNGetBasicBlocksForAddress(self.handle, addr, &mut count); Array::new(blocks, count, NativeBlock::new()) } } - fn basic_blocks_starting_at(&self, addr: u64) -> Array> { + pub fn basic_blocks_starting_at(&self, addr: u64) -> Array> { unsafe { let mut count = 0; - let blocks = BNGetBasicBlocksStartingAtAddress(self.as_ref().handle, addr, &mut count); + let blocks = BNGetBasicBlocksStartingAtAddress(self.handle, addr, &mut count); Array::new(blocks, count, NativeBlock::new()) } } - fn is_new_auto_function_analysis_suppressed(&self) -> bool { - unsafe { BNGetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle) } + pub fn is_new_auto_function_analysis_suppressed(&self) -> bool { + unsafe { BNGetNewAutoFunctionAnalysisSuppressed(self.handle) } } - fn set_new_auto_function_analysis_suppressed(&self, suppress: bool) { + pub fn set_new_auto_function_analysis_suppressed(&self, suppress: bool) { unsafe { - BNSetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle, suppress); + BNSetNewAutoFunctionAnalysisSuppressed(self.handle, suppress); } } // TODO: Should this instead be implemented on [`Function`] considering `src_func`? `Location` is local to the source function. - fn should_skip_target_analysis( + pub fn should_skip_target_analysis( &self, src_loc: impl Into, src_func: &Function, @@ -1515,7 +2052,7 @@ pub trait BinaryViewExt: BinaryViewBase { let target = target.into(); unsafe { BNShouldSkipTargetAnalysis( - self.as_ref().handle, + self.handle, &mut src_loc.into(), src_func.handle, src_end, @@ -1524,8 +2061,8 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn read_buffer(&self, offset: u64, len: usize) -> Option { - let read_buffer = unsafe { BNReadViewBuffer(self.as_ref().handle, offset, len) }; + pub fn read_buffer(&self, offset: u64, len: usize) -> Option { + let read_buffer = unsafe { BNReadViewBuffer(self.handle, offset, len) }; if read_buffer.is_null() { None } else { @@ -1533,37 +2070,37 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn debug_info(&self) -> Ref { - unsafe { DebugInfo::ref_from_raw(BNGetDebugInfo(self.as_ref().handle)) } + pub fn debug_info(&self) -> Ref { + unsafe { DebugInfo::ref_from_raw(BNGetDebugInfo(self.handle)) } } - fn set_debug_info(&self, debug_info: &DebugInfo) { - unsafe { BNSetDebugInfo(self.as_ref().handle, debug_info.handle) } + pub fn set_debug_info(&self, debug_info: &DebugInfo) { + unsafe { BNSetDebugInfo(self.handle, debug_info.handle) } } - fn apply_debug_info(&self, debug_info: &DebugInfo) { - unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) } + pub fn apply_debug_info(&self, debug_info: &DebugInfo) { + unsafe { BNApplyDebugInfo(self.handle, debug_info.handle) } } - fn show_plaintext_report(&self, title: &str, plaintext: &str) { + pub fn show_plaintext_report(&self, title: &str, plaintext: &str) { let title = title.to_cstr(); let plaintext = plaintext.to_cstr(); unsafe { BNShowPlainTextReport( - self.as_ref().handle, + self.handle, title.as_ref().as_ptr() as *mut _, plaintext.as_ref().as_ptr() as *mut _, ) } } - fn show_markdown_report(&self, title: &str, contents: &str, plaintext: &str) { + pub fn show_markdown_report(&self, title: &str, contents: &str, plaintext: &str) { let title = title.to_cstr(); let contents = contents.to_cstr(); let plaintext = plaintext.to_cstr(); unsafe { BNShowMarkdownReport( - self.as_ref().handle, + self.handle, title.as_ref().as_ptr() as *mut _, contents.as_ref().as_ptr() as *mut _, plaintext.as_ref().as_ptr() as *mut _, @@ -1571,13 +2108,13 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn show_html_report(&self, title: &str, contents: &str, plaintext: &str) { + pub fn show_html_report(&self, title: &str, contents: &str, plaintext: &str) { let title = title.to_cstr(); let contents = contents.to_cstr(); let plaintext = plaintext.to_cstr(); unsafe { BNShowHTMLReport( - self.as_ref().handle, + self.handle, title.as_ref().as_ptr() as *mut _, contents.as_ref().as_ptr() as *mut _, plaintext.as_ref().as_ptr() as *mut _, @@ -1585,34 +2122,28 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn show_graph_report(&self, raw_name: &str, graph: &FlowGraph) { + pub fn show_graph_report(&self, raw_name: &str, graph: &FlowGraph) { let raw_name = raw_name.to_cstr(); unsafe { - BNShowGraphReport(self.as_ref().handle, raw_name.as_ptr(), graph.handle); + BNShowGraphReport(self.handle, raw_name.as_ptr(), graph.handle); } } - fn load_settings(&self, view_type_name: &str) -> Result> { + pub fn load_settings(&self, view_type_name: &str) -> Ref { let view_type_name = view_type_name.to_cstr(); let settings_handle = - unsafe { BNBinaryViewGetLoadSettings(self.as_ref().handle, view_type_name.as_ptr()) }; - - if settings_handle.is_null() { - Err(()) - } else { - Ok(unsafe { Settings::ref_from_raw(settings_handle) }) + unsafe { BNBinaryViewGetLoadSettings(self.handle, view_type_name.as_ptr()) }; + match settings_handle.is_null() { + true => Settings::new(), + false => unsafe { Settings::ref_from_raw(settings_handle) }, } } - fn set_load_settings(&self, view_type_name: &str, settings: &Settings) { + pub fn set_load_settings(&self, view_type_name: &str, settings: &Settings) { let view_type_name = view_type_name.to_cstr(); unsafe { - BNBinaryViewSetLoadSettings( - self.as_ref().handle, - view_type_name.as_ptr(), - settings.handle, - ) + BNBinaryViewSetLoadSettings(self.handle, view_type_name.as_ptr(), settings.handle) }; } @@ -1621,24 +2152,24 @@ pub trait BinaryViewExt: BinaryViewBase { /// # Arguments /// * `name` - the name for the tag /// * `icon` - the icon (recommended 1 emoji or 2 chars) for the tag - fn create_tag_type(&self, name: &str, icon: &str) -> Ref { - let tag_type = TagType::create(self.as_ref(), name, icon); + pub fn create_tag_type(&self, name: &str, icon: &str) -> Ref { + let tag_type = TagType::create(self, name, icon); unsafe { - BNAddTagType(self.as_ref().handle, tag_type.handle); + BNAddTagType(self.handle, tag_type.handle); } tag_type } /// Removes a [TagType] and all tags that use it - fn remove_tag_type(&self, tag_type: &TagType) { - unsafe { BNRemoveTagType(self.as_ref().handle, tag_type.handle) } + pub fn remove_tag_type(&self, tag_type: &TagType) { + unsafe { BNRemoveTagType(self.handle, tag_type.handle) } } /// Get a tag type by its name. - fn tag_type_by_name(&self, name: &str) -> Option> { + pub fn tag_type_by_name(&self, name: &str) -> Option> { let name = name.to_cstr(); unsafe { - let handle = BNGetTagType(self.as_ref().handle, name.as_ptr()); + let handle = BNGetTagType(self.handle, name.as_ptr()); if handle.is_null() { return None; } @@ -1647,29 +2178,29 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Get all tags in all scopes - fn tags_all_scopes(&self) -> Array { + pub fn tags_all_scopes(&self) -> Array { let mut count = 0; unsafe { - let tag_references = BNGetAllTagReferences(self.as_ref().handle, &mut count); + let tag_references = BNGetAllTagReferences(self.handle, &mut count); Array::new(tag_references, count, ()) } } /// Get all tag types present for the view - fn tag_types(&self) -> Array { + pub fn tag_types(&self) -> Array { let mut count = 0; unsafe { - let tag_types_raw = BNGetTagTypes(self.as_ref().handle, &mut count); + let tag_types_raw = BNGetTagTypes(self.handle, &mut count); Array::new(tag_types_raw, count, ()) } } /// Get all tag references of a specific type - fn tags_by_type(&self, tag_type: &TagType) -> Array { + pub fn tags_by_type(&self, tag_type: &TagType) -> Array { let mut count = 0; unsafe { let tag_references = - BNGetAllTagReferencesOfType(self.as_ref().handle, tag_type.handle, &mut count); + BNGetAllTagReferencesOfType(self.handle, tag_type.handle, &mut count); Array::new(tag_references, count, ()) } } @@ -1677,10 +2208,10 @@ pub trait BinaryViewExt: BinaryViewBase { /// Get a tag by its id. /// /// Note this does not tell you anything about where it is used. - fn tag_by_id(&self, id: &str) -> Option> { + pub fn tag_by_id(&self, id: &str) -> Option> { let id = id.to_cstr(); unsafe { - let handle = BNGetTag(self.as_ref().handle, id.as_ptr()); + let handle = BNGetTag(self.handle, id.as_ptr()); if handle.is_null() { return None; } @@ -1691,55 +2222,54 @@ pub trait BinaryViewExt: BinaryViewBase { /// Creates and adds a tag to an address /// /// User tag creations will be added to the undo buffer - fn add_tag(&self, addr: u64, t: &TagType, data: &str, user: bool) { + pub fn add_tag(&self, addr: u64, t: &TagType, data: &str, user: bool) { let tag = Tag::new(t, data); - unsafe { BNAddTag(self.as_ref().handle, tag.handle, user) } + unsafe { BNAddTag(self.handle, tag.handle, user) } if user { - unsafe { BNAddUserDataTag(self.as_ref().handle, addr, tag.handle) } + unsafe { BNAddUserDataTag(self.handle, addr, tag.handle) } } else { - unsafe { BNAddAutoDataTag(self.as_ref().handle, addr, tag.handle) } + unsafe { BNAddAutoDataTag(self.handle, addr, tag.handle) } } } /// removes a Tag object at a data address. - fn remove_auto_data_tag(&self, addr: u64, tag: &Tag) { - unsafe { BNRemoveAutoDataTag(self.as_ref().handle, addr, tag.handle) } + pub fn remove_auto_data_tag(&self, addr: u64, tag: &Tag) { + unsafe { BNRemoveAutoDataTag(self.handle, addr, tag.handle) } } /// removes a Tag object at a data address. /// Since this removes a user tag, it will be added to the current undo buffer. - fn remove_user_data_tag(&self, addr: u64, tag: &Tag) { - unsafe { BNRemoveUserDataTag(self.as_ref().handle, addr, tag.handle) } + pub fn remove_user_data_tag(&self, addr: u64, tag: &Tag) { + unsafe { BNRemoveUserDataTag(self.handle, addr, tag.handle) } } /// Retrieves a list of comment addresses, the comments themselves can then be queried with - /// the function [`BinaryViewExt::comment_at`]. + /// the function [`BinaryView::comment_at`]. /// /// If you would rather retrieve the contents of **all** comments at once you can do so with - /// the helper function [`BinaryViewExt::comments`]. - fn comment_references(&self) -> Array { + /// the helper function [`BinaryView::comments`]. + pub fn comment_references(&self) -> Array { let mut count = 0; - let addresses_raw = - unsafe { BNGetGlobalCommentedAddresses(self.as_ref().handle, &mut count) }; + let addresses_raw = unsafe { BNGetGlobalCommentedAddresses(self.handle, &mut count) }; unsafe { Array::new(addresses_raw, count, ()) } } /// Retrieves a map of comment addresses to their contents. /// /// This is a helper function that eagerly reads the contents of all comments within the - /// view, use [`BinaryViewExt::comment_references`] instead if you do not wish to read all the comments. - fn comments(&self) -> BTreeMap { + /// view, use [`BinaryView::comment_references`] instead if you do not wish to read all the comments. + pub fn comments(&self) -> BTreeMap { self.comment_references() .iter() .filter_map(|cmt_ref| Some((cmt_ref.start, self.comment_at(cmt_ref.start)?))) .collect() } - fn comment_at(&self, addr: u64) -> Option { + pub fn comment_at(&self, addr: u64) -> Option { unsafe { - let comment_raw = BNGetGlobalCommentForAddress(self.as_ref().handle, addr); + let comment_raw = BNGetGlobalCommentForAddress(self.handle, addr); match comment_raw.is_null() { false => Some(BnString::into_string(comment_raw)), true => None, @@ -1751,9 +2281,9 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// NOTE: This is different from setting a comment at the function-level. To set a comment in a /// function use [`Function::set_comment_at`] - fn set_comment_at(&self, addr: u64, comment: &str) { + pub fn set_comment_at(&self, addr: u64, comment: &str) { let comment_raw = comment.to_cstr(); - unsafe { BNSetGlobalCommentForAddress(self.as_ref().handle, addr, comment_raw.as_ptr()) } + unsafe { BNSetGlobalCommentForAddress(self.handle, addr, comment_raw.as_ptr()) } } /// Retrieves a list of the next disassembly lines. @@ -1764,7 +2294,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// # Arguments /// * `pos` - Position to retrieve linear disassembly lines from - fn get_next_linear_disassembly_lines( + pub fn get_next_linear_disassembly_lines( &self, pos: &mut LinearViewCursor, ) -> Array { @@ -1788,7 +2318,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// # Arguments /// * `pos` - Position to retrieve linear disassembly lines relative to - fn get_previous_linear_disassembly_lines( + pub fn get_previous_linear_disassembly_lines( &self, pos: &mut LinearViewCursor, ) -> Array { @@ -1804,10 +2334,10 @@ pub trait BinaryViewExt: BinaryViewBase { result } - fn query_metadata(&self, key: &str) -> Option> { + pub fn query_metadata(&self, key: &str) -> Option> { let key = key.to_cstr(); let value: *mut BNMetadata = - unsafe { BNBinaryViewQueryMetadata(self.as_ref().handle, key.as_ptr()) }; + unsafe { BNBinaryViewQueryMetadata(self.handle, key.as_ptr()) }; if value.is_null() { None } else { @@ -1818,50 +2348,45 @@ pub trait BinaryViewExt: BinaryViewBase { /// Retrieve the metadata as the type `T`. /// /// Fails if the metadata does not exist, or if the metadata failed to coerce to type `T`. - fn get_metadata(&self, key: &str) -> Option> + pub fn get_metadata(&self, key: &str) -> Option where T: for<'a> TryFrom<&'a Metadata>, { self.query_metadata(key) - .map(|md| T::try_from(md.as_ref()).map_err(|_| ())) + .and_then(|md| T::try_from(md.as_ref()).ok()) } - fn store_metadata(&self, key: &str, value: V, is_auto: bool) + pub fn store_metadata(&self, key: &str, value: V, is_auto: bool) where V: Into>, { let md = value.into(); let key = key.to_cstr(); unsafe { - BNBinaryViewStoreMetadata( - self.as_ref().handle, - key.as_ptr(), - md.as_ref().handle, - is_auto, - ) + BNBinaryViewStoreMetadata(self.handle, key.as_ptr(), md.as_ref().handle, is_auto) }; } - fn remove_metadata(&self, key: &str) { + pub fn remove_metadata(&self, key: &str) { let key = key.to_cstr(); - unsafe { BNBinaryViewRemoveMetadata(self.as_ref().handle, key.as_ptr()) }; + unsafe { BNBinaryViewRemoveMetadata(self.handle, key.as_ptr()) }; } /// Retrieves a list of [CodeReference]s pointing to a given address. - fn code_refs_to_addr(&self, addr: u64) -> Array { + pub fn code_refs_to_addr(&self, addr: u64) -> Array { unsafe { let mut count = 0; - let handle = BNGetCodeReferences(self.as_ref().handle, addr, &mut count, false, 0); + let handle = BNGetCodeReferences(self.handle, addr, &mut count, false, 0); Array::new(handle, count, ()) } } /// Retrieves a list of [CodeReference]s pointing into a given [Range]. - fn code_refs_into_range(&self, range: Range) -> Array { + pub fn code_refs_into_range(&self, range: Range) -> Array { unsafe { let mut count = 0; let handle = BNGetCodeReferencesInRange( - self.as_ref().handle, + self.handle, range.start, range.end - range.start, &mut count, @@ -1873,14 +2398,13 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Retrieves a list of addresses pointed to by a given address. - fn code_refs_from_addr(&self, addr: u64, func: Option<&Function>) -> Vec { + pub fn code_refs_from_addr(&self, addr: u64, func: Option<&Function>) -> Vec { unsafe { let mut count = 0; let code_ref = CodeReference::new(addr, func.map(|f| f.to_owned()), func.map(|f| f.arch())); let mut raw_code_ref = CodeReference::into_owned_raw(&code_ref); - let addresses = - BNGetCodeReferencesFrom(self.as_ref().handle, &mut raw_code_ref, &mut count); + let addresses = BNGetCodeReferencesFrom(self.handle, &mut raw_code_ref, &mut count); let res = std::slice::from_raw_parts(addresses, count).to_vec(); BNFreeAddressList(addresses); res @@ -1888,20 +2412,20 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Retrieves a list of [DataReference]s pointing to a given address. - fn data_refs_to_addr(&self, addr: u64) -> Array { + pub fn data_refs_to_addr(&self, addr: u64) -> Array { unsafe { let mut count = 0; - let handle = BNGetDataReferences(self.as_ref().handle, addr, &mut count, false, 0); + let handle = BNGetDataReferences(self.handle, addr, &mut count, false, 0); Array::new(handle, count, ()) } } /// Retrieves a list of [DataReference]s pointing into a given [Range]. - fn data_refs_into_range(&self, range: Range) -> Array { + pub fn data_refs_into_range(&self, range: Range) -> Array { unsafe { let mut count = 0; let handle = BNGetDataReferencesInRange( - self.as_ref().handle, + self.handle, range.start, range.end - range.start, &mut count, @@ -1913,60 +2437,56 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Retrieves a list of [DataReference]s originating from a given address. - fn data_refs_from_addr(&self, addr: u64) -> Array { + pub fn data_refs_from_addr(&self, addr: u64) -> Array { unsafe { let mut count = 0; - let handle = BNGetDataReferencesFrom(self.as_ref().handle, addr, &mut count); + let handle = BNGetDataReferencesFrom(self.handle, addr, &mut count); Array::new(handle, count, ()) } } /// Retrieves a list of [CodeReference]s for locations in code that use a given named type. - fn code_refs_using_type_name>(&self, name: T) -> Array { + pub fn code_refs_using_type_name>( + &self, + name: T, + ) -> Array { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let mut count = 0; - let handle = BNGetCodeReferencesForType( - self.as_ref().handle, - &mut raw_name, - &mut count, - false, - 0, - ); + let handle = + BNGetCodeReferencesForType(self.handle, &mut raw_name, &mut count, false, 0); QualifiedName::free_raw(raw_name); Array::new(handle, count, ()) } } /// Retrieves a list of [DataReference]s for locations in data that use a given named type. - fn data_refs_using_type_name>(&self, name: T) -> Array { + pub fn data_refs_using_type_name>( + &self, + name: T, + ) -> Array { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let mut count = 0; - let handle = BNGetDataReferencesForType( - self.as_ref().handle, - &mut raw_name, - &mut count, - false, - 0, - ); + let handle = + BNGetDataReferencesForType(self.handle, &mut raw_name, &mut count, false, 0); QualifiedName::free_raw(raw_name); Array::new(handle, count, ()) } } - fn relocations_at(&self, addr: u64) -> Array { + pub fn relocations_at(&self, addr: u64) -> Array { unsafe { let mut count = 0; - let handle = BNGetRelocationsAt(self.as_ref().handle, addr, &mut count); + let handle = BNGetRelocationsAt(self.handle, addr, &mut count); Array::new(handle, count, ()) } } - fn relocation_ranges(&self) -> Vec> { + pub fn relocation_ranges(&self) -> Vec> { let ranges = unsafe { let mut count = 0; - let reloc_ranges_ptr = BNGetRelocationRanges(self.as_ref().handle, &mut count); + let reloc_ranges_ptr = BNGetRelocationRanges(self.handle, &mut count); let ranges = std::slice::from_raw_parts(reloc_ranges_ptr, count).to_vec(); BNFreeRelocationRanges(reloc_ranges_ptr); ranges @@ -1982,64 +2502,62 @@ pub trait BinaryViewExt: BinaryViewBase { .collect() } - fn component_by_guid(&self, guid: &str) -> Option> { + pub fn component_by_guid(&self, guid: &str) -> Option> { let name = guid.to_cstr(); - let result = unsafe { BNGetComponentByGuid(self.as_ref().handle, name.as_ptr()) }; + let result = unsafe { BNGetComponentByGuid(self.handle, name.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) }) } - fn root_component(&self) -> Option> { - let result = unsafe { BNGetRootComponent(self.as_ref().handle) }; + pub fn root_component(&self) -> Option> { + let result = unsafe { BNGetRootComponent(self.handle) }; NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) }) } - fn component_by_path(&self, path: &str) -> Option> { + pub fn component_by_path(&self, path: &str) -> Option> { let path = path.to_cstr(); - let result = unsafe { BNGetComponentByPath(self.as_ref().handle, path.as_ptr()) }; + let result = unsafe { BNGetComponentByPath(self.handle, path.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) }) } - fn remove_component(&self, component: &Component) -> bool { - unsafe { BNRemoveComponent(self.as_ref().handle, component.handle.as_ptr()) } + pub fn remove_component(&self, component: &Component) -> bool { + unsafe { BNRemoveComponent(self.handle, component.handle.as_ptr()) } } - fn remove_component_by_guid(&self, guid: &str) -> bool { + pub fn remove_component_by_guid(&self, guid: &str) -> bool { let path = guid.to_cstr(); - unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) } + unsafe { BNRemoveComponentByGuid(self.handle, path.as_ptr()) } } - fn data_variable_parent_components(&self, data_variable: &DataVariable) -> Array { + pub fn data_variable_parent_components( + &self, + data_variable: &DataVariable, + ) -> Array { let mut count = 0; let result = unsafe { - BNGetDataVariableParentComponents( - self.as_ref().handle, - data_variable.address, - &mut count, - ) + BNGetDataVariableParentComponents(self.handle, data_variable.address, &mut count) }; unsafe { Array::new(result, count, ()) } } - fn external_libraries(&self) -> Array { + pub fn external_libraries(&self) -> Array { let mut count = 0; - let result = unsafe { BNBinaryViewGetExternalLibraries(self.as_ref().handle, &mut count) }; + let result = unsafe { BNBinaryViewGetExternalLibraries(self.handle, &mut count) }; unsafe { Array::new(result, count, ()) } } - fn external_library(&self, name: &str) -> Option> { + pub fn external_library(&self, name: &str) -> Option> { let name_ptr = name.to_cstr(); - let result = - unsafe { BNBinaryViewGetExternalLibrary(self.as_ref().handle, name_ptr.as_ptr()) }; + let result = unsafe { BNBinaryViewGetExternalLibrary(self.handle, name_ptr.as_ptr()) }; let result_ptr = NonNull::new(result)?; Some(unsafe { ExternalLibrary::ref_from_raw(result_ptr) }) } - fn remove_external_library(&self, name: &str) { + pub fn remove_external_library(&self, name: &str) { let name_ptr = name.to_cstr(); - unsafe { BNBinaryViewRemoveExternalLibrary(self.as_ref().handle, name_ptr.as_ptr()) }; + unsafe { BNBinaryViewRemoveExternalLibrary(self.handle, name_ptr.as_ptr()) }; } - fn add_external_library( + pub fn add_external_library( &self, name: &str, backing_file: Option<&ProjectFile>, @@ -2048,7 +2566,7 @@ pub trait BinaryViewExt: BinaryViewBase { let name_ptr = name.to_cstr(); let result = unsafe { BNBinaryViewAddExternalLibrary( - self.as_ref().handle, + self.handle, name_ptr.as_ptr(), backing_file .map(|b| b.handle.as_ptr()) @@ -2059,29 +2577,28 @@ pub trait BinaryViewExt: BinaryViewBase { NonNull::new(result).map(|h| unsafe { ExternalLibrary::ref_from_raw(h) }) } - fn external_locations(&self) -> Array { + pub fn external_locations(&self) -> Array { let mut count = 0; - let result = unsafe { BNBinaryViewGetExternalLocations(self.as_ref().handle, &mut count) }; + let result = unsafe { BNBinaryViewGetExternalLocations(self.handle, &mut count) }; unsafe { Array::new(result, count, ()) } } - fn external_location_from_symbol(&self, symbol: &Symbol) -> Option> { - let result = - unsafe { BNBinaryViewGetExternalLocation(self.as_ref().handle, symbol.handle) }; + pub fn external_location_from_symbol(&self, symbol: &Symbol) -> Option> { + let result = unsafe { BNBinaryViewGetExternalLocation(self.handle, symbol.handle) }; let result_ptr = NonNull::new(result)?; Some(unsafe { ExternalLocation::ref_from_raw(result_ptr) }) } - fn remove_external_location(&self, location: &ExternalLocation) { + pub fn remove_external_location(&self, location: &ExternalLocation) { self.remove_external_location_from_symbol(&location.source_symbol()) } - fn remove_external_location_from_symbol(&self, symbol: &Symbol) { - unsafe { BNBinaryViewRemoveExternalLocation(self.as_ref().handle, symbol.handle) }; + pub fn remove_external_location_from_symbol(&self, symbol: &Symbol) { + unsafe { BNBinaryViewRemoveExternalLocation(self.handle, symbol.handle) }; } // TODO: This is awful, rewrite this. - fn add_external_location( + pub fn add_external_location( &self, symbol: &Symbol, library: &ExternalLibrary, @@ -2095,7 +2612,7 @@ pub trait BinaryViewExt: BinaryViewBase { .unwrap_or(std::ptr::null_mut()); let result = unsafe { BNBinaryViewAddExternalLocation( - self.as_ref().handle, + self.handle, symbol.handle, library.handle.as_ptr(), target_symbol_name.as_ptr(), @@ -2109,17 +2626,16 @@ pub trait BinaryViewExt: BinaryViewBase { /// Type container for all types (user and auto) in the Binary View. /// /// NOTE: Modifying an auto type will promote it to a user type. - fn type_container(&self) -> TypeContainer { - let type_container_ptr = - NonNull::new(unsafe { BNGetAnalysisTypeContainer(self.as_ref().handle) }); + pub fn type_container(&self) -> TypeContainer { + let type_container_ptr = NonNull::new(unsafe { BNGetAnalysisTypeContainer(self.handle) }); // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw` unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) } } /// Type container for user types in the Binary View. - fn user_type_container(&self) -> TypeContainer { + pub fn user_type_container(&self) -> TypeContainer { let type_container_ptr = - NonNull::new(unsafe { BNGetAnalysisUserTypeContainer(self.as_ref().handle) }); + NonNull::new(unsafe { BNGetAnalysisUserTypeContainer(self.handle) }); // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw` unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }.clone() } @@ -2127,39 +2643,39 @@ pub trait BinaryViewExt: BinaryViewBase { /// Type container for auto types in the Binary View. /// /// NOTE: Unlike [`Self::type_container`] modification of auto types will **NOT** promote it to a user type. - fn auto_type_container(&self) -> TypeContainer { + pub fn auto_type_container(&self) -> TypeContainer { let type_container_ptr = - NonNull::new(unsafe { BNGetAnalysisAutoTypeContainer(self.as_ref().handle) }); + NonNull::new(unsafe { BNGetAnalysisAutoTypeContainer(self.handle) }); // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw` unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) } } - fn type_libraries(&self) -> Array { + pub fn type_libraries(&self) -> Array { let mut count = 0; - let result = unsafe { BNGetBinaryViewTypeLibraries(self.as_ref().handle, &mut count) }; + let result = unsafe { BNGetBinaryViewTypeLibraries(self.handle, &mut count) }; unsafe { Array::new(result, count, ()) } } /// Make the contents of a type library available for type/import resolution - fn add_type_library(&self, library: &TypeLibrary) { - unsafe { BNAddBinaryViewTypeLibrary(self.as_ref().handle, library.as_raw()) } + pub fn add_type_library(&self, library: &TypeLibrary) { + unsafe { BNAddBinaryViewTypeLibrary(self.handle, library.as_raw()) } } - fn type_library_by_name(&self, name: &str) -> Option> { + pub fn type_library_by_name(&self, name: &str) -> Option> { let name = name.to_cstr(); - let result = unsafe { BNGetBinaryViewTypeLibrary(self.as_ref().handle, name.as_ptr()) }; + let result = unsafe { BNGetBinaryViewTypeLibrary(self.handle, name.as_ptr()) }; NonNull::new(result).map(|h| unsafe { TypeLibrary::ref_from_raw(h) }) } /// Should be called by custom [`BinaryView`] implementations when they have successfully /// imported an object from a type library (eg a symbol's type). Values recorded with this - /// function will then be queryable via [`BinaryViewExt::lookup_imported_object_library`]. + /// function will then be queryable via [`BinaryView::lookup_imported_object_library`]. /// /// * `lib` - Type Library containing the imported type /// * `name` - Name of the object in the type library /// * `addr` - address of symbol at import site /// * `platform` - Platform of symbol at import site - fn record_imported_object_library>( + pub fn record_imported_object_library>( &self, lib: &TypeLibrary, name: T, @@ -2169,7 +2685,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNBinaryViewRecordImportedObjectLibrary( - self.as_ref().handle, + self.handle, platform.handle, addr, lib.as_raw(), @@ -2189,7 +2705,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// Note that the name actually inserted into the view may not match the name as it exists in /// the type library in the event of a name conflict. To aid in this, the [`Type`] object /// returned is a `NamedTypeReference` to the deconflicted name used. - fn import_type_library_type>( + pub fn import_type_library_type>( &self, name: T, lib: Option<&TypeLibrary>, @@ -2199,9 +2715,8 @@ pub trait BinaryViewExt: BinaryViewBase { .map(|l| unsafe { l.as_raw() } as *mut _) .unwrap_or(std::ptr::null_mut()); let mut raw_name = QualifiedName::into_raw(name.into()); - let result = unsafe { - BNBinaryViewImportTypeLibraryType(self.as_ref().handle, &mut lib_ref, &mut raw_name) - }; + let result = + unsafe { BNBinaryViewImportTypeLibraryType(self.handle, &mut lib_ref, &mut raw_name) }; QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -2214,9 +2729,9 @@ pub trait BinaryViewExt: BinaryViewBase { /// libraries are lazily resolved when references to types provided by them are first encountered. /// /// NOTE: If you are implementing a custom [`BinaryView`] and use this method to import object types, - /// you should then call [BinaryViewExt::record_imported_object_library] with the details of + /// you should then call [BinaryView::record_imported_object_library] with the details of /// where the object is located. - fn import_type_library_object>( + pub fn import_type_library_object>( &self, name: T, lib: Option<&TypeLibrary>, @@ -2227,17 +2742,16 @@ pub trait BinaryViewExt: BinaryViewBase { .unwrap_or(std::ptr::null_mut()); let mut raw_name = QualifiedName::into_raw(name.into()); let result = unsafe { - BNBinaryViewImportTypeLibraryObject(self.as_ref().handle, &mut lib_ref, &mut raw_name) + BNBinaryViewImportTypeLibraryObject(self.handle, &mut lib_ref, &mut raw_name) }; QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } /// Recursively imports a [`Type`] given its GUID from available type libraries. - fn import_type_by_guid(&self, guid: &str) -> Option> { + pub fn import_type_by_guid(&self, guid: &str) -> Option> { let guid = guid.to_cstr(); - let result = - unsafe { BNBinaryViewImportTypeLibraryTypeByGuid(self.as_ref().handle, guid.as_ptr()) }; + let result = unsafe { BNBinaryViewImportTypeLibraryTypeByGuid(self.handle, guid.as_ptr()) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -2245,7 +2759,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// As other referenced types are encountered, they are either copied into the destination type library or /// else the type library that provided the referenced type is added as a dependency for the destination library. - fn export_type_to_library>( + pub fn export_type_to_library>( &self, lib: &TypeLibrary, name: T, @@ -2254,7 +2768,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNBinaryViewExportTypeToTypeLibrary( - self.as_ref().handle, + self.handle, lib.as_raw(), &mut raw_name, type_obj.handle, @@ -2267,7 +2781,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// As other referenced types are encountered, they are either copied into the destination type library or /// else the type library that provided the referenced type is added as a dependency for the destination library. - fn export_object_to_library>( + pub fn export_object_to_library>( &self, lib: &TypeLibrary, name: T, @@ -2276,7 +2790,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNBinaryViewExportObjectToTypeLibrary( - self.as_ref().handle, + self.handle, lib.as_raw(), &mut raw_name, type_obj.handle, @@ -2290,7 +2804,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// * `addr` - address of symbol at import site /// * `platform` - Platform of symbol at import site - fn lookup_imported_object_library( + pub fn lookup_imported_object_library( &self, addr: u64, platform: &Platform, @@ -2299,7 +2813,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut result_name = BNQualifiedName::default(); let success = unsafe { BNBinaryViewLookupImportedObjectLibrary( - self.as_ref().handle, + self.handle, platform.handle, addr, &mut result_lib, @@ -2317,7 +2831,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// Gives you details of from which type library and name a given type in the analysis was imported. /// /// * `name` - Name of type in analysis - fn lookup_imported_type_library>( + pub fn lookup_imported_type_library>( &self, name: T, ) -> Option<(Ref, QualifiedName)> { @@ -2326,7 +2840,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut result_name = BNQualifiedName::default(); let success = unsafe { BNBinaryViewLookupImportedTypeLibrary( - self.as_ref().handle, + self.handle, &raw_name, &mut result_lib, &mut result_name, @@ -2349,15 +2863,15 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// Some helpers for reading strings are available: /// - /// - [`BinaryViewExt::read_c_string_at`] - /// - [`BinaryViewExt::read_utf8_string_at`] + /// - [`BinaryView::read_c_string_at`] + /// - [`BinaryView::read_utf8_string_at`] /// /// NOTE: This returns discovered strings and is therefore governed by `analysis.limits.minStringLength` /// and other settings. - fn strings(&self) -> Array { + pub fn strings(&self) -> Array { unsafe { let mut count = 0; - let strings = BNGetStrings(self.as_ref().handle, &mut count); + let strings = BNGetStrings(self.handle, &mut count); Array::new(strings, count, ()) } } @@ -2370,14 +2884,14 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// Some helpers for reading strings are available: /// - /// - [`BinaryViewExt::read_c_string_at`] - /// - [`BinaryViewExt::read_utf8_string_at`] + /// - [`BinaryView::read_c_string_at`] + /// - [`BinaryView::read_utf8_string_at`] /// /// NOTE: This returns discovered strings and is therefore governed by `analysis.limits.minStringLength` /// and other settings. - fn string_at(&self, addr: u64) -> Option { + pub fn string_at(&self, addr: u64) -> Option { let mut str_ref = BNStringReference::default(); - let success = unsafe { BNGetStringAtAddress(self.as_ref().handle, addr, &mut str_ref) }; + let success = unsafe { BNGetStringAtAddress(self.handle, addr, &mut str_ref) }; if success { Some(str_ref.into()) } else { @@ -2393,16 +2907,16 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// Some helpers for reading strings are available: /// - /// - [`BinaryViewExt::read_c_string_at`] - /// - [`BinaryViewExt::read_utf8_string_at`] + /// - [`BinaryView::read_c_string_at`] + /// - [`BinaryView::read_utf8_string_at`] /// /// NOTE: This returns discovered strings and is therefore governed by `analysis.limits.minStringLength` /// and other settings. - fn strings_in_range(&self, range: Range) -> Array { + pub fn strings_in_range(&self, range: Range) -> Array { unsafe { let mut count = 0; let strings = BNGetStringsInRange( - self.as_ref().handle, + self.handle, range.start, range.end - range.start, &mut count, @@ -2413,14 +2927,13 @@ pub trait BinaryViewExt: BinaryViewBase { /// Retrieve the attached type archives as their [`TypeArchiveId`]. /// - /// Using the returned id you can retrieve the [`TypeArchive`] with [`BinaryViewExt::type_archive_by_id`]. - fn attached_type_archives(&self) -> Vec { + /// Using the returned id you can retrieve the [`TypeArchive`] with [`BinaryView::type_archive_by_id`]. + pub fn attached_type_archives(&self) -> Vec { let mut ids: *mut *mut c_char = std::ptr::null_mut(); let mut paths: *mut *mut c_char = std::ptr::null_mut(); - let count = - unsafe { BNBinaryViewGetTypeArchives(self.as_ref().handle, &mut ids, &mut paths) }; - // We discard the path here, you can retrieve it later with [`BinaryViewExt::type_archive_path_by_id`], - // this is so we can simplify the return type which will commonly just want to query through to the type + let count = unsafe { BNBinaryViewGetTypeArchives(self.handle, &mut ids, &mut paths) }; + // We discard the path here, you can retrieve it later with [`BinaryView::type_archive_path_by_id`]. + // This is so we can simplify the return type which will commonly just want to query through to the type // archive itself. let _path_list = unsafe { Array::::new(paths, count, ()) }; let id_list = unsafe { Array::::new(ids, count, ()) }; @@ -2433,17 +2946,17 @@ pub trait BinaryViewExt: BinaryViewBase { /// Look up a connected [`TypeArchive`] by its `id`. /// /// NOTE: A [`TypeArchive`] can be attached but not connected, returning `None`. - fn type_archive_by_id(&self, id: &TypeArchiveId) -> Option> { + pub fn type_archive_by_id(&self, id: &TypeArchiveId) -> Option> { let id = id.0.as_str().to_cstr(); - let result = unsafe { BNBinaryViewGetTypeArchive(self.as_ref().handle, id.as_ptr()) }; + let result = unsafe { BNBinaryViewGetTypeArchive(self.handle, id.as_ptr()) }; let result_ptr = NonNull::new(result)?; Some(unsafe { TypeArchive::ref_from_raw(result_ptr) }) } /// Look up the path for an attached (but not necessarily connected) [`TypeArchive`] by its `id`. - fn type_archive_path_by_id(&self, id: &TypeArchiveId) -> Option { + pub fn type_archive_path_by_id(&self, id: &TypeArchiveId) -> Option { let id = id.0.as_str().to_cstr(); - let result = unsafe { BNBinaryViewGetTypeArchivePath(self.as_ref().handle, id.as_ptr()) }; + let result = unsafe { BNBinaryViewGetTypeArchivePath(self.handle, id.as_ptr()) }; if result.is_null() { return None; } @@ -2452,132 +2965,6 @@ pub trait BinaryViewExt: BinaryViewBase { } } -impl BinaryViewExt for T {} - -/// Represents the "whole view" of the binary and its analysis. -/// -/// Analysis information: -/// -/// - [`BinaryViewExt::functions`] -/// - [`BinaryViewExt::data_variables`] -/// - [`BinaryViewExt::strings`] -/// -/// Annotation information: -/// -/// - [`BinaryViewExt::symbols`] -/// - [`BinaryViewExt::tags_all_scopes`] -/// - [`BinaryViewExt::comments`] -/// -/// Data representation and binary information: -/// -/// - [`BinaryViewExt::types`] -/// - [`BinaryViewExt::segments`] -/// - [`BinaryViewExt::sections`] -/// -/// # Cleaning up -/// -/// [`BinaryView`] has a cyclic relationship with the associated [`FileMetadata`], each holds a strong -/// reference to one another, so to properly clean up/free the [`BinaryView`], you must manually close the -/// file using [`FileMetadata::close`], this is not fixable in the general case, until [`FileMetadata`] -/// has only a weak reference to the [`BinaryView`]. -#[derive(PartialEq, Eq, Hash)] -pub struct BinaryView { - pub handle: *mut BNBinaryView, -} - -impl BinaryView { - pub unsafe fn from_raw(handle: *mut BNBinaryView) -> Self { - debug_assert!(!handle.is_null()); - Self { handle } - } - - pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref { - debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) - } - - /// Construct the raw binary view from the given metadata. - /// - /// Before calling this, make sure you have a valid file path set for the [`FileMetadata`]. It is - /// required that the [`FileMetadata::file_path`] exist in the local filesystem. - pub fn from_metadata(meta: &FileMetadata) -> Result> { - if !meta.file_path().exists() { - return Err(()); - } - let file = meta.file_path().to_cstr(); - let handle = - unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) }; - if handle.is_null() { - return Err(()); - } - unsafe { Ok(Ref::new(Self { handle })) } - } - - /// Construct the raw binary view from the given `file_path` and metadata. - /// - /// This will implicitly set the metadata file path and then construct the view. If the metadata - /// already has the desired file path, use [`BinaryView::from_metadata`] instead. - pub fn from_path(meta: &FileMetadata, file_path: impl AsRef) -> Result> { - meta.set_file_path(file_path.as_ref()); - Self::from_metadata(meta) - } - - // TODO: Provide an API that manages the lifetime of the accessor and the view. - /// Construct the raw binary view from the given `accessor` and metadata. - /// - /// It is the responsibility of the caller to keep the accessor alive for the lifetime of the view; - /// because of this, we mark the function as unsafe. - pub unsafe fn from_accessor( - meta: &FileMetadata, - accessor: &mut FileAccessor, - ) -> Result> { - let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut accessor.raw) }; - if handle.is_null() { - return Err(()); - } - unsafe { Ok(Ref::new(Self { handle })) } - } - - /// Construct the raw binary view from the given `data` and metadata. - /// - /// The data will be copied into the view, so the caller does not need to keep the data alive. - pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Ref { - let handle = unsafe { - BNCreateBinaryDataViewFromData(meta.handle, data.as_ptr() as *mut _, data.len()) - }; - assert!( - !handle.is_null(), - "BNCreateBinaryDataViewFromData should always succeed" - ); - unsafe { Ref::new(Self { handle }) } - } - - /// Save the original binary file to the provided `file_path` along with any modifications. - /// - /// WARNING: Currently there is a possibility to deadlock if the analysis has queued up a main thread action - /// that tries to take the [`FileMetadata`] lock of the current view, and is executed while we - /// are executing in this function. - /// - /// To avoid the above issue use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there - /// are no queued up main thread actions. - pub fn save_to_path(&self, file_path: impl AsRef) -> bool { - let file = file_path.as_ref().to_cstr(); - unsafe { BNSaveToFilename(self.handle, file.as_ptr() as *mut _) } - } - - /// Save the original binary file to the provided [`FileAccessor`] along with any modifications. - /// - /// WARNING: Currently there is a possibility to deadlock if the analysis has queued up a main thread action - /// that tries to take the [`FileMetadata`] lock of the current view, and is executed while we - /// are executing in this function. - /// - /// To avoid the above issue use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there - /// are no queued up main thread actions. - pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool { - unsafe { BNSaveToFile(self.handle, &mut file.raw) } - } -} - impl BinaryViewBase for BinaryView { fn read(&self, buf: &mut [u8], offset: u64) -> usize { unsafe { BNReadViewData(self.handle, buf.as_mut_ptr() as *mut _, offset, buf.len()) } @@ -2749,7 +3136,7 @@ where Handler: BinaryViewEventHandler, { unsafe extern "C" fn on_event( - ctx: *mut ::std::os::raw::c_void, + ctx: *mut c_void, view: *mut BNBinaryView, ) { ffi_wrap!("EventHandler::on_event", { @@ -2762,11 +3149,7 @@ where let raw = Box::into_raw(boxed); unsafe { - BNRegisterBinaryViewEvent( - event_type, - Some(on_event::), - raw as *mut ::std::os::raw::c_void, - ); + BNRegisterBinaryViewEvent(event_type, Some(on_event::), raw as *mut c_void); } } @@ -2879,3 +3262,318 @@ unsafe impl CoreArrayProviderInner for AddressRange { Self::from(*raw) } } + +extern "C" fn cb_valid(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool +where + T: CustomBinaryViewType, +{ + let view_type = unsafe { &*(ctxt as *mut T) }; + let data = unsafe { BinaryView::ref_from_raw(BNNewViewReference(data)) }; + let _span = ffi_span!("CustomBinaryViewType::is_valid_for", data); + view_type.is_valid_for(&data) +} + +extern "C" fn cb_deprecated(_ctxt: *mut c_void) -> bool +where + T: CustomBinaryViewType, +{ + T::DEPRECATED +} + +extern "C" fn cb_force_loadable(_ctxt: *mut c_void) -> bool +where + T: CustomBinaryViewType, +{ + T::FORCE_LOADABLE +} + +extern "C" fn cb_has_no_initial_content(_ctxt: *mut c_void) -> bool +where + T: CustomBinaryViewType, +{ + T::HAS_NO_INITIAL_CONTENT +} + +extern "C" fn cb_create(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView +where + T: CustomBinaryViewType, +{ + ffi_wrap!("CustomBinaryViewType::create", unsafe { + let view_type = &*(ctxt as *mut T); + let data = BinaryView::from_raw(data); + let _span = ffi_span!("CustomBinaryViewType::create", data); + match view_type.create_binary_view(&data) { + Ok(custom_view) => { + match BinaryView::from_custom(T::NAME, &data.file(), &data, custom_view) { + Ok(custom_view) => Ref::into_raw(custom_view).handle, + Err(_) => std::ptr::null_mut(), + } + } + Err(_) => std::ptr::null_mut(), + } + }) +} + +extern "C" fn cb_parse(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView +where + T: CustomBinaryViewType, +{ + ffi_wrap!("CustomBinaryViewType::parse", unsafe { + let view_type = &*(ctxt as *mut T); + let data = BinaryView::from_raw(data); + let _span = ffi_span!("CustomBinaryViewType::parse", data); + match view_type.create_binary_view_for_parse(&data) { + Ok(custom_view) => { + match BinaryView::from_custom(T::NAME, &data.file(), &data, custom_view) { + Ok(custom_view) => Ref::into_raw(custom_view).handle, + Err(_) => std::ptr::null_mut(), + } + } + Err(_) => std::ptr::null_mut(), + } + }) +} + +extern "C" fn cb_load_settings(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings +where + T: CustomBinaryViewType, +{ + ffi_wrap!("CustomBinaryViewType::load_settings", unsafe { + let view_type = &*(ctxt as *mut T); + let data = BinaryView::from_raw(data); + + let _span = ffi_span!("CustomBinaryViewType::load_settings", data); + let settings = view_type.load_settings_for_data(&data); + Ref::into_raw(settings).handle + }) +} + +extern "C" fn cb_init(ctxt: *mut c_void) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::init", unsafe { + let context = &mut *(ctxt as *mut CustomBinaryViewContext); + // SAFETY: The core view has been initialized by [`BinaryView::from_custom`], so it should be valid. + // SAFETY: The custom view is not being touched by anything else at the point this function is called, + // so it should be safe to mutably borrow it. + context.view.initialize(context.core_view.assume_init_ref()) + }) +} + +extern "C" fn cb_on_after_snapshot_data_applied(ctxt: *mut c_void) +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::onAfterSnapshotDataApplied", unsafe { + let context = &mut *(ctxt as *mut CustomBinaryViewContext); + // SAFETY: The custom view is not being touched by anything else at the point this function is called, + // so it should be safe to mutably borrow it. + context.view.on_after_snapshot_data_applied(); + }) +} + +extern "C" fn cb_free_object(ctxt: *mut c_void) +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::freeObject", unsafe { + let context = ctxt as *mut CustomBinaryViewContext; + let _context = Box::from_raw(context); + }) +} + +extern "C" fn cb_read(ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize) -> usize +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::read", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + let dest = std::slice::from_raw_parts_mut(dest as *mut u8, len); + context.view.read(dest, offset) + }) +} + +extern "C" fn cb_write(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::write", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + let src = std::slice::from_raw_parts(src as *const u8, len); + context.view.write(offset, src) + }) +} + +extern "C" fn cb_insert(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::insert", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + let src = std::slice::from_raw_parts(src as *const u8, len); + context.view.insert(offset, src) + }) +} + +extern "C" fn cb_remove(ctxt: *mut c_void, offset: u64, len: u64) -> usize +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::remove", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.remove(offset, len as usize) + }) +} + +extern "C" fn cb_modification(ctxt: *mut c_void, offset: u64) -> ModificationStatus +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::modification_status", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.modification_status(offset) + }) +} + +extern "C" fn cb_offset_valid(ctxt: *mut c_void, offset: u64) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::offset_valid", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.offset_valid(offset) + }) +} + +extern "C" fn cb_offset_readable(ctxt: *mut c_void, offset: u64) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::readable", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.offset_readable(offset) + }) +} + +extern "C" fn cb_offset_writable(ctxt: *mut c_void, offset: u64) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::writable", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.offset_writable(offset) + }) +} + +extern "C" fn cb_offset_executable(ctxt: *mut c_void, offset: u64) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::offset_executable", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.offset_executable(offset) + }) +} + +extern "C" fn cb_offset_backed_by_file(ctxt: *mut c_void, offset: u64) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.offset_backed_by_file(offset) + }) +} + +extern "C" fn cb_next_valid_offset(ctxt: *mut c_void, offset: u64) -> u64 +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.next_valid_offset_after(offset) + }) +} + +extern "C" fn cb_start(ctxt: *mut c_void) -> u64 +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::start", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.start() + }) +} + +extern "C" fn cb_length(ctxt: *mut c_void) -> u64 +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::len", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.len() + }) +} + +extern "C" fn cb_entry_point(ctxt: *mut c_void) -> u64 +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::entry_point", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.entry_point() + }) +} + +extern "C" fn cb_executable(ctxt: *mut c_void) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::executable", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.executable() + }) +} + +extern "C" fn cb_endianness(ctxt: *mut c_void) -> Endianness +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::default_endianness", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.default_endianness() + }) +} + +extern "C" fn cb_relocatable(ctxt: *mut c_void) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::relocatable", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.relocatable() + }) +} + +extern "C" fn cb_address_size(ctxt: *mut c_void) -> usize +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::address_size", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + context.view.address_size() + }) +} + +extern "C" fn cb_save(ctxt: *mut c_void, _file: *mut BNFileAccessor) -> bool +where + C: CustomBinaryView, +{ + ffi_wrap!("BinaryViewBase::save", unsafe { + let context = &*(ctxt as *mut CustomBinaryViewContext); + // TODO: Need to pass file accessor to save to. + // let file = FileAccessor::from_raw(file); + context.view.save() + }) +} diff --git a/rust/src/binary_view/writer.rs b/rust/src/binary_view/writer.rs index 512418e425..57f431fe4a 100644 --- a/rust/src/binary_view/writer.rs +++ b/rust/src/binary_view/writer.rs @@ -17,7 +17,7 @@ use binaryninjacore_sys::*; use std::fmt::Debug; -use crate::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; +use crate::binary_view::{BinaryView, BinaryViewBase}; use crate::Endianness; use crate::rc::Ref; diff --git a/rust/src/collaboration/file.rs b/rust/src/collaboration/file.rs index dbe2b8b9c3..84860dd828 100644 --- a/rust/src/collaboration/file.rs +++ b/rust/src/collaboration/file.rs @@ -11,7 +11,7 @@ use super::{ Remote, RemoteFolder, RemoteProject, RemoteSnapshot, }; -use crate::binary_view::{BinaryView, BinaryViewExt}; +use crate::binary_view::BinaryView; use crate::database::Database; use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback, SplitProgressBuilder}; diff --git a/rust/src/collaboration/project.rs b/rust/src/collaboration/project.rs index cfcc1db48d..50d651f635 100644 --- a/rust/src/collaboration/project.rs +++ b/rust/src/collaboration/project.rs @@ -11,7 +11,7 @@ use super::{ RemoteFileType, RemoteFolder, }; -use crate::binary_view::{BinaryView, BinaryViewExt}; +use crate::binary_view::BinaryView; use crate::database::Database; use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; diff --git a/rust/src/collaboration/snapshot.rs b/rust/src/collaboration/snapshot.rs index 203d8677fb..3e99805d09 100644 --- a/rust/src/collaboration/snapshot.rs +++ b/rust/src/collaboration/snapshot.rs @@ -3,7 +3,7 @@ use std::ptr::NonNull; use std::time::SystemTime; use super::{sync, Remote, RemoteFile, RemoteProject}; -use crate::binary_view::{BinaryView, BinaryViewExt}; +use crate::binary_view::BinaryView; use crate::collaboration::undo::{RemoteUndoEntry, RemoteUndoEntryId}; use crate::database::snapshot::Snapshot; use crate::progress::{NoProgressCallback, ProgressCallback}; diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs index 72c57a46c6..1bf9c010bb 100644 --- a/rust/src/collaboration/sync.rs +++ b/rust/src/collaboration/sync.rs @@ -6,7 +6,7 @@ use std::ffi::{c_char, c_void}; use std::path::{Path, PathBuf}; use std::ptr::NonNull; -use crate::binary_view::{BinaryView, BinaryViewExt}; +use crate::binary_view::BinaryView; use crate::database::{snapshot::Snapshot, Database}; use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; @@ -138,7 +138,7 @@ pub fn get_remote_for_local_database(database: &Database) -> Result Result>, ()> { let Some(db) = bv.file().database() else { return Ok(None); diff --git a/rust/src/component.rs b/rust/src/component.rs index f038d5c39c..c99081fad2 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -1,4 +1,4 @@ -use crate::binary_view::{BinaryView, BinaryViewExt}; +use crate::binary_view::BinaryView; use crate::function::Function; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{BnString, IntoCStr}; diff --git a/rust/src/custom_binary_view.rs b/rust/src/custom_binary_view.rs deleted file mode 100644 index 84f3917142..0000000000 --- a/rust/src/custom_binary_view.rs +++ /dev/null @@ -1,961 +0,0 @@ -// Copyright 2021-2026 Vector 35 Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! An interface for providing your own [BinaryView]s to Binary Ninja. - -use binaryninjacore_sys::*; - -pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus; - -use std::fmt::Debug; -use std::marker::PhantomData; -use std::mem::MaybeUninit; -use std::os::raw::c_void; -use std::ptr; -use std::slice; - -use crate::architecture::Architecture; -use crate::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt, Result}; -use crate::metadata::Metadata; -use crate::platform::Platform; -use crate::rc::*; -use crate::settings::Settings; -use crate::string::*; -use crate::Endianness; - -/// Registers a custom `BinaryViewType` with the core. -/// -/// The `constructor` argument is called immediately after successful registration of the type with -/// the core. The `BinaryViewType` argument passed to `constructor` is the object that the -/// `AsRef` -/// implementation of the `CustomBinaryViewType` must return. -pub fn register_view_type(name: &str, long_name: &str, constructor: F) -> &'static T -where - T: CustomBinaryViewType, - F: FnOnce(BinaryViewType) -> T, -{ - extern "C" fn cb_valid(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool - where - T: CustomBinaryViewType, - { - let view_type = unsafe { &*(ctxt as *mut T) }; - let data = unsafe { BinaryView::ref_from_raw(BNNewViewReference(data)) }; - let _span = ffi_span!("BinaryViewTypeBase::is_valid_for", data); - view_type.is_valid_for(&data) - } - - extern "C" fn cb_deprecated(ctxt: *mut c_void) -> bool - where - T: CustomBinaryViewType, - { - ffi_wrap!("BinaryViewTypeBase::is_deprecated", unsafe { - let view_type = &*(ctxt as *mut T); - view_type.is_deprecated() - }) - } - - extern "C" fn cb_force_loadable(ctxt: *mut c_void) -> bool - where - T: CustomBinaryViewType, - { - ffi_wrap!("BinaryViewTypeBase::is_force_loadable", unsafe { - let view_type = &*(ctxt as *mut T); - view_type.is_force_loadable() - }) - } - - extern "C" fn cb_has_no_initial_content(ctxt: *mut c_void) -> bool - where - T: CustomBinaryViewType, - { - ffi_wrap!("BinaryViewTypeBase::has_no_initial_content", unsafe { - let view_type = &*(ctxt as *mut T); - view_type.has_no_initial_content() - }) - } - - extern "C" fn cb_create(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView - where - T: CustomBinaryViewType, - { - ffi_wrap!("BinaryViewTypeBase::create", unsafe { - let view_type = &*(ctxt as *mut T); - let data = BinaryView::ref_from_raw(BNNewViewReference(data)); - - let builder = CustomViewBuilder { - view_type, - actual_parent: &data, - }; - - let _span = ffi_span!("BinaryViewTypeBase::create", data); - match view_type.create_custom_view(&data, builder) { - Ok(bv) => { - // force a leak of the Ref; failure to do this would result - // in the refcount going to 0 in the process of returning it - // to the core -- we're transferring ownership of the Ref here - Ref::into_raw(bv.handle).handle - } - Err(_) => ptr::null_mut(), - } - }) - } - - extern "C" fn cb_parse(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView - where - T: CustomBinaryViewType, - { - ffi_wrap!("BinaryViewTypeBase::parse", unsafe { - let view_type = &*(ctxt as *mut T); - let data = BinaryView::ref_from_raw(BNNewViewReference(data)); - - let builder = CustomViewBuilder { - view_type, - actual_parent: &data, - }; - - let _span = ffi_span!("BinaryViewTypeBase::parse", data); - match view_type.parse_custom_view(&data, builder) { - Ok(bv) => { - // force a leak of the Ref; failure to do this would result - // in the refcount going to 0 in the process of returning it - // to the core -- we're transferring ownership of the Ref here - Ref::into_raw(bv.handle).handle - } - Err(_) => ptr::null_mut(), - } - }) - } - - extern "C" fn cb_load_settings(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings - where - T: CustomBinaryViewType, - { - ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe { - let view_type = &*(ctxt as *mut T); - let data = BinaryView::ref_from_raw(BNNewViewReference(data)); - - let _span = ffi_span!("BinaryViewTypeBase::load_settings", data); - match view_type.load_settings_for_data(&data) { - Some(settings) => Ref::into_raw(settings).handle, - None => ptr::null_mut() as *mut _, - } - }) - } - - let name = name.to_cstr(); - let name_ptr = name.as_ptr(); - - let long_name = long_name.to_cstr(); - let long_name_ptr = long_name.as_ptr(); - - let ctxt = Box::leak(Box::new(MaybeUninit::zeroed())); - - let mut bn_obj = BNCustomBinaryViewType { - context: ctxt.as_mut_ptr() as *mut _, - create: Some(cb_create::), - parse: Some(cb_parse::), - isValidForData: Some(cb_valid::), - isDeprecated: Some(cb_deprecated::), - isForceLoadable: Some(cb_force_loadable::), - getLoadSettingsForData: Some(cb_load_settings::), - hasNoInitialContent: Some(cb_has_no_initial_content::), - }; - - unsafe { - let handle = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _); - if handle.is_null() { - // avoid leaking the space allocated for the type, but also - // avoid running its Drop impl (if any -- not that there should - // be one since view types live for the life of the process) as - // MaybeUninit suppress the Drop implementation of it's inner type - drop(Box::from_raw(ctxt)); - panic!("bvt registration failed"); - } - - ctxt.write(constructor(BinaryViewType { handle })); - ctxt.assume_init_mut() - } -} - -pub trait BinaryViewTypeBase: AsRef { - /// Is this [`BinaryViewType`] valid for the given the raw [`BinaryView`]? - /// - /// Typical implementations will read the magic bytes (e.g. 'MZ'), this is a performance-sensitive - /// path so prefer inexpensive checks rather than comprehensive ones. - fn is_valid_for(&self, data: &BinaryView) -> bool; - - /// Is this [`BinaryViewType`] deprecated and should not be used? - /// - /// We specify this such that the view type may still be used by existing databases, but not - /// newly created views. - fn is_deprecated(&self) -> bool { - false - } - - /// Is this [`BinaryViewType`] able to be loaded forcefully? - /// - /// If so, it will be shown in the drop-down when a user opens a file with options. - fn is_force_loadable(&self) -> bool { - false - } - - /// Do instances of this [`BinaryViewType`] start with no loaded content? - /// - /// When true, the view has no meaningful default state: the user must make a - /// selection (e.g. load images from a shared cache) before any content exists. - /// Callers can use this to suppress restoring previously-saved view state for - /// files not being loaded from a database, since a saved layout would reference - /// content that isn't available on reopen. - fn has_no_initial_content(&self) -> bool { - false - } - - fn default_load_settings_for_data(&self, data: &BinaryView) -> Option> { - let settings_handle = - unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().handle, data.handle) }; - - if settings_handle.is_null() { - None - } else { - unsafe { Some(Settings::ref_from_raw(settings_handle)) } - } - } - - fn load_settings_for_data(&self, _data: &BinaryView) -> Option> { - None - } -} - -pub trait BinaryViewTypeExt: BinaryViewTypeBase { - fn name(&self) -> String { - unsafe { BnString::into_string(BNGetBinaryViewTypeName(self.as_ref().handle)) } - } - - fn long_name(&self) -> String { - unsafe { BnString::into_string(BNGetBinaryViewTypeLongName(self.as_ref().handle)) } - } - - fn register_arch(&self, id: u32, endianness: Endianness, arch: &A) { - unsafe { - BNRegisterArchitectureForViewType( - self.as_ref().handle, - id, - endianness, - arch.as_ref().handle, - ); - } - } - - fn register_platform(&self, id: u32, plat: &Platform) { - let arch = plat.arch(); - - unsafe { - BNRegisterPlatformForViewType(self.as_ref().handle, id, arch.handle, plat.handle); - } - } - - /// Expanded identification of [`Platform`] for [`BinaryViewType`]'s. Supersedes [`BinaryViewTypeExt::register_arch`] - /// and [`BinaryViewTypeExt::register_platform`], as these have certain edge cases (overloaded elf families, for example) - /// that can't be represented. - /// - /// The callback returns a [`Platform`] object or `None` (failure), and most recently added callbacks are called first - /// to allow plugins to override any default behaviors. When a callback returns a platform, architecture will be - /// derived from the identified platform. - /// - /// The [`BinaryView`] is the *parent* view (usually 'Raw') that the [`BinaryView`] is being created for. This - /// means that generally speaking the callbacks need to be aware of the underlying file format, however the - /// [`BinaryView`] implementation may have created datavars in the 'Raw' view by the time the callback is invoked. - /// Behavior regarding when this callback is invoked and what has been made available in the [`BinaryView`] passed as an - /// argument to the callback is up to the discretion of the [`BinaryView`] implementation. - /// - /// The `id` ind `endian` arguments are used as a filter to determine which registered [`Platform`] recognizer callbacks - /// are invoked. - /// - /// Support for this API tentatively requires explicit support in the [`BinaryView`] implementation. - fn register_platform_recognizer(&self, id: u32, endian: Endianness, recognizer: R) - where - R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, - { - #[repr(C)] - struct PlatformRecognizerHandlerContext - where - R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, - { - recognizer: R, - } - - extern "C" fn cb_recognize_low_level_il( - ctxt: *mut c_void, - bv: *mut BNBinaryView, - metadata: *mut BNMetadata, - ) -> *mut BNPlatform - where - R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, - { - let context = unsafe { &*(ctxt as *mut PlatformRecognizerHandlerContext) }; - let bv = unsafe { BinaryView::from_raw(bv).to_owned() }; - let metadata = unsafe { Metadata::from_raw(metadata).to_owned() }; - match (context.recognizer)(&bv, &metadata) { - Some(plat) => unsafe { Ref::into_raw(plat).handle }, - None => std::ptr::null_mut(), - } - } - - let recognizer = PlatformRecognizerHandlerContext { recognizer }; - // TODO: Currently we leak `recognizer`. - let raw = Box::into_raw(Box::new(recognizer)); - - unsafe { - BNRegisterPlatformRecognizerForViewType( - self.as_ref().handle, - id as u64, - endian, - Some(cb_recognize_low_level_il::), - raw as *mut c_void, - ) - } - } - - fn open(&self, data: &BinaryView) -> Result> { - let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().handle, data.handle) }; - - if handle.is_null() { - // TODO: Proper Result, possibly introduce BNSetError to populate. - return Err(()); - } - - unsafe { Ok(BinaryView::ref_from_raw(handle)) } - } - - fn parse(&self, data: &BinaryView) -> Result> { - let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().handle, data.handle) }; - - if handle.is_null() { - // TODO: Proper Result, possibly introduce BNSetError to populate. - return Err(()); - } - - unsafe { Ok(BinaryView::ref_from_raw(handle)) } - } -} - -impl BinaryViewTypeExt for T {} - -/// A [`BinaryViewType`] acts as a factory for [`BinaryView`] objects. -/// -/// Each file format will have its own type, such as PE, ELF, or Mach-O. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct BinaryViewType { - pub handle: *mut BNBinaryViewType, -} - -impl BinaryViewType { - pub(crate) unsafe fn from_raw(handle: *mut BNBinaryViewType) -> Self { - debug_assert!(!handle.is_null()); - Self { handle } - } - - pub fn list_all() -> Array { - unsafe { - let mut count: usize = 0; - let types = BNGetBinaryViewTypes(&mut count as *mut _); - Array::new(types, count, ()) - } - } - - /// Enumerates all view types and checks to see if the given raw [`BinaryView`] is valid, - /// returning only those that are. - pub fn valid_types_for_data(data: &BinaryView) -> Array { - unsafe { - let mut count: usize = 0; - let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _); - Array::new(types, count, ()) - } - } - - /// Looks up a BinaryViewType by its short name - pub fn by_name(name: &str) -> Result { - let bytes = name.to_cstr(); - let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; - match handle.is_null() { - false => Ok(unsafe { BinaryViewType::from_raw(handle) }), - true => Err(()), - } - } -} - -impl BinaryViewTypeBase for BinaryViewType { - fn is_valid_for(&self, data: &BinaryView) -> bool { - unsafe { BNIsBinaryViewTypeValidForData(self.handle, data.handle) } - } - - fn is_deprecated(&self) -> bool { - unsafe { BNIsBinaryViewTypeDeprecated(self.handle) } - } - - fn is_force_loadable(&self) -> bool { - unsafe { BNIsBinaryViewTypeForceLoadable(self.handle) } - } - - fn has_no_initial_content(&self) -> bool { - unsafe { BNBinaryViewTypeHasNoInitialContent(self.handle) } - } - - fn load_settings_for_data(&self, data: &BinaryView) -> Option> { - let settings_handle = - unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) }; - - if settings_handle.is_null() { - None - } else { - unsafe { Some(Settings::ref_from_raw(settings_handle)) } - } - } -} - -impl Debug for BinaryViewType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BinaryViewType") - .field("name", &self.name()) - .field("long_name", &self.long_name()) - .finish() - } -} - -impl CoreArrayProvider for BinaryViewType { - type Raw = *mut BNBinaryViewType; - type Context = (); - type Wrapped<'a> = Guard<'a, BinaryViewType>; -} - -unsafe impl CoreArrayProviderInner for BinaryViewType { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeBinaryViewTypeList(raw); - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(BinaryViewType::from_raw(*raw), &()) - } -} - -impl AsRef for BinaryViewType { - fn as_ref(&self) -> &Self { - self - } -} - -unsafe impl Send for BinaryViewType {} -unsafe impl Sync for BinaryViewType {} - -pub trait CustomBinaryViewType: 'static + BinaryViewTypeBase + Sync { - fn create_custom_view<'builder>( - &self, - data: &BinaryView, - builder: CustomViewBuilder<'builder, Self>, - ) -> Result>; - - fn parse_custom_view<'builder>( - &self, - data: &BinaryView, - builder: CustomViewBuilder<'builder, Self>, - ) -> Result> { - // TODO: Check to make sure data.type_name is not Self::type_name ? - self.create_custom_view(data, builder) - } -} - -/// Represents a request from the core to instantiate a custom BinaryView -pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> { - view_type: &'a T, - actual_parent: &'a BinaryView, -} - -pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized { - type Args: Send; - - fn new(handle: &BinaryView, args: &Self::Args) -> Result; - fn init(&mut self, args: Self::Args) -> Result<()>; - fn on_after_snapshot_data_applied(&mut self) {} -} - -/// Represents a partially initialized custom `BinaryView` that should be returned to the core -/// from the `create_custom_view` method of a `CustomBinaryViewType`. -#[must_use] -pub struct CustomView<'builder> { - // this object can't actually be treated like a real - // BinaryView as it isn't fully initialized until the - // core receives it from the BNCustomBinaryViewType::create - // callback. - handle: Ref, - _builder: PhantomData<&'builder ()>, -} - -impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { - /// Begins creating a custom BinaryView. - /// - /// This function may only be called from the `create_custom_view` function of a - /// `CustomBinaryViewType`. - /// - /// `parent` specifies the view that the core will treat as the parent view, that - /// Segments created against the created view will be backed by `parent`. It will - /// usually be (but is not required to be) the `data` argument of the `create_custom_view` - /// callback. - /// - /// `constructor` will not be called until well after the value returned by this function - /// has been returned by `create_custom_view` callback to the core, and may not ever - /// be called if the value returned by this function is dropped or leaked. - /// - /// # Errors - /// - /// This function will fail if the `FileMetadata` object associated with the *expected* parent - /// (i.e., the `data` argument passed to the `create_custom_view` function) already has an - /// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects - /// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and - /// can cause strange, delayed segmentation faults. - /// - /// # Safety - /// - /// `constructor` should avoid doing anything with the object it returns, especially anything - /// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't - /// going to consider the object fully initialized until after that callback has run. - /// - /// The `BinaryView` argument passed to the constructor function is the object that is expected - /// to be returned by the `AsRef` implementation required by the `BinaryViewBase` trait. - /// TODO FIXME whelp this is broke going to need 2 init callbacks - pub fn create(self, parent: &BinaryView, view_args: V::Args) -> Result> - where - V: CustomBinaryView, - { - let file = self.actual_parent.file(); - let view_type = self.view_type; - - let view_name = view_type.name(); - - if let Some(bv) = file.view_of_type(&view_name) { - // while it seems to work most of the time, you can get really unlucky - // if a free of the existing view of the same type kicks off while - // BNCreateBinaryViewOfType is still running. the freeObject callback - // will run for the new view before we've even finished initializing, - // and that's all she wrote. - // - // even if we deal with it gracefully in cb_free_object, - // BNCreateBinaryViewOfType is still going to crash, so we're just - // going to try and stop this from happening in the first place. - tracing::error!( - "attempt to create duplicate view of type '{}' (existing: {:?})", - view_name, - bv.handle - ); - - return Err(()); - } - - // struct representing the context of a BNCustomBinaryView. Can be safely - // dropped at any moment. - struct CustomViewContext - where - V: CustomBinaryView, - { - raw_handle: *mut BNBinaryView, - state: CustomViewContextState, - } - - enum CustomViewContextState - where - V: CustomBinaryView, - { - Uninitialized { args: V::Args }, - Initialized { view: V }, - // dummy state, used as a helper to change states, only happen if the - // `new` or `init` function fails. - None, - } - - impl CustomViewContext { - fn assume_init_ref(&self) -> &V { - let CustomViewContextState::Initialized { view } = &self.state else { - panic!("CustomViewContextState in invalid state"); - }; - view - } - } - - extern "C" fn cb_init(ctxt: *mut c_void) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::init", unsafe { - let context = &mut *(ctxt as *mut CustomViewContext); - let handle = BinaryView::ref_from_raw(context.raw_handle); - - // take the uninitialized state and use the args to call init - let mut state = CustomViewContextState::None; - core::mem::swap(&mut context.state, &mut state); - let CustomViewContextState::Uninitialized { args } = state else { - panic!("CustomViewContextState in invalid state"); - }; - match V::new(handle.as_ref(), &args) { - Ok(mut view) => match view.init(args) { - Ok(_) => { - // put the initialized state - context.state = CustomViewContextState::Initialized { view }; - true - } - Err(_) => { - tracing::error!( - "CustomBinaryView::init failed; custom view returned Err" - ); - false - } - }, - Err(_) => { - tracing::error!("CustomBinaryView::new failed; custom view returned Err"); - false - } - } - }) - } - - extern "C" fn cb_on_after_snapshot_data_applied(ctxt: *mut c_void) - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::onAfterSnapshotDataApplied", unsafe { - let context = &mut *(ctxt as *mut CustomViewContext); - if let CustomViewContextState::Initialized { view } = &mut context.state { - view.on_after_snapshot_data_applied(); - } - }) - } - - extern "C" fn cb_free_object(ctxt: *mut c_void) - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::freeObject", unsafe { - let context = ctxt as *mut CustomViewContext; - let context = Box::from_raw(context); - - if context.raw_handle.is_null() { - // being called here is essentially a guarantee that BNCreateBinaryViewOfType - // is above above us on the call stack somewhere -- no matter what we do, a crash - // is pretty much certain at this point. - // - // this has been observed when two views of the same BinaryViewType are created - // against the same BNFileMetaData object, and one of the views gets freed while - // the second one is being initialized -- somehow the partially initialized one - // gets freed before BNCreateBinaryViewOfType returns. - // - // multiples views of the same BinaryViewType in a BNFileMetaData object are - // prohibited, so an API contract was violated in order to get here. - // - // if we're here, it's too late to do anything about it, though we can at least not - // run the destructor on the custom view since that memory is uninitialized. - tracing::error!( - "BinaryViewBase::freeObject called on partially initialized object! crash imminent!" - ); - } else if matches!( - &context.state, - CustomViewContextState::None | CustomViewContextState::Uninitialized { .. } - ) { - // making it here means somebody went out of their way to leak a BinaryView - // after calling BNCreateCustomView and never gave the BNBinaryView handle - // to the core (which would have called cb_init) - // - // the result is a half-initialized BinaryView that the core will happily hand out - // references to via BNGetFileViewofType even though it was never initialized - // all the way. - // - // TODO update when this corner case gets fixed in the core? - // - // we can't do anything to prevent this, but we can at least have the crash - // not be our fault. - tracing::error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!"); - } - }) - } - - extern "C" fn cb_read( - ctxt: *mut c_void, - dest: *mut c_void, - offset: u64, - len: usize, - ) -> usize - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::read", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - let dest = slice::from_raw_parts_mut(dest as *mut u8, len); - context.assume_init_ref().read(dest, offset) - }) - } - - extern "C" fn cb_write( - ctxt: *mut c_void, - offset: u64, - src: *const c_void, - len: usize, - ) -> usize - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::write", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - let src = slice::from_raw_parts(src as *const u8, len); - context.assume_init_ref().write(offset, src) - }) - } - - extern "C" fn cb_insert( - ctxt: *mut c_void, - offset: u64, - src: *const c_void, - len: usize, - ) -> usize - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::insert", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - let src = slice::from_raw_parts(src as *const u8, len); - context.assume_init_ref().insert(offset, src) - }) - } - - extern "C" fn cb_remove(ctxt: *mut c_void, offset: u64, len: u64) -> usize - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::remove", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().remove(offset, len as usize) - }) - } - - extern "C" fn cb_modification(ctxt: *mut c_void, offset: u64) -> ModificationStatus - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::modification_status", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().modification_status(offset) - }) - } - - extern "C" fn cb_offset_valid(ctxt: *mut c_void, offset: u64) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::offset_valid", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().offset_valid(offset) - }) - } - - extern "C" fn cb_offset_readable(ctxt: *mut c_void, offset: u64) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::readable", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().offset_readable(offset) - }) - } - - extern "C" fn cb_offset_writable(ctxt: *mut c_void, offset: u64) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::writable", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().offset_writable(offset) - }) - } - - extern "C" fn cb_offset_executable(ctxt: *mut c_void, offset: u64) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::offset_executable", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().offset_executable(offset) - }) - } - - extern "C" fn cb_offset_backed_by_file(ctxt: *mut c_void, offset: u64) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().offset_backed_by_file(offset) - }) - } - - extern "C" fn cb_next_valid_offset(ctxt: *mut c_void, offset: u64) -> u64 - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().next_valid_offset_after(offset) - }) - } - - extern "C" fn cb_start(ctxt: *mut c_void) -> u64 - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::start", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().start() - }) - } - - extern "C" fn cb_length(ctxt: *mut c_void) -> u64 - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::len", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().len() - }) - } - - extern "C" fn cb_entry_point(ctxt: *mut c_void) -> u64 - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::entry_point", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().entry_point() - }) - } - - extern "C" fn cb_executable(ctxt: *mut c_void) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::executable", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - context.assume_init_ref().executable() - }) - } - - extern "C" fn cb_endianness(ctxt: *mut c_void) -> Endianness - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::default_endianness", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - - context.assume_init_ref().default_endianness() - }) - } - - extern "C" fn cb_relocatable(ctxt: *mut c_void) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::relocatable", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - - context.assume_init_ref().relocatable() - }) - } - - extern "C" fn cb_address_size(ctxt: *mut c_void) -> usize - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::address_size", unsafe { - let context = &*(ctxt as *mut CustomViewContext); - - context.assume_init_ref().address_size() - }) - } - - extern "C" fn cb_save(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool - where - V: CustomBinaryView, - { - ffi_wrap!("BinaryViewBase::save", unsafe { - let _context = &*(ctxt as *mut CustomViewContext); - false - }) - } - - let ctxt = Box::new(CustomViewContext:: { - raw_handle: ptr::null_mut(), - state: CustomViewContextState::Uninitialized { args: view_args }, - }); - - let ctxt = Box::into_raw(ctxt); - - let mut bn_obj = BNCustomBinaryView { - context: ctxt as *mut _, - init: Some(cb_init::), - freeObject: Some(cb_free_object::), - externalRefTaken: None, - externalRefReleased: None, - read: Some(cb_read::), - write: Some(cb_write::), - insert: Some(cb_insert::), - remove: Some(cb_remove::), - getModification: Some(cb_modification::), - isValidOffset: Some(cb_offset_valid::), - isOffsetReadable: Some(cb_offset_readable::), - isOffsetWritable: Some(cb_offset_writable::), - isOffsetExecutable: Some(cb_offset_executable::), - isOffsetBackedByFile: Some(cb_offset_backed_by_file::), - getNextValidOffset: Some(cb_next_valid_offset::), - getStart: Some(cb_start::), - getLength: Some(cb_length::), - getEntryPoint: Some(cb_entry_point::), - isExecutable: Some(cb_executable::), - getDefaultEndianness: Some(cb_endianness::), - isRelocatable: Some(cb_relocatable::), - getAddressSize: Some(cb_address_size::), - save: Some(cb_save::), - onAfterSnapshotDataApplied: Some(cb_on_after_snapshot_data_applied::), - }; - - let view_name = view_name.to_cstr(); - unsafe { - let res = BNCreateCustomBinaryView( - view_name.as_ptr(), - file.handle, - parent.handle, - &mut bn_obj, - ); - assert!( - !res.is_null(), - "BNCreateCustomBinaryView cannot return null" - ); - (*ctxt).raw_handle = res; - Ok(CustomView { - handle: BinaryView::ref_from_raw(res), - _builder: PhantomData, - }) - } - } - - pub fn wrap_existing(self, wrapped_view: Ref) -> Result> { - Ok(CustomView { - handle: wrapped_view, - _builder: PhantomData, - }) - } -} diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index f4cee4ff8f..2710537f42 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -62,7 +62,6 @@ //! `DebugInfo` will then be automatically applied to binary views that contain debug information (via the setting `analysis.debugInfo.internal`), binary views that provide valid external debug info files (`analysis.debugInfo.external`), or manually fetched/applied as below: //! ```no_run //! # use binaryninja::debuginfo::DebugInfoParser; -//! # use binaryninja::binary_view::BinaryViewExt; //! let bv = binaryninja::load("example").unwrap(); //! let valid_parsers = DebugInfoParser::parsers_for_view(&bv); //! let parser = valid_parsers.get(0); diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index d1c63216a6..45d142bf23 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -34,7 +34,6 @@ pub(crate) fn time_from_bn(timestamp: u64) -> SystemTime { #[macro_export] macro_rules! ffi_span { ($name:expr, $bv:expr) => {{ - use $crate::binary_view::BinaryViewExt; #[allow(unused_imports)] use $crate::file_metadata::FileMetadata; ::tracing::info_span!($name, session_id = $bv.file().session_id().0).entered() diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs index b58daa5587..7b8a60fdb4 100644 --- a/rust/src/file_metadata.rs +++ b/rust/src/file_metadata.rs @@ -30,7 +30,7 @@ use crate::project::file::ProjectFile; use std::ptr::NonNull; #[allow(unused_imports)] -use crate::custom_binary_view::BinaryViewType; +use crate::binary_view::BinaryViewType; new_id_type!(SessionId, usize); @@ -472,6 +472,9 @@ impl FileMetadata { /// The [`BinaryViewType`]s associated with this file. /// /// For example, opening a PE binary will have the following: "Raw", "PE". + /// + /// Because the type may not have been registered, and the actual [`BinaryViewType`] is not available, + /// we instead return the name of the view type. pub fn view_types(&self) -> Array { let mut count = 0; unsafe { diff --git a/rust/src/function.rs b/rust/src/function.rs index c19e2107a2..90432af69f 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -17,7 +17,7 @@ use binaryninjacore_sys::*; use crate::{ architecture::{Architecture, CoreArchitecture, CoreRegister, Register}, basic_block::{BasicBlock, BlockContext}, - binary_view::{BinaryView, BinaryViewExt}, + binary_view::BinaryView, calling_convention::CoreCallingConvention, component::Component, disassembly::{DisassemblySettings, DisassemblyTextLine}, @@ -1157,7 +1157,7 @@ impl Function { /// Function.add_tag, you'll create an "address tag". These are good for labeling /// specific instructions. /// - /// For tagging arbitrary data, consider [BinaryViewExt::add_tag]. + /// For tagging arbitrary data, consider [BinaryView::add_tag]. /// /// * `tag_type_name` - The name of the tag type for this Tag. /// * `data` - Additional data for the Tag. @@ -1167,7 +1167,7 @@ impl Function { /// # Example /// /// ```no_run - /// # use binaryninja::binary_view::{BinaryView, BinaryViewExt}; + /// # use binaryninja::binary_view::BinaryView; /// # use binaryninja::function::Function; /// # let fun: Function = todo!(); /// # let bv: BinaryView = todo!(); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 5f23a9f6aa..f420fa30c6 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -37,7 +37,6 @@ pub mod collaboration; pub mod command; pub mod component; pub mod confidence; -pub mod custom_binary_view; pub mod data_buffer; pub mod data_notification; pub mod data_renderer; diff --git a/rust/src/platform.rs b/rust/src/platform.rs index bf135fae71..e70e127792 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -176,7 +176,7 @@ impl Platform { pub fn type_container(&self) -> TypeContainer { let type_container_ptr = NonNull::new(unsafe { BNGetPlatformTypeContainer(self.handle) }); // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw` - // TODO: We are cloning here for platforms but we dont need to do this for [BinaryViewExt::type_container] + // TODO: We are cloning here for platforms but we dont need to do this for [BinaryView::type_container] // TODO: Why does this require that we, construct a TypeContainer, duplicate the type container, then drop the original. unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) } } diff --git a/rust/src/section.rs b/rust/src/section.rs index 7668f54820..ee7e0ec7a7 100644 --- a/rust/src/section.rs +++ b/rust/src/section.rs @@ -81,7 +81,6 @@ impl Section { /// /// ```no_run /// # use binaryninja::section::Section; - /// # use binaryninja::binary_view::BinaryViewExt; /// let bv = binaryninja::load("example").unwrap(); /// bv.add_section(Section::builder("example".to_string(), 0..1024).align(4).entry_size(4)) /// ``` diff --git a/rust/src/segment.rs b/rust/src/segment.rs index 348d8f67e3..a042d1fdb7 100644 --- a/rust/src/segment.rs +++ b/rust/src/segment.rs @@ -41,6 +41,9 @@ impl SegmentBuilder { } } + /// The range of the data in the parent binary view. + /// + /// If this is not specified, then the segment is "unbacked" and will contain no data. pub fn parent_backing(mut self, parent_backing: Range) -> Self { self.parent_backing = Some(parent_backing); self @@ -107,7 +110,6 @@ impl Segment { /// /// ```no_run /// # use binaryninja::segment::{Segment, SegmentFlags}; - /// # use binaryninja::binary_view::BinaryViewExt; /// let bv = binaryninja::load("example").unwrap(); /// let segment_flags = SegmentFlags::new().writable(true).readable(true); /// bv.add_segment(Segment::builder(0..0x1000).flags(segment_flags)) diff --git a/rust/src/types.rs b/rust/src/types.rs index 65e50040ba..f45d93bc5d 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -36,7 +36,7 @@ use binaryninjacore_sys::*; use crate::{ architecture::Architecture, - binary_view::{BinaryView, BinaryViewExt}, + binary_view::BinaryView, calling_convention::CoreCallingConvention, rc::*, string::{BnString, IntoCStr}, @@ -643,7 +643,6 @@ impl Drop for TypeBuilder { /// As an example, defining a _named_ type within a [`BinaryView`]: /// /// ```no_run -/// # use crate::binaryninja::binary_view::BinaryViewExt; /// # use binaryninja::types::Type; /// let bv = binaryninja::load("example.bin").unwrap(); /// let my_custom_type_1 = Type::named_int(5, false, "my_w"); diff --git a/rust/src/types/library.rs b/rust/src/types/library.rs index 8743b8961a..9b543362ab 100644 --- a/rust/src/types/library.rs +++ b/rust/src/types/library.rs @@ -17,8 +17,6 @@ use std::ptr::NonNull; // Used for doc comments #[allow(unused_imports)] use crate::binary_view::BinaryView; -#[allow(unused_imports)] -use crate::binary_view::BinaryViewExt; // TODO: Introduce a FinalizedTypeLibrary that cannot be mutated, so we do not have APIs that are unusable. @@ -261,7 +259,7 @@ impl TypeLibrary { /// Referenced types will not automatically be added, so make sure to add referenced types to the /// library or use [`TypeLibrary::add_type_source`] to mark the references originating source. /// - /// To add objects from a binary view, prefer using [`BinaryViewExt::export_object_to_library`] which + /// To add objects from a binary view, prefer using [`BinaryView::export_object_to_library`] which /// will automatically pull in all referenced types and record additional dependencies as needed. pub fn add_named_object(&self, name: QualifiedName, type_: &Type) { let mut raw_name = QualifiedName::into_raw(name); @@ -280,7 +278,7 @@ impl TypeLibrary { /// Referenced types will not automatically be added, so make sure to add referenced types to the /// library or use [`TypeLibrary::add_type_source`] to mark the references originating source. /// - /// To add types from a binary view, prefer using [`BinaryViewExt::export_type_to_library`] which + /// To add types from a binary view, prefer using [`BinaryView::export_type_to_library`] which /// will automatically pull in all referenced types and record additional dependencies as needed. pub fn add_named_type(&self, name: QualifiedName, type_: &Type) { let mut raw_name = QualifiedName::into_raw(name); @@ -320,7 +318,7 @@ impl TypeLibrary { /// Get the object (function) associated with the given name, if any. /// - /// Prefer [`BinaryViewExt::import_type_library_object`] as it will recursively import types required. + /// Prefer [`BinaryView::import_type_library_object`] as it will recursively import types required. pub fn get_named_object(&self, name: QualifiedName) -> Option> { let mut raw_name = QualifiedName::into_raw(name); let t = unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &mut raw_name) }; @@ -330,7 +328,7 @@ impl TypeLibrary { /// Get the type associated with the given name, if any. /// - /// Prefer [`BinaryViewExt::import_type_library_type`] as it will recursively import types required. + /// Prefer [`BinaryView::import_type_library_type`] as it will recursively import types required. pub fn get_named_type(&self, name: QualifiedName) -> Option> { let mut raw_name = QualifiedName::into_raw(name); let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &mut raw_name) }; diff --git a/rust/src/types/structure.rs b/rust/src/types/structure.rs index 3b609287c2..6dd8048a6f 100644 --- a/rust/src/types/structure.rs +++ b/rust/src/types/structure.rs @@ -7,9 +7,9 @@ use crate::types::{ use binaryninjacore_sys::*; use std::fmt::{Debug, Formatter}; -// Needed for doc comments +// Used for documentation purposes. #[allow(unused)] -use crate::binary_view::BinaryViewExt; +use crate::binary_view::BinaryView; #[derive(PartialEq, Eq, Hash)] pub struct StructureBuilder { @@ -18,7 +18,6 @@ pub struct StructureBuilder { /// ```no_run /// // Includes -/// # use binaryninja::binary_view::BinaryViewExt; /// use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureBuilder, Type}; /// /// // Types to use in the members @@ -349,7 +348,7 @@ impl Structure { /// /// We must pass a [`TypeContainer`] here so that we can resolve base structure members, as they /// are treated as members through this function. Typically, you get the [`TypeContainer`] - /// through the binary view with [`BinaryViewExt::type_container`]. + /// through the binary view with [`BinaryView::type_container`]. pub fn members_at_offset( &self, container: &TypeContainer, @@ -382,7 +381,7 @@ impl Structure { /// Returns the list of all structure members, including inherited ones. /// /// Because we must traverse through base structures, we have to provide the [`TypeContainer`]; - /// in most cases it is ok to provide the binary views container via [`BinaryViewExt::type_container`]. + /// in most cases it is ok to provide the binary views container via [`BinaryView::type_container`]. pub fn members_including_inherited( &self, container: &TypeContainer, diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index e53e000152..72d0223799 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -2,7 +2,7 @@ use binaryninjacore_sys::*; // Needed for documentation. #[allow(unused)] -use crate::binary_view::{memory_map::MemoryMap, BinaryViewBase, BinaryViewExt}; +use crate::binary_view::{memory_map::MemoryMap, BinaryViewBase}; use crate::basic_block::BasicBlock; use crate::binary_view::BinaryView; diff --git a/rust/tests/base_detection.rs b/rust/tests/base_detection.rs index e42c6071d5..5addcb295d 100644 --- a/rust/tests/base_detection.rs +++ b/rust/tests/base_detection.rs @@ -1,5 +1,4 @@ use binaryninja::base_detection::{BaseAddressDetectionConfidence, BaseAddressDetectionSettings}; -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use std::path::PathBuf; diff --git a/rust/tests/binary_reader.rs b/rust/tests/binary_reader.rs index 0adac464e0..9aca3efedb 100644 --- a/rust/tests/binary_reader.rs +++ b/rust/tests/binary_reader.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryReader, BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::{BinaryReader, BinaryViewBase}; use binaryninja::headless::Session; use std::io::{Read, Seek, SeekFrom}; use std::path::PathBuf; diff --git a/rust/tests/binary_view.rs b/rust/tests/binary_view.rs index 11caf3d0f6..ff5f0514ef 100644 --- a/rust/tests/binary_view.rs +++ b/rust/tests/binary_view.rs @@ -1,15 +1,18 @@ use binaryninja::binary_view::search::SearchQuery; use binaryninja::binary_view::{ - AnalysisProgress, AnalysisState, BinaryViewBase, BinaryViewExt, StringType, + register_binary_view_type, AnalysisProgress, BinaryView, BinaryViewBase, CustomBinaryView, + CustomBinaryViewType, StringType, }; use binaryninja::data_buffer::DataBuffer; -use binaryninja::file_metadata::SaveSettings; +use binaryninja::file_metadata::{FileMetadata, SaveSettings}; use binaryninja::function::{Function, FunctionViewType}; use binaryninja::headless::Session; use binaryninja::main_thread::execute_on_main_thread_and_wait; use binaryninja::platform::Platform; use binaryninja::rc::Ref; +use binaryninja::segment::SegmentBuilder; use binaryninja::symbol::{Symbol, SymbolBuilder, SymbolType}; +use binaryninja::Endianness; use std::collections::{BTreeMap, HashSet}; use std::path::PathBuf; @@ -203,3 +206,73 @@ fn test_deterministic_functions() { insta::assert_debug_snapshot!(snapshot_name, functions); } } + +struct MyBinaryViewType; + +impl CustomBinaryViewType for MyBinaryViewType { + type CustomBinaryView = MyBinaryView; + const NAME: &'static str = "MyBinaryView"; + + fn create_binary_view(&self, _data: &BinaryView) -> Result { + Ok(MyBinaryView) + } + + fn is_valid_for(&self, data: &BinaryView) -> bool { + let mut buffer = [0u8; 4]; + data.read(&mut buffer, 0); + buffer == [0x42, 0x42, 0x42, 0x42] + } +} + +struct MyBinaryView; + +impl BinaryViewBase for MyBinaryView { + fn default_endianness(&self) -> Endianness { + Endianness::LittleEndian + } + + fn address_size(&self) -> usize { + 4 + } +} + +impl CustomBinaryView for MyBinaryView { + fn initialize(&mut self, view: &BinaryView) -> bool { + let test_sym = SymbolBuilder::new(SymbolType::Symbolic, "hello", 0).create(); + view.define_auto_symbol(&test_sym); + view.add_segment(SegmentBuilder::new(0..4).parent_backing(0..4).is_auto(true)); + true + } +} + +#[test] +fn test_custom_view() { + let _session = Session::new().expect("Failed to initialize session"); + let invalid_view = BinaryView::from_data(&FileMetadata::new(), &[0x0, 0x0, 0x0, 0x0]); + let valid_view = BinaryView::from_data(&FileMetadata::new(), &[0x42, 0x42, 0x42, 0x42]); + assert_eq!(MyBinaryViewType.is_valid_for(&invalid_view), false); + assert_eq!(MyBinaryViewType.is_valid_for(&valid_view), true); + + let (_, core_type) = register_binary_view_type(MyBinaryViewType); + assert_eq!(core_type.is_valid_for(&invalid_view), false); + assert_eq!(core_type.is_valid_for(&valid_view), true); + assert_eq!(core_type.name(), "MyBinaryView"); + assert_eq!(core_type.is_deprecated(), false); + assert_eq!(core_type.is_force_loadable(), false); + + let created_view = core_type + .create(&valid_view) + .expect("Failed to create view"); + assert_eq!(created_view.analysis_progress(), AnalysisProgress::Initial); + + let hello_symbol = created_view + .symbol_by_address(0) + .expect("Failed to get symbol"); + assert_eq!(hello_symbol.to_string(), "hello"); + + assert_eq!( + created_view.read_vec(0, 4), + vec![0x42, 0x42, 0x42, 0x42], + "View not backed by the parent data" + ); +} diff --git a/rust/tests/binary_writer.rs b/rust/tests/binary_writer.rs index b74b28374c..e6881b9824 100644 --- a/rust/tests/binary_writer.rs +++ b/rust/tests/binary_writer.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryReader, BinaryViewBase, BinaryViewExt, BinaryWriter}; +use binaryninja::binary_view::{BinaryReader, BinaryViewBase, BinaryWriter}; use binaryninja::headless::Session; use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; diff --git a/rust/tests/collaboration.rs b/rust/tests/collaboration.rs index 914935b58b..36066cf700 100644 --- a/rust/tests/collaboration.rs +++ b/rust/tests/collaboration.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::collaboration::{NoNameChangeset, Remote, RemoteFileType, RemoteProject}; use binaryninja::file_metadata::SaveSettings; use binaryninja::headless::Session; diff --git a/rust/tests/component.rs b/rust/tests/component.rs index 3341f0d7ba..2cddb468a0 100644 --- a/rust/tests/component.rs +++ b/rust/tests/component.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::component::ComponentBuilder; use binaryninja::headless::Session; use std::path::PathBuf; diff --git a/rust/tests/data_notification.rs b/rust/tests/data_notification.rs index 669c847418..f277692a0f 100644 --- a/rust/tests/data_notification.rs +++ b/rust/tests/data_notification.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::data_notification::*; use binaryninja::function::Function; use binaryninja::headless::Session; diff --git a/rust/tests/data_renderer.rs b/rust/tests/data_renderer.rs index fdf3969789..a2ab372d7f 100644 --- a/rust/tests/data_renderer.rs +++ b/rust/tests/data_renderer.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::data_renderer::{ register_data_renderer, render_lines_for_data, CustomDataRenderer, RegistrationType, TypeContext, diff --git a/rust/tests/debug_info.rs b/rust/tests/debug_info.rs index 407b41d5a5..3627b4a069 100644 --- a/rust/tests/debug_info.rs +++ b/rust/tests/debug_info.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::debuginfo::*; use binaryninja::headless::Session; use binaryninja::types::{MemberAccess, MemberScope, StructureBuilder, Type, TypeBuilder}; diff --git a/rust/tests/function.rs b/rust/tests/function.rs index 507b405db7..c7c9af7364 100644 --- a/rust/tests/function.rs +++ b/rust/tests/function.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::file_metadata::FileMetadata; use binaryninja::headless::Session; use binaryninja::platform::Platform; diff --git a/rust/tests/high_level_il.rs b/rust/tests/high_level_il.rs index d3a4b05ab5..aeb15cc6e9 100644 --- a/rust/tests/high_level_il.rs +++ b/rust/tests/high_level_il.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::BinaryViewBase; use binaryninja::headless::Session; use binaryninja::high_level_il::{ HighLevelExpressionIndex, HighLevelILInstructionKind, HighLevelInstructionIndex, diff --git a/rust/tests/language_representation.rs b/rust/tests/language_representation.rs index f4aeb7bdfc..78af7d7f16 100644 --- a/rust/tests/language_representation.rs +++ b/rust/tests/language_representation.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use binaryninja::architecture::CoreArchitecture; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::disassembly::{ DisassemblySettings, DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind, }; diff --git a/rust/tests/low_level_il.rs b/rust/tests/low_level_il.rs index e6f8d8ff3c..efae0a62bc 100644 --- a/rust/tests/low_level_il.rs +++ b/rust/tests/low_level_il.rs @@ -1,7 +1,6 @@ use binaryninja::architecture::{ Architecture, ArchitectureExt, CoreArchitecture, Intrinsic, Register, }; -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use binaryninja::low_level_il::expression::{ ExpressionHandler, LowLevelExpressionIndex, LowLevelILExpressionKind, diff --git a/rust/tests/medium_level_il.rs b/rust/tests/medium_level_il.rs index a921965fc8..d4f7634587 100644 --- a/rust/tests/medium_level_il.rs +++ b/rust/tests/medium_level_il.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use binaryninja::medium_level_il::{ MediumLevelExpressionIndex, MediumLevelILInstructionKind, MediumLevelILLiftedInstructionKind, diff --git a/rust/tests/render_layer.rs b/rust/tests/render_layer.rs index 6f6ebc9818..6ca391df62 100644 --- a/rust/tests/render_layer.rs +++ b/rust/tests/render_layer.rs @@ -1,5 +1,4 @@ use binaryninja::basic_block::BasicBlock; -use binaryninja::binary_view::BinaryViewExt; use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings, DisassemblyTextLine}; use binaryninja::function::NativeBlock; use binaryninja::headless::Session; diff --git a/rust/tests/repository.rs b/rust/tests/repository.rs index ac861b2cde..0dc26517f9 100644 --- a/rust/tests/repository.rs +++ b/rust/tests/repository.rs @@ -4,15 +4,14 @@ use binaryninja::repository::RepositoryManager; #[test] fn test_list() { let _session = Session::new().expect("Failed to initialize session"); - let manager = RepositoryManager::default(); - let repositories = manager.repositories(); + let repositories = RepositoryManager::repositories(); for repository in &repositories { let repo_path = repository.path(); - let repository_by_path = manager.repository_by_path(&repo_path).unwrap(); + let repository_by_path = RepositoryManager::repository_by_path(&repo_path).unwrap(); assert_eq!(repository.url(), repository_by_path.url()); } - let repository = manager.default_repository(); + let repository = RepositoryManager::default_repository(); let _full_path = repository.full_path(); let _path = repository.path(); let _url = repository.url(); diff --git a/rust/tests/section.rs b/rust/tests/section.rs index ea3d8e06f1..ab1703c613 100644 --- a/rust/tests/section.rs +++ b/rust/tests/section.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use binaryninja::section::{SectionBuilder, Semantics}; use std::path::PathBuf; diff --git a/rust/tests/type_container.rs b/rust/tests/type_container.rs index a0916ef41d..ab4cbb0b00 100644 --- a/rust/tests/type_container.rs +++ b/rust/tests/type_container.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::file_metadata::FileMetadata; use binaryninja::headless::Session; use binaryninja::platform::Platform; diff --git a/rust/tests/type_library.rs b/rust/tests/type_library.rs index 19115f3557..559aae7c19 100644 --- a/rust/tests/type_library.rs +++ b/rust/tests/type_library.rs @@ -1,4 +1,3 @@ -use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use binaryninja::platform::Platform; use binaryninja::types::{Type, TypeClass, TypeLibrary}; diff --git a/rust/tests/types.rs b/rust/tests/types.rs index c13a1c884a..a661b4ed71 100644 --- a/rust/tests/types.rs +++ b/rust/tests/types.rs @@ -1,4 +1,4 @@ -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::binary_view::BinaryView; use binaryninja::confidence::Conf; use binaryninja::file_metadata::FileMetadata; use binaryninja::headless::Session; diff --git a/view/minidump/.gitignore b/view/minidump/.gitignore deleted file mode 100644 index 4fffb2f89c..0000000000 --- a/view/minidump/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -/Cargo.lock diff --git a/view/minidump/Cargo.toml b/view/minidump/Cargo.toml index adca858006..2f34407219 100644 --- a/view/minidump/Cargo.toml +++ b/view/minidump/Cargo.toml @@ -11,5 +11,6 @@ crate-type = ["cdylib"] [dependencies] binaryninja.workspace = true binaryninjacore-sys.workspace = true -minidump = "0.23.0" -tracing = "0.1" \ No newline at end of file +minidump = "0.26.1" +tracing = "0.1" +object = "0.39.1" \ No newline at end of file diff --git a/view/minidump/README.md b/view/minidump/README.md index 3c54ca8e68..799cf4b9da 100644 --- a/view/minidump/README.md +++ b/view/minidump/README.md @@ -1,21 +1,6 @@ -# Binary Ninja Minidump Loader +# Binary Ninja Minidump View -A Minidump memory dump loader plugin for Binary Ninja. - -![Screenshot of Binary Ninja using the "Minidump" Binary View, with a minidump loaded and the virtual addresses of the memory segments of the minidump showing in the Memory Map window](images/loaded-minidump-screenshot-border.png) - -This plugin adds a new _Minidump_ binary view type. When a binary with the magic number `MDMP` is opened, this plugin will automatically try to load in the binary as a minidump, and create a new _Minidump_ binary view to view the contents. - -The architecture is determined automatically from the platform information embedded in the minidump. - -![Screenshot showing the Minidump binary view type in the dropdown list of available binary views for an open binary](images/minidump-binary-view-type-screenshot-border.png) - -The loaded minidump's memory regions and modules can be navigated via the _Memory Map_ window. In the _Minidump_ binary view, the meanings of "Segments" and "Sections" in the Memory Map window are modified to mean the following: - -- The memory regions in the minidump are loaded as _Segments_. The _Data Offset_ and _Data Length_ fields of each segment are the corresponding addresses in the minidump file where the data for that memory region is located. -- The modules in the minidump are loaded as _Sections_, with the name of each section being the path to the module. - -![Screenshot showing the Memory Map window with the loaded minidump's memory segments and modules (i.e. "sections")](images/minidump-segments-sections-screenshot-border.png) +A Minidump binary view plugin for Binary Ninja. ## Supported Minidump Types @@ -44,22 +29,3 @@ minidump dumpfile.dmp - Loading Minidump files from platforms or APIs other than Windows' `MinidumpWriteDump`, such as those generated by [Google Breakpad](https://chromium.googlesource.com/breakpad/breakpad/). - Loading and applyng debug information from the minidump file. In Windows minidump files, `MinidumpModuleList` streams contain information about the PDB file which contains the debug information for the module; this isn't currently read or applied, however. -- Integration with Binary Ninja's built-in debugger. Minidump files can contain information about threads, register values, and stack frames, and it would be nice in the future for minidump files to be loadable back into the debugger in order to resume a debugging session. This isn't currently done, however. - -## Building and Installing - -This plugin currently needs to be built from source, then copied into your user plugin folder. - -``` -cargo build --release -cp target/release/libminidump_bn.so ~/.binaryninja/plugins/ -``` - -The code in this plugin targets the `dev` branch of the [Binary Ninja Rust API](https://github.com/Vector35/binaryninja-api/tree/dev/rust). - -To update the Binary Ninja Rust API dependency: - -``` -cargo update -p binaryninja -cargo build --release -``` \ No newline at end of file diff --git a/view/minidump/images/loaded-minidump-screenshot-border.png b/view/minidump/images/loaded-minidump-screenshot-border.png deleted file mode 100644 index 51b8282cde..0000000000 Binary files a/view/minidump/images/loaded-minidump-screenshot-border.png and /dev/null differ diff --git a/view/minidump/images/minidump-binary-view-type-screenshot-border.png b/view/minidump/images/minidump-binary-view-type-screenshot-border.png deleted file mode 100644 index 9a52dd6626..0000000000 Binary files a/view/minidump/images/minidump-binary-view-type-screenshot-border.png and /dev/null differ diff --git a/view/minidump/images/minidump-segments-sections-screenshot-border.png b/view/minidump/images/minidump-segments-sections-screenshot-border.png deleted file mode 100644 index 0035accac4..0000000000 Binary files a/view/minidump/images/minidump-segments-sections-screenshot-border.png and /dev/null differ diff --git a/view/minidump/src/command.rs b/view/minidump/src/command.rs deleted file mode 100644 index ca7ce4c570..0000000000 --- a/view/minidump/src/command.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::str; - -use minidump::{Minidump, MinidumpMemoryInfoList}; - -use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; - -pub fn print_memory_information(bv: &BinaryView) { - tracing::debug!("Printing memory information"); - if let Some(minidump_bv) = bv.parent_view() { - if let Some(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len() as usize) { - if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) { - if let Ok(memory_info_list) = minidump_obj.get_stream::() { - let mut memory_info_list_writer = Vec::new(); - match memory_info_list.print(&mut memory_info_list_writer) { - Ok(_) => { - if let Ok(memory_info_str) = str::from_utf8(&memory_info_list_writer) { - tracing::info!("{memory_info_str}"); - } else { - tracing::error!("Could not convert the memory information description from minidump into a valid string"); - } - } - Err(_) => { - tracing::error!("Could not get memory information from minidump"); - } - } - } else { - tracing::error!( - "Could not parse a valid MinidumpMemoryInfoList stream from the minidump" - ); - } - } else { - tracing::error!("Could not parse a valid minidump file from the parent binary view's data buffer"); - } - } else { - tracing::error!("Could not read data from parent binary view"); - } - } else { - tracing::error!("Could not get the parent binary view"); - } -} diff --git a/view/minidump/src/lib.rs b/view/minidump/src/lib.rs index 1b1f7f1967..4e057378fe 100644 --- a/view/minidump/src/lib.rs +++ b/view/minidump/src/lib.rs @@ -1,35 +1,12 @@ -use binaryninja::binary_view::BinaryView; -use binaryninja::command::{register_command, Command}; -use binaryninja::custom_binary_view::register_view_type; +use crate::view::MinidumpBinaryViewType; +use binaryninja::binary_view::register_binary_view_type; -mod command; mod view; -struct PrintMemoryInformationCommand; - -impl Command for PrintMemoryInformationCommand { - fn action(&self, binary_view: &BinaryView) { - command::print_memory_information(binary_view); - } - - fn valid(&self, _binary_view: &BinaryView) -> bool { - true // TODO: Of course, the command will not always be valid! - } -} - #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn CorePluginInit() -> bool { binaryninja::tracing_init!("Minidump"); - tracing::debug!("Registering minidump binary view type"); - register_view_type("Minidump", "Minidump", view::MinidumpBinaryViewType::new); - - tracing::debug!("Registering minidump plugin commands"); - register_command( - "Minidump\\[DEBUG] Print Minidump Memory Information", - "Print a human-readable description of the contents of the MinidumpMemoryInfoList stream in the loaded minidump", - PrintMemoryInformationCommand {}, - ); - + register_binary_view_type(MinidumpBinaryViewType); true } diff --git a/view/minidump/src/view.rs b/view/minidump/src/view.rs index 8baf30c2b7..2c72f81e0b 100644 --- a/view/minidump/src/view.rs +++ b/view/minidump/src/view.rs @@ -1,357 +1,137 @@ -use std::collections::HashMap; -use std::ops::Range; - -use binaryninja::section::Section; -use binaryninja::segment::{Segment, SegmentFlags}; -use minidump::format::MemoryProtection; -use minidump::{ - Minidump, MinidumpMemory64List, MinidumpMemoryInfoList, MinidumpMemoryList, MinidumpModuleList, - MinidumpStream, MinidumpSystemInfo, Module, -}; - -use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; -use binaryninja::custom_binary_view::{ - BinaryViewType, BinaryViewTypeBase, CustomBinaryView, CustomBinaryViewType, CustomView, - CustomViewBuilder, -}; +use binaryninja::binary_view::{BinaryView, BinaryViewBase}; +use binaryninja::binary_view::{CustomBinaryView, CustomBinaryViewType}; +use binaryninja::data_buffer::DataBuffer; use binaryninja::platform::Platform; +use binaryninja::rc::Ref; +use binaryninja::section::{SectionBuilder, Semantics}; +use binaryninja::segment::SegmentFlags; +use binaryninja::symbol::{SymbolBuilder, SymbolType}; use binaryninja::Endianness; +use minidump::format::MemoryProtection; +use minidump::system_info::{Cpu, Os, PointerWidth}; +use minidump::{Minidump, MinidumpMemoryInfoList, MinidumpModuleList}; +use minidump::{MinidumpSystemInfo, Module}; +use object::{Object, ObjectSection, ObjectSymbol, SectionKind, SymbolKind}; -type BinaryViewResult = binaryninja::binary_view::Result; - -/// The _Minidump_ binary view type, which the Rust plugin registers with the Binary Ninja core -/// (via `binaryninja::custombinaryview::register_view_type`) as a possible binary view -/// that can be applied to opened binaries. -/// -/// If this view type is valid for an opened binary (determined by `is_valid_for`), -/// the Binary Ninja core then uses this view type to create an actual instance of the _Minidump_ -/// binary view (via `create_custom_view`). -pub struct MinidumpBinaryViewType { - view_type: BinaryViewType, -} - -impl MinidumpBinaryViewType { - pub fn new(view_type: BinaryViewType) -> Self { - MinidumpBinaryViewType { view_type } - } -} - -impl AsRef for MinidumpBinaryViewType { - fn as_ref(&self) -> &BinaryViewType { - &self.view_type - } -} - -impl BinaryViewTypeBase for MinidumpBinaryViewType { - fn is_deprecated(&self) -> bool { - false - } - - fn is_force_loadable(&self) -> bool { - false - } - - fn is_valid_for(&self, data: &BinaryView) -> bool { - let mut magic_number = Vec::::new(); - data.read_into_vec(&mut magic_number, 0, 4); - - magic_number == b"MDMP" - } -} +pub struct MinidumpBinaryViewType; impl CustomBinaryViewType for MinidumpBinaryViewType { - fn create_custom_view<'builder>( - &self, - data: &BinaryView, - builder: CustomViewBuilder<'builder, Self>, - ) -> BinaryViewResult> { - tracing::debug!("Creating MinidumpBinaryView from registered MinidumpBinaryViewType"); - - let binary_view = builder.create::(data, ()); - binary_view - } -} - -#[derive(Debug)] -struct SegmentData { - rva_range: Range, - mapped_addr_range: Range, -} - -impl SegmentData { - fn from_addresses_and_size(rva: u64, mapped_addr: u64, size: u64) -> Self { - SegmentData { - rva_range: Range { - start: rva, - end: rva + size, - }, - mapped_addr_range: Range { - start: mapped_addr, - end: mapped_addr + size, - }, + type CustomBinaryView = MinidumpBinaryView; + const NAME: &'static str = "Minidump"; + + fn create_binary_view(&self, data: &BinaryView) -> Result { + match MinidumpBinaryView::new(data) { + Ok(minidump_binary_view) => Ok(minidump_binary_view), + Err(e) => { + tracing::error!("Failed to create minidump binary view: {}", e); + Err(()) + } } } -} -#[derive(Debug)] -struct SegmentMemoryProtection { - readable: bool, - writable: bool, - executable: bool, + fn is_valid_for(&self, data: &BinaryView) -> bool { + // Check for the MDMP magic bytes + let magic = [0x4d, 0x44, 0x4d, 0x50]; + let mut buffer = [0u8; 4]; + data.read(&mut buffer, 0); + buffer == magic + } } -/// An instance of the actual _Minidump_ custom binary view. +/// An instance of the actual custom Minidump binary view. +/// /// This contains the main logic to load the memory segments inside a minidump file into the binary view. pub struct MinidumpBinaryView { - /// The handle to the "real" BinaryView object, in the Binary Ninja core. - inner: binaryninja::rc::Ref, + minidump: Minidump<'static, Vec>, + endianness: Endianness, + address_size: usize, + /// The entry point of the main module. + /// + /// This will be set inside [`MinidumpBinaryView::initialize`], so won't be immediately available. + main_entry_point: Option, } impl MinidumpBinaryView { - fn new(view: &BinaryView) -> Self { - MinidumpBinaryView { - inner: view.to_owned(), - } - } - - fn init(&self) -> BinaryViewResult<()> { - let parent_view = self.parent_view().ok_or(())?; - let read_buffer = parent_view - .read_buffer(0, parent_view.len() as usize) - .ok_or(())?; - - if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) { - // Architecture, platform information - if let Ok(minidump_system_info) = minidump_obj.get_stream::() { - if let Some(platform) = MinidumpBinaryView::translate_minidump_platform( - minidump_system_info.cpu, - minidump_obj.endian, - minidump_system_info.os, - ) { - self.set_default_platform(&platform); - } else { - tracing::error!( - "Could not parse valid system information from minidump: could not map system information in MinidumpSystemInfo stream (arch {:?}, endian {:?}, os {:?}) to a known architecture", - minidump_system_info.cpu, - minidump_obj.endian, - minidump_system_info.os, - ); - return Err(()); - } - } else { - tracing::error!( - "Could not parse system information from minidump: could not find a valid MinidumpSystemInfo stream" - ); - return Err(()); - } - - // Memory segments - let mut segment_data = Vec::::new(); - - // Memory segments in a full memory dump (MinidumpMemory64List) - // Grab the shared base RVA for all entries in the MinidumpMemory64List, - // since the minidump crate doesn't expose this to us - if let Ok(raw_stream) = minidump_obj.get_raw_stream(MinidumpMemory64List::STREAM_TYPE) { - if let Ok(base_rva_array) = raw_stream[8..16].try_into() { - let base_rva = u64::from_le_bytes(base_rva_array); - tracing::debug!("Found BaseRVA value {:#x}", base_rva); - - if let Ok(minidump_memory_list) = - minidump_obj.get_stream::() - { - let mut current_rva = base_rva; - for memory_segment in minidump_memory_list.iter() { - tracing::debug!( - "Found memory segment at RVA {:#x} with virtual address {:#x} and size {:#x}", - current_rva, - memory_segment.base_address, - memory_segment.size, - ); - segment_data.push(SegmentData::from_addresses_and_size( - current_rva, - memory_segment.base_address, - memory_segment.size, - )); - current_rva += memory_segment.size; - } - } - } else { - tracing::error!( - "Could not parse BaseRVA value shared by all entries in the MinidumpMemory64List stream" - ) - } - } else { - tracing::warn!( - "Could not read memory from minidump: could not find a valid MinidumpMemory64List stream. This minidump may not be a full memory dump. Trying to find partial dump memory from a MinidumpMemoryList now..." - ); - // Memory segments in a regular memory dump (MinidumpMemoryList), - // i.e. one that does not include the full process memory data. - if let Ok(minidump_memory_list) = minidump_obj.get_stream::() { - for memory_segment in minidump_memory_list.by_addr() { - tracing::debug!( - "Found memory segment at RVA {:#x} with virtual address {:#x} and size {:#x}", - memory_segment.desc.memory.rva, - memory_segment.base_address, - memory_segment.size - ); - segment_data.push(SegmentData::from_addresses_and_size( - memory_segment.desc.memory.rva as u64, - memory_segment.base_address, - memory_segment.size, - )); - } - } else { - tracing::error!( - "Could not read any memory from minidump: could not find a valid MinidumpMemory64List stream or a valid MinidumpMemoryList stream." - ); - } - } - - // Memory protection information - let mut segment_protection_data = HashMap::new(); - - if let Ok(minidump_memory_info_list) = - minidump_obj.get_stream::() - { - for memory_info in minidump_memory_info_list.iter() { - if let Some(memory_range) = memory_info.memory_range() { - tracing::debug!( - "Found memory protection info for memory segment ranging from virtual address {:#x} to {:#x}: {:#?}", - memory_range.start, - memory_range.end, - memory_info.protection - ); - segment_protection_data.insert( - // The range returned to us by MinidumpMemoryInfoList is an - // end-inclusive range_map::Range; we need to add 1 to - // the end index to make it into an end-exclusive std::ops::Range. - Range { - start: memory_range.start, - end: memory_range.end + 1, - }, - memory_info.protection, - ); - } - } + pub fn new(data: &BinaryView) -> Result { + let read_buffer = data + .read_buffer(0, data.len() as usize) + .ok_or("Failed to read data from binary view".to_string())?; + let minidump = Minidump::read(read_buffer.get_data().to_vec()) + .map_err(|e| format!("Failed to parse minidump: {}", e))?; + let system_info = minidump + .get_stream::() + .map_err(|e| format!("Failed to get system info stream: {}", e))?; + let endianness = match minidump.endian { + minidump::Endian::Little => Endianness::LittleEndian, + minidump::Endian::Big => Endianness::BigEndian, + }; + let address_size = match system_info.cpu.pointer_width() { + PointerWidth::Bits32 => 4, + PointerWidth::Bits64 => 8, + PointerWidth::Unknown => { + tracing::warn!("Unknown pointer width, defaulting to 32-bit"); + 4 } + }; - for segment in segment_data.iter() { - if let Some(segment_protection) = - segment_protection_data.get(&segment.mapped_addr_range) - { - let segment_memory_protection = - MinidumpBinaryView::translate_memory_protection(*segment_protection); - - tracing::info!( - "Adding memory segment at virtual address {:#x} to {:#x}, from data range {:#x} to {:#x}, with protections readable {}, writable {}, executable {}", - segment.mapped_addr_range.start, - segment.mapped_addr_range.end, - segment.rva_range.start, - segment.rva_range.end, - segment_memory_protection.readable, - segment_memory_protection.writable, - segment_memory_protection.executable, - ); - - let segment_flags = SegmentFlags::new() - .readable(segment_memory_protection.readable) - .writable(segment_memory_protection.writable) - .executable(segment_memory_protection.executable); - - self.add_segment( - Segment::builder(segment.mapped_addr_range.clone()) - .parent_backing(segment.rva_range.clone()) - .is_auto(true) - .flags(segment_flags), - ); - } else { - tracing::error!( - "Could not find memory protection information for memory segment from {:#x} to {:#x}", segment.mapped_addr_range.start, - segment.mapped_addr_range.end, - ); - } - } + Ok(MinidumpBinaryView { + minidump, + endianness, + address_size, + main_entry_point: None, + }) + } - // Module information - // This stretches the concept a bit, but we can add each module as a - // separate "section" of the binary. - // Sections can be named, and can span multiple segments. - if let Ok(minidump_module_list) = minidump_obj.get_stream::() { - for module_info in minidump_module_list.by_addr() { - tracing::info!( - "Found module with name {} at virtual address {:#x} with size {:#x}", - module_info.name, - module_info.base_address(), - module_info.size(), - ); - let module_address_range = Range { - start: module_info.base_address(), - end: module_info.base_address() + module_info.size(), - }; - self.add_section( - Section::builder(module_info.name.clone(), module_address_range) - .is_auto(true), - ); - } - } else { - tracing::warn!( - "Could not find valid module information in minidump: could not find a valid MinidumpModuleList stream" - ); - } - } else { - tracing::error!("Could not parse data as minidump"); - return Err(()); - } - Ok(()) + pub fn translate_platform( + &self, + system_info: &MinidumpSystemInfo, + ) -> Result, String> { + let platform_name = self.translate_platform_name(system_info.os, system_info.cpu)?; + Platform::by_name(platform_name) + .ok_or_else(|| format!("Could not find platform {}", platform_name)) } - fn translate_minidump_platform( - minidump_cpu_arch: minidump::system_info::Cpu, - minidump_endian: minidump::Endian, - minidump_os: minidump::system_info::Os, - ) -> Option> { - match minidump_os { - minidump::system_info::Os::Windows => match minidump_cpu_arch { - minidump::system_info::Cpu::Arm64 => Platform::by_name("windows-aarch64"), - minidump::system_info::Cpu::Arm => Platform::by_name("windows-armv7"), - minidump::system_info::Cpu::X86 => Platform::by_name("windows-x86"), - minidump::system_info::Cpu::X86_64 => Platform::by_name("windows-x86_64"), - _ => None, + pub fn translate_platform_name(&self, os: Os, cpu: Cpu) -> Result<&'static str, String> { + match os { + Os::Windows => match cpu { + Cpu::Arm64 => Ok("windows-aarch64"), + Cpu::Arm => Ok("windows-armv7"), + Cpu::X86 => Ok("windows-x86"), + Cpu::X86_64 => Ok("windows-x86_64"), + _ => Err("Unsupported CPU architecture".to_string()), }, - minidump::system_info::Os::MacOs => match minidump_cpu_arch { - minidump::system_info::Cpu::Arm64 => Platform::by_name("mac-aarch64"), - minidump::system_info::Cpu::Arm => Platform::by_name("mac-armv7"), - minidump::system_info::Cpu::X86 => Platform::by_name("mac-x86"), - minidump::system_info::Cpu::X86_64 => Platform::by_name("mac-x86_64"), - _ => None, + Os::MacOs => match cpu { + Cpu::Arm64 => Ok("mac-aarch64"), + Cpu::Arm => Ok("mac-armv7"), + Cpu::X86 => Ok("mac-x86"), + Cpu::X86_64 => Ok("mac-x86_64"), + _ => Err("Unsupported CPU architecture".to_string()), }, - minidump::system_info::Os::Linux => match minidump_cpu_arch { - minidump::system_info::Cpu::Arm64 => Platform::by_name("linux-aarch64"), - minidump::system_info::Cpu::Arm => Platform::by_name("linux-armv7"), - minidump::system_info::Cpu::X86 => Platform::by_name("linux-x86"), - minidump::system_info::Cpu::X86_64 => Platform::by_name("linux-x86_64"), - minidump::system_info::Cpu::Ppc => match minidump_endian { - minidump::Endian::Little => Platform::by_name("linux-ppc32_le"), - minidump::Endian::Big => Platform::by_name("linux-ppc32"), + Os::Linux => match cpu { + Cpu::Arm64 => Ok("linux-aarch64"), + Cpu::Arm => Ok("linux-armv7"), + Cpu::X86 => Ok("linux-x86"), + Cpu::X86_64 => Ok("linux-x86_64"), + Cpu::Ppc => match self.endianness { + Endianness::LittleEndian => Ok("linux-ppc32_le"), + Endianness::BigEndian => Ok("linux-ppc32"), }, - minidump::system_info::Cpu::Ppc64 => match minidump_endian { - minidump::Endian::Little => Platform::by_name("linux-ppc64_le"), - minidump::Endian::Big => Platform::by_name("linux-ppc64"), + Cpu::Ppc64 => match self.endianness { + Endianness::LittleEndian => Ok("linux-ppc64_le"), + Endianness::BigEndian => Ok("linux-ppc64"), }, - _ => None, + _ => Err("Unsupported CPU architecture".to_string()), }, - minidump::system_info::Os::NaCl => None, - minidump::system_info::Os::Android => None, - minidump::system_info::Os::Ios => None, - minidump::system_info::Os::Ps3 => None, - minidump::system_info::Os::Solaris => None, - _ => None, + // TODO: Support iOS + Os::Ios => Err("Unsupported operating system".to_string()), + _ => Err("Unsupported operating system".to_string()), } } - fn translate_memory_protection( + pub fn translate_memory_protection( + &self, minidump_memory_protection: MemoryProtection, - ) -> SegmentMemoryProtection { + ) -> SegmentFlags { let (readable, writable, executable) = match minidump_memory_protection { MemoryProtection::PAGE_NOACCESS => (false, false, false), MemoryProtection::PAGE_READONLY => (true, false, false), @@ -367,50 +147,163 @@ impl MinidumpBinaryView { MemoryProtection::PAGE_WRITECOMBINE => (false, false, false), _ => (false, false, false), }; - SegmentMemoryProtection { - readable, - writable, - executable, - } - } -} - -impl AsRef for MinidumpBinaryView { - fn as_ref(&self) -> &BinaryView { - &self.inner + SegmentFlags::new() + .readable(readable) + .writable(writable) + .executable(executable) } } impl BinaryViewBase for MinidumpBinaryView { - // TODO: This should be filled out with the actual address size - // from the platform information in the minidump. - fn address_size(&self) -> usize { - 0 + fn entry_point(&self) -> u64 { + self.main_entry_point.unwrap_or(0) } fn default_endianness(&self) -> Endianness { - // TODO: This should be filled out with the actual endianness - // from the platform information in the minidump. - Endianness::LittleEndian + self.endianness } - fn entry_point(&self) -> u64 { - // TODO: We should fill this out with a real entry point. - // This can be done by getting the main module of the minidump - // with MinidumpModuleList::main_module, - // then parsing the PE metadata of the main module to find its entry point(s). - 0 + fn address_size(&self) -> usize { + self.address_size } } -unsafe impl CustomBinaryView for MinidumpBinaryView { - type Args = (); +impl CustomBinaryView for MinidumpBinaryView { + fn initialize(&mut self, view: &BinaryView) -> bool { + let Ok(system_info) = self.minidump.get_stream::() else { + tracing::error!("Could not find a valid MinidumpSystemInfo stream"); + return false; + }; - fn new(handle: &BinaryView, _args: &Self::Args) -> BinaryViewResult { - Ok(MinidumpBinaryView::new(handle)) - } + let platform = match self.translate_platform(&system_info) { + Ok(platform) => platform, + Err(err) => { + tracing::error!("Could not determine platform: {}", err); + return false; + } + }; + view.set_default_platform(&platform); + + let Some(unified_memory_list) = self.minidump.get_memory() else { + tracing::error!("Could not find a valid memory list stream"); + return false; + }; + + // Some full memory dumps don't have memory info, so we will fall back to default segment flags in that case. + let memory_info_list = self + .minidump + .get_stream::() + .inspect_err(|e| tracing::warn!("Could not find a valid memory info list stream: '{}' no segment flags will be set", e)) + .ok(); + + for memory in unified_memory_list.iter() { + let Some(memory_range) = memory.memory_range() else { + tracing::error!( + "Could not find a valid memory range for memory segment: {:?}", + memory + ); + continue; + }; + + // If we are opening the view again, this will already be filled from the first load, so skip it. + if view + .memory_map() + .get_active_region_at(memory_range.start) + .is_some() + { + tracing::debug!("Skipping memory segment {:0x} because it overlaps with an existing memory region", memory_range.start); + continue; + } + + let segment_flags = memory_info_list + .as_ref() + .and_then(|list| list.memory_info_at_address(memory.base_address())) + .map(|info| self.translate_memory_protection(info.protection)); + + // TODO: The parent backing _is_ the memory range itself, we currently add that memory range + // TODO: after the fact instead of deriving it from the contents of the file itself. + let buffer = DataBuffer::new(memory.bytes()); + view.memory_map().add_data_memory_region( + &format!("{:0x}", memory_range.start), + memory_range.start, + &buffer, + segment_flags, + ); + } + + let Ok(module_list) = self.minidump.get_stream::() else { + tracing::warn!( + "Could not find a valid module list stream, no module sections will be added!" + ); + return true; + }; + + let main_module_addr = module_list + .main_module() + .map(|module| module.base_address()); + + for module in module_list.iter() { + tracing::info!( + "Loading module '{}' at {:0x}", + module.name, + module.base_address() + ); + let mut buffer: Vec = vec![0; module.size() as usize]; + let read_length = view.read(&mut buffer, module.base_address()); + if read_length != module.size() as usize { + tracing::error!("Could not read module: {:?}", module); + continue; + } + let file = match object::File::parse(&*buffer) { + Ok(file) => file, + Err(e) => { + tracing::error!("Could not parse module: {:?}: {}", module.name, e); + continue; + } + }; + for section in file.sections() { + let section_name = + format!("{}:{}", module.name, section.name().unwrap_or("")); + let section_range = section.address()..section.address() + section.size(); + let section_semantics = match section.kind() { + SectionKind::Unknown => Semantics::DefaultSection, + SectionKind::Text => Semantics::ReadOnlyCode, + SectionKind::Data => Semantics::ReadWriteData, + SectionKind::ReadOnlyData => Semantics::ReadOnlyData, + SectionKind::ReadOnlyDataWithRel => Semantics::ReadOnlyData, + SectionKind::ReadOnlyString => Semantics::ReadOnlyData, + SectionKind::UninitializedData => Semantics::ReadOnlyData, + _ => Semantics::DefaultSection, + }; + let section_builder = SectionBuilder::new(section_name, section_range) + .align(section.align()) + .semantics(section_semantics) + .is_auto(true); + view.add_section(section_builder); + } + for symbol in file.symbols() { + let symbol_name = symbol.name().unwrap_or(""); + let symbol_type = match symbol.kind() { + SymbolKind::Unknown => SymbolType::Symbolic, + SymbolKind::Text => SymbolType::Function, + SymbolKind::Data => SymbolType::Data, + SymbolKind::Section => SymbolType::Symbolic, + SymbolKind::File => SymbolType::Symbolic, + SymbolKind::Label => SymbolType::LocalLabel, + SymbolKind::Tls => SymbolType::Symbolic, + _ => SymbolType::Symbolic, + }; + let symbol = + SymbolBuilder::new(symbol_type, symbol_name, symbol.address()).create(); + view.define_auto_symbol(&symbol); + } + view.add_entry_point(file.entry()); + // Set this so [`BinaryView::entry_point`] knows which is the main entry point. + if main_module_addr.is_some_and(|addr| addr == module.base_address()) { + self.main_entry_point = Some(file.entry()); + } + } - fn init(&mut self, _args: Self::Args) -> BinaryViewResult<()> { - MinidumpBinaryView::init(self) + true } }