update rust crate

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

View File

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

View File

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

View File

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

View File

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