def detect_arbitrary_memory_access(self, tainted_record, individual, current_instruction): if current_instruction["op"] == "SSTORE": if tainted_record and tainted_record.stack: tainted_index = tainted_record.stack[-1] tainted_value = tainted_record.stack[-2] if tainted_index and tainted_value and is_expr( tainted_index[0]) and is_expr(tainted_value[0]): if get_vars(tainted_index[0]) and get_vars( tainted_value[0]): tainted_index_var = get_vars(tainted_index[0])[0] tainted_value_var = get_vars(tainted_value[0])[0] if tainted_index != tainted_value and "calldataload_" in str( tainted_index[0]) and "calldataload_" in str( tainted_value[0]): if len(str(tainted_index_var).split("_")) == 3: transaction_index = int( str(tainted_index_var).split("_")[1]) argument_index = int( str(tainted_index_var).split("_")[2]) + 1 if type(individual. chromosome[transaction_index] ["arguments"][argument_index] ) is int and individual.chromosome[ transaction_index]["arguments"][ argument_index] > 2**128 - 1: return current_instruction["pc"] return None
def rename_vars(pcs, global_states): ret_pcs = [] vars_mapping = {} for expr in pcs: if is_expr(expr): list_vars = get_vars(expr) for var in list_vars: if var in vars_mapping: expr = substitute(expr, (var, vars_mapping[var])) continue var_name = var.decl().name() # check if a var is global if is_storage_var(var): pos = get_storage_position(var) # if it is not modified then keep the previous name if pos not in global_states: continue # otherwise, change the name of the variable new_var_name = var_name + '_old' new_var = BitVec(new_var_name, 256) vars_mapping[var] = new_var expr = substitute(expr, (var, vars_mapping[var])) ret_pcs.append(expr) ret_gs = {} # replace variable in storage expression for storage_addr in global_states: expr = global_states[storage_addr] # z3 4.1 makes me add this line if is_expr(expr): list_vars = get_vars(expr) for var in list_vars: if var in vars_mapping: expr = substitute(expr, (var, vars_mapping[var])) continue var_name = var.decl().name() # check if a var is global if var_name.startswith("Ia_store_"): position = int( var_name.split('_')[len(var_name.split('_')) - 1]) # if it is not modified if position not in global_states: continue # otherwise, change the name of the variable new_var_name = var_name + '_old' new_var = BitVec(new_var_name, 256) vars_mapping[var] = new_var expr = substitute(expr, (var, vars_mapping[var])) ret_gs[storage_addr] = expr return ret_pcs, ret_gs
def rename_vars(pcs, global_states): ret_pcs = [] vars_mapping = {} for expr in pcs: if is_expr(expr): list_vars = get_vars(expr) for var in list_vars: if var in vars_mapping: expr = substitute(expr, (var, vars_mapping[var])) continue var_name = var.decl().name() # check if a var is global if is_storage_var(var): pos = get_storage_position(var) # if it is not modified then keep the previous name if pos not in global_states: continue # otherwise, change the name of the variable new_var_name = var_name + '_old' new_var = BitVec(new_var_name, 256) vars_mapping[var] = new_var expr = substitute(expr, (var, vars_mapping[var])) ret_pcs.append(expr) ret_gs = {} # replace variable in storage expression for storage_addr in global_states: expr = global_states[storage_addr] # z3 4.1 makes me add this line if is_expr(expr): list_vars = get_vars(expr) for var in list_vars: if var in vars_mapping: expr = substitute(expr, (var, vars_mapping[var])) continue var_name = var.decl().name() # check if a var is global if var_name.startswith("Ia_store_"): position = int(var_name.split('_')[len(var_name.split('_'))-1]) # if it is not modified if position not in global_states: continue # otherwise, change the name of the variable new_var_name = var_name + '_old' new_var = BitVec(new_var_name, 256) vars_mapping[var] = new_var expr = substitute(expr, (var, vars_mapping[var])) ret_gs[storage_addr] = expr return ret_pcs, ret_gs
def detect_unhandled_exception(self, previous_instruction, current_instruction, tainted_record): # Register all exceptions if previous_instruction and previous_instruction["op"] in [ "CALL", "CALLCODE", "DELEGATECALL", "STATICCALL" ] and convert_stack_value_to_int( current_instruction["stack"][-1]) == 1: if tainted_record and tainted_record.stack and tainted_record.stack[ -1] and is_expr(tainted_record.stack[-1][0]): self.exceptions[tainted_record.stack[-1] [0]] = previous_instruction["pc"] # Remove all handled exceptions elif current_instruction["op"] == "JUMPI" and self.exceptions: if tainted_record and tainted_record.stack and tainted_record.stack[ -2] and is_expr(tainted_record.stack[-2][0]): for var in get_vars(tainted_record.stack[-2][0]): if var in self.exceptions: del self.exceptions[var] # Report all unhandled exceptions at termination elif current_instruction["op"] in [ "RETURN", "STOP", "SUICIDE", "SELFDESTRUCT" ] and self.exceptions: for exception in self.exceptions: return self.exceptions[exception] return None
def efsmt(y, phi, maxloops=None): """Solving exists x. forall y. phi(x, y)""" vars = get_vars(phi) x = [item for item in vars if item not in y] esolver = Solver() fsolver = Solver() esolver.add(BoolVal(True)) loops = 0 while maxloops is None or loops <= maxloops: loops += 1 eres = esolver.check() if eres == unsat: return unsat else: emodel = esolver.model() tau = [emodel.eval(var, True) for var in x] sub_phi = phi for i in range(len(x)): sub_phi = simplify(substitute(sub_phi, (x[i], tau[i]))) fsolver.add(Not(sub_phi)) if fsolver.check() == sat: fmodel = fsolver.model() sigma = [fmodel.eval(v, True) for v in y] sub_phi = phi for j in range(len(y)): sub_phi = simplify(substitute(sub_phi, (y[j], sigma[j]))) esolver.add(sub_phi) else: return sat return unknown
def check_reentrancy_bug(memory, solver, path_condition: list = None): """This function is used to detect reentrancy vulnerabilities, Args: path_condition: current path condition memory: current memory of Wasm solver: current z3 solver """ list_solver = solver.units() for expr in list_solver: if not z3.is_expr(expr): continue vars = get_vars(expr) flag_empty = 0 for var in vars: if var in global_vars.dict_symbolic_address: flag_empty += 1 tmp_dict = global_vars.find_dict_root(var) if not tmp_dict: continue for item in tmp_dict: if item in global_vars.list_storageStore: continue else: global_vars.find_reentrancy_detection() if flag_empty == 0: global_vars.find_reentrancy_detection()
def efsmt(ys, phi, maxloops=None): """Solving exists xs. forall ys. phi(x, y)""" xs = [x for x in get_vars(phi) if x not in ys] E = Solver() F = Solver() E.add(BoolVal(True)) loops = 0 while maxloops is None or loops <= maxloops: loops += 1 eres = E.check() if eres == sat: emodel = E.model() sub_phi = substitute(phi, [(x, emodel.eval(x, True)) for x in xs]) F.push() F.add(Not(sub_phi)) fres = F.check() if fres == sat: fmodel = F.model() sub_phi = substitute(phi, [(y, fmodel.eval(y, True)) for y in ys]) E.add(sub_phi) else: return fres, [(x, emodel.eval(x, True)) for x in xs] F.pop() else: return eres return unknown
def efsmt(y, phi, maxloops=None): """Solving exists x. forall y. phi(x, y)""" vars = get_vars(phi) x = [item for item in vars if item not in y] esolver = Solver() esolver.add(BoolVal(True)) loops = 0 while maxloops is None or loops <= maxloops: loops += 1 eres = esolver.check() if eres == unsat: return unsat else: emodel = esolver.model() x_mappings = [(var, emodel.eval(var, model_completion=True)) for var in x] sub_phi = simplify(substitute(phi, x_mappings)) fsolver = Solver() fsolver.add(Not(sub_phi)) if fsolver.check() == sat: fmodel = fsolver.model() y_mappings = [(var, fmodel.eval(var, model_completion=True)) for var in y] sub_phi = simplify(substitute(phi, y_mappings)) esolver.add(sub_phi) else: return sat return unknown
def all_solns(problem, limit=10): if not isinstance(problem, Solvable): problem = Problem(problem, get_vars(z3.And(*problem))) s = z3.Solver() s.add(*problem.constraints()) for _ in range(limit): if s.check() != z3.sat: return soln = s.model() yield Solution(soln) s.add(negate(problem, soln)) print(f"Warning: Terminated early after {limit} solutions.")
def detect_integer_overflow(self, previous_instruction, current_instruction, tainted_record): if previous_instruction and previous_instruction["op"] == "ADD": a = convert_stack_value_to_int(previous_instruction["stack"][-2]) b = convert_stack_value_to_int(previous_instruction["stack"][-1]) c = convert_stack_value_to_int(current_instruction["stack"][-1]) if a + b != c: if tainted_record and tainted_record.stack and tainted_record.stack[-1]: if get_vars(tainted_record.stack[-1][0]): self.overflows[previous_instruction["pc"]] = get_vars(tainted_record.stack[-1][0]) elif previous_instruction and previous_instruction["op"] == "MUL": a = convert_stack_value_to_int(previous_instruction["stack"][-2]) b = convert_stack_value_to_int(previous_instruction["stack"][-1]) c = convert_stack_value_to_int(current_instruction["stack"][-1]) if a * b != c: if tainted_record and tainted_record.stack and tainted_record.stack[-1]: if get_vars(tainted_record.stack[-1][0]): self.overflows[previous_instruction["pc"]] = get_vars(tainted_record.stack[-1][0]) elif previous_instruction and previous_instruction["op"] == "SUB": a = convert_stack_value_to_int(previous_instruction["stack"][-2]) b = convert_stack_value_to_int(previous_instruction["stack"][-1]) c = convert_stack_value_to_int(current_instruction["stack"][-1]) if a - b != c: if tainted_record and tainted_record.stack and tainted_record.stack[-1]: if get_vars(tainted_record.stack[-1][0]): self.underflows[previous_instruction["pc"]] = get_vars(tainted_record.stack[-1][0]) if current_instruction and current_instruction["op"] == "SSTORE": if tainted_record and tainted_record.stack and tainted_record.stack[-2]: for pc in self.overflows: for var1 in get_vars(tainted_record.stack[-2][0]): for var2 in self.overflows[pc]: if var1 == var2: return pc, "overflow" for pc in self.underflows: for var1 in get_vars(tainted_record.stack[-2][0]): for var2 in self.underflows[pc]: if var1 == var2: return pc, "underflow" return None, None
def load(self): index = regs[self.value] if isinstance(index, int): return mem[index] else: var, = get_vars(index) result = None for i in reversed(range(16)): index_i = substitute(index, (var, z3.BitVecVal(i, 4))) index_i = eval(str(index_i)) mem_val = mem[index_i] if isinstance(mem_val, int): mem_val = z3.BitVecVal(mem_val, 16) elif mem_val.size() != 16: mem_val = z3.ZeroExt(16 - mem_val.size(), mem_val) if result is None: result = mem_val else: result = ite(var == i, mem_val, result) return result
def get_all_vars(list_of_storage_exprs): ret_vars = [] for expr in list_of_storage_exprs: ret_vars += get_vars(list_of_storage_exprs[expr]) return ret_vars
def has_storage_vars(expr, storage_vars): list_vars = get_vars(expr) for var in list_vars: if var in storage_vars: return True return False
def is_in_expr(var, expr): list_vars = get_vars(expr) set_vars = set(i.decl().name() for i in list_vars) return var in set_vars
def get_all_vars(exprs): ret_vars = [] for expr in exprs: if is_expr(expr): ret_vars += get_vars(expr) return ret_vars