Example #1
0
    def _fold_double_negations(cond):

        # !(!A) ==> A
        # !((!A) && (!B)) ==> A || B
        # !((!A) && B) ==> A || !B
        # !(A || B) ==> (!A && !B)

        if cond.op != "Not":
            return None
        if cond.args[0].op == "Not":
            return cond.args[0]

        if cond.args[0].op == "And" and len(cond.args[0].args) == 2:
            and_0, and_1 = cond.args[0].args
            if and_0.op == "Not" and and_1.op == "Not":
                expr = claripy.Or(and_0.args[0], and_1.args[0])
                return expr

            if and_0.op == "Not":  # and_1.op != "Not"
                expr = claripy.Or(
                    and_0.args[0],
                    ConditionProcessor.simplify_condition(claripy.Not(and_1)))
                return expr

        if cond.args[0].op == "Or" and len(cond.args[0].args) == 2:
            or_0, or_1 = cond.args[0].args
            expr = claripy.And(
                ConditionProcessor.simplify_condition(claripy.Not(or_0)),
                ConditionProcessor.simplify_condition(claripy.Not(or_1)),
            )
            return expr

        return None
Example #2
0
    def _extract_predicate(self, src_block, dst_block):

        if type(src_block) is ConditionalBreakNode:
            # at this point ConditionalBreakNode stores a claripy AST
            bool_var = src_block.condition
            if src_block.target == dst_block.addr:
                return bool_var
            else:
                return claripy.Not(bool_var)

        if type(src_block) is GraphRegion:
            return claripy.true

        last_stmt = self.get_last_statement(src_block)

        if last_stmt is None:
            return claripy.true
        if type(last_stmt) is ailment.Stmt.Jump:
            if isinstance(last_stmt.target, ailment.Expr.Const):
                return claripy.true
            # indirect jump
            target_ast = self.claripy_ast_from_ail_condition(last_stmt.target)
            return target_ast == dst_block.addr
        if type(last_stmt) is ailment.Stmt.ConditionalJump:
            bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition)
            if last_stmt.true_target.value == dst_block.addr:
                return bool_var
            else:
                return claripy.Not(bool_var)

        return claripy.true
Example #3
0
    def _remove_redundant_terms(cond):
        """
        Extract all terms and test for each term if its truism impacts the truism of the entire condition. If not, the
        term is redundant and can be replaced with a True.
        """

        all_terms = set()
        for term in ConditionProcessor._extract_terms(cond):
            if term not in all_terms:
                all_terms.add(term)

        negations = {}
        to_skip = set()
        all_terms_without_negs = set()
        for term in all_terms:
            if term in to_skip:
                continue
            neg = claripy.Not(term)
            if neg in all_terms:
                negations[term] = neg
                to_skip.add(neg)
                all_terms_without_negs.add(term)
            else:
                all_terms_without_negs.add(term)

        solver = claripy.SolverCacheless()
        for term in all_terms_without_negs:
            neg = negations.get(term, None)

            replaced_with_true = ConditionProcessor._replace_term_in_ast(
                cond, term, claripy.true, neg, claripy.false)
            sat0 = solver.satisfiable(extra_constraints=(
                cond,
                claripy.Not(replaced_with_true),
            ))
            sat1 = solver.satisfiable(extra_constraints=(
                claripy.Not(cond),
                replaced_with_true,
            ))
            if sat0 or sat1:
                continue

            replaced_with_false = ConditionProcessor._replace_term_in_ast(
                cond, term, claripy.false, neg, claripy.true)
            sat0 = solver.satisfiable(extra_constraints=(
                cond,
                claripy.Not(replaced_with_false),
            ))
            sat1 = solver.satisfiable(extra_constraints=(
                claripy.Not(cond),
                replaced_with_false,
            ))
            if sat0 or sat1:
                continue

            # TODO: Finish the implementation
            print(term, "is redundant")
    def _relax_offending_constraint(self, c):
        arg0 = c.args[0]
        arg00 = arg0.args[0]
        t_byte = arg00.args[1]
        arg01 = arg0.args[1]
        arg1 = c.args[1]

        diff = arg1 - arg01
        diff_val = self.pred_state.se.eval(diff)

        up_l = claripy.And(t_byte >= ord('A'), t_byte <= ord('Z'))
        low_l = claripy.And(t_byte >= ord('a'), t_byte <= ord('z'))
        num = claripy.And(t_byte >= ord('0'), t_byte <= ord('9'))
        plus = t_byte == ord('+')
        slash = t_byte == ord('/')
        c1 = claripy.Or(up_l, low_l, num, plus, slash)

        if (diff_val >= ord('A') and diff_val <= ord('Z')) or \
           (diff_val >= ord('a') and diff_val <= ord('z')) or \
           (diff_val >= ord('0') and diff_val <= ord('9')) or \
           diff_val == ord('+') or diff_val == ord('/'):

            return c1

        return claripy.Not(c1)
Example #5
0
def complement(BV, se, resultSe=None):
    #replace_dict isn't working properly... defined my own.
    #returns a copy with key ASTs in dictionary replaced by value ASTs from dictionary.
    #make sure keys and values are disjunct and the values don't already appear in the BV.
    def replace_dict(BV, dictionary):
        replaced = BV.replace_dict({"": ""
                                    })  #makes a copy, doesn't replace anything
        for key in dictionary:
            replaced = replaced.replace(key, dictionary[key])
        return replaced

    resultSe = se if resultSe == None else resultSe
    #complement = claripy.BVS('complement_(%s)_' % (bvName), BV.length)
    varDict = {}
    #    var = enumerate(BV.variables)[0]
    #    actualVar = ta.stringToVar(var,se.constraints)
    #    newvars[actualVar] = claripy.BVS("partial_complement_(%s)" % var, BV.length)
    #    complement = BV.replace(actualVar, newvars[actualVar])
    BVconstraints = claripy.true
    for var in list(BV.variables):
        actualVar = ta.stringToVar(var, se.constraints)
        varDict[actualVar] = claripy.BVS("partial_complement_(%s)" % var,
                                         BV.length)
        #complement.replace(ta.stringToVar(var,se.constraints), newvars[actualVar])
    complement = replace_dict(BV, varDict)
    for c in se.constraints:
        if not c.variables.isdisjoint(BV.variables):
            BVconstraints = claripy.And(BVconstraints,
                                        replace_dict(c, varDict))
    print(BVconstraints)
    resultSe.add(claripy.Not(BVconstraints))
    return complement
Example #6
0
    def _make_ites(self, seq):

        # search for a == ^a pairs

        while True:
            for node_0 in seq.nodes:
                if not type(node_0) is CodeNode:
                    continue
                rcond_0 = node_0.reaching_condition
                if rcond_0 is None:
                    continue
                for node_1 in seq.nodes:
                    if not type(node_1) is CodeNode:
                        continue
                    if node_0 is node_1:
                        continue
                    rcond_1 = node_1.reaching_condition
                    if rcond_1 is None:
                        continue
                    cond_ = claripy.simplify(claripy.Not(rcond_0) == rcond_1)
                    if claripy.is_true(cond_):
                        # node_0 and node_1 should be structured using an if-then-else
                        self._make_ite(seq, node_0, node_1)
                        break
            else:
                break

        # make all conditionally-reachable nodes a ConditionNode
        for i in range(len(seq.nodes)):
            node = seq.nodes[i]
            if node.reaching_condition is not None and not claripy.is_true(
                    node.reaching_condition):
                new_node = ConditionNode(node.addr, None,
                                         node.reaching_condition, node, None)
                seq.nodes[i] = new_node
Example #7
0
def test_replacement_solver():
    sr = claripy.ReplacementFrontend(claripy.FullFrontend(claripy.backends.z3))
    x = claripy.BVS('x', 32)
    nose.tools.assert_equals(len(sr.eval(x, 10)), 10)
    sr.result = None
    sr.add_replacement(x, claripy.BVV(0x101, 32))
    nose.tools.assert_items_equal(sr.eval(x, 10), [0x101])

    y = claripy.BVS('y', 32)
    sr.add([y+1 == 200])
    assert (y+1).cache_key in sr._replacements
    assert sr._replacement(y+1) is claripy.BVV(200, 32)

    srb = sr.branch()
    assert len(srb.constraints) == len(sr.constraints)
    assert (y+1).cache_key in sr._replacements
    assert sr._replacement(y+1) is claripy.BVV(200, 32)

    sr = claripy.ReplacementFrontend(claripy.FullFrontend(claripy.backends.z3))
    b = claripy.BoolS('b')
    assert sr._replacement(b) is b
    sr.add(claripy.Not(b))
    assert sr._replacement(b) is claripy.false

    sr = claripy.ReplacementFrontend(claripy.LightFrontend(claripy.backends.vsa), complex_auto_replace=True)
    x = claripy.BVS('x', 64)
    sr.add([x + 8 <= 0xffffffffffffffff])
    sr.add([x + 8 >= 0])
    assert sr._replacement(x) is not x
Example #8
0
    def _make_ites(self, seq):

        # search for a == ^a pairs

        while True:
            break_hard = False
            for i in range(len(seq.nodes)):
                node_0 = seq.nodes[i]
                if not type(node_0) is CodeNode:
                    continue
                rcond_0 = node_0.reaching_condition
                if rcond_0 is None:
                    continue
                if claripy.is_true(rcond_0) or claripy.is_false(rcond_0):
                    continue
                for j in range(i + 1, len(seq.nodes)):
                    node_1 = seq.nodes[j]
                    if not type(node_1) is CodeNode:
                        continue
                    if node_0 is node_1:
                        continue
                    rcond_1 = node_1.reaching_condition
                    if rcond_1 is None:
                        continue
                    cond_ = claripy.simplify(claripy.Not(rcond_0) == rcond_1)
                    if claripy.is_true(cond_):
                        # node_0 and node_1 should be structured using an if-then-else
                        self._make_ite(seq, node_0, node_1)
                        break_hard = True
                        break
                if break_hard:
                    break
            else:
                break
Example #9
0
    def _extract_predicate(self, src_block, dst_block):

        if type(src_block) is ConditionalBreakNode:
            # bool_var = self._bool_variable_from_ail_condition(src_block.condition)
            # if src_block.target == dst_block.addr:
            #     return bool_var
            # else:
            #     return claripy.Not(bool_var)
            if src_block.target == dst_block.addr:
                return claripy.false
            else:
                return claripy.true

        if type(src_block) is GraphRegion:
            return claripy.true

        last_stmt = self._get_last_statement(src_block)

        if last_stmt is None:
            return claripy.true
        if type(last_stmt) is ailment.Stmt.Jump:
            return claripy.true
        if type(last_stmt) is ailment.Stmt.ConditionalJump:
            bool_var = self._bool_variable_from_ail_condition(last_stmt.condition)
            if last_stmt.true_target.value == dst_block.addr:
                return bool_var
            else:
                return claripy.Not(bool_var)

        return claripy.true
Example #10
0
    def _handle_Condition(self, node, **kwargs):

        true_node = self._walker._handle(node.true_node)
        false_node = self._walker._handle(node.false_node)

        if true_node is None and false_node is None:
            # empty node
            return None
        if true_node is None and false_node is not None and self._claripy_ast_conditions:
            # swap them
            return ConditionNode(node.addr,
                                 node.reaching_condition,
                                 ConditionProcessor.simplify_condition(
                                     claripy.Not(node.condition)),
                                 false_node,
                                 false_node=None)
        if self._claripy_ast_conditions \
                and claripy.is_true(node.condition) \
                and node.true_node is not None and node.false_node is None:
            return node.true_node
        return ConditionNode(node.addr,
                             node.reaching_condition,
                             node.condition,
                             true_node,
                             false_node=false_node)
Example #11
0
def test_replacement_solver():
    sr = claripy.SolverReplacement()
    x = claripy.BVS('x', 32)
    nose.tools.assert_equal(len(sr.eval(x, 10)), 10)
    sr.add_replacement(x, claripy.BVV(0x101, 32))
    nose.tools.assert_equal(sr.eval(x, 10), (0x101, ))

    y = claripy.BVS('y', 32)
    sr.add([y + 1 == 200])
    assert (y + 1).cache_key in sr._replacements
    assert sr._replacement(y + 1) is claripy.BVV(200, 32)

    srb = sr.branch()
    assert len(srb.constraints) == len(sr.constraints)  #pylint:disable=no-member
    assert (y + 1).cache_key in sr._replacements
    assert sr._replacement(y + 1) is claripy.BVV(200, 32)

    sr = claripy.SolverReplacement()
    b = claripy.BoolS('b')
    assert sr._replacement(b) is b
    sr.add(claripy.Not(b))
    assert sr._replacement(b) is claripy.false

    sr = claripy.SolverReplacement(claripy.SolverVSA(),
                                   complex_auto_replace=True)
    x = claripy.BVS('x', 64)
    sr.add([x + 8 <= 0xffffffffffffffff])
    sr.add([x + 8 >= 0])
    assert sr._replacement(x) is not x
Example #12
0
    def _revert_short_circuit_conditions(cond):

        # revert short-circuit conditions
        # !A||(A&&!B) ==> !(A&&B)

        if cond.op != "Or":
            return cond

        if len(cond.args) == 1:
            # redundant operator. get rid of it
            return cond.args[0]

        or_arg0, or_arg1 = cond.args[:2]
        if or_arg1.op == 'And':
            pass
        elif or_arg0.op == 'And':
            or_arg0, or_arg1 = or_arg1, or_arg0
        else:
            return cond

        not_a = or_arg0
        solver = claripy.SolverCacheless()

        if not_a.variables == or_arg1.args[0].variables:
            solver.add(not_a == or_arg1.args[0])
            not_b = or_arg1.args[1]
        elif not_a.variables == or_arg1.args[1].variables:
            solver.add(not_a == or_arg1.args[1])
            not_b = or_arg1.args[0]
        else:
            return cond

        if not solver.satisfiable():
            # found it!
            b = claripy.Not(not_b)
            a = claripy.Not(not_a)
            if len(cond.args) <= 2:
                return claripy.Not(claripy.And(a, b))
            else:
                return claripy.Or(claripy.Not(claripy.And(a, b)),
                                  *cond.args[2:])
        else:
            return cond
Example #13
0
    def _extract_predicate(self, src_block, dst_block, edge_type):

        if edge_type == 'exception':
            # TODO: THIS IS ABSOLUTELY A HACK. AT THIS MOMENT YOU SHOULD NOT ATTEMPT TO MAKE SENSE OF EXCEPTION EDGES.
            self.EXC_COUNTER += 1
            return self.claripy_ast_from_ail_condition(
                ailment.Expr.BinaryOp(None, 'CmpEQ', (ailment.Expr.Register(
                    0x400000 + self.EXC_COUNTER, None, self.EXC_COUNTER,
                    64), ailment.Expr.Const(None, None, self.EXC_COUNTER, 64)),
                                      False))

        if type(src_block) is ConditionalBreakNode:
            # at this point ConditionalBreakNode stores a claripy AST
            bool_var = src_block.condition
            if src_block.target == dst_block.addr:
                return bool_var
            else:
                return claripy.Not(bool_var)

        if type(src_block) is GraphRegion:
            return claripy.true

        last_stmt = self.get_last_statement(src_block)

        if last_stmt is None:
            return claripy.true
        if type(last_stmt) is ailment.Stmt.Jump:
            if isinstance(last_stmt.target, ailment.Expr.Const):
                return claripy.true
            # indirect jump
            target_ast = self.claripy_ast_from_ail_condition(last_stmt.target)
            return target_ast == dst_block.addr
        if type(last_stmt) is ailment.Stmt.ConditionalJump:
            bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition)
            if isinstance(last_stmt.true_target, ailment.Expr.Const
                          ) and last_stmt.true_target.value == dst_block.addr:
                return bool_var
            else:
                return claripy.Not(bool_var)

        return claripy.true
Example #14
0
    def _extract_predicate(self, src_block, dst_block):
        last_stmt = self._get_last_statement(src_block)

        if type(last_stmt) is ailment.Stmt.ConditionalJump:
            bool_var = self._bool_variable_from_ail_condition(src_block, last_stmt.condition)
            if last_stmt.true_target.value == dst_block.addr:
                return bool_var
            else:
                return claripy.Not(bool_var)

        else:
            raise NotImplementedError()
Example #15
0
    def _bool_variable_from_ail_condition(self, condition):

        # Unpack a condition all the way to the leaves

        _mapping = {
            'LogicalAnd':
            lambda expr, conv: claripy.And(conv(expr.operands[0]),
                                           conv(expr.operands[1])),
            'LogicalOr':
            lambda expr, conv: claripy.Or(conv(expr.operands[0]),
                                          conv(expr.operands[1])),
            'CmpEQ':
            lambda expr, conv: conv(expr.operands[0]) == conv(expr.operands[1]
                                                              ),
            'CmpLE':
            lambda expr, conv: conv(expr.operands[0]) <= conv(expr.operands[1]
                                                              ),
            'Not':
            lambda expr, conv: claripy.Not(conv(expr.operand)),
            'Xor':
            lambda expr, conv: conv(expr.operands[0]) ^ conv(expr.operands[1]),
        }

        if isinstance(condition, (ailment.Expr.Load, ailment.Expr.Register,
                                  ailment.Expr.DirtyExpression)):
            var = claripy.BVS('ailexpr_%s' % repr(condition),
                              condition.bits,
                              explicit_name=True)
            self._condition_mapping[var] = condition
            return var
        elif isinstance(condition, ailment.Expr.Convert):
            # convert is special. if it generates a 1-bit variable, it should be treated as a BVS
            if condition.to_bits == 1:
                var = claripy.BoolS('ailcond_%s' % repr(condition),
                                    explicit_name=True)
            else:
                var = claripy.BVS('ailexpr_%s' % repr(condition),
                                  condition.to_bits,
                                  explicit_name=True)
            self._condition_mapping[var] = condition
            return var
        elif isinstance(condition, ailment.Expr.Const):
            var = claripy.BVV(condition.value, condition.bits)
            return var

        lambda_expr = _mapping.get(condition.op, None)
        if lambda_expr is None:
            raise NotImplementedError(
                "Unsupported AIL expression operation %s. Consider implementing."
                % condition.op)
        return lambda_expr(condition, self._bool_variable_from_ail_condition)
Example #16
0
    def _refine_loop_dowhile(loop_node):

        if loop_node.sort == 'while' and loop_node.condition is None:
            # it's an endless loop
            last_node = loop_node.sequence_node.nodes[-1]
            if type(last_node) is ConditionalBreakNode:
                while_cond = ConditionProcessor.simplify_condition(claripy.Not(last_node.condition))
                new_seq = loop_node.sequence_node.copy()
                new_seq.nodes = new_seq.nodes[:-1]
                new_loop_node = LoopNode('do-while', while_cond, new_seq)

                return True, new_loop_node

        return False, loop_node
Example #17
0
    def _handleLoadGWithPossibleForwarding(self, state, successors, stmt):
        # Like for WrTmpLoads, we also duplicate the processing for LoadG's ourselves, because we potentially need to fork during load processing
        # this is again basically an inlined version of what goes on in angr for a LoadG, patched to handle possible forwarding
        with state.history.subscribe_actions() as addr_deps:
            addr = self.handle_expression(state, stmt.addr)
        with state.history.subscribe_actions() as alt_deps:
            alt = self.handle_expression(state, stmt.alt)
        with state.history.subscribe_actions() as guard_deps:
            guard = self.handle_expression(state, stmt.guard)
        if guard is not None and state.solver.satisfiable(extra_constraints=[claripy.Not(guard)]):
            raise ValueError("not implemented yet: conditional load with condition that could be false")

        read_type, converted_type = stmt.cvt_types
        read_size_bits = pyvex.const.get_type_size(read_type)
        converted_size_bits = pyvex.const.get_type_size(converted_type)
        read_size = read_size_bits // state.arch.byte_width

        results = performLoadWithPossibleForwarding(state, addr, read_size, load_endness=stmt.end)

        for (l_state, l_value) in results:
            if read_size_bits == converted_size_bits:
                converted_expr = l_value
            elif "S" in stmt.cvt:
                converted_expr = l_value.sign_extend(converted_size_bits - read_size_bits)
            elif "U" in stmt.cvt:
                converted_expr = l_value.zero_extend()
            else:
                raise SimStatementError("Unrecognized IRLoadGOp %s!" % stmt.cvt)
            l_value = l_state.solver.If(guard != 0, converted_expr, alt)
            l_state.scratch.store_tmp(stmt.dst, l_value, deps=addr_deps + alt_deps + guard_deps)
            if angr.options.TRACK_MEMORY_ACTIONS in l_state.options:
                data_ao = SimActionObject(converted_expr)
                alt_ao = SimActionObject(alt, deps=alt_deps, state=l_state)
                addr_ao = SimActionObject(addr, deps=addr_deps, state=l_state)
                guard_ao = SimActionObject(guard, deps=guard_deps, state=l_state)
                size_ao = SimActionObject(converted_size_bits)
                r = SimActionData(l_state, l_state.memory.id, SimActionData.READ, addr=addr_ao, data=data_ao, condition=guard_ao, size=size_ao, fallback=alt_ao)
                l_state.history.add_action(r)

            # for comments on the below, see comments in our handling of WrTmp loads above
            if l_state is not state:
                (next_instr_addr, next_instr_stmt_idx) = nextInstruction(state.scratch.irsb, stmt)
                self._handle_irsb(l_state, successors, l_state.scratch.irsb, state.scratch.stmt_idx+1, next_instr_stmt_idx-1 if next_instr_stmt_idx is not None else None, None)

                l.debug("time {}: forking for misforwarding on a load of addr {}".format(state.spec.ins_executed, addr))
                target = next_instr_addr if next_instr_addr is not None else self.handle_expression(l_state, l_state.scratch.irsb.next)  # if next_instr_addr is None, then target the first instruction of the next irsb
                jumpkind = 'Ijk_Boring'  # seems like a reasonable choice? what is this used for?
                guard = claripy.BVV(1, 1)  # boolean True
                successors.add_successor(l_state, target, guard, jumpkind, add_guard=False, exit_stmt_idx=None, exit_ins_addr=None)
Example #18
0
    def _refine_loop_while(loop_node):

        if loop_node.sort == 'while' and loop_node.condition is None:
            # it's an endless loop
            first_node = loop_node.sequence_node.nodes[0]
            if type(first_node) is CodeNode:
                first_node = first_node.node
            if type(first_node) is ConditionalBreakNode:
                while_cond = ConditionProcessor.simplify_condition(claripy.Not(first_node.condition))
                new_seq = loop_node.sequence_node.copy()
                new_seq.nodes = new_seq.nodes[1:]
                new_loop_node = LoopNode('while', while_cond, new_seq, addr=loop_node.addr)

                return True, new_loop_node

        return False, loop_node
Example #19
0
    def _make_ites(self, seq):

        # search for a == ^a pairs

        while True:
            for node_0 in seq.nodes:
                if not type(node_0) is CodeNode:
                    continue
                rcond_0 = node_0.reaching_condition
                if rcond_0 is None:
                    continue
                for node_1 in seq.nodes:
                    if not type(node_1) is CodeNode:
                        continue
                    if node_0 is node_1:
                        continue
                    rcond_1 = node_1.reaching_condition
                    if rcond_1 is None:
                        continue
                    cond_ = claripy.simplify(claripy.Not(rcond_0) == rcond_1)
                    if claripy.is_true(cond_):
                        # node_0 and node_1 should be structured using an if-then-else
                        self._make_ite(seq, node_0, node_1)
                        break
            else:
                break

        # make all conditionally-reachable nodes ConditionNodes
        for i in range(len(seq.nodes)):
            node = seq.nodes[i]
            if node.reaching_condition is not None and not claripy.is_true(
                    node.reaching_condition):
                if isinstance(node.node, ConditionalBreakNode):
                    # Put conditions together and simplify them
                    cond = claripy.And(
                        node.reaching_condition,
                        self._bool_variable_from_ail_condition(
                            node.node.condition))
                    new_node = CodeNode(
                        ConditionalBreakNode(node.node.addr, cond,
                                             node.node.target), None)
                else:
                    new_node = ConditionNode(node.addr, None,
                                             node.reaching_condition, node,
                                             None)
                seq.nodes[i] = new_node
Example #20
0
    def _perform_vex_stmt_Exit(self, guard, target, jumpkind):
        cont_state = None
        exit_state = None
        guard = guard != 0

        if o.COPY_STATES not in self.state.options:
            # very special logic to try to minimize copies
            # first, check if this branch is impossible
            if guard.is_false():
                cont_state = self.state
            elif o.LAZY_SOLVES not in self.state.options and not self.state.solver.satisfiable(
                    extra_constraints=(guard, )):
                cont_state = self.state

            # then, check if it's impossible to continue from this branch
            elif guard.is_true():
                exit_state = self.state
            elif o.LAZY_SOLVES not in self.state.options and not self.state.solver.satisfiable(
                    extra_constraints=(claripy.Not(guard), )):
                exit_state = self.state
            else:
                exit_state = self.state.copy()
                cont_state = self.state
        else:
            exit_state = self.state.copy()
            cont_state = self.state

        if exit_state is not None:
            self.successors.add_successor(
                exit_state,
                target,
                guard,
                jumpkind,
                exit_stmt_idx=self.stmt_idx,
                exit_ins_addr=self.state.scratch.ins_addr)

        if cont_state is None:
            raise VEXEarlyExit

        # Do our bookkeeping on the continuing self.state
        cont_condition = ~guard
        cont_state.add_constraints(cont_condition)
        cont_state.scratch.guard = claripy.And(cont_state.scratch.guard,
                                               cont_condition)
Example #21
0
def angr_merge(a, b):
    """
    Merge two states after a branch in the emulated code.

    Since we don't want the virtual instruction pointer to become symbolic,
    we step both states forward until they reach a common point.
    At this point, we merge both of the states into one.

    This function assumes that both states a and b are directly after a branch opcode (0x94a49ff0)
    """
    # extract the condition that caused the branch
    cond = a.history.jump_guards[-1]

    # step forward until we reach a common point
    while get_ip(a) != get_ip(b):
        if get_ip(a) < get_ip(b):
            a = angr_until(a, get_ip(b))
            if isinstance(a, bytes):
                return a, b
        else:
            b = angr_until(b, get_ip(a))
            if isinstance(b, bytes):
                return a, b
    print("merge common", a, b, get_ip(a), get_ip(b))

    # simplify before merging
    a.solver.simplify()
    b.solver.simplify()

    # verify that the condition actually separates both states
    if not a.solver.eval(cond) or b.solver.eval(cond):
        print("unexpected symbolic branch")
        print(cond)
        return

    # merge with our custom merge condition
    m = a.merge(b, merge_conditions=[
        [cond],
        [claripy.Not(cond)],
    ])[0]

    # simplify before returning the merged state
    m.solver.simplify()
    return m
Example #22
0
    def _handle_statement(self, state, successors, stmt):
        """
        This function receives an initial state and imark and processes a list of pyvex.IRStmts
        It annotates the request with a final state, last imark, and a list of SimIRStmts
        """
        if type(stmt) == pyvex.IRStmt.IMark:
            ins_addr = stmt.addr + stmt.delta
            state.scratch.ins_addr = ins_addr

            # Raise an exception if we're suddenly in self-modifying code
            for subaddr in xrange(stmt.len):
                if subaddr + stmt.addr in state.scratch.dirty_addrs:
                    raise SimReliftException(state)
            state._inspect('instruction', BP_AFTER)

            l.debug("IMark: %#x", stmt.addr)
            state.scratch.num_insns += 1
            state._inspect('instruction', BP_BEFORE, instruction=ins_addr)

        # process it!
        s_stmt = translate_stmt(stmt, state)
        if s_stmt is not None:
            state.history.extend_actions(s_stmt.actions)

        # for the exits, put *not* taking the exit on the list of constraints so
        # that we can continue on. Otherwise, add the constraints
        if type(stmt) == pyvex.IRStmt.Exit:
            l.debug("%s adding conditional exit", self)

            # Produce our successor state!
            # Let SimSuccessors.add_successor handle the nitty gritty details
            exit_state = state.copy()
            successors.add_successor(exit_state,
                                     s_stmt.target,
                                     s_stmt.guard,
                                     s_stmt.jumpkind,
                                     exit_stmt_idx=state.scratch.stmt_idx,
                                     exit_ins_addr=state.scratch.ins_addr)

            # Do our bookkeeping on the continuing state
            cont_condition = claripy.Not(s_stmt.guard)
            state.add_constraints(cont_condition)
            state.scratch.guard = claripy.And(state.scratch.guard,
                                              cont_condition)
Example #23
0
def test_bool_simplification():
    def assert_correct(a, b):
        nose.tools.assert_true(
            claripy.backends.z3.identical(claripy.simplify(a), b))

    a, b, c = (claripy.BoolS(name) for name in ('a', 'b', 'c'))

    assert_correct(claripy.And(a, claripy.Not(a)), claripy.false)
    assert_correct(claripy.Or(a, claripy.Not(a)), claripy.true)

    complex_true_expression = claripy.Or(
        claripy.And(a, b),
        claripy.Or(claripy.And(a, claripy.Not(b)),
                   claripy.And(claripy.Not(a), c)),
        claripy.Or(claripy.And(a, claripy.Not(b)),
                   claripy.And(claripy.Not(a), claripy.Not(c))))
    assert_correct(complex_true_expression, claripy.true)
        def inspect_func(state):

            # import ipdb; ipdb.set_trace()
            sc = state.added
            if not state.satisfiable() or len(sc) == 0:
                return

            # constraints from lava_get
            if state.addr in [0x8049554, 0x804955a]:
                return

            # crashing addr too ?
            if state.addr in [0x9067084]:
                return

            # crashing addr
            if state.addr in crashing_block.instruction_addrs:
                return
            # if state.addr in [0x804cd8e, 0x804cde1]:
            # return

            # if 0x9067084 == state.addr:
            #     import ipdb; ipdb.set_trace()

            # constraints from fileno
            # if state.addr in [0x9065510]:
            #     return

            # import ipdb; ipdb.set_trace()
            # print("[%s]: %s" % (hex(state.addr), str(sc)))

            # if state.addr in [0x804b32d, 0x804c778]:
            if state.addr in [0x804b32d]:
                self.offending_constraints.extend(sc)
                return

            if state.addr in self.tprogram.c_all_instr_addrs:
                assert (len(sc) == 1)
                # print ("============== NEG ==============")
                self.collected_constraints.append(claripy.Not(sc[0]))
            else:
                self.collected_constraints.extend(sc)
Example #25
0
 def sympy_expr_to_claripy_ast(expr, memo: Dict):
     if expr.is_Symbol:
         return memo[expr]
     if isinstance(expr, sympy.Or):
         return claripy.Or(
             *(ConditionProcessor.sympy_expr_to_claripy_ast(arg, memo)
               for arg in expr.args))
     if isinstance(expr, sympy.And):
         return claripy.And(
             *(ConditionProcessor.sympy_expr_to_claripy_ast(arg, memo)
               for arg in expr.args))
     if isinstance(expr, sympy.Not):
         return claripy.Not(
             ConditionProcessor.sympy_expr_to_claripy_ast(
                 expr.args[0], memo))
     if isinstance(expr, sympy.logic.boolalg.BooleanTrue):
         return claripy.true
     if isinstance(expr, sympy.logic.boolalg.BooleanFalse):
         return claripy.false
     raise RuntimeError("Unreachable reached")
Example #26
0
    def _handle_Condition(self, node, **kwargs):

        # delayed import
        from .structurer import Structurer  # pylint:disable=import-outside-toplevel

        true_node = self._walker._handle(node.true_node)
        false_node = self._walker._handle(node.false_node)

        if true_node is None and false_node is None:
            # empty node
            return None
        if true_node is None and false_node is not None:
            # swap them
            return ConditionNode(node.addr,
                                 node.reaching_condition,
                                 Structurer._simplify_condition(
                                     claripy.Not(node.condition)),
                                 false_node,
                                 false_node=None)
        return ConditionNode(node.addr,
                             node.reaching_condition,
                             node.condition,
                             true_node,
                             false_node=false_node)
Example #27
0
    def claripy_ast_from_ail_condition(self, condition) -> claripy.ast.Base:

        # Unpack a condition all the way to the leaves
        if isinstance(condition, claripy.ast.Base):  # pylint:disable=isinstance-second-argument-not-valid-type
            return condition

        def _op_with_unified_size(op, conv, operand0, operand1):
            # ensure operand1 is of the same size as operand0
            if isinstance(operand1, ailment.Expr.Const):
                # amazing - we do the eazy thing here
                return op(conv(operand0), operand1.value)
            if operand1.bits == operand0.bits:
                return op(conv(operand0), conv(operand1))
            # extension is required
            assert operand1.bits < operand0.bits
            operand1 = ailment.Expr.Convert(None, operand1.bits, operand0.bits, False, operand1)
            return op(conv(operand0), conv(operand1))

        _mapping = {
            'LogicalAnd': lambda expr, conv: claripy.And(conv(expr.operands[0]), conv(expr.operands[1])),
            'LogicalOr': lambda expr, conv: claripy.Or(conv(expr.operands[0]), conv(expr.operands[1])),
            'CmpEQ': lambda expr, conv: conv(expr.operands[0]) == conv(expr.operands[1]),
            'CmpNE': lambda expr, conv: conv(expr.operands[0]) != conv(expr.operands[1]),
            'CmpLE': lambda expr, conv: conv(expr.operands[0]) <= conv(expr.operands[1]),
            'CmpLEs': lambda expr, conv: claripy.SLE(conv(expr.operands[0]), conv(expr.operands[1])),
            'CmpLT': lambda expr, conv: conv(expr.operands[0]) < conv(expr.operands[1]),
            'CmpLTs': lambda expr, conv: claripy.SLT(conv(expr.operands[0]), conv(expr.operands[1])),
            'CmpGE': lambda expr, conv: conv(expr.operands[0]) >= conv(expr.operands[1]),
            'CmpGEs': lambda expr, conv: claripy.SGE(conv(expr.operands[0]), conv(expr.operands[1])),
            'CmpGT': lambda expr, conv: conv(expr.operands[0]) > conv(expr.operands[1]),
            'CmpGTs': lambda expr, conv: claripy.SGT(conv(expr.operands[0]), conv(expr.operands[1])),
            'Add': lambda expr, conv: conv(expr.operands[0]) + conv(expr.operands[1]),
            'Sub': lambda expr, conv: conv(expr.operands[0]) - conv(expr.operands[1]),
            'Mul': lambda expr, conv: conv(expr.operands[0]) * conv(expr.operands[1]),
            'Not': lambda expr, conv: claripy.Not(conv(expr.operand)),
            'Xor': lambda expr, conv: conv(expr.operands[0]) ^ conv(expr.operands[1]),
            'And': lambda expr, conv: conv(expr.operands[0]) & conv(expr.operands[1]),
            'Or': lambda expr, conv: conv(expr.operands[0]) | conv(expr.operands[1]),
            'Shr': lambda expr, conv: _op_with_unified_size(claripy.LShR, conv, expr.operands[0], expr.operands[1]),
            'Shl': lambda expr, conv: _op_with_unified_size(operator.lshift, conv, expr.operands[0], expr.operands[1]),
            'Sar': lambda expr, conv: _op_with_unified_size(operator.rshift, conv, expr.operands[0], expr.operands[1]),
        }

        if isinstance(condition, (ailment.Expr.Load, ailment.Expr.DirtyExpression, ailment.Expr.BasePointerOffset,
                                  ailment.Expr.ITE, ailment.Stmt.Call)):
            var = claripy.BVS('ailexpr_%s' % repr(condition), condition.bits, explicit_name=True)
            self._condition_mapping[var.args[0]] = condition
            return var
        elif isinstance(condition, ailment.Expr.Register):
            var = claripy.BVS('ailexpr_%s-%d' % (repr(condition), condition.idx), condition.bits, explicit_name=True)
            self._condition_mapping[var.args[0]] = condition
            return var
        elif isinstance(condition, ailment.Expr.Convert):
            # convert is special. if it generates a 1-bit variable, it should be treated as a BVS
            if condition.to_bits == 1:
                var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
                name = 'ailcond_Conv(%d->%d, %s)' % (condition.from_bits, condition.to_bits, repr(var_))
                var = claripy.BoolS(name, explicit_name=True)
            else:
                var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
                name = 'ailexpr_Conv(%d->%d, %s)' % (condition.from_bits, condition.to_bits, repr(var_))
                var = claripy.BVS(name, condition.to_bits, explicit_name=True)
            self._condition_mapping[var.args[0]] = condition
            return var
        elif isinstance(condition, ailment.Expr.Const):
            var = claripy.BVV(condition.value, condition.bits)
            return var
        elif isinstance(condition, ailment.Expr.Tmp):
            l.warning("Left-over ailment.Tmp variable %s.", condition)
            if condition.bits == 1:
                var = claripy.BoolV('ailtmp_%d' % condition.tmp_idx)
            else:
                var = claripy.BVS('ailtmp_%d' % condition.tmp_idx, condition.bits, explicit_name=True)
            self._condition_mapping[var.args[0]] = condition
            return var

        lambda_expr = _mapping.get(condition.verbose_op, None)
        if lambda_expr is None:
            raise NotImplementedError("Unsupported AIL expression operation %s. Consider implementing." % condition.op)
        r = lambda_expr(condition, self.claripy_ast_from_ail_condition)
        if r is NotImplemented:
            r = claripy.BVS("ailexpr_%r" % condition, condition.bits, explicit_name=True)
            self._condition_mapping[r.args[0]] = condition
        else:
            # don't lose tags
            r = r.annotate(TagsAnnotation(**condition.tags))
        return r
Example #28
0
    def perform_opaqueness_checks(self, con_addr_asts, branch_addr, succ_addr,
                                  pos_unsat_addr, state,
                                  atoi_added_constraints):
        # Phase 1: invariant check
        solver = claripy.Solver()
        solver.add(claripy.Not(con_addr_asts[branch_addr]))

        # If the negation is unsatisfiable, the predicate is a tautology!
        if not solver.satisfiable():
            cons_str = self._pretty_constraint_str(con_addr_asts[branch_addr])
            self.log_signal.emit(
                "{:x}: Invariant OP found! succ {:x} unreachable.\n  Constraint: {}\n"
                .format(branch_addr, pos_unsat_addr, cons_str))
            self.result_signal.emit(self.ida_func.startEA,
                                    [(branch_addr, 0, cons_str)],
                                    [(branch_addr, succ_addr, pos_unsat_addr)],
                                    [], False)
            return True

        # Phase 2: contextual check
        # predicate p_n is not a tautology, but it might still be
        # contextually opaque, i.e.: (p_1 && p_2 && ...) -> p_n
        solver = claripy.Solver()  # fresh solver

        # This is a bit ugly
        # sorted list of conditions, filtered on addr <= branch_addr
        prev_conds = list(
            zip(*sorted(filter(lambda (x, _): x <= branch_addr,
                               con_addr_asts.items()),
                        key=operator.itemgetter(0)))[1])

        # If no previous conditions AND no extra added constraints
        if len(
                prev_conds
        ) < 1 and self.ida_func.startEA not in self.plugin.extra_constraints:
            self.log_signal.emit(
                "{:x}: No previous conditions, can't be contextual.\n".format(
                    branch_addr))
            return False

        # Check if AND(prev_conds[:-1]) -> prev_conds[-1]
        cond_conj = claripy.And(*(prev_conds[:-1] +
                                  [claripy.Not(prev_conds[-1])]))
        self.log_signal.emit("prev_conds[:-1] = {}".format(prev_conds[:-1]))
        self.log_signal.emit("claripy.Not(prev_conds[-1]) = {}".format(
            claripy.Not(prev_conds[-1])))

        # Make sure to add any extra user-added constraints to the solver
        self.add_extra_constraints(solver, state)

        # If we have extra atoi-constraints, add to conjunction
        for con in atoi_added_constraints:
            self.log_signal.emit(
                "Adding extra atoi constraint: {}".format(con))
            cond_conj = claripy.And(cond_conj, con)

        solver.add(cond_conj)

        # Is it satisfiable?
        self.log_signal.emit("Solver constraints: {}".format(
            solver.constraints))
        if not solver.satisfiable():
            #set_block_color(pos_unsat_addr, 0xFFFF00)
            self.log_signal.emit("cond_conj = {}".format(cond_conj))
            cons_str = self._pretty_constraint_str(cond_conj)
            self.log_signal.emit(
                "{:x}: Contextual OP found! succ {:x} unreachable.\n  Constraint: {}\n"
                .format(branch_addr, pos_unsat_addr, cons_str))
            self.result_signal.emit(self.ida_func.startEA,
                                    [(branch_addr, 1, cons_str)], [],
                                    [(branch_addr, succ_addr, pos_unsat_addr)],
                                    False)
            return True
        else:
            self.log_signal.emit(
                "{:x}: Not a contextual OP, context: {}.\n".format(
                    branch_addr, prev_conds))
            return False
Example #29
0
    def check_opacity(self, branches):
        # Shorthands
        proj = self.plugin.angr_proj

        simuvex_options_add = {
            simuvex.o.CONSTRAINT_TRACKING_IN_SOLVER,
            #simuvex.o.OPTIMIZE_IR,
            simuvex.o.SIMPLIFY_EXPRS,
            simuvex.o.SIMPLIFY_CONSTRAINTS,
            simuvex.o.SIMPLIFY_MEMORY_WRITES,
            simuvex.o.SIMPLIFY_REGISTER_WRITES
        }
        simuvex_options_remove = {
            simuvex.o.
            LAZY_SOLVES  # TODO: this needs to be a setting. Removing it usually improves speed by a lot, but sometimes doesn't
        }

        # Make a blank state and path at the start of the current function
        st = proj.factory.blank_state(addr=self.ida_func.startEA,
                                      add_options=simuvex_options_add,
                                      remove_options=simuvex_options_remove)

        # Optionally make global vars symbolic
        self.make_global_vars_symbolic(st)

        # Optionally set up special constraints for atoi-on-string-array
        atoi_added_constraints = []  # Will be filled
        self.setup_argv_atoi_constraints(st, atoi_added_constraints)

        self.add_custom_hooks()

        # Initial path
        path = proj.factory.path(st)

        # Lists of tuples of: (branch_addr, succ_addr, pos_unsat_addr)
        found_any_op = False
        found_op_branches = []

        # TODO: this assumes that `succ_addr` can only be reached through `branch_addr`

        # For every branch given:
        for (branch_addr, succ_addr, pos_unsat_addr) in branches:
            if self.plugin.stop_analysis:
                break

            # If we already found one for this addr in this run, skip
            if branch_addr in found_op_branches:
                continue

            # If the possible unsat successor has other predecessors which are not (possibly backedges), ignore
            # TODO: this is a heuristic! implement actual backedge check
            other_non_backedge_preds_of_unsat_succ = filter(
                lambda p: p < pos_unsat_addr,
                self.ida_func.preds(pos_unsat_addr))
            if len(other_non_backedge_preds_of_unsat_succ) > 1:
                continue

            # Empty any collected atoi extra constraints because
            # this will be filled again by our hooked atoi at the next explore()
            del atoi_added_constraints[:]

            self.log_signal.emit(
                "Looking at branch {:x}...".format(branch_addr))

            # Calculate the 'dominator set' of the succ_addr, which we're looking for
            slice_irsb_addrs = list(self.dominator_set(succ_addr))

            # Explore a pathgroup from the start of the function,
            # ignoring anything in the function, but outside the slice
            pathgroup = proj.factory.path_group(path)

            self.log_signal.emit(
                "Exploring pathgroup to find {:x}...".format(succ_addr))

            # Configure exploration techniques TODO: use Director? or LengthLimiter?
            #et_limiter = angr.exploration_techniques.looplimiter.LoopLimiter(count=1, discard_stash='spinning')
            #pathgroup.use_technique(et_limiter)
            #et_threading = angr.exploration_techniques.looplimiter.Threading(threads=4)
            #pathgroup.use_technique(et_threading)

            # TODO: right now we only avoid unvisited blocks in the current
            # function. Is that correct?
            # TODO: make sure path goes through the parent branch block?
            pathgroup.explore(
                find=lambda p: (
                    (p.addr == succ_addr) or self.plugin.stop_analysis),
                avoid=lambda p:
                (p.addr >= self.ida_func.startEA and p.addr < self.ida_func.
                 endEA and p.addr not in slice_irsb_addrs),
                num_find=1,  # How many paths to try to find at most?
                n=SYMBOLIC_MAX_ITERATIONS  #  Max amount of iterations.
            )

            # If we 'found' a path because of `stop_analysis`, break
            if self.plugin.stop_analysis:
                break

            self.log_signal.emit("Done exploring pathgroup")

            # Merge all found paths # TODO: check if this works well
            # TODO: instead of merging, make sure opacity holds for every path!
            #pathgroup.merge(stash="found")

            if len(pathgroup.found) == 0:
                if len(pathgroup.unsat) > 0 and (
                        pathgroup.unsat[0].addr == succ_addr
                        or pathgroup.unsat[0].addr == pos_unsat_addr):
                    # Deemed unsatisfiable already..
                    path_found = pathgroup.unsat[0]
                else:
                    self.log_signal.emit(
                        "{:x}: No path to branch found... bug? pg = {}, slice_irsb_addrs = {}\n"
                        .format(branch_addr, pathgroup,
                                map(hex, slice_irsb_addrs)))
                    self.plugin.pg = pathgroup  # TODO: remove, for debugging only
                    continue
            else:
                # Only one was found
                path_found = pathgroup.found[0]

            # TODO: actually continue processing here and invert conditions?
            #if path_found.addr == pos_unsat_addr:
            #    continue

            # Grab the conditions along the path
            # Filter out actions where .condition is None for things like the setz instruction
            con_actions = filter(
                lambda x: x.type == 'exit' and x.exit_type == 'conditional' and
                x.condition is not None, list(path_found.actions))

            # Make a list of IDA basic-block addresses hit along the path
            path_addr_trace = filter(
                lambda x: x != None,
                map(lambda x: self.ida_func.addr_to_cb_start_ea(x),
                    list(path_found.addr_trace) + [path_found.addr]))

            # For every added constraint along the path, store it in a dict, mapping
            # the addr of the IDA BB containing the condition to the constraint AST
            con_addr_asts = dict()
            for c in con_actions:
                # The addr of the IDA basic-block that contains the branch
                con_bbl_addr = self.ida_func.addr_to_cb_start_ea(c.ins_addr)

                # TODO: fixme, symbolic target will mess this up
                if not c.target.ast.concrete:
                    continue

                # The (IDA BB) target taken when the condition holds
                con_target_addr = self.ida_func.addr_to_cb_start_ea(
                    c.target.ast.args[0])

                # If that target is not the target that was taken in the path,
                # invert the AST so it always represents the branch that was taken
                # (this is needed because vex IL sometimes flips the original condition)
                if not contains_sublist(path_addr_trace,
                                        [con_bbl_addr, con_target_addr]):
                    con_addr_asts[con_bbl_addr] = claripy.Not(c.condition.ast)
                else:
                    con_addr_asts[con_bbl_addr] = c.condition.ast

            # make sure a condition was actually seen in this block
            if branch_addr not in con_addr_asts:
                self.log_signal.emit(
                    "{:x}: No condition found\n".format(branch_addr))
                self.plugin.pg = pathgroup  # TODO: remove, for debugging only
                continue

            # Perform the actual opaqueness checks!
            found_op = self.perform_opaqueness_checks(
                con_addr_asts,  # The constraint ASTS indexed by basic block addr
                branch_addr,  # The addr of the branching block we're looking at
                succ_addr,  # Non-opaque successor block addr
                pos_unsat_addr,  # Possibly opaque successor block addr
                path_found.state,  # The state of the path that was found
                atoi_added_constraints  # Extra atoi-argv constraints
            )

            # An opaque predicate was found
            if found_op:
                found_op_branches.append(branch_addr)
                found_any_op = True

        return found_any_op
Example #30
0
    def _handle_statement(self, state, successors, stmt):
        """
        This function receives an initial state and imark and processes a list of pyvex.IRStmts
        It annotates the request with a final state, last imark, and a list of SimIRStmts
        """
        if type(stmt) == pyvex.IRStmt.IMark:
            # TODO how much of this could be moved into the imark handler
            ins_addr = stmt.addr + stmt.delta
            state.scratch.ins_addr = ins_addr

            # Raise an exception if we're suddenly in self-modifying code
            for subaddr in range(stmt.len):
                if subaddr + stmt.addr in state.scratch.dirty_addrs:
                    raise SimReliftException(state)
            state._inspect('instruction', BP_AFTER)

            l.debug("IMark: %#x", stmt.addr)
            state.scratch.num_insns += 1
            state._inspect('instruction', BP_BEFORE, instruction=ins_addr)

        # process it!
        try:
            stmt_handler = self.stmt_handlers[stmt.tag_int]
        except IndexError:
            l.error("Unsupported statement type %s", (type(stmt)))
            if o.BYPASS_UNSUPPORTED_IRSTMT not in state.options:
                raise UnsupportedIRStmtError("Unsupported statement type %s" % (type(stmt)))
            state.history.add_event('resilience', resilience_type='irstmt', stmt=type(stmt).__name__, message='unsupported IRStmt')
            return None
        else:
            exit_data = stmt_handler(self, state, stmt)

        # for the exits, put *not* taking the exit on the list of constraints so
        # that we can continue on. Otherwise, add the constraints
        if exit_data is not None:
            l.debug("%s adding conditional exit", self)

            target, guard, jumpkind = exit_data

            # Produce our successor state!
            # Let SimSuccessors.add_successor handle the nitty gritty details

            cont_state = None
            exit_state = None

            if o.COPY_STATES not in state.options:
                # very special logic to try to minimize copies
                # first, check if this branch is impossible
                if guard.is_false():
                    cont_state = state
                elif o.LAZY_SOLVES not in state.options and not state.solver.satisfiable(extra_constraints=(guard,)):
                    cont_state = state

                # then, check if it's impossible to continue from this branch
                elif guard.is_true():
                    exit_state = state
                elif o.LAZY_SOLVES not in state.options and not state.solver.satisfiable(extra_constraints=(claripy.Not(guard),)):
                    exit_state = state
                else:
                    exit_state = state.copy()
                    cont_state = state
            else:
                exit_state = state.copy()
                cont_state = state

            if exit_state is not None:
                successors.add_successor(exit_state, target, guard, jumpkind,
                                         exit_stmt_idx=state.scratch.stmt_idx, exit_ins_addr=state.scratch.ins_addr)

            if cont_state is None:
                return False

            # Do our bookkeeping on the continuing state
            cont_condition = claripy.Not(guard)
            cont_state.add_constraints(cont_condition)
            cont_state.scratch.guard = claripy.And(cont_state.scratch.guard, cont_condition)

        return True