update rust crate

This commit is contained in:
2024-08-17 16:20:28 +02:00
parent 670fa334db
commit 2167e0512a
88 changed files with 20508 additions and 1741 deletions

View File

@@ -23,7 +23,7 @@ use std::{
collections::HashMap,
ffi::{c_char, c_int, CStr, CString},
hash::Hash,
mem::zeroed,
mem::{zeroed, MaybeUninit},
ops, ptr, slice,
};
@@ -97,11 +97,11 @@ impl<'a> Iterator for BranchIter<'a> {
#[repr(C)]
pub struct InstructionInfo(BNInstructionInfo);
impl InstructionInfo {
pub fn new(len: usize, branch_delay: bool) -> Self {
pub fn new(len: usize, delay_slots: u8) -> Self {
InstructionInfo(BNInstructionInfo {
length: len,
archTransitionByTargetAddr: false,
branchDelay: branch_delay,
delaySlots: delay_slots,
branchCount: 0usize,
branchType: [BranchType::UnresolvedBranch; 3],
branchTarget: [0u64; 3],
@@ -121,8 +121,8 @@ impl InstructionInfo {
self.0.branchCount
}
pub fn branch_delay(&self) -> bool {
self.0.branchDelay
pub fn delay_slots(&self) -> u8 {
self.0.delaySlots
}
pub fn branches(&self) -> BranchIter {
@@ -296,7 +296,7 @@ pub trait FlagGroup: Sized + Clone + Copy {
/// Types to represent the different comparisons, so for `cr1_lt` we
/// would return a mapping along the lines of:
///
/// ```
/// ```text
/// cr1_signed -> LLFC_SLT,
/// cr1_unsigned -> LLFC_ULT,
/// ```
@@ -313,7 +313,7 @@ pub trait Intrinsic: Sized + Clone + Copy {
fn id(&self) -> u32;
/// Reeturns the list of the input names and types for this intrinsic.
fn inputs(&self) -> Vec<NameAndType<String>>;
fn inputs(&self) -> Vec<Ref<NameAndType>>;
/// Returns the list of the output types for this intrinsic.
fn outputs(&self) -> Vec<Conf<Ref<Type>>>;
@@ -650,7 +650,7 @@ impl Intrinsic for UnusedIntrinsic {
fn id(&self) -> u32 {
unreachable!()
}
fn inputs(&self) -> Vec<NameAndType<String>> {
fn inputs(&self) -> Vec<Ref<NameAndType>> {
unreachable!()
}
fn outputs(&self) -> Vec<Conf<Ref<Type>>> {
@@ -715,6 +715,21 @@ impl Register for CoreRegister {
}
}
impl CoreArrayProvider for CoreRegister {
type Raw = u32;
type Context = CoreArchitecture;
type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for CoreRegister {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeRegisterList(raw)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Self(context.0, *raw)
}
}
pub struct CoreRegisterStackInfo(*mut BNArchitecture, BNRegisterStackInfo);
impl RegisterStackInfo for CoreRegisterStackInfo {
@@ -968,8 +983,8 @@ impl FlagGroup for CoreFlagGroup {
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct CoreIntrinsic(*mut BNArchitecture, u32);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct CoreIntrinsic(pub(crate) *mut BNArchitecture, pub(crate) u32);
impl Intrinsic for crate::architecture::CoreIntrinsic {
fn name(&self) -> Cow<str> {
@@ -992,7 +1007,7 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
self.1
}
fn inputs(&self) -> Vec<NameAndType<String>> {
fn inputs(&self) -> Vec<Ref<NameAndType>> {
let mut count: usize = 0;
unsafe {
@@ -1000,7 +1015,7 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
let ret = slice::from_raw_parts_mut(inputs, count)
.iter()
.map(NameAndType::from_raw)
.map(|x| NameAndType::from_raw(x).to_owned())
.collect();
BNFreeNameAndTypeList(inputs, count);
@@ -1162,17 +1177,18 @@ impl Architecture for CoreArchitecture {
&mut result as *mut _,
&mut count as *mut _,
) {
let vec = Vec::<BNInstructionTextToken>::from_raw_parts(result, count, count)
let vec = slice::from_raw_parts(result, count)
.iter()
.map(|x| InstructionTextToken::from_raw(x))
.map(|x| InstructionTextToken::from_raw(x).to_owned())
.collect();
BNFreeInstructionText(result, count);
Some((consumed, vec))
} else {
None
}
}
}
fn instruction_llil(
&self,
data: &[u8],
@@ -1689,8 +1705,8 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A,
{
arch: A,
func: F,
arch: MaybeUninit<A>,
func: Option<F>,
}
extern "C" fn cb_init<A, F>(ctxt: *mut c_void, obj: *mut BNArchitecture)
@@ -1704,11 +1720,10 @@ where
handle: ctxt as *mut A,
};
let create = ptr::read(&custom_arch.func);
ptr::write(
&mut custom_arch.arch,
create(custom_arch_handle, CoreArchitecture(obj)),
);
let create = custom_arch.func.take().unwrap();
custom_arch
.arch
.write(create(custom_arch_handle, CoreArchitecture(obj)));
}
}
@@ -1811,27 +1826,25 @@ where
let data = unsafe { slice::from_raw_parts(data, *len) };
let result = unsafe { &mut *result };
match custom_arch.instruction_text(data, addr) {
Some((res_size, mut res_tokens)) => {
unsafe {
// TODO: Can't use into_raw_parts as it's unstable so we do this instead...
let r_ptr = res_tokens.as_mut_ptr();
let r_count = res_tokens.len();
mem::forget(res_tokens);
let Some((res_size, res_tokens)) = custom_arch.instruction_text(data, addr) else {
return false;
};
*result = &mut (*r_ptr).0;
*count = r_count;
*len = res_size;
}
true
}
None => false,
let res_tokens: Box<[_]> = res_tokens.into_boxed_slice();
unsafe {
let res_tokens = Box::leak(res_tokens);
let r_ptr = res_tokens.as_mut_ptr();
let r_count = res_tokens.len();
*result = &mut (*r_ptr).0;
*count = r_count;
*len = res_size;
}
true
}
extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) {
let _tokens =
unsafe { Vec::from_raw_parts(tokens as *mut InstructionTextToken, count, count) };
let _tokens = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tokens, count)) };
}
extern "C" fn cb_instruction_llil<A>(
@@ -1931,15 +1944,7 @@ where
if len == 0 {
ptr::null_mut()
} else {
let mut res = Vec::with_capacity(len + 1);
res.push(len as u32);
for i in items {
res.push(i);
}
assert!(res.len() == len + 1);
let mut res: Box<[_]> = [len as u32].into_iter().chain(items).collect();
let raw = res.as_mut_ptr();
mem::forget(res);
@@ -2280,7 +2285,8 @@ where
unsafe {
let actual_start = regs.offset(-1);
let len = *actual_start + 1;
let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
let regs_ptr = ptr::slice_from_raw_parts_mut(actual_start, len.try_into().unwrap());
let _regs = Box::from_raw(regs_ptr);
}
}
@@ -2420,28 +2426,28 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
if let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) {
let inputs = intrinsic.inputs();
let mut res = Vec::with_capacity(inputs.len());
for input in inputs {
res.push(input.into_raw());
}
unsafe {
*count = res.len();
if res.is_empty() {
ptr::null_mut()
} else {
let raw = res.as_mut_ptr();
mem::forget(res);
raw
}
}
} else {
let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else {
unsafe {
*count = 0;
}
ptr::null_mut()
return ptr::null_mut();
};
let inputs = intrinsic.inputs();
let mut res: Box<[_]> = inputs
.into_iter()
.map(|input| unsafe { Ref::into_raw(input) }.0)
.collect();
unsafe {
*count = res.len();
if res.is_empty() {
ptr::null_mut()
} else {
let raw = res.as_mut_ptr();
mem::forget(res);
raw
}
}
}
@@ -2453,9 +2459,9 @@ where
if !nt.is_null() {
unsafe {
let list = Vec::from_raw_parts(nt, count, count);
for nt in list {
BnString::from_raw(nt.name);
let name_and_types = Box::from_raw(ptr::slice_from_raw_parts_mut(nt, count));
for nt in name_and_types.iter() {
Ref::new(NameAndType::from_raw(nt));
}
}
}
@@ -2473,10 +2479,7 @@ where
if let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) {
let inputs = intrinsic.outputs();
let mut res = Vec::with_capacity(inputs.len());
for input in inputs {
res.push(input.into());
}
let mut res: Box<[_]> = inputs.iter().map(|input| input.as_ref().into()).collect();
unsafe {
*count = res.len();
@@ -2505,9 +2508,7 @@ where
{
let _custom_arch = unsafe { &*(ctxt as *mut A) };
if !tl.is_null() {
unsafe {
let _list = Vec::from_raw_parts(tl, count, count);
}
let _type_list = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tl, count)) };
}
}
@@ -2685,8 +2686,8 @@ where
let name = name.into_bytes_with_nul();
let uninit_arch = ArchitectureBuilder {
arch: unsafe { zeroed() },
func,
arch: MaybeUninit::zeroed(),
func: Some(func),
};
let raw = Box::into_raw(Box::new(uninit_arch));
@@ -2776,7 +2777,7 @@ where
assert!(!res.is_null());
&(*raw).arch
(*raw).arch.assume_init_mut()
}
}

View File

@@ -104,21 +104,14 @@ unsafe impl RefCountable for BackgroundTask {
impl CoreArrayProvider for BackgroundTask {
type Raw = *mut BNBackgroundTask;
type Context = ();
type Wrapped<'a> = Guard<'a, BackgroundTask>;
}
unsafe impl CoreOwnedArrayProvider for BackgroundTask {
unsafe impl CoreArrayProviderInner for BackgroundTask {
unsafe fn free(raw: *mut *mut BNBackgroundTask, count: usize, _context: &()) {
BNFreeBackgroundTaskList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for BackgroundTask {
type Wrapped = Guard<'a, BackgroundTask>;
unsafe fn wrap_raw(
raw: &'a *mut BNBackgroundTask,
context: &'a (),
) -> Guard<'a, BackgroundTask> {
unsafe fn wrap_raw<'a>(raw: &'a *mut BNBackgroundTask, context: &'a ()) -> Self::Wrapped<'a> {
Guard::new(BackgroundTask::from_raw(*raw), context)
}
}

View File

@@ -68,18 +68,14 @@ pub struct EdgeContext<'a, C: 'a + BlockContext> {
impl<'a, C: 'a + BlockContext> CoreArrayProvider for Edge<'a, C> {
type Raw = BNBasicBlockEdge;
type Context = EdgeContext<'a, C>;
type Wrapped<'b> = Edge<'b, C> where 'a: 'b;
}
unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayProvider for Edge<'a, C> {
unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeBasicBlockEdgeList(raw, count);
}
}
unsafe impl<'a, C: 'a + BlockContext> CoreArrayWrapper<'a> for Edge<'a, C> {
type Wrapped = Edge<'a, C>;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Edge<'a, C> {
unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> {
let edge_target = Guard::new(
BasicBlock::from_raw(raw.target, context.orig_block.context.clone()),
raw,
@@ -301,18 +297,14 @@ unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> {
impl<C: BlockContext> CoreArrayProvider for BasicBlock<C> {
type Raw = *mut BNBasicBlock;
type Context = C;
type Wrapped<'a> = Guard<'a, BasicBlock<C>> where C: 'a;
}
unsafe impl<C: BlockContext> CoreOwnedArrayProvider for BasicBlock<C> {
unsafe impl<C: BlockContext> CoreArrayProviderInner for BasicBlock<C> {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeBasicBlockList(raw, count);
}
}
unsafe impl<'a, C: 'a + BlockContext> CoreArrayWrapper<'a> for BasicBlock<C> {
type Wrapped = Guard<'a, BasicBlock<C>>;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(BasicBlock::from_raw(*raw, context.clone()), context)
}
}

View File

@@ -23,24 +23,20 @@ pub use binaryninjacore_sys::BNAnalysisState as AnalysisState;
pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
use std::collections::HashMap;
use std::ffi::c_void;
use std::ffi::{c_char, c_void};
use std::ops::Range;
use std::os::raw::c_char;
use std::ptr;
use std::result;
use std::{ops, slice};
use std::{ops, ptr, result, slice};
use crate::architecture::Architecture;
use crate::architecture::CoreArchitecture;
use crate::architecture::{Architecture, CoreArchitecture};
use crate::basicblock::BasicBlock;
use crate::component::{Component, ComponentBuilder, IntoComponentGuid};
use crate::databuffer::DataBuffer;
use crate::debuginfo::DebugInfo;
use crate::fileaccessor::FileAccessor;
use crate::filemetadata::FileMetadata;
use crate::flowgraph::FlowGraph;
use crate::function::{Function, NativeBlock};
use crate::linearview::LinearDisassemblyLine;
use crate::linearview::LinearViewCursor;
use crate::linearview::{LinearDisassemblyLine, LinearViewCursor};
use crate::metadata::Metadata;
use crate::platform::Platform;
use crate::relocation::Relocation;
@@ -49,7 +45,10 @@ use crate::segment::{Segment, SegmentBuilder};
use crate::settings::Settings;
use crate::symbol::{Symbol, SymbolType};
use crate::tags::{Tag, TagType};
use crate::types::{DataVariable, NamedTypeReference, QualifiedName, QualifiedNameAndType, Type};
use crate::typelibrary::TypeLibrary;
use crate::types::{
Conf, DataVariable, NamedTypeReference, QualifiedName, QualifiedNameAndType, Type,
};
use crate::Endianness;
use crate::rc::*;
@@ -225,18 +224,10 @@ pub trait BinaryViewExt: BinaryViewBase {
/// Reads up to `len` bytes from address `offset`
fn read_vec(&self, offset: u64, len: usize) -> Vec<u8> {
let mut ret = Vec::with_capacity(len);
let mut ret = vec![0; len];
unsafe {
let res;
{
let dest_slice = ret.get_unchecked_mut(0..len);
res = self.read(dest_slice, offset);
}
ret.set_len(res);
}
let size = self.read(&mut ret, offset);
ret.truncate(size);
ret
}
@@ -244,26 +235,10 @@ pub trait BinaryViewExt: BinaryViewBase {
/// Appends up to `len` bytes from address `offset` into `dest`
fn read_into_vec(&self, dest: &mut Vec<u8>, offset: u64, len: usize) -> usize {
let starting_len = dest.len();
let space = dest.capacity() - starting_len;
if space < len {
dest.reserve(len - space);
}
unsafe {
let res;
{
let dest_slice = dest.get_unchecked_mut(starting_len..starting_len + len);
res = self.read(dest_slice, offset);
}
if res > 0 {
dest.set_len(starting_len + res);
}
res
}
dest.resize(starting_len + len, 0);
let read_size = self.read(&mut dest[starting_len..], offset);
dest.truncate(starting_len + read_size);
read_size
}
fn notify_data_written(&self, offset: u64, len: usize) {
@@ -292,6 +267,18 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe { BNIsOffsetWritableSemantics(self.as_ref().handle, offset) }
}
fn original_image_base(&self) -> u64 {
unsafe {
BNGetOriginalImageBase(self.as_ref().handle)
}
}
fn set_original_image_base(&self, image_base: u64) {
unsafe {
BNSetOriginalImageBase(self.as_ref().handle, image_base)
}
}
fn end(&self) -> u64 {
unsafe { BNGetEndOffset(self.as_ref().handle) }
}
@@ -574,16 +561,27 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
fn define_auto_data_var(&self, dv: DataVariable) {
fn data_variable_at_address(&self, addr: u64) -> Option<Ref<DataVariable>> {
let dv = BNDataVariable::default();
unsafe {
BNDefineDataVariable(self.as_ref().handle, dv.address, &mut dv.t.into());
if BNGetDataVariableAtAddress(self.as_ref().handle, addr, std::mem::transmute(&dv)) {
Some(DataVariable(dv).to_owned())
} else {
None
}
}
}
fn define_auto_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
unsafe {
BNDefineDataVariable(self.as_ref().handle, addr, &mut ty.into().into());
}
}
/// You likely would also like to call [`Self::define_user_symbol`] to bind this data variable with a name
fn define_user_data_var(&self, dv: DataVariable) {
fn define_user_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
unsafe {
BNDefineUserDataVariable(self.as_ref().handle, dv.address, &mut dv.t.into());
BNDefineUserDataVariable(self.as_ref().handle, addr, &mut ty.into().into());
}
}
@@ -674,7 +672,7 @@ pub trait BinaryViewExt: BinaryViewBase {
let name_array = unsafe { Array::<QualifiedName>::new(result_names, result_count, ()) };
for (id, name) in id_array.iter().zip(name_array.iter()) {
result.insert(id.as_str().to_owned(), name.clone());
result.insert(id.to_owned(), name.clone());
}
result
@@ -964,6 +962,15 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
fn entry_point_functions(&self) -> Array<Function> {
unsafe {
let mut count = 0;
let functions = BNGetAllEntryFunctions(self.as_ref().handle, &mut count);
Array::new(functions, count, ())
}
}
fn functions(&self) -> Array<Function> {
unsafe {
let mut count = 0;
@@ -1056,7 +1063,7 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) }
}
fn show_graph_report<S: BnStrCompatible>(&self, raw_name: S, graph: FlowGraph) {
fn show_graph_report<S: BnStrCompatible>(&self, raw_name: S, graph: &FlowGraph) {
let raw_name = raw_name.into_bytes_with_nul();
unsafe {
BNShowGraphReport(
@@ -1363,6 +1370,261 @@ pub trait BinaryViewExt: BinaryViewBase {
Array::new(handle, count, ())
}
}
fn component_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Component> {
let name = guid.into_bytes_with_nul();
let result = unsafe {
BNGetComponentByGuid(
self.as_ref().handle,
name.as_ref().as_ptr() as *const core::ffi::c_char,
)
};
core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
}
fn root_component(&self) -> Option<Component> {
let result = unsafe { BNGetRootComponent(self.as_ref().handle) };
core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
}
fn component_builder(&self) -> ComponentBuilder {
ComponentBuilder::new_from_raw(self.as_ref().handle)
}
fn component_by_path<P: BnStrCompatible>(&self, path: P) -> Option<Component> {
let path = path.into_bytes_with_nul();
let result = unsafe {
BNGetComponentByPath(
self.as_ref().handle,
path.as_ref().as_ptr() as *const core::ffi::c_char,
)
};
core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
}
fn remove_component(&self, component: &Component) -> bool {
unsafe { BNRemoveComponent(self.as_ref().handle, component.as_raw()) }
}
fn remove_component_by_guid<P: IntoComponentGuid>(&self, guid: P) -> bool {
let path = guid.component_guid();
unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) }
}
fn data_variable_parent_components(
&self,
data_variable: &DataVariable,
) -> Array<Component> {
let mut count = 0;
let result = unsafe {
BNGetDataVariableParentComponents(
self.as_ref().handle,
data_variable.address(),
&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()) }
}
fn type_library_by_name<S: BnStrCompatible>(&self, name: S) -> Option<TypeLibrary> {
let name = name.into_bytes_with_nul();
let result = unsafe {
BNGetBinaryViewTypeLibrary(
self.as_ref().handle,
name.as_ref().as_ptr() as *const core::ffi::c_char,
)
};
core::ptr::NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
/// Should be called by custom py:py:class:`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].
///
/// * `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(
&self,
lib: &TypeLibrary,
name: &QualifiedName,
addr: u64,
platform: &Platform,
) {
unsafe {
BNBinaryViewRecordImportedObjectLibrary(
self.as_ref().handle,
platform.handle,
addr,
lib.as_raw(),
&name.0 as *const _ as *mut _,
)
}
}
/// Recursively imports a type from the specified type library, or, if
/// no library was explicitly provided, the first type library associated with the current [BinaryView]
/// that provides the name requested.
///
/// This may have the impact of loading other type libraries as dependencies on other type libraries are lazily resolved
/// when references to types provided by them are first encountered.
///
/// 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(
&self,
name: &QualifiedName,
mut lib: Option<TypeLibrary>,
) -> Option<Ref<Type>> {
let mut lib_ref = lib
.as_mut()
.map(|l| unsafe { l.as_raw() } as *mut _)
.unwrap_or(ptr::null_mut());
let result = unsafe {
BNBinaryViewImportTypeLibraryType(
self.as_ref().handle,
&mut lib_ref,
&name.0 as *const _ as *mut _,
)
};
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
/// Recursively imports an object from the specified type library, or, if
/// no library was explicitly provided, the first type library associated with the current [BinaryView]
/// that provides the name requested.
///
/// This may have the impact of loading other type libraries as dependencies on other type 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 where the object is located.
fn import_type_object(
&self,
name: &QualifiedName,
mut lib: Option<TypeLibrary>,
) -> Option<Ref<Type>> {
let mut lib_ref = lib
.as_mut()
.map(|l| unsafe { l.as_raw() } as *mut _)
.unwrap_or(ptr::null_mut());
let result = unsafe {
BNBinaryViewImportTypeLibraryObject(
self.as_ref().handle,
&mut lib_ref,
&name.0 as *const _ as *mut _,
)
};
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
/// Recursively imports a type interface given its GUID.
///
/// .. note:: To support this type of lookup a type library must have
/// contain a metadata key called "type_guids" which is a map
/// Dict[string_guid, string_type_name] or
/// Dict[string_guid, Tuple[string_type_name, type_library_name]]
fn import_type_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Ref<Type>> {
let guid = guid.into_bytes_with_nul();
let result = unsafe {
BNBinaryViewImportTypeLibraryTypeByGuid(
self.as_ref().handle,
guid.as_ref().as_ptr() as *const c_char,
)
};
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
/// Recursively exports `type_obj` into `lib` as a type with name `name`
///
/// 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(&self, lib: &TypeLibrary, name: &QualifiedName, type_obj: &Type) {
unsafe {
BNBinaryViewExportTypeToTypeLibrary(
self.as_ref().handle,
lib.as_raw(),
&name.0 as *const _ as *mut _,
type_obj.handle,
)
}
}
/// Recursively exports `type_obj` into `lib` as a type with name `name`
///
/// 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(&self, lib: &TypeLibrary, name: &QualifiedName, type_obj: &Type) {
unsafe {
BNBinaryViewExportObjectToTypeLibrary(
self.as_ref().handle,
lib.as_raw(),
&name.0 as *const _ as *mut _,
type_obj.handle,
)
}
}
/// Gives you details of which type library and name was used to determine
/// the type of a symbol at a given address
///
/// * `addr` - address of symbol at import site
/// * `platform` - Platform of symbol at import site
fn lookup_imported_object_library(
&self,
addr: u64,
platform: &Platform,
) -> Option<(TypeLibrary, QualifiedName)> {
let mut result_lib = ptr::null_mut();
let mut result_name = Default::default();
let success = unsafe {
BNBinaryViewLookupImportedObjectLibrary(
self.as_ref().handle,
platform.handle,
addr,
&mut result_lib,
&mut result_name,
)
};
if !success {
return None;
}
let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) };
let name = QualifiedName(result_name);
Some((lib, name))
}
/// 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(
&self,
name: &QualifiedNameAndType,
) -> Option<(TypeLibrary, QualifiedName)> {
let mut result_lib = ptr::null_mut();
let mut result_name = Default::default();
let success = unsafe {
BNBinaryViewLookupImportedTypeLibrary(
self.as_ref().handle,
&name.0 as *const _ as *mut _,
&mut result_lib,
&mut result_name,
)
};
if !success {
return None;
}
let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) };
let name = QualifiedName(result_name);
Some((lib, name))
}
}
impl<T: BinaryViewBase> BinaryViewExt for T {}
@@ -1545,7 +1807,7 @@ pub type BinaryViewEventType = BNBinaryViewEventType;
///
/// # Example
///
/// ```rust
/// ```no_run
/// use binaryninja::binaryview::{BinaryView, BinaryViewEventHandler, BinaryViewEventType, register_binary_view_event};
///
/// struct EventHandlerContext {
@@ -1553,7 +1815,7 @@ pub type BinaryViewEventType = BNBinaryViewEventType;
/// }
///
/// impl BinaryViewEventHandler for EventHandlerContext {
/// fn on_event(&mut self, binary_view: &BinaryView) {
/// fn on_event(&self, binary_view: &BinaryView) {
/// // handle event
/// }
/// }
@@ -1576,11 +1838,9 @@ where
ctx: *mut ::std::os::raw::c_void,
view: *mut BNBinaryView,
) {
ffi_wrap!("EventHandler::on_event", unsafe {
let mut context = &mut *(ctx as *mut Handler);
let handle = BinaryView::from_raw(BNNewViewReference(view));
Handler::on_event(&mut context, handle.as_ref());
ffi_wrap!("EventHandler::on_event", {
let context = unsafe { &*(ctx as *const Handler) };
context.on_event(&BinaryView::from_raw(BNNewViewReference(view)));
})
}

View File

@@ -26,7 +26,7 @@ use binaryninjacore_sys::*;
use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register};
use crate::rc::{
CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable,
CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable,
};
use crate::string::*;
@@ -89,23 +89,15 @@ where
*count = len;
if len == 0 {
ptr::null_mut()
} else {
let mut res = Vec::with_capacity(len + 1);
res.push(len as u32);
for i in items {
res.push(i);
}
assert!(res.len() == len + 1);
let raw = res.as_mut_ptr();
mem::forget(res);
unsafe { raw.offset(1) }
return ptr::null_mut();
}
let res: Box<[_]> = [len as u32].into_iter().chain(items).collect();
debug_assert!(res.len() == len + 1);
// it's free on the function below: `cb_free_register_list`
let raw = Box::leak(res);
&mut raw[1]
}
extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) {
@@ -115,8 +107,8 @@ where
}
let actual_start = regs.offset(-1);
let len = *actual_start + 1;
let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
let len = (*actual_start) + 1;
let _regs = Box::from_raw(ptr::slice_from_raw_parts_mut(actual_start, len as usize));
})
}
@@ -136,7 +128,7 @@ where
where
C: CallingConventionBase,
{
ffi_wrap!("CallingConvention::_callee_saved_registers", unsafe {
ffi_wrap!("CallingConvention::callee_saved_registers", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
let regs = ctxt.cc.callee_saved_registers();
@@ -448,24 +440,21 @@ impl<A: Architecture> CallingConvention<A> {
unsafe { BnString::from_raw(BNGetCallingConventionName(self.handle)) }
}
pub fn variables_for_parameters<S: Clone + BnStrCompatible>(
pub fn variables_for_parameters(
&self,
params: &[FunctionParameter<S>],
params: &[FunctionParameter],
permitted_registers: Option<&[A::Register]>,
) -> Vec<Variable> {
let mut bn_params: Vec<BNFunctionParameter> = vec![];
let mut name_strings = vec![];
let name_strings = params.iter().map(|parameter| &parameter.name);
for parameter in params.iter() {
name_strings.push(parameter.name.clone().into_bytes_with_nul());
}
for (parameter, raw_name) in params.iter().zip(name_strings.iter_mut()) {
for (parameter, raw_name) in params.iter().zip(name_strings) {
let location = match &parameter.location {
Some(location) => location.raw(),
None => unsafe { mem::zeroed() },
};
bn_params.push(BNFunctionParameter {
name: raw_name.as_ref().as_ptr() as *mut _,
name: BnString::new(raw_name).into_raw(),
type_: parameter.t.contents.handle,
typeConfidence: parameter.t.confidence,
defaultLocation: parameter.location.is_none(),
@@ -501,9 +490,6 @@ impl<A: Architecture> CallingConvention<A> {
}
};
// Gotta keep this around so the pointers are valid during the call
drop(name_strings);
let vars_slice = unsafe { slice::from_raw_parts(vars, count) };
let mut result = vec![];
for var in vars_slice {
@@ -575,11 +561,43 @@ impl<A: Architecture> CallingConventionBase for CallingConvention<A> {
}
fn int_arg_registers(&self) -> Vec<A::Register> {
Vec::new()
unsafe {
let mut count = 0;
let regs = BNGetIntegerArgumentRegisters(self.handle, &mut count);
let arch = self.arch_handle.borrow();
let res = slice::from_raw_parts(regs, count)
.iter()
.map(|&r| {
arch.register_from_id(r)
.expect("bad reg id from CallingConvention")
})
.collect();
BNFreeRegisterList(regs);
res
}
}
fn float_arg_registers(&self) -> Vec<A::Register> {
Vec::new()
unsafe {
let mut count = 0;
let regs = BNGetFloatArgumentRegisters(self.handle, &mut count);
let arch = self.arch_handle.borrow();
let res = slice::from_raw_parts(regs, count)
.iter()
.map(|&r| {
arch.register_from_id(r)
.expect("bad reg id from CallingConvention")
})
.collect();
BNFreeRegisterList(regs);
res
}
}
fn arg_registers_shared_index(&self) -> bool {
@@ -660,18 +678,14 @@ unsafe impl<A: Architecture> RefCountable for CallingConvention<A> {
impl<A: Architecture> CoreArrayProvider for CallingConvention<A> {
type Raw = *mut BNCallingConvention;
type Context = A::Handle;
type Wrapped<'a> = Guard<'a, CallingConvention<A>>;
}
unsafe impl<A: Architecture> CoreOwnedArrayProvider for CallingConvention<A> {
unsafe impl<A: Architecture> CoreArrayProviderInner for CallingConvention<A> {
unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) {
BNFreeCallingConventionList(raw, count);
}
}
unsafe impl<'a, A: Architecture> CoreArrayWrapper<'a> for CallingConvention<A> {
type Wrapped = Guard<'a, CallingConvention<A>>;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(
CallingConvention {
handle: *raw,
@@ -691,7 +705,7 @@ impl Debug for CallingConvention<CoreArchitecture> {
pub struct ConventionBuilder<A: Architecture> {
caller_saved_registers: Vec<A::Register>,
_callee_saved_registers: Vec<A::Register>,
callee_saved_registers: Vec<A::Register>,
int_arg_registers: Vec<A::Register>,
float_arg_registers: Vec<A::Register>,
@@ -760,7 +774,7 @@ impl<A: Architecture> ConventionBuilder<A> {
pub fn new(arch: &A) -> Self {
Self {
caller_saved_registers: Vec::new(),
_callee_saved_registers: Vec::new(),
callee_saved_registers: Vec::new(),
int_arg_registers: Vec::new(),
float_arg_registers: Vec::new(),
@@ -785,7 +799,7 @@ impl<A: Architecture> ConventionBuilder<A> {
}
reg_list!(caller_saved_registers);
reg_list!(_callee_saved_registers);
reg_list!(callee_saved_registers);
reg_list!(int_arg_registers);
reg_list!(float_arg_registers);
@@ -819,7 +833,7 @@ impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> {
}
fn callee_saved_registers(&self) -> Vec<A::Register> {
self.caller_saved_registers.clone()
self.callee_saved_registers.clone()
}
fn int_arg_registers(&self) -> Vec<A::Register> {

View File

@@ -16,12 +16,16 @@
//!
//! All plugins need to provide one of the following functions for Binary Ninja to call:
//!
//! ```rust
//! pub extern "C" fn CorePluginInit() -> bool {}
//! ```no_run
//! pub extern "C" fn CorePluginInit() -> bool {
//! todo!();
//! }
//! ```
//!
//! ```rust
//! pub extern "C" fn UIPluginInit() -> bool {}
//! ```no_run
//! pub extern "C" fn UIPluginInit() -> bool {
//! todo!();
//! }
//! ```
//!
//! Both of these functions can call any of the following registration functions, though `CorePluginInit` is called during Binary Ninja core initialization, and `UIPluginInit` is called during Binary Ninja UI initialization.
@@ -62,7 +66,9 @@ where
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
///
/// # Example
/// ```rust
/// ```no_run
/// # use binaryninja::command::Command;
/// # use binaryninja::binaryview::BinaryView;
/// struct MyCommand;
///
/// impl Command for MyCommand {
@@ -76,6 +82,7 @@ where
/// }
/// }
///
/// # use binaryninja::command::register;
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
/// register(
@@ -160,7 +167,9 @@ where
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
///
/// # Example
/// ```rust
/// ```no_run
/// # use binaryninja::command::AddressCommand;
/// # use binaryninja::binaryview::BinaryView;
/// struct MyCommand;
///
/// impl AddressCommand for MyCommand {
@@ -174,6 +183,7 @@ where
/// }
/// }
///
/// # use binaryninja::command::register_for_address;
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
/// register_for_address(
@@ -258,10 +268,13 @@ where
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
///
/// # Example
/// ```rust
/// ```no_run
/// # use std::ops::Range;
/// # use binaryninja::command::RangeCommand;
/// # use binaryninja::binaryview::BinaryView;
/// struct MyCommand;
///
/// impl AddressCommand for MyCommand {
/// impl RangeCommand for MyCommand {
/// fn action(&self, view: &BinaryView, range: Range<u64>) {
/// // Your code here
/// }
@@ -272,6 +285,7 @@ where
/// }
/// }
///
/// # use binaryninja::command::register_for_range;
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
/// register_for_range(
@@ -361,10 +375,14 @@ where
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
///
/// # Example
/// ```rust
/// ```no_run
/// # use binaryninja::command::FunctionCommand;
/// # use binaryninja::binaryview::BinaryView;
/// # use binaryninja::function::Function;
/// # use binaryninja::command::register_for_function;
/// struct MyCommand;
///
/// impl AddressCommand for MyCommand {
/// impl FunctionCommand for MyCommand {
/// fn action(&self, view: &BinaryView, func: &Function) {
/// // Your code here
/// }

295
src/component.rs Normal file
View File

@@ -0,0 +1,295 @@
use core::{ffi, mem, ptr};
use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
use crate::function::Function;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
use crate::string::{BnStrCompatible, BnString};
use crate::types::{ComponentReferencedTypes, DataVariable};
use binaryninjacore_sys::*;
pub struct ComponentBuilder {
bv: *mut BNBinaryView,
parent: Option<BnString>,
name: Option<BnString>,
}
impl ComponentBuilder {
pub(crate) fn new_from_raw(bv: *mut BNBinaryView) -> Self {
Self {
bv,
parent: None,
name: None,
}
}
pub fn new<I: BinaryViewBase>(bv: &I) -> Self {
Self {
bv: bv.as_ref().handle,
parent: None,
name: None,
}
}
pub fn parent<G: IntoComponentGuid>(mut self, parent: G) -> Self {
self.parent = Some(parent.component_guid());
self
}
pub fn name<S: BnStrCompatible>(mut self, name: S) -> Self {
self.name = Some(BnString::new(name));
self
}
pub fn finalize(self) -> Component {
let result = match (&self.parent, &self.name) {
(None, None) => unsafe { BNCreateComponent(self.bv) },
(None, Some(name)) => unsafe { BNCreateComponentWithName(self.bv, name.as_ptr()) },
(Some(guid), None) => unsafe { BNCreateComponentWithParent(self.bv, guid.as_ptr()) },
(Some(guid), Some(name)) => unsafe {
BNCreateComponentWithParentAndName(self.bv, guid.as_ptr(), name.as_ptr())
},
};
unsafe { Component::from_raw(ptr::NonNull::new(result).unwrap()) }
}
}
/// Components are objects that can contain Functions, Data Variables, and other Components.
///
/// They can be queried for information about the items contained within them.
///
/// Components have a Guid, which persistent across saves and loads of the database, and should be
/// used for retrieving components when such is required and a reference to the Component cannot be held.
#[repr(transparent)]
pub struct Component {
handle: ptr::NonNull<BNComponent>,
}
impl Component {
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNComponent {
&mut *self.handle.as_ptr()
}
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNComponent>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNComponent) -> &Self {
assert!(!handle.is_null());
mem::transmute(handle)
}
pub fn guid(&self) -> BnString {
let result = unsafe { BNComponentGetGuid(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
/// Add function to this component.
pub fn add_function(&self, func: &Function) -> bool {
unsafe { BNComponentAddFunctionReference(self.as_raw(), func.handle) }
}
/// Check whether this component contains a function.
pub fn contains_function(&self, func: &Function) -> bool {
unsafe { BNComponentContainsFunction(self.as_raw(), func.handle) }
}
/// Remove function from this component.
pub fn remove_function(&self, func: &Function) -> bool {
unsafe { BNComponentRemoveFunctionReference(self.as_raw(), func.handle) }
}
/// Move component to this component. This will remove it from the old parent.
pub fn add_component(&self, component: &Component) -> bool {
unsafe { BNComponentAddComponent(self.as_raw(), component.as_raw()) }
}
/// Check whether this component contains a component.
pub fn contains_component(&self, component: &Component) -> bool {
unsafe { BNComponentContainsComponent(self.as_raw(), component.as_raw()) }
}
/// Remove a component from the current component, moving it to the root.
///
/// This function has no effect when used from the root component.
/// Use `BinaryView.remove_component` to Remove a component from the tree entirely.
pub fn remove_component(&self, component: &Component) -> bool {
self.view()
.unwrap()
.root_component()
.unwrap()
.add_component(component)
}
/// Add data variable to this component.
pub fn add_data_variable(&self, data_variable: &DataVariable) -> bool {
unsafe { BNComponentAddDataVariable(self.as_raw(), data_variable.address()) }
}
/// Check whether this component contains a data variable.
pub fn contains_data_variable(&self, data_variable: &DataVariable) -> bool {
unsafe { BNComponentContainsDataVariable(self.as_raw(), data_variable.address()) }
}
/// Remove data variable from this component.
pub fn remove_data_variable(&self, data_variable: &DataVariable) -> bool {
unsafe { BNComponentRemoveDataVariable(self.as_raw(), data_variable.address()) }
}
/// Original name of the component
pub fn display_name(&self) -> BnString {
let result = unsafe { BNComponentGetDisplayName(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
/// Original name set for this component
/// :note: The `.display_name` property should be used for `bv.get_component_by_path()` lookups.
/// This can differ from the .display_name property if one of its sibling components has the same .original_name; In that
/// case, .name will be an automatically generated unique name (e.g. "MyComponentName (1)") while .original_name will
/// remain what was originally set (e.g. "MyComponentName")
/// If this component has a duplicate name and is moved to a component where none of its siblings share its name,
/// .name will return the original "MyComponentName"
pub fn name(&self) -> BnString {
let result = unsafe { BNComponentGetOriginalName(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn set_name<S: BnStrCompatible>(&self, name: S) {
let name = name.into_bytes_with_nul();
unsafe { BNComponentSetName(self.as_raw(), name.as_ref().as_ptr() as *const ffi::c_char) }
}
/// The component that contains this component, if it exists.
pub fn parent(&self) -> Option<Component> {
let result = unsafe { BNComponentGetParent(self.as_raw()) };
ptr::NonNull::new(result).map(|h| unsafe { Self::from_raw(h) })
}
pub fn view(&self) -> Option<Ref<BinaryView>> {
let result = unsafe { BNComponentGetView(self.as_raw()) };
(!result.is_null()).then(|| unsafe { BinaryView::from_raw(result) })
}
/// Is an iterator for all Components contained within this Component
pub fn components(&self) -> Array<Component> {
let mut count = 0;
let result = unsafe { BNComponentGetContainedComponents(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// List of all Functions contained within this Component
pub fn functions(&self) -> Array<Function> {
let mut count = 0;
let result = unsafe { BNComponentGetContainedFunctions(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// List of all Data Variables contained within this Component
pub fn data_variables(&self) -> Array<DataVariable> {
let mut count = 0;
let result = unsafe { BNComponentGetContainedDataVariables(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get data variables referenced by this component
///
/// * `recursive` - Get all DataVariables referenced by this component and subcomponents.
pub fn get_referenced_data_variables(&self, recursive: bool) -> Array<DataVariable> {
let mut count = 0;
let result = if recursive {
unsafe { BNComponentGetReferencedDataVariablesRecursive(self.as_raw(), &mut count) }
} else {
unsafe { BNComponentGetReferencedDataVariables(self.as_raw(), &mut count) }
};
unsafe { Array::new(result, count, ()) }
}
/// Get Types referenced by this component
///
/// * `recursive` - Get all Types referenced by this component and subcomponents.
pub fn get_referenced_types(&self, recursive: bool) -> Array<ComponentReferencedTypes> {
let mut count = 0;
let result = if recursive {
unsafe { BNComponentGetReferencedTypesRecursive(self.as_raw(), &mut count) }
} else {
unsafe { BNComponentGetReferencedTypes(self.as_raw(), &mut count) }
};
unsafe { Array::new(result, count, ()) }
}
pub fn remove_all_functions(&self) {
unsafe { BNComponentRemoveAllFunctions(self.as_raw()) }
}
pub fn add_all_members_from(&self, component: &Component) {
unsafe { BNComponentAddAllMembersFromComponent(self.as_raw(), component.as_raw()) }
}
}
impl PartialEq for Component {
fn eq(&self, other: &Self) -> bool {
unsafe { BNComponentsEqual(self.as_raw(), other.as_raw()) }
}
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, other: &Self) -> bool {
unsafe { BNComponentsNotEqual(self.as_raw(), other.as_raw()) }
}
}
impl Eq for Component {}
impl Drop for Component {
fn drop(&mut self) {
unsafe { BNFreeComponent(self.as_raw()) }
}
}
impl Clone for Component {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewComponentReference(self.as_raw())).unwrap())
}
}
}
impl CoreArrayProvider for Component {
type Raw = *mut BNComponent;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for Component {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeComponents(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
pub trait IntoComponentGuid {
fn component_guid(self) -> BnString;
}
impl IntoComponentGuid for &Component {
fn component_guid(self) -> BnString {
self.guid()
}
}
impl<S: BnStrCompatible> IntoComponentGuid for S {
fn component_guid(self) -> BnString {
BnString::new(self)
}
}

View File

@@ -20,6 +20,7 @@ pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
use std::marker::PhantomData;
use std::mem;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr;
use std::slice;
@@ -122,11 +123,10 @@ where
let long_name = long_name.into_bytes_with_nul();
let long_name_ptr = long_name.as_ref().as_ptr() as *mut _;
let ctxt = Box::new(unsafe { mem::zeroed() });
let ctxt = Box::into_raw(ctxt);
let ctxt = Box::leak(Box::new(MaybeUninit::zeroed()));
let mut bn_obj = BNCustomBinaryViewType {
context: ctxt as *mut _,
context: ctxt.as_mut_ptr() as *mut _,
create: Some(cb_create::<T>),
parse: Some(cb_parse::<T>),
isValidForData: Some(cb_valid::<T>),
@@ -140,15 +140,16 @@ where
if res.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)
mem::forget(*Box::from_raw(ctxt));
// 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");
}
ptr::write(ctxt, constructor(BinaryViewType(res)));
ctxt.write(constructor(BinaryViewType(res)));
&*ctxt
ctxt.assume_init_mut()
}
}
@@ -289,19 +290,15 @@ impl BinaryViewTypeBase for BinaryViewType {
impl CoreArrayProvider for BinaryViewType {
type Raw = *mut BNBinaryViewType;
type Context = ();
type Wrapped<'a> = Guard<'a, BinaryViewType>;
}
unsafe impl CoreOwnedArrayProvider for BinaryViewType {
unsafe impl CoreArrayProviderInner for BinaryViewType {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeBinaryViewTypeList(raw);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for BinaryViewType {
type Wrapped = BinaryViewType;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
BinaryViewType(*raw)
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(BinaryViewType(*raw), &())
}
}
@@ -388,7 +385,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
let view_name = view_type.name();
if let Ok(bv) = file.get_view_of_type(view_name.as_cstr()) {
if let Ok(bv) = file.get_view_of_type(view_name.as_str()) {
// while it seems to work most of the time, you can get really unlucky
// if the a free of the existing view of the same type kicks off while
// BNCreateBinaryViewOfType is still running. the freeObject callback
@@ -772,7 +769,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
unsafe {
let res = BNCreateCustomBinaryView(
view_name.as_cstr().as_ptr(),
view_name.as_ptr(),
file.handle,
parent.handle,
&mut bn_obj,

654
src/database.rs Normal file
View File

@@ -0,0 +1,654 @@
use std::collections::HashMap;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{ffi, mem, ptr};
use binaryninjacore_sys::*;
use crate::binaryview::BinaryView;
use crate::databuffer::DataBuffer;
use crate::disassembly::InstructionTextToken;
use crate::filemetadata::FileMetadata;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
use crate::string::{BnStrCompatible, BnString};
#[repr(transparent)]
pub struct Database {
handle: ptr::NonNull<BNDatabase>,
}
impl Database {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNDatabase>) -> Self {
Self { handle }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNDatabase {
&mut *self.handle.as_ptr()
}
/// Get a snapshot by its id, or None if no snapshot with that id exists
pub fn snapshot(&self, id: i64) -> Option<Snapshot> {
let result = unsafe { BNGetDatabaseSnapshot(self.as_raw(), id) };
ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
}
/// Get a list of all snapshots in the database
pub fn snapshots(&self) -> Array<Snapshot> {
let mut count = 0;
let result = unsafe { BNGetDatabaseSnapshots(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get the current snapshot
pub fn current_snapshot(&self) -> Option<Snapshot> {
let result = unsafe { BNGetDatabaseCurrentSnapshot(self.as_raw()) };
ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
}
pub fn set_current_snapshot(&self, value: &Snapshot) {
unsafe { BNSetDatabaseCurrentSnapshot(self.as_raw(), value.id()) }
}
pub fn write_snapshot_data<N: BnStrCompatible>(
&self,
parents: &[i64],
file: &BinaryView,
name: N,
data: &KeyValueStore,
auto_save: bool,
) -> i64 {
let name_raw = name.into_bytes_with_nul();
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe {
BNWriteDatabaseSnapshotData(
self.as_raw(),
parents.as_ptr() as *mut _,
parents.len(),
file.handle,
name_ptr,
data.as_raw(),
auto_save,
ptr::null_mut(),
Some(cb_progress_nop),
)
}
}
pub fn write_snapshot_data_with_progress<N, F>(
&self,
parents: &[i64],
file: &BinaryView,
name: N,
data: &KeyValueStore,
auto_save: bool,
mut progress: F,
) -> i64
where
N: BnStrCompatible,
F: FnMut(usize, usize) -> bool,
{
let name_raw = name.into_bytes_with_nul();
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
unsafe {
BNWriteDatabaseSnapshotData(
self.as_raw(),
parents.as_ptr() as *mut _,
parents.len(),
file.handle,
name_ptr,
data.as_raw(),
auto_save,
ctxt,
Some(cb_progress::<F>),
)
}
}
/// Trim a snapshot's contents in the database by id, but leave the parent/child
/// hierarchy intact. Future references to this snapshot will return False for has_contents
pub fn trim_snapshot(&self, id: i64) -> Result<(), ()> {
if unsafe { BNTrimDatabaseSnapshot(self.as_raw(), id) } {
Ok(())
} else {
Err(())
}
}
/// Remove a snapshot in the database by id, deleting its contents and references.
/// Attempting to remove a snapshot with children will raise an exception.
pub fn remove_snapshot(&self, id: i64) -> Result<(), ()> {
if unsafe { BNRemoveDatabaseSnapshot(self.as_raw(), id) } {
Ok(())
} else {
Err(())
}
}
pub fn has_global<S: BnStrCompatible>(&self, key: S) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNDatabaseHasGlobal(self.as_raw(), key_ptr) != 0 }
}
/// Get a list of keys for all globals in the database
pub fn global_keys(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetDatabaseGlobalKeys(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get a dictionary of all globals
pub fn globals(&self) -> HashMap<String, String> {
self.global_keys()
.iter()
.filter_map(|key| Some((key.to_string(), self.read_global(key)?.to_string())))
.collect()
}
/// Get a specific global by key
pub fn read_global<S: BnStrCompatible>(&self, key: S) -> Option<BnString> {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let result = unsafe { BNReadDatabaseGlobal(self.as_raw(), key_ptr) };
unsafe { ptr::NonNull::new(result).map(|_| BnString::from_raw(result)) }
}
/// Write a global into the database
pub fn write_global<K: BnStrCompatible, V: BnStrCompatible>(&self, key: K, value: V) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let value_raw = value.into_bytes_with_nul();
let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNWriteDatabaseGlobal(self.as_raw(), key_ptr, value_ptr) }
}
/// Get a specific global by key, as a binary buffer
pub fn read_global_data<S: BnStrCompatible>(&self, key: S) -> Option<DataBuffer> {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let result = unsafe { BNReadDatabaseGlobalData(self.as_raw(), key_ptr) };
ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result))
}
/// Write a binary buffer into a global in the database
pub fn write_global_data<K: BnStrCompatible>(&self, key: K, value: &DataBuffer) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNWriteDatabaseGlobalData(self.as_raw(), key_ptr, value.as_raw()) }
}
/// Get the owning FileMetadata
pub fn file(&self) -> Ref<FileMetadata> {
let result = unsafe { BNGetDatabaseFile(self.as_raw()) };
assert!(!result.is_null());
unsafe { Ref::new(FileMetadata::from_raw(result)) }
}
/// Get the backing analysis cache kvs
pub fn analysis_cache(&self) -> KeyValueStore {
let result = unsafe { BNReadDatabaseAnalysisCache(self.as_raw()) };
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
}
pub fn reload_connection(&self) {
unsafe { BNDatabaseReloadConnection(self.as_raw()) }
}
pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
if unsafe { BNWriteDatabaseAnalysisCache(self.as_raw(), val.as_raw()) } {
Ok(())
} else {
Err(())
}
}
pub fn snapshot_has_data(&self, id: i64) -> bool {
unsafe { BNSnapshotHasData(self.as_raw(), id) }
}
}
impl Clone for Database {
fn clone(&self) -> Self {
unsafe { Self::from_raw(ptr::NonNull::new(BNNewDatabaseReference(self.as_raw())).unwrap()) }
}
}
impl Drop for Database {
fn drop(&mut self) {
unsafe { BNFreeDatabase(self.as_raw()) }
}
}
#[repr(transparent)]
pub struct Snapshot {
handle: ptr::NonNull<BNSnapshot>,
}
impl Snapshot {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNSnapshot>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNSnapshot) -> &Self {
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNSnapshot {
&mut *self.handle.as_ptr()
}
/// Get the owning database
pub fn database(&self) -> Database {
unsafe {
Database::from_raw(ptr::NonNull::new(BNGetSnapshotDatabase(self.as_raw())).unwrap())
}
}
/// Get the numerical id (read-only)
pub fn id(&self) -> i64 {
unsafe { BNGetSnapshotId(self.as_raw()) }
}
/// Get the displayed snapshot name
pub fn name(&self) -> BnString {
unsafe { BnString::from_raw(BNGetSnapshotName(self.as_raw())) }
}
/// Set the displayed snapshot name
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
let value_raw = value.into_bytes_with_nul();
let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNSetSnapshotName(self.as_raw(), value_ptr) }
}
/// If the snapshot was the result of an auto-save
pub fn is_auto_save(&self) -> bool {
unsafe { BNIsSnapshotAutoSave(self.as_raw()) }
}
/// If the snapshot has contents, and has not been trimmed
pub fn has_contents(&self) -> bool {
unsafe { BNSnapshotHasContents(self.as_raw()) }
}
/// If the snapshot has undo data
pub fn has_undo(&self) -> bool {
unsafe { BNSnapshotHasUndo(self.as_raw()) }
}
/// Get the first parent of the snapshot, or None if it has no parents
pub fn first_parent(&self) -> Option<Snapshot> {
let result = unsafe { BNGetSnapshotFirstParent(self.as_raw()) };
ptr::NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) })
}
/// Get a list of all parent snapshots of the snapshot
pub fn parents(&self) -> Array<Snapshot> {
let mut count = 0;
let result = unsafe { BNGetSnapshotParents(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get a list of all child snapshots of the snapshot
pub fn children(&self) -> Array<Snapshot> {
let mut count = 0;
let result = unsafe { BNGetSnapshotChildren(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get a buffer of the raw data at the time of the snapshot
pub fn file_contents(&self) -> Option<DataBuffer> {
self.has_contents().then(|| unsafe {
let result = BNGetSnapshotFileContents(self.as_raw());
assert!(!result.is_null());
DataBuffer::from_raw(result)
})
}
/// Get a hash of the data at the time of the snapshot
pub fn file_contents_hash(&self) -> Option<DataBuffer> {
self.has_contents().then(|| unsafe {
let result = BNGetSnapshotFileContentsHash(self.as_raw());
assert!(!result.is_null());
DataBuffer::from_raw(result)
})
}
/// Get a list of undo entries at the time of the snapshot
pub fn undo_entries(&self) -> Array<UndoEntry> {
assert!(self.has_undo());
let mut count = 0;
let result = unsafe { BNGetSnapshotUndoEntries(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn undo_entries_with_progress<F: FnMut(usize, usize) -> bool>(
&self,
mut progress: F,
) -> Array<UndoEntry> {
assert!(self.has_undo());
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
let mut count = 0;
let result = unsafe {
BNGetSnapshotUndoEntriesWithProgress(
self.as_raw(),
ctxt,
Some(cb_progress::<F>),
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get the backing kvs data with snapshot fields
pub fn read_data(&self) -> KeyValueStore {
let result = unsafe { BNReadSnapshotData(self.as_raw()) };
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
}
pub fn read_data_with_progress<F: FnMut(usize, usize) -> bool>(
&self,
mut progress: F,
) -> KeyValueStore {
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
let result =
unsafe { BNReadSnapshotDataWithProgress(self.as_raw(), ctxt, Some(cb_progress::<F>)) };
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
}
pub fn undo_data(&self) -> DataBuffer {
let result = unsafe { BNGetSnapshotUndoData(self.as_raw()) };
assert!(!result.is_null());
DataBuffer::from_raw(result)
}
pub fn store_data<F: FnMut(usize, usize) -> bool>(
&self,
data: KeyValueStore,
mut progress: F,
) -> bool {
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
unsafe { BNSnapshotStoreData(self.as_raw(), data.as_raw(), ctxt, Some(cb_progress::<F>)) }
}
/// Determine if this snapshot has another as an ancestor
pub fn has_ancestor(self, other: &Snapshot) -> bool {
unsafe { BNSnapshotHasAncestor(self.as_raw(), other.as_raw()) }
}
}
impl Clone for Snapshot {
fn clone(&self) -> Self {
unsafe { Self::from_raw(ptr::NonNull::new(BNNewSnapshotReference(self.as_raw())).unwrap()) }
}
}
impl Drop for Snapshot {
fn drop(&mut self) {
unsafe { BNFreeSnapshot(self.as_raw()) }
}
}
impl CoreArrayProvider for Snapshot {
type Raw = *mut BNSnapshot;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for Snapshot {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSnapshotList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
#[repr(transparent)]
pub struct KeyValueStore {
handle: ptr::NonNull<BNKeyValueStore>,
}
impl KeyValueStore {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNKeyValueStore>) -> Self {
Self { handle }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNKeyValueStore {
&mut *self.handle.as_ptr()
}
/// Get a list of all keys stored in the kvs
pub fn keys(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetKeyValueStoreKeys(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get the value for a single key
pub fn value<S: BnStrCompatible>(&self, key: S) -> Option<DataBuffer> {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let result = unsafe { BNGetKeyValueStoreBuffer(self.as_raw(), key_ptr) };
ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result))
}
/// Set the value for a single key
pub fn set_value<S: BnStrCompatible>(&self, key: S, value: &DataBuffer) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNSetKeyValueStoreBuffer(self.as_raw(), key_ptr, value.as_raw()) }
}
/// Get the stored representation of the kvs
pub fn serialized_data(&self) -> DataBuffer {
let result = unsafe { BNGetKeyValueStoreSerializedData(self.as_raw()) };
assert!(!result.is_null());
DataBuffer::from_raw(result)
}
/// Begin storing new keys into a namespace
pub fn begin_namespace<S: BnStrCompatible>(&self, name: S) {
let name_raw = name.into_bytes_with_nul();
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNBeginKeyValueStoreNamespace(self.as_raw(), name_ptr) }
}
/// End storing new keys into a namespace
pub fn end_namespace(&self) {
unsafe { BNEndKeyValueStoreNamespace(self.as_raw()) }
}
/// If the kvs is empty
pub fn empty(&self) -> bool {
unsafe { BNIsKeyValueStoreEmpty(self.as_raw()) }
}
/// Number of values in the kvs
pub fn value_size(&self) -> usize {
unsafe { BNGetKeyValueStoreValueSize(self.as_raw()) }
}
/// Length of serialized data
pub fn data_size(&self) -> usize {
unsafe { BNGetKeyValueStoreDataSize(self.as_raw()) }
}
/// Size of all data in storage
pub fn value_storage_size(self) -> usize {
unsafe { BNGetKeyValueStoreValueStorageSize(self.as_raw()) }
}
/// Number of namespaces pushed with begin_namespace
pub fn namespace_size(self) -> usize {
unsafe { BNGetKeyValueStoreNamespaceSize(self.as_raw()) }
}
}
impl Clone for KeyValueStore {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewKeyValueStoreReference(self.as_raw())).unwrap())
}
}
}
impl Drop for KeyValueStore {
fn drop(&mut self) {
unsafe { BNFreeKeyValueStore(self.as_raw()) }
}
}
#[repr(transparent)]
pub struct UndoEntry {
handle: ptr::NonNull<BNUndoEntry>,
}
impl UndoEntry {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNUndoEntry>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoEntry) -> &Self {
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoEntry {
&mut *self.handle.as_ptr()
}
pub fn id(&self) -> BnString {
let result = unsafe { BNUndoEntryGetId(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn actions(&self) -> Array<UndoAction> {
let mut count = 0;
let result = unsafe { BNUndoEntryGetActions(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn time(&self) -> SystemTime {
let m = Duration::from_secs(unsafe { BNUndoEntryGetTimestamp(self.as_raw()) });
UNIX_EPOCH + m
}
}
impl Clone for UndoEntry {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewUndoEntryReference(self.as_raw())).unwrap())
}
}
}
impl Drop for UndoEntry {
fn drop(&mut self) {
unsafe { BNFreeUndoEntry(self.as_raw()) }
}
}
impl CoreArrayProvider for UndoEntry {
type Raw = *mut BNUndoEntry;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for UndoEntry {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeUndoEntryList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
#[repr(transparent)]
pub struct UndoAction {
handle: ptr::NonNull<BNUndoAction>,
}
impl UndoAction {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNUndoAction>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoAction) -> &Self {
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoAction {
&mut *self.handle.as_ptr()
}
pub fn summary_text(&self) -> BnString {
let result = unsafe { BNUndoActionGetSummaryText(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn summary(&self) -> Array<InstructionTextToken> {
let mut count = 0;
let result = unsafe { BNUndoActionGetSummary(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
}
impl Clone for UndoAction {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewUndoActionReference(self.as_raw())).unwrap())
}
}
}
impl Drop for UndoAction {
fn drop(&mut self) {
unsafe { BNFreeUndoAction(self.as_raw()) }
}
}
impl CoreArrayProvider for UndoAction {
type Raw = *mut BNUndoAction;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for UndoAction {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeUndoActionList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
unsafe extern "C" fn cb_progress<F: FnMut(usize, usize) -> bool>(
ctxt: *mut ffi::c_void,
arg1: usize,
arg2: usize,
) -> bool {
let ctxt: &mut F = &mut *(ctxt as *mut F);
ctxt(arg1, arg2)
}
unsafe extern "C" fn cb_progress_nop(_ctxt: *mut ffi::c_void, _arg1: usize, _arg2: usize) -> bool {
true
}

View File

@@ -17,9 +17,10 @@
use binaryninjacore_sys::*;
use std::ffi::c_void;
use std::ptr;
use std::slice;
use crate::string::BnString;
pub struct DataBuffer(*mut BNDataBuffer);
impl DataBuffer {
@@ -31,10 +32,6 @@ impl DataBuffer {
}
pub fn get_data(&self) -> &[u8] {
if self.0.is_null() {
// TODO : Change the default value and remove this
return &[];
}
let buffer = unsafe { BNGetDataBufferContents(self.0) };
if buffer.is_null() {
&[]
@@ -43,6 +40,65 @@ impl DataBuffer {
}
}
pub fn get_data_at(&self, offset: usize) -> &[u8] {
let len = self.len();
if offset > len {
panic!();
}
let slice_len = len - offset;
let buffer = unsafe { BNGetDataBufferContentsAt(self.0, offset) };
if buffer.is_null() {
&[]
} else {
unsafe { slice::from_raw_parts(buffer as *const _, slice_len) }
}
}
/// Create a copy of a especified part of the data
pub fn get_slice(&self, start: usize, len: usize) -> Option<Self> {
if start + len > self.len() {
return None;
}
let ptr = unsafe { BNGetDataBufferSlice(self.0, start, len) };
(!ptr.is_null()).then(|| Self(ptr))
}
/// change the size of the allocated data, if new size is bigger data is
/// need to be initialized
pub unsafe fn set_len(&mut self, len: usize) {
unsafe { BNSetDataBufferLength(self.0, len) }
}
/// set the size to 0
pub fn clear(&self) {
unsafe { BNClearDataBuffer(self.0) }
}
/// Copy the contents of `src` into `dst`
pub fn assign(dst: &mut Self, src: &Self) {
unsafe { BNAssignDataBuffer(dst.0, src.0) }
}
/// Concat the contents of `src` into `dst`
pub fn append(dst: &mut Self, src: &Self) {
unsafe { BNAppendDataBuffer(dst.0, src.0) }
}
/// concat the contents of `data` into self
pub fn append_data(&self, data: &[u8]) {
unsafe { BNAppendDataBufferContents(self.0, data.as_ptr() as *const c_void, data.len()) }
}
/// Return the byte at `offset`
pub unsafe fn byte_at(&self, offset: usize) -> u8 {
unsafe { BNGetDataBufferByte(self.0, offset) }
}
/// Set the value of the byte at `offset`
pub unsafe fn set_byte_at(&mut self, offset: usize, byte: u8) {
unsafe { BNSetDataBufferByte(self.0, offset, byte) }
}
pub fn set_data(&mut self, data: &[u8]) {
unsafe {
BNSetDataBufferContents(
@@ -53,12 +109,48 @@ impl DataBuffer {
}
}
pub fn to_escaped_string(&self, null_terminates: bool) -> BnString {
unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates)) }
}
pub fn from_escaped_string(value: &BnString) -> Self {
Self(unsafe { BNDecodeEscapedString(value.as_raw()) })
}
pub fn to_base64(&self) -> BnString {
unsafe { BnString::from_raw(BNDataBufferToBase64(self.0)) }
}
pub fn from_base64(value: &BnString) -> Self {
Self(unsafe { BNDecodeBase64(value.as_raw()) })
}
pub fn zlib_compress(&self) -> Self {
Self(unsafe { BNZlibCompress(self.0) })
}
pub fn zlib_decompress(&self) -> Self {
Self(unsafe { BNZlibDecompress(self.0) })
}
pub fn lzma_decompress(&self) -> Self {
Self(unsafe { BNLzmaDecompress(self.0) })
}
pub fn lzma2_decompress(&self) -> Self {
Self(unsafe { BNLzma2Decompress(self.0) })
}
pub fn xz_decompress(&self) -> Self {
Self(unsafe { BNXzDecompress(self.0) })
}
pub fn len(&self) -> usize {
unsafe { BNGetDataBufferLength(self.0) }
}
pub fn is_empty(&self) -> bool {
unsafe { BNGetDataBufferLength(self.0) == 0 }
self.len() == 0
}
pub fn new(data: &[u8]) -> Result<Self, ()> {
@@ -71,19 +163,16 @@ impl DataBuffer {
}
}
// TODO : delete this
impl Default for DataBuffer {
fn default() -> Self {
DataBuffer::from_raw(ptr::null_mut())
Self(unsafe { BNCreateDataBuffer([].as_ptr() as *const c_void, 0) })
}
}
impl Drop for DataBuffer {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
BNFreeDataBuffer(self.0);
}
unsafe {
BNFreeDataBuffer(self.0);
}
}
}
@@ -93,3 +182,152 @@ impl Clone for DataBuffer {
Self::from_raw(unsafe { BNDuplicateDataBuffer(self.0) })
}
}
impl TryFrom<&[u8]> for DataBuffer {
type Error = ();
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
DataBuffer::new(value)
}
}
impl AsRef<[u8]> for DataBuffer {
fn as_ref(&self) -> &[u8] {
self.get_data()
}
}
impl std::borrow::Borrow<[u8]> for DataBuffer {
fn borrow(&self) -> &[u8] {
self.as_ref()
}
}
macro_rules! data_buffer_index {
($range:ty, $output:ty) => {
impl std::ops::Index<$range> for DataBuffer {
type Output = $output;
fn index(&self, index: $range) -> &Self::Output {
&self.get_data()[index]
}
}
};
}
data_buffer_index!(usize, u8);
data_buffer_index!(std::ops::Range<usize>, [u8]);
data_buffer_index!(std::ops::RangeInclusive<usize>, [u8]);
data_buffer_index!(std::ops::RangeTo<usize>, [u8]);
data_buffer_index!(std::ops::RangeFull, [u8]);
impl PartialEq for DataBuffer {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for DataBuffer {}
impl PartialOrd for DataBuffer {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.as_ref().cmp(other.as_ref()))
}
}
impl Ord for DataBuffer {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_ref().cmp(other.as_ref())
}
}
#[cfg(test)]
mod test {
use super::DataBuffer;
const DUMMY_DATA_0: &[u8] = b"0123456789\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x09\xFF";
const DUMMY_DATA_1: &[u8] = b"qwertyuiopasdfghjkl\xE7zxcvbnm\x00\x01\x00";
#[test]
fn get_slice() {
let data = DataBuffer::new(DUMMY_DATA_0).unwrap();
let slice = data.get_slice(9, 10).unwrap();
assert_eq!(slice.get_data(), &DUMMY_DATA_0[9..19]);
}
#[test]
fn set_len_write() {
let mut data = DataBuffer::default();
assert_eq!(data.get_data(), &[]);
unsafe { data.set_len(DUMMY_DATA_0.len()) };
assert_eq!(data.len(), DUMMY_DATA_0.len());
let mut contents = DUMMY_DATA_0.to_vec();
data.set_data(&contents);
// modify the orinal contents, to make sure DataBuffer copied the data
// and is not using the original pointer
contents.as_mut_slice().fill(0x55);
drop(contents);
assert_eq!(data.get_data(), &DUMMY_DATA_0[..]);
// make sure the new len truncate the original data
unsafe { data.set_len(13) };
assert_eq!(data.get_data(), &DUMMY_DATA_0[..13]);
data.clear();
assert_eq!(data.get_data(), &[]);
}
#[test]
fn assign_append() {
let mut dst = DataBuffer::new(DUMMY_DATA_0).unwrap();
let mut src = DataBuffer::new(DUMMY_DATA_1).unwrap();
DataBuffer::assign(&mut dst, &src);
assert_eq!(dst.get_data(), DUMMY_DATA_1);
assert_eq!(src.get_data(), DUMMY_DATA_1);
// overwrite the src, to make sure that src is copied to dst, and not
// moved into it
src.set_data(DUMMY_DATA_0);
assert_eq!(dst.get_data(), DUMMY_DATA_1);
assert_eq!(src.get_data(), DUMMY_DATA_0);
DataBuffer::append(&mut dst, &src);
let result: Vec<_> = DUMMY_DATA_1.iter().chain(DUMMY_DATA_0).copied().collect();
assert_eq!(dst.get_data(), &result);
assert_eq!(src.get_data(), DUMMY_DATA_0);
src.set_data(DUMMY_DATA_1);
assert_eq!(src.get_data(), DUMMY_DATA_1);
assert_eq!(dst.get_data(), &result);
}
#[test]
fn to_from_formats() {
let data = DataBuffer::new(DUMMY_DATA_0).unwrap();
let escaped = data.to_escaped_string(false);
let unescaped = DataBuffer::from_escaped_string(&escaped);
drop(escaped);
let escaped_part = data.to_escaped_string(true);
let unescaped_part = DataBuffer::from_escaped_string(&escaped_part);
drop(escaped_part);
let part = &DUMMY_DATA_0[0..DUMMY_DATA_0
.iter()
.position(|x| *x == 0)
.unwrap_or(DUMMY_DATA_0.len())];
assert_eq!(data.get_data(), DUMMY_DATA_0);
assert_eq!(unescaped.get_data(), DUMMY_DATA_0);
assert_eq!(unescaped_part.get_data(), part);
let escaped = data.to_base64();
let unescaped = DataBuffer::from_base64(&escaped);
drop(escaped);
assert_eq!(data.get_data(), DUMMY_DATA_0);
assert_eq!(unescaped.get_data(), DUMMY_DATA_0);
let compressed = data.zlib_compress();
let decompressed = compressed.zlib_decompress();
drop(compressed);
assert_eq!(data.get_data(), DUMMY_DATA_0);
assert_eq!(decompressed.get_data(), DUMMY_DATA_0);
}
}

View File

@@ -27,7 +27,7 @@
//! And finally calling `binaryninja::debuginfo::DebugInfoParser::register` to register it with the core.
//!
//! Here's a minimal, complete example boilerplate-plugin:
//! ```
//! ```no_run
//! use binaryninja::{
//! binaryview::BinaryView,
//! debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
@@ -40,8 +40,9 @@
//! true
//! }
//!
//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box<dyn Fn(usize, usize) -> bool>) {
//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>) -> bool {
//! println!("Parsing info");
//! true
//! }
//! }
//!
@@ -53,11 +54,14 @@
//! ```
//!
//! `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:
//! ```
//! let valid_parsers = DebugInfoParser::parsers_for_view(bv);
//! let parser = valid_parsers[0];
//! let debug_info = parser.parse_debug_info(bv);
//! bv.apply_debug_info(debug_info);
//! ```no_run
//! # use binaryninja::debuginfo::DebugInfoParser;
//! # use binaryninja::binaryview::BinaryViewExt;
//! let bv = binaryninja::load("example").unwrap();
//! let valid_parsers = DebugInfoParser::parsers_for_view(&bv);
//! let parser = valid_parsers.get(0);
//! let debug_info = parser.parse_debug_info(&bv, &bv, None, None).unwrap();
//! bv.apply_debug_info(&debug_info);
//! ```
//!
//! Multiple debug-info parsers can manually contribute debug info for a binary view by simply calling `parse_debug_info` with the
@@ -71,10 +75,10 @@ use crate::{
platform::Platform,
rc::*,
string::{raw_to_string, BnStrCompatible, BnString},
types::{DataVariableAndName, NameAndType, Type},
types::{DataVariableAndName, NameAndType, NamedTypedVariable, Type},
};
use std::{hash::Hash, mem, os::raw::c_void, ptr, slice};
use std::{hash::Hash, os::raw::c_void, ptr, slice};
struct ProgressContext(Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>);
@@ -109,14 +113,14 @@ impl DebugInfoParser {
/// List all debug-info parsers
pub fn list() -> Array<DebugInfoParser> {
let mut count: usize = unsafe { mem::zeroed() };
let mut count = 0;
let raw_parsers = unsafe { BNGetDebugInfoParsers(&mut count as *mut _) };
unsafe { Array::new(raw_parsers, count, ()) }
}
/// Returns a list of debug-info parsers that are valid for the provided binary view
pub fn parsers_for_view(bv: &BinaryView) -> Array<DebugInfoParser> {
let mut count: usize = unsafe { mem::zeroed() };
let mut count = 0;
let raw_parsers = unsafe { BNGetDebugInfoParsersForView(bv.handle, &mut count as *mut _) };
unsafe { Array::new(raw_parsers, count, ()) }
}
@@ -269,12 +273,16 @@ impl ToOwned for DebugInfoParser {
impl CoreArrayProvider for DebugInfoParser {
type Raw = *mut BNDebugInfoParser;
type Context = ();
type Wrapped<'a> = Guard<'a, DebugInfoParser>;
}
unsafe impl CoreOwnedArrayProvider for DebugInfoParser {
unsafe impl CoreArrayProviderInner for DebugInfoParser {
unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
BNFreeDebugInfoParserList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Self { handle: *raw }, context)
}
}
///////////////////////
@@ -293,6 +301,7 @@ pub struct DebugFunctionInfo {
address: u64,
platform: Option<Ref<Platform>>,
components: Vec<String>,
local_variables: Vec<NamedTypedVariable>,
}
impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
@@ -302,6 +311,15 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
.map(|component| raw_to_string(*component as *const _).unwrap())
.collect();
let local_variables: Vec<NamedTypedVariable> = unsafe { slice::from_raw_parts(raw.localVariables, raw.localVariableN) }
.iter()
.map(|local_variable| {
unsafe {
NamedTypedVariable::from_raw(local_variable)
}
})
.collect();
Self {
short_name: raw_to_string(raw.shortName),
full_name: raw_to_string(raw.fullName),
@@ -318,11 +336,13 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
Some(unsafe { Platform::ref_from_raw(raw.platform) })
},
components,
local_variables,
}
}
}
impl DebugFunctionInfo {
#[allow(clippy::too_many_arguments)]
pub fn new(
short_name: Option<String>,
full_name: Option<String>,
@@ -331,18 +351,17 @@ impl DebugFunctionInfo {
address: Option<u64>,
platform: Option<Ref<Platform>>,
components: Vec<String>,
local_variables: Vec<NamedTypedVariable>,
) -> Self {
Self {
short_name,
full_name,
raw_name,
type_,
address: match address {
Some(address) => address,
_ => 0,
},
address: address.unwrap_or(0),
platform,
components,
local_variables,
}
}
}
@@ -376,7 +395,7 @@ impl DebugInfo {
}
/// Returns a generator of all types provided by a named DebugInfoParser
pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<NameAndType<String>> {
pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<Ref<NameAndType>> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
@@ -387,10 +406,10 @@ impl DebugInfo {
&mut count,
)
};
let result: Vec<NameAndType<String>> = unsafe {
let result: Vec<Ref<NameAndType>> = unsafe {
slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
.map(NameAndType::<String>::from_raw)
.map(|x| NameAndType::from_raw(x).to_owned())
.collect()
};
@@ -399,13 +418,13 @@ impl DebugInfo {
}
/// A generator of all types provided by DebugInfoParsers
pub fn types(&self) -> Vec<NameAndType<String>> {
pub fn types(&self) -> Vec<Ref<NameAndType>> {
let mut count: usize = 0;
let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) };
let result: Vec<NameAndType<String>> = unsafe {
let result: Vec<Ref<NameAndType>> = unsafe {
slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
.map(NameAndType::<String>::from_raw)
.map(|x| NameAndType::from_raw(x).to_owned())
.collect()
};
@@ -416,7 +435,7 @@ impl DebugInfo {
/// Returns a generator of all functions provided by a named DebugInfoParser
pub fn functions_by_name<S: BnStrCompatible>(
&self,
parser_name: S,
parser_name: S
) -> Vec<DebugFunctionInfo> {
let parser_name = parser_name.into_bytes_with_nul();
@@ -758,30 +777,41 @@ impl DebugInfo {
let short_name_bytes = new_func.short_name.map(|name| name.into_bytes_with_nul());
let short_name = short_name_bytes
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| {
name.as_ptr() as _
});
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let full_name_bytes = new_func.full_name.map(|name| name.into_bytes_with_nul());
let full_name = full_name_bytes
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| {
name.as_ptr() as _
});
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let raw_name_bytes = new_func.raw_name.map(|name| name.into_bytes_with_nul());
let raw_name = raw_name_bytes
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| {
name.as_ptr() as _
});
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let mut components_array: Vec<*const ::std::os::raw::c_char> =
let mut components_array: Vec<*mut ::std::os::raw::c_char> =
Vec::with_capacity(new_func.components.len());
for component in &new_func.components {
components_array.push(component.as_ptr() as _);
}
let mut local_variables_array: Vec<BNVariableNameAndType> =
Vec::with_capacity(new_func.local_variables.len());
unsafe {
BNAddDebugFunction(
for component in &new_func.components {
components_array.push(BNAllocString(component.clone().into_bytes_with_nul().as_ptr() as _));
}
for local_variable in &new_func.local_variables {
local_variables_array.push(
BNVariableNameAndType {
var: local_variable.var.raw(),
autoDefined: local_variable.auto_defined,
typeConfidence: local_variable.ty.confidence,
name: BNAllocString(local_variable.name.clone().into_bytes_with_nul().as_ptr() as _),
type_: local_variable.ty.contents.handle,
}
);
}
let result = BNAddDebugFunction(
self.handle,
&mut BNDebugFunctionInfo {
shortName: short_name,
@@ -798,8 +828,19 @@ impl DebugInfo {
},
components: components_array.as_ptr() as _,
componentN: new_func.components.len(),
localVariables: local_variables_array.as_ptr() as _,
localVariableN: local_variables_array.len(),
},
)
);
for i in components_array {
BNFreeString(i);
}
for i in &local_variables_array {
BNFreeString(i.name);
}
result
}
}

View File

@@ -26,6 +26,49 @@ use crate::rc::*;
pub type Result<R> = result::Result<R, ()>;
pub fn demangle_llvm<S: BnStrCompatible>(
mangled_name: S,
simplify: bool,
) -> Result<Vec<String>> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleLLVM(
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_name,
&mut out_size,
simplify,
)
};
if !res || out_size == 0 {
let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
Ok(cstr) => cstr,
Err(_) => {
log::error!("demangle_llvm: failed to parse mangled name");
return Err(());
}
};
return Ok(vec![cstr.to_string_lossy().into_owned()]);
}
if out_name.is_null() {
log::error!("demangle_llvm: out_name is NULL");
return Err(());
}
let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
.iter()
.map(str::to_string)
.collect();
unsafe { BNFreeDemangledName(&mut out_name, out_size) };
Ok(names)
}
pub fn demangle_gnu3<S: BnStrCompatible>(
arch: &CoreArchitecture,
mangled_name: S,
@@ -33,8 +76,8 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
) -> Result<(Option<Ref<Type>>, Vec<String>)> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
let mut out_type: *mut BNType = unsafe { std::mem::zeroed() };
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
let mut out_type: *mut BNType = std::ptr::null_mut();
let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut();
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleGNU3(
@@ -73,7 +116,7 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
.iter()
.map(|name| name.to_string())
.map(str::to_string)
.collect();
unsafe { BNFreeDemangledName(&mut out_name, out_size) };
@@ -89,8 +132,8 @@ pub fn demangle_ms<S: BnStrCompatible>(
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
let mut out_type: *mut BNType = unsafe { std::mem::zeroed() };
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
let mut out_type: *mut BNType = std::ptr::null_mut();
let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut();
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleMS(

View File

@@ -16,12 +16,13 @@
use binaryninjacore_sys::*;
use crate::string::{BnStr, BnString};
use crate::string::BnString;
use crate::{BN_FULL_CONFIDENCE, BN_INVALID_EXPR};
use crate::rc::*;
use std::convert::From;
use std::ffi::CStr;
use std::mem;
use std::ptr;
@@ -72,7 +73,7 @@ pub type InstructionTextTokenContext = BNInstructionTextTokenContext;
// IndirectImportToken = 69,
// ExternalSymbolToken = 70,
#[repr(C)]
#[repr(transparent)]
pub struct InstructionTextToken(pub(crate) BNInstructionTextToken);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -98,11 +99,15 @@ pub enum InstructionTextTokenContents {
}
impl InstructionTextToken {
pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> Self {
Self(*raw)
pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> &Self {
mem::transmute(raw)
}
pub fn new(text: BnString, contents: InstructionTextTokenContents) -> Self {
pub(crate) fn into_raw(self) -> BNInstructionTextToken {
mem::ManuallyDrop::new(self).0
}
pub fn new(text: &str, contents: InstructionTextTokenContents) -> Self {
let (value, address) = match contents {
InstructionTextTokenContents::Integer(v) => (v, 0),
InstructionTextTokenContents::PossibleAddress(v)
@@ -149,7 +154,7 @@ impl InstructionTextToken {
InstructionTextToken(BNInstructionTextToken {
type_,
text: text.into_raw(),
text: BnString::new(text).into_raw(),
value,
width,
size: 0,
@@ -159,7 +164,7 @@ impl InstructionTextToken {
address,
typeNames: ptr::null_mut(),
namesCount: 0,
exprIndex: BN_INVALID_EXPR
exprIndex: BN_INVALID_EXPR,
})
}
@@ -171,8 +176,8 @@ impl InstructionTextToken {
self.0.context = context;
}
pub fn text(&self) -> &BnStr {
unsafe { BnStr::from_raw(self.0.text) }
pub fn text(&self) -> &str {
unsafe { CStr::from_ptr(self.0.text) }.to_str().unwrap()
}
pub fn contents(&self) -> InstructionTextTokenContents {
@@ -229,7 +234,7 @@ impl Default for InstructionTextToken {
address: 0,
typeNames: ptr::null_mut(),
namesCount: 0,
exprIndex: BN_INVALID_EXPR
exprIndex: BN_INVALID_EXPR,
})
}
}
@@ -248,19 +253,51 @@ impl Clone for InstructionTextToken {
confidence: 0xff,
typeNames: ptr::null_mut(),
namesCount: 0,
exprIndex: self.0.exprIndex
exprIndex: self.0.exprIndex,
})
}
}
// TODO : There is almost certainly a memory leak here - in the case where
// `impl CoreOwnedArrayProvider for InstructionTextToken` doesn't get triggered
// impl Drop for InstructionTextToken {
// fn drop(&mut self) {
// let _owned = unsafe { BnString::from_raw(self.0.text) };
// }
// }
impl Drop for InstructionTextToken {
fn drop(&mut self) {
if !self.0.text.is_null() {
let _owned = unsafe { BnString::from_raw(self.0.text) };
}
if !self.0.typeNames.is_null() && self.0.namesCount != 0 {
unsafe { BNFreeStringList(self.0.typeNames, self.0.namesCount) }
}
}
}
impl CoreArrayProvider for InstructionTextToken {
type Raw = BNInstructionTextToken;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for InstructionTextToken {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeInstructionText(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
core::mem::transmute(raw)
}
}
impl CoreArrayProvider for Array<InstructionTextToken> {
type Raw = BNInstructionTextLine;
type Context = ();
type Wrapped<'a> = mem::ManuallyDrop<Self>;
}
unsafe impl CoreArrayProviderInner for Array<InstructionTextToken> {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeInstructionTextLines(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ()))
}
}
#[repr(transparent)]
pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine);
impl DisassemblyTextLine {
@@ -289,7 +326,7 @@ impl DisassemblyTextLine {
unsafe {
std::slice::from_raw_parts::<BNInstructionTextToken>(self.0.tokens, self.0.count)
.iter()
.map(|&x| InstructionTextToken::from_raw(&x))
.map(|x| InstructionTextToken::from_raw(x).clone())
.collect()
}
}
@@ -306,10 +343,9 @@ impl std::fmt::Display for DisassemblyTextLine {
}
impl From<Vec<InstructionTextToken>> for DisassemblyTextLine {
fn from(mut tokens: Vec<InstructionTextToken>) -> Self {
tokens.shrink_to_fit();
fn from(tokens: Vec<InstructionTextToken>) -> Self {
let mut tokens: Box<[_]> = tokens.into();
assert!(tokens.len() == tokens.capacity());
// TODO: let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nightly feature
let tokens_pointer = tokens.as_mut_ptr();
let tokens_len = tokens.len();
@@ -344,12 +380,11 @@ impl From<Vec<InstructionTextToken>> for DisassemblyTextLine {
impl From<&Vec<&str>> for DisassemblyTextLine {
fn from(string_tokens: &Vec<&str>) -> Self {
let mut tokens: Vec<BNInstructionTextToken> = Vec::with_capacity(string_tokens.len());
tokens.extend(string_tokens.iter().map(|&token| {
InstructionTextToken::new(BnString::new(token), InstructionTextTokenContents::Text).0
}));
let mut tokens: Box<[BNInstructionTextToken]> = string_tokens
.iter()
.map(|&token| InstructionTextToken::new(token, InstructionTextTokenContents::Text).into_raw())
.collect();
assert!(tokens.len() == tokens.capacity());
// let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nighly feature
let tokens_pointer = tokens.as_mut_ptr();
let tokens_len = tokens.len();
@@ -413,12 +448,28 @@ impl Default for DisassemblyTextLine {
impl Drop for DisassemblyTextLine {
fn drop(&mut self) {
unsafe {
Vec::from_raw_parts(self.0.tokens, self.0.count, self.0.count);
if !self.0.tokens.is_null() {
let ptr = core::ptr::slice_from_raw_parts_mut(self.0.tokens, self.0.count);
let _ = unsafe { Box::from_raw(ptr) };
}
}
}
impl CoreArrayProvider for DisassemblyTextLine {
type Raw = BNDisassemblyTextLine;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for DisassemblyTextLine {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeDisassemblyTextLines(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
core::mem::transmute(raw)
}
}
pub type DisassemblyOption = BNDisassemblyOption;
#[derive(PartialEq, Eq, Hash)]

View File

@@ -1,11 +1,9 @@
use crate::rc::{
Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref, RefCountable,
};
use crate::rc::{Array, CoreArrayProvider, Guard, CoreArrayProviderInner, Ref, RefCountable};
use crate::settings::Settings;
use crate::string::{BnStr, BnStrCompatible, BnString};
use crate::string::{BnStrCompatible, BnString};
use binaryninjacore_sys::*;
use std::collections::HashMap;
use std::ffi::c_void;
use std::ffi::{c_void, CStr};
use std::os::raw::c_char;
use std::ptr::null_mut;
use std::slice;
@@ -63,19 +61,15 @@ impl DownloadProvider {
impl CoreArrayProvider for DownloadProvider {
type Raw = *mut BNDownloadProvider;
type Context = ();
type Wrapped<'a> = Guard<'a, DownloadProvider>;
}
unsafe impl CoreOwnedArrayProvider for DownloadProvider {
unsafe impl CoreArrayProviderInner for DownloadProvider {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeDownloadProviderList(raw);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for DownloadProvider {
type Wrapped = DownloadProvider;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
DownloadProvider::from_raw(*raw)
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(DownloadProvider::from_raw(*raw), &())
}
}
@@ -270,8 +264,8 @@ impl DownloadInstance {
.zip(response_header_values.iter())
{
response_headers.insert(
BnStr::from_raw(*key).to_string(),
BnStr::from_raw(*value).to_string(),
CStr::from_ptr(*key).to_str().unwrap().to_owned(),
CStr::from_ptr(*value).to_str().unwrap().to_owned(),
);
}
}

196
src/enterprise.rs Normal file
View File

@@ -0,0 +1,196 @@
use std::marker::PhantomData;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::rc::Array;
use crate::string::{BnStrCompatible, BnString};
pub fn server_username() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUsername()) }
}
pub fn server_url() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUrl()) }
}
pub fn set_server_url<S: BnStrCompatible>(url: S) -> Result<(), ()> {
let url = url.into_bytes_with_nul();
let result = unsafe {
binaryninjacore_sys::BNSetEnterpriseServerUrl(url.as_ref().as_ptr() as *const std::os::raw::c_char)
};
if result {
Ok(())
} else {
Err(())
}
}
pub fn server_name() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerName()) }
}
pub fn server_id() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerId()) }
}
pub fn server_version() -> u64 {
unsafe { binaryninjacore_sys::BNGetEnterpriseServerVersion() }
}
pub fn server_build_id() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerBuildId()) }
}
pub fn server_token() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerToken()) }
}
pub fn license_duration() -> Duration {
Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerLicenseDuration() })
}
pub fn license_expiration_time() -> SystemTime {
let m = Duration::from_secs(unsafe {
binaryninjacore_sys::BNGetEnterpriseServerLicenseExpirationTime()
});
UNIX_EPOCH + m
}
pub fn server_reservation_time_limit() -> Duration {
Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerReservationTimeLimit() })
}
pub fn is_server_floating_license() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerFloatingLicense() }
}
pub fn is_server_license_still_activated() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerLicenseStillActivated() }
}
pub fn authenticate_server_with_credentials<U, P>(username: U, password: P, remember: bool) -> bool
where
U: BnStrCompatible,
P: BnStrCompatible,
{
let username = username.into_bytes_with_nul();
let password = password.into_bytes_with_nul();
unsafe {
binaryninjacore_sys::BNAuthenticateEnterpriseServerWithCredentials(
username.as_ref().as_ptr() as *const std::os::raw::c_char,
password.as_ref().as_ptr() as *const std::os::raw::c_char,
remember,
)
}
}
pub fn authenticate_server_with_method<S: BnStrCompatible>(method: S, remember: bool) -> bool {
let method = method.into_bytes_with_nul();
unsafe {
binaryninjacore_sys::BNAuthenticateEnterpriseServerWithMethod(
method.as_ref().as_ptr() as *const std::os::raw::c_char,
remember,
)
}
}
pub fn connect_server() -> bool {
unsafe { binaryninjacore_sys::BNConnectEnterpriseServer() }
}
pub fn deauthenticate_server() -> bool {
unsafe { binaryninjacore_sys::BNDeauthenticateEnterpriseServer() }
}
pub fn cancel_server_authentication() {
unsafe { binaryninjacore_sys::BNCancelEnterpriseServerAuthentication() }
}
pub fn update_server_license(timeout: Duration) -> bool {
unsafe { binaryninjacore_sys::BNUpdateEnterpriseServerLicense(timeout.as_secs()) }
}
pub fn release_server_license() -> bool {
unsafe { binaryninjacore_sys::BNReleaseEnterpriseServerLicense() }
}
pub fn is_server_connected() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerConnected() }
}
pub fn is_server_authenticated() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerAuthenticated() }
}
pub fn is_server_initialized() -> bool {
unsafe { binaryninjacore_sys::BNIsEnterpriseServerInitialized() }
}
pub fn server_last_error() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerLastError()) }
}
pub fn server_authentication_methods() -> (Array<BnString>, Array<BnString>) {
let mut methods = core::ptr::null_mut();
let mut names = core::ptr::null_mut();
let count = unsafe {
binaryninjacore_sys::BNGetEnterpriseServerAuthenticationMethods(&mut methods, &mut names)
};
unsafe { (Array::new(methods, count, ()), Array::new(names, count, ())) }
}
// NOTE don't implement Clone, Copy, so each callback can only be
// register/unregistered only once
#[repr(transparent)]
#[derive(Debug)]
pub struct EnterpriseServerCallback<'a> {
handle: binaryninjacore_sys::BNEnterpriseServerCallbacks,
lifetime: PhantomData<&'a ()>,
}
pub fn register_license_changed_callback<'a, F: FnMut(bool) + 'a>(
callback: F,
) -> EnterpriseServerCallback<'a> {
unsafe extern "C" fn cb_license_status_changed<F: FnMut(bool)>(
ctxt: *mut ::std::os::raw::c_void,
still_valid: bool,
) {
let ctxt: &mut F = &mut *(ctxt as *mut F);
ctxt(still_valid)
}
let mut handle = binaryninjacore_sys::BNEnterpriseServerCallbacks {
context: Box::leak(Box::new(callback)) as *mut F as *mut core::ffi::c_void,
licenseStatusChanged: Some(cb_license_status_changed::<F>),
};
unsafe { binaryninjacore_sys::BNRegisterEnterpriseServerNotification(&mut handle) }
EnterpriseServerCallback {
handle,
lifetime: PhantomData,
}
}
pub fn unregister_license_changed_callback(mut callback_handle: EnterpriseServerCallback) {
unsafe {
binaryninjacore_sys::BNUnregisterEnterpriseServerNotification(&mut callback_handle.handle)
}
}
impl<'a> EnterpriseServerCallback<'a> {
/// register the license changed callback
pub fn register<F: FnMut(bool) + 'a>(callback: F) -> Self {
register_license_changed_callback(callback)
}
/// deregister the license changed callback, equivalent to drop the struct
pub fn deregister(self) {
// Nothing, just drop self
}
}
impl Drop for EnterpriseServerCallback<'_> {
fn drop(&mut self) {
unregister_license_changed_callback(EnterpriseServerCallback {
handle: self.handle,
lifetime: PhantomData,
})
}
}

209
src/externallibrary.rs Normal file
View File

@@ -0,0 +1,209 @@
use core::{ffi, mem, ptr};
use binaryninjacore_sys::*;
use crate::project::ProjectFile;
use crate::rc::{CoreArrayProvider, CoreArrayProviderInner};
use crate::string::{BnStrCompatible, BnString};
use crate::symbol::Symbol;
/// An ExternalLibrary is an abstraction for a library that is optionally backed
/// by a [ProjectFile].
#[repr(transparent)]
pub struct ExternalLibrary {
handle: ptr::NonNull<BNExternalLibrary>,
}
impl Drop for ExternalLibrary {
fn drop(&mut self) {
unsafe { BNFreeExternalLibrary(self.as_raw()) }
}
}
impl Clone for ExternalLibrary {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewExternalLibraryReference(self.as_raw())).unwrap())
}
}
}
impl ExternalLibrary {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNExternalLibrary>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNExternalLibrary) -> &Self {
assert!(!handle.is_null());
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNExternalLibrary {
&mut *self.handle.as_ptr()
}
/// Get the name of this external library
pub fn name(&self) -> BnString {
let result = unsafe { BNExternalLibraryGetName(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
/// Get the file backing this external library
pub fn backing_file(&self) -> Option<ProjectFile> {
let result = unsafe { BNExternalLibraryGetBackingFile(self.as_raw()) };
let handle = ptr::NonNull::new(result)?;
Some(unsafe { ProjectFile::from_raw(handle) })
}
/// Set the file backing this external library
pub fn set_backing_file(&self, file: Option<&ProjectFile>) {
let file_handle = file
.map(|x| unsafe {x.as_raw() as *mut _})
.unwrap_or(ptr::null_mut());
unsafe { BNExternalLibrarySetBackingFile(self.as_raw(), file_handle) }
}
}
impl CoreArrayProvider for ExternalLibrary {
type Raw = *mut BNExternalLibrary;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for ExternalLibrary {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeExternalLibraryList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
/// An ExternalLocation is an association from a source symbol in a binary view
/// to a target symbol and/or address in an [ExternalLibrary].
#[repr(transparent)]
pub struct ExternalLocation {
handle: ptr::NonNull<BNExternalLocation>,
}
impl Drop for ExternalLocation {
fn drop(&mut self) {
unsafe { BNFreeExternalLocation(self.as_raw()) }
}
}
impl Clone for ExternalLocation {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(
ptr::NonNull::new(BNNewExternalLocationReference(self.as_raw())).unwrap(),
)
}
}
}
impl ExternalLocation {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNExternalLocation>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNExternalLocation) -> &Self {
assert!(!handle.is_null());
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNExternalLocation {
&mut *self.handle.as_ptr()
}
/// Get the source symbol for this ExternalLocation
pub fn source_symbol(&self) -> Symbol {
let result = unsafe { BNExternalLocationGetSourceSymbol(self.as_raw()) };
assert!(!result.is_null());
unsafe { Symbol::from_raw(result) }
}
/// Get the ExternalLibrary that this ExternalLocation targets
pub fn library(&self) -> Option<ExternalLibrary> {
let result = unsafe { BNExternalLocationGetExternalLibrary(self.as_raw()) };
let handle = ptr::NonNull::new(result)?;
Some(unsafe { ExternalLibrary::from_raw(handle) })
}
/// Set the ExternalLibrary that this ExternalLocation targets
pub fn set_external_library(&self, lib: Option<&ExternalLibrary>) {
let lib_handle = lib
.map(|x| unsafe {x.as_raw() as *mut _})
.unwrap_or(ptr::null_mut());
unsafe { BNExternalLocationSetExternalLibrary(self.as_raw(), lib_handle) }
}
/// Check if this ExternalLocation has a target address
pub fn has_target_address(&self) -> bool {
unsafe { BNExternalLocationHasTargetAddress(self.as_raw()) }
}
/// Check if this ExternalLocation has a target symbol
pub fn has_target_symbol(&self) -> bool {
unsafe { BNExternalLocationHasTargetSymbol(self.as_raw()) }
}
/// Get the address pointed to by this ExternalLocation, if any
pub fn target_address(&self) -> Option<u64> {
self.has_target_address()
.then(|| unsafe { BNExternalLocationGetTargetAddress(self.as_raw()) })
}
/// Set the address pointed to by this ExternalLocation.
/// ExternalLocations must have a valid target address and/or symbol set.
pub fn set_target_address(&self, mut address: Option<u64>) -> bool {
let address_ptr = address
.as_mut()
.map(|x| x as *mut u64)
.unwrap_or(ptr::null_mut());
unsafe { BNExternalLocationSetTargetAddress(self.as_raw(), address_ptr) }
}
/// Get the symbol pointed to by this ExternalLocation, if any
pub fn target_symbol(&self) -> Option<BnString> {
self.has_target_symbol().then(|| unsafe {
let result = BNExternalLocationGetTargetSymbol(self.as_raw());
assert!(!result.is_null());
BnString::from_raw(result)
})
}
/// Set the symbol pointed to by this ExternalLocation.
/// ExternalLocations must have a valid target address and/or symbol set.
pub fn set_target_symbol<S: BnStrCompatible>(&self, symbol: Option<S>) -> bool {
let symbol = symbol
.map(|x| x.into_bytes_with_nul().as_ref().as_ptr() as *const ffi::c_char)
.unwrap_or(ptr::null_mut());
unsafe {
BNExternalLocationSetTargetSymbol(
self.as_raw(),
symbol,
)
}
}
}
impl CoreArrayProvider for ExternalLocation {
type Raw = *mut BNExternalLocation;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for ExternalLocation {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeExternalLocationList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}

View File

@@ -27,6 +27,7 @@ use binaryninjacore_sys::{
BNIsAnalysisChanged,
BNIsBackedByDatabase,
//BNSetFileMetadataNavigationHandler,
BNGetFileMetadataDatabase,
BNIsFileModified,
BNMarkFileModified,
BNMarkFileSaved,
@@ -40,8 +41,10 @@ use binaryninjacore_sys::{
BNSetFilename,
BNUndo,
};
use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
use crate::binaryview::BinaryView;
use crate::database::Database;
use crate::rc::*;
use crate::string::*;
@@ -204,16 +207,28 @@ impl FileMetadata {
}
}
pub fn create_database<S: BnStrCompatible>(&self, filename: S) -> bool {
pub fn create_database<S: BnStrCompatible>(
&self,
filename: S,
progress_func: Option<fn(usize, usize) -> bool>,
) -> bool {
let filename = filename.into_bytes_with_nul();
let filename_ptr = filename.as_ref().as_ptr() as *mut _;
let raw = "Raw".into_bytes_with_nul();
let raw_ptr = raw.as_ptr() as *mut _;
unsafe {
BNCreateDatabase(
BNGetFileViewOfType(self.handle, raw.as_ptr() as *mut _),
filename.as_ref().as_ptr() as *mut _,
ptr::null_mut() as *mut _,
)
let handle = unsafe { BNGetFileViewOfType(self.handle, raw_ptr) };
match progress_func {
None => unsafe { BNCreateDatabase(handle, filename_ptr, ptr::null_mut()) },
Some(func) => unsafe {
BNCreateDatabaseWithProgress(
handle,
filename_ptr,
func as *mut libc::c_void,
Some(cb_progress_func),
ptr::null_mut(),
)
},
}
}
@@ -244,17 +259,25 @@ impl FileMetadata {
}
}
pub fn open_database<S: BnStrCompatible>(&self, filename: S) -> Result<Ref<BinaryView>, ()> {
pub fn open_database<S: BnStrCompatible>(
&self,
filename: S,
progress_func: Option<fn(usize, usize) -> bool>,
) -> Result<Ref<BinaryView>, ()> {
let filename = filename.into_bytes_with_nul();
let filename_ptr = filename.as_ref().as_ptr() as *mut _;
let view = unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) };
// TODO : add optional progress function
// let view = match progress_func {
// None => BNOpenExistingDatabase(self.handle, filename_ptr),
// _ => BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: progress_func(cur, total)))
// };
let view = match progress_func {
None => unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) },
Some(func) => unsafe {
BNOpenExistingDatabaseWithProgress(
self.handle,
filename_ptr,
func as *mut libc::c_void,
Some(cb_progress_func),
)
},
};
if view.is_null() {
Err(())
@@ -262,6 +285,12 @@ impl FileMetadata {
Ok(unsafe { BinaryView::from_raw(view) })
}
}
/// Get the current database
pub fn database(&self) -> Option<Database> {
let result = unsafe { BNGetFileMetadataDatabase(self.handle) };
ptr::NonNull::new(result).map(|handle| unsafe { Database::from_raw(handle) })
}
}
impl ToOwned for FileMetadata {
@@ -284,7 +313,11 @@ unsafe impl RefCountable for FileMetadata {
}
}
/*
BNCreateDatabase,
BNCreateDatabaseWithProgress,
*/
unsafe extern "C" fn cb_progress_func(
ctxt: *mut ::std::os::raw::c_void,
progress: usize,
total: usize,
) -> bool {
let func: fn(usize, usize) -> bool = core::mem::transmute(ctxt);
func(progress, total)
}

View File

@@ -68,7 +68,7 @@ impl<'a> FlowGraphNode<'a> {
unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) }
}
pub fn set_disassembly_lines(&self, lines: &'a Vec<DisassemblyTextLine>) {
pub fn set_disassembly_lines(&self, lines: &'a [DisassemblyTextLine]) {
unsafe {
BNSetFlowGraphNodeLines(self.handle, lines.as_ptr() as *mut _, lines.len());
// BNFreeDisassemblyTextLines(lines.as_ptr() as *mut _, lines.len()); // Shouldn't need...would be a double free?
@@ -79,7 +79,7 @@ impl<'a> FlowGraphNode<'a> {
let lines = lines
.iter()
.map(|&line| DisassemblyTextLine::from(&vec![line]))
.collect();
.collect::<Vec<_>>();
self.set_disassembly_lines(&lines);
}
@@ -114,8 +114,6 @@ impl<'a> ToOwned for FlowGraphNode<'a> {
}
}
// TODO : FlowGraph are RefCounted objects, this needs to be changed to only return Refs to FlowGraph
#[derive(PartialEq, Eq, Hash)]
pub struct FlowGraph {
pub(crate) handle: *mut BNFlowGraph,
@@ -126,8 +124,8 @@ impl FlowGraph {
Self { handle: raw }
}
pub fn new() -> Self {
unsafe { FlowGraph::from_raw(BNCreateFlowGraph()) }
pub fn new() -> Ref<Self> {
unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) }
}
pub fn append(&self, node: &FlowGraphNode) -> usize {

File diff suppressed because it is too large Load Diff

View File

@@ -14,9 +14,8 @@
use crate::{
binaryview,
metadata::Metadata,
rc::{self, Ref},
string::BnStrCompatible,
rc,
string::{BnStrCompatible, IntoJson},
};
use std::env;
@@ -59,6 +58,11 @@ fn binja_path() -> PathBuf {
let path = CStr::from_ptr(info.dli_fname);
let path = OsStr::from_bytes(path.to_bytes());
let mut path = PathBuf::from(path);
while path.is_symlink() {
path = path
.read_link()
.expect("Failed to find libbinaryninjacore path!");
}
path.pop();
path
@@ -95,13 +99,18 @@ pub fn shutdown() {
unsafe { binaryninjacore_sys::BNShutdown() };
}
pub fn is_shutdown_requested() -> bool {
unsafe { binaryninjacore_sys::BNIsShutdownRequested() }
}
/// Prelued-postlued helper function (calls [`init`] and [`shutdown`] for you)
/// ```rust
/// ```no_run
/// # use binaryninja::binaryview::BinaryViewExt;
/// binaryninja::headless::script_helper(|| {
/// binaryninja::load("/bin/cat")
/// .expect("Couldn't open `/bin/cat`")
/// .iter()
/// .for_each(|func| println!(" `{}`", func.symbol().full_name()));
/// let cat = binaryninja::load("/bin/cat").expect("Couldn't open `/bin/cat`");
/// for function in cat.functions().iter() {
/// println!(" `{}`", function.symbol().full_name());
/// }
/// });
/// ```
pub fn script_helper(func: fn()) {
@@ -119,7 +128,7 @@ impl Session {
Self {}
}
/// ```rust
/// ```no_run
/// let headless_session = binaryninja::headless::Session::new();
///
/// let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
@@ -128,18 +137,23 @@ impl Session {
crate::load(filename)
}
/// ```rust
/// let settings = [("analysis.linearSweep.autorun", false)].into();
/// ```no_run
/// use binaryninja::{metadata::Metadata, rc::Ref};
/// use std::collections::HashMap;
///
/// let settings: Ref<Metadata> = HashMap::from([
/// ("analysis.linearSweep.autorun", false.into()),
/// ]).into();
/// let headless_session = binaryninja::headless::Session::new();
///
/// let bv = headless_session.load_with_options("/bin/cat", true, Some(settings))
/// .expect("Couldn't open `/bin/cat`");
/// ```
pub fn load_with_options(
pub fn load_with_options<O: IntoJson>(
&self,
filename: &str,
update_analysis_and_wait: bool,
options: Option<Ref<Metadata>>,
options: Option<O>,
) -> Option<rc::Ref<binaryview::BinaryView>> {
crate::load_with_options(filename, update_analysis_and_wait, options)
}

View File

@@ -2,8 +2,10 @@ use std::hash::{Hash, Hasher};
use binaryninjacore_sys::BNFreeHighLevelILFunction;
use binaryninjacore_sys::BNGetHighLevelILBasicBlockList;
use binaryninjacore_sys::BNGetHighLevelILIndexForInstruction;
use binaryninjacore_sys::BNGetHighLevelILInstructionCount;
use binaryninjacore_sys::BNGetHighLevelILOwnerFunction;
use binaryninjacore_sys::BNGetHighLevelILRootExpr;
use binaryninjacore_sys::BNGetHighLevelILSSAForm;
use binaryninjacore_sys::BNHighLevelILFunction;
use binaryninjacore_sys::BNNewHighLevelILFunctionReference;
@@ -52,6 +54,29 @@ impl HighLevelILFunction {
self.instruction_from_idx(expr_idx).lift()
}
pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> HighLevelILInstruction {
HighLevelILInstruction::new(self.as_non_ast(), unsafe {
BNGetHighLevelILIndexForInstruction(self.handle, instr_idx)
})
}
pub fn lifted_instruction_from_instruction_idx(
&self,
instr_idx: usize,
) -> HighLevelILLiftedInstruction {
self.instruction_from_instruction_idx(instr_idx).lift()
}
pub fn root(&self) -> HighLevelILInstruction {
HighLevelILInstruction::new(self.as_ast(), unsafe {
BNGetHighLevelILRootExpr(self.handle)
})
}
pub fn lifted_root(&self) -> HighLevelILLiftedInstruction {
self.root().lift()
}
pub fn instruction_count(&self) -> usize {
unsafe { BNGetHighLevelILInstructionCount(self.handle) }
}
@@ -81,6 +106,22 @@ impl HighLevelILFunction {
unsafe { Array::new(blocks, count, context) }
}
pub fn as_ast(&self) -> Ref<HighLevelILFunction> {
Self {
handle: self.handle,
full_ast: true,
}
.to_owned()
}
pub fn as_non_ast(&self) -> Ref<HighLevelILFunction> {
Self {
handle: self.handle,
full_ast: false,
}
.to_owned()
}
}
impl ToOwned for HighLevelILFunction {

View File

@@ -1,12 +1,10 @@
use binaryninjacore_sys::BNFromVariableIdentifier;
use binaryninjacore_sys::BNGetHighLevelILByIndex;
use binaryninjacore_sys::BNHighLevelILOperation;
use crate::architecture::CoreIntrinsic;
use crate::operand_iter::OperandIter;
use crate::rc::Ref;
use crate::types::{
ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable,
};
use crate::types::{ConstantData, RegisterValue, RegisterValueType, SSAVariable, Variable};
use super::operation::*;
use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind};
@@ -15,6 +13,8 @@ use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLifted
pub struct HighLevelILInstruction {
pub function: Ref<HighLevelILFunction>,
pub address: u64,
pub index: usize,
pub size: usize,
pub kind: HighLevelILInstructionKind,
}
@@ -144,8 +144,8 @@ pub enum HighLevelILInstructionKind {
DoWhileSsa(WhileSsa),
}
impl HighLevelILInstruction {
pub(crate) fn new(function: Ref<HighLevelILFunction>, idx: usize) -> Self {
let op = unsafe { BNGetHighLevelILByIndex(function.handle, idx, function.full_ast) };
pub(crate) fn new(function: Ref<HighLevelILFunction>, index: usize) -> Self {
let op = unsafe { BNGetHighLevelILByIndex(function.handle, index, function.full_ast) };
use BNHighLevelILOperation::*;
use HighLevelILInstructionKind as Op;
let kind = match op.operation {
@@ -610,8 +610,8 @@ impl HighLevelILInstruction {
body: op.operands[1] as usize,
}),
HLIL_DO_WHILE => Op::DoWhile(While {
condition: op.operands[0] as usize,
body: op.operands[1] as usize,
body: op.operands[0] as usize,
condition: op.operands[1] as usize,
}),
HLIL_WHILE_SSA => Op::WhileSsa(WhileSsa {
condition_phi: op.operands[0] as usize,
@@ -627,6 +627,8 @@ impl HighLevelILInstruction {
Self {
function,
address: op.address,
index,
size: op.size,
kind,
}
}
@@ -809,11 +811,11 @@ impl HighLevelILInstruction {
cond_false: self.lift_operand(op.cond_false),
}),
Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic {
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
params: self.lift_instruction_list(op.first_param, op.num_params),
}),
IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa {
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
params: self.lift_instruction_list(op.first_param, op.num_params),
dest_memory: op.dest_memory,
src_memory: op.src_memory,
@@ -875,6 +877,8 @@ impl HighLevelILInstruction {
HighLevelILLiftedInstruction {
function: self.function.clone(),
address: self.address,
index: self.index,
size: self.size,
kind,
}
}
@@ -979,7 +983,7 @@ fn get_float(value: u64, size: usize) -> f64 {
}
fn get_var(id: u64) -> Variable {
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
unsafe { Variable::from_identifier(id) }
}
fn get_member_index(idx: u64) -> Option<usize> {

View File

@@ -1,7 +1,9 @@
use super::{operation::*, HighLevelILFunction};
use super::operation::*;
use super::HighLevelILFunction;
use crate::architecture::CoreIntrinsic;
use crate::rc::Ref;
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
use crate::types::{ConstantData, SSAVariable, Variable};
#[derive(Clone)]
pub enum HighLevelILLiftedOperand {
@@ -11,7 +13,7 @@ pub enum HighLevelILLiftedOperand {
Float(f64),
Int(u64),
IntList(Vec<u64>),
Intrinsic(ILIntrinsic),
Intrinsic(CoreIntrinsic),
Label(GotoLabel),
MemberIndex(Option<usize>),
Var(Variable),
@@ -23,6 +25,8 @@ pub enum HighLevelILLiftedOperand {
pub struct HighLevelILLiftedInstruction {
pub function: Ref<HighLevelILFunction>,
pub address: u64,
pub index: usize,
pub size: usize,
pub kind: HighLevelILLiftedInstructionKind,
}
@@ -153,6 +157,134 @@ pub enum HighLevelILLiftedInstructionKind {
}
impl HighLevelILLiftedInstruction {
pub fn name(&self) -> &'static str {
use HighLevelILLiftedInstructionKind::*;
match self.kind {
Nop => "Nop",
Break => "Break",
Continue => "Continue",
Noret => "Noret",
Unreachable => "Unreachable",
Bp => "Bp",
Undef => "Undef",
Unimpl => "Unimpl",
Adc(_) => "Adc",
Sbb(_) => "Sbb",
Rlc(_) => "Rlc",
Rrc(_) => "Rrc",
Add(_) => "Add",
Sub(_) => "Sub",
And(_) => "And",
Or(_) => "Or",
Xor(_) => "Xor",
Lsl(_) => "Lsl",
Lsr(_) => "Lsr",
Asr(_) => "Asr",
Rol(_) => "Rol",
Ror(_) => "Ror",
Mul(_) => "Mul",
MuluDp(_) => "MuluDp",
MulsDp(_) => "MulsDp",
Divu(_) => "Divu",
DivuDp(_) => "DivuDp",
Divs(_) => "Divs",
DivsDp(_) => "DivsDp",
Modu(_) => "Modu",
ModuDp(_) => "ModuDp",
Mods(_) => "Mods",
ModsDp(_) => "ModsDp",
CmpE(_) => "CmpE",
CmpNe(_) => "CmpNe",
CmpSlt(_) => "CmpSlt",
CmpUlt(_) => "CmpUlt",
CmpSle(_) => "CmpSle",
CmpUle(_) => "CmpUle",
CmpSge(_) => "CmpSge",
CmpUge(_) => "CmpUge",
CmpSgt(_) => "CmpSgt",
CmpUgt(_) => "CmpUgt",
TestBit(_) => "TestBit",
AddOverflow(_) => "AddOverflow",
Fadd(_) => "Fadd",
Fsub(_) => "Fsub",
Fmul(_) => "Fmul",
Fdiv(_) => "Fdiv",
FcmpE(_) => "FcmpE",
FcmpNe(_) => "FcmpNe",
FcmpLt(_) => "FcmpLt",
FcmpLe(_) => "FcmpLe",
FcmpGe(_) => "FcmpGe",
FcmpGt(_) => "FcmpGt",
FcmpO(_) => "FcmpO",
FcmpUo(_) => "FcmpUo",
ArrayIndex(_) => "ArrayIndex",
ArrayIndexSsa(_) => "ArrayIndexSsa",
Assign(_) => "Assign",
AssignMemSsa(_) => "AssignMemSsa",
AssignUnpack(_) => "AssignUnpack",
AssignUnpackMemSsa(_) => "AssignUnpackMemSsa",
Block(_) => "Block",
Call(_) => "Call",
Tailcall(_) => "Tailcall",
CallSsa(_) => "CallSsa",
Case(_) => "Case",
Const(_) => "Const",
ConstPtr(_) => "ConstPtr",
Import(_) => "Import",
ConstData(_) => "ConstData",
Deref(_) => "Deref",
AddressOf(_) => "AddressOf",
Neg(_) => "Neg",
Not(_) => "Not",
Sx(_) => "Sx",
Zx(_) => "Zx",
LowPart(_) => "LowPart",
BoolToInt(_) => "BoolToInt",
UnimplMem(_) => "UnimplMem",
Fsqrt(_) => "Fsqrt",
Fneg(_) => "Fneg",
Fabs(_) => "Fabs",
FloatToInt(_) => "FloatToInt",
IntToFloat(_) => "IntToFloat",
FloatConv(_) => "FloatConv",
RoundToInt(_) => "RoundToInt",
Floor(_) => "Floor",
Ceil(_) => "Ceil",
Ftrunc(_) => "Ftrunc",
DerefFieldSsa(_) => "DerefFieldSsa",
DerefSsa(_) => "DerefSsa",
ExternPtr(_) => "ExternPtr",
FloatConst(_) => "FloatConst",
For(_) => "For",
ForSsa(_) => "ForSsa",
Goto(_) => "Goto",
Label(_) => "Label",
If(_) => "If",
Intrinsic(_) => "Intrinsic",
IntrinsicSsa(_) => "IntrinsicSsa",
Jump(_) => "Jump",
MemPhi(_) => "MemPhi",
Ret(_) => "Ret",
Split(_) => "Split",
StructField(_) => "StructField",
DerefField(_) => "DerefField",
Switch(_) => "Switch",
Syscall(_) => "Syscall",
SyscallSsa(_) => "SyscallSsa",
Trap(_) => "Trap",
VarDeclare(_) => "VarDeclare",
Var(_) => "Var",
VarInit(_) => "VarInit",
VarInitSsa(_) => "VarInitSsa",
VarPhi(_) => "VarPhi",
VarSsa(_) => "VarSsa",
While(_) => "While",
DoWhile(_) => "DoWhile",
WhileSsa(_) => "WhileSsa",
DoWhileSsa(_) => "DoWhileSsa",
}
}
pub fn operands(&self) -> Vec<(&'static str, HighLevelILLiftedOperand)> {
use HighLevelILLiftedInstructionKind::*;
use HighLevelILLiftedOperand as Operand;

View File

@@ -1,22 +1,22 @@
use binaryninjacore_sys::BNGetGotoLabelName;
use crate::architecture::CoreIntrinsic;
use crate::function::Function;
use crate::rc::Ref;
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
use crate::string::BnString;
use crate::types::{ConstantData, SSAVariable, Variable};
use super::HighLevelILLiftedInstruction;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GotoLabel {
pub(crate) function: Ref<Function>,
pub(crate) target: u64,
pub target: u64,
}
impl GotoLabel {
pub fn name(&self) -> &str {
let raw_str = unsafe { BNGetGotoLabelName(self.function.handle, self.target) };
let c_str = unsafe { core::ffi::CStr::from_ptr(raw_str) };
c_str.to_str().unwrap()
pub fn name(&self) -> BnString {
unsafe { BnString::from_raw(BNGetGotoLabelName(self.function.handle, self.target)) }
}
}
@@ -320,7 +320,7 @@ pub struct Intrinsic {
}
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedIntrinsic {
pub intrinsic: ILIntrinsic,
pub intrinsic: CoreIntrinsic,
pub params: Vec<HighLevelILLiftedInstruction>,
}
@@ -335,7 +335,7 @@ pub struct IntrinsicSsa {
}
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedIntrinsicSsa {
pub intrinsic: ILIntrinsic,
pub intrinsic: CoreIntrinsic,
pub params: Vec<HighLevelILLiftedInstruction>,
pub dest_memory: u64,
pub src_memory: u64,

View File

@@ -16,12 +16,13 @@
use binaryninjacore_sys::*;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::path::PathBuf;
use crate::binaryview::BinaryView;
use crate::rc::Ref;
use crate::string::{BnStr, BnStrCompatible, BnString};
use crate::string::{BnStrCompatible, BnString};
pub fn get_text_line_input(prompt: &str, title: &str) -> Option<String> {
let mut value: *mut libc::c_char = std::ptr::null_mut();
@@ -295,7 +296,9 @@ impl FormInputBuilder {
result.type_ = BNFormInputFieldType::AddressFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
if let Some(view) = view {
result.view = view.handle;
// the view is being moved into result, there is no need to clone
// and drop is intentionally being avoided with `Ref::into_raw`
result.view = unsafe { Ref::into_raw(view) }.handle;
}
result.currentAddress = current_address.unwrap_or(0);
result.hasDefault = default.is_some();
@@ -448,8 +451,10 @@ impl FormInputBuilder {
///
/// This API is flexible and works both in the UI via a pop-up dialog and on the command-line.
///
/// ```
/// let responses = interaction::FormInputBuilder::new()
/// ```no_run
/// # use binaryninja::interaction::FormInputBuilder;
/// # use binaryninja::interaction::FormResponses;
/// let responses = FormInputBuilder::new()
/// .text_field("First Name", None)
/// .text_field("Last Name", None)
/// .choice_field(
@@ -466,15 +471,19 @@ impl FormInputBuilder {
/// .get_form_input("Form Title");
///
/// let food = match responses[2] {
/// Index(0) => "Pizza",
/// Index(1) => "Also Pizza",
/// Index(2) => "Also Pizza",
/// Index(3) => "Wrong Answer",
/// FormResponses::Index(0) => "Pizza",
/// FormResponses::Index(1) => "Also Pizza",
/// FormResponses::Index(2) => "Also Pizza",
/// FormResponses::Index(3) => "Wrong Answer",
/// _ => panic!("This person doesn't like pizza?!?"),
/// };
///
/// let interaction::FormResponses::String(last_name) = responses[0];
/// let interaction::FormResponses::String(first_name) = responses[1];
/// let FormResponses::String(last_name) = &responses[0] else {
/// unreachable!()
/// };
/// let FormResponses::String(first_name) = &responses[1] else {
/// unreachable!()
/// };
///
/// println!("{} {} likes {}", &first_name, &last_name, food);
/// ```
@@ -499,7 +508,10 @@ impl FormInputBuilder {
| BNFormInputFieldType::SaveFileNameFormField
| BNFormInputFieldType::DirectoryNameFormField => {
FormResponses::String(unsafe {
BnStr::from_raw(form_field.stringResult).to_string()
CStr::from_ptr(form_field.stringResult)
.to_str()
.unwrap()
.to_owned()
})
}

View File

@@ -49,7 +49,7 @@
//!
//! Create a new library (`cargo new --lib <plugin-name>`) and include the following in your `Cargo.toml`:
//!
//! ```
//! ```toml
//! [lib]
//! crate-type = ["cdylib"]
//!
@@ -73,21 +73,19 @@
//!
//! ### `main.rs`
//! Standalone binaries need to initialize Binary Ninja before they can work. You can do this through [`headless::Session`], [`headless::script_helper`], or [`headless::init()`] at start and [`headless::shutdown()`] at shutdown.
//! ```rust
//! fn main() {
//! // This loads all the core architecture, platform, etc plugins
//! // Standalone executables need to call this, but plugins do not
//! let headless_session = binaryninja::headless::Session::new();
//! ```no_run
//! // This loads all the core architecture, platform, etc plugins
//! // Standalone executables need to call this, but plugins do not
//! let headless_session = binaryninja::headless::Session::new();
//!
//! println!("Loading binary...");
//! let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
//! println!("Loading binary...");
//! let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
//!
//! // Your code here...
//! }
//! // Your code here...
//! ```
//!
//! ### `Cargo.toml`
//! ```
//! ```toml
//! [dependencies]
//! binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
//! ```
@@ -135,11 +133,15 @@ pub mod binarywriter;
pub mod callingconvention;
pub mod command;
pub mod custombinaryview;
pub mod database;
pub mod databuffer;
pub mod debuginfo;
pub mod demangle;
pub mod disassembly;
pub mod enterprise;
pub mod component;
pub mod downloadprovider;
pub mod externallibrary;
pub mod fileaccessor;
pub mod filemetadata;
pub mod flowgraph;
@@ -154,6 +156,7 @@ pub mod logger;
pub mod metadata;
pub mod mlil;
pub mod platform;
pub mod project;
pub mod rc;
pub mod references;
pub mod relocation;
@@ -164,7 +167,10 @@ pub mod string;
pub mod symbol;
pub mod tags;
pub mod templatesimplifier;
pub mod typelibrary;
pub mod typearchive;
pub mod types;
pub mod update;
use std::path::PathBuf;
@@ -173,8 +179,8 @@ pub use binaryninjacore_sys::BNEndianness as Endianness;
use binaryview::BinaryView;
use metadata::Metadata;
use metadata::MetadataType;
use rc::Ref;
use string::BnStrCompatible;
use string::IntoJson;
// Commented out to suppress unused warnings
// const BN_MAX_INSTRUCTION_LENGTH: u64 = 256;
@@ -199,14 +205,14 @@ const BN_INVALID_EXPR: usize = usize::MAX;
/// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`]
pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::BinaryView>> {
let filename = filename.into_bytes_with_nul();
let metadata = Metadata::new_of_type(MetadataType::KeyValueDataType);
let options = "\x00";
let handle = unsafe {
binaryninjacore_sys::BNLoadFilename(
filename.as_ref().as_ptr() as *mut _,
true,
options.as_ptr() as *mut core::ffi::c_char,
None,
metadata.handle,
)
};
@@ -219,31 +225,83 @@ pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::Binar
/// The main way to open and load files (with options) into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`]
///
/// ```rust
/// let settings = [("analysis.linearSweep.autorun", false)].into();
/// <div class="warning">Strict JSON doesn't support single quotes for strings, so you'll need to either use a raw strings (<code>f#"{"setting": "value"}"#</code>) or escape double quotes (<code>"{\"setting\": \"value\"}"</code>). Or use <code>serde_json::json</code>.</div>
///
/// let bv = binaryninja::load_with_options("/bin/cat", true, Some(settings))
/// ```no_run
/// # // Mock implementation of json! macro for documentation purposes
/// # macro_rules! json {
/// # ($($arg:tt)*) => {
/// # stringify!($($arg)*)
/// # };
/// # }
/// use binaryninja::{metadata::Metadata, rc::Ref};
/// use std::collections::HashMap;
///
/// let bv = binaryninja::load_with_options("/bin/cat", true, Some(json!("analysis.linearSweep.autorun": false).to_string()))
/// .expect("Couldn't open `/bin/cat`");
/// ```
pub fn load_with_options<S: BnStrCompatible>(
pub fn load_with_options<S: BnStrCompatible, O: IntoJson>(
filename: S,
update_analysis_and_wait: bool,
options: Option<Ref<Metadata>>,
options: Option<O>,
) -> Option<rc::Ref<binaryview::BinaryView>> {
let filename = filename.into_bytes_with_nul();
let options_or_default = if let Some(opt) = options {
opt
opt.get_json_string()
.ok()?
.into_bytes_with_nul()
.as_ref()
.to_vec()
} else {
Metadata::new_of_type(MetadataType::KeyValueDataType)
.get_json_string()
.ok()?
.as_ref()
.to_vec()
};
let handle = unsafe {
binaryninjacore_sys::BNLoadFilename(
filename.as_ref().as_ptr() as *mut _,
update_analysis_and_wait,
options_or_default.as_ptr() as *mut core::ffi::c_char,
None,
)
};
if handle.is_null() {
None
} else {
Some(unsafe { BinaryView::from_raw(handle) })
}
}
pub fn load_view<O: IntoJson>(
bv: &BinaryView,
update_analysis_and_wait: bool,
options: Option<O>,
) -> Option<rc::Ref<binaryview::BinaryView>> {
let options_or_default = if let Some(opt) = options {
opt.get_json_string()
.ok()?
.into_bytes_with_nul()
.as_ref()
.to_vec()
} else {
Metadata::new_of_type(MetadataType::KeyValueDataType)
.get_json_string()
.ok()?
.as_ref()
.to_vec()
};
let handle = unsafe {
binaryninjacore_sys::BNLoadBinaryView(
bv.handle as *mut _,
update_analysis_and_wait,
options_or_default.as_ptr() as *mut core::ffi::c_char,
None,
options_or_default.as_ref().handle,
)
};
@@ -377,6 +435,75 @@ pub fn version() -> string::BnString {
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetVersionString()) }
}
pub fn build_id() -> u32 {
unsafe { binaryninjacore_sys::BNGetBuildId() }
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct VersionInfo {
pub major: u32,
pub minor: u32,
pub build: u32,
pub channel: string::BnString,
}
pub fn version_info() -> VersionInfo {
let info_raw = unsafe { binaryninjacore_sys::BNGetVersionInfo() };
VersionInfo {
major: info_raw.major,
minor: info_raw.minor,
build: info_raw.build,
channel: unsafe { string::BnString::from_raw(info_raw.channel) },
}
}
pub fn serial_number() -> string::BnString {
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetSerialNumber()) }
}
pub fn is_license_validated() -> bool {
unsafe { binaryninjacore_sys::BNIsLicenseValidated() }
}
pub fn licensed_user_email() -> string::BnString {
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetLicensedUserEmail()) }
}
pub fn license_count() -> i32 {
unsafe { binaryninjacore_sys::BNGetLicenseCount() }
}
pub fn set_license<S: string::BnStrCompatible>(license: S) {
let license = license.into_bytes_with_nul();
let license_slice = license.as_ref();
unsafe { binaryninjacore_sys::BNSetLicense(license_slice.as_ptr() as *const std::os::raw::c_char) }
}
pub fn product() -> string::BnString {
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProduct()) }
}
pub fn product_type() -> string::BnString {
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProductType()) }
}
pub fn license_expiration_time() -> std::time::SystemTime {
let m = std::time::Duration::from_secs(unsafe {
binaryninjacore_sys::BNGetLicenseExpirationTime()
});
std::time::UNIX_EPOCH + m
}
pub fn is_ui_enabled() -> bool {
unsafe { binaryninjacore_sys::BNIsUIEnabled() }
}
pub fn is_database<S: string::BnStrCompatible>(filename: S) -> bool {
let filename = filename.into_bytes_with_nul();
let filename_slice = filename.as_ref();
unsafe { binaryninjacore_sys::BNIsDatabase(filename_slice.as_ptr() as *const std::os::raw::c_char) }
}
pub fn plugin_abi_version() -> u32 {
binaryninjacore_sys::BN_CURRENT_CORE_ABI_VERSION
}

View File

@@ -415,18 +415,14 @@ impl std::fmt::Display for LinearDisassemblyLine {
impl CoreArrayProvider for LinearDisassemblyLine {
type Raw = BNLinearDisassemblyLine;
type Context = ();
type Wrapped<'a> = Guard<'a, LinearDisassemblyLine>;
}
unsafe impl CoreOwnedArrayProvider for LinearDisassemblyLine {
unsafe impl CoreArrayProviderInner for LinearDisassemblyLine {
unsafe fn free(raw: *mut BNLinearDisassemblyLine, count: usize, _context: &()) {
BNFreeLinearDisassemblyLines(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for LinearDisassemblyLine {
type Wrapped = Guard<'a, LinearDisassemblyLine>;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(LinearDisassemblyLine::from_raw(raw), _context)
}
}

View File

@@ -136,6 +136,8 @@ where
LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)),
LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)),
LLIL_REG_SPLIT => ExprInfo::RegSplit(Operation::new(function, op)),
LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)),
LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)),
LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)),
@@ -273,6 +275,7 @@ where
LLIL_LOAD => ExprInfo::Load(Operation::new(self.function, op)),
LLIL_POP => ExprInfo::Pop(Operation::new(self.function, op)),
LLIL_REG => ExprInfo::Reg(Operation::new(self.function, op)),
LLIL_REG_SPLIT => ExprInfo::RegSplit(Operation::new(self.function, op)),
LLIL_FLAG => ExprInfo::Flag(Operation::new(self.function, op)),
LLIL_FLAG_BIT => ExprInfo::FlagBit(Operation::new(self.function, op)),
LLIL_FLAG_COND => ExprInfo::FlagCond(Operation::new(self.function, op)), // TODO lifted only
@@ -327,6 +330,7 @@ where
match op.operation {
LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(self.function, op)),
LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => ExprInfo::Reg(Operation::new(self.function, op)),
LLIL_REG_SPLIT_SSA => ExprInfo::RegSplit(Operation::new(self.function, op)),
LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(self.function, op)),
LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(self.function, op)),
_ => common_info(self.function, op),
@@ -383,6 +387,7 @@ where
Load(Operation<'func, A, M, F, operation::Load>),
Pop(Operation<'func, A, M, F, operation::Pop>),
Reg(Operation<'func, A, M, F, operation::Reg>),
RegSplit(Operation<'func, A, M, F, operation::RegSplit>),
Const(Operation<'func, A, M, F, operation::Const>),
ConstPtr(Operation<'func, A, M, F, operation::Const>),
Flag(Operation<'func, A, M, F, operation::Flag>),
@@ -595,6 +600,8 @@ where
Reg(ref op) => &op.op,
RegSplit(ref op) => &op.op,
Flag(ref op) => &op.op,
FlagBit(ref op) => &op.op,
@@ -648,6 +655,8 @@ where
Reg(ref op) => op.flag_write(),
RegSplit(ref op) => op.flag_write(),
Flag(ref op) => op.flag_write(),
FlagBit(ref op) => op.flag_write(),
@@ -735,6 +744,31 @@ where
}
}
RegSplit(ref op) => {
let low_reg = op.low_reg();
let high_reg = op.high_reg();
let size = op.size();
let low_size = match low_reg {
Register::Temp(_) => Some(size),
Register::ArchReg(ref r) if r.info().size() != size => Some(size),
_ => None,
};
let high_size = match high_reg {
Register::Temp(_) => Some(size),
Register::ArchReg(ref r) if r.info().size() != size => Some(size),
_ => None,
};
match (low_size, high_size) {
(Some(ls), Some(hs)) => write!(f, "{:?}.{}:{:?}.{}", high_reg, hs, low_reg, ls),
(Some(ls), None) => write!(f, "{:?}:{:?}.{}", high_reg, low_reg, ls),
(None, Some(hs)) => write!(f, "{:?}.{}:{:?}", high_reg, hs, low_reg),
_ => write!(f, "{:?}:{:?}", high_reg, low_reg),
}
}
Flag(ref _op) => write!(f, "flag"), // TODO
FlagBit(ref _op) => write!(f, "flag_bit"), // TODO

View File

@@ -99,7 +99,7 @@ where
let expr_idx =
unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) };
let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) };
return op.address;
op.address
}
pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> {

View File

@@ -607,6 +607,26 @@ where
A: 'a + Architecture,
R: ExpressionResultType,
{
pub fn from_expr(expr: Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>) -> Self {
use binaryninjacore_sys::BNGetLowLevelILByIndex;
let instr = unsafe {
BNGetLowLevelILByIndex(expr.function.handle, expr.expr_idx)
};
ExpressionBuilder {
function: expr.function,
op: instr.operation,
size: instr.size,
flags: instr.flags,
op1: instr.operands[0],
op2: instr.operands[1],
op3: instr.operands[2],
op4: instr.operands[3],
_ty: PhantomData
}
}
pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self {
// TODO verify valid id
self.flags = flag_write.id();
@@ -1016,6 +1036,43 @@ where
Expression::new(self, expr_idx)
}
pub fn reg_split<H: Into<Register<A::Register>>, L: Into<Register<A::Register>>>(
&self,
size: usize,
hi_reg: H,
lo_reg: L,
) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT;
// TODO verify valid id
let hi_reg = match hi_reg.into() {
Register::ArchReg(r) => r.id(),
Register::Temp(r) => 0x8000_0000 | r,
};
// TODO verify valid id
let lo_reg = match lo_reg.into() {
Register::ArchReg(r) => r.id(),
Register::Temp(r) => 0x8000_0000 | r,
};
let expr_idx = unsafe {
BNLowLevelILAddExpr(
self.handle,
LLIL_REG_SPLIT,
size,
0,
hi_reg as u64,
lo_reg as u64,
0,
0,
)
};
Expression::new(self, expr_idx)
}
pub fn set_reg<'a, R, E>(
&'a self,
size: usize,

View File

@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use binaryninjacore_sys::BNLowLevelILInstruction;
use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction};
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::mem;
@@ -89,10 +90,10 @@ pub struct Syscall;
pub struct Intrinsic;
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Intrinsic>
where
A: 'func + Architecture,
M: FunctionMutability,
V: NonSSAVariant,
where
A: 'func + Architecture,
M: FunctionMutability,
V: NonSSAVariant,
{
// TODO: Support register and expression lists
pub fn intrinsic(&self) -> Option<A::Intrinsic> {
@@ -289,6 +290,62 @@ where
}
}
// LLIL_REG_SPLIT
pub struct RegSplit;
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, RegSplit>
where
A: 'func + Architecture,
M: FunctionMutability,
V: NonSSAVariant,
{
pub fn size(&self) -> usize {
self.op.size
}
pub fn low_reg(&self) -> Register<A::Register> {
let raw_id = self.op.operands[0] as u32;
if raw_id >= 0x8000_0000 {
Register::Temp(raw_id & 0x7fff_ffff)
} else {
self.function
.arch()
.register_from_id(raw_id)
.map(Register::ArchReg)
.unwrap_or_else(|| {
error!(
"got garbage register from LLIL_REG @ 0x{:x}",
self.op.address
);
Register::Temp(0)
})
}
}
pub fn high_reg(&self) -> Register<A::Register> {
let raw_id = self.op.operands[1] as u32;
if raw_id >= 0x8000_0000 {
Register::Temp(raw_id & 0x7fff_ffff)
} else {
self.function
.arch()
.register_from_id(raw_id)
.map(Register::ArchReg)
.unwrap_or_else(|| {
error!(
"got garbage register from LLIL_REG @ 0x{:x}",
self.op.address
);
Register::Temp(0)
})
}
}
}
// LLIL_FLAG, LLIL_FLAG_SSA
pub struct Flag;
@@ -312,6 +369,36 @@ where
// LLIL_JUMP_TO
pub struct JumpTo;
struct TargetListIter<'func, A, M, F>
where
A: 'func + Architecture,
M: FunctionMutability,
F: FunctionForm,
{
function: &'func Function<A, M, F>,
cursor: BNLowLevelILInstruction,
cursor_operand: usize,
}
impl<'func, A, M, F> TargetListIter<'func, A, M, F>
where
A: 'func + Architecture,
M: FunctionMutability,
F: FunctionForm,
{
fn next(&mut self) -> u64 {
if self.cursor_operand >= 3 {
self.cursor = unsafe {
BNGetLowLevelILByIndex(self.function.handle, self.cursor.operands[3] as usize)
};
self.cursor_operand = 0;
}
let result = self.cursor.operands[self.cursor_operand];
self.cursor_operand += 1;
result
}
}
impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo>
where
A: 'func + Architecture,
@@ -321,7 +408,26 @@ where
pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
Expression::new(self.function, self.op.operands[0] as usize)
}
// TODO target list
pub fn target_list(&self) -> BTreeMap<u64, usize> {
let mut result = BTreeMap::new();
let count = self.op.operands[1] as usize / 2;
let mut list = TargetListIter {
function: self.function,
cursor: unsafe {
BNGetLowLevelILByIndex(self.function.handle, self.op.operands[2] as usize)
},
cursor_operand: 0,
};
for _ in 0..count {
let value = list.next();
let target = list.next() as usize;
result.insert(value, target);
}
result
}
}
// LLIL_CALL, LLIL_CALL_SSA
@@ -382,12 +488,20 @@ where
}
}
pub fn true_target_idx(&self) -> usize {
self.op.operands[1] as usize
}
pub fn false_target(&self) -> Instruction<'func, A, M, F> {
Instruction {
function: self.function,
instr_idx: self.op.operands[2] as usize,
}
}
pub fn false_target_idx(&self) -> usize {
self.op.operands[2] as usize
}
}
// LLIL_GOTO
@@ -405,6 +519,10 @@ where
instr_idx: self.op.operands[0] as usize,
}
}
pub fn target_idx(&self) -> usize {
self.op.operands[0] as usize
}
}
// LLIL_FLAG_COND
@@ -640,6 +758,7 @@ impl OperationArguments for SetFlag {}
impl OperationArguments for Load {}
impl OperationArguments for Store {}
impl OperationArguments for Reg {}
impl OperationArguments for RegSplit {}
impl OperationArguments for Flag {}
impl OperationArguments for FlagBit {}
impl OperationArguments for Jump {}

View File

@@ -2,7 +2,7 @@
//! To use logging in your script, do something like:
//!
//! ```
//! ```no-test
//! use binaryninja::logger;
//! use log::{info, LevelFilter};
//!
@@ -15,7 +15,7 @@
//!
//! or
//!
//!```
//!```no-test
//! use binaryninja::logger;
//! use log::{info, LevelFilter};
//!
@@ -29,12 +29,11 @@
//! ```
//!
use crate::string::BnStr;
pub use binaryninjacore_sys::BNLogLevel as Level;
use binaryninjacore_sys::{BNLogListener, BNUpdateLogListeners};
use log;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
struct Logger;
@@ -84,7 +83,7 @@ pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {
}
pub trait LogListener: 'static + Sync {
fn log(&self, session: usize, level: Level, msg: &BnStr, logger_name: &BnStr, tid: usize);
fn log(&self, session: usize, level: Level, msg: &CStr, logger_name: &CStr, tid: usize);
fn level(&self) -> Level;
fn close(&self) {}
}
@@ -147,8 +146,8 @@ extern "C" fn cb_log<L>(
listener.log(
session,
level,
BnStr::from_raw(msg),
BnStr::from_raw(logger_name),
CStr::from_ptr(msg),
CStr::from_ptr(logger_name),
tid,
);
})

View File

@@ -1,7 +1,5 @@
use crate::rc::{
Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable,
};
use crate::string::{BnStrCompatible, BnString};
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString, IntoJson};
use binaryninjacore_sys::*;
use std::collections::HashMap;
use std::os::raw::c_char;
@@ -168,6 +166,19 @@ impl Metadata {
}
}
pub fn get_json_string(&self) -> Result<BnString, ()> {
match self.get_type() {
MetadataType::StringDataType => {
let ptr: *mut c_char = unsafe { BNMetadataGetJsonString(self.handle) };
if ptr.is_null() {
return Err(());
}
Ok(unsafe { BnString::from_raw(ptr) })
}
_ => Err(()),
}
}
pub fn get_raw(&self) -> Result<Vec<u8>, ()> {
match self.get_type() {
MetadataType::RawDataType => {
@@ -335,18 +346,14 @@ unsafe impl RefCountable for Metadata {
impl CoreArrayProvider for Metadata {
type Raw = *mut BNMetadata;
type Context = ();
type Wrapped<'a> = Guard<'a, Metadata>;
}
unsafe impl CoreOwnedArrayProvider for Metadata {
unsafe impl CoreArrayProviderInner for Metadata {
unsafe fn free(raw: *mut *mut BNMetadata, _count: usize, _context: &()) {
BNFreeMetadataArray(raw);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for Metadata {
type Wrapped = Guard<'a, Metadata>;
unsafe fn wrap_raw(raw: &'a *mut BNMetadata, context: &'a ()) -> Guard<'a, Metadata> {
unsafe fn wrap_raw<'a>(raw: &'a *mut BNMetadata, context: &'a ()) -> Self::Wrapped<'a> {
Guard::new(Metadata::from_raw(*raw), context)
}
}
@@ -403,12 +410,6 @@ impl From<&str> for Ref<Metadata> {
}
}
impl<T: Into<Ref<Metadata>>> From<&T> for Ref<Metadata> {
fn from(value: &T) -> Self {
value.into()
}
}
impl From<&Vec<u8>> for Ref<Metadata> {
fn from(value: &Vec<u8>) -> Self {
unsafe { Metadata::ref_from_raw(BNCreateMetadataRawData(value.as_ptr(), value.len())) }
@@ -441,16 +442,15 @@ impl From<&Array<Metadata>> for Ref<Metadata> {
impl<S: BnStrCompatible> From<HashMap<S, Ref<Metadata>>> for Ref<Metadata> {
fn from(value: HashMap<S, Ref<Metadata>>) -> Self {
let mut key_refs: Vec<S::Result> = vec![];
let mut keys: Vec<*const c_char> = vec![];
let mut values: Vec<*mut BNMetadata> = vec![];
for (k, v) in value.into_iter() {
key_refs.push(k.into_bytes_with_nul());
values.push(v.as_ref().handle);
}
for k in &key_refs {
keys.push(k.as_ref().as_ptr() as *const c_char);
}
let data: Vec<(S::Result, Ref<Metadata>)> = value
.into_iter()
.map(|(k, v)| (k.into_bytes_with_nul(), v))
.collect();
let mut keys: Vec<*const c_char> = data
.iter()
.map(|(k, _)| k.as_ref().as_ptr() as *const c_char)
.collect();
let mut values: Vec<*mut BNMetadata> = data.iter().map(|(_, v)| v.handle).collect();
unsafe {
Metadata::ref_from_raw(BNCreateMetadataValueStore(
@@ -462,19 +462,21 @@ impl<S: BnStrCompatible> From<HashMap<S, Ref<Metadata>>> for Ref<Metadata> {
}
}
impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>> From<&[(S, T)]> for Ref<Metadata> {
impl<S, T> From<&[(S, T)]> for Ref<Metadata>
where
S: BnStrCompatible + Copy,
for<'a> &'a T: Into<Ref<Metadata>>,
{
fn from(value: &[(S, T)]) -> Self {
let mut key_refs: Vec<S::Result> = vec![];
let mut keys: Vec<*const c_char> = vec![];
let mut values: Vec<*mut BNMetadata> = vec![];
for (k, v) in value.iter() {
key_refs.push(k.into_bytes_with_nul());
let value_metadata: Ref<Metadata> = v.into();
values.push(value_metadata.handle);
}
for k in &key_refs {
keys.push(k.as_ref().as_ptr() as *const c_char);
}
let data: Vec<(S::Result, Ref<Metadata>)> = value
.iter()
.map(|(k, v)| (k.into_bytes_with_nul(), v.into()))
.collect();
let mut keys: Vec<*const c_char> = data
.iter()
.map(|(k, _)| k.as_ref().as_ptr() as *const c_char)
.collect();
let mut values: Vec<*mut BNMetadata> = data.iter().map(|(_, v)| v.handle).collect();
unsafe {
Metadata::ref_from_raw(BNCreateMetadataValueStore(
@@ -486,29 +488,15 @@ impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>> From<&[(S, T)]> for Ref<
}
}
impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>, const N: usize> From<[(S, T); N]>
for Ref<Metadata>
impl<S, T, const N: usize> From<[(S, T); N]> for Ref<Metadata>
where
S: BnStrCompatible + Copy,
for<'a> &'a T: Into<Ref<Metadata>>,
{
fn from(value: [(S, T); N]) -> Self {
let mut key_refs: Vec<S::Result> = vec![];
let mut keys: Vec<*const c_char> = vec![];
let mut values: Vec<*mut BNMetadata> = vec![];
for (k, v) in value.into_iter() {
key_refs.push(k.into_bytes_with_nul());
let value_metadata: Ref<Metadata> = v.into();
values.push(value_metadata.handle);
}
for k in &key_refs {
keys.push(k.as_ref().as_ptr() as *const c_char);
}
unsafe {
Metadata::ref_from_raw(BNCreateMetadataValueStore(
keys.as_mut_ptr(),
values.as_mut_ptr(),
keys.len(),
))
}
let slice = &value[..];
// use the `impl From<&[(S, T)]>`
slice.into()
}
}
@@ -714,3 +702,17 @@ impl TryFrom<&Metadata> for HashMap<String, Ref<Metadata>> {
.map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
}
}
impl IntoJson for &Metadata {
type Output = BnString;
fn get_json_string(self) -> Result<BnString, ()> {
Metadata::get_json_string(self)
}
}
impl IntoJson for Ref<Metadata> {
type Output = BnString;
fn get_json_string(self) -> Result<BnString, ()> {
Metadata::get_json_string(&self)
}
}

View File

@@ -1,18 +1,18 @@
use core::hash::{Hash, Hasher};
use std::ffi::c_char;
use binaryninjacore_sys::BNFreeMediumLevelILFunction;
use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList;
use binaryninjacore_sys::BNGetMediumLevelILInstructionCount;
use binaryninjacore_sys::BNGetMediumLevelILOwnerFunction;
use binaryninjacore_sys::BNGetMediumLevelILSSAForm;
use binaryninjacore_sys::BNMediumLevelILFunction;
use binaryninjacore_sys::BNMediumLevelILGetInstructionStart;
use binaryninjacore_sys::BNNewMediumLevelILFunctionReference;
use binaryninjacore_sys::*;
use crate::architecture::CoreArchitecture;
use crate::basicblock::BasicBlock;
use crate::function::Function;
use crate::function::Location;
use crate::rc::{Array, Ref, RefCountable};
use crate::disassembly::DisassemblySettings;
use crate::flowgraph::FlowGraph;
use crate::function::{Function, Location};
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
use crate::string::BnStrCompatible;
use crate::types::{
Conf, PossibleValueSet, RegisterValue, SSAVariable, Type, UserVariableValues, Variable,
};
use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction};
@@ -65,6 +65,19 @@ impl MediumLevelILFunction {
self.instruction_from_idx(expr_idx).lift()
}
pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> MediumLevelILInstruction {
MediumLevelILInstruction::new(self.to_owned(), unsafe {
BNGetMediumLevelILIndexForInstruction(self.handle, instr_idx)
})
}
pub fn lifted_instruction_from_instruction_idx(
&self,
instr_idx: usize,
) -> MediumLevelILLiftedInstruction {
self.instruction_from_instruction_idx(instr_idx).lift()
}
pub fn instruction_count(&self) -> usize {
unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
}
@@ -91,6 +104,478 @@ impl MediumLevelILFunction {
unsafe { Array::new(blocks, count, context) }
}
pub fn get_var_definitions<'a>(&'a self, var: &Variable) -> MediumLevelILInstructionList<'a> {
let mut count = 0;
let raw_instrs =
unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &var.raw(), &mut count) };
assert!(!raw_instrs.is_null());
let instrs = unsafe { core::slice::from_raw_parts(raw_instrs, count) };
MediumLevelILInstructionList {
mlil: self,
ptr: raw_instrs,
instr_idxs: instrs.iter(),
}
}
pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
self,
offset: i64,
var_type: C,
name: S,
) {
let var_type = var_type.into();
let mut raw_var_type: BNTypeWithConfidence = var_type.into();
let name = name.into_bytes_with_nul();
unsafe {
BNCreateUserStackVariable(
self.get_function().handle,
offset,
&mut raw_var_type,
name.as_ref().as_ptr() as *const c_char,
)
}
}
pub fn delete_user_stack_var(self, offset: i64) {
unsafe { BNDeleteUserStackVariable(self.get_function().handle, offset) }
}
pub fn create_user_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
&self,
var: &Variable,
var_type: C,
name: S,
ignore_disjoint_uses: bool,
) {
let var_type = var_type.into();
let raw_var_type: BNTypeWithConfidence = var_type.into();
let name = name.into_bytes_with_nul();
unsafe {
BNCreateUserVariable(
self.get_function().handle,
&var.raw(),
&raw_var_type as *const _ as *mut _,
name.as_ref().as_ptr() as *const _,
ignore_disjoint_uses,
)
}
}
pub fn delete_user_var(&self, var: &Variable) {
unsafe { BNDeleteUserVariable(self.get_function().handle, &var.raw()) }
}
pub fn is_var_user_defined(&self, var: &Variable) -> bool {
unsafe { BNIsVariableUserDefined(self.get_function().handle, &var.raw()) }
}
/// Allows the user to specify a PossibleValueSet value for an MLIL
/// variable at its definition site.
///
/// .. warning:: Setting the variable value, triggers a reanalysis of the
/// function and allows the dataflow to compute and propagate values which
/// depend on the current variable. This implies that branch conditions
/// whose values can be determined statically will be computed, leading to
/// potential branch elimination at the HLIL layer.
///
/// * `var` - Variable for which the value is to be set
/// * `addr` - Address of the definition site of the variable
/// * `value` - Informed value of the variable
///
/// # Example
/// ```no_run
/// # use binaryninja::mlil::MediumLevelILFunction;
/// # use binaryninja::types::PossibleValueSet;
/// # let mlil_fun: MediumLevelILFunction = todo!();
/// let (mlil_var, arch_addr, _val) = mlil_fun.user_var_values().all().next().unwrap();
/// let def_address = arch_addr.address;
/// let var_value = PossibleValueSet::ConstantValue{value: 5};
/// mlil_fun.set_user_var_value(&mlil_var, def_address, var_value).unwrap();
/// ```
pub fn set_user_var_value(
&self,
var: &Variable,
addr: u64,
value: PossibleValueSet,
) -> Result<(), ()> {
let Some(_def_site) = self
.get_var_definitions(var)
.find(|def| def.address == addr)
else {
// Error "No definition for Variable found at given address"
return Err(());
};
let function = self.get_function();
let def_site = BNArchitectureAndAddress {
arch: function.arch().0,
address: addr,
};
let value = value.into_raw();
unsafe { BNSetUserVariableValue(function.handle, &var.raw(), &def_site, value.as_ffi()) }
Ok(())
}
/// Clears a previously defined user variable value.
///
/// * `var` - Variable for which the value was informed
/// * `def_addr` - Address of the definition site of the variable
pub fn clear_user_var_value(&self, var: &Variable, addr: u64) -> Result<(), ()> {
let Some(_var_def) = self
.get_var_definitions(var)
.find(|site| site.address == addr)
else {
//error "Could not get definition for Variable"
return Err(());
};
let function = self.get_function();
let def_site = BNArchitectureAndAddress {
arch: function.arch().0,
address: addr,
};
unsafe { BNClearUserVariableValue(function.handle, &var.raw(), &def_site) };
Ok(())
}
/// Returns a map of current defined user variable values.
/// Returns a Map of user current defined user variable values and their definition sites.
pub fn user_var_values(&self) -> UserVariableValues {
let mut count = 0;
let function = self.get_function();
let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
assert!(!var_values.is_null());
UserVariableValues {
vars: core::ptr::slice_from_raw_parts(var_values, count),
}
}
/// Clear all user defined variable values.
pub fn clear_user_var_values(&self) -> Result<(), ()> {
for (var, arch_and_addr, _value) in self.user_var_values().all() {
self.clear_user_var_value(&var, arch_and_addr.address)?;
}
Ok(())
}
pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>(
&self,
offset: i64,
var_type: T,
name: S,
) {
let var_type: Conf<&Type> = var_type.into();
let mut var_type = var_type.into();
let name = name.into_bytes_with_nul();
let name_c_str = name.as_ref();
unsafe {
BNCreateAutoStackVariable(
self.get_function().handle,
offset,
&mut var_type,
name_c_str.as_ptr() as *const c_char,
)
}
}
pub fn delete_auto_stack_var(&self, offset: i64) {
unsafe { BNDeleteAutoStackVariable(self.get_function().handle, offset) }
}
pub fn create_auto_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
&self,
var: &Variable,
var_type: C,
name: S,
ignore_disjoint_uses: bool,
) {
let var_type: Conf<&Type> = var_type.into();
let mut var_type = var_type.into();
let name = name.into_bytes_with_nul();
let name_c_str = name.as_ref();
unsafe {
BNCreateAutoVariable(
self.get_function().handle,
&var.raw(),
&mut var_type,
name_c_str.as_ptr() as *const c_char,
ignore_disjoint_uses,
)
}
}
/// Returns a list of ILReferenceSource objects (IL xrefs or cross-references)
/// that reference the given variable. The variable is a local variable that can be either on the stack,
/// in a register, or in a flag.
/// This function is related to get_hlil_var_refs(), which returns variable references collected
/// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
/// into a single variable in HLIL.
///
/// * `var` - Variable for which to query the xref
///
/// # Example
/// ```no_run
/// # use binaryninja::mlil::MediumLevelILFunction;
/// # use binaryninja::types::Variable;
/// # let mlil_fun: MediumLevelILFunction = todo!();
/// # let mlil_var: Variable = todo!();
/// let instr = mlil_fun.var_refs(&mlil_var).get(0).expr();
/// ```
pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
let mut count = 0;
let refs = unsafe {
BNGetMediumLevelILVariableReferences(
self.get_function().handle,
&mut var.raw(),
&mut count,
)
};
assert!(!refs.is_null());
unsafe { Array::new(refs, count, self.to_owned()) }
}
/// Returns a list of variables referenced by code in the function ``func``,
/// of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from
/// all functions and containing the address will be returned. If no architecture is specified, the
/// architecture of the function will be used.
/// This function is related to get_hlil_var_refs_from(), which returns variable references collected
/// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
/// into a single variable in HLIL.
///
/// * `addr` - virtual address to query for variable references
/// * `length` - optional length of query
/// * `arch` - optional architecture of query
pub fn var_refs_from(
&self,
addr: u64,
length: Option<u64>,
arch: Option<CoreArchitecture>,
) -> Array<VariableReferenceSource> {
let function = self.get_function();
let arch = arch.unwrap_or_else(|| function.arch());
let mut count = 0;
let refs = if let Some(length) = length {
unsafe {
BNGetMediumLevelILVariableReferencesInRange(
function.handle,
arch.0,
addr,
length,
&mut count,
)
}
} else {
unsafe {
BNGetMediumLevelILVariableReferencesFrom(function.handle, arch.0, addr, &mut count)
}
};
assert!(!refs.is_null());
unsafe { Array::new(refs, count, self.to_owned()) }
}
/// Current IL Address
pub fn current_address(&self) -> u64 {
unsafe { BNMediumLevelILGetCurrentAddress(self.handle) }
}
/// Set the current IL Address
pub fn set_current_address(&self, value: u64, arch: Option<CoreArchitecture>) {
let arch = arch
.map(|x| x.0)
.unwrap_or_else(|| self.get_function().arch().0);
unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, value) }
}
/// Returns the BasicBlock at the given MLIL `instruction`.
pub fn basic_block_containing(
&self,
instruction: &MediumLevelILInstruction,
) -> Option<BasicBlock<MediumLevelILBlock>> {
let index = instruction.index;
let block = unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index) };
(!block.is_null()).then(|| unsafe {
BasicBlock::from_raw(
block,
MediumLevelILBlock {
function: self.to_owned(),
},
)
})
}
/// ends the function and computes the list of basic blocks.
pub fn finalize(&self) {
unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
}
/// Generate SSA form given the current MLIL
///
/// * `analyze_conditionals` - whether or not to analyze conditionals
/// * `handle_aliases` - whether or not to handle aliases
/// * `known_not_aliases` - optional list of variables known to be not aliased
/// * `known_aliases` - optional list of variables known to be aliased
pub fn generate_ssa_form(
&self,
analyze_conditionals: bool,
handle_aliases: bool,
known_not_aliases: impl IntoIterator<Item = Variable>,
known_aliases: impl IntoIterator<Item = Variable>,
) {
let mut known_not_aliases: Box<[_]> =
known_not_aliases.into_iter().map(|x| x.raw()).collect();
let mut known_aliases: Box<[_]> = known_aliases.into_iter().map(|x| x.raw()).collect();
let (known_not_aliases_ptr, known_not_aliases_len) = if known_not_aliases.is_empty() {
(core::ptr::null_mut(), 0)
} else {
(known_not_aliases.as_mut_ptr(), known_not_aliases.len())
};
let (known_aliases_ptr, known_aliases_len) = if known_not_aliases.is_empty() {
(core::ptr::null_mut(), 0)
} else {
(known_aliases.as_mut_ptr(), known_aliases.len())
};
unsafe {
BNGenerateMediumLevelILSSAForm(
self.handle,
analyze_conditionals,
handle_aliases,
known_not_aliases_ptr,
known_not_aliases_len,
known_aliases_ptr,
known_aliases_len,
)
}
}
/// Gets the instruction that contains the given SSA variable's definition.
///
/// Since SSA variables can only be defined once, this will return the single instruction where that occurs.
/// For SSA variable version 0s, which don't have definitions, this will return None instead.
pub fn ssa_variable_definition(&self, var: SSAVariable) -> Option<MediumLevelILInstruction> {
let result = unsafe {
BNGetMediumLevelILSSAVarDefinition(self.handle, &var.variable.raw(), var.version)
};
(result < self.instruction_count())
.then(|| MediumLevelILInstruction::new(self.to_owned(), result))
}
pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
(result < self.instruction_count())
.then(|| MediumLevelILInstruction::new(self.to_owned(), result))
}
///Gets all the instructions that use the given SSA variable.
pub fn ssa_variable_uses(&self, ssa_var: SSAVariable) -> Array<MediumLevelILInstruction> {
let mut count = 0;
let uses = unsafe {
BNGetMediumLevelILSSAVarUses(
self.handle,
&ssa_var.variable.raw(),
ssa_var.version,
&mut count,
)
};
assert!(!uses.is_null());
unsafe { Array::new(uses, count, self.to_owned()) }
}
pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
let mut count = 0;
let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
assert!(!uses.is_null());
unsafe { Array::new(uses, count, self.to_owned()) }
}
/// determines if `ssa_var` is live at any point in the function
pub fn is_ssa_variable_live(&self, ssa_var: SSAVariable) -> bool {
unsafe {
BNIsMediumLevelILSSAVarLive(self.handle, &ssa_var.variable.raw(), ssa_var.version)
}
}
pub fn variable_definitions(&self, variable: Variable) -> Array<MediumLevelILInstruction> {
let mut count = 0;
let defs = unsafe {
BNGetMediumLevelILVariableDefinitions(self.handle, &variable.raw(), &mut count)
};
unsafe { Array::new(defs, count, self.to_owned()) }
}
pub fn variable_uses(&self, variable: Variable) -> Array<MediumLevelILInstruction> {
let mut count = 0;
let uses =
unsafe { BNGetMediumLevelILVariableUses(self.handle, &variable.raw(), &mut count) };
unsafe { Array::new(uses, count, self.to_owned()) }
}
/// Computes the list of instructions for which `var` is live.
/// If `include_last_use` is false, the last use of the variable will not be included in the
/// list (this allows for easier computation of overlaps in liveness between two variables).
/// If the variable is never used, this function will return an empty list.
///
/// `var` - the variable to query
/// `include_last_use` - whether to include the last use of the variable in the list of instructions
pub fn live_instruction_for_variable(
&self,
variable: Variable,
include_last_user: bool,
) -> Array<MediumLevelILInstruction> {
let mut count = 0;
let uses = unsafe {
BNGetMediumLevelILLiveInstructionsForVariable(
self.handle,
&variable.raw(),
include_last_user,
&mut count,
)
};
unsafe { Array::new(uses, count, self.to_owned()) }
}
pub fn ssa_variable_value(&self, ssa_var: SSAVariable) -> RegisterValue {
unsafe {
BNGetMediumLevelILSSAVarValue(self.handle, &ssa_var.variable.raw(), ssa_var.version)
}
.into()
}
pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph {
let settings = settings.map(|x| x.handle).unwrap_or(core::ptr::null_mut());
let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
unsafe { FlowGraph::from_raw(graph) }
}
/// This gets just the MLIL variables - you may be interested in the union
/// of [MediumLevelILFunction::aliased_variables] and
/// [crate::function::Function::parameter_variables] for all the
/// variables used in the function
pub fn variables(&self) -> Array<Variable> {
let mut count = 0;
let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
unsafe { Array::new(uses, count, ()) }
}
/// This returns a list of Variables that are taken reference to and used
/// elsewhere. You may also wish to consider [MediumLevelILFunction::variables]
/// and [crate::function::Function::parameter_variables]
pub fn aliased_variables(&self) -> Array<Variable> {
let mut count = 0;
let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
unsafe { Array::new(uses, count, ()) }
}
/// This gets just the MLIL SSA variables - you may be interested in the
/// union of [MediumLevelILFunction::aliased_variables] and
/// [crate::function::Function::parameter_variables] for all the
/// variables used in the function.
pub fn ssa_variables(&self) -> Array<Array<SSAVariable>> {
let mut count = 0;
let vars = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
unsafe { Array::new(vars, count, self.to_owned()) }
}
}
impl ToOwned for MediumLevelILFunction {
@@ -118,3 +603,125 @@ impl core::fmt::Debug for MediumLevelILFunction {
write!(f, "<mlil func handle {:p}>", self.handle)
}
}
#[derive(Clone, Debug)]
pub struct MediumLevelILInstructionList<'a> {
mlil: &'a MediumLevelILFunction,
ptr: *mut usize,
instr_idxs: core::slice::Iter<'a, usize>,
}
impl Drop for MediumLevelILInstructionList<'_> {
fn drop(&mut self) {
unsafe { BNFreeILInstructionList(self.ptr) };
}
}
impl Iterator for MediumLevelILInstructionList<'_> {
type Item = MediumLevelILInstruction;
fn next(&mut self) -> Option<Self::Item> {
self.instr_idxs
.next()
.map(|i| self.mlil.instruction_from_instruction_idx(*i))
}
}
impl DoubleEndedIterator for MediumLevelILInstructionList<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.instr_idxs
.next_back()
.map(|i| self.mlil.instruction_from_instruction_idx(*i))
}
}
impl ExactSizeIterator for MediumLevelILInstructionList<'_> {}
impl core::iter::FusedIterator for MediumLevelILInstructionList<'_> {}
/////////////////////////
// FunctionGraphType
pub type FunctionGraphType = binaryninjacore_sys::BNFunctionGraphType;
/////////////////////////
// ILReferenceSource
pub struct ILReferenceSource {
mlil: Ref<MediumLevelILFunction>,
_func: Ref<Function>,
_arch: CoreArchitecture,
addr: u64,
type_: FunctionGraphType,
expr_id: usize,
}
impl ILReferenceSource {
unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref<MediumLevelILFunction>) -> Self {
Self {
mlil,
_func: Function::from_raw(value.func),
_arch: CoreArchitecture::from_raw(value.arch),
addr: value.addr,
type_: value.type_,
expr_id: value.exprId,
}
}
pub fn addr(&self) -> u64 {
self.addr
}
pub fn graph_type(&self) -> FunctionGraphType {
self.type_
}
pub fn expr(&self) -> MediumLevelILInstruction {
self.mlil.instruction_from_idx(self.expr_id)
}
}
impl CoreArrayProvider for ILReferenceSource {
type Raw = BNILReferenceSource;
type Context = Ref<MediumLevelILFunction>;
type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for ILReferenceSource {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeILReferences(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::from_raw(*raw, context.to_owned())
}
}
/////////////////////////
// VariableReferenceSource
pub struct VariableReferenceSource {
var: Variable,
source: ILReferenceSource,
}
impl VariableReferenceSource {
pub fn variable(&self) -> &Variable {
&self.var
}
pub fn source(&self) -> &ILReferenceSource {
&self.source
}
}
impl CoreArrayProvider for VariableReferenceSource {
type Raw = BNVariableReferenceSource;
type Context = Ref<MediumLevelILFunction>;
type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for VariableReferenceSource {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeVariableReferenceSourceList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Self {
var: Variable::from_raw(raw.var),
source: ILReferenceSource::from_raw(raw.source, context.to_owned()),
}
}
}

View File

@@ -1,12 +1,12 @@
use binaryninjacore_sys::BNFromVariableIdentifier;
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
use binaryninjacore_sys::BNMediumLevelILInstruction;
use binaryninjacore_sys::BNMediumLevelILOperation;
use binaryninjacore_sys::*;
use crate::architecture::CoreIntrinsic;
use crate::disassembly::InstructionTextToken;
use crate::operand_iter::OperandIter;
use crate::rc::Ref;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
use crate::types::{
ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable,
Conf, ConstantData, DataFlowQueryOption, ILBranchDependence, PossibleValueSet,
RegisterValue, RegisterValueType, SSAVariable, Type, Variable,
};
use super::lift::*;
@@ -17,6 +17,8 @@ use super::MediumLevelILFunction;
pub struct MediumLevelILInstruction {
pub function: Ref<MediumLevelILFunction>,
pub address: u64,
pub index: usize,
pub size: usize,
pub kind: MediumLevelILInstructionKind,
}
@@ -166,8 +168,8 @@ impl core::fmt::Debug for MediumLevelILInstruction {
}
impl MediumLevelILInstruction {
pub(crate) fn new(function: Ref<MediumLevelILFunction>, idx: usize) -> Self {
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, idx) };
pub(crate) fn new(function: Ref<MediumLevelILFunction>, index: usize) -> Self {
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index) };
use BNMediumLevelILOperation::*;
use MediumLevelILInstructionKind as Op;
let kind = match op.operation {
@@ -703,7 +705,12 @@ impl MediumLevelILInstruction {
}),
// translated directly into a list for Expression or Variables
// TODO MLIL_MEMORY_INTRINSIC_SSA needs to be handled properly
MLIL_CALL_OUTPUT | MLIL_CALL_PARAM | MLIL_CALL_PARAM_SSA | MLIL_CALL_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_SSA => {
MLIL_CALL_OUTPUT
| MLIL_CALL_PARAM
| MLIL_CALL_PARAM_SSA
| MLIL_CALL_OUTPUT_SSA
| MLIL_MEMORY_INTRINSIC_OUTPUT_SSA
| MLIL_MEMORY_INTRINSIC_SSA => {
unreachable!()
}
};
@@ -711,6 +718,8 @@ impl MediumLevelILInstruction {
Self {
function,
address: op.address,
index,
size: op.size,
kind,
}
}
@@ -897,7 +906,7 @@ impl MediumLevelILInstruction {
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
.vars()
.collect(),
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
.exprs()
.map(|expr| expr.lift())
@@ -916,7 +925,7 @@ impl MediumLevelILInstruction {
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
.ssa_vars()
.collect(),
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
.exprs()
.map(|expr| expr.lift())
@@ -1019,10 +1028,394 @@ impl MediumLevelILInstruction {
MediumLevelILLiftedInstruction {
function: self.function.clone(),
address: self.address,
index: self.index,
size: self.size,
kind,
}
}
pub fn tokens(&self) -> Array<InstructionTextToken> {
let mut count = 0;
let mut tokens = core::ptr::null_mut();
assert!(unsafe {
BNGetMediumLevelILExprText(
self.function.handle,
self.function.get_function().arch().0,
self.index,
&mut tokens,
&mut count,
core::ptr::null_mut(),
)
});
unsafe { Array::new(tokens, count, ()) }
}
/// Value of expression if constant or a known value
pub fn value(&self) -> RegisterValue {
unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.index) }.into()
}
/// Possible values of expression using path-sensitive static data flow analysis
pub fn possible_values(&self, options: Option<&[DataFlowQueryOption]>) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleExprValues(
self.function.handle,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
pub fn possible_ssa_variable_values(
&self,
ssa_var: SSAVariable,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleSSAVarValues(
self.function.handle,
&ssa_var.variable.raw(),
ssa_var.version,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
/// return the variable version used at this instruction
pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable {
let version = unsafe {
BNGetMediumLevelILSSAVarVersionAtILInstruction(
self.function.handle,
&var.raw(),
self.index,
)
};
SSAVariable::new(var, version)
}
/// Set of branching instructions that must take the true or false path to reach this instruction
pub fn branch_dependence(&self) -> Array<BranchDependence> {
let mut count = 0;
let deps = unsafe {
BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index, &mut count)
};
assert!(!deps.is_null());
unsafe { Array::new(deps, count, self.function.clone()) }
}
pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence {
let deps = unsafe {
BNGetMediumLevelILBranchDependence(self.function.handle, self.index, instruction.index)
};
BranchDependence {
instruction,
dependence: deps,
}
}
/// Version of active memory contents in SSA form for this instruction
pub fn ssa_memory_version(&self) -> usize {
unsafe {
BNGetMediumLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.index)
}
}
/// Type of expression
pub fn expr_type(&self) -> Option<Conf<Ref<Type>>> {
let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.index) };
(!result.type_.is_null()).then(|| {
Conf::new(
unsafe { Type::ref_from_raw(result.type_) },
result.confidence,
)
})
}
/// Set type of expression
///
/// This API is only meant for workflows or for debugging purposes, since the changes they make are not persistent
/// and get lost after a database save and reload. To make persistent changes to the analysis, one should use other
/// APIs to, for example, change the type of variables. The analysis will then propagate the type of the variable
/// and update the type of related expressions.
pub fn set_expr_type<'a, T: Into<Conf<&'a Type>>>(&self, value: T) {
let type_: Conf<&'a Type> = value.into();
let mut type_raw: BNTypeWithConfidence = BNTypeWithConfidence {
type_: type_.contents.handle,
confidence: type_.confidence,
};
unsafe { BNSetMediumLevelILExprType(self.function.handle, self.index, &mut type_raw) }
}
pub fn variable_for_register(&self, reg_id: u32) -> Variable {
let result = unsafe {
BNGetMediumLevelILVariableForRegisterAtInstruction(
self.function.handle,
reg_id,
self.index,
)
};
unsafe { Variable::from_raw(result) }
}
pub fn variable_for_flag(&self, flag_id: u32) -> Variable {
let result = unsafe {
BNGetMediumLevelILVariableForFlagAtInstruction(
self.function.handle,
flag_id,
self.index,
)
};
unsafe { Variable::from_raw(result) }
}
pub fn variable_for_stack_location(&self, offset: i64) -> Variable {
let result = unsafe {
BNGetMediumLevelILVariableForStackLocationAtInstruction(
self.function.handle,
offset,
self.index,
)
};
unsafe { Variable::from_raw(result) }
}
pub fn register_value(&self, reg_id: u32) -> RegisterValue {
unsafe {
BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index)
}
.into()
}
pub fn register_value_after(&self, reg_id: u32) -> RegisterValue {
unsafe {
BNGetMediumLevelILRegisterValueAfterInstruction(
self.function.handle,
reg_id,
self.index,
)
}
.into()
}
pub fn possible_register_values(
&self,
reg_id: u32,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleRegisterValuesAtInstruction(
self.function.handle,
reg_id,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
pub fn possible_register_values_after(
&self,
reg_id: u32,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleRegisterValuesAfterInstruction(
self.function.handle,
reg_id,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
pub fn flag_value(&self, flag_id: u32) -> RegisterValue {
unsafe {
BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index)
}
.into()
}
pub fn flag_value_after(&self, flag_id: u32) -> RegisterValue {
unsafe {
BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index)
}
.into()
}
pub fn possible_flag_values(
&self,
flag_id: u32,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleFlagValuesAtInstruction(
self.function.handle,
flag_id,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
pub fn possible_flag_values_after(
&self,
flag_id: u32,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleFlagValuesAfterInstruction(
self.function.handle,
flag_id,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue {
unsafe {
BNGetMediumLevelILStackContentsAtInstruction(
self.function.handle,
offset,
size,
self.index,
)
}
.into()
}
pub fn stack_contents_after(&self, offset: i64, size: usize) -> RegisterValue {
unsafe {
BNGetMediumLevelILStackContentsAfterInstruction(
self.function.handle,
offset,
size,
self.index,
)
}
.into()
}
pub fn possible_stack_contents(
&self,
offset: i64,
size: usize,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleStackContentsAtInstruction(
self.function.handle,
offset,
size,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
pub fn possible_stack_contents_after(
&self,
offset: i64,
size: usize,
options: Option<&[DataFlowQueryOption]>,
) -> PossibleValueSet {
let options_ptr = options
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
.unwrap_or(core::ptr::null_mut());
let options_len = options.map(|op| op.len()).unwrap_or(0);
let mut value = unsafe {
BNGetMediumLevelILPossibleStackContentsAfterInstruction(
self.function.handle,
offset,
size,
self.index,
options_ptr,
options_len,
)
};
let result = unsafe { PossibleValueSet::from_raw(value) };
unsafe { BNFreePossibleValueSet(&mut value) }
result
}
/// Gets the unique variable for a definition instruction. This unique variable can be passed
/// to [crate::function::Function::split_variable] to split a variable at a definition. The given `var` is the
/// assigned variable to query.
///
/// * `var` - variable to query
pub fn split_var_for_definition(&self, var: Variable) -> Variable {
let index = unsafe {
BNGetDefaultIndexForMediumLevelILVariableDefinition(
self.function.handle,
&var.raw(),
self.index,
)
};
Variable::new(var.t, index, var.storage)
}
/// alias for [MediumLevelILInstruction::split_var_for_definition]
#[inline]
pub fn get_split_var_for_definition(&self, var: &Variable) -> Variable {
self.split_var_for_definition(*var)
}
fn lift_operand(&self, expr_idx: usize) -> Box<MediumLevelILLiftedInstruction> {
Box::new(self.function.lifted_instruction_from_idx(expr_idx))
}
@@ -1096,6 +1489,22 @@ impl MediumLevelILInstruction {
}
}
impl CoreArrayProvider for MediumLevelILInstruction {
type Raw = usize;
type Context = Ref<MediumLevelILFunction>;
type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for MediumLevelILInstruction {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeILInstructionList(raw)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
context.instruction_from_idx(*raw)
}
}
fn get_float(value: u64, size: usize) -> f64 {
match size {
4 => f32::from_bits(value as u32) as f64,
@@ -1110,7 +1519,7 @@ fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLe
}
fn get_var(id: u64) -> Variable {
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
unsafe { Variable::from_identifier(id) }
}
fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
@@ -1149,3 +1558,28 @@ fn get_call_params_ssa(
assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA);
OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs()
}
/// Conditional branching instruction and an expected conditional result
pub struct BranchDependence {
pub instruction: MediumLevelILInstruction,
pub dependence: ILBranchDependence,
}
impl CoreArrayProvider for BranchDependence {
type Raw = BNILBranchInstructionAndDependence;
type Context = Ref<MediumLevelILFunction>;
type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for BranchDependence {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
unsafe { BNFreeILBranchDependenceList(raw) };
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Self {
instruction: MediumLevelILInstruction::new(context.clone(), raw.branch),
dependence: raw.dependence,
}
}
}

View File

@@ -1,7 +1,8 @@
use std::collections::BTreeMap;
use crate::architecture::CoreIntrinsic;
use crate::rc::Ref;
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
use crate::types::{ConstantData, SSAVariable, Variable};
use super::operation::*;
use super::MediumLevelILFunction;
@@ -9,7 +10,7 @@ use super::MediumLevelILFunction;
#[derive(Clone)]
pub enum MediumLevelILLiftedOperand {
ConstantData(ConstantData),
Intrinsic(ILIntrinsic),
Intrinsic(CoreIntrinsic),
Expr(MediumLevelILLiftedInstruction),
ExprList(Vec<MediumLevelILLiftedInstruction>),
Float(f64),
@@ -26,6 +27,8 @@ pub enum MediumLevelILLiftedOperand {
pub struct MediumLevelILLiftedInstruction {
pub function: Ref<MediumLevelILFunction>,
pub address: u64,
pub index: usize,
pub size: usize,
pub kind: MediumLevelILLiftedInstructionKind,
}
@@ -164,6 +167,142 @@ pub enum MediumLevelILLiftedInstructionKind {
}
impl MediumLevelILLiftedInstruction {
pub fn name(&self) -> &'static str {
use MediumLevelILLiftedInstructionKind::*;
match self.kind {
Nop => "Nop",
Noret => "Noret",
Bp => "Bp",
Undef => "Undef",
Unimpl => "Unimpl",
If(_) => "If",
FloatConst(_) => "FloatConst",
Const(_) => "Const",
ConstPtr(_) => "ConstPtr",
Import(_) => "Import",
ExternPtr(_) => "ExternPtr",
ConstData(_) => "ConstData",
Jump(_) => "Jump",
RetHint(_) => "RetHint",
StoreSsa(_) => "StoreSsa",
StoreStructSsa(_) => "StoreStructSsa",
StoreStruct(_) => "StoreStruct",
Store(_) => "Store",
JumpTo(_) => "JumpTo",
Goto(_) => "Goto",
FreeVarSlot(_) => "FreeVarSlot",
SetVarField(_) => "SetVarField",
SetVar(_) => "SetVar",
FreeVarSlotSsa(_) => "FreeVarSlotSsa",
SetVarSsaField(_) => "SetVarSsaField",
SetVarAliasedField(_) => "SetVarAliasedField",
SetVarAliased(_) => "SetVarAliased",
SetVarSsa(_) => "SetVarSsa",
VarPhi(_) => "VarPhi",
MemPhi(_) => "MemPhi",
VarSplit(_) => "VarSplit",
SetVarSplit(_) => "SetVarSplit",
VarSplitSsa(_) => "VarSplitSsa",
SetVarSplitSsa(_) => "SetVarSplitSsa",
Add(_) => "Add",
Sub(_) => "Sub",
And(_) => "And",
Or(_) => "Or",
Xor(_) => "Xor",
Lsl(_) => "Lsl",
Lsr(_) => "Lsr",
Asr(_) => "Asr",
Rol(_) => "Rol",
Ror(_) => "Ror",
Mul(_) => "Mul",
MuluDp(_) => "MuluDp",
MulsDp(_) => "MulsDp",
Divu(_) => "Divu",
DivuDp(_) => "DivuDp",
Divs(_) => "Divs",
DivsDp(_) => "DivsDp",
Modu(_) => "Modu",
ModuDp(_) => "ModuDp",
Mods(_) => "Mods",
ModsDp(_) => "ModsDp",
CmpE(_) => "CmpE",
CmpNe(_) => "CmpNe",
CmpSlt(_) => "CmpSlt",
CmpUlt(_) => "CmpUlt",
CmpSle(_) => "CmpSle",
CmpUle(_) => "CmpUle",
CmpSge(_) => "CmpSge",
CmpUge(_) => "CmpUge",
CmpSgt(_) => "CmpSgt",
CmpUgt(_) => "CmpUgt",
TestBit(_) => "TestBit",
AddOverflow(_) => "AddOverflow",
FcmpE(_) => "FcmpE",
FcmpNe(_) => "FcmpNe",
FcmpLt(_) => "FcmpLt",
FcmpLe(_) => "FcmpLe",
FcmpGe(_) => "FcmpGe",
FcmpGt(_) => "FcmpGt",
FcmpO(_) => "FcmpO",
FcmpUo(_) => "FcmpUo",
Fadd(_) => "Fadd",
Fsub(_) => "Fsub",
Fmul(_) => "Fmul",
Fdiv(_) => "Fdiv",
Adc(_) => "Adc",
Sbb(_) => "Sbb",
Rlc(_) => "Rlc",
Rrc(_) => "Rrc",
Call(_) => "Call",
Tailcall(_) => "Tailcall",
Syscall(_) => "Syscall",
Intrinsic(_) => "Intrinsic",
IntrinsicSsa(_) => "IntrinsicSsa",
CallSsa(_) => "CallSsa",
TailcallSsa(_) => "TailcallSsa",
CallUntypedSsa(_) => "CallUntypedSsa",
TailcallUntypedSsa(_) => "TailcallUntypedSsa",
SyscallSsa(_) => "SyscallSsa",
SyscallUntypedSsa(_) => "SyscallUntypedSsa",
CallUntyped(_) => "CallUntyped",
TailcallUntyped(_) => "TailcallUntyped",
SyscallUntyped(_) => "SyscallUntyped",
SeparateParamList(_) => "SeparateParamList",
SharedParamSlot(_) => "SharedParamSlot",
Neg(_) => "Neg",
Not(_) => "Not",
Sx(_) => "Sx",
Zx(_) => "Zx",
LowPart(_) => "LowPart",
BoolToInt(_) => "BoolToInt",
UnimplMem(_) => "UnimplMem",
Fsqrt(_) => "Fsqrt",
Fneg(_) => "Fneg",
Fabs(_) => "Fabs",
FloatToInt(_) => "FloatToInt",
IntToFloat(_) => "IntToFloat",
FloatConv(_) => "FloatConv",
RoundToInt(_) => "RoundToInt",
Floor(_) => "Floor",
Ceil(_) => "Ceil",
Ftrunc(_) => "Ftrunc",
Load(_) => "Load",
LoadStruct(_) => "LoadStruct",
LoadStructSsa(_) => "LoadStructSsa",
LoadSsa(_) => "LoadSsa",
Ret(_) => "Ret",
Var(_) => "Var",
AddressOf(_) => "AddressOf",
VarField(_) => "VarField",
AddressOfField(_) => "AddressOfField",
VarSsa(_) => "VarSsa",
VarAliased(_) => "VarAliased",
VarSsaField(_) => "VarSsaField",
VarAliasedField(_) => "VarAliasedField",
Trap(_) => "Trap",
}
}
pub fn operands(&self) -> Vec<(&'static str, MediumLevelILLiftedOperand)> {
use MediumLevelILLiftedInstructionKind::*;
use MediumLevelILLiftedOperand as Operand;

View File

@@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
use crate::{architecture::CoreIntrinsic, types::{ConstantData, SSAVariable, Variable}};
use super::MediumLevelILLiftedInstruction;
@@ -355,7 +355,7 @@ pub struct Intrinsic {
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedIntrinsic {
pub output: Vec<Variable>,
pub intrinsic: ILIntrinsic,
pub intrinsic: CoreIntrinsic,
pub params: Vec<MediumLevelILLiftedInstruction>,
}
@@ -371,7 +371,7 @@ pub struct IntrinsicSsa {
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedIntrinsicSsa {
pub output: Vec<SSAVariable>,
pub intrinsic: ILIntrinsic,
pub intrinsic: CoreIntrinsic,
pub params: Vec<MediumLevelILLiftedInstruction>,
}

View File

@@ -1,4 +1,3 @@
use binaryninjacore_sys::BNFromVariableIdentifier;
use binaryninjacore_sys::BNGetHighLevelILByIndex;
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
use binaryninjacore_sys::BNHighLevelILOperation;
@@ -215,7 +214,7 @@ impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandSSAVarIter<F> {
}
pub fn get_var(id: u64) -> Variable {
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
unsafe { Variable::from_identifier(id) }
}
pub fn get_var_ssa(id: u64, version: usize) -> SSAVariable {

View File

@@ -23,6 +23,7 @@ use crate::{
callingconvention::CallingConvention,
rc::*,
string::*,
typelibrary::TypeLibrary,
types::{QualifiedName, QualifiedNameAndType, Type},
};
@@ -163,6 +164,15 @@ impl Platform {
unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) }
}
pub fn get_type_libraries_by_name(&self, name: &QualifiedName) -> Array<TypeLibrary> {
let mut count = 0;
let result = unsafe {
BNGetPlatformTypeLibrariesByName(self.handle, &name.0 as *const _ as *mut _, &mut count)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn register_os<S: BnStrCompatible>(&self, os: S) {
let os = os.into_bytes_with_nul();
@@ -365,18 +375,14 @@ unsafe impl RefCountable for Platform {
impl CoreArrayProvider for Platform {
type Raw = *mut BNPlatform;
type Context = ();
type Wrapped<'a> = Guard<'a, Platform>;
}
unsafe impl CoreOwnedArrayProvider for Platform {
unsafe impl CoreArrayProviderInner for Platform {
unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) {
BNFreePlatformList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for Platform {
type Wrapped = Guard<'a, Platform>;
unsafe fn wrap_raw(raw: &'a *mut BNPlatform, context: &'a ()) -> Guard<'a, Platform> {
unsafe fn wrap_raw<'a>(raw: &'a *mut BNPlatform, context: &'a ()) -> Self::Wrapped<'a> {
debug_assert!(!raw.is_null());
Guard::new(Platform { handle: *raw }, context)
}

1464
src/project.rs Normal file

File diff suppressed because it is too large Load Diff

117
src/rc.rs
View File

@@ -35,7 +35,7 @@ use std::slice;
// `T` does not have the `Drop` impl in order to allow more
// efficient handling of core owned objects we receive pointers
// to in callbacks
pub unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
pub(crate) unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
unsafe fn inc_ref(handle: &Self) -> Ref<Self>;
unsafe fn dec_ref(handle: &Self);
}
@@ -43,10 +43,12 @@ pub unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
// Represents an 'owned' reference tracked by the core
// that we are responsible for cleaning up once we're
// done with the encapsulated value.
#[allow(private_bounds)]
pub struct Ref<T: RefCountable> {
contents: T,
}
#[allow(private_bounds)]
impl<T: RefCountable> Ref<T> {
/// Safety: You need to make sure wherever you got the contents from incremented the ref count already. Anywhere the core passes out an object to the API does this.
pub(crate) unsafe fn new(contents: T) -> Self {
@@ -151,6 +153,7 @@ impl<'a, T> Guard<'a, T> {
}
}
#[allow(private_bounds)]
impl<'a, T> Guard<'a, T>
where
T: RefCountable,
@@ -190,23 +193,18 @@ impl<'a, T> Borrow<T> for Guard<'a, T> {
pub trait CoreArrayProvider {
type Raw;
type Context;
type Wrapped<'a>
where
Self: 'a;
}
pub unsafe trait CoreOwnedArrayProvider: CoreArrayProvider {
pub(crate) unsafe trait CoreArrayProviderInner: CoreArrayProvider {
unsafe fn free(raw: *mut Self::Raw, count: usize, context: &Self::Context);
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a>;
}
pub unsafe trait CoreArrayWrapper<'a>: CoreArrayProvider
where
Self::Raw: 'a,
Self::Context: 'a,
{
type Wrapped: 'a;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped;
}
pub struct Array<P: CoreOwnedArrayProvider> {
#[allow(private_bounds)]
pub struct Array<P: CoreArrayProviderInner> {
contents: *mut P::Raw,
count: usize,
context: P::Context,
@@ -214,18 +212,19 @@ pub struct Array<P: CoreOwnedArrayProvider> {
unsafe impl<P> Sync for Array<P>
where
P: CoreOwnedArrayProvider,
P: CoreArrayProviderInner,
P::Context: Sync,
{
}
unsafe impl<P> Send for Array<P>
where
P: CoreOwnedArrayProvider,
P: CoreArrayProviderInner,
P::Context: Send,
{
}
impl<P: CoreOwnedArrayProvider> Array<P> {
#[allow(private_bounds)]
impl<P: CoreArrayProviderInner> Array<P> {
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
Self {
contents: raw,
@@ -243,23 +242,19 @@ impl<P: CoreOwnedArrayProvider> Array<P> {
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn into_raw_parts(self) -> (*mut P::Raw, usize) {
let me = mem::ManuallyDrop::new(self);
(me.contents, me.count)
}
}
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> Array<P> {
#[allow(private_bounds)]
impl<P: CoreArrayProviderInner> Array<P> {
#[inline]
pub fn get(&'a self, index: usize) -> P::Wrapped {
pub fn get(&self, index: usize) -> P::Wrapped<'_> {
unsafe {
let backing = slice::from_raw_parts(self.contents, self.count);
P::wrap_raw(&backing[index], &self.context)
}
}
pub fn iter(&'a self) -> ArrayIter<'a, P> {
pub fn iter(&self) -> ArrayIter<P> {
ArrayIter {
it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
context: &self.context,
@@ -267,8 +262,8 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> Array<P> {
}
}
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> IntoIterator for &'a Array<P> {
type Item = P::Wrapped;
impl<'a, P: CoreArrayProviderInner> IntoIterator for &'a Array<P> {
type Item = P::Wrapped<'a>;
type IntoIter = ArrayIter<'a, P>;
fn into_iter(self) -> Self::IntoIter {
@@ -276,7 +271,7 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> IntoIterator for
}
}
impl<P: CoreOwnedArrayProvider> Drop for Array<P> {
impl<P: CoreArrayProviderInner> Drop for Array<P> {
fn drop(&mut self) {
unsafe {
P::free(self.contents, self.count, &self.context);
@@ -284,7 +279,8 @@ impl<P: CoreOwnedArrayProvider> Drop for Array<P> {
}
}
pub struct ArrayGuard<P: CoreArrayProvider> {
#[allow(private_bounds)]
pub struct ArrayGuard<P: CoreArrayProviderInner> {
contents: *mut P::Raw,
count: usize,
context: P::Context,
@@ -292,18 +288,19 @@ pub struct ArrayGuard<P: CoreArrayProvider> {
unsafe impl<P> Sync for ArrayGuard<P>
where
P: CoreArrayProvider,
P: CoreArrayProviderInner,
P::Context: Sync,
{
}
unsafe impl<P> Send for ArrayGuard<P>
where
P: CoreArrayProvider,
P: CoreArrayProviderInner,
P::Context: Send,
{
}
impl<P: CoreArrayProvider> ArrayGuard<P> {
#[allow(private_bounds)]
impl<P: CoreArrayProviderInner> ArrayGuard<P> {
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
Self {
contents: raw,
@@ -323,16 +320,17 @@ impl<P: CoreArrayProvider> ArrayGuard<P> {
}
}
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> ArrayGuard<P> {
#[allow(private_bounds)]
impl<P: CoreArrayProviderInner> ArrayGuard<P> {
#[inline]
pub fn get(&'a self, index: usize) -> P::Wrapped {
pub fn get(&self, index: usize) -> P::Wrapped<'_> {
unsafe {
let backing = slice::from_raw_parts(self.contents, self.count);
P::wrap_raw(&backing[index], &self.context)
}
}
pub fn iter(&'a self) -> ArrayIter<'a, P> {
pub fn iter(&self) -> ArrayIter<P> {
ArrayIter {
it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
context: &self.context,
@@ -340,8 +338,8 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> ArrayGuard<P> {
}
}
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> IntoIterator for &'a ArrayGuard<P> {
type Item = P::Wrapped;
impl<'a, P: CoreArrayProviderInner> IntoIterator for &'a ArrayGuard<P> {
type Item = P::Wrapped<'a>;
type IntoIter = ArrayIter<'a, P>;
fn into_iter(self) -> Self::IntoIter {
@@ -349,29 +347,30 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> IntoIterator for &'a
}
}
#[allow(private_bounds)]
pub struct ArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: CoreArrayProviderInner,
{
it: slice::Iter<'a, P::Raw>,
context: &'a P::Context,
}
unsafe impl<'a, P> Send for ArrayIter<'a, P>
unsafe impl<P> Send for ArrayIter<'_, P>
where
P: CoreArrayWrapper<'a>,
P: CoreArrayProviderInner,
P::Context: Sync,
{
}
impl<'a, P> Iterator for ArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: 'a + CoreArrayProviderInner,
{
type Item = P::Wrapped;
type Item = P::Wrapped<'a>;
#[inline]
fn next(&mut self) -> Option<P::Wrapped> {
fn next(&mut self) -> Option<Self::Item> {
self.it
.next()
.map(|r| unsafe { P::wrap_raw(r, self.context) })
@@ -385,7 +384,7 @@ where
impl<'a, P> ExactSizeIterator for ArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: 'a + CoreArrayProviderInner,
{
#[inline]
fn len(&self) -> usize {
@@ -395,10 +394,10 @@ where
impl<'a, P> DoubleEndedIterator for ArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: 'a + CoreArrayProviderInner,
{
#[inline]
fn next_back(&mut self) -> Option<P::Wrapped> {
fn next_back(&mut self) -> Option<P::Wrapped<'a>> {
self.it
.next_back()
.map(|r| unsafe { P::wrap_raw(r, self.context) })
@@ -411,21 +410,23 @@ use rayon::prelude::*;
#[cfg(feature = "rayon")]
use rayon::iter::plumbing::*;
#[allow(private_bounds)]
#[cfg(feature = "rayon")]
impl<'a, P> Array<P>
impl<P> Array<P>
where
P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider,
P: CoreArrayProviderInner,
P::Context: Sync,
P::Wrapped: Send,
for<'a> P::Wrapped<'a>: Send,
{
pub fn par_iter(&'a self) -> ParArrayIter<'a, P> {
pub fn par_iter(&self) -> ParArrayIter<'_, P> {
ParArrayIter { it: self.iter() }
}
}
#[allow(private_bounds)]
#[cfg(feature = "rayon")]
pub struct ParArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: CoreArrayProviderInner,
ArrayIter<'a, P>: Send,
{
it: ArrayIter<'a, P>,
@@ -434,11 +435,11 @@ where
#[cfg(feature = "rayon")]
impl<'a, P> ParallelIterator for ParArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P::Wrapped: Send,
P: 'a + CoreArrayProviderInner,
P::Wrapped<'a>: Send,
ArrayIter<'a, P>: Send,
{
type Item = P::Wrapped;
type Item = P::Wrapped<'a>;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
@@ -455,8 +456,8 @@ where
#[cfg(feature = "rayon")]
impl<'a, P> IndexedParallelIterator for ParArrayIter<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P::Wrapped: Send,
P: 'a + CoreArrayProviderInner,
P::Wrapped<'a>: Send,
ArrayIter<'a, P>: Send,
{
fn drive<C>(self, consumer: C) -> C::Result
@@ -481,7 +482,7 @@ where
#[cfg(feature = "rayon")]
struct ArrayIterProducer<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: 'a + CoreArrayProviderInner,
ArrayIter<'a, P>: Send,
{
it: ArrayIter<'a, P>,
@@ -490,10 +491,10 @@ where
#[cfg(feature = "rayon")]
impl<'a, P> Producer for ArrayIterProducer<'a, P>
where
P: 'a + CoreArrayWrapper<'a>,
P: 'a + CoreArrayProviderInner,
ArrayIter<'a, P>: Send,
{
type Item = P::Wrapped;
type Item = P::Wrapped<'a>;
type IntoIter = ArrayIter<'a, P>;
fn into_iter(self) -> ArrayIter<'a, P> {

View File

@@ -1,6 +1,6 @@
use crate::architecture::CoreArchitecture;
use crate::function::Function;
use crate::rc::{CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref};
use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref};
use binaryninjacore_sys::{BNFreeCodeReferences, BNFreeDataReferences, BNReferenceSource};
use std::mem::ManuallyDrop;
@@ -56,19 +56,15 @@ impl<'a> CodeReference {
impl CoreArrayProvider for CodeReference {
type Raw = BNReferenceSource;
type Context = ();
type Wrapped<'a> = Guard<'a, CodeReference>;
}
unsafe impl CoreOwnedArrayProvider for CodeReference {
unsafe impl CoreArrayProviderInner for CodeReference {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeCodeReferences(raw, count)
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for CodeReference {
type Wrapped = CodeReference;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
CodeReference::new(raw)
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(CodeReference::new(raw), &())
}
}
@@ -77,18 +73,14 @@ unsafe impl<'a> CoreArrayWrapper<'a> for CodeReference {
impl CoreArrayProvider for DataReference {
type Raw = u64;
type Context = ();
type Wrapped<'a> = DataReference;
}
unsafe impl CoreOwnedArrayProvider for DataReference {
unsafe impl CoreArrayProviderInner for DataReference {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeDataReferences(raw)
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for DataReference {
type Wrapped = DataReference;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
DataReference { address: *raw }
}
}

View File

@@ -1,13 +1,15 @@
use crate::rc::Guard;
use crate::string::BnStrCompatible;
use crate::{
architecture::{Architecture, CoreArchitecture},
binaryview::BinaryView,
llil,
rc::{CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref, RefCountable},
rc::{CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable},
symbol::Symbol,
};
use binaryninjacore_sys::*;
use std::borrow::Borrow;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -219,18 +221,15 @@ impl Relocation {
impl CoreArrayProvider for Relocation {
type Raw = *mut BNRelocation;
type Context = ();
type Wrapped<'a> = Guard<'a, Relocation>;
}
unsafe impl CoreOwnedArrayProvider for Relocation {
unsafe impl CoreArrayProviderInner for Relocation {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeRelocationList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for Relocation {
type Wrapped = Relocation;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
Relocation(*raw)
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Relocation(*raw), &())
}
}
@@ -501,12 +500,9 @@ where
let name = name.into_bytes_with_nul();
let uninit_handler = RelocationHandlerBuilder {
handler: unsafe { std::mem::zeroed() },
};
let raw = Box::into_raw(Box::new(uninit_handler));
let raw = Box::leak(Box::new(MaybeUninit::<RelocationHandlerBuilder<_>>::zeroed()));
let mut custom_handler = BNCustomRelocationHandler {
context: raw as *mut _,
context: raw.as_mut_ptr() as *mut _,
freeObject: Some(cb_free::<R>),
getRelocationInfo: Some(cb_get_relocation_info::<R>),
applyRelocation: Some(cb_apply_relocation::<R>),
@@ -517,13 +513,12 @@ where
assert!(!handle_raw.is_null());
let handle = CoreRelocationHandler(handle_raw);
let custom_handle = CustomRelocationHandlerHandle {
handle: raw as *mut R,
handle: raw.as_mut_ptr() as *mut R,
};
unsafe {
core::ptr::write(
&mut raw.as_mut().unwrap().handler,
func(custom_handle, CoreRelocationHandler(handle.0)),
);
raw.write(RelocationHandlerBuilder {
handler: func(custom_handle, CoreRelocationHandler(handle.0)),
});
BNArchitectureRegisterRelocationHandler(
arch.handle().as_ref().0,

View File

@@ -72,8 +72,11 @@ impl Section {
/// You need to create a section builder, customize that section, then add it to a binary view:
///
/// ```
/// bv.add_section(Section::new().align(4).entry_size(4))
/// ```no_run
/// # use binaryninja::section::Section;
/// # use binaryninja::binaryview::BinaryViewExt;
/// let bv = binaryninja::load("example").unwrap();
/// bv.add_section(Section::builder("example", 0..1024).align(4).entry_size(4))
/// ```
pub fn builder<S: BnStrCompatible>(name: S, range: Range<u64>) -> SectionBuilder<S> {
SectionBuilder::new(name, range)
@@ -171,18 +174,14 @@ unsafe impl RefCountable for Section {
impl CoreArrayProvider for Section {
type Raw = *mut BNSection;
type Context = ();
type Wrapped<'a> = Guard<'a, Section>;
}
unsafe impl CoreOwnedArrayProvider for Section {
unsafe impl CoreArrayProviderInner for Section {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSectionList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for Section {
type Wrapped = Guard<'a, Section>;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Section::from_raw(*raw), context)
}
}

View File

@@ -117,8 +117,11 @@ impl Segment {
/// You need to create a segment builder, customize that segment, then add it to a binary view:
///
/// ```
/// bv.add_segment(Segment::new().align(4).entry_size(4))
/// ```no_run
/// # use binaryninja::segment::Segment;
/// # use binaryninja::binaryview::BinaryViewExt;
/// let bv = binaryninja::load("example").unwrap();
/// bv.add_segment(Segment::builder(0..0x1000).writable(true).readable(true))
/// ```
pub fn builder(ea_range: Range<u64>) -> SegmentBuilder {
SegmentBuilder::new(ea_range)
@@ -201,18 +204,14 @@ unsafe impl RefCountable for Segment {
impl CoreArrayProvider for Segment {
type Raw = *mut BNSegment;
type Context = ();
type Wrapped<'a> = Guard<'a, Segment>;
}
unsafe impl CoreOwnedArrayProvider for Segment {
unsafe impl CoreArrayProviderInner for Segment {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSegmentList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for Segment {
type Wrapped = Guard<'a, Segment>;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Segment::from_raw(*raw), context)
}
}

View File

@@ -14,7 +14,7 @@
//! String wrappers for core-owned strings and strings being passed to the core
use std::borrow::{Borrow, Cow};
use std::borrow::Cow;
use std::ffi::{CStr, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
@@ -33,61 +33,9 @@ pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
}
}
/// These are strings that the core will both allocate and free.
/// We just have a reference to these strings and want to be able use them, but aren't responsible for cleanup
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(C)]
pub struct BnStr {
raw: [u8],
}
impl BnStr {
pub(crate) unsafe fn from_raw<'a>(ptr: *const raw::c_char) -> &'a Self {
mem::transmute(CStr::from_ptr(ptr).to_bytes_with_nul())
}
pub fn as_str(&self) -> &str {
self.as_cstr().to_str().unwrap()
}
pub fn as_cstr(&self) -> &CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(&self.raw) }
}
}
impl Deref for BnStr {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for BnStr {
fn as_ref(&self) -> &[u8] {
&self.raw
}
}
impl AsRef<str> for BnStr {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for BnStr {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for BnStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_cstr().to_string_lossy())
}
}
#[repr(C)]
/// Is the quivalent of `core::ffi::CString` but using the allocation and free
/// functions provided by binaryninja_sys.
#[repr(transparent)]
pub struct BnString {
raw: *mut raw::c_char,
}
@@ -131,8 +79,28 @@ impl BnString {
res
}
pub(crate) fn as_raw(&self) -> &raw::c_char {
unsafe { &*self.raw }
}
pub fn as_str(&self) -> &str {
unsafe { BnStr::from_raw(self.raw).as_str() }
unsafe { CStr::from_ptr(self.raw).to_str().unwrap() }
}
pub fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
pub fn as_bytes_with_null(&self) -> &[u8] {
self.deref().to_bytes()
}
pub fn len(&self) -> usize {
self.as_ref().len()
}
pub fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
}
@@ -158,16 +126,16 @@ impl Clone for BnString {
}
impl Deref for BnString {
type Target = BnStr;
type Target = CStr;
fn deref(&self) -> &BnStr {
unsafe { BnStr::from_raw(self.raw) }
fn deref(&self) -> &CStr {
unsafe { CStr::from_ptr(self.raw) }
}
}
impl AsRef<[u8]> for BnString {
fn as_ref(&self) -> &[u8] {
self.as_cstr().to_bytes_with_nul()
self.to_bytes_with_nul()
}
}
@@ -187,33 +155,29 @@ impl Eq for BnString {}
impl fmt::Display for BnString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_cstr().to_string_lossy())
write!(f, "{}", self.to_string_lossy())
}
}
impl fmt::Debug for BnString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_cstr().to_string_lossy())
write!(f, "{}", self.to_string_lossy())
}
}
impl CoreArrayProvider for BnString {
type Raw = *mut raw::c_char;
type Context = ();
type Wrapped<'a> = &'a str;
}
unsafe impl CoreOwnedArrayProvider for BnString {
unsafe impl CoreArrayProviderInner for BnString {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
use binaryninjacore_sys::BNFreeStringList;
BNFreeStringList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for BnString {
type Wrapped = &'a BnStr;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
BnStr::from_raw(*raw)
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
CStr::from_ptr(*raw).to_str().unwrap()
}
}
@@ -222,11 +186,11 @@ pub unsafe trait BnStrCompatible {
fn into_bytes_with_nul(self) -> Self::Result;
}
unsafe impl<'a> BnStrCompatible for &'a BnStr {
unsafe impl<'a> BnStrCompatible for &'a CStr {
type Result = &'a [u8];
fn into_bytes_with_nul(self) -> Self::Result {
self.as_cstr().to_bytes_with_nul()
self.to_bytes_with_nul()
}
}
@@ -238,14 +202,6 @@ unsafe impl BnStrCompatible for BnString {
}
}
unsafe impl<'a> BnStrCompatible for &'a CStr {
type Result = &'a [u8];
fn into_bytes_with_nul(self) -> Self::Result {
self.to_bytes_with_nul()
}
}
unsafe impl BnStrCompatible for CString {
type Result = Vec<u8>;
@@ -294,3 +250,15 @@ unsafe impl BnStrCompatible for &QualifiedName {
self.string().into_bytes_with_nul()
}
}
pub trait IntoJson {
type Output: BnStrCompatible;
fn get_json_string(self) -> Result<Self::Output, ()>;
}
impl<S: BnStrCompatible> IntoJson for S {
type Output = S;
fn get_json_string(self) -> Result<Self::Output, ()> {
Ok(self)
}
}

View File

@@ -230,8 +230,10 @@ impl Symbol {
/// To create a new symbol, you need to create a symbol builder, customize that symbol, then add `SymbolBuilder::create` it into a `Ref<Symbol>`:
///
/// ```
/// Symbol::new().short_name("hello").full_name("hello").create();
/// ```no_run
/// # use binaryninja::symbol::Symbol;
/// # use binaryninja::symbol::SymbolType;
/// Symbol::builder(SymbolType::Data, "hello", 0x1337).short_name("hello").full_name("hello").create();
/// ```
pub fn builder(ty: SymbolType, raw_name: &str, addr: u64) -> SymbolBuilder {
SymbolBuilder::new(ty, raw_name, addr)
@@ -246,24 +248,15 @@ impl Symbol {
}
pub fn full_name(&self) -> BnString {
unsafe {
let name = BNGetSymbolFullName(self.handle);
BnString::from_raw(name)
}
unsafe { BnString::from_raw(BNGetSymbolFullName(self.handle)) }
}
pub fn short_name(&self) -> BnString {
unsafe {
let name = BNGetSymbolShortName(self.handle);
BnString::from_raw(name)
}
unsafe { BnString::from_raw(BNGetSymbolShortName(self.handle)) }
}
pub fn raw_name(&self) -> BnString {
unsafe {
let name = BNGetSymbolRawName(self.handle);
BnString::from_raw(name)
}
unsafe { BnString::from_raw(BNGetSymbolRawName(self.handle)) }
}
pub fn address(&self) -> u64 {
@@ -326,18 +319,14 @@ unsafe impl RefCountable for Symbol {
impl CoreArrayProvider for Symbol {
type Raw = *mut BNSymbol;
type Context = ();
type Wrapped<'a> = Guard<'a, Symbol>;
}
unsafe impl CoreOwnedArrayProvider for Symbol {
unsafe impl CoreArrayProviderInner for Symbol {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSymbolList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for Symbol {
type Wrapped = Guard<'a, Symbol>;
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Symbol::from_raw(*raw), context)
}
}

View File

@@ -16,8 +16,10 @@
use binaryninjacore_sys::*;
use crate::architecture::CoreArchitecture;
use crate::binaryview::BinaryView;
use crate::function::Function;
use crate::rc::*;
use crate::string::*;
@@ -77,6 +79,21 @@ impl ToOwned for Tag {
}
}
impl CoreArrayProvider for Tag {
type Raw = *mut BNTag;
type Context = ();
type Wrapped<'a> = Guard<'a, Self>;
}
unsafe impl CoreArrayProviderInner for Tag {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTagList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Self { handle: *raw }, &context)
}
}
unsafe impl Send for Tag {}
unsafe impl Sync for Tag {}
@@ -115,7 +132,7 @@ impl TagType {
pub fn set_icon<S: BnStrCompatible>(&self, icon: S) {
let icon = icon.into_bytes_with_nul();
unsafe {
BNTagTypeSetName(self.handle, icon.as_ref().as_ptr() as *mut _);
BNTagTypeSetIcon(self.handle, icon.as_ref().as_ptr() as *mut _);
}
}
@@ -176,3 +193,60 @@ impl ToOwned for TagType {
unsafe impl Send for TagType {}
unsafe impl Sync for TagType {}
pub type TagReferenceType = BNTagReferenceType;
pub struct TagReference {
ref_type: TagReferenceType,
auto_defined: bool,
tag: Ref<Tag>,
arch: CoreArchitecture,
func: Ref<Function>,
addr: u64,
}
impl TagReference {
unsafe fn from_borrowed_raw(value: &BNTagReference) -> Self {
Self {
ref_type: value.refType,
auto_defined: value.autoDefined,
tag: Tag { handle: value.tag }.to_owned(),
arch: CoreArchitecture::from_raw(value.arch),
func: Function { handle: value.func }.to_owned(),
addr: value.addr,
}
}
pub fn ref_type(&self) -> TagReferenceType {
self.ref_type
}
pub fn auto(&self) -> bool {
self.auto_defined
}
pub fn tag(&self) -> &Tag {
&self.tag
}
pub fn arch(&self) -> CoreArchitecture {
self.arch
}
pub fn functions(&self) -> &Function {
&self.func
}
pub fn address(&self) -> u64 {
self.addr
}
}
impl CoreArrayProvider for TagReference {
type Raw = BNTagReference;
type Context = ();
type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for TagReference {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTagReferences(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::from_borrowed_raw(raw)
}
}

947
src/typearchive.rs Normal file
View File

@@ -0,0 +1,947 @@
use core::{ffi, mem, ptr};
use binaryninjacore_sys::*;
use crate::databuffer::DataBuffer;
use crate::metadata::Metadata;
use crate::platform::Platform;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
use crate::string::{BnStrCompatible, BnString};
use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type};
/// Type Archives are a collection of types which can be shared between different analysis
/// sessions and are backed by a database file on disk. Their types can be modified, and
/// a history of previous versions of types is stored in snapshots in the archive.
#[repr(transparent)]
pub struct TypeArchive {
handle: ptr::NonNull<BNTypeArchive>,
}
impl Drop for TypeArchive {
fn drop(&mut self) {
unsafe { BNFreeTypeArchiveReference(self.as_raw()) }
}
}
impl Clone for TypeArchive {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap())
}
}
}
impl PartialEq for TypeArchive {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl Eq for TypeArchive {}
impl core::hash::Hash for TypeArchive {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self.handle.as_ptr() as usize).hash(state);
}
}
impl core::fmt::Debug for TypeArchive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let path = self.path().map(|x| x.to_string());
f.debug_struct("TypeArchive").field("path", &path).finish()
}
}
impl TypeArchive {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNTypeArchive>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeArchive) -> &Self {
assert!(!handle.is_null());
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeArchive {
&mut *self.handle.as_ptr()
}
/// Open the Type Archive at the given path, if it exists.
pub fn open<S: BnStrCompatible>(path: S) -> Option<TypeArchive> {
let path = path.into_bytes_with_nul();
let handle = unsafe { BNOpenTypeArchive(path.as_ref().as_ptr() as *const ffi::c_char) };
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
}
/// Create a Type Archive at the given path, returning None if it could not be created.
pub fn create<S: BnStrCompatible>(path: S, platform: &Platform) -> Option<TypeArchive> {
let path = path.into_bytes_with_nul();
let handle = unsafe {
BNCreateTypeArchive(
path.as_ref().as_ptr() as *const ffi::c_char,
platform.handle,
)
};
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
}
/// Create a Type Archive at the given path and id, returning None if it could not be created.
pub fn create_with_id<P: BnStrCompatible, I: BnStrCompatible>(
path: P,
id: I,
platform: &Platform,
) -> Option<TypeArchive> {
let path = path.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let handle = unsafe {
BNCreateTypeArchiveWithId(
path.as_ref().as_ptr() as *const ffi::c_char,
platform.handle,
id.as_ref().as_ptr() as *const ffi::c_char,
)
};
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
}
/// Get a reference to the Type Archive with the known id, if one exists.
pub fn lookup_by_id<S: BnStrCompatible>(id: S) -> Option<TypeArchive> {
let id = id.into_bytes_with_nul();
let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const ffi::c_char) };
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
}
/// Get the path to the Type Archive's file
pub fn path(&self) -> Option<BnString> {
let result = unsafe { BNGetTypeArchivePath(self.as_raw()) };
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
}
/// Get the guid for a Type Archive
pub fn id(&self) -> Option<BnString> {
let result = unsafe { BNGetTypeArchiveId(self.as_raw()) };
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
}
/// Get the associated Platform for a Type Archive
pub fn platform(&self) -> Ref<Platform> {
let result = unsafe { BNGetTypeArchivePlatform(self.as_raw()) };
assert!(!result.is_null());
unsafe { Platform::ref_from_raw(result) }
}
/// Get the id of the current snapshot in the type archive
pub fn current_snapshot_id(&self) -> BnString {
let result = unsafe { BNGetTypeArchiveCurrentSnapshotId(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
/// Revert the type archive's current snapshot to the given snapshot
pub fn set_current_snapshot_id<S: BnStrCompatible>(&self, id: S) {
let id = id.into_bytes_with_nul();
unsafe {
BNSetTypeArchiveCurrentSnapshot(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
)
}
}
/// Get a list of every snapshot's id
pub fn all_snapshot_ids(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetTypeArchiveAllSnapshotIds(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get the ids of the parents to the given snapshot
pub fn get_snapshot_parent_ids<S: BnStrCompatible>(
&self,
snapshot: S,
) -> Option<Array<BnString>> {
let snapshot = snapshot.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveSnapshotParentIds(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
(!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
}
/// Get the ids of the children to the given snapshot
pub fn get_snapshot_child_ids<S: BnStrCompatible>(
&self,
snapshot: S,
) -> Option<Array<BnString>> {
let snapshot = snapshot.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveSnapshotChildIds(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
(!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
}
/// Add named types to the type archive. Type must have all dependant named types added
/// prior to being added, or this function will fail.
/// If the type already exists, it will be overwritten.
///
/// * `name` - Name of new type
/// * `type` - Definition of new type
pub fn add_type(&self, name: &QualifiedNameAndType) {
self.add_types(core::slice::from_ref(name))
}
/// Add named types to the type archive. Types must have all dependant named
/// types prior to being added, or included in the list, or this function will fail.
/// Types already existing with any added names will be overwritten.
///
/// * `new_types` - Names and definitions of new types
pub fn add_types(&self, new_types: &[QualifiedNameAndType]) {
// SAFETY BNQualifiedNameAndType and QualifiedNameAndType are transparent
let new_types_raw: &[BNQualifiedNameAndType] = unsafe { mem::transmute(new_types) };
let result = unsafe {
BNAddTypeArchiveTypes(self.as_raw(), new_types_raw.as_ptr(), new_types.len())
};
assert!(result);
}
/// Change the name of an existing type in the type archive.
///
/// * `old_name` - Old type name in archive
/// * `new_name` - New type name
pub fn rename_type(&self, old_name: &QualifiedName, new_name: &QualifiedNameAndType) {
let id = self
.get_type_id(old_name, self.current_snapshot_id())
.unwrap();
return self.rename_type_by_id(id, new_name.name());
}
/// Change the name of an existing type in the type archive.
///
/// * `id` - Old id of type in archive
/// * `new_name` - New type name
pub fn rename_type_by_id<S: BnStrCompatible>(&self, id: S, new_name: &QualifiedName) {
let id = id.into_bytes_with_nul();
let result = unsafe {
BNRenameTypeArchiveType(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
&new_name.0,
)
};
assert!(result);
}
/// Delete an existing type in the type archive.
pub fn delete_type(&self, name: &QualifiedName) {
let id = self.get_type_id(name, self.current_snapshot_id());
let Some(id) = id else {
panic!("Unknown type {}", name.string())
};
self.delete_type_by_id(id);
}
/// Delete an existing type in the type archive.
pub fn delete_type_by_id<S: BnStrCompatible>(&self, id: S) {
let id = id.into_bytes_with_nul();
let result = unsafe {
BNDeleteTypeArchiveType(self.as_raw(), id.as_ref().as_ptr() as *const ffi::c_char)
};
assert!(result);
}
/// Retrieve a stored type in the archive
///
/// * `name` - Type name
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_by_name<S: BnStrCompatible>(
&self,
name: &QualifiedName,
snapshot: S,
) -> Option<Ref<Type>> {
let snapshot = snapshot.into_bytes_with_nul();
let result = unsafe {
BNGetTypeArchiveTypeByName(
self.as_raw(),
&name.0,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
)
};
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
/// Retrieve a stored type in the archive by id
///
/// * `id` - Type id
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_by_id<I: BnStrCompatible, S: BnStrCompatible>(
&self,
id: I,
snapshot: S,
) -> Option<Ref<Type>> {
let snapshot = snapshot.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let result = unsafe {
BNGetTypeArchiveTypeById(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
)
};
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
/// Retrieve a type's name by its id
///
/// * `id` - Type id
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_name_by_id<I: BnStrCompatible, S: BnStrCompatible>(
&self,
id: I,
snapshot: S,
) -> QualifiedName {
let snapshot = snapshot.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let result = unsafe {
BNGetTypeArchiveTypeName(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
)
};
QualifiedName(result)
}
/// Retrieve a type's id by its name
///
/// * `name` - Type name
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_id<S: BnStrCompatible>(
&self,
name: &QualifiedName,
snapshot: S,
) -> Option<BnString> {
let snapshot = snapshot.into_bytes_with_nul();
let result = unsafe {
BNGetTypeArchiveTypeId(
self.as_raw(),
&name.0,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
)
};
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
}
/// Retrieve all stored types in the archive at a snapshot
///
/// * `snapshot` - Snapshot id to search for types
pub fn get_types_and_ids<S: BnStrCompatible>(
&self,
snapshot: S,
) -> Array<QualifiedNameTypeAndId> {
let snapshot = snapshot.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveTypes(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get a list of all types' ids in the archive at a snapshot
///
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_ids<S: BnStrCompatible>(&self, snapshot: S) -> Array<BnString> {
let snapshot = snapshot.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveTypeIds(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get a list of all types' names in the archive at a snapshot
///
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_names<S: BnStrCompatible>(&self, snapshot: S) -> Array<QualifiedName> {
let snapshot = snapshot.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveTypeNames(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get a list of all types' names and ids in the archive at a current snapshot
/// * `snapshot` - Snapshot id to search for types
pub fn get_type_names_and_ids<S: BnStrCompatible>(
&self,
snapshot: S,
) -> (Array<QualifiedName>, Array<BnString>) {
let snapshot = snapshot.into_bytes_with_nul();
let mut count = 0;
let mut names = ptr::null_mut();
let mut ids = ptr::null_mut();
let result = unsafe {
BNGetTypeArchiveTypeNamesAndIds(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut names,
&mut ids,
&mut count,
)
};
assert!(result);
(unsafe { Array::new(names, count, ()) }, unsafe {
Array::new(ids, count, ())
})
}
/// Get all types a given type references directly
///
/// * `id` - Source type id
/// * `snapshot` - Snapshot id to search for types
pub fn get_outgoing_direct_references<I: BnStrCompatible, S: BnStrCompatible>(
&self,
id: I,
snapshot: S,
) -> Array<BnString> {
let snapshot = snapshot.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveOutgoingDirectTypeReferences(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get all types a given type references, and any types that the referenced types reference
///
/// :param id: Source type id
/// :param snapshot: Snapshot id to search for types
pub fn get_outgoing_recursive_references<I: BnStrCompatible, S: BnStrCompatible>(
&self,
id: I,
snapshot: S,
) -> Array<BnString> {
let snapshot = snapshot.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveOutgoingRecursiveTypeReferences(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get all types that reference a given type
///
/// * `id` - Target type id
/// * `snapshot` - Snapshot id to search for types
pub fn get_incoming_direct_references<I: BnStrCompatible, S: BnStrCompatible>(
&self,
id: I,
snapshot: S,
) -> Array<BnString> {
let snapshot = snapshot.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveIncomingDirectTypeReferences(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Get all types that reference a given type, and all types that reference them, recursively
///
/// * `id` - Target type id
/// * `snapshot` - Snapshot id to search for types, or empty string to search the latest snapshot
pub fn get_incoming_recursive_references<I: BnStrCompatible, S: BnStrCompatible>(
&self,
id: I,
snapshot: S,
) -> Array<BnString> {
let snapshot = snapshot.into_bytes_with_nul();
let id = id.into_bytes_with_nul();
let mut count = 0;
let result = unsafe {
BNGetTypeArchiveIncomingRecursiveTypeReferences(
self.as_raw(),
id.as_ref().as_ptr() as *const ffi::c_char,
snapshot.as_ref().as_ptr() as *const ffi::c_char,
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Look up a metadata entry in the archive
pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Option<Ref<Metadata>> {
let key = key.into_bytes_with_nul();
let result = unsafe {
BNTypeArchiveQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
};
(!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) })
}
/// Store a key/value pair in the archive's metadata storage
///
/// * `key` - key value to associate the Metadata object with
/// * `md` - object to store.
pub fn store_metadata<S: BnStrCompatible>(&self, key: S, md: &Metadata) {
let key = key.into_bytes_with_nul();
let result = unsafe {
BNTypeArchiveStoreMetadata(
self.as_raw(),
key.as_ref().as_ptr() as *const ffi::c_char,
md.handle,
)
};
assert!(result);
}
/// Delete a given metadata entry in the archive from the `key`
pub fn remove_metadata<S: BnStrCompatible>(&self, key: S) -> bool {
let key = key.into_bytes_with_nul();
unsafe {
BNTypeArchiveRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
}
}
/// Turn a given `snapshot` id into a data stream
pub fn serialize_snapshot<S: BnStrCompatible>(&self, snapshot: S) -> DataBuffer {
let snapshot = snapshot.into_bytes_with_nul();
let result = unsafe {
BNTypeArchiveSerializeSnapshot(
self.as_raw(),
snapshot.as_ref().as_ptr() as *const ffi::c_char,
)
};
assert!(!result.is_null());
DataBuffer::from_raw(result)
}
/// Take a serialized snapshot `data` stream and create a new snapshot from it
pub fn deserialize_snapshot(&self, data: &DataBuffer) -> BnString {
let result = unsafe { BNTypeArchiveDeserializeSnapshot(self.as_raw(), data.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
/// Register a notification listener
pub fn register_notification_callback<T: TypeArchiveNotificationCallback>(
&self,
callback: T,
) -> TypeArchiveCallbackHandle<T> {
// SAFETY free on [TypeArchiveCallbackHandle::Drop]
let callback = Box::leak(Box::new(callback));
let mut notification = BNTypeArchiveNotification {
context: callback as *mut T as *mut ffi::c_void,
typeAdded: Some(cb_type_added::<T>),
typeUpdated: Some(cb_type_updated::<T>),
typeRenamed: Some(cb_type_renamed::<T>),
typeDeleted: Some(cb_type_deleted::<T>),
};
unsafe { BNRegisterTypeArchiveNotification(self.as_raw(), &mut notification) }
TypeArchiveCallbackHandle {
callback,
type_archive: self.clone(),
}
}
// NOTE NotificationClosure is left private, there is no need for the user
// to know or use it.
#[allow(private_interfaces)]
pub fn register_notification_closure<A, U, R, D>(
&self,
type_added: A,
type_updated: U,
type_renamed: R,
type_deleted: D,
) -> TypeArchiveCallbackHandle<NotificationClosure<A, U, R, D>>
where
A: FnMut(&TypeArchive, &str, &Type),
U: FnMut(&TypeArchive, &str, &Type, &Type),
R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
D: FnMut(&TypeArchive, &str, &Type),
{
self.register_notification_callback(NotificationClosure {
fun_type_added: type_added,
fun_type_updated: type_updated,
fun_type_renamed: type_renamed,
fun_type_deleted: type_deleted,
})
}
/// Close a type archive, disconnecting it from any active views and closing
/// any open file handles
pub fn close(self) {
unsafe { BNCloseTypeArchive(self.as_raw()) }
// NOTE self must be dropped after, don't make it `&self`
}
/// Determine if `file` is a Type Archive
pub fn is_type_archive<P: BnStrCompatible>(file: P) -> bool {
let file = file.into_bytes_with_nul();
unsafe { BNIsTypeArchive(file.as_ref().as_ptr() as *const ffi::c_char) }
}
// TODO implement TypeContainer
///// Get the TypeContainer interface for this Type Archive, presenting types
///// at the current snapshot in the archive.
//pub fn type_container(&self) -> TypeContainer {
// let result = unsafe { BNGetTypeArchiveTypeContainer(self.as_raw()) };
// unsafe { TypeContainer::from_raw(ptr::NonNull::new(result).unwrap()) }
//}
/// Do some function in a transaction making a new snapshot whose id is passed to func. If func throws,
/// the transaction will be rolled back and the snapshot will not be created.
///
/// * `func` - Function to call
/// * `parents` - Parent snapshot ids
///
/// Returns Created snapshot id
pub fn new_snapshot_transaction<P, F>(&self, mut function: F, parents: &[BnString]) -> BnString
where
P: BnStrCompatible,
F: FnMut(&str) -> bool,
{
unsafe extern "C" fn cb_callback<F: FnMut(&str) -> bool>(
ctxt: *mut ffi::c_void,
id: *const ffi::c_char,
) -> bool {
let fun: &mut F = &mut *(ctxt as *mut F);
fun(&ffi::CStr::from_ptr(id).to_string_lossy())
}
// SAFETY BnString and `*const ffi::c_char` are transparent
let parents_raw = parents.as_ptr() as *const *const ffi::c_char;
let result = unsafe {
BNTypeArchiveNewSnapshotTransaction(
self.as_raw(),
Some(cb_callback::<F>),
&mut function as *mut F as *mut ffi::c_void,
parents_raw,
parents.len(),
)
};
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
/// Merge two snapshots in the archive to produce a new snapshot
///
/// * `base_snapshot` - Common ancestor of snapshots
/// * `first_snapshot` - First snapshot to merge
/// * `second_snapshot` - Second snapshot to merge
/// * `merge_conflicts` - List of all conflicting types, id <-> target snapshot
/// * `progress` - Function to call for progress updates
///
/// Returns Snapshot id, if merge was successful, otherwise the List of
/// conflicting type ids
pub fn merge_snapshots<B, F, S, P, M, MI, MK>(
&self,
base_snapshot: B,
first_snapshot: F,
second_snapshot: S,
merge_conflicts: M,
mut progress: P,
) -> Result<BnString, Array<BnString>>
where
B: BnStrCompatible,
F: BnStrCompatible,
S: BnStrCompatible,
P: FnMut(usize, usize) -> bool,
M: IntoIterator<Item = (MI, MK)>,
MI: BnStrCompatible,
MK: BnStrCompatible,
{
unsafe extern "C" fn cb_callback<F: FnMut(usize, usize) -> bool>(
ctxt: *mut ffi::c_void,
progress: usize,
total: usize,
) -> bool {
let ctxt: &mut F = &mut *(ctxt as *mut F);
ctxt(progress, total)
}
let base_snapshot = base_snapshot.into_bytes_with_nul();
let first_snapshot = first_snapshot.into_bytes_with_nul();
let second_snapshot = second_snapshot.into_bytes_with_nul();
let (merge_keys, merge_values): (Vec<BnString>, Vec<BnString>) = merge_conflicts
.into_iter()
.map(|(k, v)| (BnString::new(k), BnString::new(v)))
.unzip();
// SAFETY BnString and `*const ffi::c_char` are transparent
let merge_keys_raw = merge_keys.as_ptr() as *const *const ffi::c_char;
let merge_values_raw = merge_values.as_ptr() as *const *const ffi::c_char;
let mut conflicts_errors = ptr::null_mut();
let mut conflicts_errors_count = 0;
let mut result = ptr::null_mut();
let success = unsafe {
BNTypeArchiveMergeSnapshots(
self.as_raw(),
base_snapshot.as_ref().as_ptr() as *const ffi::c_char,
first_snapshot.as_ref().as_ptr() as *const ffi::c_char,
second_snapshot.as_ref().as_ptr() as *const ffi::c_char,
merge_keys_raw,
merge_values_raw,
merge_keys.len(),
&mut conflicts_errors,
&mut conflicts_errors_count,
&mut result,
Some(cb_callback::<P>),
(&mut progress) as *mut P as *mut ffi::c_void,
)
};
if success {
assert!(!result.is_null());
Ok(unsafe { BnString::from_raw(result) })
} else {
assert!(!conflicts_errors.is_null());
Err(unsafe { Array::new(conflicts_errors, conflicts_errors_count, ()) })
}
}
}
impl CoreArrayProvider for TypeArchive {
type Raw = *mut BNTypeArchive;
type Context = ();
type Wrapped<'a> = &'a TypeArchive;
}
unsafe impl CoreArrayProviderInner for TypeArchive {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTypeArchiveList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
pub struct TypeArchiveCallbackHandle<T: TypeArchiveNotificationCallback> {
callback: *mut T,
type_archive: TypeArchive,
}
impl<T: TypeArchiveNotificationCallback> Drop for TypeArchiveCallbackHandle<T> {
fn drop(&mut self) {
let mut notification = BNTypeArchiveNotification {
context: self.callback as *mut ffi::c_void,
typeAdded: Some(cb_type_added::<T>),
typeUpdated: Some(cb_type_updated::<T>),
typeRenamed: Some(cb_type_renamed::<T>),
typeDeleted: Some(cb_type_deleted::<T>),
};
// unregister the notification callback
unsafe {
BNUnregisterTypeArchiveNotification(self.type_archive.as_raw(), &mut notification)
}
// free the context created at [TypeArchive::register_notification_callback]
drop(unsafe { Box::from_raw(self.callback) });
}
}
pub trait TypeArchiveNotificationCallback {
/// Called when a type is added to the archive
///
/// * `archive` - Source Type archive
/// * `id` - Id of type added
/// * `definition` - Definition of type
fn type_added(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
/// Called when a type in the archive is updated to a new definition
///
/// * `archive` - Source Type archive
/// * `id` - Id of type
/// * `old_definition` - Previous definition
/// * `new_definition` - Current definition
fn type_updated(
&mut self,
_archive: &TypeArchive,
_id: &str,
_old_definition: &Type,
_new_definition: &Type,
) {
}
/// Called when a type in the archive is renamed
///
/// * `archive` - Source Type archive
/// * `id` - Type id
/// * `old_name` - Previous name
/// * `new_name` - Current name
fn type_renamed(
&mut self,
_archive: &TypeArchive,
_id: &str,
_old_name: &QualifiedName,
_new_name: &QualifiedName,
) {
}
/// Called when a type in the archive is deleted from the archive
///
/// * `archive` - Source Type archive
/// * `id` - Id of type deleted
/// * `definition` - Definition of type deleted
fn type_deleted(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
}
struct NotificationClosure<A, U, R, D>
where
A: FnMut(&TypeArchive, &str, &Type),
U: FnMut(&TypeArchive, &str, &Type, &Type),
R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
D: FnMut(&TypeArchive, &str, &Type),
{
fun_type_added: A,
fun_type_updated: U,
fun_type_renamed: R,
fun_type_deleted: D,
}
impl<A, U, R, D> TypeArchiveNotificationCallback for NotificationClosure<A, U, R, D>
where
A: FnMut(&TypeArchive, &str, &Type),
U: FnMut(&TypeArchive, &str, &Type, &Type),
R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
D: FnMut(&TypeArchive, &str, &Type),
{
fn type_added(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
(self.fun_type_added)(archive, id, definition)
}
fn type_updated(
&mut self,
archive: &TypeArchive,
id: &str,
old_definition: &Type,
new_definition: &Type,
) {
(self.fun_type_updated)(archive, id, old_definition, new_definition)
}
fn type_renamed(
&mut self,
archive: &TypeArchive,
id: &str,
old_name: &QualifiedName,
new_name: &QualifiedName,
) {
(self.fun_type_renamed)(archive, id, old_name, new_name)
}
fn type_deleted(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
(self.fun_type_deleted)(archive, id, definition)
}
}
unsafe extern "C" fn cb_type_added<T: TypeArchiveNotificationCallback>(
ctxt: *mut ::std::os::raw::c_void,
archive: *mut BNTypeArchive,
id: *const ::std::os::raw::c_char,
definition: *mut BNType,
) {
let ctxt: &mut T = &mut *(ctxt as *mut T);
ctxt.type_added(
unsafe { TypeArchive::ref_from_raw(&archive) },
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
&Type { handle: definition },
)
}
unsafe extern "C" fn cb_type_updated<T: TypeArchiveNotificationCallback>(
ctxt: *mut ::std::os::raw::c_void,
archive: *mut BNTypeArchive,
id: *const ::std::os::raw::c_char,
old_definition: *mut BNType,
new_definition: *mut BNType,
) {
let ctxt: &mut T = &mut *(ctxt as *mut T);
ctxt.type_updated(
unsafe { TypeArchive::ref_from_raw(&archive) },
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
&Type {
handle: old_definition,
},
&Type {
handle: new_definition,
},
)
}
unsafe extern "C" fn cb_type_renamed<T: TypeArchiveNotificationCallback>(
ctxt: *mut ::std::os::raw::c_void,
archive: *mut BNTypeArchive,
id: *const ::std::os::raw::c_char,
old_name: *const BNQualifiedName,
new_name: *const BNQualifiedName,
) {
let ctxt: &mut T = &mut *(ctxt as *mut T);
let old_name = mem::ManuallyDrop::new(QualifiedName(*old_name));
let new_name = mem::ManuallyDrop::new(QualifiedName(*new_name));
ctxt.type_renamed(
unsafe { TypeArchive::ref_from_raw(&archive) },
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
&old_name,
&new_name,
)
}
unsafe extern "C" fn cb_type_deleted<T: TypeArchiveNotificationCallback>(
ctxt: *mut ::std::os::raw::c_void,
archive: *mut BNTypeArchive,
id: *const ::std::os::raw::c_char,
definition: *mut BNType,
) {
let ctxt: &mut T = &mut *(ctxt as *mut T);
ctxt.type_deleted(
unsafe { TypeArchive::ref_from_raw(&archive) },
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
&Type { handle: definition },
)
}

367
src/typelibrary.rs Normal file
View File

@@ -0,0 +1,367 @@
use binaryninjacore_sys::*;
use core::{ffi, mem, ptr};
use crate::{
architecture::CoreArchitecture,
metadata::Metadata,
platform::Platform,
rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref},
string::{BnStrCompatible, BnString},
types::{QualifiedName, QualifiedNameAndType, Type},
};
#[repr(transparent)]
pub struct TypeLibrary {
handle: ptr::NonNull<BNTypeLibrary>,
}
impl TypeLibrary {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNTypeLibrary>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeLibrary) -> &Self {
assert!(!handle.is_null());
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeLibrary {
&mut *self.handle.as_ptr()
}
pub fn new_reference(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewTypeLibraryReference(self.as_raw())).unwrap())
}
}
pub fn new_duplicated(&self) -> Self {
unsafe { Self::from_raw(ptr::NonNull::new(BNDuplicateTypeLibrary(self.as_raw())).unwrap()) }
}
/// Creates an empty type library object with a random GUID and the provided name.
pub fn new<S: BnStrCompatible>(arch: CoreArchitecture, name: S) -> TypeLibrary {
let name = name.into_bytes_with_nul();
let new_lib =
unsafe { BNNewTypeLibrary(arch.0, name.as_ref().as_ptr() as *const ffi::c_char) };
unsafe { TypeLibrary::from_raw(ptr::NonNull::new(new_lib).unwrap()) }
}
pub fn all(arch: CoreArchitecture) -> Array<TypeLibrary> {
let mut count = 0;
let result = unsafe { BNGetArchitectureTypeLibraries(arch.0, &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Decompresses a type library file to a file on disk.
pub fn decompress_to_file<P: BnStrCompatible, O: BnStrCompatible>(path: P, output: O) -> bool {
let path = path.into_bytes_with_nul();
let output = output.into_bytes_with_nul();
unsafe {
BNTypeLibraryDecompressToFile(
path.as_ref().as_ptr() as *const ffi::c_char,
output.as_ref().as_ptr() as *const ffi::c_char,
)
}
}
/// Loads a finalized type library instance from file
pub fn load_from_file<S: BnStrCompatible>(path: S) -> Option<TypeLibrary> {
let path = path.into_bytes_with_nul();
let handle =
unsafe { BNLoadTypeLibraryFromFile(path.as_ref().as_ptr() as *const ffi::c_char) };
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
/// Saves a finalized type library instance to file
pub fn write_to_file<S: BnStrCompatible>(&self, path: S) -> bool {
let path = path.into_bytes_with_nul();
unsafe {
BNWriteTypeLibraryToFile(self.as_raw(), path.as_ref().as_ptr() as *const ffi::c_char)
}
}
/// Looks up the first type library found with a matching name. Keep in mind that names are not
/// necessarily unique.
pub fn from_name<S: BnStrCompatible>(arch: CoreArchitecture, name: S) -> Option<TypeLibrary> {
let name = name.into_bytes_with_nul();
let handle = unsafe {
BNLookupTypeLibraryByName(arch.0, name.as_ref().as_ptr() as *const ffi::c_char)
};
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
/// Attempts to grab a type library associated with the provided Architecture and GUID pair
pub fn from_guid<S: BnStrCompatible>(arch: CoreArchitecture, guid: S) -> Option<TypeLibrary> {
let guid = guid.into_bytes_with_nul();
let handle = unsafe {
BNLookupTypeLibraryByGuid(arch.0, guid.as_ref().as_ptr() as *const ffi::c_char)
};
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
/// The Architecture this type library is associated with
pub fn arch(&self) -> CoreArchitecture {
let arch = unsafe { BNGetTypeLibraryArchitecture(self.as_raw()) };
assert!(!arch.is_null());
CoreArchitecture(arch)
}
/// The primary name associated with this type library
pub fn name(&self) -> Option<BnString> {
let result = unsafe { BNGetTypeLibraryName(self.as_raw()) };
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
}
/// Sets the name of a type library instance that has not been finalized
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
unsafe {
BNSetTypeLibraryName(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char)
}
}
/// The `dependency_name` of a library is the name used to record dependencies across
/// type libraries. This allows, for example, a library with the name "musl_libc" to have
/// dependencies on it recorded as "libc_generic", allowing a type library to be used across
/// multiple platforms where each has a specific libc that also provides the name "libc_generic"
/// as an `alternate_name`.
pub fn dependency_name(&self) -> Option<BnString> {
let result = unsafe { BNGetTypeLibraryDependencyName(self.as_raw()) };
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
}
/// Sets the dependency name of a type library instance that has not been finalized
pub fn set_dependency_name<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
unsafe {
BNSetTypeLibraryDependencyName(
self.as_raw(),
value.as_ref().as_ptr() as *const ffi::c_char,
)
}
}
/// Returns the GUID associated with the type library
pub fn guid(&self) -> Option<BnString> {
let result = unsafe { BNGetTypeLibraryGuid(self.as_raw()) };
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
}
/// Sets the GUID of a type library instance that has not been finalized
pub fn set_guid<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
unsafe {
BNSetTypeLibraryGuid(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char)
}
}
/// A list of extra names that will be considered a match by [Platform::get_type_libraries_by_name]
pub fn alternate_names(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetTypeLibraryAlternateNames(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Adds an extra name to this type library used during library lookups and dependency resolution
pub fn add_alternate_name<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
unsafe {
BNAddTypeLibraryAlternateName(
self.as_raw(),
value.as_ref().as_ptr() as *const ffi::c_char,
)
}
}
/// Returns a list of all platform names that this type library will register with during platform
/// type registration.
///
/// This returns strings, not Platform objects, as type libraries can be distributed with support for
/// Platforms that may not be present.
pub fn platform_names(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetTypeLibraryPlatforms(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// Associate a platform with a type library instance that has not been finalized.
///
/// This will cause the library to be searchable by [Platform::get_type_libraries_by_name]
/// when loaded.
///
/// This does not have side affects until finalization of the type library.
pub fn add_platform(&self, plat: &Platform) {
unsafe { BNAddTypeLibraryPlatform(self.as_raw(), plat.handle) }
}
/// Clears the list of platforms associated with a type library instance that has not been finalized
pub fn clear_platforms(&self) {
unsafe { BNClearTypeLibraryPlatforms(self.as_raw()) }
}
/// Flags a newly created type library instance as finalized and makes it available for Platform and Architecture
/// type library searches
pub fn finalize(&self) -> bool {
unsafe { BNFinalizeTypeLibrary(self.as_raw()) }
}
/// Retrieves a metadata associated with the given key stored in the type library
pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Option<Metadata> {
let key = key.into_bytes_with_nul();
let result = unsafe {
BNTypeLibraryQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
};
(!result.is_null()).then(|| unsafe { Metadata::from_raw(result) })
}
/// Stores an object for the given key in the current type library. Objects stored using
/// `store_metadata` can be retrieved from any reference to the library. Objects stored are not arbitrary python
/// objects! The values stored must be able to be held in a Metadata object. See [Metadata]
/// for more information. Python objects could obviously be serialized using pickle but this intentionally
/// a task left to the user since there is the potential security issues.
///
/// This is primarily intended as a way to store Platform specific information relevant to BinaryView implementations;
/// for example the PE BinaryViewType uses type library metadata to retrieve ordinal information, when available.
///
/// * `key` - key value to associate the Metadata object with
/// * `md` - object to store.
pub fn store_metadata<S: BnStrCompatible>(&self, key: S, md: &Metadata) {
let key = key.into_bytes_with_nul();
unsafe {
BNTypeLibraryStoreMetadata(
self.as_raw(),
key.as_ref().as_ptr() as *const ffi::c_char,
md.handle,
)
}
}
/// Removes the metadata associated with key from the current type library.
pub fn remove_metadata<S: BnStrCompatible>(&self, key: S) {
let key = key.into_bytes_with_nul();
unsafe {
BNTypeLibraryRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
}
}
/// Retrieves the metadata associated with the current type library.
pub fn metadata(&self) -> Metadata {
let md_handle = unsafe { BNTypeLibraryGetMetadata(self.as_raw()) };
assert!(!md_handle.is_null());
unsafe { Metadata::from_raw(md_handle) }
}
// TODO: implement TypeContainer
// /// Type Container for all TYPES within the Type Library. Objects are not included.
// /// The Type Container's Platform will be the first platform associated with the Type Library.
// pub fn type_container(&self) -> TypeContainer {
// let result = unsafe{ BNGetTypeLibraryTypeContainer(self.as_raw())};
// unsafe{TypeContainer::from_raw(ptr::NonNull::new(result).unwrap())}
// }
/// Directly inserts a named object into the type library's object store.
/// This is not done recursively, so care should be taken that types referring to other types
/// through NamedTypeReferences are already appropriately prepared.
///
/// To add types and objects from an existing BinaryView, it is recommended to use
/// `export_object_to_library <binaryview.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) {
unsafe {
BNAddTypeLibraryNamedObject(self.as_raw(), &name.0 as *const _ as *mut _, type_.handle)
}
}
/// Directly inserts a named object into the type library's object store.
/// This is not done recursively, so care should be taken that types referring to other types
/// through NamedTypeReferences are already appropriately prepared.
///
/// To add types and objects from an existing BinaryView, it is recommended to use
/// `export_type_to_library <binaryview.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: &QualifiedNameAndType, type_: &Type) {
unsafe {
BNAddTypeLibraryNamedType(self.as_raw(), &name.0 as *const _ as *mut _, type_.handle)
}
}
/// Manually flag NamedTypeReferences to the given QualifiedName as originating from another source
/// TypeLibrary with the given dependency name.
///
/// <div class="warning">
///
/// Use this api with extreme caution.
///
/// </div/
pub fn add_type_source<S: BnStrCompatible>(&self, name: &QualifiedName, source: S) {
let source = source.into_bytes_with_nul();
unsafe {
BNAddTypeLibraryNamedTypeSource(
self.as_raw(),
&name.0 as *const _ as *mut _,
source.as_ref().as_ptr() as *const ffi::c_char,
)
}
}
/// Direct extracts a reference to a contained object -- when
/// attempting to extract types from a library into a BinaryView, consider using
/// `import_library_object <binaryview.BinaryView.import_library_object>` instead.
pub fn get_named_object(&self, name: &QualifiedName) -> Option<Ref<Type>> {
let t =
unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &name.0 as *const _ as *mut _) };
(!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
}
/// Direct extracts a reference to a contained type -- when
/// attempting to extract types from a library into a BinaryView, consider using
/// `import_library_type <binaryview.BinaryView.import_library_type>` instead.
pub fn get_named_type(&self, name: &QualifiedName) -> Option<Ref<Type>> {
let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &name.0 as *const _ as *mut _) };
(!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
}
/// A dict containing all named objects (functions, exported variables) provided by a type library
pub fn named_objects(&self) -> Array<QualifiedNameAndType> {
let mut count = 0;
let result = unsafe { BNGetTypeLibraryNamedObjects(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
/// A dict containing all named types provided by a type library
pub fn named_types(&self) -> Array<QualifiedNameAndType> {
let mut count = 0;
let result = unsafe { BNGetTypeLibraryNamedTypes(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
}
impl Drop for TypeLibrary {
fn drop(&mut self) {
unsafe { BNFreeTypeLibrary(self.as_raw()) }
}
}
impl CoreArrayProvider for TypeLibrary {
type Raw = *mut BNTypeLibrary;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for TypeLibrary {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTypeLibraryList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}

File diff suppressed because it is too large Load Diff

272
src/update.rs Normal file
View File

@@ -0,0 +1,272 @@
use core::{ffi, mem, ptr};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use binaryninjacore_sys::*;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
use crate::string::BnString;
pub type UpdateResult = BNUpdateResult;
#[repr(C)]
pub struct UpdateChannel {
pub name: BnString,
pub description: BnString,
pub latest_version: BnString,
// NOTE don't allow the user to create his own UpdateChannel
_lock: core::marker::PhantomData<()>,
}
impl UpdateChannel {
pub(crate) unsafe fn ref_from_raw(handle: &BNUpdateChannel) -> &Self {
mem::transmute(handle)
}
pub fn all() -> Result<Array<UpdateChannel>, BnString> {
let mut count = 0;
let mut errors = ptr::null_mut();
let result = unsafe { BNGetUpdateChannels(&mut count, &mut errors) };
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
assert!(!result.is_null());
Ok(unsafe { Array::new(result, count, ()) })
}
}
/// List of versions
pub fn versions(&self) -> Result<Array<UpdateVersion>, BnString> {
let mut count = 0;
let mut errors = ptr::null_mut();
let result =
unsafe { BNGetUpdateChannelVersions(self.name.as_ptr(), &mut count, &mut errors) };
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
assert!(!result.is_null());
Ok(unsafe { Array::new(result, count, ()) })
}
}
/// Latest version
pub fn latest_version(&self) -> Result<UpdateVersion, BnString> {
let last_version = &self.latest_version;
let versions = self.versions()?;
for version in &versions {
if &version.version == last_version {
return Ok(version.clone());
}
}
panic!();
}
/// Whether updates are available
pub fn updates_available(&self) -> Result<bool, BnString> {
let mut errors = ptr::null_mut();
let result = unsafe {
BNAreUpdatesAvailable(
self.name.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
&mut errors,
)
};
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
Ok(result)
}
}
pub fn update_to_latest(&self) -> Result<UpdateResult, BnString> {
let mut errors = ptr::null_mut();
let result = unsafe {
BNUpdateToLatestVersion(
self.name.as_ptr(),
&mut errors,
Some(cb_progress_nop),
ptr::null_mut(),
)
};
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
Ok(result)
}
}
pub fn update_to_latest_with_progress<F>(
&self,
mut progress: F,
) -> Result<UpdateResult, BnString>
where
F: FnMut(u64, u64) -> bool,
{
let mut errors = ptr::null_mut();
let result = unsafe {
BNUpdateToLatestVersion(
self.name.as_ptr(),
&mut errors,
Some(cb_progress::<F>),
&mut progress as *mut _ as *mut ffi::c_void,
)
};
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
Ok(result)
}
}
pub fn update(&self, version: &UpdateVersion) -> Result<UpdateResult, BnString> {
let mut errors = ptr::null_mut();
let result = unsafe {
BNUpdateToVersion(
self.name.as_ptr(),
version.version.as_ptr(),
&mut errors,
Some(cb_progress_nop),
ptr::null_mut(),
)
};
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
Ok(result)
}
}
pub fn update_with_progress<F>(
&self,
version: &UpdateVersion,
mut progress: F,
) -> Result<UpdateResult, BnString>
where
F: FnMut(u64, u64) -> bool,
{
let mut errors = ptr::null_mut();
let result = unsafe {
BNUpdateToVersion(
self.name.as_ptr(),
version.version.as_ptr(),
&mut errors,
Some(cb_progress::<F>),
&mut progress as *mut _ as *mut ffi::c_void,
)
};
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
Ok(result)
}
}
}
impl CoreArrayProvider for UpdateChannel {
type Raw = BNUpdateChannel;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for UpdateChannel {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeUpdateChannelList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
UpdateChannel::ref_from_raw(raw)
}
}
#[repr(C)]
#[derive(Clone)]
pub struct UpdateVersion {
pub version: BnString,
pub notes: BnString,
time: u64,
// NOTE don't allow the user to create his own UpdateVersion
_lock: core::marker::PhantomData<()>,
}
impl UpdateVersion {
pub(crate) unsafe fn ref_from_raw(handle: &BNUpdateVersion) -> &Self {
mem::transmute(handle)
}
pub fn time(&self) -> SystemTime {
UNIX_EPOCH + Duration::from_secs(self.time)
}
pub fn set_time(&mut self, time: SystemTime) {
let epoch = time.duration_since(UNIX_EPOCH).unwrap();
self.time = epoch.as_secs();
}
}
impl CoreArrayProvider for UpdateVersion {
type Raw = BNUpdateVersion;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for UpdateVersion {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeUpdateChannelVersionList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
UpdateVersion::ref_from_raw(raw)
}
}
/// queries if auto updates are enabled.
pub fn are_auto_updates_enabled() -> bool {
unsafe { BNAreAutoUpdatesEnabled() }
}
/// sets auto update enabled status.
pub fn set_auto_updates_enabled(enabled: bool) {
unsafe { BNSetAutoUpdatesEnabled(enabled) }
}
/// returns the time stamp for the last time updates were checked.
pub fn get_time_since_last_update_check() -> u64 {
unsafe { BNGetTimeSinceLastUpdateCheck() }
}
/// whether an update has been downloaded and is waiting installation
pub fn is_update_installation_pending() -> bool {
unsafe { BNIsUpdateInstallationPending() }
}
/// installs any pending updates
pub fn install_pending_update() -> Result<(), BnString> {
let mut errors = ptr::null_mut();
unsafe { BNInstallPendingUpdate(&mut errors) };
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
Ok(())
}
}
pub fn updates_checked() {
unsafe { BNUpdatesChecked() }
}
unsafe extern "C" fn cb_progress_nop(
_ctxt: *mut ::std::os::raw::c_void,
_progress: u64,
_total: u64,
) -> bool {
true
}
unsafe extern "C" fn cb_progress<F: FnMut(u64, u64) -> bool>(
ctxt: *mut ::std::os::raw::c_void,
progress: u64,
total: u64,
) -> bool {
let ctxt: &mut F = &mut *(ctxt as *mut F);
ctxt(progress, total)
}