Exemplo n.º 1
0
def main():
    global b
    global sol
    extras = {angr.options.REVERSE_MEMORY_NAME_MAP, \
    angr.options.TRACK_ACTION_HISTORY, \
    angr.options.CONSTRAINT_TRACKING_IN_SOLVER}
    s = b.factory.blank_state(addr=start_addr, add_options=extras)
    if use_custom_concretization_strategy_first:
        #add_concretization_strategy(s, 0xffffffff8109a62a)
        add_concretization_strategy(s)

    add_instrumentation(s)

    if debug_qemu_backend:
        r = remote('127.0.0.1', QEMU_PORT)
        s = install_context(r, s)
        install_section(r, s, '.data')
        install_section(r, s, '.bss')
        install_section(r, s, '.brk')
        r.close()
    s.memory.store(uaf_object_base, s.se.BVS("uaf_obj", uaf_object_size * 8))
    raw_input('Start conclic exploration?')
    explore_by_pathgroup(b, s)


if __name__ == '__main__':
    k = ELF(KERNEL_PATH)
    b = angr.Project(KERNEL_PATH)
    sol = claripy.Solver()
    main()
Exemplo n.º 2
0
Arquivo: sol.py Projeto: GNUp/ctfs
import claripy
import code

s = claripy.Solver()

inte = claripy.BVS("a", 32)
inte = 214013 * inte
inte = inte & 0xffffffff
inte = inte + 2531011
inte = inte & 0xffffffff
re = (inte >> 16) & 0x7fff

s.add(re == 0)

a = s.eval(inte, 10)

code.interact(local=locals())
Exemplo n.º 3
0
    def _categorize_successor(self, state):
        """
        Append state into successor lists.

        :param state: a SimState instance
        :param target: The target (of the jump/call/ret)
        :return: The state
        """

        self.all_successors.append(state)
        target = state.scratch.target

        # categorize the state
        if o.APPROXIMATE_GUARDS in state.options and state.solver.is_false(
                state.scratch.guard, exact=False):
            if o.VALIDATE_APPROXIMATIONS in state.options:
                if state.satisfiable():
                    raise Exception('WTF')
            self.unsat_successors.append(state)
        elif o.APPROXIMATE_SATISFIABILITY in state.options and not state.solver.satisfiable(
                exact=False):
            if o.VALIDATE_APPROXIMATIONS in state.options:
                if state.solver.satisfiable():
                    raise Exception('WTF')
            self.unsat_successors.append(state)
        elif not state.scratch.guard.symbolic and state.solver.is_false(
                state.scratch.guard):
            self.unsat_successors.append(state)
        elif o.LAZY_SOLVES not in state.options and not state.satisfiable():
            self.unsat_successors.append(state)
        elif o.NO_SYMBOLIC_JUMP_RESOLUTION in state.options and state.solver.symbolic(
                target):
            self.unconstrained_successors.append(state)
        elif not state.solver.symbolic(
                target) and not state.history.jumpkind.startswith("Ijk_Sys"):
            # a successor with a concrete IP, and it's not a syscall
            self.successors.append(state)
            self.flat_successors.append(state)
        elif state.history.jumpkind.startswith("Ijk_Sys"):
            # syscall
            self.successors.append(state)

            # Misuse the ip_at_syscall register to save the return address for this syscall
            # state.ip *might be* changed to be the real address of syscall SimProcedures by syscall handling code in
            # angr
            state.regs.ip_at_syscall = state.ip

            try:
                symbolic_syscall_num, concrete_syscall_nums = self._resolve_syscall(
                    state)
                if concrete_syscall_nums is not None:
                    for n in concrete_syscall_nums:
                        split_state = state.copy()
                        split_state.add_constraints(symbolic_syscall_num == n)
                        split_state.inspect.downsize()
                        self._fix_syscall_ip(split_state)

                        self.flat_successors.append(split_state)
                else:
                    # We cannot resolve the syscall number
                    # However, we still put it to the flat_successors list, and angr.SimOS.handle_syscall will pick it
                    # up, and create a "unknown syscall" stub for it.
                    self._fix_syscall_ip(state)
                    self.flat_successors.append(state)
            except AngrUnsupportedSyscallError:
                self.unsat_successors.append(state)

        else:
            # a successor with a symbolic IP
            _max_targets = state.options.symbolic_ip_max_targets
            _max_jumptable_targets = state.options.jumptable_symbolic_ip_max_targets
            try:
                if o.KEEP_IP_SYMBOLIC in state.options:
                    s = claripy.Solver()
                    addrs = s.eval(target,
                                   _max_targets + 1,
                                   extra_constraints=tuple(
                                       state.ip_constraints))
                    if len(addrs) > _max_targets:
                        # It is not a library
                        l.debug("It is not a Library")
                        addrs = state.solver.eval_upto(target,
                                                       _max_targets + 1)
                        l.debug("addrs :%s", addrs)
                    cond_and_targets = [(target == addr, addr)
                                        for addr in addrs]
                    max_targets = _max_targets
                else:
                    cond_and_targets = self._eval_target_jumptable(
                        state, target, _max_jumptable_targets + 1)
                    if cond_and_targets is None:
                        # Fallback to the traditional and slow method
                        cond_and_targets = self._eval_target_brutal(
                            state, target, _max_targets + 1)
                        max_targets = _max_targets
                    else:
                        max_targets = _max_jumptable_targets

                if len(cond_and_targets) > max_targets:
                    l.warning(
                        "Exit state has over %d possible solutions. Likely unconstrained; skipping. %s",
                        max_targets, target.shallow_repr())
                    self.unconstrained_successors.append(state)
                else:
                    for cond, a in cond_and_targets:
                        split_state = state.copy()
                        if o.KEEP_IP_SYMBOLIC in split_state.options:
                            split_state.regs.ip = target
                        else:
                            split_state.add_constraints(cond, action=True)
                            split_state.regs.ip = a
                        split_state.inspect.downsize()
                        self.flat_successors.append(split_state)
                    self.successors.append(state)
            except SimSolverModeError:
                self.unsat_successors.append(state)

        return state
Exemplo n.º 4
0
    def compare_states(self, sl, sr):
        """
        Compares two states for similarity.
        """
        joint_solver = claripy.Solver()

        # make sure the canonicalized constraints are the same
        n_map, n_counter, n_canon_constraint = claripy.And(
            *sr.solver.constraints).canonicalize()  #pylint:disable=no-member
        u_map, u_counter, u_canon_constraint = claripy.And(
            *sl.solver.constraints).canonicalize()  #pylint:disable=no-member
        n_canoner_constraint = sr.solver.simplify(n_canon_constraint)
        u_canoner_constraint = sl.solver.simplify(u_canon_constraint)
        joint_solver.add((n_canoner_constraint, u_canoner_constraint))
        if n_canoner_constraint is not u_canoner_constraint:
            self._report_incongruency("Different constraints!")
            return False

        # get the differences in registers and memory
        mem_diff = sr.memory.changed_bytes(sl.memory)
        reg_diff = sr.registers.changed_bytes(sl.registers)

        # this is only for unicorn
        if "UNICORN" in sl.options or "UNICORN" in sr.options:
            if sl.arch.name == "X86":
                reg_diff -= set(range(40, 52))  #ignore cc psuedoregisters
                reg_diff -= set(range(320, 324))  #some other VEX weirdness
                reg_diff -= set(range(340, 344))  #ip_at_syscall
            elif sl.arch.name == "AMD64":
                reg_diff -= set(range(144, 168))  #ignore cc psuedoregisters

        # make sure the differences in registers and memory are actually just renamed
        # versions of the same ASTs
        for diffs, (um, nm) in (
            (reg_diff, (sl.registers, sr.registers)),
            (mem_diff, (sl.memory, sr.memory)),
        ):
            for i in diffs:
                bn = nm.load(i, 1)
                bu = um.load(i, 1)

                bnc = bn.canonicalize(var_map=n_map, counter=n_counter)[-1]
                buc = bu.canonicalize(var_map=u_map, counter=u_counter)[-1]

                if bnc is not buc:
                    self._report_incongruency(
                        "Different memory or registers (index %d, values %r and %r)!",
                        i, bn, bu)
                    return False

        # make sure the flags are the same
        if sl.arch.name in ("AMD64", "X86", "ARM", "ARMEL", "ARMHF",
                            "AARCH64"):
            # pylint: disable=unused-variable
            n_bkp = sr.regs.cc_op, sr.regs.cc_dep1, sr.regs.cc_dep2, sr.regs.cc_ndep
            u_bkp = sl.regs.cc_op, sl.regs.cc_dep1, sl.regs.cc_dep2, sl.regs.cc_ndep
            if sl.arch.name in ('AMD64', 'X86'):
                n_flags = sr.regs.eflags.canonicalize(var_map=n_map,
                                                      counter=n_counter)[-1]
                u_flags = sl.regs.eflags.canonicalize(var_map=u_map,
                                                      counter=u_counter)[-1]
            else:
                n_flags = sr.regs.flags.canonicalize(var_map=n_map,
                                                     counter=n_counter)[-1]
                u_flags = sl.regs.flags.canonicalize(var_map=u_map,
                                                     counter=u_counter)[-1]
            if n_flags is not u_flags and sl.solver.simplify(
                    n_flags) is not sr.solver.simplify(u_flags):
                self._report_incongruency("Different flags!")
                return False

        return True
Exemplo n.º 5
0
 def get_max_min(self, val, idx):
     solver = claripy.Solver()
     solver.add(val)
     sym = self.arg1a[idx]
     return (solver.max(sym), solver.min(sym))
Exemplo n.º 6
0
def ast_prove_f1_in_f2(f1, f2, c1=None, c2=None):
    # we merely prove the f1==f2 in the interval of join(c1, c2)
    input1 = get_expression_input(f1)
    input2 = get_expression_input(f2)
    if c1:
        for tmp in c1:
            input1.update(get_expression_input(tmp))
    if c2:
        for tmp in c2:
            input2.update(get_expression_input(tmp))
    input1 = list(input1)
    input2 = list(input2)

    print("f0:=%s\nf1:=%s\ninput0:=%s\ninput1:=%s\ncons0:=%s\ncons1:=%s" %
          (str(f1), str(f2), str(input1), str(input2), str(c1), str(c2)))
    print("%d / %d\n" % (len(input1), len(input2)))

    # we think f2 is always more complex than f1
    if len(input2) < len(input1):
        return False

    # compute the number of permutations
    permute_variables = len(input1)
    num_permutations = len(input2)
    for _i in range(len(input1)):
        num_permutations *= len(input2) - _i
    if len(input1) > 3 or num_permutations > 1000:
        # too many permutations, it wastes too much time
        # assigning same value to several input sources
        # merely permute the first 3 variables
        permute_variables = 3
    unsat_expr = claripy.ast.Bool(op='__ne__', args=(f1, f2))
    sat_expr = claripy.ast.Bool(op='__eq__', args=(f1, f2))
    ec2 = claripy.And(*[claripy.ast.Bool(op='__eq__', args=(claripy.Or(*c1), claripy.BoolV(True))),
                        claripy.ast.Bool(op='__eq__', args=(claripy.Or(*c2), claripy.BoolV(True)))])

    for in2 in permutations(input2, permute_variables):
        ec1 = []
        try:
            solver = claripy.Solver(backend=_MyBackendZ3())
            # symbolic input constraints
            for idx in range(len(in2)):
                ec1.append(claripy.ast.Bool(op='__eq__', args=(input1[idx], in2[idx])))
            ec1 = claripy.And(*ec1)
            solver.add(ec2)
            solver.add(ec1)
            if permute_variables == len(input1):
                # we do not need to concrete symbolic values here
                if solver.satisfiable(extra_constraints=[sat_expr]):
                    solver.add(unsat_expr)
                    if not solver.satisfiable():
                        return True
            else:
                # permute_variables < len(input1), we concrete all other variables of f1 and f2 to same value
                for concrete_value in [0, 1, 0xffffff]:
                    ec3 = []
                    for i in range(permute_variables, len(input1)):
                        ec3.append(input1[i] == concrete_value)
                    in2_var_names = set([var._encoded_name for var in in2])
                    for i in range(len(input2)):
                        if input2[i]._encoded_name not in in2_var_names:
                            ec3.append(input2[i] == concrete_value)
                    ec3 = claripy.And(*ec3)
                    # finally solve here repeatedly
                    if solver.satisfiable(extra_constraints=[sat_expr, ec3]):
                        if not solver.satisfiable([unsat_expr, ec3]):
                            return True
        except Exception as e:
            log.warning('Meet Z3 solver error %s' % str(e))
    return False
Exemplo n.º 7
0
def get_claripy_solver():
    # TODO: What about SolverComposite? Tried, and seems slower.
    return claripy.Solver()
Exemplo n.º 8
0
 def __init__(self):
     self.solver = claripy.Solver()
Exemplo n.º 9
0
def prove_equal(merged_f1, merged_f2, ptr_size1, ptr_size2, c1=None, c2=None, cmp_limit=120, equal_var=True):
    """
    prove f1 == f2, using SMT solver Z3
    :param f1:
    :param f2:
    :param c1: extra-constraints of f1
    :param c2: extra-constraints of f2
    :return:
    """
    log.debug('prove_equal')
    f1, orig_f1 = merged_f1
    f2, orig_f2 = merged_f2
    if c1 is not None and c2 is not None:
        if len(c1) > 0 and len(c2) > 0:
            solver = claripy.Solver(backend=_MyBackendZ3())
            # our normalization may make the constraints become UNSAT, ignore it when it happens
            try:
                if not solver.satisfiable(c1) or not solver.satisfiable(c2):
                    f1 = orig_f1
                    f2 = orig_f2
                    c1 = None
                    c2 = None
            except Exception:
                f1 = orig_f1
                f2 = orig_f2
                c1 = None
                c2 = None

    # ne_f1_f2 = ne_formulas(f1, f2)
    # if ne_f1_f2 is None:
    #     log.debug("fail1 to create not_equal formula")
    #     log.debug(f1)
    #     log.debug(f2)
    #     return False
    f1_in1, f2_in2 = check_and_return_formula_pair_with_inputs(f1, f2, ptr_size1, ptr_size2)
    if f1_in1 is None or f2_in2 is None:
        log.debug('inputs variables do not match')
        return False
    f1, input1 = f1_in1
    f2, input2 = f2_in2
    ne_f1_f2 = ne_formulas(f1, f2)
    if ne_f1_f2 is None:
        log.debug("fail to create not_equal formula")
        log.debug(f1)
        log.debug(f2)
        return False
    log.debug(f"To prove {str(ne_f1_f2)} UNSAT")

    count = 0
    min_num_var = min(len(input1), len(input2))
    if min_num_var == 0:
        # this means no extra-constraints should be added, input1 or input2 is a constant
        solver = claripy.Solver(backend=_MyBackendZ3())
        solver.add(ne_f1_f2)
        return not solver.satisfiable()

    if factorial(min_num_var) > cmp_limit:
        raise TooManyVariables4Comparison(f1, f2, cmp_limit)

    for in1 in permutations(input1, min_num_var):
        constraints = []
        count += 1
        if count > cmp_limit:
            raise TooManyVariables4Comparison(f1, f2, cmp_limit)
        try:
            constraints = get_var_constraints(in1, input2[:min_num_var])
            if constraints is None:
                continue
            cmp_expr = ne_f1_f2
            # print(str(constraints))
            # print(str(cmp_expr))

            # if we have extra constraints, we need to ensure that constraints can be satisfied first
            need_pre_condition_sat_check = False
            if c1 is not None:
                need_pre_condition_sat_check = True
                constraints.extend(c1)
            if c2 is not None:
                need_pre_condition_sat_check = True
                constraints.extend(c2)

            if need_pre_condition_sat_check:
                solver = claripy.Solver(backend=_MyBackendZ3())
                # check the constraints first
                solver.add(constraints)
                if not solver.satisfiable():
                    continue

            solver = claripy.Solver(backend=_MyBackendZ3())
            solver.add(cmp_expr)
            if not solver.satisfiable(extra_constraints=constraints):
                return True
        except Exception as e:
            # print('f1', f1)
            # print('f2', f2)
            # print('input1', input1)
            # print('input2', input2)
            # constraints = get_var_constraints(in1, input2[:min_num_var])
            # print('constraints')
            # for c in constraints:
            #     print(c)
            log.warning('Meet Z3 solver error %s' % str(e))
    return False
Exemplo n.º 10
0
    def __init__(self, kernel_path=None, dbcred=\
            '/home/ww9210/develop/concolic_execution/concolicexecutor/dbcredentials.bin'):
        self.kernel_path = kernel_path
        self.k = ELF(kernel_path)
        self.b = angr.Project(kernel_path)
        self.sol = claripy.Solver()
        self.claripy = claripy
        self.typebv = claripy.ast.bv.BV
        self.r = None
        self.statebroker = statebroker.StateBroker()
        self.krop = None
        self.interested_funcs = {}
        self.memory_leak_funcs = {}

        # build connection to knowledge graph
        tmpf = open(dbcred, 'r')
        url, username, password = pickle.load(tmpf)
        print url, username, password
        try:
            self.neo4j_inst = neo4j_interace.MyNeo4jInterface(
                url, username, password)
        except neo4j.exceptions.ServiceUnavailable:
            print '[-] could not connect database'
        tmpf.close()

        # primitives
        self.control_flow_hijack_primitive = [
        ]  # control flow hijack primitive
        self.write_to_uaf_object_primitive = [
        ]  # write to uaf object primitive
        self.write_to_symbolic_address_primitive = [
        ]  # write to symbolic address primitive
        self.double_free_primitive = []  # double free primitive
        self.memory_leak_primitive = []  # memory leak primitive

        self.seen_symbolic_write_primitive_addr = []
        self.seen_write_to_uaf_object_primitive_addr = []

        # attributes
        self.start_addr = None
        self.use_custom_concretization_strategy_first = None
        self.qemu_port = None
        self.debug_qemu_backend = None
        self.debug_irsb = None
        self.debug_after_address_concretization = None
        self.resolve_uninit = None
        self.pause_on_finish_memory_loading = None
        self.pause_on_write_to_symbolic_address = None
        self.pause_on_uninit_write = None
        self.pause_on_each_step = None
        self.pause_on_read_from_symbolic_address = None
        self.pause_on_write_to_uaf_object_primitive = None
        self.dump_uaf_buffer = None
        self.dump_userspace_buffer = None
        self.pause_on_failed_memory_resolving = None
        self.pause_on_control_flow_hijack = None
        self.pause_on_memory_leak = None
        self.remove_range_concretize = None
        self.userspace_base = None
        self.kuafffp_concretizer = None

        self.seen_control_flow_hijack_primitive = []
        self.seen_memory_leak_primitive = []

        self.concretization_range = None
        self.extra_bp = None
        self.execution_time_limit = None
        self.expected_start_rip = None
        self.limit_loop = None
        self.detect_rcu_free = None
        self.detect_memory_leak = None
        self.assigned_start_time = None
        self.start_time_of_symbolic_execution = None
        self.function_call_to_disable = None
        self.stack_pivot_address = None
        self.additional_symbolic_registers = None
        self.name_concretization_strategy = None
        self.num_user_pages = None
        self.spray_method = None
        self.pause_on_strange_state = None
        self.target_allocator = None
        self.vulnerability_type = None
        self.look_for_second_control_flow_hijack = None
        return
Exemplo n.º 11
0
    def patch_function_stack(self, func, technique):
        solver = claripy.Solver()
        analysis_result = StructureAnalysis(self.project, self.cfg, [func],
                                            False)
        stack = analysis_result.stack_frames[func.addr]
        if stack is None:
            return
        stack = analysis_result.structures[stack]

        if stack.alloc_op is None:
            l.info(
                '\tFunction does not appear to have a stack frame (No alloc)')
            return False

        if func.has_return and stack.least_alloc.value != self.project.arch.bytes if self.project.arch.call_pushes_ret else 0:
            l.info(
                '\tFunction does not ever deallocate stack frame (Least alloc is %d for %s)',
                -stack.least_alloc.value, self.project.arch.name)
            return False

        if func.has_return and len(stack.dealloc_ops) == 0:
            l.warning(
                '\tFunction does not ever deallocate stack frame (No zero alloc)'
            )
            return False

        if stack.conc_size <= 0:
            l.warning('\tFunction has invalid stack size of %#x',
                      stack.conc_size)
            return False

        if stack.num_vars == 0:
            l.info(
                "\tFunction has %#x-byte stack frame, but doesn't use it for local vars",
                stack.conc_size)
            return False

        l.info('\tFunction has a stack frame of %#x bytes', stack.conc_size)
        l.info('\t%d access%s to %d address%s %s made.', stack.num_accs,
               '' if stack.num_accs == 1 else 'es', stack.num_vars,
               '' if stack.num_vars == 1 else 'es',
               'is' if stack.num_accs == 1 else 'are')

        l.debug('Stack addresses: [%s]',
                ', '.join(hex(var.conc_addr) for var in stack))
        l.debug('Running strategy...')
        technique.constrain_variables(func, solver, stack)
        l.debug('Stack variables: [%s]',
                ', '.join(hex(var.conc_addr) for var in stack))

        if not solver.satisfiable():
            l.critical('(%#x) Safe constraints unsatisfiable, fix this NOW',
                       func.addr)
            raise FidgetError("You're a terrible programmer")

        # bit of a hack: the first unsafe constraint is always the one making the stack bigger, so we can quick-bail if it fails
        if not solver.satisfiable(
                extra_constraints=[stack.unsafe_constraints[0]]):
            l.info('\tUnable to resize stack')
            return False

        # z3 is smart enough that this doesn't add any noticable overhead
        for constraint in stack.unsafe_constraints:
            if solver.satisfiable(extra_constraints=[constraint]):
                l.debug("Added unsafe constraint:         %s", constraint)
                solver.add(constraint)
            else:
                l.debug("Failed to add unsafe constraint: %s", constraint)

        new_stack = solver.eval(stack.sym_size, 1)[0]
        if new_stack == stack.conc_size:
            l.info('\tUnable to resize stack')
            return False

        l.info('\tResized stack from %#x to %#x', stack.conc_size, new_stack)

        for var in stack:
            fixedval = solver.eval_to_ast(var.sym_addr,
                                          1)[0]._model_concrete.signed
            if var.size is None:
                l.debug('Moved %#x (unsized) to %#x', var.conc_addr, fixedval)
            else:
                l.debug('Moved %#x (size %#x) to %#x', var.conc_addr, var.size,
                        fixedval)

        self._stack_patch_data += stack.get_patches(solver)
        self.stack_increases[func.addr] = new_stack - stack.conc_size
        return True
Exemplo n.º 12
0
 def val_arg(key):
     tmp_solver = claripy.Solver()
     tmp_solver.add(constraint_sets[key])
     return tmp_solver.eval(
         self.template_args[0], 1
     )[0]  # Grabbing the first arg is correct because we've already verified there's only one!
Exemplo n.º 13
0
 def val_ret(key):
     tmp_solver = claripy.Solver()
     tmp_solver.add(constraint_sets[key])
     return tmp_solver.eval(key.ast, 1)[0]
Exemplo n.º 14
0
 def fully_constrained(value, key):
     tmp_solver = claripy.Solver()
     tmp_solver.add(constraint_sets[key][0])
     tmp_solver.add(self.arg_size_constraint)
     return len(tmp_solver.eval(value, 2)) == 1
Exemplo n.º 15
0
    def _linearize(self, constraint_sets):
        solver = claripy.Solver()

        def fully_constrained(value, key):
            tmp_solver = claripy.Solver()
            tmp_solver.add(constraint_sets[key][0])
            tmp_solver.add(self.arg_size_constraint)
            return len(tmp_solver.eval(value, 2)) == 1

        def can_linearize(key):
            if self.num_args != 1:
                # It sure would be cool if we could do more fancy stuff than just linearizing, but that's for later.
                return False
            if len(constraint_sets[key]) != 1:
                # There's more than one input that leads to this output, so we can't linearize
                # Don't get rid of this behavior because it's relied upon for correctness later on.
                return False
            else:
                # Note that we can linearize symbolic values, the only requirement is that they must be fully constrained by the accompanying constraint set
                # This is imperative because we're going to apply a continuous (linear) function to the input to get the output
                return all([
                    fully_constrained(arg, key) for arg in self.template_args
                ])

        # This is a list of the keys associated with all return values
        all_keys = constraint_sets.keys()

        # Seperate out linearizable keys from unlinearizable keys
        linearizable_keys = [key for key in all_keys if can_linearize(key)]
        unlinearizable_keys = [
            key for key in all_keys if key not in linearizable_keys
        ]

        # Each unlinearizable key gets its own return bucket
        for key in unlinearizable_keys:
            self._create_return_bucket(key, constraint_sets[key])

        # These are methods to get the values of the return val and argument for any given key
        # We've already checked that these are fully constrained values
        def val_ret(key):
            tmp_solver = claripy.Solver()
            tmp_solver.add(constraint_sets[key])
            return tmp_solver.eval(key.ast, 1)[0]

        def val_arg(key):
            tmp_solver = claripy.Solver()
            tmp_solver.add(constraint_sets[key])
            return tmp_solver.eval(
                self.template_args[0], 1
            )[0]  # Grabbing the first arg is correct because we've already verified there's only one!

        # Linearization happens here. it's basically y=mx+b, restarting with a new line every time there's a discontinuity.
        buckets = []
        bucket = []
        coefficient = 0
        for key in sorted(linearizable_keys, key=lambda key: val_ret(key)):
            if len(bucket) == 0:
                bucket.append(key)
                continue

            dy = val_ret(key) - val_ret(bucket[-1])
            dx = val_arg(key) - val_arg(bucket[-1])

            if len(bucket) == 1:
                if dy % dx == 0:
                    #define the coefficient, greedily add this key to the bucket
                    coefficient = dy / dx
                    bucket.append(key)
                else:
                    #we can't do anything with this bucket if the coefficient isn't an integer
                    buckets.append(bucket)
                    bucket = [key]
            else:
                if dy % dx == 0 and coefficient == dy / dx:
                    #the linear pattern continues! add it to the bucket
                    bucket.append(key)
                else:
                    #done with this bucket, start a new one
                    buckets.append(bucket)
                    bucket = [key]

        #after all that, make sure to add the last bucket to our list of all buckets
        if len(bucket) != 0:
            buckets.append(bucket)

        for bucket in buckets:
            if len(bucket) == 1:
                #this is an easy case, bucketing bought us nothing.
                self._create_return_bucket(bucket[0], constraint_sets[key][0])
            else:
                #create the constraint
                constraints = claripy.Or(
                    *[claripy.Or(*constraint_sets[key]) for key in bucket])

                #define a symbolic return
                retval = claripy.BVS(
                    'auto_simproc_linearized_return_MUST_REPLACE', 64)
                self.symbolic_return_templates.append(retval.cache_key)

                #save this so we can use it later to establish mutual exclusivity! this is sort of a hack but it's very important
                # constraints_without_bucket_info[retval.cache_key] = constraints

                #now we constrain the symbolic return value to be a function of the input, with good old y=mx+b
                m = (val_ret(bucket[1]) - val_ret(bucket[0])) / (
                    val_arg(bucket[1]) - val_arg(bucket[0]))
                b = val_ret(bucket[0]) - m * val_arg(bucket[0])
                constraints = claripy.And(
                    retval == ((m * self.template_args[0]) + b), constraints)

                #now add the whole bucket with one key
                self._create_return_bucket(retval.cache_key, (constraints, ),
                                           linearized=True)
Exemplo n.º 16
0
def test_fp_ops():
    a = claripy.FPV(1.5, claripy.FSORT_DOUBLE)
    b = claripy.fpToUBV(claripy.fp.RM_NearestTiesEven, a, 32)

    s = claripy.Solver()
    assert s.eval(b, 1)[0] == 2