From dfa1d0f867000d83eebcc5639fd7f6f21f74cc07 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Tue, 7 May 2024 09:52:19 -0300 Subject: [PATCH] Implement python Function methods to rust Squashed: add Function::add_tag method add Function::{add_user_code_ref,remove_user_code_ref} methods add Function::{add_user_type_field_ref, remove_user_type_field_ref} methods add Function::{add_user_type_ref, remove_user_type_ref} methods add Function::apply_auto_discovered_type add Function/MediumLevelILFunction user_var_values and related functions simplify UserVariableValues::values_from_variable cargo fmt fix fix doc and QualifiedName params fix MediumLevelILFunction::is_var_user_defined parameter Remove unecessary Safety comment add Function::{create_auto_stack_var,delete_auto_stack_var} methods add MediumLevelILFunction::create_auto_var method move Function::{create_auto_stack_var,delete_auto_stack_var} methods to MediumLevelILFunction add MediumLevelILFunction::{create_user_stack_var,delete_user_stack_var} methods add Function::block_annotations method add Function::{call_stack_adjustment, call_type_adjustment} method add Function::{constant_data, constants_referenced_by, constants_referenced_by_address_if_available} methods add Function::function_tags method add Function::{indirect_branches, indirect_branches_at} methods add Function::instr_highlight method add Function::instruction_containing_address method add Function::{int_display_type,int_enum_display_typeid,int_display_type_and_typeid} methods add MediumLevelILFunction::{var_refs, var_refs_from} methods add Function::{parameter_at, parameter_at_low_level_il_instruction} methods rebase GAT changes add Function::{reg_value_at, reg_value_after, reg_value_at_exit} methods Add alpha to HighlightColor::NoHighlightColor add Function::{regs_read_by, regs_written_by} methods add Function::{stack_contents_at, stack_contents_after} methods add Function::{stack_vars_referenced_by, stack_vars_referenced_by_address_if_available} methods add Function::{tags_at, tags_at_range} methods add Function::type_tokens method add Function::variables method add Function::{is_call_instruction, is_var_user_defined} methods add Function::{mark_updates_required, mark_caller_updates_required, mark_recent_use} methods add Function::{merge_vars, unmerge_vars, split_var, unsplit_var} methods add Function::reanalyze method add Function::request_debug_report method add Function::remove_tag method add Function::remove_tags_of_type method add Function::analysis_performance_info method add Function::call_sites method add Function::caller_sites method add Function::{calling_convention, set_calling_convention} methods add Function::{can_return, set_can_return} methods add Function::comments method add Function::{clobbered_regs, set_clobbered_regs} methods add Function::{has_explicitly_defined_type, has_user_annotations, has_user_type, has_variable_arguments} methods fix Function::set_can_return_user function add Function::{high_level_il_if_available, medium_level_il_if_available, low_level_il_if_available, lifted_il_if_available} methods add Function::{global_pointer_value, has_unresolved_indirect_branches} methods add Function::lowest_address method add Function::{inline_during_analysis, set_auto_inline_during_analysis, set_user_inline_during_analysis} methods add Function::{is_pure, is_too_large, is_update_needed} methods add Function::{provenance, stack_adjustment, set_user_stack_adjustment, set_auto_stack_adjustment} methods add Function::{set_user_pure, set_auto_pure} methods add Function::set_auto_type method add Function::{return_regs, set_return_regs} methods add Function::{set_auto_return_type, set_user_return_type} methods add Function::unresolved_stack_adjustment_graph method add Function::create_graph method add Function::tags method add Function::split_variables method add Function::{reg_stack_adjustments, set_reg_stack_adjustments} methods add Function::{mapped_medium_level_il, merged_variables} methods add Function::{set_auto_instr_highlight, set_user_instr_highlight} methods add stack_adjustments methods add Function::set_user_call_stack_adjustment add Function::set_auto_call_stack_adjustment add Function::call_type_adjustment add Function::set_user_call_type_adjustment add Function::set_auto_call_type_adjustment add Function::call_reg_stack_adjustment add Function::set_user_call_reg_stack_adjustment add Function::set_auto_call_reg_stack_adjustment add Function::call_reg_stack_adjustment_for_reg_stack add Function::set_user_call_reg_stack_adjustment_for_reg_stack add Function::set_auto_call_reg_stack_adjustment_for_reg_stack add Function::set_user_reg_stack_adjustments add Function::set_auto_reg_stack_adjustments add Function::set_auto_reg_stack_adjustments add Function::set_auto_calling_convention method add Function::set_auto_can_return method add Function::{set_user_has_variable_arguments, set_auto_has_variable_arguments} methods add Function::{set_user_indirect_branches, set_auto_indirect_branches} methods add Function::set_int_display_type method add Function::{set_user_parameter_variables, set_auto_parameter_variables} methods fix documentation add Function::unresolved_indirect_branches method add Function::mapped_medium_level_il_if_available method change methods `Function::{set_user_type, set_auto_type}` to receive `&Type` merge RegisterList and RegisterSet into CoreRegister impl fix function names fix function names fix documentation syntax fix documentation syntax fix Function documentation by adding MediumLevelILInstruction::get_split_var_for_definition fix documentation links rename `VariableDefinitions` to `MediumLevelILInstructionList` fix `PossibleValueSet` switched `NotInSetOfValues` and `ReturnAddressValue` --- rust/src/architecture.rs | 20 +- rust/src/disassembly.rs | 44 + rust/src/function.rs | 1854 +++++++++++++++++++++++++++++++++- rust/src/hlil/instruction.rs | 3 +- rust/src/mlil/function.rs | 411 +++++++- rust/src/mlil/instruction.rs | 20 +- rust/src/operand_iter.rs | 3 +- rust/src/tags.rs | 74 ++ rust/src/types.rs | 872 +++++++++++++++- 9 files changed, 3261 insertions(+), 40 deletions(-) diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 713e8bf1e..6831844ef 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -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 { @@ -2419,7 +2434,10 @@ where }; let inputs = intrinsic.inputs(); - let mut res: Box<[_]> = inputs.into_iter().map(|input| unsafe { Ref::into_raw(input) }.0).collect(); + let mut res: Box<[_]> = inputs + .into_iter() + .map(|input| unsafe { Ref::into_raw(input) }.0) + .collect(); unsafe { *count = res.len(); diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 855807b13..1fd51cee9 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -265,6 +265,35 @@ impl Drop for InstructionTextToken { } } +impl CoreArrayProvider for InstructionTextToken { + type Raw = BNInstructionTextToken; + type Context = (); + type Wrapped<'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> { + Self(*raw) + } +} + +impl CoreArrayProvider for Array { + type Raw = BNInstructionTextLine; + type Context = (); + type Wrapped<'a> = Self; +} +unsafe impl CoreArrayProviderInner for Array { + 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> { + Self::new(raw.tokens, raw.count, ()) + } +} + +#[repr(transparent)] pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine); impl DisassemblyTextLine { @@ -422,6 +451,21 @@ impl Drop for DisassemblyTextLine { } } +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)] diff --git a/rust/src/function.rs b/rust/src/function.rs index 04be898ff..78b7da9f3 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -14,24 +14,33 @@ use binaryninjacore_sys::*; -use crate::rc::*; -use crate::string::*; -use crate::types::Variable; use crate::{ - architecture::CoreArchitecture, + architecture::{Architecture, CoreArchitecture, CoreRegister, Register}, basicblock::{BasicBlock, BlockContext}, binaryview::{BinaryView, BinaryViewExt}, - hlil, llil, mlil, + callingconvention::CallingConvention, + disassembly::{DisassemblySettings, DisassemblyTextLine}, + flowgraph::FlowGraph, + hlil, llil, + mlil::{self, FunctionGraphType}, platform::Platform, + references::CodeReference, + string::*, symbol::Symbol, - types::{Conf, NamedTypedVariable, Type}, + tags::{Tag, TagReference, TagType}, + types::{ + Conf, ConstantReference, HighlightColor, IndirectBranchInfo, IntegerDisplayType, + MergedVariable, NamedTypedVariable, QualifiedName, RegisterStackAdjustment, RegisterValue, + RegisterValueType, StackVariableReference, Type, UnresolvedIndirectBranches, Variable, + }, }; +use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*}; pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason; pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride; pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; -use std::hash::Hash; use std::{fmt, mem}; +use std::{hash::Hash, ops::Range}; pub struct Location { pub arch: Option, @@ -157,6 +166,10 @@ impl Function { unsafe { BNGetFunctionStart(self.handle) } } + pub fn lowest_address(&self) -> u64 { + unsafe { BNGetFunctionLowestAddress(self.handle) } + } + pub fn highest_address(&self) -> u64 { unsafe { BNGetFunctionHighestAddress(self.handle) } } @@ -189,7 +202,7 @@ impl Function { pub fn set_can_return_user>>(&self, can_return: T) { let mut bool_with_confidence = can_return.into().into(); - unsafe { BNSetAutoFunctionCanReturn(self.handle, &mut bool_with_confidence) } + unsafe { BNSetUserFunctionCanReturn(self.handle, &mut bool_with_confidence) } } pub fn comment_at(&self, addr: u64) -> BnString { @@ -204,6 +217,13 @@ impl Function { } } + /// All comments in the function + pub fn comments(&self) -> Array { + let mut count = 0; + let lines = unsafe { BNGetCommentedAddresses(self.handle, &mut count) }; + unsafe { Array::new(lines, count, self.to_owned()) } + } + pub fn basic_blocks(&self) -> Array> { unsafe { let mut count = 0; @@ -214,11 +234,23 @@ impl Function { } } + /// Returns the BasicBlock that contains the given address `addr`. + /// + /// * `addr` - Address of the BasicBlock to retrieve. + /// * `arch` - Architecture of the basic block if different from the Function's self.arch + /// + /// # Example + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// let blocks = fun.basic_block_containing(0x1000, None); + /// ``` pub fn basic_block_containing( &self, - arch: &CoreArchitecture, addr: u64, + arch: Option, ) -> Option>> { + let arch = arch.unwrap_or_else(|| self.arch()); unsafe { let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr); let context = NativeBlock { _priv: () }; @@ -231,6 +263,18 @@ impl Function { } } + pub fn block_annotations( + &self, + addr: u64, + arch: Option, + ) -> Array> { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let lines = unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.0, addr, &mut count) }; + assert!(!lines.is_null()); + unsafe { Array::new(lines, count, ()) } + } + pub fn get_variable_name(&self, var: &Variable) -> BnString { unsafe { let raw_var = var.raw(); @@ -251,6 +295,30 @@ impl Function { } } + pub fn high_level_il_if_available(&self) -> Option> { + let hlil = unsafe { BNGetFunctionHighLevelILIfAvailable(self.handle) }; + (!hlil.is_null()).then(|| unsafe { hlil::HighLevelILFunction::ref_from_raw(hlil, true) }) + } + + /// MediumLevelILFunction used to represent Function mapped medium level IL + pub fn mapped_medium_level_il(&self) -> Result, ()> { + let mlil = unsafe { BNGetFunctionMappedMediumLevelIL(self.handle) }; + if mlil.is_null() { + return Err(()); + } + Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) + } + + pub fn mapped_medium_level_il_if_available( + &self, + ) -> Result, ()> { + let mlil = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) }; + if mlil.is_null() { + return Err(()); + } + Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) + } + pub fn medium_level_il(&self) -> Result, ()> { unsafe { let mlil = BNGetFunctionMediumLevelIL(self.handle); @@ -263,6 +331,11 @@ impl Function { } } + pub fn medium_level_il_if_available(&self) -> Option> { + let mlil = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) }; + (!mlil.is_null()).then(|| unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) + } + pub fn low_level_il(&self) -> Result>, ()> { unsafe { let llil = BNGetFunctionLowLevelIL(self.handle); @@ -275,6 +348,13 @@ impl Function { } } + pub fn low_level_il_if_available( + &self, + ) -> Option>> { + let llil = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) }; + (!llil.is_null()).then(|| unsafe { llil::RegularFunction::from_raw(self.arch(), llil) }) + } + pub fn lifted_il(&self) -> Result>, ()> { unsafe { let llil = BNGetFunctionLiftedIL(self.handle); @@ -287,6 +367,11 @@ impl Function { } } + pub fn lifted_il_if_available(&self) -> Option>> { + let llil = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) }; + (!llil.is_null()).then(|| unsafe { llil::LiftedFunction::from_raw(self.arch(), llil) }) + } + pub fn return_type(&self) -> Conf> { let result = unsafe { BNGetFunctionReturnType(self.handle) }; @@ -296,16 +381,54 @@ impl Function { ) } - pub fn function_type(&self) -> Ref { - unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) } + pub fn set_auto_return_type<'a, C>(&self, return_type: C) + where + C: Into>, + { + let return_type: Conf<&Type> = return_type.into(); + unsafe { + BNSetAutoFunctionReturnType( + self.handle, + &mut BNTypeWithConfidence { + type_: return_type.contents.handle, + confidence: return_type.confidence, + }, + ) + } } - pub fn set_user_type(&self, t: Type) { + pub fn set_user_return_type<'a, C>(&self, return_type: C) + where + C: Into>, + { + let return_type: Conf<&Type> = return_type.into(); unsafe { - BNSetFunctionUserType(self.handle, t.handle); + BNSetUserFunctionReturnType( + self.handle, + &mut BNTypeWithConfidence { + type_: return_type.contents.handle, + confidence: return_type.confidence, + }, + ) } } + pub fn function_type(&self) -> Ref { + unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) } + } + + pub fn has_user_type(&self) -> bool { + unsafe { BNFunctionHasUserType(self.handle) } + } + + pub fn set_user_type(&self, t: &Type) { + unsafe { BNSetFunctionUserType(self.handle, t.handle) } + } + + pub fn set_auto_type(&self, t: &Type) { + unsafe { BNSetFunctionAutoType(self.handle, t.handle) } + } + pub fn stack_layout(&self) -> Array { let mut count = 0; unsafe { @@ -314,6 +437,317 @@ impl Function { } } + /// Gets number of bytes removed from the stack after return + pub fn stack_adjustment(&self) -> Conf { + unsafe { BNGetFunctionStackAdjustment(self.handle) }.into() + } + + /// Sets number of bytes removed from the stack after return + pub fn set_user_stack_adjustment(&self, value: C) + where + C: Into>, + { + let value: Conf = value.into(); + let mut value_raw = value.into(); + unsafe { BNSetUserFunctionStackAdjustment(self.handle, &mut value_raw) } + } + + /// Sets number of bytes removed from the stack after return + pub fn set_auto_stack_adjustment(&self, value: C) + where + C: Into>, + { + let value: Conf = value.into(); + let mut value_raw = value.into(); + unsafe { BNSetAutoFunctionStackAdjustment(self.handle, &mut value_raw) } + } + + pub fn call_stack_adjustment(&self, addr: u64, arch: Option) -> Conf { + let arch = arch.unwrap_or_else(|| self.arch()); + let result = unsafe { BNGetCallStackAdjustment(self.handle, arch.0, addr) }; + result.into() + } + + pub fn set_user_call_stack_adjustment( + &self, + addr: u64, + adjust: I, + arch: Option, + ) where + I: Into>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf = adjust.into(); + unsafe { + BNSetUserCallStackAdjustment( + self.handle, + arch.0, + addr, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn set_auto_call_stack_adjustment( + &self, + addr: u64, + adjust: I, + arch: Option, + ) where + I: Into>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf = adjust.into(); + unsafe { + BNSetAutoCallStackAdjustment( + self.handle, + arch.0, + addr, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn call_type_adjustment( + &self, + addr: u64, + arch: Option, + ) -> Option>> { + let arch = arch.unwrap_or_else(|| self.arch()); + let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.0, addr) }; + (!result.type_.is_null()) + .then(|| unsafe { Conf::new(Type::ref_from_raw(result.type_), result.confidence) }) + } + + /// Sets or removes the call type override at a call site to the given type. + /// + /// * `addr` - virtual address of the call instruction to adjust + /// * `adjust_type` - (optional) overridden call type, or `None` to remove an existing adjustment + /// * `arch` - (optional) Architecture of the instruction if different from self.arch + pub fn set_user_call_type_adjustment<'a, I>( + &self, + addr: u64, + adjust_type: Option, + arch: Option, + ) where + I: Into>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut adjust_type = adjust_type.map(|adjust_type| { + let adjust_type = adjust_type.into(); + BNTypeWithConfidence { + type_: adjust_type.contents.handle, + confidence: adjust_type.confidence, + } + }); + let adjust_ptr = adjust_type + .as_mut() + .map(|x| x as *mut _) + .unwrap_or(core::ptr::null_mut()); + unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.0, addr, adjust_ptr) } + } + + pub fn set_auto_call_type_adjustment<'a, I>( + &self, + addr: u64, + adjust_type: I, + arch: Option, + ) where + I: Into>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust_type: Conf<&Type> = adjust_type.into(); + unsafe { + BNSetAutoCallTypeAdjustment( + self.handle, + arch.0, + addr, + &mut BNTypeWithConfidence { + type_: adjust_type.contents.handle, + confidence: adjust_type.confidence, + }, + ) + } + } + + pub fn call_reg_stack_adjustment( + &self, + addr: u64, + arch: Option, + ) -> Array> { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let adjust = + unsafe { BNGetCallRegisterStackAdjustment(self.handle, arch.0, addr, &mut count) }; + assert!(!adjust.is_null()); + unsafe { Array::new(adjust, count, arch.handle()) } + } + + pub fn set_user_call_reg_stack_adjustment( + self, + addr: u64, + adjust: I, + arch: Option, + ) where + I: IntoIterator>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut adjust_buf: Box<[BNRegisterStackAdjustment]> = + adjust.into_iter().map(|adjust| adjust.into_raw()).collect(); + unsafe { + BNSetUserCallRegisterStackAdjustment( + self.handle, + arch.0, + addr, + adjust_buf.as_mut_ptr(), + adjust_buf.len(), + ) + } + } + + pub fn set_auto_call_reg_stack_adjustment( + &self, + addr: u64, + adjust: I, + arch: Option, + ) where + I: IntoIterator>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut adjust_buf: Box<[BNRegisterStackAdjustment]> = + adjust.into_iter().map(|reg| reg.into_raw()).collect(); + + unsafe { + BNSetAutoCallRegisterStackAdjustment( + self.handle, + arch.0, + addr, + adjust_buf.as_mut_ptr(), + adjust_buf.len(), + ) + } + } + + pub fn call_reg_stack_adjustment_for_reg_stack( + &self, + addr: u64, + reg_stack_id: u32, + arch: Option, + ) -> RegisterStackAdjustment { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust = unsafe { + BNGetCallRegisterStackAdjustmentForRegisterStack( + self.handle, + arch.0, + addr, + reg_stack_id, + ) + }; + unsafe { RegisterStackAdjustment::from_raw(adjust, arch) } + } + + pub fn set_user_call_reg_stack_adjustment_for_reg_stack( + &self, + addr: u64, + reg_stack_id: u32, + adjust: I, + arch: Option, + ) where + I: Into>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf = adjust.into(); + unsafe { + BNSetUserCallRegisterStackAdjustmentForRegisterStack( + self.handle, + arch.0, + addr, + reg_stack_id, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn set_auto_call_reg_stack_adjustment_for_reg_stack( + &self, + addr: u64, + reg_stack_id: u32, + adjust: I, + arch: Option, + ) where + I: Into>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf = adjust.into(); + unsafe { + BNSetAutoCallRegisterStackAdjustmentForRegisterStack( + self.handle, + arch.0, + addr, + reg_stack_id, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn reg_stack_adjustments(&self) -> Array> { + let mut count = 0; + let adjust = unsafe { BNGetFunctionRegisterStackAdjustments(self.handle, &mut count) }; + assert!(!adjust.is_null()); + unsafe { Array::new(adjust, count, self.arch().handle()) } + } + + pub fn set_user_reg_stack_adjustments(&self, values: I) + where + I: IntoIterator>, + A: Architecture, + { + let mut values: Box<[BNRegisterStackAdjustment]> = + values.into_iter().map(|r| r.into_raw()).collect(); + unsafe { + BNSetUserFunctionRegisterStackAdjustments( + self.handle, + values.as_mut_ptr(), + values.len(), + ) + } + } + + pub fn set_auto_reg_stack_adjustments(&self, values: I) + where + I: IntoIterator>, + A: Architecture, + { + let mut values: Box<[BNRegisterStackAdjustment]> = + values.into_iter().map(|r| r.into_raw()).collect(); + unsafe { + BNSetAutoFunctionRegisterStackAdjustments( + self.handle, + values.as_mut_ptr(), + values.len(), + ) + } + } + + /// List of function variables: including name, variable and type + pub fn variables(&self) -> Array<(&str, Variable, &Type)> { + let mut count = 0; + let vars = unsafe { BNGetFunctionVariables(self.handle, &mut count) }; + assert!(!vars.is_null()); + unsafe { Array::new(vars, count, ()) } + } + + pub fn split_variables(&self) -> Array { + let mut count = 0; + let vars = unsafe { BNGetSplitVariables(self.handle, &mut count) }; + assert!(!vars.is_null()); + unsafe { Array::new(vars, count, ()) } + } + pub fn parameter_variables(&self) -> Conf> { unsafe { let mut variables = BNGetFunctionParameterVariables(self.handle); @@ -330,6 +764,66 @@ impl Function { } } + pub fn set_user_parameter_variables(&self, values: I, confidence: u8) + where + I: IntoIterator, + { + let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect(); + unsafe { + BNSetUserFunctionParameterVariables( + self.handle, + &mut BNParameterVariablesWithConfidence { + vars: vars.as_mut_ptr(), + count: vars.len(), + confidence, + }, + ) + } + } + + pub fn set_auto_parameter_variables(&self, values: I, confidence: u8) + where + I: IntoIterator, + { + let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect(); + unsafe { + BNSetAutoFunctionParameterVariables( + self.handle, + &mut BNParameterVariablesWithConfidence { + vars: vars.as_mut_ptr(), + count: vars.len(), + confidence, + }, + ) + } + } + + pub fn parameter_at( + &self, + addr: u64, + func_type: Option<&Type>, + i: usize, + arch: Option, + ) -> RegisterValue { + let arch = arch.unwrap_or_else(|| self.arch()); + let func_type = func_type.map(|f| f.handle).unwrap_or(core::ptr::null_mut()); + let value = + unsafe { BNGetParameterValueAtInstruction(self.handle, arch.0, addr, func_type, i) }; + value.into() + } + + pub fn parameter_at_low_level_il_instruction( + &self, + instr: usize, + func_type: &Type, + i: usize, + ) -> RegisterValue { + let value = unsafe { + BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type.handle, i) + }; + value.into() + } + pub fn apply_imported_types(&self, sym: &Symbol, t: Option<&Type>) { unsafe { BNApplyImportedTypes( @@ -344,6 +838,12 @@ impl Function { } } + pub fn apply_auto_discovered_type(&self, func_type: &Type) { + unsafe { BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) } + } + + /// Whether automatic analysis was skipped for this function. + /// Can be set to false to re-enable analysis. pub fn analysis_skipped(&self) -> bool { unsafe { BNIsFunctionAnalysisSkipped(self.handle) } } @@ -377,6 +877,1267 @@ impl Function { pub fn set_analysis_skip_override(&self, override_: FunctionAnalysisSkipOverride) { unsafe { BNSetFunctionAnalysisSkipOverride(self.handle, override_) } } + + ///Whether the function's IL should be inlined into all callers' IL + pub fn inline_during_analysis(&self) -> Conf { + let result = unsafe { BNIsFunctionInlinedDuringAnalysis(self.handle) }; + result.into() + } + + pub fn set_auto_inline_during_analysis(&self, value: C) + where + C: Into>, + { + let value: Conf = value.into(); + unsafe { + BNSetAutoFunctionInlinedDuringAnalysis( + self.handle, + BNBoolWithConfidence { + value: value.contents, + confidence: value.confidence, + }, + ) + } + } + + pub fn set_user_inline_during_analysis(&self, value: C) + where + C: Into>, + { + let value: Conf = value.into(); + unsafe { + BNSetUserFunctionInlinedDuringAnalysis( + self.handle, + BNBoolWithConfidence { + value: value.contents, + confidence: value.confidence, + }, + ) + } + } + + pub fn analysis_performance_info(&self) -> Array { + let mut count = 0; + let info = unsafe { BNGetFunctionAnalysisPerformanceInfo(self.handle, &mut count) }; + assert!(!info.is_null()); + unsafe { Array::new(info, count, ()) } + } + + /// Creates and adds a [Tag] object on either a function, or on + /// an address inside of a function. + /// + /// "Function tags" appear at the top of a function and are a good way to label an + /// entire function with some information. If you include an address when you call + /// Function.add_tag, you'll create an "address tag". These are good for labeling + /// specific instructions. + /// + /// For tagging arbitrary data, consider [BinaryViewExt::add_tag]. + /// + /// * `tag_type_name` - The name of the tag type for this Tag. + /// * `data` - Additional data for the Tag. + /// * `addr` - Address at which to add the tag. + /// * `user` - Whether or not a user tag. + /// + /// # Example + /// + /// ```no_run + /// # use binaryninja::binaryview::{BinaryView, BinaryViewExt}; + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// # let bv: BinaryView = todo!(); + /// let important = bv.create_tag_type("Important", "⚠️"); + /// fun.add_tag(&important, "I think this is the main function", None, false, None); + /// let crash = bv.create_tag_type("Crashes", "🎯"); + /// fun.add_tag(&crash, "Nullpointer dereference", Some(0x1337), false, None); + /// ``` + pub fn add_tag( + &self, + tag_type: &TagType, + data: S, + addr: Option, + user: bool, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + + // Create tag + let tag = Tag::new(tag_type, data); + let binaryview = unsafe { BinaryView::from_raw(BNGetFunctionData(self.handle)) }; + unsafe { BNAddTag(binaryview.handle, tag.handle, user) }; + + unsafe { + match (user, addr) { + (false, None) => BNAddAutoFunctionTag(self.handle, tag.handle), + (false, Some(addr)) => BNAddAutoAddressTag(self.handle, arch.0, addr, tag.handle), + (true, None) => BNAddUserFunctionTag(self.handle, tag.handle), + (true, Some(addr)) => BNAddUserAddressTag(self.handle, arch.0, addr, tag.handle), + } + } + } + + /// Remove [Tag] object on either a function, or on an address inside of a function. + /// + /// * `tag` - The tag to remove. + /// * `addr` - (optional) Address at which to remove the tag. + /// * `user` - Whether or not a user tag. + pub fn remove_tag( + &self, + tag: &Tag, + addr: Option, + user: bool, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { + match (user, addr) { + (false, None) => BNRemoveAutoFunctionTag(self.handle, tag.handle), + (false, Some(addr)) => { + BNRemoveAutoAddressTag(self.handle, arch.0, addr, tag.handle) + } + (true, None) => BNRemoveUserFunctionTag(self.handle, tag.handle), + (true, Some(addr)) => BNRemoveUserAddressTag(self.handle, arch.0, addr, tag.handle), + } + } + } + + /// Remove [Tag] object of type on either a function, or on an address + /// inside of a function. + /// + /// * `tag_type` - The type of the to remove. + /// * `addr` - Address at which to add the tag. + /// * `user` - Whether or not a user tag. + pub fn remove_tags_of_type( + &self, + tag_type: &TagType, + addr: Option, + user: bool, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { + match (user, addr) { + (false, None) => BNRemoveAutoFunctionTagsOfType(self.handle, tag_type.handle), + (false, Some(addr)) => { + BNRemoveAutoAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle) + } + (true, None) => BNRemoveUserFunctionTagsOfType(self.handle, tag_type.handle), + (true, Some(addr)) => { + BNRemoveUserAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle) + } + } + } + } + + /// Places a user-defined cross-reference from the instruction at + /// the given address and architecture to the specified target address. If the specified + /// source instruction is not contained within this function, no action is performed. + /// To remove the reference, use [Function::remove_user_code_ref]. + /// + /// * `from_addr` - Virtual address of the source instruction. + /// * `to_addr` - Virtual address of the xref's destination. + /// * `arch` - Architecture of the source instruction. + /// + /// # Example + /// + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// fun.add_user_code_ref(0x1337, 0x400000, None); + /// ``` + pub fn add_user_code_ref(&self, from_addr: u64, to_addr: u64, arch: Option) { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { BNAddUserCodeReference(self.handle, arch.0, from_addr, to_addr) } + } + + /// Removes a user-defined cross-reference. + /// If the given address is not contained within this function, or if there is no + /// such user-defined cross-reference, no action is performed. + /// + /// * `from_addr` - virtual address of the source instruction + /// * `to_addr` - virtual address of the xref's destination. + /// * `arch` - architecture of the source instruction + /// + /// #Example + /// + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// fun.remove_user_code_ref(0x1337, 0x400000, None); + /// ``` + pub fn remove_user_code_ref( + self, + from_addr: u64, + to_addr: u64, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { BNRemoveUserCodeReference(self.handle, arch.0, from_addr, to_addr) } + } + + /// Places a user-defined type cross-reference from the instruction at + /// the given address and architecture to the specified type. If the specified + /// source instruction is not contained within this function, no action is performed. + /// To remove the reference, use [Function::remove_user_type_ref]. + /// + /// * `from_addr` - Virtual address of the source instruction. + /// * `name` - Name of the referenced type. + /// * `arch` - Architecture of the source instruction. + /// + /// # Example + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// fun.add_user_type_ref(0x1337, &"A".into(), None); + /// ``` + pub fn add_user_type_ref( + &self, + from_addr: u64, + name: &QualifiedName, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + let name_ptr = &name.0 as *const BNQualifiedName as *mut _; + unsafe { BNAddUserTypeReference(self.handle, arch.0, from_addr, name_ptr) } + } + + /// Removes a user-defined type cross-reference. + /// If the given address is not contained within this function, or if there is no + /// such user-defined cross-reference, no action is performed. + /// + /// * `from_addr` - Virtual address of the source instruction. + /// * `name` - Name of the referenced type. + /// * `from_arch` - Architecture of the source instruction. + /// + /// # Example + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// fun.remove_user_type_ref(0x1337, &"A".into(), None); + /// ``` + pub fn remove_user_type_ref( + &self, + from_addr: u64, + name: &QualifiedName, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + let name_ptr = &name.0 as *const BNQualifiedName as *mut _; + unsafe { BNRemoveUserTypeReference(self.handle, arch.0, from_addr, name_ptr) } + } + + /// Places a user-defined type field cross-reference from the + /// instruction at the given address and architecture to the specified type. If the specified + /// source instruction is not contained within this function, no action is performed. + /// To remove the reference, use [Function::remove_user_type_field_ref]. + /// + /// * `from_addr` - Virtual address of the source instruction. + /// * `name` - Name of the referenced type. + /// * `offset` - Offset of the field, relative to the type. + /// * `arch` - Architecture of the source instruction. + /// * `size` - The size of the access. + /// + /// # Example + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// fun.add_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None); + /// ``` + pub fn add_user_type_field_ref( + &self, + from_addr: u64, + name: &QualifiedName, + offset: u64, + arch: Option, + size: Option, + ) { + let size = size.unwrap_or(0); + let arch = arch.unwrap_or_else(|| self.arch()); + let name_ptr = &name.0 as *const _ as *mut _; + unsafe { + BNAddUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size) + } + } + + /// Removes a user-defined type field cross-reference. + /// If the given address is not contained within this function, or if there is no + /// such user-defined cross-reference, no action is performed. + /// + /// * `from_addr` - Virtual address of the source instruction + /// * `name` - Name of the referenced type + /// * `offset` - Offset of the field, relative to the type + /// * `arch` - Architecture of the source instruction + /// * `size` - The size of the access + /// + /// # Example + /// ```no_run + /// # use binaryninja::function::Function; + /// # let fun: Function = todo!(); + /// fun.remove_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None); + /// ``` + pub fn remove_user_type_field_ref( + &self, + from_addr: u64, + name: &QualifiedName, + offset: u64, + arch: Option, + size: Option, + ) { + let size = size.unwrap_or(0); + let arch = arch.unwrap_or_else(|| self.arch()); + let name_ptr = &name.0 as *const _ as *mut _; + unsafe { + BNRemoveUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size) + } + } + + pub fn constant_data( + &self, + state: RegisterValueType, + value: u64, + size: Option, + ) -> DataBuffer { + let size = size.unwrap_or(0); + let state_raw = state.into_raw_value(); + DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size) }) + } + + pub fn constants_referenced_by( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let refs = + unsafe { BNGetConstantsReferencedByInstruction(self.handle, arch.0, addr, &mut count) }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, ()) } + } + + pub fn constants_referenced_by_address_if_available( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let refs = unsafe { + BNGetConstantsReferencedByInstructionIfAvailable(self.handle, arch.0, addr, &mut count) + }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, ()) } + } + + /// Returns a list of function Tags for the function. + /// + /// `auto` - If `None`, gets all tags, if `true`, gets auto tags, if `false`, gets user tags + /// `tag_type` - If `None`, gets all tags, otherwise only gets tags of the given type + pub fn function_tags(&self, auto: Option, tag_type: Option<&str>) -> Array { + let mut count = 0; + + let tag_type = tag_type.map(|tag_type| self.view().get_tag_type(tag_type)); + + let tags = unsafe { + match (tag_type, auto) { + // received a tag_type, BinaryView found none + (Some(None), _) => return Array::new(core::ptr::null_mut(), 0, ()), + + // with tag_type + (Some(Some(tag_type)), None) => { + BNGetFunctionTagsOfType(self.handle, tag_type.handle, &mut count) + } + (Some(Some(tag_type)), Some(true)) => { + BNGetAutoFunctionTagsOfType(self.handle, tag_type.handle, &mut count) + } + (Some(Some(tag_type)), Some(false)) => { + BNGetUserFunctionTagsOfType(self.handle, tag_type.handle, &mut count) + } + // without tag_type + (None, None) => BNGetFunctionTags(self.handle, &mut count), + (None, Some(true)) => BNGetAutoFunctionTags(self.handle, &mut count), + (None, Some(false)) => BNGetUserFunctionTags(self.handle, &mut count), + } + }; + assert!(!tags.is_null()); + + unsafe { Array::new(tags, count, ()) } + } + + pub fn tags(&self) -> Array { + let mut count = 0; + let tags = unsafe { BNGetAddressTagReferences(self.handle, &mut count) }; + unsafe { Array::new(tags, count, ()) } + } + + /// Gets a list of Tags at the address. + /// + /// * `addr` - Address to get tags from. + /// * `auto` - If `None`, gets all tags, if `true`, gets auto tags, if `false`, gets user tags + pub fn tags_at( + &self, + addr: u64, + auto: Option, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + + let tags = match auto { + None => unsafe { BNGetAddressTags(self.handle, arch.0, addr, &mut count) }, + Some(true) => unsafe { BNGetAutoAddressTags(self.handle, arch.0, addr, &mut count) }, + Some(false) => unsafe { BNGetUserAddressTags(self.handle, arch.0, addr, &mut count) }, + }; + assert!(!tags.is_null()); + unsafe { Array::new(tags, count, ()) } + } + + /// Gets a list of Tags in the address range. + /// + /// * `addr` - Address to get tags from. + /// * `auto` - If `None`, gets all tags, if `true`, gets auto tags, if `false`, gets user tags + pub fn tags_in_range( + &self, + range: Range, + auto: Option, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + + let tags = match auto { + None => unsafe { + BNGetAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count) + }, + Some(true) => unsafe { + BNGetAutoAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count) + }, + Some(false) => unsafe { + BNGetUserAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count) + }, + }; + assert!(!tags.is_null()); + unsafe { Array::new(tags, count, ()) } + } + + /// List of indirect branches + pub fn indirect_branches(&self) -> Array { + let mut count = 0; + let branches = unsafe { BNGetIndirectBranches(self.handle, &mut count) }; + assert!(!branches.is_null()); + unsafe { Array::new(branches, count, ()) } + } + + pub fn set_user_indirect_branches( + &self, + source: u64, + branches: I, + arch: Option, + ) where + I: IntoIterator, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut branches: Box<[BNArchitectureAndAddress]> = branches + .into_iter() + .map(|address| BNArchitectureAndAddress { + address, + arch: arch.0, + }) + .collect(); + unsafe { + BNSetUserIndirectBranches( + self.handle, + arch.0, + source, + branches.as_mut_ptr(), + branches.len(), + ) + } + } + + pub fn set_auto_indirect_branches( + &self, + source: u64, + branches: I, + arch: Option, + ) where + I: IntoIterator, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut branches: Box<[BNArchitectureAndAddress]> = branches + .into_iter() + .map(|address| BNArchitectureAndAddress { + address, + arch: arch.0, + }) + .collect(); + unsafe { + BNSetAutoIndirectBranches( + self.handle, + arch.0, + source, + branches.as_mut_ptr(), + branches.len(), + ) + } + } + + /// List of indirect branches at this address + pub fn indirect_branches_at( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let branches = unsafe { BNGetIndirectBranchesAt(self.handle, arch.0, addr, &mut count) }; + assert!(!branches.is_null()); + unsafe { Array::new(branches, count, ()) } + } + + /// # Example + /// ```no_run + /// # let fun: binaryninja::function::Function = todo!(); + /// let color = fun.instr_highlight(0x1337, None); + /// ``` + pub fn instr_highlight(&self, addr: u64, arch: Option) -> HighlightColor { + let arch = arch.unwrap_or_else(|| self.arch()); + let color = unsafe { BNGetInstructionHighlight(self.handle, arch.0, addr) }; + HighlightColor::from_raw(color) + } + + /// Sets the highlights the instruction at the specified address with the supplied color + /// + ///
Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.
+ /// + /// * `addr` - virtual address of the instruction to be highlighted + /// * `color` - Color value to use for highlighting + /// * `arch` - (optional) Architecture of the instruction if different from self.arch + pub fn set_auto_instr_highlight( + &self, + addr: u64, + color: HighlightColor, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + let color_raw = color.into_raw(); + unsafe { BNSetAutoInstructionHighlight(self.handle, arch.0, addr, color_raw) } + } + + /// Sets the highlights the instruction at the specified address with the supplied color + /// + /// * `addr` - virtual address of the instruction to be highlighted + /// * `color` - Color value to use for highlighting + /// * `arch` - (optional) Architecture of the instruction if different from self.arch + /// + /// # Example + /// ```no_run + /// # use binaryninja::types::HighlightColor; + /// # let fun: binaryninja::function::Function = todo!(); + /// let color = HighlightColor::NoHighlightColor { alpha: u8::MAX }; + /// fun.set_user_instr_highlight(0x1337, color, None); + /// ``` + pub fn set_user_instr_highlight( + &self, + addr: u64, + color: HighlightColor, + arch: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + let color_raw = color.into_raw(); + unsafe { BNSetUserInstructionHighlight(self.handle, arch.0, addr, color_raw) } + } + + /// return the address, if any, of the instruction that contains the + /// provided address + pub fn instruction_containing_address( + &self, + addr: u64, + arch: Option, + ) -> Option { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut start = 0; + unsafe { BNGetInstructionContainingAddress(self.handle, arch.0, addr, &mut start) } + .then_some(start) + } + + /// Get the current text display type for an integer token in the disassembly or IL views + /// + /// See also see [Function::int_display_type_and_typeid] + /// + /// * `instr_addr` - Address of the instruction or IL line containing the token + /// * `value` - field of the InstructionTextToken object for the token, usually the constant displayed + /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token + /// * `arch` - (optional) Architecture of the instruction or IL line containing the token + pub fn int_display_type( + &self, + instr_addr: u64, + value: u64, + operand: usize, + arch: Option, + ) -> IntegerDisplayType { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { BNGetIntegerConstantDisplayType(self.handle, arch.0, instr_addr, value, operand) } + } + + /// Change the text display type for an integer token in the disassembly or IL views + /// + /// * `instr_addr` - Address of the instruction or IL line containing the token + /// * `value` - Field of the InstructionTextToken object for the token, usually the constant displayed + /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token + /// * `display_type` - Desired display type + /// * `arch` - (optional) Architecture of the instruction or IL line containing the token + /// * `enum_display_typeid` - (optional) Whenever passing EnumDisplayType to `display_type`, passing a type ID here will specify the Enumeration display type. Must be a valid type ID and resolve to an enumeration type. + pub fn set_int_display_type( + &self, + instr_addr: u64, + value: u64, + operand: usize, + display_type: IntegerDisplayType, + arch: Option, + enum_display_typeid: Option, + ) { + let arch = arch.unwrap_or_else(|| self.arch()); + let enum_display_typeid = enum_display_typeid.map(BnStrCompatible::into_bytes_with_nul); + let enum_display_typeid_ptr = enum_display_typeid + .map(|x| x.as_ref().as_ptr() as *const i8) + .unwrap_or(core::ptr::null()); + unsafe { + BNSetIntegerConstantDisplayType( + self.handle, + arch.0, + instr_addr, + value, + operand, + display_type, + enum_display_typeid_ptr, + ) + } + } + + /// Get the current text display enum type for an integer token in the disassembly or IL views. + /// + /// See also see [Function::int_display_type_and_typeid] + /// + /// * `instr_addr` - Address of the instruction or IL line containing the token + /// * `value` - field of the InstructionTextToken object for the token, usually the constant displayed + /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token + /// * `arch` - (optional) Architecture of the instruction or IL line containing the token + pub fn int_enum_display_typeid( + &self, + instr_addr: u64, + value: u64, + operand: usize, + arch: Option, + ) -> BnString { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { + BnString::from_raw(BNGetIntegerConstantDisplayTypeEnumerationType( + self.handle, + arch.0, + instr_addr, + value, + operand, + )) + } + } + + /// Get the current text display type for an integer token in the disassembly or IL views + /// + /// * `instr_addr` - Address of the instruction or IL line containing the token + /// * `value` - field of the InstructionTextToken object for the token, usually the constant displayed + /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token + /// * `arch` - (optional) Architecture of the instruction or IL line containing the token + pub fn int_display_type_and_typeid( + &self, + instr_addr: u64, + value: u64, + operand: usize, + arch: Option, + ) -> (IntegerDisplayType, BnString) { + let arch = arch.unwrap_or_else(|| self.arch()); + let name = self.int_enum_display_typeid(instr_addr, value, operand, Some(arch)); + let display = self.int_display_type(instr_addr, value, operand, Some(arch)); + (display, name) + } + + /// Get the value the provided string register address corresponding to the given virtual address + /// + /// * `addr` - virtual address of the instruction to query + /// * `reg` - string value of native register to query + /// * `arch` - (optional) Architecture for the given function + /// + /// # Example + /// ```no_run + /// # use binaryninja::architecture::{ArchitectureExt, Register}; + /// # let fun: binaryninja::function::Function = todo!(); + /// let reg = fun.arch().register_by_name("rdi").unwrap(); + /// let value = fun.register_value_at(0x400dbe, reg.id(), None); + /// ``` + pub fn register_value_at( + &self, + addr: u64, + reg: u32, + arch: Option, + ) -> RegisterValue { + let arch = arch.unwrap_or_else(|| self.arch()); + let register = unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.0, addr, reg) }; + register.into() + } + + /// Gets the value instruction address corresponding to the given virtual address + /// + /// * `addr` - virtual address of the instruction to query + /// * `reg` - string value of native register to query + /// * `arch` - (optional) Architecture for the given function + /// + /// # Example + /// ```no_run + /// # use binaryninja::architecture::{ArchitectureExt, Register}; + /// # let fun: binaryninja::function::Function = todo!(); + /// let reg = fun.arch().register_by_name("rdi").unwrap(); + /// let value = fun.register_value_after(0x400dbe, reg.id(), None); + /// ``` + pub fn register_value_after( + &self, + addr: u64, + reg: u32, + arch: Option, + ) -> RegisterValue { + let arch = arch.unwrap_or_else(|| self.arch()); + let register = + unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.0, addr, reg) }; + register.into() + } + + pub fn register_value_at_exit(&self, reg: u32) -> Conf { + let register = unsafe { BNGetFunctionRegisterValueAtExit(self.handle, reg) }; + Conf::new(register.value.into(), register.confidence) + } + + pub fn registers_read_by( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let regs = + unsafe { BNGetRegistersReadByInstruction(self.handle, arch.0, addr, &mut count) }; + assert!(!regs.is_null()); + unsafe { Array::new(regs, count, arch) } + } + + pub fn registers_written_by( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let regs = + unsafe { BNGetRegistersWrittenByInstruction(self.handle, arch.0, addr, &mut count) }; + assert!(!regs.is_null()); + unsafe { Array::new(regs, count, arch) } + } + + /// Registers that are modified by this function + pub fn clobbered_registers(&self) -> Conf> { + let result = unsafe { BNGetFunctionClobberedRegisters(self.handle) }; + + let reg_set = unsafe { Array::new(result.regs, result.count, self.arch().handle()) }; + Conf::new(reg_set, result.confidence) + } + + pub fn set_user_clobbered_registers(&self, registers: I, confidence: u8) + where + I: IntoIterator, + { + let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect(); + let mut regs = BNRegisterSetWithConfidence { + regs: regs.as_mut_ptr(), + count: regs.len(), + confidence, + }; + unsafe { BNSetUserFunctionClobberedRegisters(self.handle, &mut regs) } + } + + pub fn set_auto_clobbered_registers(&self, registers: I, confidence: u8) + where + I: IntoIterator, + { + let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect(); + let mut regs = BNRegisterSetWithConfidence { + regs: regs.as_mut_ptr(), + count: regs.len(), + confidence, + }; + unsafe { BNSetAutoFunctionClobberedRegisters(self.handle, &mut regs) } + } + + pub fn stack_contents_at( + &self, + addr: u64, + offset: i64, + size: usize, + arch: Option, + ) -> RegisterValue { + let arch = arch.unwrap_or_else(|| self.arch()); + let value = + unsafe { BNGetStackContentsAtInstruction(self.handle, arch.0, addr, offset, size) }; + value.into() + } + + pub fn stack_contents_after( + &self, + addr: u64, + offset: i64, + size: usize, + arch: Option, + ) -> RegisterValue { + let arch = arch.unwrap_or_else(|| self.arch()); + let value = + unsafe { BNGetStackContentsAfterInstruction(self.handle, arch.0, addr, offset, size) }; + value.into() + } + + pub fn stack_var_at_frame_offset( + &self, + addr: u64, + offset: i64, + arch: Option, + ) -> Option<(Variable, BnString, Conf>)> { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut found_value: BNVariableNameAndType = unsafe { mem::zeroed() }; + let found = unsafe { + BNGetStackVariableAtFrameOffset(self.handle, arch.0, addr, offset, &mut found_value) + }; + if !found { + return None; + } + let var = unsafe { Variable::from_raw(found_value.var) }; + let name = unsafe { BnString::from_raw(found_value.name) }; + let var_type = Conf::new( + unsafe { Type::ref_from_raw(found_value.type_) }, + found_value.typeConfidence, + ); + Some((var, name, var_type)) + } + + pub fn stack_variables_referenced_by( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let refs = unsafe { + BNGetStackVariablesReferencedByInstruction(self.handle, arch.0, addr, &mut count) + }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, ()) } + } + + pub fn stack_variables_referenced_by_address_if_available( + &self, + addr: u64, + arch: Option, + ) -> Array { + let arch = arch.unwrap_or_else(|| self.arch()); + let mut count = 0; + let refs = unsafe { + BNGetStackVariablesReferencedByInstructionIfAvailable( + self.handle, + arch.0, + addr, + &mut count, + ) + }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, ()) } + } + + /// Discovered value of the global pointer register, if the function uses one + pub fn global_pointer_value(&self) -> Conf { + let result = unsafe { BNGetFunctionGlobalPointerValue(self.handle) }; + Conf::new(result.value.into(), result.confidence) + } + + pub fn type_tokens( + &self, + settings: Option<&DisassemblySettings>, + ) -> Array { + let settings = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut()); + let mut count = 0; + let lines = unsafe { BNGetFunctionTypeTokens(self.handle, settings, &mut count) }; + assert!(!lines.is_null()); + unsafe { Array::new(lines, count, ()) } + } + + pub fn is_call_instruction(&self, addr: u64, arch: Option) -> bool { + let arch = arch.unwrap_or_else(|| self.arch()); + unsafe { BNIsCallInstruction(self.handle, arch.0, addr) } + } + + pub fn is_variable_user_defined(&self, var: &Variable) -> bool { + unsafe { BNIsVariableUserDefined(self.handle, &var.raw()) } + } + + pub fn is_pure(&self) -> Conf { + unsafe { BNIsFunctionPure(self.handle) }.into() + } + + pub fn set_user_pure(&self, value: C) + where + C: Into>, + { + let value: Conf = value.into(); + let mut value_raw = value.into(); + unsafe { BNSetUserFunctionPure(self.handle, &mut value_raw) }; + } + + pub fn set_auto_pure(&self, value: C) + where + C: Into>, + { + let value: Conf = value.into(); + let mut value_raw = value.into(); + unsafe { BNSetAutoFunctionPure(self.handle, &mut value_raw) }; + } + + pub fn is_too_large(&self) -> bool { + unsafe { BNIsFunctionTooLarge(self.handle) } + } + + pub fn is_update_needed(&self) -> bool { + unsafe { BNIsFunctionUpdateNeeded(self.handle) } + } + + /// Indicates that this function needs to be reanalyzed during the next update cycle + /// + /// * `update_type` - Desired update type + pub fn mark_updates_required(&self, update_type: FunctionUpdateType) { + unsafe { BNMarkUpdatesRequired(self.handle, update_type) } + } + + /// Indicates that callers of this function need to be reanalyzed during the next update cycle + /// + /// * `uppdate_type` - Desired update type + pub fn mark_caller_updates_required(&self, update_type: FunctionUpdateType) { + unsafe { BNMarkCallerUpdatesRequired(self.handle, update_type) } + } + + pub fn mark_recent_use(&self) { + unsafe { BNMarkFunctionAsRecentlyUsed(self.handle) } + } + + // Gets the list of merged variables + pub fn merged_variables(&self) -> Array { + let mut count = 0; + let vars = unsafe { BNGetMergedVariables(self.handle, &mut count) }; + assert!(!vars.is_null()); + unsafe { Array::new(vars, count, ()) } + } + + /// Merge one or more varibles in `sources` into the `target` variable. All + /// variable accesses to the variables in `sources` will be rewritten to use `target`. + /// + /// * `target` - target variable + /// * `sources` - list of source variables + pub fn merge_variables<'a>( + &self, + target: &Variable, + sources: impl IntoIterator, + ) { + let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect(); + unsafe { + BNMergeVariables( + self.handle, + &target.raw(), + sources_raw.as_ptr(), + sources_raw.len(), + ) + } + } + + /// Undoes variable merging performed with [Function::merge_variables]. The variables in + /// `sources` will no longer be merged into the `target` variable. + /// + /// * `target` - target variable + /// * `sources` - list of source variables + pub fn unmerge_variables<'a>( + &self, + target: &Variable, + sources: impl IntoIterator, + ) { + let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect(); + unsafe { + BNUnmergeVariables( + self.handle, + &target.raw(), + sources_raw.as_ptr(), + sources_raw.len(), + ) + } + } + + /// Splits a varible at the definition site. The given `var` must be the + /// variable unique to the definition and should be obtained by using + /// [mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site. + /// + /// This function is not meant to split variables that have been previously merged. Use + /// [Function::unmerge_variables] to split previously merged variables. + /// + ///
+ /// + /// Binary Ninja automatically splits all variables that the analysis determines + /// to be safely splittable. Splitting a variable manually with [Function::split_variable] can cause + /// IL and decompilation to be incorrect. There are some patterns where variables can be safely + /// split semantically but analysis cannot determine that it is safe. This function is provided + /// to allow variable splitting to be performed in these cases by plugins or by the user. + /// + ///
+ /// + /// * `var` - variable to split + pub fn split_variable(&self, var: &Variable) { + unsafe { BNSplitVariable(self.handle, &var.raw()) } + } + + /// Undoes varible splitting performed with [Function::split_variable]. The given `var` + /// must be the variable unique to the definition and should be obtained by using + /// [mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site. + /// + /// * `var` - variable to unsplit + pub fn unsplit_variable(&self, var: &Variable) { + unsafe { BNUnsplitVariable(self.handle, &var.raw()) } + } + + /// Causes this function to be reanalyzed. This function does not wait for the analysis to finish. + /// + /// * `update_type` - Desired update type + /// + ///
+ /// + /// If analysis_skipped is `true`, using this API will not trigger + /// re-analysis. Instead, use [Function::set_analysis_skipped] with `false`. + /// + ///
+ pub fn reanalyze(&self, update_type: FunctionUpdateType) { + unsafe { BNReanalyzeFunction(self.handle, update_type) } + } + + /// Generate internal debug reports for a variety of analysis. + /// Current list of possible values include: + /// + /// - mlil_translator + /// - stack_adjust_graph + /// - high_level_il + /// + /// * `name` - Name of the debug report + pub fn request_debug_report(&self, name: &str) { + const DEBUG_REPORT_ALIAS: &[(&str, &str)] = &[ + ("stack", "stack_adjust_graph\x00"), + ("mlil", "mlil_translator\x00"), + ("hlil", "high_level_il\x00"), + ]; + + if let Some(alias_idx) = DEBUG_REPORT_ALIAS + .iter() + .position(|(alias, _value)| *alias == name) + { + let name = DEBUG_REPORT_ALIAS[alias_idx].1.as_ptr() as *const i8; + unsafe { BNRequestFunctionDebugReport(self.handle, name) } + } else { + let name = std::ffi::CString::new(name.to_string()).unwrap(); + unsafe { BNRequestFunctionDebugReport(self.handle, name.as_ptr()) } + } + + self.view().update_analysis() + } + + /// Whether function was automatically discovered s a result of some creation of a 'user' function. + /// 'user' functions may or may not have been created by a user through the or API. For instance the entry point + /// into a function is always created a 'user' function. 'user' functions should be considered the root of auto + /// analysis. + pub fn auto(&self) -> bool { + unsafe { BNWasFunctionAutomaticallyDiscovered(self.handle) } + } + + /// Returns a list of possible call sites contained in this function. + /// This includes ordinary calls, tail calls, and indirect jumps. Not all of + /// the returned call sites are necessarily true call sites; some may simply + /// be unresolved indirect jumps, for example. + pub fn call_sites(&self) -> Array { + let mut count = 0; + let refs = unsafe { BNGetFunctionCallSites(self.handle, &mut count) }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, ()) } + } + + /// Returns a list of ReferenceSource objects corresponding to the addresses + /// in functions which reference this function + pub fn caller_sites(&self) -> Array { + self.view().get_code_refs(self.start()) + } + + /// Calling convention used by the function + pub fn calling_convention(&self) -> Option>>> { + let result = unsafe { BNGetFunctionCallingConvention(self.handle) }; + (!result.convention.is_null()).then(|| { + Conf::new( + unsafe { CallingConvention::ref_from_raw(result.convention, self.arch()) }, + result.confidence, + ) + }) + } + + /// Set the User calling convention used by the function + pub fn set_user_calling_convention<'a, I>(&self, value: Option) + where + I: Into>>, + { + let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() }; + if let Some(value) = value { + let value = value.into(); + conv_conf.convention = value.contents.handle; + conv_conf.confidence = value.confidence; + } + unsafe { BNSetUserFunctionCallingConvention(self.handle, &mut conv_conf) } + } + + /// Set the calling convention used by the function + pub fn set_auto_calling_convention<'a, I>(&self, value: Option) + where + I: Into>>, + { + let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() }; + if let Some(value) = value { + let value = value.into(); + conv_conf.convention = value.contents.handle; + conv_conf.confidence = value.confidence; + } + unsafe { BNSetAutoFunctionCallingConvention(self.handle, &mut conv_conf) } + } + + pub fn can_return(&self) -> Conf { + unsafe { BNCanFunctionReturn(self.handle) }.into() + } + + pub fn set_user_can_return(&self, value: I) + where + I: Into>, + { + let value: Conf = value.into(); + let mut value_raw: BNBoolWithConfidence = value.into(); + unsafe { BNSetUserFunctionCanReturn(self.handle, &mut value_raw) } + } + + pub fn set_auto_can_return(&self, value: I) + where + I: Into>, + { + let value: Conf = value.into(); + let mut value_raw: BNBoolWithConfidence = value.into(); + unsafe { BNSetAutoFunctionCanReturn(self.handle, &mut value_raw) } + } + + /// Whether function has explicitly defined types + pub fn has_explicitly_defined_type(&self) -> bool { + unsafe { BNFunctionHasExplicitlyDefinedType(self.handle) } + } + + pub fn has_user_annotations(&self) -> bool { + unsafe { BNFunctionHasUserAnnotations(self.handle) } + } + + pub fn has_variable_arguments(&self) -> Conf { + unsafe { BNFunctionHasVariableArguments(self.handle) }.into() + } + + pub fn set_user_has_variable_arguments(&self, value: I) + where + I: Into>, + { + let bc: Conf = value.into(); + let mut bc = bc.into(); + unsafe { BNSetUserFunctionHasVariableArguments(self.handle, &mut bc) } + } + + pub fn set_auto_has_variable_arguments(&self, value: I) + where + I: Into>, + { + let bc: Conf = value.into(); + let mut bc = bc.into(); + unsafe { BNSetAutoFunctionHasVariableArguments(self.handle, &mut bc) } + } + + /// Has unresolved indirect branches + pub fn has_unresolved_indirect_branches(&self) -> bool { + unsafe { BNHasUnresolvedIndirectBranches(self.handle) } + } + + /// List of address of unresolved indirect branches + pub fn unresolved_indirect_branches(&self) -> Array { + let mut count = 0; + let result = unsafe { BNGetUnresolvedIndirectBranches(self.handle, &mut count) }; + unsafe { Array::new(result, count, ()) } + } + + /// Returns a string representing the provenance. This portion of the API + /// is under develoment. Currently the provenance information is + /// undocumented, not persistent, and not saved to a database. + pub fn provenance(&self) -> BnString { + unsafe { BnString::from_raw(BNGetProvenanceString(self.handle)) } + } + + /// Get registers that are used for the return value + pub fn return_registers(&self) -> Conf> { + let result = unsafe { BNGetFunctionReturnRegisters(self.handle) }; + let regs = unsafe { Array::new(result.regs, result.count, self.arch().handle()) }; + Conf::new(regs, result.confidence) + } + + pub fn set_user_return_registers(&self, values: I, confidence: u8) + where + I: IntoIterator, + { + let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect(); + let mut regs = BNRegisterSetWithConfidence { + regs: regs.as_mut_ptr(), + count: regs.len(), + confidence, + }; + unsafe { BNSetUserFunctionReturnRegisters(self.handle, &mut regs) } + } + + pub fn set_auto_return_registers(&self, values: I, confidence: u8) + where + I: IntoIterator, + { + let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect(); + let mut regs = BNRegisterSetWithConfidence { + regs: regs.as_mut_ptr(), + count: regs.len(), + confidence, + }; + unsafe { BNSetAutoFunctionReturnRegisters(self.handle, &mut regs) } + } + + /// Flow graph of unresolved stack adjustments + pub fn unresolved_stack_adjustment_graph(&self) -> Option> { + let graph = unsafe { BNGetUnresolvedStackAdjustmentGraph(self.handle) }; + (!graph.is_null()).then(|| unsafe { Ref::new(FlowGraph::from_raw(graph)) }) + } + + pub fn create_graph( + &self, + graph_type: FunctionGraphType, + settings: Option, + ) -> Ref { + let settings_raw = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut()); + let result = unsafe { BNCreateFunctionGraph(self.handle, graph_type, settings_raw) }; + unsafe { Ref::new(FlowGraph::from_raw(result)) } + } } impl fmt::Debug for Function { @@ -475,3 +2236,70 @@ unsafe impl CoreArrayProviderInner for AddressRange { mem::transmute(raw) } } + +///////////////// +// PerformanceInfo + +// NOTE only exists as Array, cant be owned +#[repr(transparent)] +pub struct PerformanceInfo(BNPerformanceInfo); + +impl PerformanceInfo { + pub fn name(&self) -> &str { + unsafe { std::ffi::CStr::from_ptr(self.0.name) } + .to_str() + .unwrap() + } + pub fn seconds(&self) -> f64 { + self.0.seconds + } +} + +impl CoreArrayProvider for PerformanceInfo { + type Raw = BNPerformanceInfo; + type Context = (); + type Wrapped<'a> = Guard<'a, PerformanceInfo>; +} +unsafe impl CoreArrayProviderInner for PerformanceInfo { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeAnalysisPerformanceInfo(raw, count); + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(Self(*raw), context) + } +} + +///////////////// +// Comments + +// NOTE only exists as Array, cant be owned +pub struct Comments { + addr: u64, + comment: BnString, +} + +impl Comments { + pub fn address(&self) -> u64 { + self.addr + } + pub fn comment(&self) -> &str { + self.comment.as_str() + } +} + +impl CoreArrayProvider for Comments { + type Raw = u64; + type Context = Ref; + type Wrapped<'a> = Comments; +} +unsafe impl CoreArrayProviderInner for Comments { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeAddressList(raw); + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, function: &'a Self::Context) -> Self::Wrapped<'a> { + Comments { + addr: *raw, + comment: function.comment_at(*raw), + } + } +} diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index 7e77e379e..f98b18847 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -1,4 +1,3 @@ -use binaryninjacore_sys::BNFromVariableIdentifier; use binaryninjacore_sys::BNGetHighLevelILByIndex; use binaryninjacore_sys::BNHighLevelILOperation; @@ -985,7 +984,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 { diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs index 0b662578c..e0856b931 100644 --- a/rust/src/mlil/function.rs +++ b/rust/src/mlil/function.rs @@ -1,19 +1,13 @@ use core::hash::{Hash, Hasher}; -use binaryninjacore_sys::BNFreeMediumLevelILFunction; -use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList; -use binaryninjacore_sys::BNGetMediumLevelILIndexForInstruction; -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::function::{Function, Location}; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable}; +use crate::string::BnStrCompatible; +use crate::types::{Conf, PossibleValueSet, Type, UserVariableValues, Variable}; use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction}; @@ -105,6 +99,277 @@ 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>>( + 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 i8, + ) + } + } + + 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>>( + &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>, 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 i8, + ) + } + } + + 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>>( + &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 i8, + 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 { + 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, + arch: Option, + ) -> Array { + 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()) } + } } impl ToOwned for MediumLevelILFunction { @@ -132,3 +397,125 @@ impl core::fmt::Debug for MediumLevelILFunction { write!(f, "", 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.instr_idxs + .next() + .map(|i| self.mlil.instruction_from_instruction_idx(*i)) + } +} + +impl DoubleEndedIterator for MediumLevelILInstructionList<'_> { + fn next_back(&mut self) -> Option { + 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, + _func: Ref, + _arch: CoreArchitecture, + addr: u64, + type_: FunctionGraphType, + expr_id: usize, +} + +impl ILReferenceSource { + unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref) -> 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; + 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; + 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()), + } + } +} diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs index bd2cc7171..cb7628853 100644 --- a/rust/src/mlil/instruction.rs +++ b/rust/src/mlil/instruction.rs @@ -1,4 +1,4 @@ -use binaryninjacore_sys::BNFromVariableIdentifier; +use binaryninjacore_sys::BNGetDefaultIndexForMediumLevelILVariableDefinition; use binaryninjacore_sys::BNGetMediumLevelILByIndex; use binaryninjacore_sys::BNMediumLevelILInstruction; use binaryninjacore_sys::BNMediumLevelILOperation; @@ -1034,6 +1034,22 @@ impl MediumLevelILInstruction { } } + /// 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 get_split_var_for_definition(&self, var: &Variable) -> Variable { + let new_index = unsafe { + BNGetDefaultIndexForMediumLevelILVariableDefinition( + self.function.handle, + &var.raw(), + self.index, + ) + }; + Variable::new(var.t, new_index, var.storage) + } + fn lift_operand(&self, expr_idx: usize) -> Box { Box::new(self.function.lifted_instruction_from_idx(expr_idx)) } @@ -1121,7 +1137,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 { diff --git a/rust/src/operand_iter.rs b/rust/src/operand_iter.rs index 5f0fbd8ec..0d24fd495 100644 --- a/rust/src/operand_iter.rs +++ b/rust/src/operand_iter.rs @@ -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 ExactSizeIterator for OperandSSAVarIter { } 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 { diff --git a/rust/src/tags.rs b/rust/src/tags.rs index 65680b8cb..912ee81b1 100644 --- a/rust/src/tags.rs +++ b/rust/src/tags.rs @@ -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 {} @@ -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, + arch: CoreArchitecture, + func: Ref, + 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) + } +} diff --git a/rust/src/types.rs b/rust/src/types.rs index 8292db073..1a5918012 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -31,15 +31,14 @@ use crate::{ use lazy_static::lazy_static; use std::{ - borrow::Cow, - collections::HashSet, + borrow::{Borrow, Cow}, + collections::{HashMap, HashSet}, ffi::CStr, - fmt, - fmt::{Debug, Display, Formatter}, + fmt::{self, Debug, Display, Formatter}, hash::{Hash, Hasher}, iter::{zip, IntoIterator}, - mem, - mem::ManuallyDrop, + mem::{self, ManuallyDrop}, + ops::Range, os::raw::c_char, ptr, result, slice, sync::Mutex, @@ -215,6 +214,8 @@ impl Clone for Conf { } } +impl Copy for Conf {} + impl From for Conf { fn from(contents: T) -> Self { Self::new(contents, max_confidence()) @@ -1378,6 +1379,9 @@ impl Variable { storage: var.storage, } } + pub(crate) unsafe fn from_identifier(var: u64) -> Self { + Self::from_raw(unsafe { BNFromVariableIdentifier(var) }) + } pub(crate) fn raw(&self) -> BNVariable { BNVariable { @@ -1388,6 +1392,43 @@ impl Variable { } } +impl CoreArrayProvider for Variable { + type Raw = BNVariable; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for Variable { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeVariableList(raw) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Variable::from_raw(*raw) + } +} + +// Name, Variable and Type +impl CoreArrayProvider for (&str, Variable, &Type) { + type Raw = BNVariableNameAndType; + type Context = (); + type Wrapped<'a> = (&'a str, Variable, &'a Type) where Self: 'a; +} + +unsafe impl CoreArrayProviderInner for (&str, Variable, &Type) { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeVariableNameAndTypeList(raw, count) + } + unsafe fn wrap_raw<'a>( + raw: &'a Self::Raw, + _context: &'a Self::Context, + ) -> (&'a str, Variable, &'a Type) { + let name = CStr::from_ptr(raw.name).to_str().unwrap(); + let var = Variable::from_raw(raw.var); + let var_type = core::mem::transmute(&raw.type_); + (name, var, var_type) + } +} + ////////////// // SSAVariable @@ -1805,7 +1846,7 @@ impl StructureBuilder { &self, members: impl IntoIterator, ) -> &Self { - for (t, name) in members.into_iter() { + for (t, name) in members { self.append(t, name, MemberAccess::NoAccess, MemberScope::NoScope); } self @@ -2496,7 +2537,7 @@ pub struct NameAndType(pub(crate) BNNameAndType); impl NameAndType { pub(crate) unsafe fn from_raw(raw: &BNNameAndType) -> Self { - Self ( *raw ) + Self(*raw) } } @@ -2851,3 +2892,818 @@ impl ConstantData { // mem::transmute(raw) // } // } + +///////////////////////// +// ValueRange + +#[repr(transparent)] +#[derive(Copy, Clone, Debug)] +pub struct ValueRange { + raw: BNValueRange, + _t: core::marker::PhantomData, +} + +impl ValueRange { + fn from_raw(value: BNValueRange) -> Self { + Self { + raw: value, + _t: core::marker::PhantomData, + } + } + fn into_raw(self) -> BNValueRange { + self.raw + } +} + +impl IntoIterator for ValueRange { + type Item = u64; + type IntoIter = core::iter::StepBy>; + + fn into_iter(self) -> Self::IntoIter { + (self.raw.start..self.raw.end).step_by(self.raw.step.try_into().unwrap()) + } +} +impl IntoIterator for ValueRange { + type Item = i64; + type IntoIter = core::iter::StepBy>; + + fn into_iter(self) -> Self::IntoIter { + (self.raw.start as i64..self.raw.end as i64).step_by(self.raw.step.try_into().unwrap()) + } +} + +///////////////////////// +// PossibleValueSet + +#[derive(Clone, Debug)] +pub enum PossibleValueSet { + UndeterminedValue, + EntryValue { + reg: i64, + }, + ConstantValue { + value: i64, + }, + ConstantPointerValue { + value: i64, + }, + ExternalPointerValue, + StackFrameOffset { + offset: i64, + }, + ReturnAddressValue, + ImportedAddressValue, + SignedRangeValue { + offset: i64, + ranges: Vec>, + }, + UnsignedRangeValue { + offset: i64, + ranges: Vec>, + }, + LookupTableValue { + tables: Vec, + }, + InSetOfValues { + values: HashSet, + }, + NotInSetOfValues { + values: HashSet, + }, + ConstantDataValue { + value_type: ConstantDataType, + value: i64, + }, +} + +#[derive(Copy, Clone, Debug)] +pub enum ConstantDataType { + Value, + ZeroExtend, + SignExtend, + Aggregate, +} + +impl PossibleValueSet { + pub(crate) unsafe fn from_raw(value: BNPossibleValueSet) -> Self { + unsafe fn from_range(value: BNPossibleValueSet) -> Vec> { + core::slice::from_raw_parts(value.ranges, value.count) + .iter() + .copied() + .map(|range| ValueRange::from_raw(range)) + .collect() + } + let from_sets = |value: BNPossibleValueSet| { + unsafe { core::slice::from_raw_parts(value.valueSet, value.count) } + .iter() + .copied() + .collect() + }; + use BNRegisterValueType::*; + match value.state { + UndeterminedValue => Self::UndeterminedValue, + EntryValue => Self::EntryValue { reg: value.value }, + ConstantValue => Self::ConstantValue { value: value.value }, + ConstantPointerValue => Self::ConstantPointerValue { value: value.value }, + StackFrameOffset => Self::StackFrameOffset { + offset: value.value, + }, + ConstantDataValue => Self::ConstantDataValue { + value_type: ConstantDataType::Value, + value: value.value, + }, + ConstantDataZeroExtendValue => Self::ConstantDataValue { + value_type: ConstantDataType::ZeroExtend, + value: value.value, + }, + ConstantDataSignExtendValue => Self::ConstantDataValue { + value_type: ConstantDataType::SignExtend, + value: value.value, + }, + ConstantDataAggregateValue => Self::ConstantDataValue { + value_type: ConstantDataType::Aggregate, + value: value.value, + }, + SignedRangeValue => Self::SignedRangeValue { + offset: value.value, + ranges: from_range(value), + }, + UnsignedRangeValue => Self::UnsignedRangeValue { + offset: value.value, + ranges: from_range(value), + }, + LookupTableValue => { + let raw_tables = unsafe { core::slice::from_raw_parts(value.table, value.count) }; + let raw_from_tables = |i: &BNLookupTableEntry| unsafe { + core::slice::from_raw_parts(i.fromValues, i.fromCount) + }; + let tables = raw_tables + .iter() + .map(|table| LookupTableEntry { + from_values: raw_from_tables(table).to_vec(), + to_value: table.toValue, + }) + .collect(); + Self::LookupTableValue { tables } + } + NotInSetOfValues => Self::NotInSetOfValues { + values: from_sets(value), + }, + InSetOfValues => Self::InSetOfValues { + values: from_sets(value), + }, + ImportedAddressValue => Self::ImportedAddressValue, + ReturnAddressValue => Self::ReturnAddressValue, + ExternalPointerValue => Self::ExternalPointerValue, + } + } + pub(crate) fn into_raw(self) -> PossibleValueSetRaw { + let mut raw: BNPossibleValueSet = unsafe { core::mem::zeroed() }; + // set the state field + raw.state = self.value_type().into_raw_value(); + // set all other fields + match self { + PossibleValueSet::UndeterminedValue + | PossibleValueSet::ExternalPointerValue + | PossibleValueSet::ReturnAddressValue + | PossibleValueSet::ImportedAddressValue => {} + PossibleValueSet::EntryValue { reg: value } + | PossibleValueSet::ConstantValue { value } + | PossibleValueSet::ConstantPointerValue { value } + | PossibleValueSet::ConstantDataValue { value, .. } + | PossibleValueSet::StackFrameOffset { offset: value } => raw.value = value, + PossibleValueSet::NotInSetOfValues { values } + | PossibleValueSet::InSetOfValues { values } => { + let values = Box::leak(values.into_iter().collect()); + raw.valueSet = values.as_mut_ptr(); + raw.count = values.len(); + } + PossibleValueSet::SignedRangeValue { offset, ranges } => { + let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect()); + raw.value = offset; + raw.ranges = ranges.as_mut_ptr(); + raw.count = ranges.len(); + } + PossibleValueSet::UnsignedRangeValue { offset, ranges } => { + let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect()); + raw.value = offset; + raw.ranges = ranges.as_mut_ptr(); + raw.count = ranges.len(); + } + PossibleValueSet::LookupTableValue { tables } => { + let tables = Box::leak(tables.into_iter().map(|table| table.into_raw()).collect()); + // SAFETY: BNLookupTableEntry and LookupTableEntryRaw are transparent + raw.table = tables.as_mut_ptr() as *mut BNLookupTableEntry; + raw.count = tables.len(); + } + } + PossibleValueSetRaw(raw) + } + + pub fn value_type(&self) -> RegisterValueType { + use RegisterValueType::*; + match self { + PossibleValueSet::UndeterminedValue => UndeterminedValue, + PossibleValueSet::EntryValue { .. } => EntryValue, + PossibleValueSet::ConstantValue { .. } => ConstantValue, + PossibleValueSet::ConstantPointerValue { .. } => ConstantPointerValue, + PossibleValueSet::ExternalPointerValue => ExternalPointerValue, + PossibleValueSet::StackFrameOffset { .. } => StackFrameOffset, + PossibleValueSet::ReturnAddressValue => ReturnAddressValue, + PossibleValueSet::ImportedAddressValue => ImportedAddressValue, + PossibleValueSet::SignedRangeValue { .. } => SignedRangeValue, + PossibleValueSet::UnsignedRangeValue { .. } => UnsignedRangeValue, + PossibleValueSet::LookupTableValue { .. } => LookupTableValue, + PossibleValueSet::InSetOfValues { .. } => InSetOfValues, + PossibleValueSet::NotInSetOfValues { .. } => NotInSetOfValues, + PossibleValueSet::ConstantDataValue { + value_type: ConstantDataType::Value, + .. + } => ConstantDataValue, + PossibleValueSet::ConstantDataValue { + value_type: ConstantDataType::ZeroExtend, + .. + } => ConstantDataZeroExtendValue, + PossibleValueSet::ConstantDataValue { + value_type: ConstantDataType::SignExtend, + .. + } => ConstantDataSignExtendValue, + PossibleValueSet::ConstantDataValue { + value_type: ConstantDataType::Aggregate, + .. + } => ConstantDataAggregateValue, + } + } +} + +/// The owned version of the BNPossibleValueSet +#[repr(transparent)] +pub(crate) struct PossibleValueSetRaw(BNPossibleValueSet); + +impl PossibleValueSetRaw { + pub fn as_ffi(&self) -> &BNPossibleValueSet { + &self.0 + } +} + +impl Drop for PossibleValueSetRaw { + fn drop(&mut self) { + use BNRegisterValueType::*; + match self.0.state { + UndeterminedValue + | ExternalPointerValue + | ReturnAddressValue + | ImportedAddressValue + | EntryValue + | ConstantValue + | ConstantPointerValue + | StackFrameOffset + | ConstantDataValue + | ConstantDataZeroExtendValue + | ConstantDataSignExtendValue + | ConstantDataAggregateValue => {} + InSetOfValues | NotInSetOfValues => { + let _values: Box<[i64]> = unsafe { + Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.valueSet, self.0.count)) + }; + } + SignedRangeValue | UnsignedRangeValue => { + let _ranges: Box<[BNValueRange]> = unsafe { + Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.ranges, self.0.count)) + }; + } + LookupTableValue => { + // SAFETY: LookupTableEntryRaw and BNLookupTableEntry can be safely transmuted + let table_ptr = self.0.table as *mut LookupTableEntryRaw; + let _table: Box<[LookupTableEntryRaw]> = unsafe { + Box::from_raw(ptr::slice_from_raw_parts_mut(table_ptr, self.0.count)) + }; + } + } + } +} + +///////////////////////// +// LookupTableEntry + +#[derive(Clone, Debug)] +pub struct LookupTableEntry { + pub from_values: Vec, + pub to_value: i64, +} + +impl LookupTableEntry { + fn into_raw(self) -> LookupTableEntryRaw { + let from_value = Box::leak(self.from_values.into_boxed_slice()); + LookupTableEntryRaw(BNLookupTableEntry { + toValue: self.to_value, + fromValues: from_value.as_mut_ptr(), + fromCount: from_value.len(), + }) + } +} + +/// The owned version of the BNLookupTableEntry +#[repr(transparent)] +struct LookupTableEntryRaw(BNLookupTableEntry); +impl Drop for LookupTableEntryRaw { + fn drop(&mut self) { + let _from_value: Box<[i64]> = unsafe { + Box::from_raw(ptr::slice_from_raw_parts_mut( + self.0.fromValues, + self.0.fromCount, + )) + }; + } +} + +///////////////////////// +// ArchAndAddr + +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub struct ArchAndAddr { + pub arch: CoreArchitecture, + pub address: u64, +} + +///////////////////////// +// UserVariableValues + +pub struct UserVariableValues { + pub(crate) vars: *const [BNUserVariableValue], +} + +impl UserVariableValues { + pub fn into_hashmap(self) -> HashMap> { + let mut result: HashMap> = HashMap::new(); + for (var, def_site, possible_val) in self.all() { + result + .entry(var) + .or_default() + .entry(def_site) + .or_insert(possible_val); + } + result + } + pub fn all(&self) -> impl Iterator { + unsafe { &*self.vars }.iter().map(|var_val| { + let var = unsafe { Variable::from_raw(var_val.var) }; + let def_site = ArchAndAddr { + arch: unsafe { CoreArchitecture::from_raw(var_val.defSite.arch) }, + address: var_val.defSite.address, + }; + let possible_val = unsafe { PossibleValueSet::from_raw(var_val.value) }; + (var, def_site, possible_val) + }) + } + pub fn values_from_variable( + &self, + var: Variable, + ) -> impl Iterator { + self.all() + .filter(move |(t_var, _, _)| t_var == &var) + .map(|(_var, def_site, possible_val)| (def_site, possible_val)) + } +} + +impl Drop for UserVariableValues { + fn drop(&mut self) { + unsafe { BNFreeUserVariableValues(self.vars as *mut BNUserVariableValue) }; + } +} + +///////////////////////// +// ConstantReference + +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub struct ConstantReference { + pub value: i64, + pub size: usize, + pub pointer: bool, + pub intermediate: bool, +} + +impl ConstantReference { + pub fn from_raw(value: BNConstantReference) -> Self { + Self { + value: value.value, + size: value.size, + pointer: value.pointer, + intermediate: value.intermediate, + } + } + pub fn into_raw(self) -> BNConstantReference { + BNConstantReference { + value: self.value, + size: self.size, + pointer: self.pointer, + intermediate: self.intermediate, + } + } +} + +impl CoreArrayProvider for ConstantReference { + type Raw = BNConstantReference; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for ConstantReference { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeConstantReferenceList(raw) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(*raw) + } +} + +///////////////////////// +// IndirectBranchInfo + +pub struct IndirectBranchInfo { + pub source_arch: CoreArchitecture, + pub source_addr: u64, + pub dest_arch: CoreArchitecture, + pub dest_addr: u64, + pub auto_defined: bool, +} + +impl IndirectBranchInfo { + pub fn from_raw(value: BNIndirectBranchInfo) -> Self { + Self { + source_arch: unsafe { CoreArchitecture::from_raw(value.sourceArch) }, + source_addr: value.sourceAddr, + dest_arch: unsafe { CoreArchitecture::from_raw(value.destArch) }, + dest_addr: value.destAddr, + auto_defined: value.autoDefined, + } + } + pub fn into_raw(self) -> BNIndirectBranchInfo { + BNIndirectBranchInfo { + sourceArch: self.source_arch.0, + sourceAddr: self.source_addr, + destArch: self.dest_arch.0, + destAddr: self.dest_addr, + autoDefined: self.auto_defined, + } + } +} + +impl CoreArrayProvider for IndirectBranchInfo { + type Raw = BNIndirectBranchInfo; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for IndirectBranchInfo { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeIndirectBranchList(raw) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(*raw) + } +} + +///////////////////////// +// HighlightStandardColor + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum HighlightStandardColor { + //NoHighlightColor, + BlueHighlightColor, + GreenHighlightColor, + CyanHighlightColor, + RedHighlightColor, + MagentaHighlightColor, + YellowHighlightColor, + OrangeHighlightColor, + WhiteHighlightColor, + BlackHighlightColor, +} + +impl HighlightStandardColor { + pub fn from_raw(value: BNHighlightStandardColor) -> Option { + Some(match value { + BNHighlightStandardColor::NoHighlightColor => return None, + BNHighlightStandardColor::BlueHighlightColor => Self::BlueHighlightColor, + BNHighlightStandardColor::GreenHighlightColor => Self::GreenHighlightColor, + BNHighlightStandardColor::CyanHighlightColor => Self::CyanHighlightColor, + BNHighlightStandardColor::RedHighlightColor => Self::RedHighlightColor, + BNHighlightStandardColor::MagentaHighlightColor => Self::MagentaHighlightColor, + BNHighlightStandardColor::YellowHighlightColor => Self::YellowHighlightColor, + BNHighlightStandardColor::OrangeHighlightColor => Self::OrangeHighlightColor, + BNHighlightStandardColor::WhiteHighlightColor => Self::WhiteHighlightColor, + BNHighlightStandardColor::BlackHighlightColor => Self::BlackHighlightColor, + }) + } + pub fn into_raw(self) -> BNHighlightStandardColor { + match self { + //Self::NoHighlightColor => BNHighlightStandardColor::NoHighlightColor, + Self::BlueHighlightColor => BNHighlightStandardColor::BlueHighlightColor, + Self::GreenHighlightColor => BNHighlightStandardColor::GreenHighlightColor, + Self::CyanHighlightColor => BNHighlightStandardColor::CyanHighlightColor, + Self::RedHighlightColor => BNHighlightStandardColor::RedHighlightColor, + Self::MagentaHighlightColor => BNHighlightStandardColor::MagentaHighlightColor, + Self::YellowHighlightColor => BNHighlightStandardColor::YellowHighlightColor, + Self::OrangeHighlightColor => BNHighlightStandardColor::OrangeHighlightColor, + Self::WhiteHighlightColor => BNHighlightStandardColor::WhiteHighlightColor, + Self::BlackHighlightColor => BNHighlightStandardColor::BlackHighlightColor, + } + } +} + +///////////////////////// +// HighlightColor + +#[derive(Debug, Copy, Clone)] +pub enum HighlightColor { + NoHighlightColor { + alpha: u8, + }, + StandardHighlightColor { + color: HighlightStandardColor, + alpha: u8, + }, + MixedHighlightColor { + color: HighlightStandardColor, + mix_color: HighlightStandardColor, + mix: u8, + alpha: u8, + }, + CustomHighlightColor { + r: u8, + g: u8, + b: u8, + alpha: u8, + }, +} + +impl HighlightColor { + pub fn from_raw(raw: BNHighlightColor) -> Self { + const HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::StandardHighlightColor as u32; + const MIXED_HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::MixedHighlightColor as u32; + const CUSTOM_HIGHLIHGT_COLOR: u32 = BNHighlightColorStyle::CustomHighlightColor as u32; + match raw.style as u32 { + HIGHLIGHT_COLOR => { + let Some(color) = HighlightStandardColor::from_raw(raw.color) else { + // StandardHighlightColor with NoHighlightColor, is no color + return Self::NoHighlightColor { alpha: raw.alpha }; + }; + Self::StandardHighlightColor { + color, + alpha: raw.alpha, + } + } + MIXED_HIGHLIGHT_COLOR => { + let Some(color) = HighlightStandardColor::from_raw(raw.color) else { + panic!("Highlight mixed color with no color"); + }; + let Some(mix_color) = HighlightStandardColor::from_raw(raw.mixColor) else { + panic!("Highlight mixed color with no mix_color"); + }; + Self::MixedHighlightColor { + color, + mix_color, + mix: raw.mix, + alpha: raw.alpha, + } + } + CUSTOM_HIGHLIHGT_COLOR => Self::CustomHighlightColor { + r: raw.r, + g: raw.g, + b: raw.b, + alpha: raw.alpha, + }, + // other color style is just no color + _ => Self::NoHighlightColor { alpha: u8::MAX }, + } + } + + pub fn into_raw(self) -> BNHighlightColor { + let zeroed: BNHighlightColor = unsafe { core::mem::zeroed() }; + match self { + Self::NoHighlightColor { alpha } => BNHighlightColor { + style: BNHighlightColorStyle::StandardHighlightColor, + color: BNHighlightStandardColor::NoHighlightColor, + alpha, + ..zeroed + }, + Self::StandardHighlightColor { color, alpha } => BNHighlightColor { + style: BNHighlightColorStyle::StandardHighlightColor, + color: color.into_raw(), + alpha, + ..zeroed + }, + Self::MixedHighlightColor { + color, + mix_color, + mix, + alpha, + } => BNHighlightColor { + color: color.into_raw(), + mixColor: mix_color.into_raw(), + mix, + alpha, + ..zeroed + }, + Self::CustomHighlightColor { r, g, b, alpha } => BNHighlightColor { + r, + g, + b, + alpha, + ..zeroed + }, + } + } +} + +///////////////////////// +// IntegerDisplayType + +pub type IntegerDisplayType = binaryninjacore_sys::BNIntegerDisplayType; + +///////////////////////// +// StackVariableReference + +#[derive(Debug, Clone)] +pub struct StackVariableReference { + _source_operand: u32, + var_type: Conf>, + name: BnString, + var: Variable, + offset: i64, + size: usize, +} + +impl StackVariableReference { + pub fn from_raw(value: BNStackVariableReference) -> Self { + let var_type = Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, + ); + let name = unsafe { BnString::from_raw(value.name) }; + let var = unsafe { Variable::from_identifier(value.varIdentifier) }; + let offset = value.referencedOffset; + let size = value.size; + Self { + _source_operand: value.sourceOperand, + var_type, + name, + var, + offset, + size, + } + } + pub fn variable(&self) -> &Variable { + &self.var + } + pub fn variable_type(&self) -> Conf<&Type> { + self.var_type.as_ref() + } + pub fn name(&self) -> &str { + self.name.as_str() + } + pub fn offset(&self) -> i64 { + self.offset + } + pub fn size(&self) -> usize { + self.size + } +} + +impl CoreArrayProvider for StackVariableReference { + type Raw = BNStackVariableReference; + type Context = (); + type Wrapped<'a> = Guard<'a, Self>; +} + +unsafe impl CoreArrayProviderInner for StackVariableReference { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeStackVariableReferenceList(raw, count) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(Self::from_raw(*raw), context) + } +} + +///////////////////////// +// RegisterStackAdjustment + +#[derive(Debug, Copy, Clone)] +pub struct RegisterStackAdjustment { + reg_id: u32, + adjustment: Conf, + arch: A::Handle, +} + +impl RegisterStackAdjustment { + pub(crate) unsafe fn from_raw(value: BNRegisterStackAdjustment, arch: A::Handle) -> Self { + RegisterStackAdjustment { + reg_id: value.regStack, + adjustment: Conf::new(value.adjustment, value.confidence), + arch, + } + } + pub(crate) fn into_raw(self) -> BNRegisterStackAdjustment { + BNRegisterStackAdjustment { + regStack: self.reg_id, + adjustment: self.adjustment.contents, + confidence: self.adjustment.confidence, + } + } + pub fn new(reg_id: u32, adjustment: I, arch_handle: A::Handle) -> Self + where + I: Into>, + { + Self { + reg_id, + adjustment: adjustment.into(), + arch: arch_handle, + } + } + pub const fn register_id(&self) -> u32 { + self.reg_id + } + pub fn register(&self) -> A::Register { + self.arch.borrow().register_from_id(self.reg_id).unwrap() + } +} + +impl CoreArrayProvider for RegisterStackAdjustment { + type Raw = BNRegisterStackAdjustment; + type Context = A::Handle; + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for RegisterStackAdjustment { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRegisterStackAdjustments(raw) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(*raw, context.clone()) + } +} + +///////////////////////// +// RegisterStackAdjustment + +// NOTE only exists as part of an Array, never owned +pub struct MergedVariable { + target: Variable, + // droped by the CoreArrayProviderInner::free + sources: ManuallyDrop>, +} + +impl MergedVariable { + pub fn target(&self) -> Variable { + self.target + } + pub fn sources(&self) -> &Array { + &self.sources + } +} + +impl CoreArrayProvider for MergedVariable { + type Raw = BNMergedVariable; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for MergedVariable { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeMergedVariableList(raw, count) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self { + target: Variable::from_raw(raw.target), + sources: ManuallyDrop::new(Array::new(raw.sources, raw.sourceCount, ())), + } + } +} + +///////////////////////// +// UnresolvedIndirectBranches + +// NOTE only exists as part of an Array, never owned +pub struct UnresolvedIndirectBranches(u64); + +impl UnresolvedIndirectBranches { + pub fn address(&self) -> u64 { + self.0 + } +} + +impl CoreArrayProvider for UnresolvedIndirectBranches { + type Raw = u64; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for UnresolvedIndirectBranches { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeAddressList(raw) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self(*raw) + } +}