diff options
| author | Mark Rowe <mark@vector35.com> | 2026-03-23 19:34:55 -0700 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2026-03-30 09:19:20 -0700 |
| commit | efbde23abcf1f38d98a096044e2256c5619f9004 (patch) | |
| tree | 2c5d910ef3c2215431e3e112779ab2f203de7d84 /plugins/workflow_objc/src/activities/super_init.rs | |
| parent | dca922496f96d18f9060b1f7a349ecb5c1c1a9a1 (diff) | |
[ObjC] Propagate types from objc_alloc_init and friends
Diffstat (limited to 'plugins/workflow_objc/src/activities/super_init.rs')
| -rw-r--r-- | plugins/workflow_objc/src/activities/super_init.rs | 157 |
1 files changed, 15 insertions, 142 deletions
diff --git a/plugins/workflow_objc/src/activities/super_init.rs b/plugins/workflow_objc/src/activities/super_init.rs index 68c6d3ee..81b9a453 100644 --- a/plugins/workflow_objc/src/activities/super_init.rs +++ b/plugins/workflow_objc/src/activities/super_init.rs @@ -1,21 +1,16 @@ use binaryninja::{ - binary_view::{BinaryView, BinaryViewBase, BinaryViewExt as _}, - confidence::Conf, - function::Function, + binary_view::{BinaryView, BinaryViewBase as _, BinaryViewExt as _}, medium_level_il::{ - operation::{ - Constant, LiftedCallSsa, LiftedLoadSsa, LiftedSetVarSsa, LiftedSetVarSsaField, Var, - VarSsa, - }, - MediumLevelILFunction, MediumLevelILLiftedInstruction, MediumLevelILLiftedInstructionKind, + operation::{Constant, LiftedSetVarSsa, LiftedSetVarSsaField, Var, VarSsa}, + MediumLevelILLiftedInstruction, MediumLevelILLiftedInstructionKind, }, rc::Ref, types::Type, - variable::{RegisterValueType, SSAVariable}, workflow::AnalysisContext, }; -use bstr::{BStr, ByteSlice}; +use bstr::ByteSlice; +use super::util; use crate::{ error::ILLevel, metadata::{GlobalState, Selector}, @@ -32,110 +27,16 @@ const OBJC_MSG_SEND_SUPER_FUNCTIONS: &[&[u8]] = &[ b"j__objc_msgSendSuper", ]; -fn ssa_variable_value_or_load_of_constant_pointer( - function: &MediumLevelILFunction, - var: &SSAVariable, -) -> Option<u64> { - let value = function.ssa_variable_value(var); - match value.state { - RegisterValueType::ConstantPointerValue => return Some(value.value as u64), - RegisterValueType::UndeterminedValue => {} - _ => return None, - } - - let def = function.ssa_variable_definition(var)?; - let MediumLevelILLiftedInstructionKind::SetVarSsa(set_var) = def.lift().kind else { - return None; - }; - - let MediumLevelILLiftedInstructionKind::LoadSsa(LiftedLoadSsa { src, .. }) = set_var.src.kind - else { - return None; - }; - - match src.kind { - MediumLevelILLiftedInstructionKind::ConstPtr(Constant { constant }) => Some(constant), - _ => None, - } -} - -/// If `instr` is a constant pointer or is a variable whose value is loaded from a constant pointer, -/// return that pointer address. -fn match_constant_pointer_or_load_of_constant_pointer( - instr: &MediumLevelILLiftedInstruction, -) -> Option<u64> { - match instr.kind { - MediumLevelILLiftedInstructionKind::ConstPtr(Constant { constant }) => Some(constant), - MediumLevelILLiftedInstructionKind::VarSsa(var) => { - ssa_variable_value_or_load_of_constant_pointer(&instr.function, &var.src) - } - _ => None, - } -} - -#[allow(clippy::struct_field_names)] -struct Call<'a> { - pub instr: &'a MediumLevelILLiftedInstruction, - pub call: &'a LiftedCallSsa, - pub target: Ref<Function>, -} - -/// Returns a `Call` if `instr` is a call or tail call to a function whose name appears in `function_names` -fn match_call_to_function_named<'a>( - instr: &'a MediumLevelILLiftedInstruction, - view: &'a BinaryView, - function_names: &'a [&[u8]], -) -> Option<Call<'a>> { - let (MediumLevelILLiftedInstructionKind::TailcallSsa(ref call) - | MediumLevelILLiftedInstructionKind::CallSsa(ref call)) = instr.kind - else { - return None; - }; - - let MediumLevelILLiftedInstructionKind::ConstPtr(Constant { - constant: call_target, - }) = call.dest.kind - else { - return None; - }; - - let target_function = view.function_at(&instr.function.function().platform(), call_target)?; - let function_name = target_function.symbol().full_name(); - if !function_names.contains(&function_name.to_bytes()) { - return None; - } - - Some(Call { - instr, - call, - target: target_function, - }) -} - -fn class_name_from_symbol_name(symbol_name: &BStr) -> Option<&BStr> { - // The symbol name for the `objc_class_t` can have different names depending - // on factors such as being local or external, and whether the reference - // is from the shared cache or a standalone Mach-O file. - Some(if symbol_name.starts_with(b"cls_") { - &symbol_name[4..] - } else if symbol_name.starts_with(b"clsRef_") { - &symbol_name[7..] - } else if symbol_name.starts_with(b"_OBJC_CLASS_$_") { - &symbol_name[14..] - } else { - return None; - }) -} - /// Detect the return type for a call to `objc_msgSendSuper2` where the selector is in the `init` family. /// Returns `None` if selector is not in the `init` family or the return type cannot be determined. -fn return_type_for_super_init(call: &Call, view: &BinaryView) -> Option<Ref<Type>> { +fn return_type_for_super_init(call: &util::Call, view: &BinaryView) -> Option<Ref<Type>> { // Expecting to see at least `objc_super` and a selector. if call.call.params.len() < 2 { return None; } - let selector_addr = match_constant_pointer_or_load_of_constant_pointer(&call.call.params[1])?; + let selector_addr = + util::match_constant_pointer_or_load_of_constant_pointer(&call.call.params[1])?; let selector = Selector::from_address(view, selector_addr).ok()?; // TODO: This will match `initialize` and `initiate` which are not init methods. @@ -238,7 +139,7 @@ fn return_type_for_super_init(call: &Call, view: &BinaryView) -> Option<Ref<Type let super_class_symbol_name = super_class_symbol.full_name(); let Some(class_name) = - class_name_from_symbol_name(super_class_symbol_name.to_bytes().as_bstr()) + util::class_name_from_symbol_name(super_class_symbol_name.to_bytes().as_bstr()) else { tracing::debug!( "Unable to extract class name from symbol name: {super_class_symbol_name:?}" @@ -254,42 +155,14 @@ fn return_type_for_super_init(call: &Call, view: &BinaryView) -> Option<Ref<Type Some(Type::pointer(&call.target.arch(), &class_type)) } -/// Adjust the return type of the call represented by `call`. -fn adjust_return_type_of_call(call: &Call<'_>, return_type: &Type) { - let function = call.instr.function.function(); - - // We're changing only the return type, so preserve other aspects of any existing call type adjustment. - let target_function_type = if let Some(existing_call_type_adjustment) = - function.call_type_adjustment(call.instr.address, None) - { - existing_call_type_adjustment.contents - } else { - call.target.function_type() - }; - - // There's nothing to do if the return type is already correct - if let Some(conf) = target_function_type.return_value() { - if &*conf.contents == return_type { - return; - } - } - - let adjusted_call_type = target_function_type - .to_builder() - .set_child_type(return_type) - .finalize(); - - function.set_auto_call_type_adjustment( - call.instr.address, - Conf::new(&*adjusted_call_type, Confidence::SuperInit as u8), - None, - ); -} - fn process_instruction(instr: &MediumLevelILLiftedInstruction, view: &BinaryView) -> Option<()> { - let call = match_call_to_function_named(instr, view, OBJC_MSG_SEND_SUPER_FUNCTIONS)?; + let call = util::match_call_to_function_named(instr, view, OBJC_MSG_SEND_SUPER_FUNCTIONS)?; - adjust_return_type_of_call(&call, return_type_for_super_init(&call, view)?.as_ref()); + util::adjust_return_type_of_call( + &call, + return_type_for_super_init(&call, view)?.as_ref(), + Confidence::SuperInit as u8, + ); Some(()) } |
