280 lines
13 KiB
Python
280 lines
13 KiB
Python
import logging
|
|
import HexRaysPyTools.Actions as Actions
|
|
import HexRaysPyTools.Core.Cache
|
|
from HexRaysPyTools.Core.TemporaryStructure import *
|
|
import HexRaysPyTools.Forms as Forms
|
|
import idaapi
|
|
import HexRaysPyTools.Core.NegativeOffsets as NegativeOffsets
|
|
import HexRaysPyTools.Core.Helper as Helper
|
|
import HexRaysPyTools.Core.Cache as Cache
|
|
import HexRaysPyTools.Core.Const as Const
|
|
from HexRaysPyTools.Core.SpaghettiCode import SpaghettiVisitor, SwapThenElseVisitor
|
|
from HexRaysPyTools.Core.StructXrefs import *
|
|
|
|
potential_negatives = {}
|
|
|
|
|
|
def hexrays_events_callback(*args):
|
|
global potential_negatives
|
|
|
|
hexrays_event = args[0]
|
|
|
|
if hexrays_event == idaapi.hxe_populating_popup:
|
|
form, popup, hx_view = args[1:]
|
|
item = hx_view.item # current ctree_item_t
|
|
|
|
if Actions.GuessAllocation.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.GuessAllocation.name, None)
|
|
|
|
if Actions.RecastItemRight.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RecastItemRight.name, None)
|
|
|
|
if Actions.RecastItemLeft.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RecastItemLeft.name, None)
|
|
|
|
if Actions.RenameOther.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RenameOther.name, None)
|
|
|
|
if Actions.RenameInside.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RenameInside.name, None)
|
|
|
|
if Actions.RenameOutside.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RenameOutside.name, None)
|
|
|
|
if Actions.RenameUsingAssert.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RenameUsingAssert.name, None)
|
|
|
|
if Actions.SwapThenElse.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.SwapThenElse.name, None)
|
|
|
|
if Actions.ShallowScanVariable.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.ShallowScanVariable.name, None)
|
|
idaapi.attach_action_to_popup(form, popup, Actions.DeepScanVariable.name, None)
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RecognizeShape.name, None)
|
|
|
|
if Actions.CreateNewField.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.CreateNewField.name, None)
|
|
|
|
if Actions.FindFieldXrefs.check(item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.FindFieldXrefs.name, None)
|
|
|
|
if Actions.PropagateName.check(hx_view.cfunc, item):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.PropagateName.name, None)
|
|
|
|
if item.citype == idaapi.VDI_FUNC:
|
|
# If we clicked on function
|
|
if not hx_view.cfunc.entry_ea == idaapi.BADADDR: # Probably never happen
|
|
idaapi.attach_action_to_popup(form, popup, Actions.AddRemoveReturn.name, None)
|
|
idaapi.attach_action_to_popup(form, popup, Actions.ConvertToUsercall.name, None)
|
|
if Actions.DeepScanReturn.check(hx_view):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.DeepScanReturn.name, None)
|
|
|
|
elif item.citype == idaapi.VDI_LVAR:
|
|
# If we clicked on argument
|
|
local_variable = hx_view.item.get_lvar() # idaapi.lvar_t
|
|
if local_variable.is_arg_var:
|
|
idaapi.attach_action_to_popup(form, popup, Actions.RemoveArgument.name, None)
|
|
|
|
elif item.citype == idaapi.VDI_EXPR:
|
|
if item.e.op == idaapi.cot_num:
|
|
# number_format = item.e.n.nf # idaapi.number_format_t
|
|
# print "(number) flags: {0:#010X}, type_name: {1}, opnum: {2}".format(
|
|
# number_format.flags,
|
|
# number_format.type_name,
|
|
# number_format.opnum
|
|
# )
|
|
idaapi.attach_action_to_popup(form, popup, Actions.GetStructureBySize.name, None)
|
|
elif item.e.op == idaapi.cot_var:
|
|
# Check if we clicked on variable that is a pointer to a structure that is potentially part of
|
|
# containing structure
|
|
if item.e.v.idx in potential_negatives:
|
|
idaapi.attach_action_to_popup(form, popup, Actions.SelectContainingStructure.name, None)
|
|
if Actions.ResetContainingStructure.check(hx_view.cfunc.get_lvars()[item.e.v.idx]):
|
|
idaapi.attach_action_to_popup(form, popup, Actions.ResetContainingStructure.name, None)
|
|
|
|
elif hexrays_event == idaapi.hxe_double_click:
|
|
hx_view = args[1]
|
|
item = hx_view.item
|
|
if item.citype == idaapi.VDI_EXPR and item.e.op == idaapi.cot_memptr:
|
|
# Look if we double clicked on expression that is member pointer. Then get tinfo_t of the structure.
|
|
# After that remove pointer and get member name with the same offset
|
|
if item.e.x.op == idaapi.cot_memref and item.e.x.x.op == idaapi.cot_memptr:
|
|
vtable_tinfo = item.e.x.type.get_pointed_object()
|
|
method_offset = item.e.m
|
|
class_tinfo = item.e.x.x.x.type.get_pointed_object()
|
|
vtable_offset = item.e.x.x.m
|
|
elif item.e.x.op == idaapi.cot_memptr:
|
|
vtable_tinfo = item.e.x.type.get_pointed_object()
|
|
method_offset = item.e.m
|
|
class_tinfo = item.e.x.x.type.get_pointed_object()
|
|
vtable_offset = item.e.x.m
|
|
else:
|
|
func_offset = item.e.m
|
|
struct_tinfo = item.e.x.type.get_pointed_object()
|
|
func_ea = Helper.get_virtual_func_address(Helper.get_member_name(struct_tinfo, func_offset))
|
|
if func_ea:
|
|
idaapi.jumpto(func_ea)
|
|
return 0
|
|
|
|
func_name = Helper.get_member_name(vtable_tinfo, method_offset)
|
|
func_ea = Helper.get_virtual_func_address(func_name, class_tinfo, vtable_offset)
|
|
if func_ea:
|
|
idaapi.open_pseudocode(func_ea, 0)
|
|
return 1
|
|
|
|
elif hexrays_event == idaapi.hxe_maturity:
|
|
cfunc, level_of_maturity = args[1:]
|
|
|
|
if level_of_maturity == idaapi.CMAT_BUILT:
|
|
# print '=' * 40
|
|
# print '=' * 15, "LEVEL", level_of_maturity, '=' * 16
|
|
# print '=' * 40
|
|
# print cfunc
|
|
|
|
# First search for CONTAINING_RECORD made by Ida
|
|
visitor = NegativeOffsets.SearchVisitor(cfunc)
|
|
visitor.apply_to(cfunc.body, None)
|
|
negative_lvars = visitor.result
|
|
|
|
# Second get saved information from comments
|
|
lvars = cfunc.get_lvars()
|
|
for idx in xrange(len(lvars)):
|
|
result = NegativeOffsets.parse_lvar_comment(lvars[idx])
|
|
if result and result.tinfo.equals_to(lvars[idx].type().get_pointed_object()):
|
|
negative_lvars[idx] = result
|
|
|
|
# Third make an analysis of local variables that a structure pointers and have reference that pass
|
|
# through structure boundaries. This variables will be considered as potential pointers to substructure
|
|
# and will get a menu on right click that helps to select Containing Structure from different libraries
|
|
|
|
structure_pointer_variables = {}
|
|
for idx in set(range(len(lvars))) - set(negative_lvars.keys()):
|
|
if lvars[idx].type().is_ptr():
|
|
pointed_tinfo = lvars[idx].type().get_pointed_object()
|
|
if pointed_tinfo.is_udt():
|
|
structure_pointer_variables[idx] = pointed_tinfo
|
|
|
|
if structure_pointer_variables:
|
|
visitor = NegativeOffsets.AnalyseVisitor(structure_pointer_variables, potential_negatives)
|
|
visitor.apply_to(cfunc.body, None)
|
|
|
|
if negative_lvars:
|
|
visitor = NegativeOffsets.ReplaceVisitor(negative_lvars)
|
|
visitor.apply_to(cfunc.body, None)
|
|
|
|
elif level_of_maturity == idaapi.CMAT_TRANS1:
|
|
|
|
visitor = SwapThenElseVisitor(cfunc.entry_ea)
|
|
visitor.apply_to(cfunc.body, None)
|
|
|
|
elif level_of_maturity == idaapi.CMAT_TRANS2:
|
|
# print '=' * 40
|
|
# print '=' * 15, "LEVEL", level_of_maturity, '=' * 16
|
|
# print '=' * 40
|
|
# print cfunc
|
|
visitor = SpaghettiVisitor()
|
|
visitor.apply_to(cfunc.body, None)
|
|
|
|
elif level_of_maturity == idaapi.CMAT_FINAL:
|
|
StructXrefVisitor(cfunc).process()
|
|
|
|
return 0
|
|
|
|
|
|
class MyPlugin(idaapi.plugin_t):
|
|
# flags = idaapi.PLUGIN_HIDE
|
|
flags = 0
|
|
comment = "Plugin for automatic classes reconstruction"
|
|
help = "This is help"
|
|
wanted_name = "HexRaysPyTools"
|
|
wanted_hotkey = "Alt-F8"
|
|
|
|
@staticmethod
|
|
def init():
|
|
if not idaapi.init_hexrays_plugin():
|
|
print "[ERROR] Failed to initialize Hex-Rays SDK"
|
|
return idaapi.PLUGIN_SKIP
|
|
|
|
Cache.temporary_structure = TemporaryStructureModel()
|
|
# Actions.register(Actions.CreateVtable)
|
|
Actions.register(Actions.ShowGraph)
|
|
Actions.register(Actions.ShowClasses)
|
|
Actions.register(Actions.GetStructureBySize)
|
|
Actions.register(Actions.RemoveArgument)
|
|
Actions.register(Actions.AddRemoveReturn)
|
|
Actions.register(Actions.ConvertToUsercall)
|
|
Actions.register(Actions.ShallowScanVariable, Cache.temporary_structure)
|
|
Actions.register(Actions.DeepScanVariable, Cache.temporary_structure)
|
|
Actions.register(Actions.DeepScanReturn, Cache.temporary_structure)
|
|
Actions.register(Actions.DeepScanFunctions, Cache.temporary_structure)
|
|
Actions.register(Actions.RecognizeShape)
|
|
Actions.register(Actions.CreateNewField)
|
|
Actions.register(Actions.SelectContainingStructure, potential_negatives)
|
|
Actions.register(Actions.ResetContainingStructure)
|
|
Actions.register(Actions.RecastItemRight)
|
|
Actions.register(Actions.RecastItemLeft)
|
|
Actions.register(Actions.RenameOther)
|
|
Actions.register(Actions.RenameInside)
|
|
Actions.register(Actions.RenameOutside)
|
|
Actions.register(Actions.RenameUsingAssert)
|
|
Actions.register(Actions.SwapThenElse)
|
|
Actions.register(Actions.FindFieldXrefs)
|
|
Actions.register(Actions.PropagateName)
|
|
Actions.register(Actions.GuessAllocation)
|
|
|
|
idaapi.attach_action_to_menu('View/Open subviews/Local types', Actions.ShowClasses.name, idaapi.SETMENU_APP)
|
|
idaapi.install_hexrays_callback(hexrays_events_callback)
|
|
|
|
Const.init()
|
|
XrefStorage().open()
|
|
|
|
return idaapi.PLUGIN_KEEP
|
|
|
|
@staticmethod
|
|
def run(arg):
|
|
tform = idaapi.find_tform("Structure Builder")
|
|
if tform:
|
|
idaapi.switchto_tform(tform, True)
|
|
else:
|
|
Forms.StructureBuilder(Cache.temporary_structure).Show()
|
|
|
|
@staticmethod
|
|
def term():
|
|
if Cache.temporary_structure:
|
|
Cache.temporary_structure.clear()
|
|
# Actions.unregister(Actions.CreateVtable)
|
|
Actions.unregister(Actions.ShowGraph)
|
|
Actions.unregister(Actions.ShowClasses)
|
|
Actions.unregister(Actions.GetStructureBySize)
|
|
Actions.unregister(Actions.RemoveArgument)
|
|
Actions.unregister(Actions.AddRemoveReturn)
|
|
Actions.unregister(Actions.ConvertToUsercall)
|
|
Actions.unregister(Actions.ShallowScanVariable)
|
|
Actions.unregister(Actions.DeepScanVariable)
|
|
Actions.unregister(Actions.DeepScanReturn)
|
|
Actions.unregister(Actions.DeepScanFunctions)
|
|
Actions.unregister(Actions.RecognizeShape)
|
|
Actions.unregister(Actions.CreateNewField)
|
|
Actions.unregister(Actions.SelectContainingStructure)
|
|
Actions.unregister(Actions.ResetContainingStructure)
|
|
Actions.unregister(Actions.RecastItemRight)
|
|
Actions.unregister(Actions.RecastItemLeft)
|
|
Actions.unregister(Actions.RenameOther)
|
|
Actions.unregister(Actions.RenameInside)
|
|
Actions.unregister(Actions.RenameOutside)
|
|
Actions.unregister(Actions.RenameUsingAssert)
|
|
Actions.unregister(Actions.SwapThenElse)
|
|
Actions.unregister(Actions.FindFieldXrefs)
|
|
Actions.unregister(Actions.PropagateName)
|
|
Actions.unregister(Actions.GuessAllocation)
|
|
idaapi.term_hexrays_plugin()
|
|
XrefStorage().close()
|
|
|
|
|
|
def PLUGIN_ENTRY():
|
|
idaapi.notify_when(idaapi.NW_OPENIDB, Cache.init_demangled_names)
|
|
idaapi.notify_when(idaapi.NW_OPENIDB, Cache.init_imported_ea)
|
|
idaapi.notify_when(idaapi.NW_OPENIDB, Cache.reset_touched_functions)
|
|
Helper.extend_ida()
|
|
return MyPlugin()
|