11use binaryninja:: {
2- binary_view:: { BinaryView , BinaryViewBase , BinaryViewExt as _} ,
3- confidence:: Conf ,
4- function:: Function ,
2+ binary_view:: { BinaryView , BinaryViewBase as _, BinaryViewExt as _} ,
53 medium_level_il:: {
6- operation:: {
7- Constant , LiftedCallSsa , LiftedLoadSsa , LiftedSetVarSsa , LiftedSetVarSsaField , Var ,
8- VarSsa ,
9- } ,
10- MediumLevelILFunction , MediumLevelILLiftedInstruction , MediumLevelILLiftedInstructionKind ,
4+ operation:: { Constant , LiftedSetVarSsa , LiftedSetVarSsaField , Var , VarSsa } ,
5+ MediumLevelILLiftedInstruction , MediumLevelILLiftedInstructionKind ,
116 } ,
127 rc:: Ref ,
138 types:: Type ,
14- variable:: { RegisterValueType , SSAVariable } ,
159 workflow:: AnalysisContext ,
1610} ;
17- use bstr:: { BStr , ByteSlice } ;
11+ use bstr:: ByteSlice ;
1812
13+ use super :: util;
1914use crate :: {
2015 error:: ILLevel ,
2116 metadata:: { GlobalState , Selector } ,
@@ -32,110 +27,16 @@ const OBJC_MSG_SEND_SUPER_FUNCTIONS: &[&[u8]] = &[
3227 b"j__objc_msgSendSuper" ,
3328] ;
3429
35- fn ssa_variable_value_or_load_of_constant_pointer (
36- function : & MediumLevelILFunction ,
37- var : & SSAVariable ,
38- ) -> Option < u64 > {
39- let value = function. ssa_variable_value ( var) ;
40- match value. state {
41- RegisterValueType :: ConstantPointerValue => return Some ( value. value as u64 ) ,
42- RegisterValueType :: UndeterminedValue => { }
43- _ => return None ,
44- }
45-
46- let def = function. ssa_variable_definition ( var) ?;
47- let MediumLevelILLiftedInstructionKind :: SetVarSsa ( set_var) = def. lift ( ) . kind else {
48- return None ;
49- } ;
50-
51- let MediumLevelILLiftedInstructionKind :: LoadSsa ( LiftedLoadSsa { src, .. } ) = set_var. src . kind
52- else {
53- return None ;
54- } ;
55-
56- match src. kind {
57- MediumLevelILLiftedInstructionKind :: ConstPtr ( Constant { constant } ) => Some ( constant) ,
58- _ => None ,
59- }
60- }
61-
62- /// If `instr` is a constant pointer or is a variable whose value is loaded from a constant pointer,
63- /// return that pointer address.
64- fn match_constant_pointer_or_load_of_constant_pointer (
65- instr : & MediumLevelILLiftedInstruction ,
66- ) -> Option < u64 > {
67- match instr. kind {
68- MediumLevelILLiftedInstructionKind :: ConstPtr ( Constant { constant } ) => Some ( constant) ,
69- MediumLevelILLiftedInstructionKind :: VarSsa ( var) => {
70- ssa_variable_value_or_load_of_constant_pointer ( & instr. function , & var. src )
71- }
72- _ => None ,
73- }
74- }
75-
76- #[ allow( clippy:: struct_field_names) ]
77- struct Call < ' a > {
78- pub instr : & ' a MediumLevelILLiftedInstruction ,
79- pub call : & ' a LiftedCallSsa ,
80- pub target : Ref < Function > ,
81- }
82-
83- /// Returns a `Call` if `instr` is a call or tail call to a function whose name appears in `function_names`
84- fn match_call_to_function_named < ' a > (
85- instr : & ' a MediumLevelILLiftedInstruction ,
86- view : & ' a BinaryView ,
87- function_names : & ' a [ & [ u8 ] ] ,
88- ) -> Option < Call < ' a > > {
89- let ( MediumLevelILLiftedInstructionKind :: TailcallSsa ( ref call)
90- | MediumLevelILLiftedInstructionKind :: CallSsa ( ref call) ) = instr. kind
91- else {
92- return None ;
93- } ;
94-
95- let MediumLevelILLiftedInstructionKind :: ConstPtr ( Constant {
96- constant : call_target,
97- } ) = call. dest . kind
98- else {
99- return None ;
100- } ;
101-
102- let target_function = view. function_at ( & instr. function . function ( ) . platform ( ) , call_target) ?;
103- let function_name = target_function. symbol ( ) . full_name ( ) ;
104- if !function_names. contains ( & function_name. to_bytes ( ) ) {
105- return None ;
106- }
107-
108- Some ( Call {
109- instr,
110- call,
111- target : target_function,
112- } )
113- }
114-
115- fn class_name_from_symbol_name ( symbol_name : & BStr ) -> Option < & BStr > {
116- // The symbol name for the `objc_class_t` can have different names depending
117- // on factors such as being local or external, and whether the reference
118- // is from the shared cache or a standalone Mach-O file.
119- Some ( if symbol_name. starts_with ( b"cls_" ) {
120- & symbol_name[ 4 ..]
121- } else if symbol_name. starts_with ( b"clsRef_" ) {
122- & symbol_name[ 7 ..]
123- } else if symbol_name. starts_with ( b"_OBJC_CLASS_$_" ) {
124- & symbol_name[ 14 ..]
125- } else {
126- return None ;
127- } )
128- }
129-
13030/// Detect the return type for a call to `objc_msgSendSuper2` where the selector is in the `init` family.
13131/// Returns `None` if selector is not in the `init` family or the return type cannot be determined.
132- fn return_type_for_super_init ( call : & Call , view : & BinaryView ) -> Option < Ref < Type > > {
32+ fn return_type_for_super_init ( call : & util :: Call , view : & BinaryView ) -> Option < Ref < Type > > {
13333 // Expecting to see at least `objc_super` and a selector.
13434 if call. call . params . len ( ) < 2 {
13535 return None ;
13636 }
13737
138- let selector_addr = match_constant_pointer_or_load_of_constant_pointer ( & call. call . params [ 1 ] ) ?;
38+ let selector_addr =
39+ util:: match_constant_pointer_or_load_of_constant_pointer ( & call. call . params [ 1 ] ) ?;
13940 let selector = Selector :: from_address ( view, selector_addr) . ok ( ) ?;
14041
14142 // 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
238139
239140 let super_class_symbol_name = super_class_symbol. full_name ( ) ;
240141 let Some ( class_name) =
241- class_name_from_symbol_name ( super_class_symbol_name. to_bytes ( ) . as_bstr ( ) )
142+ util :: class_name_from_symbol_name ( super_class_symbol_name. to_bytes ( ) . as_bstr ( ) )
242143 else {
243144 tracing:: debug!(
244145 "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
254155 Some ( Type :: pointer ( & call. target . arch ( ) , & class_type) )
255156}
256157
257- /// Adjust the return type of the call represented by `call`.
258- fn adjust_return_type_of_call ( call : & Call < ' _ > , return_type : & Type ) {
259- let function = call. instr . function . function ( ) ;
260-
261- // We're changing only the return type, so preserve other aspects of any existing call type adjustment.
262- let target_function_type = if let Some ( existing_call_type_adjustment) =
263- function. call_type_adjustment ( call. instr . address , None )
264- {
265- existing_call_type_adjustment. contents
266- } else {
267- call. target . function_type ( )
268- } ;
269-
270- // There's nothing to do if the return type is already correct
271- if let Some ( conf) = target_function_type. return_value ( ) {
272- if & * conf. contents == return_type {
273- return ;
274- }
275- }
276-
277- let adjusted_call_type = target_function_type
278- . to_builder ( )
279- . set_child_type ( return_type)
280- . finalize ( ) ;
281-
282- function. set_auto_call_type_adjustment (
283- call. instr . address ,
284- Conf :: new ( & * adjusted_call_type, Confidence :: SuperInit as u8 ) ,
285- None ,
286- ) ;
287- }
288-
289158fn process_instruction ( instr : & MediumLevelILLiftedInstruction , view : & BinaryView ) -> Option < ( ) > {
290- let call = match_call_to_function_named ( instr, view, OBJC_MSG_SEND_SUPER_FUNCTIONS ) ?;
159+ let call = util :: match_call_to_function_named ( instr, view, OBJC_MSG_SEND_SUPER_FUNCTIONS ) ?;
291160
292- adjust_return_type_of_call ( & call, return_type_for_super_init ( & call, view) ?. as_ref ( ) ) ;
161+ util:: adjust_return_type_of_call (
162+ & call,
163+ return_type_for_super_init ( & call, view) ?. as_ref ( ) ,
164+ Confidence :: SuperInit as u8 ,
165+ ) ;
293166 Some ( ( ) )
294167}
295168
0 commit comments