def _min(self, expr, extra_constraints=(), solver=None, model_callback=None): global solve_count lo = 0 hi = 2**expr.size() - 1 vals = set() numpop = 0 if len(extra_constraints) > 0: solver.push() numpop += 1 solver.add(*[self.convert(e) for e in extra_constraints]) # TODO: Can only deal with bitvectors, not floats while hi - lo > 1: middle = (lo + hi) // 2 #l.debug("h/m/l/d: %d %d %d %d", hi, middle, lo, hi-lo) solver.push() solver.add(z3.UGE(expr, lo), z3.ULE(expr, middle)) numpop += 1 solve_count += 1 l.debug("Doing a check!") if solver.check() == z3.sat: l.debug("... still sat") if model_callback is not None: model_callback(self._generic_model(solver.model())) vals.add(self._primitive_from_model(solver.model(), expr)) hi = middle else: l.debug("... now unsat") lo = middle solver.pop() numpop -= 1 for _ in range(numpop): solver.pop() #l.debug("final hi/lo: %d, %d", hi, lo) if hi == lo: return lo else: solver.push() solver.add(expr == lo) l.debug("Doing a check!") if solver.check() == z3.sat: if model_callback is not None: model_callback(self._generic_model(solver.model())) vals.add(lo) solver.pop() else: vals.add(hi) solver.pop() return min(vals)
def UGE(self, other): if isinstance(other, int): other = BVV(other, self.size) else: assert isinstance(other, BV) assert self.size == other.size return BoolExpr(z3.UGE(self.z3obj, other.z3obj))
def UGE(a: BitVec, b: BitVec) -> Bool: """Create an unsigned greater or equals expression. :param a: :param b: :return: """ annotations = a.annotations + b.annotations return Bool(z3.UGE(a.raw, b.raw), annotations)
def _min_values(self, expr, extra_constraints=(), result=None, solver=None): global solve_count lo = 0 hi = 2**expr.size() - 1 vals = set() numpop = 0 if len(extra_constraints) > 0: solver.push() numpop += 1 solver.add(*[self.convert(e) for e in extra_constraints]) while hi - lo > 1: middle = (lo + hi) / 2 #l.debug("h/m/l/d: %d %d %d %d", hi, middle, lo, hi-lo) solver.push() solver.add(z3.UGE(expr, lo), z3.ULE(expr, middle)) numpop += 1 solve_count += 1 l.debug("Doing a check!") if solver.check() == z3.sat: l.debug("... still sat") vals.add(self._primitive_from_model(solver.model(), expr)) hi = middle else: l.debug("... now unsat") lo = middle solver.pop() numpop -= 1 for _ in range(numpop): solver.pop() #l.debug("final hi/lo: %d, %d", hi, lo) if hi == lo: return lo else: solver.push() solver.add(expr == lo) l.debug("Doing a check!") if solver.check() == z3.sat: vals.add(lo) solver.pop() else: vals.add(hi) solver.pop() return vals
def _touches_address(self, op_name, address): "Return a boolean indicating whether `op_name` touches `address`" # Check for overflow, which is defined to wrap around upper_bound = self.base_address[op_name] + self.width_bytes[op_name] overflow = z3.ULT(upper_bound, self.base_address[op_name]) return z3.If( overflow, # If overflow, account for wraparound z3.Or( z3.UGE(address, self.base_address[op_name]), z3.ULT(address, upper_bound), ), # If no overflow, the address should be in [base, base + offset) z3.And( z3.UGE(address, self.base_address[op_name]), z3.ULT(address, upper_bound), ), )
def newf(*args): assert len(args) == len(dst_end_args) cond = [] for a, b in zip(args[:-1], dst_start_args[:-1]): cond.append(a == b) cond.append(z3.UGE(args[-1], dst_start_args[-1])) cond.append(z3.ULE(args[-1], dst_end_args[-1])) cond = z3.And(*cond) return util.If(cond, z3.BitVecVal(0, write_size), dstfn(*args))
def newf(*args): assert len(args) == len(dst_end_args) cond = [] for a, b in zip(args[:-1], dst_start_args[:-1]): cond.append(a == b) cond.append(z3.UGE(args[-1], dst_start_args[-1])) cond.append(z3.ULT(args[-1], dst_end_args[-1])) cond = z3.And(*cond) srcargs = src_start_args[:-1] + [args[-1]] return util.If(cond, srcfn(*srcargs), dstfn(*args))
def __init__(self, meta, opcode, src1, src2, target): self.meta = meta self.opcode = opcode self.src1 = src1 self.src2 = src2 self.target = target self.op = { "beq": lambda a, b: a == b, "bne": lambda a, b: a != b, "blt": lambda a, b: a < b, "bltu": lambda a, b: z3.ULT(a, b), "bge": lambda a, b: a >= b, "bgeu": lambda a, b: z3.UGE(a, b), }[opcode.lower()]
def main(): global dse, todo, current sys.setrecursionlimit(2000) # oof # Parse arguments parser = Sandbox_Win_x86_64.parser(description="PE sandboxer") parser.add_argument("filename", help="PE Filename") options = parser.parse_args() options.dependencies = True # So we dont need to reimplement qt sb = Sandbox_Win_x86_64(LocationDB(), options.filename, options, custom_methods=qt_methods) sb.jitter.add_breakpoint(end_ptr, stop_exec) # End condition # Setup the qt string memory and a pointer to it sb.jitter.vm.add_memory_page(0x10000018, PAGE_READ | PAGE_WRITE, pck64(0x20000000)) # Hooking in here sb.jitter.vm.add_memory_page(0x20000000, PAGE_READ | PAGE_WRITE, qtstring) # The initial qstring sb.jitter.vm.add_memory_page(0x10000020, PAGE_READ | PAGE_WRITE, b'\x00') # The result sb.jitter.cpu.R15 = 0x10000000 sb.jitter.cpu.RSP = sb.jitter.stack_base + 0x8000 # Setup and attach the DSE dse = DSEPC(sb.machine, sb.loc_db, produce_solution=DSEPC.PRODUCE_SOLUTION_PATH_COV) sb.jitter.init_run(0x140004B61) dse.attach(sb.jitter) dse.update_state_from_concrete() dse.symbolize_memory(interval([(flag_ptr, flag_ptr + 0x20)])) # Printable unicode only for address in range(flag_ptr, flag_ptr + 0x20, 0x2): z3_mem = dse.z3_trans.from_expr( dse.eval_expr(ExprMem(ExprInt(address, 64), 16))) unicode_constraint = z3.And( \ z3.UGE(z3_mem, dse.z3_trans.from_expr(ExprInt(0x0020, 16))), \ z3.ULE(z3_mem, dse.z3_trans.from_expr(ExprInt(0x007E, 16))) \ ) dse.cur_solver.add(unicode_constraint) snapshot = dse.take_snapshot() # Begin run todo = [b'\x41\x00' * 0x10] while todo: dse.restore_snapshot(snapshot) current = todo.pop() sb.jitter.vm.set_mem(flag_ptr, current) # Update the password in jitter memory print('-' * 40 + f' CONCRETE: {unicode_string(current)}') sb.jitter.continue_run()
def ComparisonToSmt(self): assert (VertexNode.OpCode.IsComparison(self.operator)) lhs = self.operands[0].VertexNameToSmt() rhs = self.operands[1].VertexNameToSmt() if self.operator == VertexNode.OpCode.GT: return z3.UGT(lhs, rhs) elif self.operator == VertexNode.OpCode.GE: return z3.UGE(lhs, rhs) elif self.operator == VertexNode.OpCode.LT: return z3.ULT(lhs, rhs) elif self.operator == VertexNode.OpCode.LE: return z3.ULE(lhs, rhs) elif self.operator == VertexNode.OpCode.EQ: return (lhs == rhs) elif self.operator == VertexNode.OpCode.NE: return (lhs != rhs)
def solve_for_memory_access(self, expr_mem, access_type, additional_constraints=set()): # Check that input has effect on memory referenced if get_expr_ids(expr_mem.ptr) & self.symbolized_mem_ids: for possibility in possible_values(expr_mem.ptr): address_expr = possibility.value access_len = int(expr_mem.size/8) # 5 sec timeout #self.dse.cur_solver.set('timeout', 5000) # Save solver state self.dse.cur_solver.push() # Add constraints from the expr itself for cons in possibility.constraints.union(additional_constraints): eaff = cons.to_constraint() #print '\tADDING CONSTRAINT: ' + str(eaff) self.dse.cur_solver.add(self.dse.z3_trans.from_expr(eaff)) # Add memory constraints for mem_range in self.dse.valid_ranges: # Add range constraint rc = z3.Not(z3.And(z3.UGE(self.dse.z3_trans.from_expr(address_expr), self.dse.z3_trans.from_expr(mem_range[0])), z3.ULE(self.dse.z3_trans.from_expr(address_expr), self.dse.z3_trans.from_expr(mem_range[1]-ExprInt(access_len-1, 64))) ) ) self.dse.cur_solver.add(rc) #print solver #import pdb; pdb.set_trace() if self.dse.cur_solver.check()==z3.sat: model = self.dse.cur_solver.model() #import pdb; pdb.set_trace() log.debug('SYMB 0x{:08X}: {:s} -> {}AV[{:s}] '.format(self.dse.jitter.pc, \ str(model), access_type, \ str(self.dse.symb.eval_expr(address_expr, eval_cache={}))) ) # Evaluate the buffer that would cause the crash crashbuf = self.derive_crashbuf(model) # Evaluate the AV adress values = self.get_values_from_model(model) self.dse.crashes.append(crash( self.dse.jitter.pc, \ self.dse.symb.eval_expr(address_expr, eval_cache=values), \ access_type, \ crashbuf, \ int(self.dse.callsite) ) ) # Reset the solver self.dse.cur_solver.pop() return
def check(self, instance, conj, is_owner_valid, is_owned1_valid, is_owned2_valid, max_refs, ownerfn=None, ownedby=None): """Emit correctness definitions for this refcnt""" is_owned_valid = lambda a, b: z3.And(is_owned1_valid(a), is_owned2_valid(b)) assert ownerfn != None or ownedby != None assert not (ownerfn and ownedby) if ownerfn: ownedby = lambda arg1, arg2: ownerfn(arg2) == arg1 fn = instance._fields[self._name] owner = util.FreshBitVec('owner', self.get_types()[0]) owned1 = util.FreshBitVec('owned1', self.get_types()[1][0]) owned2 = util.FreshBitVec('owned2', self.get_types()[1][1]) idx = util.FreshBitVec('idx', self.get_types()[2]) ref, perm1, perm2, perm_inv = fn # 1) a valid input implies a valid owned output conj.append(z3.ForAll([owner, owned1, owned2], z3.Implies( z3.And( is_owner_valid(owner), is_owned_valid(owned1, owned2)), z3.ULT(perm_inv(owner, owned1, owned2), max_refs) ))) conj.append(z3.ForAll([owner, idx], z3.Implies( z3.And( is_owner_valid(owner), z3.ULT(idx, max_refs)), z3.And( is_owned1_valid(perm1(owner, idx)), is_owned2_valid(perm2(owner, idx)))))) # 2) The function function is a bijection conj.append(z3.ForAll([owner, owned1, owned2, idx], z3.Implies(z3.And( is_owner_valid(owner), is_owned_valid(owned1, owned2), z3.ULT(idx, max_refs)), z3.And( perm_inv(owner, perm1(owner, idx), perm2(owner, idx)) == idx, perm1(owner, perm_inv(owner, owned1, owned2)) == owned1, perm2(owner, perm_inv(owner, owned1, owned2)) == owned2, )))) # 3) if 'owner' owns 'owned', then f(owned) < ref, otherwise w(owned) >= ref conj.append(z3.ForAll([owner, owned1, owned2], z3.Implies( z3.And( is_owner_valid(owner), is_owned_valid(owned1, owned2), ), z3.And( z3.Implies(ownedby(owner, (owned1, owned2)), z3.ULT(perm_inv(owner, owned1, owned2), ref(owner))), z3.Implies(z3.Not(ownedby(owner, (owned1, owned2))), z3.UGE(perm_inv(owner, owned1, owned2), ref(owner))) )))) # Checks that the refcnt don't overflows, that if its # value is 0 that the owner owns nothing and if its value is max_refs that # process owns all the resources. # refcount is in the range 0 <= r <= max_refs conj.append(z3.ForAll([owner], z3.Implies(is_owner_valid(owner), z3.ULE(ref(owner), max_refs)))) # if refcount is 0, then you own no pages conj.append(z3.ForAll([owner, owned1, owned2], z3.Implies( z3.And( is_owner_valid(owner), is_owned_valid(owned1, owned2), ref(owner) == z3.BitVecVal(0, self.get_types()[-1]), ), z3.Not(ownedby(owner, (owned1, owned2))), ))) # if refcount is max_refs, then that owner owns all the resources conj.append(z3.ForAll([owner, owned1, owned2], z3.Implies( z3.And( is_owner_valid(owner), is_owned_valid(owned1, owned2), ref(owner) == max_refs ), ownedby(owner, (owned1, owned2)), )))
def uge(self, other): return Bool(z3.UGE(self.symbol, other.symbol))
from os import cpu_count from rfb_mc.implementation.eamp.eamp_edge_scheduler import EampEdgeScheduler from rfb_mc.implementation.eamp.eamp_rfmi_z3 import EampRfmiZ3 from rfb_mc.implementation.multi_processing_integrator_z3 import MultiProcessingIntegratorZ3 from rfb_mc.implementation.runner_z3 import RunnerZ3, FormulaParamsZ3 from rfb_mc.implementation.in_memory_store import InMemoryStore from rfb_mc.store import StoreData from rfb_mc.types import Params RunnerZ3.register_restrictive_formula_module_implementation(EampRfmiZ3) if __name__ == "__main__": k = 18 x, y, z = z3.BitVecs("x y z", k) f = z3.And([ z3.UGE(x, 0), z3.UGE(y, 0), z3.URem(x, 4) == 0, z3.URem(y, 5) == 0, z3.ULT(z, x + y), ]) s2 = perf_counter() s = perf_counter() a = 100 store = InMemoryStore(data=StoreData(params=Params( bit_width_counter=Counter({k: 2})))) print(
# This check is done on 32 bits, but the size is not use by Miasm formulas, so # it should be OK for any size > 0 x1 = ExprId("x1", 32) x2 = ExprId("x2", 32) i1_tmp = ExprInt(1, 1) x1_z3 = trans.from_expr(x1) x2_z3 = trans.from_expr(x2) i1_z3 = trans.from_expr(i1_tmp) # (Assumptions, function(arg1, arg2) -> True/False (= i1/i0) to check) tests = [ (x1_z3 == x2_z3, expr_is_equal), (x1_z3 != x2_z3, expr_is_not_equal), (z3.UGT(x1_z3, x2_z3), expr_is_unsigned_greater), (z3.UGE(x1_z3, x2_z3), expr_is_unsigned_greater_or_equal), (z3.ULT(x1_z3, x2_z3), expr_is_unsigned_lower), (z3.ULE(x1_z3, x2_z3), expr_is_unsigned_lower_or_equal), (x1_z3 > x2_z3, expr_is_signed_greater), (x1_z3 >= x2_z3, expr_is_signed_greater_or_equal), (x1_z3 < x2_z3, expr_is_signed_lower), (x1_z3 <= x2_z3, expr_is_signed_lower_or_equal), ] for assumption, func in tests: solver = z3.Solver() solver.add(assumption) solver.add(trans.from_expr(func(x1, x2)) != i1_z3) assert solver.check() == z3.unsat x = ExprId('x', 32)
def uge(self, ctx, return_type, a, atype, b, btype, **kwargs): assert atype == btype return z3.UGE(a, b)
def bvuge(self, other): return self.get_family().Bit(z3.UGE(self.value, other.value))
things = name.split('[')[1:] # print "things:".format(things) if not all(t.endswith(']') for t in things): raise SyntaxError() return [int(t[:-1]) for t in things] def is_solver_sort(name): return name.startswith('bv[') and name.endswith(']') or name == 'int' relations_dict = { '<': (lambda x, y: z3.ULT(x, y) if z3.is_bv(x) else x < y), '<=': (lambda x, y: z3.ULE(x, y) if z3.is_bv(x) else x <= y), '>': (lambda x, y: z3.UGT(x, y) if z3.is_bv(x) else x > y), '>=': (lambda x, y: z3.UGE(x, y) if z3.is_bv(x) else x >= y), } def relations(name): return relations_dict.get(name) functions_dict = { "+": (lambda x, y: x + y), "-": my_minus, "*": (lambda x, y: x * y), "concat": (lambda x, y: z3.Concat(x, y)), }
def VertexOperationToSmt(self): assert (self.type != VertexNode.VertexType.NONE) if self.type == VertexNode.VertexType.VAR: # Possible Vertex : input Variable, name = operand1 # input variable: there is nothing to do. # assigned Variable: name = operands[0] # It's an input variable if there is no operand : if self.operands == None: return None # otherwise, it's an assigned variable, but make sure just in case assert (self.operator == VertexNode.OpCode.ASSIGN) return self.VertexNameToSmt() == self.operands[0].VertexNameToSmt() elif self.type == VertexNode.VertexType.TEMP: # Possible Vertex : Function Call, Array Load, Binary Operation, Comparison, # Conditional Assignment, Unary Operation # function call: name = func_name(arguments) # array load: name = array[index] # binary operation: name = operand1 op operand2 # comparison: name = operand1 comp operand2 # conditional assignment: name = ite(operand1, operand2, operand3) # unary operation: name = op operand1 # It's a function call if self.operator == VertexNode.OpCode.FUNCCALL: assert (self.operands[0].type == VertexNode.VertexType.FUNC) # There are four possible functions that can last until now: if self.operands[0].name == "merge": args = [] for op in self.operands[1:]: args.append(op.VertexNameToSmt()) return self.VertexNameToSmt() == z3.Concat(args) elif self.operands[0].name == "split": toSplit = self.operands[1].VertexNameToSmt() # Extract requires actual numerical value. lowerBound = self.operands[2].value upperBound = self.operands[3].value return self.VertexNameToSmt() == z3.Extract( upperBound, lowerBound, toSplit) elif self.operands[0].name == "zeroext": toExtend = self.operands[1].VertexNameToSmt() # ZeroExt requires actual numerical value n = self.operands[2].value return self.VertexNameToSmt() == z3.ZeroExt(n, toExtend) elif self.operands[0].name == "concat": args = [] for op in self.operands[1:]: args.append(op.VertexNameToSmt()) return self.VertexNameToSmt() == z3.Concat(args) # It's an array load elif self.operator == VertexNode.OpCode.LOAD: array = self.operands[0].VertexNameToSmt() arrayIndex = self.operands[1].VertexNameToSmt() return self.VertexNameToSmt() == z3.Select(array, arrayIndex) # It's a conditional statement elif self.operator == VertexNode.OpCode.CONDITIONAL: cond = self.operands[0].VertexNameToSmt() truePath = self.operands[1].VertexNameToSmt() falsePath = self.operands[2].VertexNameToSmt() return self.VertexNameToSmt() == z3.If(cond, truePath, falsePath) # It's a comparison (x < y) elif VertexNode.OpCode.IsComparison(self.operator): lhs = self.operands[0].VertexNameToSmt() rhs = self.operands[1].VertexNameToSmt() if self.operator == VertexNode.OpCode.GT: return self.VertexNameToSmt() == z3.UGT(lhs, rhs) elif self.operator == VertexNode.OpCode.GE: return self.VertexNameToSmt() == z3.UGE(lhs, rhs) elif self.operator == VertexNode.OpCode.LT: return self.VertexNameToSmt() == z3.ULT(lhs, rhs) elif self.operator == VertexNode.OpCode.LE: return self.VertexNameToSmt() == z3.ULE(lhs, rhs) elif self.operator == VertexNode.OpCode.EQ: return self.VertexNameToSmt() == (lhs == rhs) elif self.operator == VertexNode.OpCode.NE: return self.VertexNameToSmt() == (lhs != rhs) # It's a binary operation elif VertexNode.OpCode.IsBinaryOp(self.operator): lhs = self.operands[0].VertexNameToSmt() rhs = self.operands[1].VertexNameToSmt() if self.operator == VertexNode.OpCode.PLUS: return self.VertexNameToSmt() == (lhs + rhs) elif self.operator == VertexNode.OpCode.MINUS: return self.VertexNameToSmt() == (lhs - rhs) elif self.operator == VertexNode.OpCode.AND: return self.VertexNameToSmt() == (lhs & rhs) elif self.operator == VertexNode.OpCode.OR: return self.VertexNameToSmt() == (lhs | rhs) elif self.operator == VertexNode.OpCode.XOR: return self.VertexNameToSmt() == (lhs ^ rhs) elif self.operator == VertexNode.OpCode.SHL: return self.VertexNameToSmt() == (lhs << rhs) elif self.operator == VertexNode.OpCode.SHR: return self.VertexNameToSmt() == (z3.LShR(lhs, rhs)) elif self.operator == VertexNode.OpCode.ROL: return self.VertexNameToSmt() == (z3.RotateLeft(lhs, rhs)) elif self.operator == VertexNode.OpCode.ROR: return self.VertexNameToSmt() == (z3.RotateRight(lhs, rhs)) elif self.operator == VertexNode.OpCode.MUL: return self.VertexNameToSmt() == (lhs * rhs) elif self.operator == VertexNnode.OpCode.DIV: return self.VertexNameToSmt() == (lhs / rhs) # It's a unary operation elif VertexNode.OpCode.IsUnaryOp(self.operator): rhs = self.operands[0].VertexNameToSmt() if self.operator == VertexNode.OpCode.NOT: return self.VertexNameToSmt() == ~rhs elif self.type == VertexNode.VertexType.IMM: # Possible Vertex : Immediate Value return None elif self.type == VertexNode.VertexType.ARR: # Possible Vertex : Input array, array store # input array: there is nothing to do # array store: newarray = store(array, index, value) # if operator == None, it's an "input" array if self.operator == None: return None if self.operator == VertexNode.OpCode.NONE: return None # Otherwise, it must be an array store operation vertex assert (self.operator == VertexNode.OpCode.STORE) oldArray = self.operands[0].VertexNameToSmt() index = self.operands[1].VertexNameToSmt() value = self.operands[2].VertexNameToSmt() newArray = self.VertexNameToSmt() return newArray == z3.Store(oldArray, index, value) elif self.type == VertexNode.VertexType.FUNC: # Possible Vertex : Name of the function return None
lx12 = z3.Int('lx12') lx21 = z3.Int('lx21') lx22 = z3.Int('lx22') lx31 = z3.Int('lx31') lx32 = z3.Int('lx32') lx41 = z3.Int('lx41') lx42 = z3.Int('lx42') # List of components. phi-lib phi1 = (Y1 == X11 ^ X12) phi2 = (Y2 == -z3.If(z3.UGT(X21, X22), z3.BitVecVal(0, 8), z3.BitVecVal(1, 8))) phi3 = (Y3 == X31 & X32) phi4 = (Y4 == X41 ^ X42) # Write the spec spec = z3.And(z3.Implies(z3.UGE(J, I), O == J), z3.Implies(z3.UGT(I, J), O == I)) # phi cons = line number of two different instructions cannot be the same phicons = z3.And(ly1 != ly2, ly2 != ly3, ly1 != ly3, ly1 != ly4, ly4 != ly2, ly4 != ly3) # We only have three instructions. # Bound the line number of each instruction and operand. phibound = z3.And(ly1 >= 1, ly1 <= 4, ly2 >= 1, ly2 <= 4, ly3 >= 1, ly3 <= 4, ly4 >= 1, ly4 <= 4, lx11 >= -1, lx11 <= 4, lx12 >= -1, lx12 <= 4, lx21 >= -1, lx21 <= 4, lx22 >= -1, lx22 <= 4, lx31 >= -1, lx31 <= 4, lx32 >= -1, lx32 <= 4, lx41 >= -1, lx41 <= 4, lx42 >= -1, lx42 <= 4) # The operands of an instruction should use variables from previous lines. acyclicity