update rust crate
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
use core::hash::{Hash, Hasher};
|
||||
use std::ffi::c_char;
|
||||
|
||||
use binaryninjacore_sys::BNFreeMediumLevelILFunction;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILInstructionCount;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILOwnerFunction;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILSSAForm;
|
||||
use binaryninjacore_sys::BNMediumLevelILFunction;
|
||||
use binaryninjacore_sys::BNMediumLevelILGetInstructionStart;
|
||||
use binaryninjacore_sys::BNNewMediumLevelILFunctionReference;
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::basicblock::BasicBlock;
|
||||
use crate::function::Function;
|
||||
use crate::function::Location;
|
||||
use crate::rc::{Array, Ref, RefCountable};
|
||||
use crate::disassembly::DisassemblySettings;
|
||||
use crate::flowgraph::FlowGraph;
|
||||
use crate::function::{Function, Location};
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
|
||||
use crate::string::BnStrCompatible;
|
||||
use crate::types::{
|
||||
Conf, PossibleValueSet, RegisterValue, SSAVariable, Type, UserVariableValues, Variable,
|
||||
};
|
||||
|
||||
use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction};
|
||||
|
||||
@@ -65,6 +65,19 @@ impl MediumLevelILFunction {
|
||||
self.instruction_from_idx(expr_idx).lift()
|
||||
}
|
||||
|
||||
pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> MediumLevelILInstruction {
|
||||
MediumLevelILInstruction::new(self.to_owned(), unsafe {
|
||||
BNGetMediumLevelILIndexForInstruction(self.handle, instr_idx)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lifted_instruction_from_instruction_idx(
|
||||
&self,
|
||||
instr_idx: usize,
|
||||
) -> MediumLevelILLiftedInstruction {
|
||||
self.instruction_from_instruction_idx(instr_idx).lift()
|
||||
}
|
||||
|
||||
pub fn instruction_count(&self) -> usize {
|
||||
unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
|
||||
}
|
||||
@@ -91,6 +104,478 @@ impl MediumLevelILFunction {
|
||||
|
||||
unsafe { Array::new(blocks, count, context) }
|
||||
}
|
||||
|
||||
pub fn get_var_definitions<'a>(&'a self, var: &Variable) -> MediumLevelILInstructionList<'a> {
|
||||
let mut count = 0;
|
||||
let raw_instrs =
|
||||
unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &var.raw(), &mut count) };
|
||||
assert!(!raw_instrs.is_null());
|
||||
let instrs = unsafe { core::slice::from_raw_parts(raw_instrs, count) };
|
||||
MediumLevelILInstructionList {
|
||||
mlil: self,
|
||||
ptr: raw_instrs,
|
||||
instr_idxs: instrs.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
|
||||
self,
|
||||
offset: i64,
|
||||
var_type: C,
|
||||
name: S,
|
||||
) {
|
||||
let var_type = var_type.into();
|
||||
let mut raw_var_type: BNTypeWithConfidence = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNCreateUserStackVariable(
|
||||
self.get_function().handle,
|
||||
offset,
|
||||
&mut raw_var_type,
|
||||
name.as_ref().as_ptr() as *const c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_user_stack_var(self, offset: i64) {
|
||||
unsafe { BNDeleteUserStackVariable(self.get_function().handle, offset) }
|
||||
}
|
||||
|
||||
pub fn create_user_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
|
||||
&self,
|
||||
var: &Variable,
|
||||
var_type: C,
|
||||
name: S,
|
||||
ignore_disjoint_uses: bool,
|
||||
) {
|
||||
let var_type = var_type.into();
|
||||
let raw_var_type: BNTypeWithConfidence = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNCreateUserVariable(
|
||||
self.get_function().handle,
|
||||
&var.raw(),
|
||||
&raw_var_type as *const _ as *mut _,
|
||||
name.as_ref().as_ptr() as *const _,
|
||||
ignore_disjoint_uses,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_user_var(&self, var: &Variable) {
|
||||
unsafe { BNDeleteUserVariable(self.get_function().handle, &var.raw()) }
|
||||
}
|
||||
|
||||
pub fn is_var_user_defined(&self, var: &Variable) -> bool {
|
||||
unsafe { BNIsVariableUserDefined(self.get_function().handle, &var.raw()) }
|
||||
}
|
||||
|
||||
/// Allows the user to specify a PossibleValueSet value for an MLIL
|
||||
/// variable at its definition site.
|
||||
///
|
||||
/// .. warning:: Setting the variable value, triggers a reanalysis of the
|
||||
/// function and allows the dataflow to compute and propagate values which
|
||||
/// depend on the current variable. This implies that branch conditions
|
||||
/// whose values can be determined statically will be computed, leading to
|
||||
/// potential branch elimination at the HLIL layer.
|
||||
///
|
||||
/// * `var` - Variable for which the value is to be set
|
||||
/// * `addr` - Address of the definition site of the variable
|
||||
/// * `value` - Informed value of the variable
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use binaryninja::mlil::MediumLevelILFunction;
|
||||
/// # use binaryninja::types::PossibleValueSet;
|
||||
/// # let mlil_fun: MediumLevelILFunction = todo!();
|
||||
/// let (mlil_var, arch_addr, _val) = mlil_fun.user_var_values().all().next().unwrap();
|
||||
/// let def_address = arch_addr.address;
|
||||
/// let var_value = PossibleValueSet::ConstantValue{value: 5};
|
||||
/// mlil_fun.set_user_var_value(&mlil_var, def_address, var_value).unwrap();
|
||||
/// ```
|
||||
pub fn set_user_var_value(
|
||||
&self,
|
||||
var: &Variable,
|
||||
addr: u64,
|
||||
value: PossibleValueSet,
|
||||
) -> Result<(), ()> {
|
||||
let Some(_def_site) = self
|
||||
.get_var_definitions(var)
|
||||
.find(|def| def.address == addr)
|
||||
else {
|
||||
// Error "No definition for Variable found at given address"
|
||||
return Err(());
|
||||
};
|
||||
let function = self.get_function();
|
||||
let def_site = BNArchitectureAndAddress {
|
||||
arch: function.arch().0,
|
||||
address: addr,
|
||||
};
|
||||
let value = value.into_raw();
|
||||
|
||||
unsafe { BNSetUserVariableValue(function.handle, &var.raw(), &def_site, value.as_ffi()) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clears a previously defined user variable value.
|
||||
///
|
||||
/// * `var` - Variable for which the value was informed
|
||||
/// * `def_addr` - Address of the definition site of the variable
|
||||
pub fn clear_user_var_value(&self, var: &Variable, addr: u64) -> Result<(), ()> {
|
||||
let Some(_var_def) = self
|
||||
.get_var_definitions(var)
|
||||
.find(|site| site.address == addr)
|
||||
else {
|
||||
//error "Could not get definition for Variable"
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let function = self.get_function();
|
||||
let def_site = BNArchitectureAndAddress {
|
||||
arch: function.arch().0,
|
||||
address: addr,
|
||||
};
|
||||
|
||||
unsafe { BNClearUserVariableValue(function.handle, &var.raw(), &def_site) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a map of current defined user variable values.
|
||||
/// Returns a Map of user current defined user variable values and their definition sites.
|
||||
pub fn user_var_values(&self) -> UserVariableValues {
|
||||
let mut count = 0;
|
||||
let function = self.get_function();
|
||||
let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
|
||||
assert!(!var_values.is_null());
|
||||
UserVariableValues {
|
||||
vars: core::ptr::slice_from_raw_parts(var_values, count),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all user defined variable values.
|
||||
pub fn clear_user_var_values(&self) -> Result<(), ()> {
|
||||
for (var, arch_and_addr, _value) in self.user_var_values().all() {
|
||||
self.clear_user_var_value(&var, arch_and_addr.address)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>(
|
||||
&self,
|
||||
offset: i64,
|
||||
var_type: T,
|
||||
name: S,
|
||||
) {
|
||||
let var_type: Conf<&Type> = var_type.into();
|
||||
let mut var_type = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
let name_c_str = name.as_ref();
|
||||
unsafe {
|
||||
BNCreateAutoStackVariable(
|
||||
self.get_function().handle,
|
||||
offset,
|
||||
&mut var_type,
|
||||
name_c_str.as_ptr() as *const c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_auto_stack_var(&self, offset: i64) {
|
||||
unsafe { BNDeleteAutoStackVariable(self.get_function().handle, offset) }
|
||||
}
|
||||
|
||||
pub fn create_auto_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
|
||||
&self,
|
||||
var: &Variable,
|
||||
var_type: C,
|
||||
name: S,
|
||||
ignore_disjoint_uses: bool,
|
||||
) {
|
||||
let var_type: Conf<&Type> = var_type.into();
|
||||
let mut var_type = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
let name_c_str = name.as_ref();
|
||||
unsafe {
|
||||
BNCreateAutoVariable(
|
||||
self.get_function().handle,
|
||||
&var.raw(),
|
||||
&mut var_type,
|
||||
name_c_str.as_ptr() as *const c_char,
|
||||
ignore_disjoint_uses,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of ILReferenceSource objects (IL xrefs or cross-references)
|
||||
/// that reference the given variable. The variable is a local variable that can be either on the stack,
|
||||
/// in a register, or in a flag.
|
||||
/// This function is related to get_hlil_var_refs(), which returns variable references collected
|
||||
/// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
|
||||
/// into a single variable in HLIL.
|
||||
///
|
||||
/// * `var` - Variable for which to query the xref
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use binaryninja::mlil::MediumLevelILFunction;
|
||||
/// # use binaryninja::types::Variable;
|
||||
/// # let mlil_fun: MediumLevelILFunction = todo!();
|
||||
/// # let mlil_var: Variable = todo!();
|
||||
/// let instr = mlil_fun.var_refs(&mlil_var).get(0).expr();
|
||||
/// ```
|
||||
pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
|
||||
let mut count = 0;
|
||||
let refs = unsafe {
|
||||
BNGetMediumLevelILVariableReferences(
|
||||
self.get_function().handle,
|
||||
&mut var.raw(),
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!refs.is_null());
|
||||
unsafe { Array::new(refs, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// Returns a list of variables referenced by code in the function ``func``,
|
||||
/// of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from
|
||||
/// all functions and containing the address will be returned. If no architecture is specified, the
|
||||
/// architecture of the function will be used.
|
||||
/// This function is related to get_hlil_var_refs_from(), which returns variable references collected
|
||||
/// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
|
||||
/// into a single variable in HLIL.
|
||||
///
|
||||
/// * `addr` - virtual address to query for variable references
|
||||
/// * `length` - optional length of query
|
||||
/// * `arch` - optional architecture of query
|
||||
pub fn var_refs_from(
|
||||
&self,
|
||||
addr: u64,
|
||||
length: Option<u64>,
|
||||
arch: Option<CoreArchitecture>,
|
||||
) -> Array<VariableReferenceSource> {
|
||||
let function = self.get_function();
|
||||
let arch = arch.unwrap_or_else(|| function.arch());
|
||||
let mut count = 0;
|
||||
|
||||
let refs = if let Some(length) = length {
|
||||
unsafe {
|
||||
BNGetMediumLevelILVariableReferencesInRange(
|
||||
function.handle,
|
||||
arch.0,
|
||||
addr,
|
||||
length,
|
||||
&mut count,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
BNGetMediumLevelILVariableReferencesFrom(function.handle, arch.0, addr, &mut count)
|
||||
}
|
||||
};
|
||||
assert!(!refs.is_null());
|
||||
unsafe { Array::new(refs, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// Current IL Address
|
||||
pub fn current_address(&self) -> u64 {
|
||||
unsafe { BNMediumLevelILGetCurrentAddress(self.handle) }
|
||||
}
|
||||
|
||||
/// Set the current IL Address
|
||||
pub fn set_current_address(&self, value: u64, arch: Option<CoreArchitecture>) {
|
||||
let arch = arch
|
||||
.map(|x| x.0)
|
||||
.unwrap_or_else(|| self.get_function().arch().0);
|
||||
unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, value) }
|
||||
}
|
||||
|
||||
/// Returns the BasicBlock at the given MLIL `instruction`.
|
||||
pub fn basic_block_containing(
|
||||
&self,
|
||||
instruction: &MediumLevelILInstruction,
|
||||
) -> Option<BasicBlock<MediumLevelILBlock>> {
|
||||
let index = instruction.index;
|
||||
let block = unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index) };
|
||||
(!block.is_null()).then(|| unsafe {
|
||||
BasicBlock::from_raw(
|
||||
block,
|
||||
MediumLevelILBlock {
|
||||
function: self.to_owned(),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
/// ends the function and computes the list of basic blocks.
|
||||
pub fn finalize(&self) {
|
||||
unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
|
||||
}
|
||||
|
||||
/// Generate SSA form given the current MLIL
|
||||
///
|
||||
/// * `analyze_conditionals` - whether or not to analyze conditionals
|
||||
/// * `handle_aliases` - whether or not to handle aliases
|
||||
/// * `known_not_aliases` - optional list of variables known to be not aliased
|
||||
/// * `known_aliases` - optional list of variables known to be aliased
|
||||
pub fn generate_ssa_form(
|
||||
&self,
|
||||
analyze_conditionals: bool,
|
||||
handle_aliases: bool,
|
||||
known_not_aliases: impl IntoIterator<Item = Variable>,
|
||||
known_aliases: impl IntoIterator<Item = Variable>,
|
||||
) {
|
||||
let mut known_not_aliases: Box<[_]> =
|
||||
known_not_aliases.into_iter().map(|x| x.raw()).collect();
|
||||
let mut known_aliases: Box<[_]> = known_aliases.into_iter().map(|x| x.raw()).collect();
|
||||
let (known_not_aliases_ptr, known_not_aliases_len) = if known_not_aliases.is_empty() {
|
||||
(core::ptr::null_mut(), 0)
|
||||
} else {
|
||||
(known_not_aliases.as_mut_ptr(), known_not_aliases.len())
|
||||
};
|
||||
let (known_aliases_ptr, known_aliases_len) = if known_not_aliases.is_empty() {
|
||||
(core::ptr::null_mut(), 0)
|
||||
} else {
|
||||
(known_aliases.as_mut_ptr(), known_aliases.len())
|
||||
};
|
||||
unsafe {
|
||||
BNGenerateMediumLevelILSSAForm(
|
||||
self.handle,
|
||||
analyze_conditionals,
|
||||
handle_aliases,
|
||||
known_not_aliases_ptr,
|
||||
known_not_aliases_len,
|
||||
known_aliases_ptr,
|
||||
known_aliases_len,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the instruction that contains the given SSA variable's definition.
|
||||
///
|
||||
/// Since SSA variables can only be defined once, this will return the single instruction where that occurs.
|
||||
/// For SSA variable version 0s, which don't have definitions, this will return None instead.
|
||||
pub fn ssa_variable_definition(&self, var: SSAVariable) -> Option<MediumLevelILInstruction> {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILSSAVarDefinition(self.handle, &var.variable.raw(), var.version)
|
||||
};
|
||||
(result < self.instruction_count())
|
||||
.then(|| MediumLevelILInstruction::new(self.to_owned(), result))
|
||||
}
|
||||
|
||||
pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
|
||||
let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
|
||||
(result < self.instruction_count())
|
||||
.then(|| MediumLevelILInstruction::new(self.to_owned(), result))
|
||||
}
|
||||
|
||||
///Gets all the instructions that use the given SSA variable.
|
||||
pub fn ssa_variable_uses(&self, ssa_var: SSAVariable) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe {
|
||||
BNGetMediumLevelILSSAVarUses(
|
||||
self.handle,
|
||||
&ssa_var.variable.raw(),
|
||||
ssa_var.version,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!uses.is_null());
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
|
||||
assert!(!uses.is_null());
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// determines if `ssa_var` is live at any point in the function
|
||||
pub fn is_ssa_variable_live(&self, ssa_var: SSAVariable) -> bool {
|
||||
unsafe {
|
||||
BNIsMediumLevelILSSAVarLive(self.handle, &ssa_var.variable.raw(), ssa_var.version)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn variable_definitions(&self, variable: Variable) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let defs = unsafe {
|
||||
BNGetMediumLevelILVariableDefinitions(self.handle, &variable.raw(), &mut count)
|
||||
};
|
||||
unsafe { Array::new(defs, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
pub fn variable_uses(&self, variable: Variable) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses =
|
||||
unsafe { BNGetMediumLevelILVariableUses(self.handle, &variable.raw(), &mut count) };
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// Computes the list of instructions for which `var` is live.
|
||||
/// If `include_last_use` is false, the last use of the variable will not be included in the
|
||||
/// list (this allows for easier computation of overlaps in liveness between two variables).
|
||||
/// If the variable is never used, this function will return an empty list.
|
||||
///
|
||||
/// `var` - the variable to query
|
||||
/// `include_last_use` - whether to include the last use of the variable in the list of instructions
|
||||
pub fn live_instruction_for_variable(
|
||||
&self,
|
||||
variable: Variable,
|
||||
include_last_user: bool,
|
||||
) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe {
|
||||
BNGetMediumLevelILLiveInstructionsForVariable(
|
||||
self.handle,
|
||||
&variable.raw(),
|
||||
include_last_user,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
pub fn ssa_variable_value(&self, ssa_var: SSAVariable) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILSSAVarValue(self.handle, &ssa_var.variable.raw(), ssa_var.version)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph {
|
||||
let settings = settings.map(|x| x.handle).unwrap_or(core::ptr::null_mut());
|
||||
let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
|
||||
unsafe { FlowGraph::from_raw(graph) }
|
||||
}
|
||||
|
||||
/// This gets just the MLIL variables - you may be interested in the union
|
||||
/// of [MediumLevelILFunction::aliased_variables] and
|
||||
/// [crate::function::Function::parameter_variables] for all the
|
||||
/// variables used in the function
|
||||
pub fn variables(&self) -> Array<Variable> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
|
||||
unsafe { Array::new(uses, count, ()) }
|
||||
}
|
||||
|
||||
/// This returns a list of Variables that are taken reference to and used
|
||||
/// elsewhere. You may also wish to consider [MediumLevelILFunction::variables]
|
||||
/// and [crate::function::Function::parameter_variables]
|
||||
pub fn aliased_variables(&self) -> Array<Variable> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
|
||||
unsafe { Array::new(uses, count, ()) }
|
||||
}
|
||||
|
||||
/// This gets just the MLIL SSA variables - you may be interested in the
|
||||
/// union of [MediumLevelILFunction::aliased_variables] and
|
||||
/// [crate::function::Function::parameter_variables] for all the
|
||||
/// variables used in the function.
|
||||
pub fn ssa_variables(&self) -> Array<Array<SSAVariable>> {
|
||||
let mut count = 0;
|
||||
let vars = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
|
||||
unsafe { Array::new(vars, count, self.to_owned()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for MediumLevelILFunction {
|
||||
@@ -118,3 +603,125 @@ impl core::fmt::Debug for MediumLevelILFunction {
|
||||
write!(f, "<mlil func handle {:p}>", self.handle)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MediumLevelILInstructionList<'a> {
|
||||
mlil: &'a MediumLevelILFunction,
|
||||
ptr: *mut usize,
|
||||
instr_idxs: core::slice::Iter<'a, usize>,
|
||||
}
|
||||
|
||||
impl Drop for MediumLevelILInstructionList<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeILInstructionList(self.ptr) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for MediumLevelILInstructionList<'_> {
|
||||
type Item = MediumLevelILInstruction;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.instr_idxs
|
||||
.next()
|
||||
.map(|i| self.mlil.instruction_from_instruction_idx(*i))
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for MediumLevelILInstructionList<'_> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.instr_idxs
|
||||
.next_back()
|
||||
.map(|i| self.mlil.instruction_from_instruction_idx(*i))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for MediumLevelILInstructionList<'_> {}
|
||||
impl core::iter::FusedIterator for MediumLevelILInstructionList<'_> {}
|
||||
|
||||
/////////////////////////
|
||||
// FunctionGraphType
|
||||
|
||||
pub type FunctionGraphType = binaryninjacore_sys::BNFunctionGraphType;
|
||||
|
||||
/////////////////////////
|
||||
// ILReferenceSource
|
||||
|
||||
pub struct ILReferenceSource {
|
||||
mlil: Ref<MediumLevelILFunction>,
|
||||
_func: Ref<Function>,
|
||||
_arch: CoreArchitecture,
|
||||
addr: u64,
|
||||
type_: FunctionGraphType,
|
||||
expr_id: usize,
|
||||
}
|
||||
|
||||
impl ILReferenceSource {
|
||||
unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref<MediumLevelILFunction>) -> Self {
|
||||
Self {
|
||||
mlil,
|
||||
_func: Function::from_raw(value.func),
|
||||
_arch: CoreArchitecture::from_raw(value.arch),
|
||||
addr: value.addr,
|
||||
type_: value.type_,
|
||||
expr_id: value.exprId,
|
||||
}
|
||||
}
|
||||
pub fn addr(&self) -> u64 {
|
||||
self.addr
|
||||
}
|
||||
pub fn graph_type(&self) -> FunctionGraphType {
|
||||
self.type_
|
||||
}
|
||||
pub fn expr(&self) -> MediumLevelILInstruction {
|
||||
self.mlil.instruction_from_idx(self.expr_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for ILReferenceSource {
|
||||
type Raw = BNILReferenceSource;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
unsafe impl CoreArrayProviderInner for ILReferenceSource {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeILReferences(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::from_raw(*raw, context.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// VariableReferenceSource
|
||||
|
||||
pub struct VariableReferenceSource {
|
||||
var: Variable,
|
||||
source: ILReferenceSource,
|
||||
}
|
||||
|
||||
impl VariableReferenceSource {
|
||||
pub fn variable(&self) -> &Variable {
|
||||
&self.var
|
||||
}
|
||||
pub fn source(&self) -> &ILReferenceSource {
|
||||
&self.source
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for VariableReferenceSource {
|
||||
type Raw = BNVariableReferenceSource;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for VariableReferenceSource {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeVariableReferenceSourceList(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self {
|
||||
var: Variable::from_raw(raw.var),
|
||||
source: ILReferenceSource::from_raw(raw.source, context.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use binaryninjacore_sys::BNFromVariableIdentifier;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
|
||||
use binaryninjacore_sys::BNMediumLevelILInstruction;
|
||||
use binaryninjacore_sys::BNMediumLevelILOperation;
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::disassembly::InstructionTextToken;
|
||||
use crate::operand_iter::OperandIter;
|
||||
use crate::rc::Ref;
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
|
||||
use crate::types::{
|
||||
ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable,
|
||||
Conf, ConstantData, DataFlowQueryOption, ILBranchDependence, PossibleValueSet,
|
||||
RegisterValue, RegisterValueType, SSAVariable, Type, Variable,
|
||||
};
|
||||
|
||||
use super::lift::*;
|
||||
@@ -17,6 +17,8 @@ use super::MediumLevelILFunction;
|
||||
pub struct MediumLevelILInstruction {
|
||||
pub function: Ref<MediumLevelILFunction>,
|
||||
pub address: u64,
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
pub kind: MediumLevelILInstructionKind,
|
||||
}
|
||||
|
||||
@@ -166,8 +168,8 @@ impl core::fmt::Debug for MediumLevelILInstruction {
|
||||
}
|
||||
|
||||
impl MediumLevelILInstruction {
|
||||
pub(crate) fn new(function: Ref<MediumLevelILFunction>, idx: usize) -> Self {
|
||||
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, idx) };
|
||||
pub(crate) fn new(function: Ref<MediumLevelILFunction>, index: usize) -> Self {
|
||||
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index) };
|
||||
use BNMediumLevelILOperation::*;
|
||||
use MediumLevelILInstructionKind as Op;
|
||||
let kind = match op.operation {
|
||||
@@ -703,7 +705,12 @@ impl MediumLevelILInstruction {
|
||||
}),
|
||||
// translated directly into a list for Expression or Variables
|
||||
// TODO MLIL_MEMORY_INTRINSIC_SSA needs to be handled properly
|
||||
MLIL_CALL_OUTPUT | MLIL_CALL_PARAM | MLIL_CALL_PARAM_SSA | MLIL_CALL_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_SSA => {
|
||||
MLIL_CALL_OUTPUT
|
||||
| MLIL_CALL_PARAM
|
||||
| MLIL_CALL_PARAM_SSA
|
||||
| MLIL_CALL_OUTPUT_SSA
|
||||
| MLIL_MEMORY_INTRINSIC_OUTPUT_SSA
|
||||
| MLIL_MEMORY_INTRINSIC_SSA => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
@@ -711,6 +718,8 @@ impl MediumLevelILInstruction {
|
||||
Self {
|
||||
function,
|
||||
address: op.address,
|
||||
index,
|
||||
size: op.size,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
@@ -897,7 +906,7 @@ impl MediumLevelILInstruction {
|
||||
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
|
||||
.vars()
|
||||
.collect(),
|
||||
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
|
||||
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
|
||||
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
|
||||
.exprs()
|
||||
.map(|expr| expr.lift())
|
||||
@@ -916,7 +925,7 @@ impl MediumLevelILInstruction {
|
||||
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
|
||||
.ssa_vars()
|
||||
.collect(),
|
||||
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
|
||||
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
|
||||
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
|
||||
.exprs()
|
||||
.map(|expr| expr.lift())
|
||||
@@ -1019,10 +1028,394 @@ impl MediumLevelILInstruction {
|
||||
MediumLevelILLiftedInstruction {
|
||||
function: self.function.clone(),
|
||||
address: self.address,
|
||||
index: self.index,
|
||||
size: self.size,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tokens(&self) -> Array<InstructionTextToken> {
|
||||
let mut count = 0;
|
||||
let mut tokens = core::ptr::null_mut();
|
||||
assert!(unsafe {
|
||||
BNGetMediumLevelILExprText(
|
||||
self.function.handle,
|
||||
self.function.get_function().arch().0,
|
||||
self.index,
|
||||
&mut tokens,
|
||||
&mut count,
|
||||
core::ptr::null_mut(),
|
||||
)
|
||||
});
|
||||
unsafe { Array::new(tokens, count, ()) }
|
||||
}
|
||||
|
||||
/// Value of expression if constant or a known value
|
||||
pub fn value(&self) -> RegisterValue {
|
||||
unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.index) }.into()
|
||||
}
|
||||
|
||||
/// Possible values of expression using path-sensitive static data flow analysis
|
||||
pub fn possible_values(&self, options: Option<&[DataFlowQueryOption]>) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleExprValues(
|
||||
self.function.handle,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_ssa_variable_values(
|
||||
&self,
|
||||
ssa_var: SSAVariable,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleSSAVarValues(
|
||||
self.function.handle,
|
||||
&ssa_var.variable.raw(),
|
||||
ssa_var.version,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
/// return the variable version used at this instruction
|
||||
pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable {
|
||||
let version = unsafe {
|
||||
BNGetMediumLevelILSSAVarVersionAtILInstruction(
|
||||
self.function.handle,
|
||||
&var.raw(),
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
SSAVariable::new(var, version)
|
||||
}
|
||||
|
||||
/// Set of branching instructions that must take the true or false path to reach this instruction
|
||||
pub fn branch_dependence(&self) -> Array<BranchDependence> {
|
||||
let mut count = 0;
|
||||
let deps = unsafe {
|
||||
BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index, &mut count)
|
||||
};
|
||||
assert!(!deps.is_null());
|
||||
unsafe { Array::new(deps, count, self.function.clone()) }
|
||||
}
|
||||
|
||||
pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence {
|
||||
let deps = unsafe {
|
||||
BNGetMediumLevelILBranchDependence(self.function.handle, self.index, instruction.index)
|
||||
};
|
||||
BranchDependence {
|
||||
instruction,
|
||||
dependence: deps,
|
||||
}
|
||||
}
|
||||
|
||||
/// Version of active memory contents in SSA form for this instruction
|
||||
pub fn ssa_memory_version(&self) -> usize {
|
||||
unsafe {
|
||||
BNGetMediumLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of expression
|
||||
pub fn expr_type(&self) -> Option<Conf<Ref<Type>>> {
|
||||
let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.index) };
|
||||
(!result.type_.is_null()).then(|| {
|
||||
Conf::new(
|
||||
unsafe { Type::ref_from_raw(result.type_) },
|
||||
result.confidence,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set type of expression
|
||||
///
|
||||
/// This API is only meant for workflows or for debugging purposes, since the changes they make are not persistent
|
||||
/// and get lost after a database save and reload. To make persistent changes to the analysis, one should use other
|
||||
/// APIs to, for example, change the type of variables. The analysis will then propagate the type of the variable
|
||||
/// and update the type of related expressions.
|
||||
pub fn set_expr_type<'a, T: Into<Conf<&'a Type>>>(&self, value: T) {
|
||||
let type_: Conf<&'a Type> = value.into();
|
||||
let mut type_raw: BNTypeWithConfidence = BNTypeWithConfidence {
|
||||
type_: type_.contents.handle,
|
||||
confidence: type_.confidence,
|
||||
};
|
||||
unsafe { BNSetMediumLevelILExprType(self.function.handle, self.index, &mut type_raw) }
|
||||
}
|
||||
|
||||
pub fn variable_for_register(&self, reg_id: u32) -> Variable {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILVariableForRegisterAtInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
unsafe { Variable::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn variable_for_flag(&self, flag_id: u32) -> Variable {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILVariableForFlagAtInstruction(
|
||||
self.function.handle,
|
||||
flag_id,
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
unsafe { Variable::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn variable_for_stack_location(&self, offset: i64) -> Variable {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILVariableForStackLocationAtInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
unsafe { Variable::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn register_value(&self, reg_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn register_value_after(&self, reg_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILRegisterValueAfterInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn possible_register_values(
|
||||
&self,
|
||||
reg_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleRegisterValuesAtInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_register_values_after(
|
||||
&self,
|
||||
reg_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleRegisterValuesAfterInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn flag_value(&self, flag_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn flag_value_after(&self, flag_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn possible_flag_values(
|
||||
&self,
|
||||
flag_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleFlagValuesAtInstruction(
|
||||
self.function.handle,
|
||||
flag_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_flag_values_after(
|
||||
&self,
|
||||
flag_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleFlagValuesAfterInstruction(
|
||||
self.function.handle,
|
||||
flag_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILStackContentsAtInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn stack_contents_after(&self, offset: i64, size: usize) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILStackContentsAfterInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn possible_stack_contents(
|
||||
&self,
|
||||
offset: i64,
|
||||
size: usize,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleStackContentsAtInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_stack_contents_after(
|
||||
&self,
|
||||
offset: i64,
|
||||
size: usize,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleStackContentsAfterInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
/// Gets the unique variable for a definition instruction. This unique variable can be passed
|
||||
/// to [crate::function::Function::split_variable] to split a variable at a definition. The given `var` is the
|
||||
/// assigned variable to query.
|
||||
///
|
||||
/// * `var` - variable to query
|
||||
pub fn split_var_for_definition(&self, var: Variable) -> Variable {
|
||||
let index = unsafe {
|
||||
BNGetDefaultIndexForMediumLevelILVariableDefinition(
|
||||
self.function.handle,
|
||||
&var.raw(),
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
Variable::new(var.t, index, var.storage)
|
||||
}
|
||||
|
||||
/// alias for [MediumLevelILInstruction::split_var_for_definition]
|
||||
#[inline]
|
||||
pub fn get_split_var_for_definition(&self, var: &Variable) -> Variable {
|
||||
self.split_var_for_definition(*var)
|
||||
}
|
||||
|
||||
fn lift_operand(&self, expr_idx: usize) -> Box<MediumLevelILLiftedInstruction> {
|
||||
Box::new(self.function.lifted_instruction_from_idx(expr_idx))
|
||||
}
|
||||
@@ -1096,6 +1489,22 @@ impl MediumLevelILInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for MediumLevelILInstruction {
|
||||
type Raw = usize;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for MediumLevelILInstruction {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeILInstructionList(raw)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
context.instruction_from_idx(*raw)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_float(value: u64, size: usize) -> f64 {
|
||||
match size {
|
||||
4 => f32::from_bits(value as u32) as f64,
|
||||
@@ -1110,7 +1519,7 @@ fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLe
|
||||
}
|
||||
|
||||
fn get_var(id: u64) -> Variable {
|
||||
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
|
||||
unsafe { Variable::from_identifier(id) }
|
||||
}
|
||||
|
||||
fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
|
||||
@@ -1149,3 +1558,28 @@ fn get_call_params_ssa(
|
||||
assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA);
|
||||
OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs()
|
||||
}
|
||||
|
||||
/// Conditional branching instruction and an expected conditional result
|
||||
pub struct BranchDependence {
|
||||
pub instruction: MediumLevelILInstruction,
|
||||
pub dependence: ILBranchDependence,
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for BranchDependence {
|
||||
type Raw = BNILBranchInstructionAndDependence;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for BranchDependence {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
unsafe { BNFreeILBranchDependenceList(raw) };
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self {
|
||||
instruction: MediumLevelILInstruction::new(context.clone(), raw.branch),
|
||||
dependence: raw.dependence,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
143
src/mlil/lift.rs
143
src/mlil/lift.rs
@@ -1,7 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::rc::Ref;
|
||||
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
|
||||
use crate::types::{ConstantData, SSAVariable, Variable};
|
||||
|
||||
use super::operation::*;
|
||||
use super::MediumLevelILFunction;
|
||||
@@ -9,7 +10,7 @@ use super::MediumLevelILFunction;
|
||||
#[derive(Clone)]
|
||||
pub enum MediumLevelILLiftedOperand {
|
||||
ConstantData(ConstantData),
|
||||
Intrinsic(ILIntrinsic),
|
||||
Intrinsic(CoreIntrinsic),
|
||||
Expr(MediumLevelILLiftedInstruction),
|
||||
ExprList(Vec<MediumLevelILLiftedInstruction>),
|
||||
Float(f64),
|
||||
@@ -26,6 +27,8 @@ pub enum MediumLevelILLiftedOperand {
|
||||
pub struct MediumLevelILLiftedInstruction {
|
||||
pub function: Ref<MediumLevelILFunction>,
|
||||
pub address: u64,
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
pub kind: MediumLevelILLiftedInstructionKind,
|
||||
}
|
||||
|
||||
@@ -164,6 +167,142 @@ pub enum MediumLevelILLiftedInstructionKind {
|
||||
}
|
||||
|
||||
impl MediumLevelILLiftedInstruction {
|
||||
pub fn name(&self) -> &'static str {
|
||||
use MediumLevelILLiftedInstructionKind::*;
|
||||
match self.kind {
|
||||
Nop => "Nop",
|
||||
Noret => "Noret",
|
||||
Bp => "Bp",
|
||||
Undef => "Undef",
|
||||
Unimpl => "Unimpl",
|
||||
If(_) => "If",
|
||||
FloatConst(_) => "FloatConst",
|
||||
Const(_) => "Const",
|
||||
ConstPtr(_) => "ConstPtr",
|
||||
Import(_) => "Import",
|
||||
ExternPtr(_) => "ExternPtr",
|
||||
ConstData(_) => "ConstData",
|
||||
Jump(_) => "Jump",
|
||||
RetHint(_) => "RetHint",
|
||||
StoreSsa(_) => "StoreSsa",
|
||||
StoreStructSsa(_) => "StoreStructSsa",
|
||||
StoreStruct(_) => "StoreStruct",
|
||||
Store(_) => "Store",
|
||||
JumpTo(_) => "JumpTo",
|
||||
Goto(_) => "Goto",
|
||||
FreeVarSlot(_) => "FreeVarSlot",
|
||||
SetVarField(_) => "SetVarField",
|
||||
SetVar(_) => "SetVar",
|
||||
FreeVarSlotSsa(_) => "FreeVarSlotSsa",
|
||||
SetVarSsaField(_) => "SetVarSsaField",
|
||||
SetVarAliasedField(_) => "SetVarAliasedField",
|
||||
SetVarAliased(_) => "SetVarAliased",
|
||||
SetVarSsa(_) => "SetVarSsa",
|
||||
VarPhi(_) => "VarPhi",
|
||||
MemPhi(_) => "MemPhi",
|
||||
VarSplit(_) => "VarSplit",
|
||||
SetVarSplit(_) => "SetVarSplit",
|
||||
VarSplitSsa(_) => "VarSplitSsa",
|
||||
SetVarSplitSsa(_) => "SetVarSplitSsa",
|
||||
Add(_) => "Add",
|
||||
Sub(_) => "Sub",
|
||||
And(_) => "And",
|
||||
Or(_) => "Or",
|
||||
Xor(_) => "Xor",
|
||||
Lsl(_) => "Lsl",
|
||||
Lsr(_) => "Lsr",
|
||||
Asr(_) => "Asr",
|
||||
Rol(_) => "Rol",
|
||||
Ror(_) => "Ror",
|
||||
Mul(_) => "Mul",
|
||||
MuluDp(_) => "MuluDp",
|
||||
MulsDp(_) => "MulsDp",
|
||||
Divu(_) => "Divu",
|
||||
DivuDp(_) => "DivuDp",
|
||||
Divs(_) => "Divs",
|
||||
DivsDp(_) => "DivsDp",
|
||||
Modu(_) => "Modu",
|
||||
ModuDp(_) => "ModuDp",
|
||||
Mods(_) => "Mods",
|
||||
ModsDp(_) => "ModsDp",
|
||||
CmpE(_) => "CmpE",
|
||||
CmpNe(_) => "CmpNe",
|
||||
CmpSlt(_) => "CmpSlt",
|
||||
CmpUlt(_) => "CmpUlt",
|
||||
CmpSle(_) => "CmpSle",
|
||||
CmpUle(_) => "CmpUle",
|
||||
CmpSge(_) => "CmpSge",
|
||||
CmpUge(_) => "CmpUge",
|
||||
CmpSgt(_) => "CmpSgt",
|
||||
CmpUgt(_) => "CmpUgt",
|
||||
TestBit(_) => "TestBit",
|
||||
AddOverflow(_) => "AddOverflow",
|
||||
FcmpE(_) => "FcmpE",
|
||||
FcmpNe(_) => "FcmpNe",
|
||||
FcmpLt(_) => "FcmpLt",
|
||||
FcmpLe(_) => "FcmpLe",
|
||||
FcmpGe(_) => "FcmpGe",
|
||||
FcmpGt(_) => "FcmpGt",
|
||||
FcmpO(_) => "FcmpO",
|
||||
FcmpUo(_) => "FcmpUo",
|
||||
Fadd(_) => "Fadd",
|
||||
Fsub(_) => "Fsub",
|
||||
Fmul(_) => "Fmul",
|
||||
Fdiv(_) => "Fdiv",
|
||||
Adc(_) => "Adc",
|
||||
Sbb(_) => "Sbb",
|
||||
Rlc(_) => "Rlc",
|
||||
Rrc(_) => "Rrc",
|
||||
Call(_) => "Call",
|
||||
Tailcall(_) => "Tailcall",
|
||||
Syscall(_) => "Syscall",
|
||||
Intrinsic(_) => "Intrinsic",
|
||||
IntrinsicSsa(_) => "IntrinsicSsa",
|
||||
CallSsa(_) => "CallSsa",
|
||||
TailcallSsa(_) => "TailcallSsa",
|
||||
CallUntypedSsa(_) => "CallUntypedSsa",
|
||||
TailcallUntypedSsa(_) => "TailcallUntypedSsa",
|
||||
SyscallSsa(_) => "SyscallSsa",
|
||||
SyscallUntypedSsa(_) => "SyscallUntypedSsa",
|
||||
CallUntyped(_) => "CallUntyped",
|
||||
TailcallUntyped(_) => "TailcallUntyped",
|
||||
SyscallUntyped(_) => "SyscallUntyped",
|
||||
SeparateParamList(_) => "SeparateParamList",
|
||||
SharedParamSlot(_) => "SharedParamSlot",
|
||||
Neg(_) => "Neg",
|
||||
Not(_) => "Not",
|
||||
Sx(_) => "Sx",
|
||||
Zx(_) => "Zx",
|
||||
LowPart(_) => "LowPart",
|
||||
BoolToInt(_) => "BoolToInt",
|
||||
UnimplMem(_) => "UnimplMem",
|
||||
Fsqrt(_) => "Fsqrt",
|
||||
Fneg(_) => "Fneg",
|
||||
Fabs(_) => "Fabs",
|
||||
FloatToInt(_) => "FloatToInt",
|
||||
IntToFloat(_) => "IntToFloat",
|
||||
FloatConv(_) => "FloatConv",
|
||||
RoundToInt(_) => "RoundToInt",
|
||||
Floor(_) => "Floor",
|
||||
Ceil(_) => "Ceil",
|
||||
Ftrunc(_) => "Ftrunc",
|
||||
Load(_) => "Load",
|
||||
LoadStruct(_) => "LoadStruct",
|
||||
LoadStructSsa(_) => "LoadStructSsa",
|
||||
LoadSsa(_) => "LoadSsa",
|
||||
Ret(_) => "Ret",
|
||||
Var(_) => "Var",
|
||||
AddressOf(_) => "AddressOf",
|
||||
VarField(_) => "VarField",
|
||||
AddressOfField(_) => "AddressOfField",
|
||||
VarSsa(_) => "VarSsa",
|
||||
VarAliased(_) => "VarAliased",
|
||||
VarSsaField(_) => "VarSsaField",
|
||||
VarAliasedField(_) => "VarAliasedField",
|
||||
Trap(_) => "Trap",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operands(&self) -> Vec<(&'static str, MediumLevelILLiftedOperand)> {
|
||||
use MediumLevelILLiftedInstructionKind::*;
|
||||
use MediumLevelILLiftedOperand as Operand;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
|
||||
use crate::{architecture::CoreIntrinsic, types::{ConstantData, SSAVariable, Variable}};
|
||||
|
||||
use super::MediumLevelILLiftedInstruction;
|
||||
|
||||
@@ -355,7 +355,7 @@ pub struct Intrinsic {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LiftedIntrinsic {
|
||||
pub output: Vec<Variable>,
|
||||
pub intrinsic: ILIntrinsic,
|
||||
pub intrinsic: CoreIntrinsic,
|
||||
pub params: Vec<MediumLevelILLiftedInstruction>,
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ pub struct IntrinsicSsa {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LiftedIntrinsicSsa {
|
||||
pub output: Vec<SSAVariable>,
|
||||
pub intrinsic: ILIntrinsic,
|
||||
pub intrinsic: CoreIntrinsic,
|
||||
pub params: Vec<MediumLevelILLiftedInstruction>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user