Example #1
0
    def _resolve_stack_argument(self, call_stmt, arg_loc):  # pylint:disable=unused-argument

        size = arg_loc.size
        offset = arg_loc.stack_offset
        if self.project.arch.call_pushes_ret:
            # adjust the offset
            offset -= self.project.arch.bytes

        return Expr.Load(None,
                         Expr.Register(None, None, self.project.arch.sp_offset, self.project.arch.bits) +
                            Expr.Const(None, None, offset, self.project.arch.bits),
                         size,
                         self.project.arch.memory_endness,
                         )
Example #2
0
    def _resolve_stack_argument(self, call_stmt, arg_loc) -> Tuple[Any, Any]:  # pylint:disable=unused-argument

        size = arg_loc.size
        offset = arg_loc.stack_offset
        if self.project.arch.call_pushes_ret:
            # adjust the offset
            offset -= self.project.arch.bytes

        # TODO: Support extracting values

        return None, Expr.Load(
            self._atom_idx(),
            Expr.Register(self._atom_idx(), None, self.project.arch.sp_offset,
                          self.project.arch.bits) +
            Expr.Const(self._atom_idx(), None, offset, self.project.arch.bits),
            size,
            self.project.arch.memory_endness,
        )
Example #3
0
    def _ail_handle_Call(self, expr_stmt: Stmt.Call):
        if isinstance(expr_stmt.target, Expr.Expression):
            _ = self._expr(expr_stmt.target)

        self.state._inside_call_stmt = True

        if expr_stmt.args:
            for arg in expr_stmt.args:
                _ = self._expr(arg)

        if expr_stmt.ret_expr is not None:
            if isinstance(expr_stmt.ret_expr, Expr.Register):
                # it has a return expression. awesome - treat it as an assignment

                # assume the return value always uses a full-width register
                # FIXME: Expose it as a configuration option
                return_value_use_full_width_reg = True
                if return_value_use_full_width_reg:
                    v = PropValue.from_value_and_details(
                        self.state.top(self.arch.bits), self.arch.bytes, expr_stmt.ret_expr, self._codeloc()
                    )
                    self.state.store_register(
                        Expr.Register(None, expr_stmt.ret_expr.variable, expr_stmt.ret_expr.reg_offset, self.arch.bits),
                        v
                    )
                else:
                    v = PropValue.from_value_and_details(
                        self.state.top(expr_stmt.ret_expr.size * self.arch.byte_width),
                        expr_stmt.ret_expr.size, expr_stmt.ret_expr, self._codeloc()
                    )
                    self.state.store_register(expr_stmt.ret_expr, v)
                # set equivalence
                self.state.add_equivalence(self._codeloc(), expr_stmt.ret_expr, expr_stmt)
            else:
                l.warning("Unsupported ret_expr type %s.", expr_stmt.ret_expr.__class__)

        self.state._inside_call_stmt = False
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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