def expr_range(expr): """Return a ModularIntervals containing the range of possible values of @expr""" max_bound = (1 << expr.size) - 1 if expr.is_int(): return ModularIntervals(expr.size, [(int(expr), int(expr))]) elif expr.is_id() or expr.is_mem(): return ModularIntervals(expr.size, [(0, max_bound)]) elif expr.is_slice(): interval_mask = ((1 << expr.start) - 1) ^ ((1 << expr.stop) - 1) arg = expr_range(expr.arg) # Mask for possible range, and shift range return ((arg & interval_mask) >> expr.start).size_update(expr.size) elif expr.is_compose(): sub_ranges = [expr_range(arg) for arg in expr.args] args_idx = [info[0] for info in expr.iter_args()] # No shift for the first one ret = sub_ranges[0].size_update(expr.size) # Doing it progressively (2 by 2) for shift, sub_range in zip(args_idx[1:], sub_ranges[1:]): ret |= sub_range.size_update(expr.size) << shift return ret elif expr.is_op(): # A few operation are handled with care # Otherwise, overapproximate (ie. full range interval) if expr.op in _op_range_handler: sub_ranges = [expr_range(arg) for arg in expr.args] return reduce(_op_range_handler[expr.op], (sub_range for sub_range in sub_ranges[1:]), sub_ranges[0]) elif expr.op == "-": assert len(expr.args) == 1 return -expr_range(expr.args[0]) elif expr.op == "%": assert len(expr.args) == 2 op, mod = [expr_range(arg) for arg in expr.args] if mod.intervals.length == 1: # Modulo intervals is not supported return op % mod.intervals.hull()[0] # Operand not handled, return the full domain return ModularIntervals(expr.size, [(0, max_bound)]) elif expr.is_cond(): return expr_range(expr.src1).union(expr_range(expr.src2)) else: raise TypeError("Unsupported type: %s" % expr.__class__)
def handle(self, cur_addr): cur_addr = canonize_to_exprloc(self.ir_arch.loc_db, cur_addr) symb_pc = self.eval_expr(self.ir_arch.IRDst) possibilities = possible_values(symb_pc) cur_path_constraint = set() # path_constraint for the concrete path if len(possibilities) == 1: dst = next(iter(possibilities)).value dst = canonize_to_exprloc(self.ir_arch.loc_db, dst) assert dst == cur_addr else: for possibility in possibilities: target_addr = canonize_to_exprloc(self.ir_arch.loc_db, possibility.value) path_constraint = set( ) # Set of ExprAssign for the possible path # Get constraint associated to the possible path memory_to_add = ModularIntervals(symb_pc.size) for cons in possibility.constraints: eaff = cons.to_constraint() # eaff.get_r(mem_read=True) is not enough # ExprAssign consider a Memory access in dst as a write mem = eaff.dst.get_r(mem_read=True) mem.update(eaff.src.get_r(mem_read=True)) for expr in mem: if expr.is_mem(): addr_range = expr_range(expr.ptr) # At upper bounds, add the size of the memory access # if addr (- [a, b], then @size[addr] reachables # values are in @8[a, b + size[ for start, stop in addr_range: stop += expr.size // 8 - 1 full_range = ModularIntervals( symb_pc.size, [(start, stop)]) memory_to_add.update(full_range) path_constraint.add(eaff) if memory_to_add.length > self.MAX_MEMORY_INJECT: # TODO re-croncretize the constraint or z3-try raise RuntimeError("Not implemented: too long memory area") # Inject memory for start, stop in memory_to_add: for address in range(start, stop + 1): expr_mem = ExprMem( ExprInt(address, self.ir_arch.pc.size), 8) value = self.eval_expr(expr_mem) if not value.is_int(): raise TypeError("Rely on a symbolic memory case, " \ "address 0x%x" % address) path_constraint.add(ExprAssign(expr_mem, value)) if target_addr == cur_addr: # Add path constraint cur_path_constraint = path_constraint elif self.produce_solution(target_addr): # Looking for a new solution self.cur_solver.push() for cons in path_constraint: trans = self.z3_trans.from_expr(cons) trans = z3.simplify(trans) self.cur_solver.add(trans) result = self.cur_solver.check() if result == z3.sat: model = self.cur_solver.model() self.handle_solution(model, target_addr) self.cur_solver.pop() self.handle_correct_destination(cur_addr, cur_path_constraint)
def test(left, right): """Launch tests on left OP right""" global size, mask for left_i in left: left_i = ModularIntervals(size, left_i) left_values = list(interval_elements(left_i)) # Check operations without other arguments ## Check NEG result = - left_i for x in left_values: rez = (- x) & mask assert rez in result # Check operations on intervals for right_i in right: right_i = ModularIntervals(size, right_i) right_values = list(interval_elements(right_i)) # Check operations available only on integer if len(right_values) == 1: # Check mod value = right_values[0] # Avoid division by zero if value != 0: result = left_i % value for x in left_values: rez = (x % value) & mask assert rez in result # Check ADD result = left_i + right_i for x in left_values: for y in right_values: rez = (x + y) & mask assert rez in result # Check OR result = left_i | right_i for x in left_values: for y in right_values: rez = (x | y) & mask assert rez in result # Check AND result = left_i & right_i for x in left_values: for y in right_values: rez = (x & y) & mask assert rez in result # Check XOR result = left_i ^ right_i for x in left_values: for y in right_values: rez = (x ^ y) & mask assert rez in result # Check MUL result = left_i * right_i for x in left_values: for y in right_values: rez = (x * y) & mask assert rez in result # Check >> result = left_i >> right_i for x in left_values: for y in right_values: rez = (x >> y) & mask assert rez in result # Check << result = left_i << right_i for x in left_values: for y in right_values: rez = (x << y) & mask assert rez in result # Check a>> result = left_i.arithmetic_shift_right(right_i) for x in left_values: x = ExprInt(x, size) for y in right_values: y = ExprInt(y, size) rez = int(expr_simp(ExprOp('a>>', x, y))) assert rez in result # Check >>> result = left_i.rotation_right(right_i) for x in left_values: x = ExprInt(x, size) for y in right_values: y = ExprInt(y, size) rez = int(expr_simp(ExprOp('>>>', x, y))) assert rez in result # Check <<< result = left_i.rotation_left(right_i) for x in left_values: x = ExprInt(x, size) for y in right_values: y = ExprInt(y, size) rez = int(expr_simp(ExprOp('<<<', x, y))) assert rez in result
def test(left, right): """Launch tests on left OP right""" global size, mask for left_i in left: left_i = ModularIntervals(size, left_i) left_values = list(interval_elements(left_i)) # Check operations without other arguments ## Check NEG result = -left_i for x in left_values: rez = (-x) & mask assert rez in result # Check operations on intervals for right_i in right: right_i = ModularIntervals(size, right_i) right_values = list(interval_elements(right_i)) # Check operations available only on integer if len(right_values) == 1: # Check mod value = right_values[0] # Avoid division by zero if value != 0: result = left_i % value for x in left_values: rez = (x % value) & mask assert rez in result # Check ADD result = left_i + right_i for x in left_values: for y in right_values: rez = (x + y) & mask assert rez in result # Check OR result = left_i | right_i for x in left_values: for y in right_values: rez = (x | y) & mask assert rez in result # Check AND result = left_i & right_i for x in left_values: for y in right_values: rez = (x & y) & mask assert rez in result # Check XOR result = left_i ^ right_i for x in left_values: for y in right_values: rez = (x ^ y) & mask assert rez in result # Check MUL result = left_i * right_i for x in left_values: for y in right_values: rez = (x * y) & mask assert rez in result # Check >> result = left_i >> right_i for x in left_values: for y in right_values: rez = (x >> y) & mask assert rez in result # Check << result = left_i << right_i for x in left_values: for y in right_values: rez = (x << y) & mask assert rez in result # Check a>> result = left_i.arithmetic_shift_right(right_i) for x in left_values: x = ExprInt(x, size) for y in right_values: y = ExprInt(y, size) rez = int(expr_simp(ExprOp('a>>', x, y))) assert rez in result # Check >>> result = left_i.rotation_right(right_i) for x in left_values: x = ExprInt(x, size) for y in right_values: y = ExprInt(y, size) rez = int(expr_simp(ExprOp('>>>', x, y))) assert rez in result # Check <<< result = left_i.rotation_left(right_i) for x in left_values: x = ExprInt(x, size) for y in right_values: y = ExprInt(y, size) rez = int(expr_simp(ExprOp('<<<', x, y))) assert rez in result
def handle(self, cur_addr): cur_addr = self.ir_arch.loc_db.canonize_to_exprloc(cur_addr) symb_pc = self.eval_expr(self.ir_arch.IRDst) possibilities = possible_values(symb_pc) cur_path_constraint = set() # path_constraint for the concrete path if len(possibilities) == 1: dst = next(iter(possibilities)).value dst = self.ir_arch.loc_db.canonize_to_exprloc(dst) assert dst == cur_addr else: for possibility in possibilities: target_addr = self.ir_arch.loc_db.canonize_to_exprloc( possibility.value ) path_constraint = set() # Set of ExprAssign for the possible path # Get constraint associated to the possible path memory_to_add = ModularIntervals(symb_pc.size) for cons in possibility.constraints: eaff = cons.to_constraint() # eaff.get_r(mem_read=True) is not enough # ExprAssign consider a Memory access in dst as a write mem = eaff.dst.get_r(mem_read=True) mem.update(eaff.src.get_r(mem_read=True)) for expr in mem: if expr.is_mem(): addr_range = expr_range(expr.ptr) # At upper bounds, add the size of the memory access # if addr (- [a, b], then @size[addr] reachables # values are in @8[a, b + size[ for start, stop in addr_range: stop += expr.size // 8 - 1 full_range = ModularIntervals( symb_pc.size, [(start, stop)] ) memory_to_add.update(full_range) path_constraint.add(eaff) if memory_to_add.length > self.MAX_MEMORY_INJECT: # TODO re-croncretize the constraint or z3-try raise RuntimeError("Not implemented: too long memory area") # Inject memory for start, stop in memory_to_add: for address in range(start, stop + 1): expr_mem = ExprMem(ExprInt(address, self.ir_arch.pc.size), 8) value = self.eval_expr(expr_mem) if not value.is_int(): raise TypeError("Rely on a symbolic memory case, " \ "address 0x%x" % address) path_constraint.add(ExprAssign(expr_mem, value)) if target_addr == cur_addr: # Add path constraint cur_path_constraint = path_constraint elif self.produce_solution(target_addr): # Looking for a new solution self.cur_solver.push() for cons in path_constraint: trans = self.z3_trans.from_expr(cons) trans = z3.simplify(trans) self.cur_solver.add(trans) result = self.cur_solver.check() if result == z3.sat: model = self.cur_solver.model() self.handle_solution(model, target_addr) self.cur_solver.pop() self.handle_correct_destination(cur_addr, cur_path_constraint)