11use binaryninja:: {
2+ architecture:: CoreRegister ,
23 binary_view:: { BinaryView , BinaryViewBase as _, BinaryViewExt } ,
34 confidence:: Conf ,
45 function:: Function ,
56 low_level_il:: {
6- function:: { Mutable , SSA } ,
7- instruction:: LowLevelILInstruction ,
7+ expression:: {
8+ ExpressionHandler as _, LowLevelILExpression , LowLevelILExpressionKind , ValueExpr ,
9+ } ,
10+ function:: { LowLevelILFunction , Mutable , SSA } ,
11+ instruction:: { InstructionHandler as _, LowLevelILInstruction , LowLevelILInstructionKind } ,
12+ operation:: { CallSsa , Operation } ,
13+ LowLevelILSSARegisterKind ,
814 } ,
915 rc:: Ref ,
1016 types:: { FunctionParameter , Type } ,
17+ variable:: PossibleValueSet ,
1118} ;
19+ use bstr:: ByteSlice as _;
1220
1321use super :: MessageSendType ;
14- use crate :: { metadata:: Selector , workflow:: Confidence , Error } ;
22+ use crate :: { activities :: util , metadata:: Selector , workflow:: Confidence , Error } ;
1523
1624fn named_type ( bv : & BinaryView , name : & str ) -> Option < Ref < Type > > {
1725 bv. type_by_name ( name)
1826 . map ( |t| Type :: named_type_from_type ( name, & t) )
1927}
2028
29+ // j_ prefixes are for stub functions in the dyld shared cache.
30+ const ALLOC_FUNCTIONS : & [ & str ] = & [
31+ "_objc_alloc_init" ,
32+ "_objc_alloc_initWithZone" ,
33+ "_objc_alloc" ,
34+ "_objc_allocWithZone" ,
35+ "_objc_opt_new" ,
36+ "j__objc_alloc_init" ,
37+ "j__objc_alloc_initWithZone" ,
38+ "j__objc_alloc" ,
39+ "j__objc_allocWithZone" ,
40+ "j__objc_opt_new" ,
41+ ] ;
42+
43+ /// Extract parameter expressions from a call, handling the SeparateParamListSsa wrapper.
44+ fn call_param_exprs < ' a > (
45+ call_op : & ' a Operation < ' a , Mutable , SSA , CallSsa > ,
46+ ) -> Option < Vec < LowLevelILExpression < ' a , Mutable , SSA , ValueExpr > > > {
47+ let LowLevelILExpressionKind :: CallParamSsa ( params) = & call_op. param_expr ( ) . kind ( ) else {
48+ return None ;
49+ } ;
50+
51+ let param_exprs = params. param_exprs ( ) ;
52+ Some (
53+ if let Some ( LowLevelILExpressionKind :: SeparateParamListSsa ( inner) ) =
54+ param_exprs. first ( ) . map ( |e| e. kind ( ) )
55+ {
56+ inner. param_exprs ( )
57+ } else {
58+ param_exprs
59+ } ,
60+ )
61+ }
62+
63+ /// Follow an SSA register back through register-to-register copies to find the
64+ /// instruction that originally defined its value.
65+ fn source_def_for_register < ' a > (
66+ ssa : & ' a LowLevelILFunction < Mutable , SSA > ,
67+ reg : LowLevelILSSARegisterKind < CoreRegister > ,
68+ ) -> Option < LowLevelILInstruction < ' a , Mutable , SSA > > {
69+ let mut def = ssa. get_ssa_register_definition ( reg) ?;
70+ while let LowLevelILInstructionKind :: SetRegSsa ( set_reg) = def. kind ( ) {
71+ let LowLevelILExpressionKind :: RegSsa ( src_reg) = set_reg. source_expr ( ) . kind ( ) else {
72+ break ;
73+ } ;
74+ def = ssa. get_ssa_register_definition ( src_reg. source_reg ( ) ) ?;
75+ }
76+ Some ( def)
77+ }
78+
79+ /// For init-family selectors on a normal message send, try to determine the return type
80+ /// by tracing the receiver register back to an alloc call and resolving the class.
81+ fn return_type_for_init_receiver (
82+ bv : & BinaryView ,
83+ func : & Function ,
84+ ssa : & LowLevelILFunction < Mutable , SSA > ,
85+ insn : & LowLevelILInstruction < Mutable , SSA > ,
86+ selector : & Selector ,
87+ message_send_type : MessageSendType ,
88+ ) -> Option < Ref < Type > > {
89+ if message_send_type != MessageSendType :: Normal || !selector. name . starts_with ( "init" ) {
90+ return None ;
91+ }
92+
93+ let call_op = match insn. kind ( ) {
94+ LowLevelILInstructionKind :: CallSsa ( op) | LowLevelILInstructionKind :: TailCallSsa ( op) => op,
95+ _ => return None ,
96+ } ;
97+
98+ let param_exprs = call_param_exprs ( & call_op) ?;
99+ let LowLevelILExpressionKind :: RegSsa ( receiver_reg) = param_exprs. first ( ) ?. kind ( ) else {
100+ return None ;
101+ } ;
102+
103+ let def = source_def_for_register ( ssa, receiver_reg. source_reg ( ) ) ?;
104+ let def_call_op = match def. kind ( ) {
105+ LowLevelILInstructionKind :: CallSsa ( op) | LowLevelILInstructionKind :: TailCallSsa ( op) => op,
106+ _ => return None ,
107+ } ;
108+
109+ // Check if the defining call is to an alloc function.
110+ let target_values = def_call_op. target ( ) . possible_values ( ) ;
111+ let call_target = match target_values {
112+ PossibleValueSet :: ConstantValue { value }
113+ | PossibleValueSet :: ConstantPointerValue { value }
114+ | PossibleValueSet :: ImportedAddressValue { value } => value as u64 ,
115+ _ => return None ,
116+ } ;
117+
118+ let target_name = bv
119+ . symbol_by_address ( call_target) ?
120+ . raw_name ( )
121+ . to_string_lossy ( )
122+ . into_owned ( ) ;
123+ if !ALLOC_FUNCTIONS . contains ( & target_name. as_str ( ) ) {
124+ return None ;
125+ }
126+
127+ // Get the class from the alloc call's first parameter.
128+ let alloc_params = call_param_exprs ( & def_call_op) ?;
129+ let LowLevelILExpressionKind :: RegSsa ( class_reg) = alloc_params. first ( ) ?. kind ( ) else {
130+ return None ;
131+ } ;
132+
133+ let class_addr = ssa. get_ssa_register_value ( class_reg. source_reg ( ) ) ?. value as u64 ;
134+ if class_addr == 0 {
135+ return None ;
136+ }
137+
138+ let class_symbol_name = bv. symbol_by_address ( class_addr) ?. full_name ( ) ;
139+ let class_name = util:: class_name_from_symbol_name ( class_symbol_name. to_bytes ( ) . as_bstr ( ) ) ?;
140+ let class_type = bv. type_by_name ( class_name. to_str_lossy ( ) ) ?;
141+ Some ( Type :: pointer ( & func. arch ( ) , & class_type) )
142+ }
143+
21144pub fn process_call (
22145 bv : & BinaryView ,
23146 func : & Function ,
147+ ssa : & LowLevelILFunction < Mutable , SSA > ,
24148 insn : & LowLevelILInstruction < Mutable , SSA > ,
25149 selector : & Selector ,
26150 message_send_type : MessageSendType ,
@@ -39,8 +163,9 @@ pub fn process_call(
39163 } ;
40164 let sel = named_type ( bv, "SEL" ) . unwrap_or_else ( || Type :: pointer ( & arch, & Type :: char ( ) ) ) ;
41165
42- // TODO: Infer return type based on receiver type / selector.
43- let return_type = id. clone ( ) ;
166+ let return_type =
167+ return_type_for_init_receiver ( bv, func, ssa, insn, selector, message_send_type)
168+ . unwrap_or_else ( || id. clone ( ) ) ;
44169
45170 let mut params = vec ! [
46171 FunctionParameter :: new( receiver_type, receiver_name. to_string( ) , None ) ,
0 commit comments