diff options
| author | Rubens Brandao <git@rubens.io> | 2024-05-07 09:52:19 -0300 |
|---|---|---|
| committer | Kyle Martin <krm504@nyu.edu> | 2024-05-20 15:43:59 -0400 |
| commit | 27bc08fa90d4381b4fd5d896dda991824504b0fd (patch) | |
| tree | 842d477d932439765e94783f65f46dc66afd661c /rust/src/function.rs | |
| parent | 82dd5d4b6fb97f4754d80aefe514d74b61c79054 (diff) | |
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`
Diffstat (limited to 'rust/src/function.rs')
| -rw-r--r-- | rust/src/function.rs | 1854 |
1 files changed, 1841 insertions, 13 deletions
diff --git a/rust/src/function.rs b/rust/src/function.rs index 04be898f..78b7da9f 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<CoreArchitecture>, @@ -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<T: Into<Conf<bool>>>(&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<Comments> { + 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<BasicBlock<NativeBlock>> { 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<CoreArchitecture>, ) -> Option<Ref<BasicBlock<NativeBlock>>> { + 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<CoreArchitecture>, + ) -> Array<Array<InstructionTextToken>> { + 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<Ref<hlil::HighLevelILFunction>> { + 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<Ref<mlil::MediumLevelILFunction>, ()> { + 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<Ref<mlil::MediumLevelILFunction>, ()> { + 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<Ref<mlil::MediumLevelILFunction>, ()> { unsafe { let mlil = BNGetFunctionMediumLevelIL(self.handle); @@ -263,6 +331,11 @@ impl Function { } } + pub fn medium_level_il_if_available(&self) -> Option<Ref<mlil::MediumLevelILFunction>> { + let mlil = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) }; + (!mlil.is_null()).then(|| unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) + } + pub fn low_level_il(&self) -> Result<Ref<llil::RegularFunction<CoreArchitecture>>, ()> { unsafe { let llil = BNGetFunctionLowLevelIL(self.handle); @@ -275,6 +348,13 @@ impl Function { } } + pub fn low_level_il_if_available( + &self, + ) -> Option<Ref<llil::RegularFunction<CoreArchitecture>>> { + let llil = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) }; + (!llil.is_null()).then(|| unsafe { llil::RegularFunction::from_raw(self.arch(), llil) }) + } + pub fn lifted_il(&self) -> Result<Ref<llil::LiftedFunction<CoreArchitecture>>, ()> { unsafe { let llil = BNGetFunctionLiftedIL(self.handle); @@ -287,6 +367,11 @@ impl Function { } } + pub fn lifted_il_if_available(&self) -> Option<Ref<llil::LiftedFunction<CoreArchitecture>>> { + let llil = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) }; + (!llil.is_null()).then(|| unsafe { llil::LiftedFunction::from_raw(self.arch(), llil) }) + } + pub fn return_type(&self) -> Conf<Ref<Type>> { let result = unsafe { BNGetFunctionReturnType(self.handle) }; @@ -296,16 +381,54 @@ impl Function { ) } - pub fn function_type(&self) -> Ref<Type> { - unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) } + pub fn set_auto_return_type<'a, C>(&self, return_type: C) + where + C: Into<Conf<&'a Type>>, + { + 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<Conf<&'a Type>>, + { + 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<Type> { + 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<NamedTypedVariable> { 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<i64> { + unsafe { BNGetFunctionStackAdjustment(self.handle) }.into() + } + + /// Sets number of bytes removed from the stack after return + pub fn set_user_stack_adjustment<C>(&self, value: C) + where + C: Into<Conf<i64>>, + { + let value: Conf<i64> = 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<C>(&self, value: C) + where + C: Into<Conf<i64>>, + { + let value: Conf<i64> = 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<CoreArchitecture>) -> Conf<i64> { + 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<I>( + &self, + addr: u64, + adjust: I, + arch: Option<CoreArchitecture>, + ) where + I: Into<Conf<i64>>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf<i64> = adjust.into(); + unsafe { + BNSetUserCallStackAdjustment( + self.handle, + arch.0, + addr, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn set_auto_call_stack_adjustment<I>( + &self, + addr: u64, + adjust: I, + arch: Option<CoreArchitecture>, + ) where + I: Into<Conf<i64>>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf<i64> = adjust.into(); + unsafe { + BNSetAutoCallStackAdjustment( + self.handle, + arch.0, + addr, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn call_type_adjustment( + &self, + addr: u64, + arch: Option<CoreArchitecture>, + ) -> Option<Conf<Ref<Type>>> { + 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<I>, + arch: Option<CoreArchitecture>, + ) where + I: Into<Conf<&'a Type>>, + { + 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<CoreArchitecture>, + ) where + I: Into<Conf<&'a Type>>, + { + 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<CoreArchitecture>, + ) -> Array<RegisterStackAdjustment<CoreArchitecture>> { + 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<I>( + self, + addr: u64, + adjust: I, + arch: Option<CoreArchitecture>, + ) where + I: IntoIterator<Item = RegisterStackAdjustment<CoreArchitecture>>, + { + 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<I>( + &self, + addr: u64, + adjust: I, + arch: Option<CoreArchitecture>, + ) where + I: IntoIterator<Item = RegisterStackAdjustment<CoreArchitecture>>, + { + 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<CoreArchitecture>, + ) -> RegisterStackAdjustment<CoreArchitecture> { + 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<I>( + &self, + addr: u64, + reg_stack_id: u32, + adjust: I, + arch: Option<CoreArchitecture>, + ) where + I: Into<Conf<i32>>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf<i32> = 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<I>( + &self, + addr: u64, + reg_stack_id: u32, + adjust: I, + arch: Option<CoreArchitecture>, + ) where + I: Into<Conf<i32>>, + { + let arch = arch.unwrap_or_else(|| self.arch()); + let adjust: Conf<i32> = adjust.into(); + unsafe { + BNSetAutoCallRegisterStackAdjustmentForRegisterStack( + self.handle, + arch.0, + addr, + reg_stack_id, + adjust.contents, + adjust.confidence, + ) + } + } + + pub fn reg_stack_adjustments(&self) -> Array<RegisterStackAdjustment<CoreArchitecture>> { + 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<I, A>(&self, values: I) + where + I: IntoIterator<Item = RegisterStackAdjustment<A>>, + 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<I, A>(&self, values: I) + where + I: IntoIterator<Item = RegisterStackAdjustment<A>>, + 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<Variable> { + 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<Vec<Variable>> { unsafe { let mut variables = BNGetFunctionParameterVariables(self.handle); @@ -330,6 +764,66 @@ impl Function { } } + pub fn set_user_parameter_variables<I>(&self, values: I, confidence: u8) + where + I: IntoIterator<Item = Variable>, + { + 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<I>(&self, values: I, confidence: u8) + where + I: IntoIterator<Item = Variable>, + { + 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<CoreArchitecture>, + ) -> 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<bool> { + let result = unsafe { BNIsFunctionInlinedDuringAnalysis(self.handle) }; + result.into() + } + + pub fn set_auto_inline_during_analysis<C>(&self, value: C) + where + C: Into<Conf<bool>>, + { + let value: Conf<bool> = value.into(); + unsafe { + BNSetAutoFunctionInlinedDuringAnalysis( + self.handle, + BNBoolWithConfidence { + value: value.contents, + confidence: value.confidence, + }, + ) + } + } + + pub fn set_user_inline_during_analysis<C>(&self, value: C) + where + C: Into<Conf<bool>>, + { + let value: Conf<bool> = value.into(); + unsafe { + BNSetUserFunctionInlinedDuringAnalysis( + self.handle, + BNBoolWithConfidence { + value: value.contents, + confidence: value.confidence, + }, + ) + } + } + + pub fn analysis_performance_info(&self) -> Array<PerformanceInfo> { + 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<S: BnStrCompatible>( + &self, + tag_type: &TagType, + data: S, + addr: Option<u64>, + user: bool, + arch: Option<CoreArchitecture>, + ) { + 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<u64>, + user: bool, + arch: Option<CoreArchitecture>, + ) { + 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<u64>, + user: bool, + arch: Option<CoreArchitecture>, + ) { + 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<CoreArchitecture>) { + 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<CoreArchitecture>, + ) { + 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<CoreArchitecture>, + ) { + 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<CoreArchitecture>, + ) { + 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<CoreArchitecture>, + size: Option<usize>, + ) { + 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<CoreArchitecture>, + size: Option<usize>, + ) { + 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<usize>, + ) -> 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<CoreArchitecture>, + ) -> Array<ConstantReference> { + 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<CoreArchitecture>, + ) -> Array<ConstantReference> { + 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<bool>, tag_type: Option<&str>) -> Array<Tag> { + 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<TagReference> { + 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<bool>, + arch: Option<CoreArchitecture>, + ) -> Array<Tag> { + 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<u64>, + auto: Option<bool>, + arch: Option<CoreArchitecture>, + ) -> Array<TagReference> { + 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<IndirectBranchInfo> { + 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<I>( + &self, + source: u64, + branches: I, + arch: Option<CoreArchitecture>, + ) where + I: IntoIterator<Item = u64>, + { + 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<I>( + &self, + source: u64, + branches: I, + arch: Option<CoreArchitecture>, + ) where + I: IntoIterator<Item = u64>, + { + 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<CoreArchitecture>, + ) -> Array<IndirectBranchInfo> { + 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<CoreArchitecture>) -> 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 + /// + /// <div class="warning">Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.</div> + /// + /// * `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<CoreArchitecture>, + ) { + 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<CoreArchitecture>, + ) { + 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<CoreArchitecture>, + ) -> Option<u64> { + 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<CoreArchitecture>, + ) -> 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<CoreArchitecture>, + enum_display_typeid: Option<impl BnStrCompatible>, + ) { + 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<CoreArchitecture>, + ) -> 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<CoreArchitecture>, + ) -> (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<CoreArchitecture>, + ) -> 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<CoreArchitecture>, + ) -> 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<RegisterValue> { + let register = unsafe { BNGetFunctionRegisterValueAtExit(self.handle, reg) }; + Conf::new(register.value.into(), register.confidence) + } + + pub fn registers_read_by( + &self, + addr: u64, + arch: Option<CoreArchitecture>, + ) -> Array<CoreRegister> { + 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<CoreArchitecture>, + ) -> Array<CoreRegister> { + 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<Array<CoreRegister>> { + 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<I>(&self, registers: I, confidence: u8) + where + I: IntoIterator<Item = CoreRegister>, + { + 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<I>(&self, registers: I, confidence: u8) + where + I: IntoIterator<Item = CoreRegister>, + { + 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<CoreArchitecture>, + ) -> 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<CoreArchitecture>, + ) -> 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<CoreArchitecture>, + ) -> Option<(Variable, BnString, Conf<Ref<Type>>)> { + 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<CoreArchitecture>, + ) -> Array<StackVariableReference> { + 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<CoreArchitecture>, + ) -> Array<StackVariableReference> { + 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<RegisterValue> { + let result = unsafe { BNGetFunctionGlobalPointerValue(self.handle) }; + Conf::new(result.value.into(), result.confidence) + } + + pub fn type_tokens( + &self, + settings: Option<&DisassemblySettings>, + ) -> Array<DisassemblyTextLine> { + 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<CoreArchitecture>) -> 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<bool> { + unsafe { BNIsFunctionPure(self.handle) }.into() + } + + pub fn set_user_pure<C>(&self, value: C) + where + C: Into<Conf<bool>>, + { + let value: Conf<bool> = value.into(); + let mut value_raw = value.into(); + unsafe { BNSetUserFunctionPure(self.handle, &mut value_raw) }; + } + + pub fn set_auto_pure<C>(&self, value: C) + where + C: Into<Conf<bool>>, + { + let value: Conf<bool> = 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<MergedVariable> { + 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<Item = &'a Variable>, + ) { + 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<Item = &'a Variable>, + ) { + 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. + /// + /// <div class="warning"> + /// + /// 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. + /// + /// </div> + /// + /// * `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 + /// + /// <div class="warning"> + /// + /// If analysis_skipped is `true`, using this API will not trigger + /// re-analysis. Instead, use [Function::set_analysis_skipped] with `false`. + /// + /// </div> + 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<CodeReference> { + 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<CodeReference> { + self.view().get_code_refs(self.start()) + } + + /// Calling convention used by the function + pub fn calling_convention(&self) -> Option<Conf<Ref<CallingConvention<CoreArchitecture>>>> { + 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<I>) + where + I: Into<Conf<&'a CallingConvention<CoreArchitecture>>>, + { + 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<I>) + where + I: Into<Conf<&'a CallingConvention<CoreArchitecture>>>, + { + 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<bool> { + unsafe { BNCanFunctionReturn(self.handle) }.into() + } + + pub fn set_user_can_return<I>(&self, value: I) + where + I: Into<Conf<bool>>, + { + let value: Conf<bool> = value.into(); + let mut value_raw: BNBoolWithConfidence = value.into(); + unsafe { BNSetUserFunctionCanReturn(self.handle, &mut value_raw) } + } + + pub fn set_auto_can_return<I>(&self, value: I) + where + I: Into<Conf<bool>>, + { + let value: Conf<bool> = 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<bool> { + unsafe { BNFunctionHasVariableArguments(self.handle) }.into() + } + + pub fn set_user_has_variable_arguments<I>(&self, value: I) + where + I: Into<Conf<bool>>, + { + let bc: Conf<bool> = value.into(); + let mut bc = bc.into(); + unsafe { BNSetUserFunctionHasVariableArguments(self.handle, &mut bc) } + } + + pub fn set_auto_has_variable_arguments<I>(&self, value: I) + where + I: Into<Conf<bool>>, + { + let bc: Conf<bool> = 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<UnresolvedIndirectBranches> { + 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<Array<CoreRegister>> { + 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<I>(&self, values: I, confidence: u8) + where + I: IntoIterator<Item = CoreRegister>, + { + 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<I>(&self, values: I, confidence: u8) + where + I: IntoIterator<Item = CoreRegister>, + { + 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<Ref<FlowGraph>> { + 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<DisassemblySettings>, + ) -> Ref<FlowGraph> { + 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<PerformanceInfo>, 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<Comments>, 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<Function>; + 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), + } + } +} |
