def main(): global b global sol extras = {angr.options.REVERSE_MEMORY_NAME_MAP, \ angr.options.TRACK_ACTION_HISTORY, \ angr.options.CONSTRAINT_TRACKING_IN_SOLVER} s = b.factory.blank_state(addr=start_addr, add_options=extras) if use_custom_concretization_strategy_first: #add_concretization_strategy(s, 0xffffffff8109a62a) add_concretization_strategy(s) add_instrumentation(s) if debug_qemu_backend: r = remote('127.0.0.1', QEMU_PORT) s = install_context(r, s) install_section(r, s, '.data') install_section(r, s, '.bss') install_section(r, s, '.brk') r.close() s.memory.store(uaf_object_base, s.se.BVS("uaf_obj", uaf_object_size * 8)) raw_input('Start conclic exploration?') explore_by_pathgroup(b, s) if __name__ == '__main__': k = ELF(KERNEL_PATH) b = angr.Project(KERNEL_PATH) sol = claripy.Solver() main()
import claripy import code s = claripy.Solver() inte = claripy.BVS("a", 32) inte = 214013 * inte inte = inte & 0xffffffff inte = inte + 2531011 inte = inte & 0xffffffff re = (inte >> 16) & 0x7fff s.add(re == 0) a = s.eval(inte, 10) code.interact(local=locals())
def _categorize_successor(self, state): """ Append state into successor lists. :param state: a SimState instance :param target: The target (of the jump/call/ret) :return: The state """ self.all_successors.append(state) target = state.scratch.target # categorize the state if o.APPROXIMATE_GUARDS in state.options and state.solver.is_false( state.scratch.guard, exact=False): if o.VALIDATE_APPROXIMATIONS in state.options: if state.satisfiable(): raise Exception('WTF') self.unsat_successors.append(state) elif o.APPROXIMATE_SATISFIABILITY in state.options and not state.solver.satisfiable( exact=False): if o.VALIDATE_APPROXIMATIONS in state.options: if state.solver.satisfiable(): raise Exception('WTF') self.unsat_successors.append(state) elif not state.scratch.guard.symbolic and state.solver.is_false( state.scratch.guard): self.unsat_successors.append(state) elif o.LAZY_SOLVES not in state.options and not state.satisfiable(): self.unsat_successors.append(state) elif o.NO_SYMBOLIC_JUMP_RESOLUTION in state.options and state.solver.symbolic( target): self.unconstrained_successors.append(state) elif not state.solver.symbolic( target) and not state.history.jumpkind.startswith("Ijk_Sys"): # a successor with a concrete IP, and it's not a syscall self.successors.append(state) self.flat_successors.append(state) elif state.history.jumpkind.startswith("Ijk_Sys"): # syscall self.successors.append(state) # Misuse the ip_at_syscall register to save the return address for this syscall # state.ip *might be* changed to be the real address of syscall SimProcedures by syscall handling code in # angr state.regs.ip_at_syscall = state.ip try: symbolic_syscall_num, concrete_syscall_nums = self._resolve_syscall( state) if concrete_syscall_nums is not None: for n in concrete_syscall_nums: split_state = state.copy() split_state.add_constraints(symbolic_syscall_num == n) split_state.inspect.downsize() self._fix_syscall_ip(split_state) self.flat_successors.append(split_state) else: # We cannot resolve the syscall number # However, we still put it to the flat_successors list, and angr.SimOS.handle_syscall will pick it # up, and create a "unknown syscall" stub for it. self._fix_syscall_ip(state) self.flat_successors.append(state) except AngrUnsupportedSyscallError: self.unsat_successors.append(state) else: # a successor with a symbolic IP _max_targets = state.options.symbolic_ip_max_targets _max_jumptable_targets = state.options.jumptable_symbolic_ip_max_targets try: if o.KEEP_IP_SYMBOLIC in state.options: s = claripy.Solver() addrs = s.eval(target, _max_targets + 1, extra_constraints=tuple( state.ip_constraints)) if len(addrs) > _max_targets: # It is not a library l.debug("It is not a Library") addrs = state.solver.eval_upto(target, _max_targets + 1) l.debug("addrs :%s", addrs) cond_and_targets = [(target == addr, addr) for addr in addrs] max_targets = _max_targets else: cond_and_targets = self._eval_target_jumptable( state, target, _max_jumptable_targets + 1) if cond_and_targets is None: # Fallback to the traditional and slow method cond_and_targets = self._eval_target_brutal( state, target, _max_targets + 1) max_targets = _max_targets else: max_targets = _max_jumptable_targets if len(cond_and_targets) > max_targets: l.warning( "Exit state has over %d possible solutions. Likely unconstrained; skipping. %s", max_targets, target.shallow_repr()) self.unconstrained_successors.append(state) else: for cond, a in cond_and_targets: split_state = state.copy() if o.KEEP_IP_SYMBOLIC in split_state.options: split_state.regs.ip = target else: split_state.add_constraints(cond, action=True) split_state.regs.ip = a split_state.inspect.downsize() self.flat_successors.append(split_state) self.successors.append(state) except SimSolverModeError: self.unsat_successors.append(state) return state
def compare_states(self, sl, sr): """ Compares two states for similarity. """ joint_solver = claripy.Solver() # make sure the canonicalized constraints are the same n_map, n_counter, n_canon_constraint = claripy.And( *sr.solver.constraints).canonicalize() #pylint:disable=no-member u_map, u_counter, u_canon_constraint = claripy.And( *sl.solver.constraints).canonicalize() #pylint:disable=no-member n_canoner_constraint = sr.solver.simplify(n_canon_constraint) u_canoner_constraint = sl.solver.simplify(u_canon_constraint) joint_solver.add((n_canoner_constraint, u_canoner_constraint)) if n_canoner_constraint is not u_canoner_constraint: self._report_incongruency("Different constraints!") return False # get the differences in registers and memory mem_diff = sr.memory.changed_bytes(sl.memory) reg_diff = sr.registers.changed_bytes(sl.registers) # this is only for unicorn if "UNICORN" in sl.options or "UNICORN" in sr.options: if sl.arch.name == "X86": reg_diff -= set(range(40, 52)) #ignore cc psuedoregisters reg_diff -= set(range(320, 324)) #some other VEX weirdness reg_diff -= set(range(340, 344)) #ip_at_syscall elif sl.arch.name == "AMD64": reg_diff -= set(range(144, 168)) #ignore cc psuedoregisters # make sure the differences in registers and memory are actually just renamed # versions of the same ASTs for diffs, (um, nm) in ( (reg_diff, (sl.registers, sr.registers)), (mem_diff, (sl.memory, sr.memory)), ): for i in diffs: bn = nm.load(i, 1) bu = um.load(i, 1) bnc = bn.canonicalize(var_map=n_map, counter=n_counter)[-1] buc = bu.canonicalize(var_map=u_map, counter=u_counter)[-1] if bnc is not buc: self._report_incongruency( "Different memory or registers (index %d, values %r and %r)!", i, bn, bu) return False # make sure the flags are the same if sl.arch.name in ("AMD64", "X86", "ARM", "ARMEL", "ARMHF", "AARCH64"): # pylint: disable=unused-variable n_bkp = sr.regs.cc_op, sr.regs.cc_dep1, sr.regs.cc_dep2, sr.regs.cc_ndep u_bkp = sl.regs.cc_op, sl.regs.cc_dep1, sl.regs.cc_dep2, sl.regs.cc_ndep if sl.arch.name in ('AMD64', 'X86'): n_flags = sr.regs.eflags.canonicalize(var_map=n_map, counter=n_counter)[-1] u_flags = sl.regs.eflags.canonicalize(var_map=u_map, counter=u_counter)[-1] else: n_flags = sr.regs.flags.canonicalize(var_map=n_map, counter=n_counter)[-1] u_flags = sl.regs.flags.canonicalize(var_map=u_map, counter=u_counter)[-1] if n_flags is not u_flags and sl.solver.simplify( n_flags) is not sr.solver.simplify(u_flags): self._report_incongruency("Different flags!") return False return True
def get_max_min(self, val, idx): solver = claripy.Solver() solver.add(val) sym = self.arg1a[idx] return (solver.max(sym), solver.min(sym))
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
def get_claripy_solver(): # TODO: What about SolverComposite? Tried, and seems slower. return claripy.Solver()
def __init__(self): self.solver = claripy.Solver()
def prove_equal(merged_f1, merged_f2, ptr_size1, ptr_size2, c1=None, c2=None, cmp_limit=120, equal_var=True): """ prove f1 == f2, using SMT solver Z3 :param f1: :param f2: :param c1: extra-constraints of f1 :param c2: extra-constraints of f2 :return: """ log.debug('prove_equal') f1, orig_f1 = merged_f1 f2, orig_f2 = merged_f2 if c1 is not None and c2 is not None: if len(c1) > 0 and len(c2) > 0: solver = claripy.Solver(backend=_MyBackendZ3()) # our normalization may make the constraints become UNSAT, ignore it when it happens try: if not solver.satisfiable(c1) or not solver.satisfiable(c2): f1 = orig_f1 f2 = orig_f2 c1 = None c2 = None except Exception: f1 = orig_f1 f2 = orig_f2 c1 = None c2 = None # ne_f1_f2 = ne_formulas(f1, f2) # if ne_f1_f2 is None: # log.debug("fail1 to create not_equal formula") # log.debug(f1) # log.debug(f2) # return False f1_in1, f2_in2 = check_and_return_formula_pair_with_inputs(f1, f2, ptr_size1, ptr_size2) if f1_in1 is None or f2_in2 is None: log.debug('inputs variables do not match') return False f1, input1 = f1_in1 f2, input2 = f2_in2 ne_f1_f2 = ne_formulas(f1, f2) if ne_f1_f2 is None: log.debug("fail to create not_equal formula") log.debug(f1) log.debug(f2) return False log.debug(f"To prove {str(ne_f1_f2)} UNSAT") count = 0 min_num_var = min(len(input1), len(input2)) if min_num_var == 0: # this means no extra-constraints should be added, input1 or input2 is a constant solver = claripy.Solver(backend=_MyBackendZ3()) solver.add(ne_f1_f2) return not solver.satisfiable() if factorial(min_num_var) > cmp_limit: raise TooManyVariables4Comparison(f1, f2, cmp_limit) for in1 in permutations(input1, min_num_var): constraints = [] count += 1 if count > cmp_limit: raise TooManyVariables4Comparison(f1, f2, cmp_limit) try: constraints = get_var_constraints(in1, input2[:min_num_var]) if constraints is None: continue cmp_expr = ne_f1_f2 # print(str(constraints)) # print(str(cmp_expr)) # if we have extra constraints, we need to ensure that constraints can be satisfied first need_pre_condition_sat_check = False if c1 is not None: need_pre_condition_sat_check = True constraints.extend(c1) if c2 is not None: need_pre_condition_sat_check = True constraints.extend(c2) if need_pre_condition_sat_check: solver = claripy.Solver(backend=_MyBackendZ3()) # check the constraints first solver.add(constraints) if not solver.satisfiable(): continue solver = claripy.Solver(backend=_MyBackendZ3()) solver.add(cmp_expr) if not solver.satisfiable(extra_constraints=constraints): return True except Exception as e: # print('f1', f1) # print('f2', f2) # print('input1', input1) # print('input2', input2) # constraints = get_var_constraints(in1, input2[:min_num_var]) # print('constraints') # for c in constraints: # print(c) log.warning('Meet Z3 solver error %s' % str(e)) return False
def __init__(self, kernel_path=None, dbcred=\ '/home/ww9210/develop/concolic_execution/concolicexecutor/dbcredentials.bin'): self.kernel_path = kernel_path self.k = ELF(kernel_path) self.b = angr.Project(kernel_path) self.sol = claripy.Solver() self.claripy = claripy self.typebv = claripy.ast.bv.BV self.r = None self.statebroker = statebroker.StateBroker() self.krop = None self.interested_funcs = {} self.memory_leak_funcs = {} # build connection to knowledge graph tmpf = open(dbcred, 'r') url, username, password = pickle.load(tmpf) print url, username, password try: self.neo4j_inst = neo4j_interace.MyNeo4jInterface( url, username, password) except neo4j.exceptions.ServiceUnavailable: print '[-] could not connect database' tmpf.close() # primitives self.control_flow_hijack_primitive = [ ] # control flow hijack primitive self.write_to_uaf_object_primitive = [ ] # write to uaf object primitive self.write_to_symbolic_address_primitive = [ ] # write to symbolic address primitive self.double_free_primitive = [] # double free primitive self.memory_leak_primitive = [] # memory leak primitive self.seen_symbolic_write_primitive_addr = [] self.seen_write_to_uaf_object_primitive_addr = [] # attributes self.start_addr = None self.use_custom_concretization_strategy_first = None self.qemu_port = None self.debug_qemu_backend = None self.debug_irsb = None self.debug_after_address_concretization = None self.resolve_uninit = None self.pause_on_finish_memory_loading = None self.pause_on_write_to_symbolic_address = None self.pause_on_uninit_write = None self.pause_on_each_step = None self.pause_on_read_from_symbolic_address = None self.pause_on_write_to_uaf_object_primitive = None self.dump_uaf_buffer = None self.dump_userspace_buffer = None self.pause_on_failed_memory_resolving = None self.pause_on_control_flow_hijack = None self.pause_on_memory_leak = None self.remove_range_concretize = None self.userspace_base = None self.kuafffp_concretizer = None self.seen_control_flow_hijack_primitive = [] self.seen_memory_leak_primitive = [] self.concretization_range = None self.extra_bp = None self.execution_time_limit = None self.expected_start_rip = None self.limit_loop = None self.detect_rcu_free = None self.detect_memory_leak = None self.assigned_start_time = None self.start_time_of_symbolic_execution = None self.function_call_to_disable = None self.stack_pivot_address = None self.additional_symbolic_registers = None self.name_concretization_strategy = None self.num_user_pages = None self.spray_method = None self.pause_on_strange_state = None self.target_allocator = None self.vulnerability_type = None self.look_for_second_control_flow_hijack = None return
def patch_function_stack(self, func, technique): solver = claripy.Solver() analysis_result = StructureAnalysis(self.project, self.cfg, [func], False) stack = analysis_result.stack_frames[func.addr] if stack is None: return stack = analysis_result.structures[stack] if stack.alloc_op is None: l.info( '\tFunction does not appear to have a stack frame (No alloc)') return False if func.has_return and stack.least_alloc.value != self.project.arch.bytes if self.project.arch.call_pushes_ret else 0: l.info( '\tFunction does not ever deallocate stack frame (Least alloc is %d for %s)', -stack.least_alloc.value, self.project.arch.name) return False if func.has_return and len(stack.dealloc_ops) == 0: l.warning( '\tFunction does not ever deallocate stack frame (No zero alloc)' ) return False if stack.conc_size <= 0: l.warning('\tFunction has invalid stack size of %#x', stack.conc_size) return False if stack.num_vars == 0: l.info( "\tFunction has %#x-byte stack frame, but doesn't use it for local vars", stack.conc_size) return False l.info('\tFunction has a stack frame of %#x bytes', stack.conc_size) l.info('\t%d access%s to %d address%s %s made.', stack.num_accs, '' if stack.num_accs == 1 else 'es', stack.num_vars, '' if stack.num_vars == 1 else 'es', 'is' if stack.num_accs == 1 else 'are') l.debug('Stack addresses: [%s]', ', '.join(hex(var.conc_addr) for var in stack)) l.debug('Running strategy...') technique.constrain_variables(func, solver, stack) l.debug('Stack variables: [%s]', ', '.join(hex(var.conc_addr) for var in stack)) if not solver.satisfiable(): l.critical('(%#x) Safe constraints unsatisfiable, fix this NOW', func.addr) raise FidgetError("You're a terrible programmer") # bit of a hack: the first unsafe constraint is always the one making the stack bigger, so we can quick-bail if it fails if not solver.satisfiable( extra_constraints=[stack.unsafe_constraints[0]]): l.info('\tUnable to resize stack') return False # z3 is smart enough that this doesn't add any noticable overhead for constraint in stack.unsafe_constraints: if solver.satisfiable(extra_constraints=[constraint]): l.debug("Added unsafe constraint: %s", constraint) solver.add(constraint) else: l.debug("Failed to add unsafe constraint: %s", constraint) new_stack = solver.eval(stack.sym_size, 1)[0] if new_stack == stack.conc_size: l.info('\tUnable to resize stack') return False l.info('\tResized stack from %#x to %#x', stack.conc_size, new_stack) for var in stack: fixedval = solver.eval_to_ast(var.sym_addr, 1)[0]._model_concrete.signed if var.size is None: l.debug('Moved %#x (unsized) to %#x', var.conc_addr, fixedval) else: l.debug('Moved %#x (size %#x) to %#x', var.conc_addr, var.size, fixedval) self._stack_patch_data += stack.get_patches(solver) self.stack_increases[func.addr] = new_stack - stack.conc_size return True
def val_arg(key): tmp_solver = claripy.Solver() tmp_solver.add(constraint_sets[key]) return tmp_solver.eval( self.template_args[0], 1 )[0] # Grabbing the first arg is correct because we've already verified there's only one!
def val_ret(key): tmp_solver = claripy.Solver() tmp_solver.add(constraint_sets[key]) return tmp_solver.eval(key.ast, 1)[0]
def fully_constrained(value, key): tmp_solver = claripy.Solver() tmp_solver.add(constraint_sets[key][0]) tmp_solver.add(self.arg_size_constraint) return len(tmp_solver.eval(value, 2)) == 1
def _linearize(self, constraint_sets): solver = claripy.Solver() def fully_constrained(value, key): tmp_solver = claripy.Solver() tmp_solver.add(constraint_sets[key][0]) tmp_solver.add(self.arg_size_constraint) return len(tmp_solver.eval(value, 2)) == 1 def can_linearize(key): if self.num_args != 1: # It sure would be cool if we could do more fancy stuff than just linearizing, but that's for later. return False if len(constraint_sets[key]) != 1: # There's more than one input that leads to this output, so we can't linearize # Don't get rid of this behavior because it's relied upon for correctness later on. return False else: # Note that we can linearize symbolic values, the only requirement is that they must be fully constrained by the accompanying constraint set # This is imperative because we're going to apply a continuous (linear) function to the input to get the output return all([ fully_constrained(arg, key) for arg in self.template_args ]) # This is a list of the keys associated with all return values all_keys = constraint_sets.keys() # Seperate out linearizable keys from unlinearizable keys linearizable_keys = [key for key in all_keys if can_linearize(key)] unlinearizable_keys = [ key for key in all_keys if key not in linearizable_keys ] # Each unlinearizable key gets its own return bucket for key in unlinearizable_keys: self._create_return_bucket(key, constraint_sets[key]) # These are methods to get the values of the return val and argument for any given key # We've already checked that these are fully constrained values def val_ret(key): tmp_solver = claripy.Solver() tmp_solver.add(constraint_sets[key]) return tmp_solver.eval(key.ast, 1)[0] def val_arg(key): tmp_solver = claripy.Solver() tmp_solver.add(constraint_sets[key]) return tmp_solver.eval( self.template_args[0], 1 )[0] # Grabbing the first arg is correct because we've already verified there's only one! # Linearization happens here. it's basically y=mx+b, restarting with a new line every time there's a discontinuity. buckets = [] bucket = [] coefficient = 0 for key in sorted(linearizable_keys, key=lambda key: val_ret(key)): if len(bucket) == 0: bucket.append(key) continue dy = val_ret(key) - val_ret(bucket[-1]) dx = val_arg(key) - val_arg(bucket[-1]) if len(bucket) == 1: if dy % dx == 0: #define the coefficient, greedily add this key to the bucket coefficient = dy / dx bucket.append(key) else: #we can't do anything with this bucket if the coefficient isn't an integer buckets.append(bucket) bucket = [key] else: if dy % dx == 0 and coefficient == dy / dx: #the linear pattern continues! add it to the bucket bucket.append(key) else: #done with this bucket, start a new one buckets.append(bucket) bucket = [key] #after all that, make sure to add the last bucket to our list of all buckets if len(bucket) != 0: buckets.append(bucket) for bucket in buckets: if len(bucket) == 1: #this is an easy case, bucketing bought us nothing. self._create_return_bucket(bucket[0], constraint_sets[key][0]) else: #create the constraint constraints = claripy.Or( *[claripy.Or(*constraint_sets[key]) for key in bucket]) #define a symbolic return retval = claripy.BVS( 'auto_simproc_linearized_return_MUST_REPLACE', 64) self.symbolic_return_templates.append(retval.cache_key) #save this so we can use it later to establish mutual exclusivity! this is sort of a hack but it's very important # constraints_without_bucket_info[retval.cache_key] = constraints #now we constrain the symbolic return value to be a function of the input, with good old y=mx+b m = (val_ret(bucket[1]) - val_ret(bucket[0])) / ( val_arg(bucket[1]) - val_arg(bucket[0])) b = val_ret(bucket[0]) - m * val_arg(bucket[0]) constraints = claripy.And( retval == ((m * self.template_args[0]) + b), constraints) #now add the whole bucket with one key self._create_return_bucket(retval.cache_key, (constraints, ), linearized=True)
def test_fp_ops(): a = claripy.FPV(1.5, claripy.FSORT_DOUBLE) b = claripy.fpToUBV(claripy.fp.RM_NearestTiesEven, a, 32) s = claripy.Solver() assert s.eval(b, 1)[0] == 2