def call_effects(self, ad, instr): call_assignblk = AssignBlock([ ExprAssign( self.ret_reg, ExprOp( 'call_func_ret', ad, self.sp, self.arch.regs.RCX, self.arch.regs.RDX, self.arch.regs.R8, self.arch.regs.R9, )), ExprAssign(self.sp, ExprOp('call_func_stack', ad, self.sp)), ], instr) return [call_assignblk], []
def jmp(ir, instr, reg_or_imm): """JMP - Change PC to a register content or an immediate. Note: the behavior in VLIW mode is not implemented""" take_jmp = ExprInt(1, 32) if isinstance(reg_or_imm, ExprId): # PC <- Rm31..1||0 new_PC = ExprAssign(PC, reg_or_imm) else: # PC <- PC31..28||0000||(target24)23..1||0 new_PC = ExprAssign( PC, ExprOp("+", ExprOp("&", PC, ExprInt(0xF0000000, 32)), reg_or_imm)) return [new_PC, ExprAssign(ir.IRDst, new_PC)], []
def simp_cond_logic_ext(expr_s, expr): """(X.zeroExt() + ... + Int) ? A:B => X + ... + int[:] ? A:B""" cond = expr.cond if not cond.is_op(): return expr if cond.op not in ["&", "^", "|"]: return expr is_ok = True sizes = set() for arg in cond.args: if arg.is_int(): continue if (arg.is_op() and arg.op.startswith("zeroExt")): sizes.add(arg.args[0].size) continue is_ok = False break if not is_ok: return expr if len(sizes) != 1: return expr size = list(sizes)[0] args = [expr_s(arg[:size]) for arg in cond.args] cond = ExprOp(cond.op, *args) return ExprCond(cond, expr.src1, expr.src2)
def asm_ast_to_expr(self, arg, loc_db): """Convert AST to expressions Note: - Must be implemented""" if isinstance(arg, AstId): if isinstance(arg.name, ExprId): return arg.name if isinstance(arg.name, str) and arg.name in gpr_names: return None # GV: why? loc_key = loc_db.get_or_create_name_location(arg.name.encode()) return ExprLoc(loc_key, 32) elif isinstance(arg, AstMem): addr = self.asm_ast_to_expr(arg.ptr, loc_db) if addr is None: return None return ExprMem(addr, 32) elif isinstance(arg, AstInt): return ExprInt(arg.value, 32) elif isinstance(arg, AstOp): args = [self.asm_ast_to_expr(tmp, loc_db) for tmp in arg.args] if None in args: return None return ExprOp(arg.op, *args) # Raise an exception if the argument was not processed message = "mep_arg.asm_ast_to_expr(): don't know what \ to do with a '%s' instance." % type(arg) raise Exception(message)
def btstm(ir, instr, r0, rm_deref, imm3): """BTSTM - Bit Test Memory""" # R0 <- ZeroExt( MemByte(Rm) and (1<<imm3) ) e = [] e.append(ExprAssign(r0, ExprOp("&", ExprMem(rm_deref.ptr, 8), i8(1) << imm3[:8]).zeroExtend(32))) return e, []
def bnotm(ir, instr, rm_deref, imm3): """BNOTM - Bit Not Memory""" # MemByte(Rm) <- MemByte(Rm) xor (1<<imm3) e = [] e.append(ExprAssign(ExprMem(rm_deref.ptr, 8), ExprOp("^", ExprMem(rm_deref.ptr, 8), (i8(1) << imm3[:8])))) return e, []
def bsetm(ir, instr, rm_deref, imm3): """BSETM - Bit Set Memory""" # MemByte(Rm) <- MemByte(Rm) or (1<<imm3) e = [] e.append(ExprAssign(ExprMem(rm_deref.ptr, 8), ExprOp("|", ExprMem(rm_deref.ptr, 8), (i8(1) << imm3[:8])))) return e, []
def simp_slice_of_op_ext(expr_s, expr): """ (X.zeroExt() + {Z, } + ... + Int)[0:8] => X + ... + int[:] (X.zeroExt() | ... | Int)[0:8] => X | ... | int[:] ... """ if expr.start != 0: return expr src = expr.arg if not src.is_op(): return expr if src.op not in ['+', '|', '^', '&']: return expr is_ok = True for arg in src.args: if arg.is_int(): continue if (arg.is_op() and arg.op.startswith("zeroExt") and arg.args[0].size == expr.stop): continue if arg.is_compose(): continue is_ok = False break if not is_ok: return expr args = [expr_s(arg[:expr.stop]) for arg in src.args] return ExprOp(src.op, *args)
def call_effects(self, addr, instr): assignblks, extra = super(IRADelModCallStack, self).call_effects(addr, instr) if use_ida_stack: stk_before = idc.get_spd(instr.offset) stk_after = idc.get_spd(instr.offset + instr.l) stk_diff = stk_after - stk_before print(hex(stk_diff)) call_assignblk = AssignBlock([ ExprAssign(self.ret_reg, ExprOp('call_func_ret', addr)), ExprAssign(self.sp, self.sp + ExprInt(stk_diff, self.sp.size)) ], instr) return [call_assignblk], [] else: if not dontmodstack: return assignblks, extra out = [] for assignblk in assignblks: dct = dict(assignblk) dct = { dst: src for (dst, src) in viewitems(dct) if dst != self.sp } out.append(AssignBlock(dct, assignblk.instr)) return out, extra
def update_flag_arith_addwc_zn(arg1, arg2, arg3): """ Compute znp flags for (arg1 + arg2 + cf) """ e = [] e += update_flag_zfaddwc_eq(arg1, arg2, arg3) e += [ExprAssign(nf, ExprOp("FLAG_SIGN_ADDWC", arg1, arg2, arg3))] return e
def update_flag_arith_sub_zn(arg1, arg2): """ Compute zf and nf flags for (arg1 - arg2) """ e = [] e += update_flag_zf_eq(arg1, arg2) e += [ExprAssign(nf, ExprOp("FLAG_SIGN_SUB", arg1, arg2))] return e
def sbvck3(ir, instr, r0, rn, rm): """SBVCK3 - Check subtraction overflow""" # if(Overflow(Rn-Rm)) R0<-1 else R0<-0 (Signed) # Subtract registers reg_sub = ExprOp("+", rn, rm) # Get the register storing the highest value max_rn_rm = ExprCond(ExprOp(">", rn, rm), rn, rm) # Check for an overflow overflow_test = ExprOp(">", reg_sub, max_rn_rm) # Return the result condition = ExprCond(overflow_test, ExprInt(1, 32), ExprInt(0, 32)) return [ExprAssign(r0, condition)], []
def eval_exprop(self, expr, **kwargs): """[DEV]: Evaluate an ExprOp using the current state""" args = [] for oarg in expr.args: arg = self.eval_expr_visitor(oarg, **kwargs) args.append(arg) ret = ExprOp(expr.op, *args) return ret
def update_flag_arith_subwc_zn(arg1, arg2, arg3): """ Compute znp flags for (arg1 - (arg2 + cf)) """ e = [] e += update_flag_zfsubwc_eq(arg1, arg2, arg3) e += [ExprAssign(nf, ExprOp("FLAG_SIGN_SUBWC", arg1, arg2, arg3))] return e
def tst(ir, instr, arg1, arg2): e = [] arg2 = extend_arg(arg1, arg2) res = arg1 & arg2 e += [ExprAssign(zf, ExprOp('FLAG_EQ_AND', arg1, arg2))] e += update_flag_nf(res) return e, []
def bgei(reg_test, imm4, disp16): """BGEI - Branch if the register is greater or equal to imm4.""" # if(Rn>=ZeroExt(imm4)) PC <- PC +SignExt((disp17)16..1||0) - (Signed comparison) cond = i32(1) if ExprOp(TOK_EQUAL, reg_test, imm4) else compute_s_inf(imm4, reg_test).zeroExtend(32) dst = disp16 if cond else ExprLoc(ir.get_next_break_loc_key(instr), 32) take_jmp = ExprInt(1, 32) if cond else ExprInt(0, 32) PC = dst ir.IRDst = dst
def simp_double_signext(_, expr): """A.signExt(X).signExt(Y) => A.signExt(Y)""" if not (expr.is_op() and expr.op.startswith("signExt")): return expr arg1 = expr.args[0] if not (arg1.is_op() and arg1.op.startswith("signExt")): return expr arg2 = arg1.args[0] return ExprOp(expr.op, arg2)
def simp_cond_inf_eq_unsigned_zero(expr_s, expr): """ (a <=u 0) => a == 0 """ if not expr.is_op(TOK_INF_EQUAL_UNSIGNED): return expr if not expr.args[1].is_int(0): return expr return ExprOp(TOK_EQUAL, expr.args[0], expr.args[1])
def check(self): regs = self.dse.lifter.arch.regs value = self.dse.eval_expr(regs.EDX) # The expected value should contains '<<', showing it has been in the # corresponding generated label expected = ExprOp( '<<', regs.EDX, ExprCompose(regs.ECX[0:8], ExprInt(0x0, 24)) & ExprInt(0x1F, 32)) assert value == expected
def asm_ast_to_expr_with_size(arg, loc_db, size): if isinstance(arg, AstId): return ExprId(arg.name.encode(), size) if isinstance(arg, AstOp): args = [asm_ast_to_expr_with_size(tmp, loc_db, size) for tmp in arg.args] return ExprOp(arg.op, *args) if isinstance(arg, AstInt): return ExprInt(arg.value, size) return None
def simp_cond_factor(e_s, expr): "Merge similar conditions" if not expr.op in ["+", "|", "^", "&", "*", '<<', '>>', 'a>>']: return expr if len(expr.args) < 2: return expr if expr.op in ['>>', '<<', 'a>>']: assert len(expr.args) == 2 # Note: the following code is correct for non-commutative operation only if # there is 2 arguments. Otherwise, the order is not conserved # Regroup sub-expression by similar conditions conds = {} not_conds = [] multi_cond = False for arg in expr.args: if not arg.is_cond(): not_conds.append(arg) continue cond = arg.cond if not cond in conds: conds[cond] = [] else: multi_cond = True conds[cond].append(arg) if not multi_cond: return expr # Rebuild the new expression c_out = not_conds for cond, vals in viewitems(conds): new_src1 = [x.src1 for x in vals] new_src2 = [x.src2 for x in vals] src1 = e_s.expr_simp_wrapper(ExprOp(expr.op, *new_src1)) src2 = e_s.expr_simp_wrapper(ExprOp(expr.op, *new_src2)) c_out.append(ExprCond(cond, src1, src2)) if len(c_out) == 1: new_e = c_out[0] else: new_e = ExprOp(expr.op, *c_out) return new_e
def simp_sign_inf_zeroext(expr_s, expr): """ /!\ Ensure before: X.zeroExt(X.size) => X X.zeroExt() <s 0 => 0 X.zeroExt() <=s 0 => X == 0 X.zeroExt() <s cst => X.zeroExt() <u cst (cst positive) X.zeroExt() <=s cst => X.zeroExt() <=u cst (cst positive) X.zeroExt() <s cst => 0 (cst negative) X.zeroExt() <=s cst => 0 (cst negative) """ if not (expr.is_op(TOK_INF_SIGNED) or expr.is_op(TOK_INF_EQUAL_SIGNED)): return expr arg1, arg2 = expr.args if not arg2.is_int(): return expr if not (arg1.is_op() and arg1.op.startswith("zeroExt")): return expr src = arg1.args[0] assert src.size < arg1.size # If cst is zero if arg2.is_int(0): if expr.is_op(TOK_INF_SIGNED): # X.zeroExt() <s 0 => 0 return ExprInt(0, expr.size) else: # X.zeroExt() <=s 0 => X == 0 return ExprOp(TOK_EQUAL, src, ExprInt(0, src.size)) # cst is not zero cst = int(arg2) if cst & (1 << (arg2.size - 1)): # cst is negative return ExprInt(0, expr.size) # cst is positive if expr.is_op(TOK_INF_SIGNED): # X.zeroExt() <s cst => X.zeroExt() <u cst (cst positive) return ExprOp(TOK_INF_UNSIGNED, src, expr_s(arg2[:src.size])) # X.zeroExt() <=s cst => X.zeroExt() <=u cst (cst positive) return ExprOp(TOK_INF_EQUAL_UNSIGNED, src, expr_s(arg2[:src.size]))
def from_ExprOp(self, expr: Expr) -> Expr: """ Translates an ExprOp. Args: expr: Expression to translate. Returns: Expression as ExprOp. """ args = list(map(self.from_expr, expr.args)) res = args[0] if len(args) > 1: for arg in args[1:]: res = ExprOp(expr.op, res, self.from_expr(arg)) else: res = ExprOp(expr.op, res) return res
def call_effects(self, addr, instr): """Default modelisation of a function call to @addr. This may be used to: * insert dependencies to arguments (stack base, registers, ...) * add some side effects (stack clean, return value, ...) Return a couple: * list of assignments to add to the current irblock * list of additional irblocks @addr: (Expr) address of the called function @instr: native instruction which is responsible of the call """ call_assignblk = AssignBlock([ ExprAssign(self.ret_reg, ExprOp('call_func_ret', addr, self.sp)), ExprAssign(self.sp, ExprOp('call_func_stack', addr, self.sp)) ], instr) return [call_assignblk], []
def _fill_phi(self, *args): """ Fills a phi function with variables. phi(x.1, x.5, x.6) :param args: list of ExprId :return: ExprOp """ return ExprOp(self.PHI_STR, *set(args))
def ands(ir, instr, arg1, arg2, arg3): e = [] arg3 = extend_arg(arg2, arg3) res = arg2 & arg3 e += [ExprAssign(zf, ExprOp('FLAG_EQ_AND', arg2, arg3))] e += update_flag_nf(res) e.append(ExprAssign(arg1, res)) return e, []
def bics(ir, instr, arg1, arg2, arg3): e = [] tmp1, tmp2 = arg2, (~extend_arg(arg2, arg3)) res = tmp1 & tmp2 e += [ExprAssign(zf, ExprOp('FLAG_EQ_AND', tmp1, tmp2))] e += update_flag_nf(res) e.append(ExprAssign(arg1, res)) return e, []
def categorize(self, node, lvl=0, **kwargs): """Recursively apply rules to @node @node: ExprNode to analyze @lvl: actual recursion level """ expr = node.expr log_reduce.debug("\t" * lvl + "Reduce...: %s", node.expr) if isinstance(expr, ExprId): node = ExprNodeId(expr) elif isinstance(expr, ExprInt): node = ExprNodeInt(expr) elif isinstance(expr, ExprLoc): node = ExprNodeLoc(expr) elif isinstance(expr, ExprMem): ptr = self.categorize(node.ptr, lvl=lvl + 1, **kwargs) node = ExprNodeMem(ExprMem(ptr.expr, expr.size)) node.ptr = ptr elif isinstance(expr, ExprSlice): arg = self.categorize(node.arg, lvl=lvl + 1, **kwargs) node = ExprNodeSlice(ExprSlice(arg.expr, expr.start, expr.stop)) node.arg = arg elif isinstance(expr, ExprOp): new_args = [] for arg in node.args: new_a = self.categorize(arg, lvl=lvl + 1, **kwargs) assert new_a.expr.size == arg.expr.size new_args.append(new_a) node = ExprNodeOp(ExprOp(expr.op, *[x.expr for x in new_args])) node.args = new_args expr = node.expr elif isinstance(expr, ExprCompose): new_args = [] new_expr_args = [] for arg in node.args: arg = self.categorize(arg, lvl=lvl + 1, **kwargs) new_args.append(arg) new_expr_args.append(arg.expr) new_expr = ExprCompose(*new_expr_args) node = ExprNodeCompose(new_expr) node.args = new_args elif isinstance(expr, ExprCond): cond = self.categorize(node.cond, lvl=lvl + 1, **kwargs) src1 = self.categorize(node.src1, lvl=lvl + 1, **kwargs) src2 = self.categorize(node.src2, lvl=lvl + 1, **kwargs) node = ExprNodeCond(ExprCond(cond.expr, src1.expr, src2.expr)) node.cond, node.src1, node.src2 = cond, src1, src2 else: raise TypeError("Unknown Expr Type %r", type(expr)) node.info = self.apply_rules(node, lvl=lvl, **kwargs) log_reduce.debug("\t" * lvl + "Reduce result: %s %r", node.expr, node.info) return node
def simp_cond_add(expr_s, expr): """ (a+b)?X:Y => (a == b)?Y:X (a^b)?X:Y => (a == b)?Y:X """ cond = expr.cond if not cond.is_op(): return expr if cond.op not in ['+', '^']: return expr if len(cond.args) != 2: return expr arg1, arg2 = cond.args if cond.is_op('+'): new_cond = ExprOp('==', arg1, expr_s(-arg2)) elif cond.is_op('^'): new_cond = ExprOp('==', arg1, arg2) else: raise ValueError('Bad case') return ExprCond(new_cond, expr.src2, expr.src1)
def get_ir(self, instr): args = instr.args if len(args) and isinstance(args[-1], ExprOp): if (args[-1].op in ['<<', '>>', '<<a', 'a>>', '<<<', '>>>'] and isinstance(args[-1].args[-1], ExprId)): args[-1] = ExprOp(args[-1].op, args[-1].args[0], args[-1].args[-1][:8].zeroExtend(32)) instr_ir, extra_ir = get_mnemo_expr(self, instr, *args) self.mod_pc(instr, instr_ir, extra_ir) instr_ir, extra_ir = self.del_dst_zr(instr, instr_ir, extra_ir) return instr_ir, extra_ir
def bclrm(rm_deref, imm3): """BCLRM - Bit Clear Memory""" # MemByte(Rm) <- MemByte(Rm) and ~(1<<imm3) shift = ExprOp("<<", i8(1), imm3[:8]) mem8[rm_deref.ptr] = ExprOp("&", mem8[rm_deref.ptr], shift.__invert__())