def get_op(ea, op, stkvars=None): '''ea_t -> int -> opt:{int : tinfo_t} -> op_ret''' cmd = idautils.DecodeInstruction(ea) cmd.Operands = get_operands(cmd) # for mips_op_hack op = mips_op_hack(cmd, op) opd = cmd[op] if opd.type == idaapi.o_reg: # gpr, XXX sorta MIPS-specific return op_ret(op_ty.reg, regs.gpr(opd.reg), 0) elif opd.type == idaapi.o_idpspec1: # fpr, XXX sorta MIPS-specific return op_ret(op_ty.reg, regs.fpr(opd.reg), 0) elif opd.type in [idaapi.o_near, idaapi.o_mem]: return op_ret(op_ty.name, idc.Name(opd.addr), 0) elif idc.isStkvar1(idc.GetFlags(ea)): # IDA seems to set this flag even for operands beyond the second, # i.e. both of these are true for isStkvar1: # .text:10003A84 sd $a1, 0x2E0+var_58($sp) # .text:10003A68 addiu $a1, $sp, 0x2E0+var_2D8 try: func = idaapi.get_func(ea) off = idaapi.calc_stkvar_struc_offset(func, ea, op) (name, ti) = stkvars[off] return op_ret_for_ti(ti, name, off, off) except KeyError: raise OperandUnresolvableError('unable to get operand %u at %s' % (op, idc.atoa(ea))) elif opd.type in [idaapi.o_imm, idaapi.o_displ]: return cpu_ida.ida_current_cpu().data.get_op_addrmode(ea, op, cmd) else: raise OperandUnresolvableError('unable to get operand %u at %s' % (op, idc.atoa(ea)))
def type_to_reg_and_slot(node, chooser, i): '''c_ast -> fn -> int -> (reg_type, slot_type) | None''' def yield_void(): # return an empty list for (void) arglists raise StopIteration def maybe_fail(node): if type(node) is c_ast.Struct: raise StructByValueError('structs by value not yet supported') return type_to_reg_and_slot(node.type, chooser, i) def get(names): '''[str] -> (gpr|fpr, slot_ty)''' if 'void' in names: return yield_void() ti = ida.parse_decl(' '.join(names)) (ty, base, slot) = chooser(ti) return (ty(base + i), slot) sw = { c_ast.Decl: lambda x: type_to_reg_and_slot(x.type, chooser, i), c_ast.TypeDecl: lambda x: type_to_reg_and_slot(x.type, chooser, i), c_ast.Typename: lambda x: type_to_reg_and_slot(x.type, chooser, i), c_ast.IdentifierType: lambda x: get(x.names) } if i > 7: raise RegSpillError('spilling registers to stack not yet supported') # in order to use chooser we need a tinfo_t--make one suitable for an # an int (which is also suitable for a pointer on N32) dummy_ti = ida.parse_decl('int') (_, base, _) = chooser(dummy_ti) node_ty = type(node) if node_ty in [c_ast.ArrayDecl, c_ast.PtrDecl]: return (regs.gpr(base + i), ep_ct.slot_types.u64) elif node_ty is c_ast.Enum: return (regs.gpr(base + i), ep_ct.slot_types.i64) else: return utils.dictswitch(node_ty, sw, node, maybe_fail, node)
def type_to_reg_and_slot(node, chooser, i): '''c_ast -> fn -> int -> (reg_type, slot_type) | None''' def yield_void(): # return an empty list for (void) arglists raise StopIteration def maybe_fail(node): if type(node) is c_ast.Struct: raise StructByValueError('structs by value not yet supported') return type_to_reg_and_slot(node.type, chooser, i) def get(names): '''[str] -> (gpr|fpr, slot_ty)''' if 'void' in names: return yield_void() ti = ida.parse_decl(' '.join(names)) (ty, base, slot) = chooser(ti) return (ty(base + i), slot) sw = { c_ast.Decl : lambda x: type_to_reg_and_slot(x.type, chooser, i), c_ast.TypeDecl : lambda x: type_to_reg_and_slot(x.type, chooser, i), c_ast.Typename : lambda x: type_to_reg_and_slot(x.type, chooser, i), c_ast.IdentifierType : lambda x: get(x.names) } if i > 7: raise RegSpillError('spilling registers to stack not yet supported') # in order to use chooser we need a tinfo_t--make one suitable for an # an int (which is also suitable for a pointer on N32) dummy_ti = ida.parse_decl('int') (_, base, _) = chooser(dummy_ti) node_ty = type(node) if node_ty in [c_ast.ArrayDecl, c_ast.PtrDecl]: return (regs.gpr(base + i), ep_ct.slot_types.u64) elif node_ty is c_ast.Enum: return (regs.gpr(base + i), ep_ct.slot_types.i64) else: return utils.dictswitch(node_ty, sw, node, maybe_fail, node)
def get_arg_for_va_function(callee, start_ea): '''str -> ea_t -> str''' # XXX hacky; not a very general function # XXX imperative # get a relevant item needed for processing a variadic function sw = { 'printf' : regs.gpr(abi.arg_regs[0]), 'scanf' : regs.gpr(abi.arg_regs[0]), 'sscanf' : regs.gpr(abi.arg_regs[1]) } try: wanted_reg = sw[callee] except KeyError: raise utils.BugError('unrecognized callee %s' % callee) distance = 0 fn = ida.get_func(start_ea) # first, look at the delay slot ea = ida.next_head(start_ea, fn.endEA) while True: if distance > 10: raise VarargsError( 'gave up looking for needed varargs argument for %s between ' + '%s..%s' % (ida.atoa(ea), ida.atoa(start_ea))) if ea == start_ea: ea = ida.prev_head(ea) continue # skip the call insn elif list(ida.code_refs_from(ea, 0)) != []: raise VarargsError( 'encountered branch/jump while looking for varargs argument ' + 'between %s..%s' % (ida.atoa(ea), ida.atoa(start_ea))) rd = ida.get_op(ea, 0) if rd.val == wanted_reg: opvals = ida.get_opvals(ea) # XXX should try to track stkvar values s = ida.get_string(opvals[-1].target) if s is not None: return s ea = ida.prev_head(ea) distance += 1
def get_arg_for_va_function(callee, start_ea): """str -> ea_t -> str""" # XXX hacky; not a very general function # XXX imperative # get a relevant item needed for processing a variadic function sw = {"printf": regs.gpr(abi.arg_regs[0]), "scanf": regs.gpr(abi.arg_regs[0]), "sscanf": regs.gpr(abi.arg_regs[1])} try: wanted_reg = sw[callee] except KeyError: raise utils.BugError("unrecognized callee %s" % callee) distance = 0 fn = ida.get_func(start_ea) # first, look at the delay slot ea = ida.next_head(start_ea, fn.endEA) while True: if distance > 10: raise VarargsError( "gave up looking for needed varargs argument for %s between " + "%s..%s" % (ida.atoa(ea), ida.atoa(start_ea)) ) if ea == start_ea: ea = ida.prev_head(ea) continue # skip the call insn elif list(ida.code_refs_from(ea, 0)) != []: raise VarargsError( "encountered branch/jump while looking for varargs argument " + "between %s..%s" % (ida.atoa(ea), ida.atoa(start_ea)) ) rd = ida.get_op(ea, 0) if rd.val == wanted_reg: opvals = ida.get_opvals(ea) # XXX should try to track stkvar values s = ida.get_string(opvals[-1].target) if s is not None: return s ea = ida.prev_head(ea) distance += 1
def get_op_addrmode(ea, op, cmd): '''ea_t -> int -> insn_t -> op_ret''' # the ida module calls back into this module to deal with some MIPS-specific # operand handling here mnem = ida.get_mnem(ea) op = ida.mips_op_hack(cmd, op) if cmd[op].type == ida.o_imm: val = cmd[op].value elif cmd[op].type == ida.o_displ: val = cmd[op].addr else: raise utils.BugError('neither imm nor displ passed to get_op_addrmode') if mnem in insns.has_special_opnd: target = val return ida.resolve_opnd(target, val) # addiu is often used for address calculation, which IDA will resolve to a # name, so handle addiu's immval only if we fail to resolve it later elif mnem != 'addiu' and mnem in insns.has_imm: return ida.op_ret(ida.op_ty.value, immval(val), 0) else: target = ida.calc_target(ea, ea, op, immval(val)) if target == ida.BADADDR and cmd[op].type == ida.o_displ: reg = cmd[op].reg if reg >= 0 and reg <= 31: reg = regs.gpr(reg) elif reg >= 32 and reg <= 63: reg = regs.fpr(reg) else: raise utils.BugError('bogus register %u' % reg) return ida.op_ret(ida.op_ty.displ, ida.displ(reg=reg, displ=immval(val)), 0) else: opnd = ida.resolve_opnd(target, val) if mnem == 'addiu' and opnd.ty == ida.op_ty.value: # addiu is being used for regular addition; handle its third # operand as an immediate value return ida.op_ret(ida.op_ty.value, immval(opnd.val), 0) else: return opnd
def fmt_reg(mnem, arg, slot=None): '''str -> int -> reg -> opt:slot_types -> str''' if arg == regs.gpr(0): return c_ast.Constant('int', '0') else: insn = insns.insns[mnem] stripped = reg_strip(arg) # XXX FIXME: we store the register gpr/fpr register number, but # regs_by_reference is offsets into IDA's register list regnum = arg.reg + (abi.fpr_off if type(arg) is regs.fpr else 0) if regnum in abi.regs_by_reference: # refer to argument via ARGS-> r = c_ast.StructRef(c_ast.ID(utils.args_tag), '->', c_ast.ID(stripped)) else: r = c_ast.ID(stripped) if slot is not None and slot != ep_ct.slot_types._: # use a union slot return c_ast.StructRef(r, '.', c_ast.ID(slot.name)) else: return r
def get_op_addrmode(ea, op, cmd): """ea_t -> int -> insn_t -> op_ret""" # the ida module calls back into this module to deal with some MIPS-specific # operand handling here mnem = ida.get_mnem(ea) op = ida.mips_op_hack(cmd, op) if cmd[op].type == ida.o_imm: val = cmd[op].value elif cmd[op].type == ida.o_displ: val = cmd[op].addr else: raise utils.BugError("neither imm nor displ passed to get_op_addrmode") if mnem in insns.has_special_opnd: target = val return ida.resolve_opnd(target, val) # addiu is often used for address calculation, which IDA will resolve to a # name, so handle addiu's immval only if we fail to resolve it later elif mnem != "addiu" and mnem in insns.has_imm: return ida.op_ret(ida.op_ty.value, immval(val), 0) else: target = ida.calc_target(ea, ea, op, immval(val)) if target == ida.BADADDR and cmd[op].type == ida.o_displ: reg = cmd[op].reg if reg >= 0 and reg <= 31: reg = regs.gpr(reg) elif reg >= 32 and reg <= 63: reg = regs.fpr(reg) else: raise utils.BugError("bogus register %u" % reg) return ida.op_ret(ida.op_ty.displ, ida.displ(reg=reg, displ=immval(val)), 0) else: opnd = ida.resolve_opnd(target, val) if mnem == "addiu" and opnd.ty == ida.op_ty.value: # addiu is being used for regular addition; handle its third # operand as an immediate value return ida.op_ret(ida.op_ty.value, immval(opnd.val), 0) else: return opnd