def _ail_handle_Call(self, stmt): target = self._expr(stmt.target) new_args = None if stmt.args: new_args = [] for arg in stmt.args: new_arg = self._expr(arg) replacement = self.state.get_variable(new_arg) if replacement is not None: new_args.append(replacement) else: new_args.append(arg) if new_args != stmt.args: new_call_stmt = Stmt.Call( stmt.idx, target, calling_convention=stmt.calling_convention, prototype=stmt.prototype, args=new_args, ret_expr=stmt.ret_expr, **stmt.tags) self.state.add_replacement( self._codeloc(), stmt, new_call_stmt, )
def _ail_handle_Call(self, stmt): target = self._expr(stmt.target) new_args = None if stmt.args: new_args = [] for arg in stmt.args: new_arg = self._expr(arg) new_args.append(new_arg) return Stmt.Call(stmt.idx, target, calling_convention=stmt.calling_convention, prototype=stmt.prototype, args=new_args, ret_expr=stmt.ret_expr, **stmt.tags)
def _analyze(self): if not self.block.statements: return last_stmt = self.block.statements[-1] if not type(last_stmt) is Stmt.Call: self.result_block = self.block return cc = None prototype = None args = None stack_arg_locs: List[SimStackArg] = [] stackarg_sp_diff = 0 target = self._get_call_target(last_stmt) if target is not None and target in self.kb.functions: # function-specific logic when the calling target is known func = self.kb.functions[target] if func.prototype is None: func.find_declaration() cc = func.calling_convention prototype = func.prototype args = [] arg_locs = None if func.calling_convention is None: l.warning('%s has an unknown calling convention.', repr(func)) else: stackarg_sp_diff = func.calling_convention.STACKARG_SP_DIFF if func.prototype is not None: # Make arguments arg_locs = func.calling_convention.arg_locs() if func.prototype.variadic: # determine the number of variadic arguments variadic_args = self._determine_variadic_arguments( func, func.calling_convention, last_stmt) if variadic_args: arg_sizes = [arg.size // self.project.arch.byte_width for arg in func.prototype.args] + \ ([self.project.arch.bytes] * variadic_args) is_fp = [False] * len(arg_sizes) arg_locs = func.calling_convention.arg_locs( is_fp=is_fp, sizes=arg_sizes) else: if func.calling_convention.args is not None: arg_locs = func.calling_convention.arg_locs() if arg_locs is not None: for arg_loc in arg_locs: if type(arg_loc) is SimRegArg: size = arg_loc.size offset = arg_loc._fix_offset(None, size, arch=self.project.arch) _, the_arg = self._resolve_register_argument( last_stmt, arg_loc) if the_arg is not None: args.append(the_arg) else: # Reaching definitions are not available. Create a register expression instead. args.append( Expr.Register(self._atom_idx(), None, offset, size * 8, reg_name=arg_loc.reg_name)) elif type(arg_loc) is SimStackArg: stack_arg_locs.append(arg_loc) _, the_arg = self._resolve_stack_argument( last_stmt, arg_loc) if the_arg is not None: args.append(the_arg) else: args.append(None) else: raise NotImplementedError('Not implemented yet.') # Remove the old call statement new_stmts = self.block.statements[:-1] # remove the statement that stores the return address if self.project.arch.call_pushes_ret: # check if the last statement is storing the return address onto the top of the stack if len(new_stmts) >= 1: the_stmt = new_stmts[-1] if isinstance(the_stmt, Stmt.Store) and isinstance( the_stmt.data, Expr.Const): if isinstance(the_stmt.addr, Expr.StackBaseOffset) and \ the_stmt.data.value == self.block.addr + self.block.original_size: # yes it is! new_stmts = new_stmts[:-1] else: # if there is an lr register... lr_offset = None if archinfo.arch_arm.is_arm_arch( self.project.arch) or self.project.arch.name in { 'PPC32', 'PPC64' }: lr_offset = self.project.arch.registers['lr'][0] elif self.project.arch.name in {'MIPS32', 'MIPS64'}: lr_offset = self.project.arch.registers['ra'][0] if lr_offset is not None: # remove the assignment to the lr register if len(new_stmts) >= 1: the_stmt = new_stmts[-1] if (isinstance(the_stmt, Stmt.Assignment) and isinstance(the_stmt.dst, Expr.Register) and the_stmt.dst.reg_offset == lr_offset): # found it new_stmts = new_stmts[:-1] # calculate stack offsets for arguments that are put on the stack. these offsets will be consumed by # simplification steps in the future, which may decide to remove statements that stores arguments on the stack. if stack_arg_locs: sp_offset = self._stack_pointer_tracker.offset_before( last_stmt.ins_addr, self.project.arch.sp_offset) if sp_offset is None: l.warning( "Failed to calculate the stack pointer offset at pc %#x. You may find redundant Store " "statements.", last_stmt.ins_addr) self.stack_arg_offsets = None else: self.stack_arg_offsets = set( (last_stmt.ins_addr, sp_offset + arg.stack_offset - stackarg_sp_diff) for arg in stack_arg_locs) ret_expr = last_stmt.ret_expr # if ret_expr is None, it means in previous steps (such as during AIL simplification) we have deemed the return # value of this call statement as useless and is removed. new_stmts.append( Stmt.Call( last_stmt, last_stmt.target, calling_convention=cc, prototype=prototype, args=args, ret_expr=ret_expr, **last_stmt.tags, )) new_block = self.block.copy() new_block.statements = new_stmts self.result_block = new_block
def _analyze(self): if not self.block.statements: return last_stmt = self.block.statements[-1] if not type(last_stmt) is Stmt.Call: self.result_block = self.block return target = self._get_call_target(last_stmt) if target is None: return if target not in self.kb.functions: return func = self.kb.functions[target] if func.prototype is None: func.find_declaration() args = [] arg_locs = None if func.calling_convention is None: l.warning('%s has an unknown calling convention.', repr(func)) else: if func.prototype is not None: # Make arguments arg_locs = func.calling_convention.arg_locs() if func.prototype.variadic: # determine the number of variadic arguments variadic_args = self._determine_variadic_arguments( func, func.calling_convention, last_stmt) if variadic_args: arg_sizes = [arg.size // self.project.arch.byte_width for arg in func.prototype.args] + \ ([self.project.arch.bytes] * variadic_args) is_fp = [False] * len(arg_sizes) arg_locs = func.calling_convention.arg_locs( is_fp=is_fp, sizes=arg_sizes) else: if func.calling_convention.args is not None: arg_locs = func.calling_convention.arg_locs() stack_arg_locs: List[SimStackArg] = [] if arg_locs is not None: for arg_loc in arg_locs: if type(arg_loc) is SimRegArg: size = arg_loc.size offset = arg_loc._fix_offset(None, size, arch=self.project.arch) _, the_arg = self._resolve_register_argument( last_stmt, arg_loc) if the_arg is not None: args.append(the_arg) else: # Reaching definitions are not available. Create a register expression instead. args.append( Expr.Register(None, None, offset, size * 8, reg_name=arg_loc.reg_name)) elif type(arg_loc) is SimStackArg: stack_arg_locs.append(arg_loc) _, the_arg = self._resolve_stack_argument( last_stmt, arg_loc) if the_arg is not None: args.append(the_arg) else: args.append(None) else: raise NotImplementedError('Not implemented yet.') new_stmts = self.block.statements[:-1] # remove the statement that stores the return address if self.project.arch.call_pushes_ret: # check if the last statement is storing the return address onto the top of the stack if len(new_stmts) >= 1: the_stmt = new_stmts[-1] if isinstance(the_stmt, Stmt.Store) and isinstance( the_stmt.data, Expr.Const): if isinstance(the_stmt.addr, Expr.StackBaseOffset) and \ the_stmt.data.value == self.block.addr + self.block.original_size: # yes it is! new_stmts = new_stmts[:-1] else: # if there is an lr register... lr_offset = None if archinfo.arch_arm.is_arm_arch( self.project.arch) or self.project.arch.name in { 'PPC32', 'PPC64' }: lr_offset = self.project.arch.registers['lr'][0] elif self.project.arch.name in {'MIPS32', 'MIPS64'}: lr_offset = self.project.arch.registers['ra'][0] if lr_offset is not None: # remove the assignment to the lr register if len(new_stmts) >= 1: the_stmt = new_stmts[-1] if (isinstance(the_stmt, Stmt.Assignment) and isinstance(the_stmt.dst, Expr.Register) and the_stmt.dst.reg_offset == lr_offset): # found it new_stmts = new_stmts[:-1] # remove statements that stores arguments on the stack if stack_arg_locs: sp_offset = self._stack_pointer_tracker.offset_before( last_stmt.ins_addr, self.project.arch.sp_offset) if sp_offset is None: l.warning( "Failed to calculate the stack pointer offset at pc %#x. You may find redundant Store " "statements.", last_stmt.ins_addr) else: stack_arg_offsets = set( (arg.stack_offset + sp_offset) for arg in stack_arg_locs) old_stmts = new_stmts new_stmts = [] for stmt in old_stmts: if isinstance(stmt, Stmt.Store) and isinstance( stmt.addr, Expr.StackBaseOffset): offset = stmt.addr.offset if offset < 0: offset &= (1 << self.project.arch.bits) - 1 if offset in stack_arg_offsets: continue new_stmts.append(stmt) ret_expr = last_stmt.ret_expr if ret_expr is None: ret_expr = None if func.prototype is not None: if func.prototype.returnty is not None and not isinstance( func.prototype.returnty, SimTypeBottom): # it has a return value if func.calling_convention is not None: ret_expr_size = func.prototype.returnty._with_arch( self.project.arch).size reg_offset = func.calling_convention.RETURN_VAL._fix_offset( None, ret_expr_size, arch=self.project.arch, ) ret_expr = Expr.Register(None, None, reg_offset, ret_expr_size * 8) new_stmts.append( Stmt.Call( last_stmt, last_stmt.target, calling_convention=func.calling_convention, prototype=func.prototype, args=args, ret_expr=ret_expr, **last_stmt.tags, )) new_block = self.block.copy() new_block.statements = new_stmts self.result_block = new_block
def _analyze(self): if not self.block.statements: return last_stmt = self.block.statements[-1] if not type(last_stmt) is Stmt.Call: self.result_block = self.block return target = self._get_call_target(last_stmt) if target is None: return if target not in self.kb.functions: return func = self.kb.functions[target] if func.prototype is None: func.find_declaration() args = [] arg_locs = None if func.calling_convention is None: l.warning('%s has an unknown calling convention.', repr(func)) else: if func.prototype is not None: # Make arguments arg_locs = func.calling_convention.arg_locs() else: if func.calling_convention.args is not None: arg_locs = func.calling_convention.arg_locs() if arg_locs is not None: for arg_loc in arg_locs: if type(arg_loc) is SimRegArg: size = arg_loc.size offset = arg_loc._fix_offset(None, size, arch=self.project.arch) the_arg = self._resolve_register_argument( last_stmt, arg_loc) if the_arg is not None: args.append(the_arg) else: # Reaching definitions are not available. Create a register expression instead. args.append( Expr.Register(None, None, offset, size * 8, reg_name=arg_loc.reg_name)) elif type(arg_loc) is SimStackArg: the_arg = self._resolve_stack_argument(last_stmt, arg_loc) if the_arg is not None: args.append(the_arg) else: args.append(None) else: raise NotImplementedError('Not implemented yet.') new_stmts = self.block.statements[:-1] if self.project.arch.call_pushes_ret: # check if the last statement is storing the return address onto the top of the stack if len(new_stmts) >= 1: the_stmt = new_stmts[-1] if isinstance(the_stmt, Stmt.Store) and isinstance( the_stmt.data, Expr.Const): if isinstance(the_stmt.variable, SimStackVariable) and \ the_stmt.data.value == self.block.addr + self.block.original_size: # yes it is! new_stmts = new_stmts[:-1] ret_expr = last_stmt.ret_expr if ret_expr is None: ret_expr = None if func.prototype is not None: if func.prototype.returnty is not None and not isinstance( func.prototype.returnty, SimTypeBottom): # it has a return value if func.calling_convention is not None: ret_expr_size = func.prototype.returnty._with_arch( self.project.arch).size reg_offset = func.calling_convention.RETURN_VAL._fix_offset( None, ret_expr_size, arch=self.project.arch, ) ret_expr = Expr.Register(None, None, reg_offset, ret_expr_size * 8) new_stmts.append( Stmt.Call( last_stmt, last_stmt.target, calling_convention=func.calling_convention, prototype=func.prototype, args=args, ret_expr=ret_expr, **last_stmt.tags, )) new_block = self.block.copy() new_block.statements = new_stmts self.result_block = new_block