def _gen_path_constraints(self, translator, expr, expected): """Generate path constraint from @expr. Handle special case with generated loc_keys """ out = [] expected = self._ira.loc_db.canonize_to_exprloc(expected) expected_is_loc_key = expected.is_loc() for consval in possible_values(expr): value = self._ira.loc_db.canonize_to_exprloc(consval.value) if expected_is_loc_key and value != expected: continue if not expected_is_loc_key and value.is_loc_key(): continue conds = z3.And(*[ translator.from_expr(cond.to_constraint()) for cond in consval.constraints ]) if expected != value: conds = z3.And(conds, translator.from_expr(ExprAff(value, expected))) out.append(conds) if out: conds = z3.Or(*out) else: # Ex: expr: lblgen1, expected: 0x1234 # -> Avoid unconsistent solution lblgen1 = 0x1234 conds = translator.from_expr(self.unsat_expr) return conds
def _gen_path_constraints(self, translator, expr, expected): """Generate path constraint from @expr. Handle special case with generated labels """ out = [] expected_is_label = expr_is_label(expected) for consval in possible_values(expr): if (expected_is_label and consval.value != expected): continue if (not expected_is_label and expr_is_label(consval.value)): continue conds = z3.And(*[ translator.from_expr(cond.to_constraint()) for cond in consval.constraints ]) if expected != consval.value: conds = z3.And( conds, translator.from_expr( m2_expr.ExprAff(consval.value, expected))) out.append(conds) if out: conds = z3.Or(*out) else: # Ex: expr: lblgen1, expected: 0x1234 # -> Avoid unconsistent solution lblgen1 = 0x1234 conds = translator.from_expr(self.unsat_expr) return conds
def _gen_path_constraints(self, translator, expr, expected): """Generate path constraint from @expr. Handle special case with generated labels """ out = [] expected_is_label = expr_is_label(expected) for consval in possible_values(expr): if (expected_is_label and consval.value != expected): continue if (not expected_is_label and expr_is_label(consval.value)): continue conds = z3.And(*[translator.from_expr(cond.to_constraint()) for cond in consval.constraints]) if expected != consval.value: conds = z3.And(conds, translator.from_expr( m2_expr.ExprAff(consval.value, expected))) out.append(conds) if out: conds = z3.Or(*out) else: # Ex: expr: lblgen1, expected: 0x1234 # -> Avoid unconsistent solution lblgen1 = 0x1234 conds = translator.from_expr(self.unsat_expr) return conds
def _gen_path_constraints(self, translator, expr, expected): """Generate path constraint from @expr. Handle special case with generated loc_keys """ out = [] expected = self._ira.symbol_pool.canonize_to_exprloc(expected) expected_is_loc_key = expected.is_loc() for consval in possible_values(expr): value = self._ira.symbol_pool.canonize_to_exprloc(consval.value) if expected_is_loc_key and value != expected: continue if not expected_is_loc_key and value.is_loc_key(): continue conds = z3.And(*[translator.from_expr(cond.to_constraint()) for cond in consval.constraints]) if expected != value: conds = z3.And( conds, translator.from_expr( ExprAff(value, expected)) ) out.append(conds) if out: conds = z3.Or(*out) else: # Ex: expr: lblgen1, expected: 0x1234 # -> Avoid unconsistent solution lblgen1 = 0x1234 conds = translator.from_expr(self.unsat_expr) return conds
def del_unused_edges(ircfg, heads): """ Delete non accessible edges in the @ircfg graph. @ircfg: IRCFG instance in ssa form @heads: location of the heads of the graph """ deleted_vars = set() modified = False edges_to_del_1 = set() for node in ircfg.nodes(): successors = set(ircfg.successors(node)) block = ircfg.blocks[node] dst = block.dst possible_dsts = set(solution.value for solution in possible_values(dst)) if not all(dst.is_loc() for dst in possible_dsts): continue possible_dsts = set(dst.loc_key for dst in possible_dsts) if len(possible_dsts) == len(successors): continue dsts_to_del = successors.difference(possible_dsts) for dst in dsts_to_del: edges_to_del_1.add((node, dst)) # Remove edges and update phi accordingly # Two cases here: # - edge is directly linked to a phi node # - edge is indirect linked to a phi node nodes_to_del, edges_to_del_2 = get_unreachable_nodes( ircfg, edges_to_del_1, heads) modified |= update_phi_with_deleted_edges( ircfg, edges_to_del_1.union(edges_to_del_2)) for src, dst in edges_to_del_1.union(edges_to_del_2): ircfg.del_edge(src, dst) for node in nodes_to_del: block = ircfg.blocks[node] ircfg.del_node(node) for assignblock in block: for dst in assignblock: deleted_vars.add(dst) if deleted_vars: modified |= discard_phi_sources(ircfg, deleted_vars) return modified
def compute_cst_propagation_states(ir_arch, ircfg, init_addr, init_infos): """ Propagate "constant expressions" in a function. The attribute "constant expression" is true if the expression is based on constants or "init" regs values. @ir_arch: IntermediateRepresentation instance @init_addr: analysis start address @init_infos: dictionnary linking expressions to their values at @init_addr """ done = set() state = SymbExecState.StateEngine(init_infos) lbl = ircfg.get_loc_key(init_addr) todo = set([lbl]) states = {lbl: state} while todo: if not todo: break lbl = todo.pop() state = states[lbl] if (lbl, state) in done: continue done.add((lbl, state)) if lbl not in ircfg.blocks: continue symbexec_engine = SymbExecState(ir_arch, ircfg, state) addr = symbexec_engine.run_block_at(ircfg, lbl) symbexec_engine.del_mem_above_stack(ir_arch.sp) for dst in possible_values(addr): value = dst.value if value.is_mem(): LOG_CST_PROPAG.warning('Bad destination: %s', value) continue elif value.is_int(): value = ircfg.get_loc_key(value) add_state( ircfg, todo, states, value, symbexec_engine.get_state() ) return states
def compute_cst_propagation_states(ir_arch, init_addr, init_infos): """ Propagate "constant expressions" in a function. The attribute "constant expression" is true if the expression is based on constants or "init" regs values. @ir_arch: IntermediateRepresentation instance @init_addr: analysis start address @init_infos: dictionnary linking expressions to their values at @init_addr """ done = set() state = SymbExecState.StateEngine(init_infos) lbl = ir_arch.get_label(init_addr) todo = set([lbl]) states = {lbl: state} while todo: if not todo: break lbl = todo.pop() state = states[lbl] if (lbl, state) in done: continue done.add((lbl, state)) if lbl not in ir_arch.blocks: continue symbexec_engine = SymbExecState(ir_arch, state) addr = symbexec_engine.emul_ir_block(lbl) symbexec_engine.del_mem_above_stack(ir_arch.sp) for dst in possible_values(addr): value = dst.value if value.is_mem(): LOG_CST_PROPAG.warning('Bad destination: %s', value) continue elif value.is_int(): value = ir_arch.get_label(value) add_state(ir_arch, todo, states, value, symbexec_engine.get_state()) return states
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 xrange(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 handle(self, cur_addr): symb_pc = self.eval_expr(self.ir_arch.IRDst) possibilities = possible_values(symb_pc) if len(possibilities) == 1: assert next(iter(possibilities)).value == cur_addr else: cur_path_constraint = set() # path_constraint for the concrete path for possibility in possibilities: path_constraint = set() # Set of ExprAff 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 # ExprAff 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.arg) # 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 xrange(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(ExprAff(expr_mem, value)) if possibility.value == cur_addr: # Add path constraint cur_path_constraint = path_constraint elif self.produce_solution: # 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, possibility.value) self.cur_solver.pop() # Update current solver for cons in cur_path_constraint: self.cur_solver.add(self.z3_trans.from_expr(cons))