def do_SIGN(op, stack, state): arg1, arg2 = pop_values(stack, state, 2) size = arg2 if z3.is_bv(size): size = size.as_long() if not z3.is_bv(arg1): arg1 = z3.BitVecVal(arg1, SIZE) stack.append(z3.SignExt(SIZE - size, z3.Extract(size - 1, 0, arg1)))
def __setitem__(self, addr, value): if isinstance(addr, int) or z3.is_bv(addr): return self.write(addr, value) elif isinstance(addr, str): addr = self.r2api.get_address(addr) return self.write(addr, value) elif isinstance(addr, slice): length = int(addr.stop - addr.start) if isinstance(value, list): self.write(addr.start, value[:length]) elif z3.is_bv(value): new_val = z3.Extract(length * 8 - 1, 0, value) self.write(addr.start, new_val)
def __setitem__(self, addr, value): if type(addr) == int or z3.is_bv(addr): return self.write(addr, value) elif type(addr) == str: addr = self.r2api.get_address(addr) return self.write(addr, value) elif type(addr) == slice: length = int(addr.stop - addr.start) if type(value) == list: self.write(addr.start, value[:length]) elif z3.is_bv(value): new_val = z3.Extract(length * 8 - 1, 0, value) self.write(addr.start, new_val)
def write(self, addr, data): if self._refs["count"] > 1: self.finish_clone() if type(addr) != int: addr = self.bv_to_int(addr) if z3.is_bv(data): length = int(data.size() / BYTE) data = self.unpack_bv(data, length) elif type(data) == bytes: data = list(data) elif type(data) == str: data = list(data.encode()) + [0] # add null byte elif type(data) == int: data = self.unpack_bv(data, int(self.bits / 8)) data = self.prepare_data(data) maddr = self.mask(addr) offset = addr - maddr length = len(data) if maddr != addr or length % self.chunklen != 0: prev_len = length + (self.chunklen - (length % self.chunklen)) prev = self.read(maddr, prev_len) data = prev[:offset] + data + prev[offset + length:] chunks = int(length / self.chunklen) + min(1, length % self.chunklen) for chunk in range(chunks): o = chunk * self.chunklen caddr = maddr + o self._memory[caddr] = data[o:o + self.chunklen]
def prepare(val, signext=False, size=SIZE) -> z3.BitVecRef: if z3.is_bv(val): szdiff = size - val.size() if szdiff > 0: if signext: result = z3.SignExt(szdiff, val) else: result = z3.ZeroExt(szdiff, val) elif szdiff < 0: result = z3.Extract(size - 1, 0, val) else: result = val elif type(val) == int: result = z3.BitVecVal(val, size) elif z3.is_int(val): result = z3.Int2BV(val, size) elif z3.is_fp(val): # changing up this logic to align with r2ghidra impl result = z3.fpToIEEEBV(val) #result = val else: result = z3.BitVecVal(val, size) #return z3.simplify(result) return result
def decode_instruction(address, data): """ data is the raw bytes, in little-endian, of at least the area containing all information about the current instruction. You can give more, but you need at least enough to fully decode the instruction, so 6 bytes should be enough. Reference: http://mspgcc.sourceforge.net/manual/x223.html https://en.wikipedia.org/wiki/MSP430#MSP430_CPU """ is_single_operand_instruction = lambda x: (x >> 10) == 0b000100 is_jump_instruction = lambda x: (x >> 13) == 0b001 is_double_operand_instruction = lambda x: \ not is_single_operand_instruction(x) and not is_jump_instruction(x) # turn a list of BitVecVals and ints into a list of ints unBVV = lambda l: [simplify(x).as_long() if is_bv(x) else x for x in data] data = unBVV(data) instruction = int.from_bytes(data[:2], 'little') if is_single_operand_instruction(instruction): return decode_single_operand_instruction(address, data) elif is_jump_instruction(instruction): return decode_jump_instruction(address, data) elif is_double_operand_instruction(instruction): return decode_double_operand_instruction(address, data) else: raise ValueError( \ '0x{:x} does not look like a valid MSP430 instruction!'.format( \ instruction))
def check_addr(self, bv, mode="r", length=None, data=None): if isinstance(bv, int): return elif z3.is_bv_value(bv): return bv = z3.simplify(bv) if z3.is_bv_value(bv): return elif z3.is_bv(bv): mode_to_event = { "r": ESILSolveEvent.SymRead, "w": ESILSolveEvent.SymWrite, "x": ESILSolveEvent.SymExec, "f": ESILSolveEvent.SymFree } event = mode_to_event[mode] if event in self.events: # normalize everything to be bvs if isinstance(length, int): length = BV(length, self.bits) if isinstance(data, list): data = self.memory.pack_bv(data) ctx = EventContext(bv, length, data) for hook in self.events[event]: hook(self, ctx)
def write_con(self, addr, data): if self._refs["count"] > 1: self.finish_clone() if self.check_perms: self.check(addr, "w") if z3.is_bv(data): length = data.size() // 8 data = self.unpack_bv(data, length) elif isinstance(data, bytes): data = list(data) elif isinstance(data, str): data = list(data.encode()) + [0] # add null byte elif isinstance(data, int): data = self.unpack_bv(data, self.bits // 8) data = self.prepare_data(data) maddr = self.mask(addr) offset = addr - maddr length = len(data) if maddr != addr or length % self.chunklen != 0: prev_len = length + (self.chunklen - (length % self.chunklen)) prev = self.read(maddr, prev_len) data = prev[:offset] + data + prev[offset + length:] chunks = length // self.chunklen + min(1, length % self.chunklen) for chunk in range(chunks): o = chunk * self.chunklen caddr = maddr + o self._memory[caddr] = data[o:o + self.chunklen]
def __setitem__(self, key: str, val): """ Set register value """ if self._refs["count"] > 1: self.finish_clone() if key in self.aliases: key = self.aliases[key]["reg"] if key not in self._registers: return register = self._registers[key] # hack for pcode, always weak set flags if register["type_str"] == "flg": self.weak_set(key, val) return reg_value = self.get_register_from_bounds(register) # idk this should be fine and its faster but gives complex answers if False and z3.is_bv(val) and val.size() == reg_value["size"]: reg_value["bv"] = z3.simplify(val) else: zero = z3.BitVecVal(0, reg_value["size"]) new_reg = self.set_register_bits(register, reg_value, zero, val) reg_value["bv"] = z3.simplify(new_reg)
def bv_to_int(self, bv): bv = z3.simplify(bv) if z3.is_bv_value(bv): return bv.as_long() # this is terrible and temporary elif z3.is_bv(bv): print("symbolic addr: %s" % bv) self.hit_symbolic_addr = True sat = self.solver.check() if sat == z3.sat: model = self.solver.model() try: val = model.eval(bv, model_completion=True).as_long() #print(val) '''if self.multi_concretize: self.solver.push() self.solver.add(bv != val) vals = solver.EvalMax(self.solver, bv) if len(vals) > 0: self.concrete_addrs.append({"bv": bv, "values": vals}) self.solver.pop()''' self.solver.add(bv == val) return val except: # idk man i need a default value in case # there are no constraints on the addr self.solver.add(bv == self.default_addr) return self.default_addr else: raise ESILUnsatException("no sat symbolic address found")
def prepare(val, signext=False, size=SIZE) -> z3.BitVecRef: if z3.is_bv(val): szdiff = size-val.size() if szdiff == 0: result = val elif szdiff > 0: if signext: result = z3.SignExt(szdiff, val) else: result = z3.ZeroExt(szdiff, val) elif szdiff < 0: result = z3.Extract(size-1, 0, val) elif isinstance(val, int): result = z3.BitVecVal(val, size) elif z3.is_int(val): result = z3.Int2BV(val, size) elif z3.is_fp(val): result = z3.fpToIEEEBV(val) else: result = z3.BitVecVal(val, size) if not z3.is_bv_value(result): return z3.simplify(result) else: return result
def do_SIGN(op, stack, state): arg1, arg2 = pop_values(stack, state, 2) size = arg2 if z3.is_bv(size): size = size.as_long() szdiff = SIZE - size stack.append(z3.SignExt(szdiff, z3.Extract(size - 1, 0, arg1)))
def is_symbolic(state, val): """ return whether or not a value is symbolic within a state """ if not is_bv(val): return False val = simplify(val) return not isinstance(val, BitVecNumRef)
def __getitem__(self, addr) -> z3.BitVecRef: length = self.chunklen if isinstance(addr, int) or z3.is_bv(addr): return self.read_bv(addr, length) elif isinstance(addr, str): addr = self.r2api.get_address(addr) return self.read_bv(addr, length) elif isinstance(addr, slice): length = int(addr.stop - addr.start) return self.read_bv(addr.start, length)
def __getitem__(self, addr) -> z3.BitVecRef: length = self.chunklen if type(addr) == int or z3.is_bv(addr): return self.read_bv(addr, length) elif type(addr) == str: addr = self.r2api.get_address(addr) return self.read_bv(addr, length) elif type(addr) == slice: length = int(addr.stop - addr.start) return self.read_bv(addr.start, length)
def is_typed_bv_be32(bv_variable): if not z3.is_bv(bv_variable): return False if not bv_variable.decl().kind() == z3.Z3_OP_UNINTERPRETED: return False if bv_variable.size() != 32: return False if bv_variable.decl().name().endswith("_intbe:32"): return True return False
def get_bv(exp): """ Ensures that the returned value is a binary vector by transforming the expression if necessary. :param exp: expression to transform into a binary vector :return: a binary vector """ if is_bv(exp): return exp if type(exp) == int: return BitVecVal(exp, 256) raise ValueError("Bitvector or int needed")
def genmask(bits): if not isinstance(bits, int): bits = z3.simplify(bits) if z3.is_bv(bits): bits = bits.as_long() m = (2 << 63) - 1 if(bits > 0 and bits < 64): m = (2 << bits) - 1 return m
def path_specific_var(prefix, var, var_mapping): """If var is not fixed, returns a renamed copy that can be constrained without affecting the original var, or copies with different prefixes. var_mapping caches all path specific variables.""" assert z3.is_const(var), var if var_is_fixed(var): # No ability or need to convert fixed values return var # Only currently need to support bitvecs, expand as needed. assert z3.is_bv(var), var return var_mapping.setdefault(var, z3.BitVec(prefix + str(var), var.size()))
def __k_bool__(self): if z3.is_int(self.value) or z3.is_real(self.value) or z3.is_bv( self.value): yield inference.InferenceResult(Z3Proxy.init_expr( self.value != 0, self.defaults), status=True) elif z3.is_string(self.value): yield inference.InferenceResult(Z3Proxy.init_expr( self.value != z3.StringVal(""), self.defaults), status=True) else: yield inference.InferenceResult(self, status=True)
def step(self, enable_unsound_optimizations=True): """ Tick the cpu forward one instruction. Returns a list of successor states. """ instruction_pointer = self.cpu.registers[Register.R0] if z3.is_bv(instruction_pointer): instruction_pointer = z3.simplify(instruction_pointer).as_long() # pull enough to encode any instruction raw_instruction = \ self.memory[instruction_pointer : instruction_pointer + 6] instruction, instruction_length = \ decode_instruction(instruction_pointer, raw_instruction) #print(instruction, instruction_length) step_functions = { Opcode.RRC: self.cpu.step_rrc, Opcode.SWPB: self.cpu.step_swpb, Opcode.RRA: self.cpu.step_rra, Opcode.SXT: self.cpu.step_sxt, Opcode.PUSH: self.cpu.step_push, Opcode.CALL: self.cpu.step_call, Opcode.RETI: self.cpu.step_reti, Opcode.JNZ: self.cpu.step_jnz, Opcode.JZ: self.cpu.step_jz, Opcode.JNC: self.cpu.step_jnc, Opcode.JC: self.cpu.step_jc, Opcode.JN: self.cpu.step_jn, Opcode.JGE: self.cpu.step_jge, Opcode.JL: self.cpu.step_jl, Opcode.JMP: self.cpu.step_jmp, Opcode.MOV: self.cpu.step_mov, Opcode.ADD: self.cpu.step_add, Opcode.ADDC: self.cpu.step_addc, Opcode.SUBC: self.cpu.step_subc, Opcode.SUB: self.cpu.step_sub, Opcode.CMP: self.cpu.step_cmp, Opcode.DADD: self.cpu.step_dadd, Opcode.BIT: self.cpu.step_bit, Opcode.BIC: self.cpu.step_bic, Opcode.BIS: self.cpu.step_bis, Opcode.XOR: self.cpu.step_xor, Opcode.AND: self.cpu.step_and, } self.cpu.registers[ Register.R0] += instruction_length # preincrement ip instruction_fn = step_functions[instruction.opcode] successor_states = instruction_fn(self, instruction, \ enable_unsound_optimizations=enable_unsound_optimizations) return successor_states
def genmask(bits): if type(bits) != int: bits = z3.simplify(bits) if z3.is_bv(bits): bits = bits.as_long() m = (2 << 63) - 1 if (bits > 0 and bits < 64): m = (2 << bits) - 1 return m
def prepare(val): if z3.is_bv(val): #print(val) szdiff = SIZE - val.size() #print(szdiff, val.size()) if szdiff > 0: return z3.ZeroExt(szdiff, val) else: return val elif z3.is_int(val): return z3.Int2BV(val, SIZE) else: return z3.BitVecVal(val, SIZE)
def data_to_bv(self, data): if z3.is_bv(data): return data elif isinstance(data, list): data = self.pack_bv(data) elif isinstance(data, bytes): data = self.pack_bv(list(data)) elif isinstance(data, str): data = self.pack_bv(list(data.encode()) + [0]) # add null byte elif isinstance(data, int): data = BV(data, self.bits) return data
def constrain_bytes(self, bv, regex: Union[str, bytes]): # if its a bytes expr just constrain beginning to those values if type(regex) == bytes: for i in range(len(regex)): self.constrain(z3.Extract(7 + i * 8, i * 8, bv) == regex[i]) return all_bytes = "".join([chr(x) for x in range(256)]) if z3.is_bv(bv): bv = [ z3.Extract(b * 8 + 7, b * 8, bv) for b in range(int(bv.size() / 8)) ] # this is gross and could probably break opts = [] new_regex = regex[:] negate = False if len(regex) > 2 and regex[:2] == "[^": negate = True new_regex = new_regex.replace("[^", "[") dashes = [i for i, c in enumerate(regex) if c == "-"] for d in dashes: if regex[d - 1] != "\\" and len(regex) > d: x = ord(regex[d - 1]) y = ord(regex[d + 1]) opts.append([x, y]) new_regex = new_regex.replace(regex[d - 1:d + 2], "") vals = [] if new_regex != "[]": vals = [ ord(x) for x in re.findall(new_regex, all_bytes, re.DOTALL) ] for b in bv: or_vals = [] for val in vals: or_vals.append(b == val) for opt in opts: or_vals.append(z3.And(b >= opt[0], b <= opt[1])) if negate: self.constrain(z3.Not(z3.Or(*or_vals))) else: self.constrain(z3.Or(*or_vals))
def _concretize(self, value): if is_bv(value): # try to simplify and deal with BV's that come in... value = simplify(value) if isinstance(value, BitVecNumRef): value = value.as_long( ) # if this succeeds, we had a single value! yay else: # symbolic lengths # TODO: deal with symbolic values raise ValueError( 'Tried to concretize symbolic data in an input... much danger, much scare, much fail!' ) return value
def get_children_of(z3from): children = z3from.children() children_converted = [] found_one_bv = False found_one_not_bv = False for child in children: converted_child = from_bv_to_int_domain(child) if z3.is_bv(converted_child): found_one_bv = True else: found_one_not_bv = True children_converted.append(converted_child) #if some of the children are still bv but not all of them, its a problem if found_one_bv and found_one_not_bv: return children return children_converted
def bv_length(e): li = [-1] if e in seen: return -1 if (z3.is_bv(e) and z3.is_const(e) and e.decl().kind() == z3.Z3_OP_UNINTERPRETED): li.append(e.size()) seen.add(e) if z3.is_app(e): for ch in e.children(): li.append(bv_length(ch)) elif z3.is_quantifier(e): for ch in e.body().children(): li.append(bv_length(ch)) return max(li)
def try_bool(b): # Just ignore attributes here if isinstance(b, Value): b = b.value if z3.is_bv(b) or isinstance(b, int): b = (b != 0) b = try_simplify(b) if isinstance(b, bool): return (b, None) # HACK: z3 python interface has bug/weird behavior where (x == y) is # always False for unknown x and y, so use is_true and is_false instead if z3.is_true(b): return (True, None) if z3.is_false(b): return (False, None) return (None, b)
def extend(value, width, signed=None): if not is_z3(value): return value # XXX range check if not z3.is_bv(value): value = z3.Int2BV(value, width) assert value.size() > 0 diff = try_simplify(width - value.size()) if diff > 0: if signed is None: # XXX default to unsigned signed = False if signed: return z3.SignExt(diff, value) else: return z3.ZeroExt(diff, value) return value
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)),
#sorts = {} #sorts = {"S":S, # "Int":z3.IntSort()} 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)), }