mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
100 lines
3.7 KiB
Python
100 lines
3.7 KiB
Python
"""
|
|
summary: dynamically provide a custom call type
|
|
|
|
description:
|
|
This plugin can greatly improve decompilation of indirect calls:
|
|
|
|
call [eax+4]
|
|
|
|
For them, the decompiler has to guess the prototype of the called function.
|
|
This has to be done at a very early phase of decompilation because
|
|
the function prototype influences the data flow analysis. On the other
|
|
hand, we do not have global data flow analysis results yet because
|
|
we haven't analyzed all calls in the function. It is a chicked-and-egg
|
|
problem.
|
|
|
|
The decompiler uses various techniques to guess the called function
|
|
prototype. While it works very well, it may fail in some cases.
|
|
|
|
To fix, the user can specify the call prototype manually, using
|
|
"Edit, Operand types, Set operand type" at the call instruction.
|
|
|
|
This plugin illustrates another approach to the problem:
|
|
if you happen to be able to calculate the call prototypes dynamically,
|
|
this is how to inform the decompiler about them.
|
|
|
|
level: intermediate
|
|
"""
|
|
|
|
import ida_idaapi
|
|
import ida_nalt
|
|
import ida_kernwin
|
|
import ida_typeinf
|
|
import ida_hexrays
|
|
|
|
testing = False # only for testing purposes
|
|
|
|
class callinfo_provider_t(ida_hexrays.Hexrays_Hooks):
|
|
|
|
# this callback will be called for all call instructions
|
|
# our plugin may provide the function prototype or even a complete new callinfo
|
|
# object. The callinfo object may be useful if the prototype is not enough
|
|
# to express all details of the call.
|
|
def build_callinfo(self, blk, type):
|
|
# it is a good idea to skip direct calls.
|
|
# note that some indirect calls may be resolved and become direct calls,
|
|
# and will be filtered out here:
|
|
ida_kernwin.msg("%x: got called for: %s\n" % (blk.tail.ea, blk.tail.dstr()))
|
|
tail = blk.tail
|
|
if tail.opcode == ida_hexrays.m_call:
|
|
return
|
|
|
|
# also, if the type was specified by the user, do not interfere
|
|
call_ea = tail.ea
|
|
tif = ida_typeinf.tinfo_t()
|
|
if ida_nalt.get_op_tinfo(tif, call_ea, 0):
|
|
return
|
|
|
|
global testing
|
|
if not testing:
|
|
# ok, the decompiler really has to guess the type.
|
|
# just for the sake of an example, return a predefined prototype.
|
|
# in real life you will provide the prototype you discovered yourself,
|
|
# using your magic of yours :)
|
|
my_proto = "int f();"
|
|
ida_kernwin.msg("%x: providing prototype %s\n" % (call_ea, my_proto))
|
|
ida_typeinf.parse_decl(type, None, my_proto, 0)
|
|
else:
|
|
# as an alternative to filling 'type', you can
|
|
# choose to return a mcallinfo_t instance.
|
|
mi = ida_hexrays.mcallinfo_t()
|
|
mi.cc = ida_typeinf.CM_CC_STDCALL | ida_typeinf.CM_N32_F48 # let's use stdcall to differentiate from the tinfo_t-filling version
|
|
mi.return_type = ida_typeinf.tinfo_t(ida_typeinf.BT_INT)
|
|
mi.return_argloc.set_reg1(0) # eax
|
|
return mi
|
|
|
|
# a plugin interface, boilerplate code
|
|
class my_plugin_t(ida_idaapi.plugin_t):
|
|
flags = ida_idaapi.PLUGIN_HIDE
|
|
wanted_name = "Hex-Rays custom prototype provider (IDAPython)"
|
|
wanted_hotkey = ""
|
|
comment = "Sample plugin21 for Hex-Rays decompiler"
|
|
help = ""
|
|
def init(self):
|
|
if ida_hexrays.init_hexrays_plugin():
|
|
self.hooks = callinfo_provider_t()
|
|
self.hooks.hook()
|
|
ida_kernwin.warning(
|
|
"Installed callinfo provider sample (vds21.py)\n" +\
|
|
"Please note that it is just an example\n" +\
|
|
"and will spoil your decompilations!")
|
|
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
|
|
def term(self):
|
|
self.hooks.unhook()
|
|
def run(self, arg):
|
|
pass
|
|
|
|
def PLUGIN_ENTRY():
|
|
return my_plugin_t()
|
|
|