def _max(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.UGT(expr, middle), z3.ULE(expr, hi)) numpop += 1 solve_count += 1 l.debug("Doing a check!") if solver.check() == z3.sat: l.debug("... still sat") lo = middle vals.add(self._primitive_from_model(solver.model(), expr)) if model_callback is not None: model_callback(self._generic_model(solver.model())) else: l.debug("... now unsat") hi = middle solver.pop() numpop -= 1 #l.debug(" now: %d %d %d %d", hi, middle, lo, hi-lo) for _ in range(numpop): solver.pop() if hi == lo: vals.add(hi) else: solver.push() solver.add(expr == hi) 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(hi) solver.pop() else: vals.add(lo) solver.pop() return max(vals)
def _z3Clauses(self, prefix): self.bitvec = z3.BitVec('_bv_in_range_%s_%s_' % (prefix, self.name), self.width) exprs = [] loz3 = self.lo.toZ3(prefix) hiz3 = self.hi.toZ3(prefix) r1 = self.lo.z3Clauses(prefix) r2 = self.hi.z3Clauses(prefix) if r1: exprs.append(r1) if r2: exprs.append(r2) exprs.append(z3.ULE(loz3, self.bitvec)) exprs.append(z3.ULE(self.bitvec, hiz3)) return z3.And(*exprs)
def ULE(self, other): if isinstance(other, int): other = BVV(other, self.size) else: assert isinstance(other, BV) assert self.size == other.size return BoolExpr(z3.ULE(self.z3obj, other.z3obj))
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]) 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 _signbits(term, smt): x = smt.eval(term._args[0]) ty = smt.type(term) #b = ComputeNumSignBits(smt.fresh_bv(size), size) b = smt.fresh_var(ty, 'ana_') smt.add_defs(z3.ULE(b, ComputeNumSignBits(ty.width, x))) return b
def _max_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.UGT(expr, middle), z3.ULE(expr, hi)) numpop += 1 solve_count += 1 l.debug("Doing a check!") if solver.check() == z3.sat: l.debug("... still sat") lo = middle vals.add(self._primitive_from_model(solver.model(), expr)) else: l.debug("... now unsat") hi = middle solver.pop() numpop -= 1 #l.debug(" now: %d %d %d %d", hi, middle, lo, hi-lo) for _ in range(numpop): solver.pop() if hi == lo: vals.add(hi) else: solver.push() solver.add(expr == hi) l.debug("Doing a check!") if solver.check() == z3.sat: vals.add(hi) solver.pop() else: vals.add(lo) solver.pop() return vals
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 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 sys_send(old, pid, val, pn, size, fd): cond = z3.And( is_pid_valid(pid), old.procs[pid].state == dt.proc_state.PROC_SLEEPING, is_pn_valid(pn), old.pages[pn].owner == old.current, z3.ULE(size, dt.PAGE_SIZE), z3.Implies(is_fd_valid(fd), is_fn_valid(old.procs[old.current].ofile(fd))), ) new = old.copy() new.procs[pid].ipc_from = old.current new.procs[pid].ipc_val = val new.procs[pid].ipc_size = size # memcpy new.pages.data = lambda pn0, idx0, oldfn: \ util.If(z3.And(pn0 == old.procs[pid].ipc_page, z3.ULT(idx0, size)), oldfn(pn, idx0), oldfn(pn0, idx0)) ######## new2 = new.copy() cond2 = z3.And(is_fd_valid(fd), is_fd_valid(new2.procs[pid].ipc_fd)) fn = old.procs[old.current].ofile(fd) fd = old.procs[pid].ipc_fd new2.procs[pid].ofile[fd] = fn # bump proc nr_fds new2.procs[pid].nr_fds[fd] += 1 # bump file refcnt new2.files[fn].refcnt[(pid, fd)] += 1 new3 = util.If(cond2, new2, new) new3.procs[pid].state = dt.proc_state.PROC_RUNNING new3.procs[old.current].state = dt.proc_state.PROC_RUNNABLE new3.current = pid return cond, util.If(cond, new3, old)
def _uitofp(term, smt): v = smt.eval(term.arg) src = smt.type(term.arg) tgt = smt.type(term) w = 2**(tgt.exp-1) if src.width >= w: m = 2**w conds = [z3.ULE(v, m)] else: conds = [] return smt._conditional_conv_value(conds, z3.fpToFPUnsigned(z3.get_default_rounding_mode(), v, _ty_sort(tgt)), term.name)
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 _min(self, expr, extra_constraints=(), result=None, solver=None): global solve_count lo = 0 hi = 2**expr.size()-1 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") 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 NativeBVV(lo, expr.size()) else: solver.push() solver.add(expr == lo) l.debug("Doing a check!") if solver.check() == z3.sat: solver.pop() return NativeBVV(lo, expr.size()) else: solver.pop() return NativeBVV(hi, expr.size())
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 from_ExprOp(self, expr): args = list(map(self.from_expr, expr.args)) res = args[0] if len(args) > 1: for arg in args[1:]: if expr.op in self.trivial_ops: res = eval("res %s arg" % expr.op) elif expr.op == ">>": res = z3.LShR(res, arg) elif expr.op == "a>>": res = res >> arg elif expr.op == "<<<": res = z3.RotateLeft(res, arg) elif expr.op == ">>>": res = z3.RotateRight(res, arg) elif expr.op == "sdiv": res = self._sdivC(res, arg) elif expr.op == "udiv": res = z3.UDiv(res, arg) elif expr.op == "smod": res = res - (arg * (self._sdivC(res, arg))) elif expr.op == "umod": res = z3.URem(res, arg) elif expr.op == "==": res = z3.If(args[0] == args[1], z3.BitVecVal(1, 1), z3.BitVecVal(0, 1)) elif expr.op == "<u": res = z3.If(z3.ULT(args[0], args[1]), z3.BitVecVal(1, 1), z3.BitVecVal(0, 1)) elif expr.op == "<s": res = z3.If(args[0] < args[1], z3.BitVecVal(1, 1), z3.BitVecVal(0, 1)) elif expr.op == "<=u": res = z3.If(z3.ULE(args[0], args[1]), z3.BitVecVal(1, 1), z3.BitVecVal(0, 1)) elif expr.op == "<=s": res = z3.If(args[0] <= args[1], z3.BitVecVal(1, 1), z3.BitVecVal(0, 1)) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) elif expr.op == 'parity': arg = z3.Extract(7, 0, res) res = z3.BitVecVal(1, 1) for i in range(8): res = res ^ z3.Extract(i, i, arg) elif expr.op == '-': res = -res elif expr.op == "cnttrailzeros": size = expr.size src = res res = z3.If(src == 0, size, src) for i in range(size - 1, -1, -1): res = z3.If((src & (1 << i)) != 0, i, res) elif expr.op == "cntleadzeros": size = expr.size src = res res = z3.If(src == 0, size, src) for i in range(size, 0, -1): index = -i % size out = size - (index + 1) res = z3.If((src & (1 << index)) != 0, out, res) elif expr.op.startswith("zeroExt"): arg, = expr.args res = z3.ZeroExt(expr.size - arg.size, self.from_expr(arg)) elif expr.op.startswith("signExt"): arg, = expr.args res = z3.SignExt(expr.size - arg.size, self.from_expr(arg)) else: raise NotImplementedError("Unsupported OP yet: %s" % expr.op) return res
def send_recv(old, pid, val, inpn, size, infd, outpn, outfd): cond = z3.And( is_pid_valid(pid), old.procs[pid].state == dt.proc_state.PROC_SLEEPING, # inpn is a valid pn and belongs to current is_pn_valid(inpn), old.pages[inpn].owner == old.current, z3.ULE(size, dt.PAGE_SIZE), z3.Implies(is_fd_valid(infd), is_fn_valid(old.procs[old.current].ofile(infd))), # outpn is a valid pn and belongs to current is_pn_valid(outpn), old.pages[outpn].owner == old.current, old.pages[outpn].type == dt.page_type.PAGE_TYPE_FRAME, z3.Implies(is_fd_valid(outfd), z3.Not(is_fn_valid(old.procs[old.current].ofile(outfd)))), # if ipc from is set, it must be set to current z3.Implies(old.procs[pid].ipc_from != 0, old.procs[pid].ipc_from == old.current) ) new = old.copy() new.procs[old.current].ipc_page = outpn new.procs[old.current].ipc_fd = outfd new.procs[pid].ipc_from = old.current new.procs[pid].ipc_val = val # memcpy new.pages.data = lambda pn0, idx0, oldfn=new.pages.data: \ util.If(z3.And(pn0 == old.procs[pid].ipc_page, z3.ULT(idx0, size)), oldfn(inpn, idx0), oldfn(pn0, idx0)) new.procs[pid].ipc_size = size new2 = new.copy() cond2 = z3.And(is_fd_valid(infd), is_fd_valid(new2.procs[pid].ipc_fd)) fn = old.procs[old.current].ofile(infd) fd = old.procs[pid].ipc_fd new2.procs[pid].ofile[fd] = fn # bump proc nr_fds new2.procs[pid].nr_fds[fd] += 1 # bump file refcnt new2.files[fn].refcnt[(pid, fd)] += 1 new3 = util.If(cond2, new2, new) new3.procs[old.current].state = dt.proc_state.PROC_SLEEPING new3.procs[pid].state = dt.proc_state.PROC_RUNNING return cond, util.If(cond, new3, old)
ExprOp(">>>", ExprInt8(4), a), ExprOp(">>>", a, ExprInt8(4)), ExprOp(">>>", a, a), ExprOp(">>>", a[1:2].zeroExtend(8) + ExprInt8(1), ExprCond(a[0:1], ExprInt8(5), ExprInt8(18))), # Fuzzed by ExprRandom, with previous bug ExprSlice(ExprSlice(ExprOp('<<<', ExprInt(0x7FBE84D6, 51), ExprId('WYBZj', 51)), 6, 48), 3, 35), ExprOp('>>>', ExprOp('-', ExprOp('&', ExprInt(0x347384F7, 32), ExprId('oIkka', 32), ExprId('jSfOB', 32), ExprId('dUXBp', 32), ExprInt(0x7169DEAA, 32))), ExprId('kMVuR', 32)), ExprOp('|', ExprInt(0x94A3AB47, 32), ExprCompose(ExprId('dTSkf', 21), ExprOp('>>', ExprInt(0x24, 8), ExprId('HTHES', 8)), ExprId('WHNIZ', 1), ExprMem(ExprInt(0x100, 9), 1), ExprId('kPQck', 1))), ExprOp('<<<', ExprOp('<<<', ExprCompose(ExprId('OOfuB', 6), ExprInt(0x24, 11), ExprInt(0xE8C, 12), ExprId('jbUWR', 1), ExprInt(0x2, 2)), ExprId('mLlTH', 32)), ExprInt(0xE600B6B2, 32)), ]: computed_range = expr_range(expr) print expr, computed_range # Trivia checks assert all(x[1] < (1 << expr.size) for x in computed_range) # Check against z3 s = z3.Solver() cond = [] ## Constraint expr to be in computed intervals z3_expr = trans.from_expr(expr) for mini, maxi in computed_range: cond.append(z3.And(z3.ULE(mini, z3_expr), z3.ULE(z3_expr, maxi))) ## Ask for a solution outside intervals (should not exists) s.add(z3.Not(z3.Or(*cond))) assert s.check() == z3.unsat
def bvule(self, other): return self.get_family().Bit(z3.ULE(self.value, other.value))
def ule(self, other): return Bool(z3.ULE(self.symbol, other.symbol))
def walk_bv_ule(self, formula, args, **kwargs): return z3.ULE(args[0], args[1])
def semantics(self, a, b): return z3.If(z3.ULE(a, b), z3.BitVecVal(-1, 32, self.ctx), z3.BitVecVal(0, 32, self.ctx))
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
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) y = ExprId('y', 32) z = ExprId('z', 32)
def sym_call_address(self, address, wstate, method=None, arguments=None, amount=None, timestamp=None): # Initialize the execution environment account = wstate.address_to_account[address] if account.typ == AccountType.LIBRARY: return [] child_wstate = wstate.child() logging.debug(BColors.GREEN + BColors.REVERSED + 'Starting SVM execution' + BColors.ENDC + ' with address: ' + hex(address) + ' contract: ' + account.contract.name) caller_vec = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLER, child_wstate.gen) caller_constraint = z3.Or([caller_vec == p for p in self.possible_caller_addresses]) child_wstate.constraints.append(caller_constraint) calldata = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA_ARRAY, child_wstate.gen, acc=account.id) if method is not None and method.name != Method.FALLBACK: calldata_0 = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA, child_wstate.gen, index=0) calldata_4bytes, _ = svm_utils.split_bv(calldata_0, calldata_0.size() - 32) calldatasize = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATASIZE, child_wstate.gen, acc=account.id) entry_account = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.ENTRY_ACCOUNT, child_wstate.gen) method_constraint = z3.And(calldata_4bytes == method.idd, z3.ULE(4, calldatasize), entry_account == address) child_wstate.constraints.append(method_constraint) if arguments is not None and len(arguments): encoded_args = utils.abi_encode(method.inputs, arguments) for i, arg in enumerate(encoded_args): if arg is None: continue calldata_i = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA, child_wstate.gen, index=4+32*i) child_wstate.constraints.append(calldata_i == z3.BitVecVal(arg, 256)) gasprice_vec = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.GASPRICE, child_wstate.gen) callvalue_vec = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLVALUE, child_wstate.gen) origin_vec = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.ORIGIN, child_wstate.gen) timestamp_vec = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.TIMESTAMP, child_wstate.gen) parent_timestamp_vec = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.TIMESTAMP, wstate.gen) callvalue_constraint = ULT(callvalue_vec, svm_utils.ETHER_LIMIT) timestamp_constraint = ULT(parent_timestamp_vec, timestamp_vec) entry_account = self.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.ENTRY_ACCOUNT, child_wstate.gen) child_wstate.constraints.append(callvalue_constraint) child_wstate.constraints.append(timestamp_constraint) child_wstate.constraints.append(entry_account == account.address) environment = Environment(active_address=address, sender=caller_vec, calldata=calldata, gasprice=gasprice_vec, callvalue=callvalue_vec, origin=origin_vec, calldata_type=CalldataType.UNDEFINED, disassembly=account.contract.disassembly, runtime_bytecode_bytes=list(account.contract.disassembly.bytecode), timestamp=timestamp_vec) gstate = GlobalState(child_wstate, environment) return self.executor.execute_gstate(gstate)
def parse_int_params(name): 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 ule(self, ctx, return_type, a, atype, b, btype, **kwargs): assert atype == btype return z3.ULE(a, b)
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)), )))
z = exact(x, ty) if isinstance(arg, Constant): return z r = smt.new_analysis(name, arg, type=ty) smt.add_aux(*mk_implies(nx, [restrict(r, z)])) # this allows the analysis to be incorrect if the argument is poison # if the argument has undefined behavior, then the result should be irrelevant return r eval.register(SignBitsCnxp, BaseSMTEncoder, value_analysis('numsignbits', lambda x, ty: ComputeNumSignBits(ty.width, x), lambda r, z: z3.ULE(r, z))) eval.register(OneBitsCnxp, BaseSMTEncoder, value_analysis('onebits', lambda x, ty: x, lambda r, z: r & ~z == 0)) eval.register(ZeroBitsCnxp, BaseSMTEncoder, value_analysis('zerobits', lambda x, ty: ~x, lambda r, z: r & ~z == 0)) eval.register(LeadingZerosCnxp, BaseSMTEncoder, lambda term,smt: ctlz(smt.type(term).width, smt.eval(term._args[0])))