def run(self, this_ref, key_ref): log.debug('Called SimProcedure java.util.Map.containsKey with args: {} {}'.format(this_ref, key_ref)) if this_ref.symbolic: return claripy.BoolS('contains_key') try: this_ref.load_field(self.state, get_map_key(self.state, key_ref), 'java.lang.Object') return claripy.BoolV(1) except (KeyError, AttributeError): return claripy.BoolV(0)
def _store_fully_symbolic(self, address, addresses, size, data, endness, condition): stored_values = [ ] byte_dict = defaultdict(list) max_bytes = data.length//self.state.arch.byte_width if condition is None: condition = claripy.BoolV(True) # chop data into byte-chunks original_values = [self._read_from(a, max_bytes) for a in addresses] if endness == "Iend_LE" or (endness is None and self.endness == "Iend_LE"): original_values = [ ov.reversed for ov in original_values ] data_bytes = data.chop(bits=self.state.arch.byte_width) for a, fv in zip(addresses, original_values): original_bytes = fv.chop(self.state.arch.byte_width) for index, (d_byte, o_byte) in enumerate(zip(data_bytes, original_bytes)): # create a dict of all all possible values for a certain address byte_dict[a+index].append((a, index, d_byte, o_byte)) for byte_addr in sorted(byte_dict.keys()): write_list = byte_dict[byte_addr] # If this assertion fails something is really wrong! assert all(v[3] is write_list[0][3] for v in write_list) conditional_value = write_list[0][3] for a, index, d_byte, o_byte in write_list: # create the ast for each byte conditional_value = self.state.solver.If(self.state.se.And(address == a, size > index, condition), d_byte, conditional_value) stored_values.append(dict(value=conditional_value, addr=byte_addr, size=1)) return stored_values
def _store_symbolic_addr(self, address, addresses, size, data, endness, condition): size = self.state.solver.eval(size) segments = self._get_segments(addresses, size) if condition is None: condition = claripy.BoolV(True) original_values = [ self._read_from(segment['start'], segment['size']) for segment in segments ] if endness == "Iend_LE" or (endness is None and self.endness == "Iend_LE"): original_values = [ ov.reversed for ov in original_values ] stored_values = [] for segment, original_value in zip(segments, original_values): conditional_value = original_value for opt in segment['options']: if endness == "Iend_LE" or (endness is None and self.endness == "Iend_LE"): high = ((opt['idx']+segment['size']) * self.state.arch.byte_width)-1 low = opt['idx']*self.state.arch.byte_width else: high = len(data) - 1 - (opt['idx']*self.state.arch.byte_width) low = len(data) - ((opt['idx']+segment['size']) *self.state.arch.byte_width) data_slice = data[high:low] conditional_value = self.state.solver.If(self.state.solver.And(address == segment['start']-opt['idx'], condition), data_slice, conditional_value) stored_values.append(dict(value=conditional_value, addr=segment['start'], size=segment['size'])) return stored_values
def syscall_hook(state): # FIXME maybe we need to fix transmit/receive to handle huge vals properly # kill path that try to read/write large amounts syscall_name = state.inspect.syscall_name if syscall_name == "transmit": count = state.se.eval(state.regs.edx) if count > 0x10000: state.regs.edx = 0 state.add_constraints(claripy.BoolV(False)) if syscall_name == "receive": count = state.se.eval(state.regs.edx) if count > 0x10000: state.regs.edx = 0 state.add_constraints(claripy.BoolV(False)) if syscall_name == "random": count = state.se.eval(state.regs.ecx) if count > 0x1000: state.regs.ecx = 0 state.add_constraints(claripy.BoolV(False))
def test_concrete(): a = claripy.BVV(10, 32) b = claripy.BoolV(True) #c = claripy.BVS('x', 32) nose.tools.assert_is(type(claripy.backends.concrete.convert(a)), claripy.bv.BVV) nose.tools.assert_is(type(claripy.backends.concrete.convert(b)), bool) a = claripy.BVV(1337, 32) b = a[31:16] c = claripy.BVV(0, 16) nose.tools.assert_is(b, c)
def run(self, this_ref): log.debug( 'Called SimProcedure java.util.Iterator.hasNext with args: {}'. format(this_ref)) if this_ref.symbolic: return claripy.BoolS('iterator.hasNext') iterator_size = this_ref.load_field(self.state, SIZE, 'int') iterator_index = this_ref.load_field(self.state, INDEX, 'int') has_next = self.state.solver.eval( iterator_index) < self.state.solver.eval(iterator_size) return claripy.BoolV(has_next)
def merge_path_to_same_lib_call_together(self): res = dict() for libcall_addr in self.all_lib_calls.keys(): try: res[libcall_addr] = [] all_paths = self.all_lib_calls[libcall_addr] all_formulas = dict() all_cons = [] for path in all_paths: fs, cs, inputs = self.fe.get_formulas_from(path) if cs: all_cons.extend(cs) for f in fs: v_name, ast = f if v_name not in all_formulas: all_formulas[v_name] = [] all_formulas[v_name].append((ast, cs)) if len(all_formulas.keys()) > 0: # here is a libcall with arguments for v_name, fc_list in all_formulas.items(): tmp_f, tmp_cons = merge_fe_formulas( fc_list, self.fe.ptr_size) tmp_f = claripy.simplify(tmp_f) if len(tmp_cons) == 0: # no constraints is equivalent to always True tmp_cons = None else: tmp_cons = claripy.simplify(claripy.Or(*tmp_cons)) if tmp_cons.depth == 1 and tmp_cons.args == ( True, ): tmp_cons = None res[libcall_addr].append((v_name, tmp_f, tmp_cons)) else: # here is a libcall with no argument if len(all_cons) == 0: tmp_cons = claripy.BoolV(True) else: tmp_cons = claripy.simplify(claripy.Or(*all_cons)) res[libcall_addr].append((None, all_paths, tmp_cons)) except Exception: log.error( 'meets error in merge_path_to_same_lib_call_together base_entry=0x%x' % self.base_entry) # if this lib call meets error, we skip merging formulas of it if libcall_addr in res.keys(): res.pop(libcall_addr) return res
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]), 'CmpLT': lambda expr, conv: conv(expr.operands[0]) < conv(expr.operands[1]), 'Add': 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]), 'Shr': lambda expr, conv: claripy.LShR(conv(expr.operands[0]), 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 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) self._condition_mapping[var] = condition 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)
def test_concrete(): a = claripy.BVV(10, 32) b = claripy.BoolV(True) #c = claripy.BVS('x', 32) nose.tools.assert_is(type(claripy.backends.concrete.convert(a)), claripy.bv.BVV) nose.tools.assert_is(type(claripy.backends.concrete.convert(b)), bool) a = claripy.BVV(1337, 32) b = a[31:16] c = claripy.BVV(0, 16) nose.tools.assert_is(b, c) bc = claripy.backends.concrete d = claripy.BVV(-1, 32) nose.tools.assert_equal(bc.convert(d), 0xffffffff) e = claripy.BVV(2**32 + 1337, 32) nose.tools.assert_equal(bc.convert(e), 1337)
def run(self, this_ref, obj_ref): log.debug( 'Called SimProcedure java.util.List.add with args: {} {}'.format( this_ref, obj_ref)) if this_ref.symbolic: return claripy.BoolS('list.append') try: array_ref = this_ref.load_field(self.state, ELEMS, 'java.lang.Object[]') array_len = this_ref.load_field(self.state, SIZE, 'int') self.state.javavm_memory.store_array_element( array_ref, array_len, obj_ref) # Update size new_array_len = claripy.BVV( self.state.solver.eval(array_len) + 1, 32) this_ref.store_field(self.state, SIZE, 'int', new_array_len) except KeyError: log.warning('Could not add element to list {}'.format(this_ref)) return claripy.BoolV(1)
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
def claripy_ast_from_ail_condition(self, condition): # Unpack a condition all the way to the leaves if isinstance(condition, claripy.ast.Base): return condition _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]), '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: claripy.LShR(conv(expr.operands[0]), expr. operands[1].value) } if isinstance(condition, (ailment.Expr.Load, ailment.Expr.DirtyExpression, ailment.Expr.BasePointerOffset)): var = claripy.BVS('ailexpr_%s' % repr(condition), condition.bits, explicit_name=True) self._condition_mapping[var] = 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] = 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] = 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) self._condition_mapping[var] = 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] = condition return r
def raw_solver(solver_type): #bc = claripy.backends.BackendConcrete(clrp) #bz = claripy.backends.BackendZ3(clrp) #claripy.expression_backends = [ bc, bz, ba ] s = solver_type() s.simplify() x = claripy.BVS('x', 32) y = claripy.BVS('y', 32) z = claripy.BVS('z', 32) l.debug("adding constraints") s.add(x == 10) s.add(y == 15) l.debug("checking") nose.tools.assert_true(s.satisfiable()) nose.tools.assert_false(s.satisfiable(extra_constraints=[x == 5])) nose.tools.assert_equal(s.eval(x + 5, 1)[0], 15) nose.tools.assert_true(s.solution(x + 5, 15)) nose.tools.assert_true(s.solution(x, 10)) nose.tools.assert_true(s.solution(y, 15)) nose.tools.assert_false(s.solution(y, 13)) # Batch evaluation results = s.batch_eval([x + 5, x + 6, 3], 2) nose.tools.assert_equal(len(results), 1) nose.tools.assert_equal(results[0][0], 15) # x + 5 nose.tools.assert_equal(results[0][1], 16) # x + 6 nose.tools.assert_equal(results[0][2], 3) # constant shards = s.split() nose.tools.assert_equal(len(shards), 2) nose.tools.assert_equal(len(shards[0].variables), 1) nose.tools.assert_equal(len(shards[1].variables), 1) nose.tools.assert_equal({ len(shards[0].constraints), len(shards[1].constraints) }, { 2, 1 }) # adds the != from the solution() check # test result caching s = solver_type() s.add(x == 10) s.add(y == 15) nose.tools.assert_is(s.result, None) nose.tools.assert_false(s.satisfiable(extra_constraints=(x==5,))) nose.tools.assert_is(s.result, None) nose.tools.assert_true(s.satisfiable()) nose.tools.assert_is_not(s.result, None) s = solver_type() #claripy.expression_backends = [ bc, ba, bz ] s.add(claripy.UGT(x, 10)) s.add(claripy.UGT(x, 20)) s.simplify() nose.tools.assert_equal(len(s.constraints), 1) #nose.tools.assert_equal(str(s.constraints[0]._obj), "Not(ULE(x <= 20))") s.add(claripy.UGT(y, x)) s.add(claripy.ULT(z, 5)) # test that duplicate constraints are ignored old_count = len(s.constraints) s.add(claripy.ULT(z, 5)) nose.tools.assert_equal(len(s.constraints), old_count) #print "========================================================================================" #print "========================================================================================" #print "========================================================================================" #print "========================================================================================" #a = s.eval(z, 100) #print "ANY:", a #print "========================================================================================" #mx = s.max(z) #print "MAX",mx #print "========================================================================================" #mn = s.min(z) #print "MIN",mn #print "========================================================================================" #print "========================================================================================" #print "========================================================================================" #print "========================================================================================" print "CONSTRATINT COUNTS:", [ len(_.constraints) for _ in s.split() ] nose.tools.assert_equal(s.max(z), 4) nose.tools.assert_equal(s.min(z), 0) nose.tools.assert_equal(s.min(y), 22) nose.tools.assert_equal(s.max(y), 2**y.size()-1) print "CONSTRATINT COUNTS:", [ len(_.constraints) for _ in s.split() ] ss = s.split() nose.tools.assert_equal(len(ss), 2) if isinstance(s, claripy.FullFrontend): nose.tools.assert_equal({ len(_.constraints) for _ in ss }, { 2, 3 }) # constraints from min or max elif isinstance(s, claripy.CompositeFrontend): #nose.tools.assert_equal({ len(_.constraints) for _ in ss }, { 3, 3 }) # constraints from min or max print "TODO: figure out why this is different now" nose.tools.assert_equal({ len(_.constraints) for _ in ss }, { 2, 2 }) # constraints from min or max # Batch evaluation s.add(y < 24) s.add(z < x) # Just to make sure x, y, and z belong to the same solver, since batch evaluation does not support the # situation where expressions belong to more than one solver results = s.batch_eval([x, y, z], 20) nose.tools.assert_set_equal( set(results), {(21L, 23L, 1L), (22L, 23L, 3L), (22L, 23L, 2L), (22L, 23L, 4L), (21L, 22L, 4L), (21L, 23L, 4L), (22L, 23L, 0L), (22L, 23L, 1L), (21L, 22L, 1L), (21L, 22L, 3L), (21L, 22L, 2L), (21L, 22L, 0L), (21L, 23L, 0L), (21L, 23L, 2L), (21L, 23L, 3L) } ) # test that False makes it unsat s = solver_type() s.add(claripy.BVV(1,1) == claripy.BVV(1,1)) nose.tools.assert_true(s.satisfiable()) s.add(claripy.BVV(1,1) == claripy.BVV(0,1)) nose.tools.assert_false(s.satisfiable()) # test extra constraints s = solver_type() x = claripy.BVS('x', 32) nose.tools.assert_equal(s.eval(x, 2, extra_constraints=[x==10]), ( 10, )) s.add(x == 10) nose.tools.assert_false(s.solution(x, 2)) nose.tools.assert_true(s.solution(x, 10)) # test result caching s = solver_type() nose.tools.assert_true(s.satisfiable()) s.add(claripy.BoolV(False)) nose.tools.assert_false(s.satisfiable()) s.result = None nose.tools.assert_false(s.satisfiable()) s = solver_type() x = claripy.BVS('x', 32) s.add(x == 10) nose.tools.assert_true(s.satisfiable()) nose.tools.assert_true(s.result is not None) nose.tools.assert_equals(s.eval(x, 1)[0], 10) nose.tools.assert_true(s.result is not None) s.add(x == 10) nose.tools.assert_true(s.result is not None) s.add(x > 9) nose.tools.assert_true(s.result is not None) s.add(x <= 11) nose.tools.assert_true(s.result is not None)
def exec_branch(self, state): # pylint:disable=invalid-name """Execute forward from a state, queuing new states if needed.""" logger.debug("Constraints: %s", state.solver.constraints) def solution(variable): """Returns the solution. There must be one or we fail.""" solutions = state.solver.eval(variable, 2) if len(solutions) > 1: raise ValueError("Ambiguous solution for %s (%s)" % (variable, self.code[state.pc])) solution = solutions[0] return solution if isinstance(solution, numbers.Number) else solution.value state.score += 1 self.code.pc = state.pc while True: if state.pc >= len(self.code): return True op = self.code.next() self.coverage[state.pc] += 1 logger.debug("NEW STEP") logger.debug("Memory: %s", state.memory) logger.debug("Stack: %s", state.stack) logger.debug("PC: %i, %s", state.pc, op) assert self.code.pc == state.pc + 1 assert isinstance(op, numbers.Number) assert all( hasattr(i, "symbolic") for i in state.stack), "The stack musty only contains claripy BV's" # Trivial operations first if not self.code.is_valid_opcode(state.pc): raise utils.CodeError("Trying to execute PUSH data") elif op == 254: # INVALID opcode raise utils.CodeError("designed INVALID opcode") elif op == opcode_values.JUMPDEST: pass elif op == opcode_values.ADD: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(s0 + s1) elif op == opcode_values.SUB: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(s0 - s1) elif op == opcode_values.MUL: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(s0 * s1) elif op == opcode_values.DIV: # We need to use claripy.LShR instead of a division if possible, # because the solver is bad dealing with divisions, better # with shifts. And we need shifts to handle the solidity ABI # for function selection. s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) # pylint:disable=invalid-name except ValueError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0 / s1)) else: if s1 == 0: state.stack_push(BVV_0) elif s1 == 1: state.stack_push(s0) elif s1 & (s1 - 1) == 0: exp = int(math.log(s1, 2)) state.stack_push(s0.LShR(exp)) else: state.stack_push(s0 / s1) elif op == opcode_values.SDIV: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) except ValueError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0.SDiv(s1))) else: state.stack_push(BVV_0 if s1 == 0 else s0.SDiv(s1)) elif op == opcode_values.MOD: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) except ValueError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0 % s1)) else: state.stack_push(BVV_0 if s1 == 0 else s0 % s1) elif op == opcode_values.SMOD: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name try: s1 = solution(s1) except ValueError: state.stack_push(claripy.If(s1 == 0, BVV_0, s0.SMod(s1))) else: state.stack_push(BVV_0 if s1 == 0 else s0.SMod(s1)) elif op == opcode_values.ADDMOD: s0, s1, s2 = state.stack_pop(), state.stack_pop( ), state.stack_pop() try: s2 = solution(s2) except ValueError: state.stack_push(claripy.If(s2 == 0, BVV_0, (s0 + s1) % s2)) else: state.stack_push(BVV_0 if s2 == 0 else (s0 + s1) % s2) elif op == opcode_values.MULMOD: s0, s1, s2 = state.stack_pop(), state.stack_pop( ), state.stack_pop() try: s2 = solution(s2) except ValueError: state.stack_push(claripy.If(s2 == 0, BVV_0, (s0 * s1) % s2)) else: state.stack_push(BVV_0 if s2 == 0 else (s0 * s1) % s2) elif op == opcode_values.EXP: base, exponent = solution(state.stack_pop()), state.stack_pop() if base == 2: state.stack_push(1 << exponent) else: exponent = solution(exponent) state.stack_push(claripy.BVV(base**exponent, 256)) elif op == opcode_values.LT: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(claripy.ULT(s0, s1)) elif op == opcode_values.GT: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(claripy.UGT(s0, s1)) elif op == opcode_values.SLT: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(claripy.SLT(s0, s1)) elif op == opcode_values.SGT: s0, s1 = ( not_bool(state.stack_pop()), not_bool(state.stack_pop()), ) # pylint:disable=invalid-name state.stack_push(claripy.SGT(s0, s1)) elif op == opcode_values.SIGNEXTEND: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name # s0 is the number of bits. s1 the number we want to extend. s0 = solution(s0) if s0 <= 31: sign_bit = 1 << (s0 * 8 + 7) state.stack_push( claripy.If( s1 & sign_bit == 0, s1 & (sign_bit - 1), s1 | ((1 << 256) - sign_bit), )) else: state.stack_push(s1) elif op == opcode_values.EQ: s0, s1 = state.stack_pop(), state.stack_pop() if isinstance(s0, claripy.ast.Bool) and isinstance( s1, claripy.ast.Bool): state.stack_push(s0 == s1) else: state.stack_push(not_bool(s0) == not_bool(s1)) elif op == opcode_values.ISZERO: condition = state.stack_pop() if isinstance(condition, claripy.ast.Bool): state.stack_push(claripy.Not(condition)) else: state.stack_push(condition == BVV_0) elif op == opcode_values.AND: s0, s1 = make_consistent(state.stack_pop(), state.stack_pop()) if isinstance(s0, claripy.ast.Bool) and isinstance( s1, claripy.ast.Bool): state.stack_push(s0 and s1) else: state.stack_push(s0 & s1) elif op == opcode_values.OR: s0, s1 = make_consistent(state.stack_pop(), state.stack_pop()) if isinstance(s0, claripy.ast.Bool) and isinstance( s1, claripy.ast.Bool): state.stack_push(s0 or s1) else: state.stack_push(s0 | s1) elif op == opcode_values.XOR: s0, s1 = make_consistent(state.stack_pop(), state.stack_pop()) state.stack_push(s0 ^ s1) elif op == opcode_values.NOT: state.stack_push(~state.stack_pop()) elif op == opcode_values.BYTE: s0, s1 = ( state.stack_pop(), state.stack_pop(), ) # pylint:disable=invalid-name state.stack_push( s1.LShR(claripy.If(s0 > 31, 32, 31 - s0) * 8) & 0xFF) elif op == opcode_values.PC: state.stack_push(bvv(state.pc)) elif op == opcode_values.GAS: state.stack_push(state.env.gas) elif op == opcode_values.ADDRESS: state.stack_push(state.env.address) elif op == opcode_values.BALANCE: addr = solution(state.stack_pop()) if addr != solution(state.env.address): raise utils.InterpreterError( state, "Can only query balance of the current contract for now" ) state.stack_push(state.env.balance) elif op == opcode_values.ORIGIN: state.stack_push(state.env.origin) elif op == opcode_values.CALLER: state.stack_push(state.env.caller) elif op == opcode_values.CALLVALUE: state.stack_push(state.env.value) elif op == opcode_values.BLOCKHASH: block_num = state.stack_pop() if block_num not in state.env.block_hashes: state.env.block_hashes[block_num] = claripy.BVS( "blockhash[%s]" % block_num, 256) state.stack_push(state.env.block_hashes[block_num]) elif op == opcode_values.TIMESTAMP: state.stack_push(state.env.block_timestamp) elif op == opcode_values.NUMBER: state.stack_push(state.env.block_number) elif op == opcode_values.COINBASE: state.stack_push(state.env.coinbase) elif op == opcode_values.DIFFICULTY: state.stack_push(state.env.difficulty) elif op == opcode_values.POP: state.stack_pop() elif op == opcode_values.JUMP: addr = solution(state.stack_pop()) if addr >= len(self.code ) or self.code[addr] != opcode_values.JUMPDEST: raise utils.CodeError("Invalid jump (%i)" % addr) state.pc = addr self.add_branch(state) return False elif op == opcode_values.JUMPI: addr, condition = solution( state.stack_pop()), state.stack_pop() state_false = state.copy() if isinstance(condition, claripy.ast.Bool): state.solver.add(condition) state_false.solver.add(claripy.Not(condition)) else: state.solver.add(condition != 0) state_false.solver.add(condition == 0) state_false.pc += 1 self.add_branch(state_false) state.pc = addr if (state.pc >= len(self.code) or self.code[state.pc] != opcode_values.JUMPDEST): raise utils.CodeError("Invalid jump (%i)" % (state.pc - 1)) self.add_branch(state) return False elif opcode_values.PUSH1 <= op <= opcode_values.PUSH32: pushnum = op - opcode_values.PUSH1 + 1 raw_value = self.code.read(pushnum) state.pc += pushnum state.stack_push( bvv(int.from_bytes(raw_value, byteorder="big"))) elif opcode_values.DUP1 <= op <= opcode_values.DUP16: depth = op - opcode_values.DUP1 + 1 state.stack_push(state.stack[-depth]) elif opcode_values.SWAP1 <= op <= opcode_values.SWAP16: depth = op - opcode_values.SWAP1 + 1 temp = state.stack[-depth - 1] state.stack[-depth - 1] = state.stack[-1] state.stack[-1] = temp elif opcode_values.LOG0 <= op <= opcode_values.LOG4: depth = op - opcode_values.LOG0 mstart, msz = (state.stack_pop(), state.stack_pop()) topics = [state.stack_pop() for x in range(depth)] elif op == opcode_values.SHA3: start, length = solution(state.stack_pop()), solution( state.stack_pop()) memory = state.memory.read(start, length) state.stack_push(Sha3(memory)) elif op == opcode_values.STOP: return True elif op == opcode_values.RETURN: return True elif op == opcode_values.CALLDATALOAD: indexes = state.stack_pop() try: index = solution(indexes) except ValueError: # Multiple solutions, let's fuzz. state.stack_push(indexes) # restore the stack self.add_for_fuzzing(state, indexes, CALLDATASIZE_FUZZ) return False state.solver.add(state.env.calldata_size >= index + 32) state.stack_push(state.env.calldata.read(index, 32)) elif op == opcode_values.CALLDATASIZE: state.stack_push(state.env.calldata_size) elif op == opcode_values.CALLDATACOPY: old_state = state.copy() mstart, dstart, size = ( state.stack_pop(), state.stack_pop(), state.stack_pop(), ) mstart, dstart = solution(mstart), solution(dstart) try: size = solution(size) except ValueError: self.add_for_fuzzing(old_state, size, CALLDATASIZE_FUZZ) return False state.memory.copy_from(state.env.calldata, mstart, dstart, size) state.solver.add(state.env.calldata_size >= dstart + size) elif op == opcode_values.CODESIZE: state.stack_push(bvv(len(self.code))) elif op == opcode_values.EXTCODESIZE: addr = state.stack_pop() if (addr == state.env.address).is_true(): state.stack_push(bvv(len(self.code))) else: # TODO: Improve that... It's clearly not constraining enough. state.stack_push(claripy.BVS("EXTCODESIZE[%s]" % addr, 256)) elif op == opcode_values.CODECOPY: mem_start, code_start, size = [ solution(state.stack_pop()) for _ in range(3) ] for i in range(size): if code_start + i < len(state.env.code): state.memory.write( mem_start + i, 1, claripy.BVV(state.env.code[code_start + i], 8), ) else: state.memory.write(mem_start + i, 1, claripy.BVV(0, 8)) elif op == opcode_values.MLOAD: index = solution(state.stack_pop()) state.stack_push(state.memory.read(index, 32)) elif op == opcode_values.MSTORE: index, value = solution(state.stack_pop()), not_bool( state.stack_pop()) state.memory.write(index, 32, value) elif op == opcode_values.MSTORE8: index, value = solution(state.stack_pop()), not_bool( state.stack_pop()) state.memory.write(index, 1, value[7:0]) elif op == opcode_values.MSIZE: state.stack_push(bvv(state.memory.size())) elif op == opcode_values.SLOAD: # TODO: This is inaccurate, because the storage can change # in a single transaction. # See commit d98cab834f8f359f01ef805256d179f5529ebe30. key = state.stack_pop() if key in state.storage_written: state.stack_push(state.storage_written[key]) else: if key not in state.storage_read: state.storage_read[key] = claripy.BVS( "storage[%s]" % key, 256) state.stack_push(state.storage_read[key]) elif op == opcode_values.SSTORE: # TODO: This is inaccurate, because the storage can change # in a single transaction. # See commit d98cab834f8f359f01ef805256d179f5529ebe30. key = state.stack_pop() value = state.stack_pop() state.storage_written[key] = value elif op == opcode_values.CALL: state.pc += 1 # First possibility: the call fails # (always possible with a call stack big enough) state_fail = state.copy() state_fail.stack_push(claripy.BoolV(False)) self.add_branch(state_fail) # Second possibility: success. state.calls.append(state.stack[-7:]) # pylint:disable=unused-variable gas, to_, value, meminstart, meminsz, memoutstart, memoutsz = ( state.stack_pop() for _ in range(7)) if solution(memoutsz) != 0: raise utils.InterpreterError(state, "CALL seems to return data") if solution(meminsz) != 0: raise utils.InterpreterError(state, "CALL seems to take data") state.stack_push(claripy.BoolV(True)) self.add_branch(state) return False elif op == opcode_values.SELFDESTRUCT: state.selfdestruct_to = state.stack[-1] return True elif op == opcode_values.REVERT: return False else: raise utils.InterpreterError(state, "Unknown opcode %s" % op) state.pc += 1
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