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 csneg(ir, instr, arg1, arg2, arg3, arg4): e = [] cond_expr = cond2expr[arg4.name] e.append(m2_expr.ExprAff(arg1, m2_expr.ExprCond(cond_expr, arg2, -arg3))) return e, []
def strh(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append(m2_expr.ExprAff(m2_expr.ExprMem(addr, 16), arg1[:16])) if updt: e.append(updt) return e, []
def l_str(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append(m2_expr.ExprAff(m2_expr.ExprMem(addr, arg1.size), arg1)) if updt: e.append(updt) return e, []
def ands(ir, instr, arg1, arg2, arg3): e = [] arg3 = extend_arg(arg2, arg3) res = arg2 & arg3 e += update_flag_logic(res) e.append(m2_expr.ExprAff(arg1, res)) return e, []
def csinc(ir, instr, arg1, arg2, arg3, arg4): e = [] cond_expr = cond2expr[arg4.name] e.append(m2_expr.ExprAff(arg1, m2_expr.ExprCond(cond_expr, arg2, arg3 + m2_expr.ExprInt(1, arg3.size)))) return e, []
def csetm(ir, instr, arg1, arg2): e = [] cond_expr = cond2expr[arg2.name] e.append(m2_expr.ExprAff(arg1, m2_expr.ExprCond(cond_expr, m2_expr.ExprInt_from( arg1, -1), m2_expr.ExprInt_from(arg1, 0)))) return e, []
def emit_mov(ir, instr, a, b): # movの中間表現を生成 instr_ir, extra_ir = sem.mov(ir, instr, a, b) # カウンタをインクリメントする中間表現を追加 dst = expr.ExprMem(expr.ExprInt64(ADDR_COUNTER), 64) new_value = dst + expr.ExprInt64(1) instr_ir.append(expr.ExprAff(dst, new_value)) return instr_ir, extra_ir
def lui(ir, instr, a, b): """The immediate value @b is shifted left 16 bits and stored in the register @a. The lower 16 bits are zeroes.""" e = [] e.append(m2_expr.ExprAff(a, m2_expr.ExprCompose([(m2_expr.ExprInt16(0), 0, 16), (b[:16], 16, 32)]))) return e, []
def ldr_size(ir, instr, arg1, arg2, size): e = [] addr, updt = get_mem_access(arg2) e.append( m2_expr.ExprAff(arg1, m2_expr.ExprMem(addr, size).zeroExtend(arg1.size))) if updt: e.append(updt) return e, []
def ldrsw(ir, instr, arg1, arg2): e = [] addr, updt = get_mem_access(arg2) e.append( m2_expr.ExprAff(arg1, m2_expr.ExprMem(addr, 32).signExtend(arg1.size))) if updt: e.append(updt) return e, []
def subs(ir, instr, arg1, arg2, arg3): e = [] arg3 = extend_arg(arg2, arg3) res = arg2 - arg3 e += update_flag_arith(res) e += update_flag_sub(arg2, arg3, res) e.append(m2_expr.ExprAff(arg1, res)) return e, []
def csetm(ir, instr, arg1, arg2): e = [] cond_expr = cond2expr[arg2.name] e.append(m2_expr.ExprAff(arg1, m2_expr.ExprCond(cond_expr, m2_expr.ExprInt( -1, arg1.size), m2_expr.ExprInt(0, arg1.size)))) return e, []
def set_empty_dst_to_next(self, bloc, ir_blocs): for b in ir_blocs: if b.dst is not None: continue dst = m2_expr.ExprId(self.get_next_label(bloc.lines[-1]), self.pc.size) b.irs.append([m2_expr.ExprAff(self.IRDst, dst)]) b.lines.append(b.lines[-1])
def mod_pc(self, instr, instr_ir, extra_ir): "Replace PC by the instruction's offset" cur_offset = m2_expr.ExprInt64(instr.offset) for i, expr in enumerate(instr_ir): dst, src = expr.dst, expr.src if dst != self.pc: dst = dst.replace_expr({self.pc: cur_offset}) src = src.replace_expr({self.pc: cur_offset}) instr_ir[i] = m2_expr.ExprAff(dst, src) for b in extra_ir: for irs in b.irs: for i, expr in enumerate(irs): dst, src = expr.dst, expr.src if dst != self.pc: dst = dst.replace_expr({self.pc: cur_offset}) src = src.replace_expr({self.pc: cur_offset}) irs[i] = m2_expr.ExprAff(dst, src)
def ubfm(ir, instr, arg1, arg2, arg3, arg4): e = [] rim, sim = int(arg3.arg), int(arg4) + 1 if sim > rim: res = arg2[rim:sim].zeroExtend(arg1.size) else: shift = m2_expr.ExprInt(arg2.size - rim, arg2.size) res = (arg2[:sim].zeroExtend(arg1.size) << shift) e.append(m2_expr.ExprAff(arg1, res)) return e, []
def _set_dst(self, value): """Find and replace the IRDst affectation's source by @value""" if self._dst_linenb is None: self._get_dst() ir = self.irs[self._dst_linenb] for i, expr in enumerate(ir): if isinstance(expr.dst, m2_expr.ExprId) and expr.dst.name == "IRDst": ir[i] = m2_expr.ExprAff(expr.dst, value) self._dst = value
def get_ir(self, instr): args = instr.args instr_ir, extra_ir = get_mnemo_expr(self, instr, *args) for i, x in enumerate(instr_ir): x = m2_expr.ExprAff( x.dst, x.src.replace_expr( {self.pc: m2_expr.ExprInt(instr.offset + 4, 32)})) instr_ir[i] = x for irblock in extra_ir: for irs in irblock.irs: for i, x in enumerate(irs): x = m2_expr.ExprAff( x.dst, x.src.replace_expr( {self.pc: m2_expr.ExprInt(instr.offset + 4, 32)})) irs[i] = x return instr_ir, extra_ir
def merge_multi_affect(self, affect_list): """ If multiple affection to a same ExprId are present in @affect_list, merge them (in place). For instance, XCGH AH, AL semantic is [ RAX = {RAX[0:8],0,8, RAX[0:8],8,16, RAX[16:64],16,64} RAX = {RAX[8:16],0,8, RAX[8:64],8,64} ] This function will update @affect_list to replace previous ExprAff by [ RAX = {RAX[8:16],0,8, RAX[0:8],8,16, RAX[16:64],16,64} ] """ # Extract side effect effect = {} for expr in affect_list: effect[expr.dst] = effect.get(expr.dst, []) + [expr] # Find candidates for dst, expr_list in effect.items(): if len(expr_list) <= 1: continue # Only treat ExprCompose list if any(map(lambda e: not(isinstance(e.src, m2_expr.ExprCompose)), expr_list)): continue # Find collision e_colision = reduce(lambda x, y: x.union(y), (e.get_modified_slice() for e in expr_list), set()) # Sort interval collision known_intervals = sorted([(x[1], x[2]) for x in e_colision]) # Fill with missing data missing_i = get_missing_interval(known_intervals, 0, dst.size) remaining = ((m2_expr.ExprSlice(dst, *interval), interval[0], interval[1]) for interval in missing_i) # Build the merging expression slices = sorted(e_colision.union(remaining), key=lambda x: x[1]) final_dst = m2_expr.ExprCompose(slices) # Remove unused expression for expr in expr_list: affect_list.remove(expr) # Add the merged one affect_list.append(m2_expr.ExprAff(dst, final_dst))
def set_dst(self, value): """Find and replace the IRDst affectation's source by @value""" dst = None for ir in self.irs: for i, expr in enumerate(ir): if isinstance(expr.dst, m2_expr.ExprId) and expr.dst.name == "IRDst": if dst is not None: raise ValueError('Multiple destinations!') dst = value ir[i] = m2_expr.ExprAff(expr.dst, value) self._dst = value
def set_empty_dst_to_next(self, bloc, ir_blocs): for b in ir_blocs: if b.dst is not None: continue next_lbl = bloc.get_next() if next_lbl is None: dst = m2_expr.ExprId(self.get_next_label(bloc.lines[-1]), self.pc.size) else: dst = m2_expr.ExprId(next_lbl, self.pc.size) b.irs.append(AssignBlock([m2_expr.ExprAff(self.IRDst, dst)])) b.lines.append(b.lines[-1])
def get_ir(self, instr): args = instr.args instr_ir, extra_ir = get_mnemo_expr(self, instr, *args) pc_fixed = {self.pc: m2_expr.ExprInt(instr.offset + 4, 32)} instr_ir = [m2_expr.ExprAff(expr.dst, expr.src.replace_expr(pc_fixed)) for expr in instr_ir] new_extra_ir = [irblock.modify_exprs(mod_src=lambda expr: expr.replace_expr(pc_fixed)) for irblock in extra_ir] return instr_ir, new_extra_ir
def apply_expr(self, expr): """Evaluate @expr and apply side effect if needed (ie. if expr is an assignment). Return the evaluated value""" # Eval expression to_eval = expr.src if isinstance(expr, m2_expr.ExprAff) else expr ret = self.expr_simp(self.eval_expr(to_eval)) # Update value if needed if isinstance(expr, m2_expr.ExprAff): self.eval_ir(AssignBlock([m2_expr.ExprAff(expr.dst, ret)])) return ret
def irbloc_fix_regs_for_mode(self, irbloc, mode=64): for irs in irbloc.irs: for i, e in enumerate(irs): """ special case for 64 bits: if destination is a 32 bit reg, zero extend the 64 bit reg """ if (isinstance(e.dst, m2_expr.ExprId) and e.dst.size == 32 and e.dst in replace_regs): src = self.expr_fix_regs_for_mode(e.src) dst = replace_regs[e.dst].arg e = m2_expr.ExprAff(dst, src.zeroExtend(64)) irs[i] = self.expr_fix_regs_for_mode(e) irbloc.dst = self.expr_fix_regs_for_mode(irbloc.dst)
def mod_pc(self, instr, instr_ir, extra_ir): "Replace PC by the instruction's offset" cur_offset = m2_expr.ExprInt(instr.offset, 64) pc_fixed = {self.pc: cur_offset} for i, expr in enumerate(instr_ir): dst, src = expr.dst, expr.src if dst != self.pc: dst = dst.replace_expr(pc_fixed) src = src.replace_expr(pc_fixed) instr_ir[i] = m2_expr.ExprAff(dst, src) for idx, irblock in enumerate(extra_ir): extra_ir[idx] = irblock.modify_exprs(lambda expr: expr.replace_expr(pc_fixed) \ if expr != self.pc else expr, lambda expr: expr.replace_expr(pc_fixed))
def ins(ir, instr, a, b, c, d): e = [] pos = int(c) l = int(d) my_slices = [] if pos != 0: my_slices.append((a[:pos], 0, pos)) if l != 0: my_slices.append((b[:l], pos, pos + l)) if pos + l != 32: my_slices.append((a[pos + l:], pos + l, 32)) r = m2_expr.ExprCompose(my_slices) e.append(m2_expr.ExprAff(a, r)) return e, []
def teq(ir, instr, arg1, arg2): e = [] loc_except, loc_except_expr = ir.gen_loc_key_and_expr(ir.IRDst.size) loc_next = ir.get_next_loc_key(instr) loc_next_expr = m2_expr.ExprLoc(loc_next, ir.IRDst.size) do_except = [] do_except.append( m2_expr.ExprAff( exception_flags, m2_expr.ExprInt(EXCEPT_DIV_BY_ZERO, exception_flags.size))) do_except.append(m2_expr.ExprAff(ir.IRDst, loc_next_expr)) blk_except = IRBlock(loc_except.index, [AssignBlock(do_except, instr)]) cond = arg1 - arg2 e = [] e.append( m2_expr.ExprAff(ir.IRDst, m2_expr.ExprCond(cond, loc_next_expr, loc_except_expr))) return e, [blk_except]
def patch_idiv(ir, instr, src1): e = [] size = src1.size if size == 8: src2 = mRAX[instr.mode][:16] elif size in [16, 32, 64]: s1, s2 = mRDX[size], mRAX[size] src2 = m2_expr.ExprCompose(s2, s1) else: raise ValueError('div arg not impl', src1) c_d = m2_expr.ExprOp('idiv', src2, src1.signExtend(src2.size)) c_r = m2_expr.ExprOp('imod', src2, src1.signExtend(src2.size)) # if 8 bit div, only ax is affected if size == 8: e.append( m2_expr.ExprAff(src2, m2_expr.ExprCompose(c_d[:8], c_r[:8]))) else: e.append(m2_expr.ExprAff(s1, c_r[:size])) e.append(m2_expr.ExprAff(s2, c_d[:size])) return e, []
def ccmp(ir, instr, arg1, arg2, arg3, arg4): e = [] if (arg2.is_int): arg2 = m2_expr.ExprInt(arg2.arg.arg, arg1.size) default_nf = arg3[0:1] default_zf = arg3[1:2] default_cf = arg3[2:3] default_of = arg3[3:4] cond_expr = cond2expr[arg4.name] res = arg1 - arg2 new_nf = nf new_zf = update_flag_zf(res)[0].src new_cf = update_flag_sub_cf(arg1, arg2, res).src new_of = update_flag_sub_of(arg1, arg2, res).src e.append( m2_expr.ExprAff(nf, m2_expr.ExprCond(cond_expr, new_nf, default_nf))) e.append( m2_expr.ExprAff(zf, m2_expr.ExprCond(cond_expr, new_zf, default_zf))) e.append( m2_expr.ExprAff(cf, m2_expr.ExprCond(cond_expr, new_cf, default_cf))) e.append( m2_expr.ExprAff(of, m2_expr.ExprCond(cond_expr, new_of, default_of))) return e, []
def emul(self, ctx=None, step=False): # Init ctx_init = self._ira.arch.regs.regs_init if ctx is not None: ctx_init.update(ctx) depnodes = self.relevant_nodes solver = z3.Solver() symb_exec = symbexec(self._ira, ctx_init) temp_label = asm_label("Temp") history = self.relevant_labels[::-1] history_size = len(history) for hist_nb, label in enumerate(history): # Build block with relevant lines only affected_lines = set(depnode.line_nb for depnode in depnodes if depnode.label == label) irs = self._ira.blocs[label].irs affects = [] for line_nb in sorted(affected_lines): affects.append(irs[line_nb]) # Emul the block and get back destination dst = symb_exec.emulbloc(irbloc(temp_label, affects), step=step) # Add constraint if hist_nb + 1 < history_size: next_label = history[hist_nb + 1] expected = symb_exec.eval_expr(m2_expr.ExprId(next_label, 32)) constraint = m2_expr.ExprAff(dst, expected) solver.add(Translator.to_language("z3").from_expr(constraint)) # Save the solver self._solver = solver # Return only inputs values (others could be wrongs) return { depnode.element: symb_exec.symbols[depnode.element] for depnode in self.input }