from miasm.ir.translators.translator import Translator from miasm.core.utils import size2mask from miasm.expression.expression import ExprInt, ExprCond, ExprCompose, \ TOK_EQUAL, \ TOK_INF_SIGNED, TOK_INF_UNSIGNED, \ TOK_INF_EQUAL_SIGNED, TOK_INF_EQUAL_UNSIGNED, \ is_associative def int_size_to_bn(value, size): if size < 32: int_str = "%.8x" % value size_nibble = 8 else: # size must be multiple of 4 size = ((size + 31) // 32) * 32 size_nibble = size // 4 fmt_str = "%%.%dx" % size_nibble int_str = fmt_str % value assert len(int_str) == size_nibble return int_str, size_nibble TOK_CMP_TO_NATIVE_C = { TOK_EQUAL: "==", TOK_INF_SIGNED: "<", TOK_INF_UNSIGNED: "<", TOK_INF_EQUAL_SIGNED: "<=", TOK_INF_EQUAL_UNSIGNED: "<=", } TOK_CMP_TO_BIGNUM_C = { TOK_EQUAL: "equal", TOK_INF_SIGNED: "inf_signed", TOK_INF_UNSIGNED: "inf_unsigned", TOK_INF_EQUAL_SIGNED: "inf_equal_signed", TOK_INF_EQUAL_UNSIGNED: "inf_equal_unsigned", } def get_c_common_next_pow2(size): # For uncommon expression size, use at least uint8 size = max(size, 8) next_power = 1 while next_power < size: next_power <<= 1 size = next_power return size class TranslatorC(Translator): "Translate a Miasm expression to an equivalent C code" # Implemented language __LANG__ = "C" # Operations translation dct_shift = {'a>>': "right_arith", '>>': "right_logic", '<<': "left_logic", } dct_rot = {'<<<': 'rot_left', '>>>': 'rot_right', } NATIVE_INT_MAX_SIZE = 64 def __init__(self, loc_db=None, **kwargs): """Instance a C translator @loc_db: LocationDB instance """ super(TranslatorC, self).__init__(**kwargs) # symbol pool self.loc_db = loc_db def _size2mask(self, size): """Return a C string corresponding to the size2mask operation, with support for @size <= 64""" assert size <= 64 mask = size2mask(size) return "0x%x" % mask def from_ExprId(self, expr): return str(expr) def from_ExprInt(self, expr): if expr.size <= self.NATIVE_INT_MAX_SIZE: assert expr.size <= 64 out = "0x%x" % int(expr) if expr.size == 64: out += "ULL" return out value, int_size = int_size_to_bn(int(expr), expr.size) return 'bignum_from_string("%s", %d)' % (value, int_size) def from_ExprLoc(self, expr): loc_key = expr.loc_key if self.loc_db is None: return str(loc_key) offset = self.loc_db.get_location_offset(loc_key) if offset is None: return str(loc_key) if expr.size <= self.NATIVE_INT_MAX_SIZE: return "0x%x" % offset value, int_size = int_size_to_bn(offset, 64) return 'bignum_from_string("%s", %d)' % (value, int_size) def from_ExprAssign(self, expr): new_dst = self.from_expr(expr.dst) new_src = self.from_expr(expr.src) return "%s = %s" % (new_dst, new_src) def from_ExprCond(self, expr): cond = self.from_expr(expr.cond) src1 = self.from_expr(expr.src1) src2 = self.from_expr(expr.src2) if not expr.cond.size <= self.NATIVE_INT_MAX_SIZE: cond = "(!bignum_is_zero(%s))" % cond out = "(%s?%s:%s)" % (cond, src1, src2) return out def from_ExprMem(self, expr): ptr = expr.ptr if ptr.size <= self.NATIVE_INT_MAX_SIZE: new_ptr = self.from_expr(ptr) if expr.size <= self.NATIVE_INT_MAX_SIZE: # Native ptr, Native Mem return "MEM_LOOKUP_%.2d(jitcpu, %s)" % (expr.size, new_ptr) else: # Native ptr, BN mem return "MEM_LOOKUP_INT_BN(jitcpu, %d, %s)" % (expr.size, new_ptr) # BN ptr new_ptr = self.from_expr(ptr) if expr.size <= self.NATIVE_INT_MAX_SIZE: # BN ptr, Native Mem return "MEM_LOOKUP_BN_INT(jitcpu, %d, %s)" % (expr.size, new_ptr) else: # BN ptr, BN mem return "MEM_LOOKUP_BN_BN(jitcpu, %d, %s)" % (expr.size, new_ptr) def from_ExprOp(self, expr): if len(expr.args) == 1: if expr.op == 'parity': arg = expr.args[0] out = self.from_expr(arg) if arg.size <= self.NATIVE_INT_MAX_SIZE: out = "(%s&%s)" % (out, self._size2mask(arg.size)) else: out = 'bignum_mask(%s, 8)' % (out, 8) out = 'bignum_to_uint64(%s)' % out out = 'parity(%s)' % out return out elif expr.op.startswith("zeroExt_"): arg = expr.args[0] if expr.size == arg.size: return arg return self.from_expr(ExprCompose(arg, ExprInt(0, expr.size - arg.size))) elif expr.op.startswith("signExt_"): arg = expr.args[0] if expr.size == arg.size: return arg add_size = expr.size - arg.size new_expr = ExprCompose( arg, ExprCond( arg.msb(), ExprInt(size2mask(add_size), add_size), ExprInt(0, add_size) ) ) return self.from_expr(new_expr) elif expr.op in ['cntleadzeros', 'cnttrailzeros']: arg = expr.args[0] out = self.from_expr(arg) if arg.size <= self.NATIVE_INT_MAX_SIZE: out = "%s(0x%x, %s)" % (expr.op, expr.args[0].size, out) else: out = "bignum_%s(%s, %d)" % (expr.op, out, arg.size) return out elif expr.op == '!': arg = expr.args[0] out = self.from_expr(arg) if expr.size <= self.NATIVE_INT_MAX_SIZE: out = "(~ %s)&%s" % (out, self._size2mask(arg.size)) else: out = "bignum_not(%s)" % out out = "bignum_mask(%s, expr.size)" % out return out elif expr.op in [ "ftan", "frndint", "f2xm1", "fsin", "fsqrt", "fabs", "fcos", "fchs", ]: return "fpu_%s%d(%s)" % ( expr.op, expr.size, self.from_expr(expr.args[0]), ) elif (expr.op.startswith("access_") or expr.op.startswith("load_") or expr.op.startswith("fxam_c")): arg = expr.args[0] out = self.from_expr(arg) out = "%s(%s)" % (expr.op, out) return out elif expr.op == "-": arg = expr.args[0] out = self.from_expr(arg) if arg.size <= self.NATIVE_INT_MAX_SIZE: out = "(%s(%s))" % (expr.op, out) out = "(%s&%s)" % (out, self._size2mask(arg.size)) else: out = "bignum_sub(bignum_from_uint64(0), %s)" % out out = "bignum_mask(%s, %d)"% (out, expr.size) return out elif expr.op.startswith("fpround_"): return "%s_fp%d(%s)" % ( expr.op, expr.size, self.from_expr(expr.args[0]), ) elif expr.op == "sint_to_fp": size = expr.size arg = expr.args[0] if size not in [32, 64]: raise RuntimeError( "Unsupported size for sint_to_fp: %r" % size ) return "%s_%d(%s)" % (expr.op, size, self.from_expr(arg)) elif expr.op.startswith("fp_to_sint"): dest_size = expr.size arg_size = expr.args[0].size if (arg_size, dest_size) in [ (32, 32), (64, 64), (64, 32), ]: func = "fp%d_to_sint%d" % (arg_size, dest_size) else: raise RuntimeError( "Unsupported size for fp_to_sint: %r to %r" % ( arg_size, dest_size )) return "%s(%s)" % (func, self.from_expr(expr.args[0])) elif expr.op.startswith("fpconvert_fp"): dest_size = expr.size arg_size = expr.args[0].size if (arg_size, dest_size) in [ (32, 64), (64, 32) ]: func = "fp%d_to_fp%d" % (arg_size, dest_size) else: raise RuntimeError( "Unsupported size for fpconvert: %r to %r" % (arg_size, dest_size) ) return "%s(%s)" % (func, self.from_expr(expr.args[0])) else: raise NotImplementedError('Unknown op: %r' % expr.op) elif len(expr.args) == 2: if expr.op in self.dct_shift: arg0 = self.from_expr(expr.args[0]) arg1 = self.from_expr(expr.args[1]) if expr.size <= self.NATIVE_INT_MAX_SIZE: out = 'SHIFT_%s(%d, %s, %s)' % ( self.dct_shift[expr.op].upper(), expr.args[0].size, arg0, arg1 ) else: op = { "<<": "lshift", ">>": "rshift", "a>>": "a_rshift" } out = "bignum_%s(%s, bignum_to_uint64(%s))" % ( op[expr.op], arg0, arg1 ) out = "bignum_mask(%s, %d)"% (out, expr.size) return out elif is_associative(expr): args = [self.from_expr(arg) for arg in expr.args] if expr.size <= self.NATIVE_INT_MAX_SIZE: out = (" %s " % expr.op).join(args) out = "((%s)&%s)" % (out, self._size2mask(expr.size)) else: op_to_bn_func = { "+": "add", "*": "mul", "|": "or", "^": "xor", "&": "and", } args = list(expr.args) out = self.from_expr(args.pop()) while args: out = 'bignum_mask(bignum_%s(%s, %s), %d)' % ( op_to_bn_func[expr.op], out, self.from_expr(args.pop()), expr.size ) return out elif expr.op in ['-']: return '(((%s&%s) %s (%s&%s))&%s)' % ( self.from_expr(expr.args[0]), self._size2mask(expr.args[0].size), str(expr.op), self.from_expr(expr.args[1]), self._size2mask(expr.args[1].size), self._size2mask(expr.args[0].size) ) elif expr.op in self.dct_rot: arg0 = self.from_expr(expr.args[0]) arg1 = self.from_expr(expr.args[1]) if expr.size <= self.NATIVE_INT_MAX_SIZE: out = '(%s(%s, %s, %s) &%s)' % ( self.dct_rot[expr.op], expr.args[0].size, arg0, arg1, self._size2mask(expr.args[0].size), ) else: op = { ">>>": "ror", "<<<": "rol" } out = "bignum_%s(%s, %d, bignum_to_uint64(%s))" % ( op[expr.op], arg0, expr.size, arg1 ) out = "bignum_mask(%s, %d)"% (out, expr.size) return out elif expr.op == 'x86_cpuid': return "%s(%s, %s)" % (expr.op, self.from_expr(expr.args[0]), self.from_expr(expr.args[1])) elif expr.op.startswith("fcom"): arg0 = self.from_expr(expr.args[0]) arg1 = self.from_expr(expr.args[1]) if not expr.args[0].size <= self.NATIVE_INT_MAX_SIZE: raise ValueError("Bad semantic: fpu do operations do not support such size") out = "fpu_%s(%s, %s)" % (expr.op, arg0, arg1) return out elif expr.op in ["fadd", "fsub", "fdiv", 'fmul', "fscale", "fprem", "fyl2x", "fpatan"]: arg0 = self.from_expr(expr.args[0]) arg1 = self.from_expr(expr.args[1]) if not expr.args[0].size <= self.NATIVE_INT_MAX_SIZE: raise ValueError("Bad semantic: fpu do operations do not support such size") out = "fpu_%s%d(%s, %s)" % (expr.op, expr.size, arg0, arg1) return out elif expr.op == "segm": return "segm2addr(jitcpu, %s, %s)" % ( self.from_expr(expr.args[0]), self.from_expr(expr.args[1]) ) elif expr.op in ['udiv', 'umod']: arg0 = self.from_expr(expr.args[0]) arg1 = self.from_expr(expr.args[1]) if expr.size <= self.NATIVE_INT_MAX_SIZE: out = '%s%d(%s, %s)' % ( expr.op, expr.args[0].size, arg0, arg1 ) else: out = "bignum_%s(%s, %s)" % ( expr.op, arg0, arg1 ) out = "bignum_mask(%s, %d)"% (out, expr.size) return out elif expr.op in ['sdiv', 'smod']: arg0 = self.from_expr(expr.args[0]) arg1 = self.from_expr(expr.args[1]) if expr.size <= self.NATIVE_INT_MAX_SIZE: out = '%s%d(%s, %s)' % ( expr.op, expr.args[0].size, arg0, arg1 ) else: out = "bignum_%s(%s, %s, %d)" % ( expr.op, arg0, arg1, expr.size ) out = "bignum_mask(%s, %d)"% (out, expr.size) return out elif expr.op in ["bcdadd", "bcdadd_cf"]: return "%s_%d(%s, %s)" % ( expr.op, expr.args[0].size, self.from_expr(expr.args[0]), self.from_expr(expr.args[1]) ) elif expr.op in [ TOK_EQUAL, TOK_INF_SIGNED, TOK_INF_UNSIGNED, TOK_INF_EQUAL_SIGNED, TOK_INF_EQUAL_UNSIGNED, ]: arg0, arg1 = expr.args if expr.size <= self.NATIVE_INT_MAX_SIZE: size = get_c_common_next_pow2(arg0.size) op = TOK_CMP_TO_NATIVE_C[expr.op] if expr.op in [TOK_INF_SIGNED, TOK_INF_EQUAL_SIGNED]: arg0 = arg0.signExtend(size) arg1 = arg1.signExtend(size) arg0_C = self.from_expr(arg0) arg1_C = self.from_expr(arg1) cast = "(int%d_t)" % size else: arg0 = arg0.signExtend(size) arg1 = arg1.signExtend(size) arg0_C = self.from_expr(arg0) arg1_C = self.from_expr(arg1) cast = "(uint%d_t)" % size out = '((%s%s %s %s%s)?1:0)' % ( cast, arg0_C, op, cast, arg1_C ) else: op = TOK_CMP_TO_BIGNUM_C[expr.op] out = "bignum_is_%s(%s, %s)" % ( op, arg0, arg1 ) out = "bignum_mask(%s, %d)"% (out, expr.size) return out else: raise NotImplementedError('Unknown op: %r' % expr.op) elif len(expr.args) >= 3 and is_associative(expr): # ????? oper = ['(%s&%s)' % ( self.from_expr(arg), self._size2mask(arg.size), ) for arg in expr.args] oper = str(expr.op).join(oper) return "((%s)&%s)" % ( oper, self._size2mask(expr.args[0].size) ) else: raise NotImplementedError('Unknown op: %s' % expr.op) def from_ExprSlice(self, expr): out = self.from_expr(expr.arg) if expr.arg.size <= self.NATIVE_INT_MAX_SIZE: # XXX check mask for 64 bit & 32 bit compat out = "((%s>>%d) &%s)" % ( out, expr.start, self._size2mask(expr.stop - expr.start) ) else: out = "bignum_rshift(%s, %d)" % (out, expr.start) out = "bignum_mask(%s, %d)" % (out, expr.stop - expr.start) if expr.size <= self.NATIVE_INT_MAX_SIZE: # Convert bignum to int out = "bignum_to_uint64(%s)" % out return out def from_ExprCompose(self, expr): if expr.size <= self.NATIVE_INT_MAX_SIZE: out = [] size = get_c_common_next_pow2(expr.size) dst_cast = "uint%d_t" % size for index, arg in expr.iter_args(): out.append("(((%s)(%s & %s)) << %d)" % ( dst_cast, self.from_expr(arg), self._size2mask(arg.size), index) ) out = ' | '.join(out) return '(' + out + ')' else: # Convert all parts to bignum args = [] for index, arg in expr.iter_args(): arg_str = self.from_expr(arg) if arg.size <= self.NATIVE_INT_MAX_SIZE: arg_str = '((%s) & %s)' % (arg_str, self._size2mask(arg.size)) arg_str = 'bignum_from_uint64(%s)' % arg_str else: arg_str = 'bignum_mask(%s, %d)' % (arg_str, arg.size) arg_str = 'bignum_lshift(%s, %d)' % (arg_str, index) args.append(arg_str) out = args.pop() while args: arg = args.pop() out = "bignum_or(%s, %s)" % (out, arg) return out # Register the class Translator.register(TranslatorC)