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/mlil/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/mlil/function.rs')
| -rw-r--r-- | rust/src/mlil/function.rs | 411 |
1 files changed, 399 insertions, 12 deletions
diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs index 0b662578..e0856b93 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<Conf<&'a Type>>>( + self, + offset: i64, + var_type: C, + name: S, + ) { + let var_type = var_type.into(); + let mut raw_var_type: BNTypeWithConfidence = var_type.into(); + let name = name.into_bytes_with_nul(); + unsafe { + BNCreateUserStackVariable( + self.get_function().handle, + offset, + &mut raw_var_type, + name.as_ref().as_ptr() as *const 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<Conf<&'a Type>>>( + &self, + var: &Variable, + var_type: C, + name: S, + ignore_disjoint_uses: bool, + ) { + let var_type = var_type.into(); + let raw_var_type: BNTypeWithConfidence = var_type.into(); + let name = name.into_bytes_with_nul(); + unsafe { + BNCreateUserVariable( + self.get_function().handle, + &var.raw(), + &raw_var_type as *const _ as *mut _, + name.as_ref().as_ptr() as *const _, + ignore_disjoint_uses, + ) + } + } + + pub fn delete_user_var(&self, var: &Variable) { + unsafe { BNDeleteUserVariable(self.get_function().handle, &var.raw()) } + } + + pub fn is_var_user_defined(&self, var: &Variable) -> bool { + unsafe { BNIsVariableUserDefined(self.get_function().handle, &var.raw()) } + } + + /// Allows the user to specify a PossibleValueSet value for an MLIL + /// variable at its definition site. + /// + /// .. warning:: Setting the variable value, triggers a reanalysis of the + /// function and allows the dataflow to compute and propagate values which + /// depend on the current variable. This implies that branch conditions + /// whose values can be determined statically will be computed, leading to + /// potential branch elimination at the HLIL layer. + /// + /// * `var` - Variable for which the value is to be set + /// * `addr` - Address of the definition site of the variable + /// * `value` - Informed value of the variable + /// + /// # Example + /// ```no_run + /// # use binaryninja::mlil::MediumLevelILFunction; + /// # use binaryninja::types::PossibleValueSet; + /// # let mlil_fun: MediumLevelILFunction = todo!(); + /// let (mlil_var, arch_addr, _val) = mlil_fun.user_var_values().all().next().unwrap(); + /// let def_address = arch_addr.address; + /// let var_value = PossibleValueSet::ConstantValue{value: 5}; + /// mlil_fun.set_user_var_value(&mlil_var, def_address, var_value).unwrap(); + /// ``` + pub fn set_user_var_value( + &self, + var: &Variable, + addr: u64, + value: PossibleValueSet, + ) -> Result<(), ()> { + let Some(_def_site) = self + .get_var_definitions(var) + .find(|def| def.address == addr) + else { + // Error "No definition for Variable found at given address" + return Err(()); + }; + let function = self.get_function(); + let def_site = BNArchitectureAndAddress { + arch: function.arch().0, + address: addr, + }; + let value = value.into_raw(); + + unsafe { BNSetUserVariableValue(function.handle, &var.raw(), &def_site, value.as_ffi()) } + Ok(()) + } + + /// Clears a previously defined user variable value. + /// + /// * `var` - Variable for which the value was informed + /// * `def_addr` - Address of the definition site of the variable + pub fn clear_user_var_value(&self, var: &Variable, addr: u64) -> Result<(), ()> { + let Some(_var_def) = self + .get_var_definitions(var) + .find(|site| site.address == addr) + else { + //error "Could not get definition for Variable" + return Err(()); + }; + + let function = self.get_function(); + let def_site = BNArchitectureAndAddress { + arch: function.arch().0, + address: addr, + }; + + unsafe { BNClearUserVariableValue(function.handle, &var.raw(), &def_site) }; + Ok(()) + } + + /// Returns a map of current defined user variable values. + /// Returns a Map of user current defined user variable values and their definition sites. + pub fn user_var_values(&self) -> UserVariableValues { + let mut count = 0; + let function = self.get_function(); + let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) }; + assert!(!var_values.is_null()); + UserVariableValues { + vars: core::ptr::slice_from_raw_parts(var_values, count), + } + } + + /// Clear all user defined variable values. + pub fn clear_user_var_values(&self) -> Result<(), ()> { + for (var, arch_and_addr, _value) in self.user_var_values().all() { + self.clear_user_var_value(&var, arch_and_addr.address)?; + } + Ok(()) + } + + pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>( + &self, + offset: i64, + var_type: T, + name: S, + ) { + let var_type: Conf<&Type> = var_type.into(); + let mut var_type = var_type.into(); + let name = name.into_bytes_with_nul(); + let name_c_str = name.as_ref(); + unsafe { + BNCreateAutoStackVariable( + self.get_function().handle, + offset, + &mut var_type, + name_c_str.as_ptr() as *const 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<Conf<&'a Type>>>( + &self, + var: &Variable, + var_type: C, + name: S, + ignore_disjoint_uses: bool, + ) { + let var_type: Conf<&Type> = var_type.into(); + let mut var_type = var_type.into(); + let name = name.into_bytes_with_nul(); + let name_c_str = name.as_ref(); + unsafe { + BNCreateAutoVariable( + self.get_function().handle, + &var.raw(), + &mut var_type, + name_c_str.as_ptr() as *const 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<ILReferenceSource> { + let mut count = 0; + let refs = unsafe { + BNGetMediumLevelILVariableReferences( + self.get_function().handle, + &mut var.raw(), + &mut count, + ) + }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, self.to_owned()) } + } + + /// Returns a list of variables referenced by code in the function ``func``, + /// of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from + /// all functions and containing the address will be returned. If no architecture is specified, the + /// architecture of the function will be used. + /// This function is related to get_hlil_var_refs_from(), which returns variable references collected + /// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged + /// into a single variable in HLIL. + /// + /// * `addr` - virtual address to query for variable references + /// * `length` - optional length of query + /// * `arch` - optional architecture of query + pub fn var_refs_from( + &self, + addr: u64, + length: Option<u64>, + arch: Option<CoreArchitecture>, + ) -> Array<VariableReferenceSource> { + let function = self.get_function(); + let arch = arch.unwrap_or_else(|| function.arch()); + let mut count = 0; + + let refs = if let Some(length) = length { + unsafe { + BNGetMediumLevelILVariableReferencesInRange( + function.handle, + arch.0, + addr, + length, + &mut count, + ) + } + } else { + unsafe { + BNGetMediumLevelILVariableReferencesFrom(function.handle, arch.0, addr, &mut count) + } + }; + assert!(!refs.is_null()); + unsafe { Array::new(refs, count, self.to_owned()) } + } } impl ToOwned for MediumLevelILFunction { @@ -132,3 +397,125 @@ impl core::fmt::Debug for MediumLevelILFunction { write!(f, "<mlil func handle {:p}>", self.handle) } } + +#[derive(Clone, Debug)] +pub struct MediumLevelILInstructionList<'a> { + mlil: &'a MediumLevelILFunction, + ptr: *mut usize, + instr_idxs: core::slice::Iter<'a, usize>, +} + +impl Drop for MediumLevelILInstructionList<'_> { + fn drop(&mut self) { + unsafe { BNFreeILInstructionList(self.ptr) }; + } +} + +impl Iterator for MediumLevelILInstructionList<'_> { + type Item = MediumLevelILInstruction; + + fn next(&mut self) -> Option<Self::Item> { + self.instr_idxs + .next() + .map(|i| self.mlil.instruction_from_instruction_idx(*i)) + } +} + +impl DoubleEndedIterator for MediumLevelILInstructionList<'_> { + fn next_back(&mut self) -> Option<Self::Item> { + self.instr_idxs + .next_back() + .map(|i| self.mlil.instruction_from_instruction_idx(*i)) + } +} + +impl ExactSizeIterator for MediumLevelILInstructionList<'_> {} +impl core::iter::FusedIterator for MediumLevelILInstructionList<'_> {} + +///////////////////////// +// FunctionGraphType + +pub type FunctionGraphType = binaryninjacore_sys::BNFunctionGraphType; + +///////////////////////// +// ILReferenceSource + +pub struct ILReferenceSource { + mlil: Ref<MediumLevelILFunction>, + _func: Ref<Function>, + _arch: CoreArchitecture, + addr: u64, + type_: FunctionGraphType, + expr_id: usize, +} + +impl ILReferenceSource { + unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref<MediumLevelILFunction>) -> Self { + Self { + mlil, + _func: Function::from_raw(value.func), + _arch: CoreArchitecture::from_raw(value.arch), + addr: value.addr, + type_: value.type_, + expr_id: value.exprId, + } + } + pub fn addr(&self) -> u64 { + self.addr + } + pub fn graph_type(&self) -> FunctionGraphType { + self.type_ + } + pub fn expr(&self) -> MediumLevelILInstruction { + self.mlil.instruction_from_idx(self.expr_id) + } +} + +impl CoreArrayProvider for ILReferenceSource { + type Raw = BNILReferenceSource; + type Context = Ref<MediumLevelILFunction>; + type Wrapped<'a> = Self; +} +unsafe impl CoreArrayProviderInner for ILReferenceSource { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeILReferences(raw, count) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(*raw, context.to_owned()) + } +} + +///////////////////////// +// VariableReferenceSource + +pub struct VariableReferenceSource { + var: Variable, + source: ILReferenceSource, +} + +impl VariableReferenceSource { + pub fn variable(&self) -> &Variable { + &self.var + } + pub fn source(&self) -> &ILReferenceSource { + &self.source + } +} + +impl CoreArrayProvider for VariableReferenceSource { + type Raw = BNVariableReferenceSource; + type Context = Ref<MediumLevelILFunction>; + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for VariableReferenceSource { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeVariableReferenceSourceList(raw, count) + } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Self { + var: Variable::from_raw(raw.var), + source: ILReferenceSource::from_raw(raw.source, context.to_owned()), + } + } +} |
