def __init__(self, view: BinaryView): self.view = view super().__init__() addr_size = self.view.address_size self.mem = { 1: Array("mem1", BitVecSort(addr_size * 8), BitVecSort(8)), 2: Array('mem2', BitVecSort(addr_size * 8), BitVecSort(16)), 4: Array('mem4', BitVecSort(addr_size * 8), BitVecSort(32)), 8: Array('mem8', BitVecSort(addr_size * 8), BitVecSort(64)), 16: Array('mem16', BitVecSort(addr_size * 8), BitVecSort(128)) }
def __init__(self, num, addr, raw): """ (raw) is the raw string of the instructions of the gadget """ # irsb is an array of BARF instructions # ins is an array of Assembly instructions try: (irsb, ins) = Analysis.getIR(raw, addr) except Exception as e: raise GadgetException(str(e.msg)) # Some strings representations self.asmStr = "; ".join(str(i) for i in ins) self.hexStr = "\\x" + "\\x".join("{:02x}".format(ord(c)) for c in raw) # Initializing the memory in Z3 for this gadget memorySMT = Array("MEM", BitVecSort(REGSIZE.size), BitVecSort(8)) self.addr = addr # int # Get the string for the address, depends on the architecture size self.addrStr = '0x' + format( addr, '0' + str(Analysis.ArchInfo.bits / 4) + 'x') self.graph = Graph() self.regCount = { } # Keys are integers, values are integers. regCount[2] = 0 <=> R2_0 have appeared but R2_1 not yet self.spInc = None # How much have Stack Pointer been incremented by self.num = num # Identifier or the gadget self.normalRet = None # True iff the gadgets ends up by a normal ret; instruction self.nbInstr = 0 # Number of REIL instructions of this gadget self.dep = None self.valuesTable = {} # Used dinamically when building graph # Building graph and computing the dependencies self.buildGraph(irsb) self.getDependencies()
def __init__(self, tx_id, starting_calldata=None): """ Constructor for Calldata :param tx_id: unique value representing the transaction the calldata is for :param starting_calldata: byte array representing the concrete calldata of a transaction """ self.tx_id = tx_id if starting_calldata: self._calldata = [] self.calldatasize = BitVecVal(len(starting_calldata), 256) self.concrete = True else: self._calldata = Array("{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8)) self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) self.concrete = False self.starting_calldata = starting_calldata or []
def prove_adversarial_property_z3(a_pace, b_pace, min_a, max_b, n_iterations): ''' Using z3 to probe the formula checking ReLu(sk * w) <= ylim[1] while sk <= sklim :param invariant_property: maximum value for sk :param weight: the weight between sk and the output :param ylim: max output :return: True if for every sk <= sklim implies that ReLu(sk * w) <= ylim ''' from math import log2, ceil num_bytes = ceil(log2(n_iterations)) + 1 print('n_iterations', n_iterations, '\nnum_bytes', num_bytes) assert n_iterations <= 2**num_bytes # if the bit vec is 32 z3 takes to long a_invariants = Array('a_invariants', BitVecSort(num_bytes), RealSort()) b_invariants = Array('b_invariants', BitVecSort(num_bytes), RealSort()) i = BitVec('i', num_bytes) n = BitVec('n', num_bytes) s = Solver() s.add(a_invariants[0] == min_a) s.add(b_invariants[0] == max_b) s.add(n == BitVecVal(n_iterations, num_bytes)) # The invariant s.add(ForAll(i, a_invariants[i] >= a_invariants[0] + BV2Int(i) * a_pace)) s.add(ForAll(i, b_invariants[i] <= b_invariants[0] + BV2Int(i) * b_pace)) # s.add(ForAll(i, a_invariants[i] >= a_invariants[0] + BV2Int(i * BitVecVal(a_pace, num_bytes)))) # s.add(ForAll(i, b_invariants[i] <= b_invariants[0] + BV2Int(i * BitVecVal(b_pace, num_bytes)))) # NOT the property to prove s.add(a_invariants[n] < b_invariants[n]) t = s.check() if t == sat: print("z3 result:", s.model()) return False else: print('proved adversarial property using z3') return True
def __init__(self, tx_id, starting_calldata=None): """ Constructor for Calldata :param tx_id: unique value representing the transaction the calldata is for :param starting_calldata: byte array representing the concrete calldata of a transaction """ self.tx_id = tx_id if starting_calldata is not None: self._calldata = [] self.calldatasize = BitVecVal(len(starting_calldata), 256) self.concrete = True else: self._calldata = Array("{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8)) self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) self.concrete = False if self.concrete: for calldata_byte in starting_calldata: if type(calldata_byte) == int: self._calldata.append(BitVecVal(calldata_byte, 8)) else: self._calldata.append(calldata_byte)
def __init__(self, var, address_size): super(ByteSwapModeler, self).__init__() if (not isinstance(var, MediumLevelILInstruction) or var.operation != MediumLevelILOperation.MLIL_VAR_SSA): raise TypeError('var must be an MLIL_VAR_SSA operation') self.address_size = address_size self._memory = Array('Memory', BitVecSort(address_size * 8), BitVecSort(8)) self.solver = Solver() self.visited = set() self.to_visit = list() self.byte_values = dict() self.byte_vars = set() self.var = var self.function = var.function
def main(): dim = int(input("size of the array: ")) i = [Int(f"i_{x}") for x in range(dim + 1)] j = [Int(f"j_{x}") for x in range(dim + 1)] A = [Array(f"A_{x}", IntSort(), IntSort()) for x in range(dim + 1)] tmp = [Int(f"tmp_{x}") for x in range(dim)] s = Solver() init_condition = init(i[0], j[0]) s.add(init_condition) tran_condition = mk_tran_condition(A, i, j, tmp, dim) s.add(And(*tran_condition)) values = [Int(f"n_{x}") for x in range(dim)] init_check_condition = check(values, A[-1], dim) s.add(And(*init_check_condition)) post_condition = mk_post_condition(values) print("Bubble sort") print("---------------------") s.push() s.add(Not(post_condition)) print("Testing the validity of the algorithm; `valid expected`:") if s.check() == sat: print(f"counterexample:\n{s.model()}") else: print("valid") print("---------------------") s.pop() s.add(post_condition) print("Getting a model...") print("Model:") if s.check() == sat: print(s.model())
def make_load(src, size): mem = Array('mem', BitVecSort(32), BitVecSort(8)) load_bytes = [mem[src + i] for i in range(0, size)] return Concat(*load_bytes)
def env_variable(self, type, expr, instance, name): return Array(self.env_variable_name(type, expr, instance, name), BitVecSort(32), BitVecSort(8))
for e in range(dim): yield variables[e] == Select(Ar, e) def mk_post_condition(values): condition = [] for v1, v2 in zip(values, values[1:]): condition.append(v1 <= v2) return And(*condition) dim = int(input("size of the array: ")) i = [Int(f"i_{x}") for x in range(dim + 1)] j = [Int(f"j_{x}") for x in range(dim + 1)] A = [Array(f"A_{x}", IntSort(), IntSort()) for x in range(dim + 1)] tmp = [Int(f"tmp_{x}") for x in range(dim)] s = Solver() init_condition = init(i[0], j[0]) s.add(init_condition) tran_condition = mk_tran_condition(A, i, j, tmp, dim) s.add(And(*tran_condition)) values = [Int(f"n_{x}") for x in range(dim)] init_check_condition = check(values, A[-1], dim) s.add(And(*init_check_condition)) post_condition = mk_post_condition(values)
def __init__( self, problem, debug: Optional[bool] = False, max_time: Optional[int] = 10, parallel: Optional[bool] = False, random_values: Optional[bool] = False, logics: Optional[str] = None, verbosity: Optional[int] = 0, ): """Scheduling Solver debug: True or False, False by default max_time: time in seconds, 60 by default parallel: True to enable mutlthreading, False by default """ self.problem = problem self.problem_context = problem.context self.debug = debug # objectives list self.objective = None # the list of all objectives defined in this problem self.current_solution = None # no solution until the problem is solved # set_option('smt.arith.auto_config_simplex', True) if debug: set_option("verbose", 2) else: set_option("verbose", verbosity) if random_values: set_option("sat.random_seed", random.randint(1, 1e3)) set_option("smt.random_seed", random.randint(1, 1e3)) set_option("smt.arith.random_initial_value", True) else: set_option("sat.random_seed", 0) set_option("smt.random_seed", 0) set_option("smt.arith.random_initial_value", False) # set timeout self.max_time = max_time # in seconds set_option("timeout", int(self.max_time * 1000)) # in milliseconds # create the solver print("Solver type:\n===========") # check if the problem is an optimization problem self.is_not_optimization_problem = len( self.problem_context.objectives) == 0 self.is_optimization_problem = len(self.problem_context.objectives) > 0 self.is_multi_objective_optimization_problem = (len( self.problem_context.objectives) > 1) # the Optimize() solver is used only in the case of a mutli-optimization # problem. This enables to choose the priority method. # in the case of a single objective optimization, the Optimize() solver # apperas to be less robust than the basic Solver(). The # incremental solver is then used. # see this url for a documentation about logics # http://smtlib.cs.uiowa.edu/logics.shtml if logics is None: self._solver = Solver() print("\t-> Standard SAT/SMT solver") else: self._solver = SolverFor(logics) print("\t-> SMT solver using logics", logics) if debug: set_option(unsat_core=True) if parallel: set_option("parallel.enable", True) # enable parallel computation # add all tasks assertions to the solver for task in self.problem_context.tasks: self.add_constraint(task.get_assertions()) self.add_constraint(task.end <= self.problem.horizon) # then process tasks constraints for constraint in self.problem_context.constraints: self.add_constraint(constraint) # process resources requirements for ress in self.problem_context.resources: self.add_constraint(ress.get_assertions()) # process resource intervals for ress in self.problem_context.resources: busy_intervals = ress.get_busy_intervals() nb_intervals = len(busy_intervals) for i in range(nb_intervals): start_task_i, end_task_i = busy_intervals[i] for k in range(i + 1, nb_intervals): start_task_k, end_task_k = busy_intervals[k] self.add_constraint( Or(start_task_k >= end_task_i, start_task_i >= end_task_k)) # process indicators for indic in self.problem_context.indicators: self.add_constraint(indic.get_assertions()) # work amounts # for each task, compute the total work for all required resources""" for task in self.problem_context.tasks: if task.work_amount > 0.0: work_total_for_all_resources = [] for required_resource in task.required_resources: # work contribution for the resource interv_low, interv_up = required_resource.busy_intervals[ task] work_contribution = required_resource.productivity * ( interv_up - interv_low) work_total_for_all_resources.append(work_contribution) self.add_constraint( Sum(work_total_for_all_resources) >= task.work_amount) # process buffers for buffer in self.problem_context.buffers: # # create an array that stores the mapping between start times and # quantities. For example, if a start T1 starts at 2 and consumes # 8, and T3 ends at 6 and consumes 5 then the mapping array # will look like : A[2]=8 and A[6]=-5 # SO far, no way to have the same start time at different inst buffer_mapping = Array("Buffer_%s_mapping" % buffer.name, IntSort(), IntSort()) for t in buffer.unloading_tasks: self.add_constraint(buffer_mapping == Store( buffer_mapping, t.start, -buffer.unloading_tasks[t])) for t in buffer.loading_tasks: self.add_constraint(buffer_mapping == Store( buffer_mapping, t.end, +buffer.loading_tasks[t])) # sort consume/feed times in asc order tasks_start_unload = [t.start for t in buffer.unloading_tasks] tasks_end_load = [t.end for t in buffer.loading_tasks] sorted_times, sort_assertions = sort_no_duplicates( tasks_start_unload + tasks_end_load) self.add_constraint(sort_assertions) # create as many buffer state changes as sorted_times buffer.state_changes_time = [ Int("%s_sc_time_%i" % (buffer.name, k)) for k in range(len(sorted_times)) ] # add the constraints that give the buffer state change times for st, bfst in zip(sorted_times, buffer.state_changes_time): self.add_constraint(st == bfst) # compute the different buffer states according to state changes buffer.buffer_states = [ Int("%s_state_%i" % (buffer.name, k)) for k in range(len(buffer.state_changes_time) + 1) ] # add constraints for buffer states # the first buffer state is equal to the buffer initial level if buffer.initial_state is not None: self.add_constraint( buffer.buffer_states[0] == buffer.initial_state) if buffer.final_state is not None: self.add_constraint( buffer.buffer_states[-1] == buffer.final_state) if buffer.lower_bound is not None: for st in buffer.buffer_states: self.add_constraint(st >= buffer.lower_bound) if buffer.upper_bound is not None: for st in buffer.buffer_states: self.add_constraint(st <= buffer.upper_bound) # and, for the other, the buffer state i+1 is the buffer state i +/- the buffer change for i in range(len(buffer.buffer_states) - 1): self.add_constraint( buffer.buffer_states[i + 1] == buffer.buffer_states[i] + buffer_mapping[buffer.state_changes_time[i]]) # optimization if self.is_optimization_problem: self.create_objective()
def output_variable(type, name, instance): s = '{}!output!{}!{}'.format(type, name, instance) if type == 'long': return Array(s, BitVecSort(32), BitVecSort(8)) else: return Array(s, BitVecSort(32), BitVecSort(8))
def env_variable(expr, instance, name): pattern = 'int!choice!{}!{}!{}!{}!{}!env!{}' s = pattern.format(expr[0], expr[1], expr[2], expr[3], instance, name) return Array(s, BitVecSort(32), BitVecSort(8))
def original_variable(self, type, expr, instance): return Array(self.original_variable_name(type, expr, instance), BitVecSort(32), BitVecSort(8))
class Calldata: """ Calldata class representing the calldata of a transaction """ def __init__(self, tx_id, starting_calldata=None): """ Constructor for Calldata :param tx_id: unique value representing the transaction the calldata is for :param starting_calldata: byte array representing the concrete calldata of a transaction """ self.tx_id = tx_id if starting_calldata is not None: self._calldata = [] self.calldatasize = BitVecVal(len(starting_calldata), 256) self.concrete = True else: self._calldata = Array("{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8)) self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) self.concrete = False if self.concrete: for calldata_byte in starting_calldata: if type(calldata_byte) == int: self._calldata.append(BitVecVal(calldata_byte, 8)) else: self._calldata.append(calldata_byte) def concretized(self, model): result = [] for i in range( get_concrete_int( model.eval(self.calldatasize, model_completion=True))): result.append( get_concrete_int( model.eval(self._calldata[i], model_completion=True))) return result def get_word_at(self, index: int): return self[index:index + 32] def __getitem__(self, item: Union[int, slice]) -> Any: if isinstance(item, slice): start, step, stop = item.start, item.step, item.stop try: if start is None: start = 0 if step is None: step = 1 if stop is None: stop = self.calldatasize current_index = (start if isinstance(start, BitVecRef) else BitVecVal(start, 256)) dataparts = [] while simplify(current_index != stop): dataparts.append(self[current_index]) current_index = simplify(current_index + step) except Z3Exception: raise IndexError("Invalid Calldata Slice") values, constraints = zip(*dataparts) result_constraints = [] for c in constraints: result_constraints.extend(c) return simplify(Concat(values)), result_constraints if self.concrete: try: return self._calldata[get_concrete_int(item)], () except IndexError: return BitVecVal(0, 8), () else: constraints = [ Implies(self._calldata[item] != 0, UGT(self.calldatasize, item)) ] return self._calldata[item], constraints
class Calldata: """ Calldata class representing the calldata of a transaction """ def __init__(self, tx_id, starting_calldata=None): """ Constructor for Calldata :param tx_id: unique value representing the transaction the calldata is for :param starting_calldata: byte array representing the concrete calldata of a transaction """ self.tx_id = tx_id if starting_calldata: self._calldata = [] self.calldatasize = BitVecVal(len(starting_calldata), 256) self.concrete = True else: self._calldata = Array("{}_calldata".format(self.tx_id), BitVecSort(256), BitVecSort(8)) self.calldatasize = BitVec("{}_calldatasize".format(self.tx_id), 256) self.concrete = False self.starting_calldata = starting_calldata or [] @property def constraints(self): constraints = [] if self.concrete: for calldata_byte in self.starting_calldata: if type(calldata_byte) == int: self._calldata.append(BitVecVal(calldata_byte, 8)) else: self._calldata.append(calldata_byte) constraints.append( self.calldatasize == len(self.starting_calldata)) else: x = BitVec("x", 256) constraints.append( ForAll(x, Implies(self[x] != 0, UGT(self.calldatasize, x)))) return constraints def concretized(self, model): result = [] for i in range( get_concrete_int( model.eval(self.calldatasize, model_completion=True))): result.append( get_concrete_int(model.eval(self[i], model_completion=True))) return result def get_word_at(self, index: int): return self[index:index + 32] def __getitem__(self, item): if isinstance(item, slice): try: current_index = (item.start if isinstance( item.start, BitVecRef) else BitVecVal(item.start, 256)) dataparts = [] while simplify(current_index != item.stop): dataparts.append(self[current_index]) current_index = simplify(current_index + 1) except Z3Exception: raise IndexError("Invalid Calldata Slice") return simplify(Concat(dataparts)) if self.concrete: try: return self._calldata[get_concrete_int(item)] except IndexError: return BitVecVal(0, 8) else: return self._calldata[item]
def output_variable(self, type, name, instance): return Array(self.output_variable_name(type, name, instance), BitVecSort(32), BitVecSort(8))
def original_variable(type, expr, instance): pattern = '{}!choice!{}!{}!{}!{}!{}!original' s = pattern.format(type, expr[0], expr[1], expr[2], expr[3], instance) return Array(s, BitVecSort(32), BitVecSort(8))
def angelic_variable(self, type, expr, instance): return Array(self.angelic_variable_name(type, expr, instance), BitVecSort(32), BitVecSort(8))