mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
259 lines
6.9 KiB
Python
259 lines
6.9 KiB
Python
"""
|
|
summary: invert if/else blocks in decompilation
|
|
|
|
description:
|
|
Registers an action that can be used to invert the `if`
|
|
and `else` blocks of a `ida_hexrays.cif_t`.
|
|
|
|
For example, a statement like
|
|
|
|
if ( cond )
|
|
{
|
|
statements1;
|
|
}
|
|
else
|
|
{
|
|
statements2;
|
|
}
|
|
|
|
will be displayed as
|
|
|
|
if ( !cond )
|
|
{
|
|
statements2;
|
|
}
|
|
else
|
|
{
|
|
statements1;
|
|
}
|
|
|
|
The modifications are persistent: the user can quit & restart
|
|
IDA, and the changes will be present.
|
|
|
|
author: EiNSTeiN_ (einstein@g3nius.org)
|
|
|
|
level: advanced
|
|
"""
|
|
|
|
import idautils
|
|
|
|
import ida_kernwin
|
|
import ida_hexrays
|
|
import ida_netnode
|
|
import ida_idaapi
|
|
import ida_idp
|
|
|
|
import traceback
|
|
|
|
NETNODE_NAME = '$ hexrays-inverted-if'
|
|
|
|
inverter_actname = "vds3:invert"
|
|
|
|
class invert_action_handler_t(ida_kernwin.action_handler_t):
|
|
def __init__(self, inverter):
|
|
ida_kernwin.action_handler_t.__init__(self)
|
|
self.inverter = inverter
|
|
|
|
def activate(self, ctx):
|
|
vdui = ida_hexrays.get_widget_vdui(ctx.widget)
|
|
self.inverter.invert_if_event(vdui)
|
|
return 1
|
|
|
|
def update(self, ctx):
|
|
return ida_kernwin.AST_ENABLE_FOR_WIDGET if \
|
|
ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE else \
|
|
ida_kernwin.AST_DISABLE_FOR_WIDGET
|
|
|
|
|
|
class hexrays_callback_info(object):
|
|
|
|
def __init__(self):
|
|
self.vu = None
|
|
|
|
self.node = ida_netnode.netnode()
|
|
if not self.node.create(NETNODE_NAME):
|
|
# node exists
|
|
self.load()
|
|
else:
|
|
self.stored = []
|
|
|
|
return
|
|
|
|
def load(self):
|
|
|
|
self.stored = []
|
|
|
|
try:
|
|
data = self.node.getblob(0, 'I')
|
|
if data:
|
|
self.stored = eval(data.decode("UTF-8"))
|
|
print('Invert-if: Loaded %s' % (repr(self.stored), ))
|
|
except:
|
|
print('Failed to load invert-if locations')
|
|
traceback.print_exc()
|
|
return
|
|
|
|
return
|
|
|
|
def save(self):
|
|
|
|
try:
|
|
self.node.setblob(repr(self.stored).encode("UTF-8"), 0, 'I')
|
|
except:
|
|
print('Failed to save invert-if locations')
|
|
traceback.print_exc()
|
|
return
|
|
|
|
return
|
|
|
|
def invert_if(self, insn):
|
|
|
|
if insn.opname != 'if':
|
|
return False
|
|
|
|
cif = insn.details
|
|
|
|
if not cif.ithen or not cif.ielse:
|
|
return False
|
|
|
|
ida_hexrays.qswap(cif.ithen, cif.ielse)
|
|
# Make a copy of 'cif.expr': 'lnot' might destroy its toplevel
|
|
# cexpr_t and return a pointer to its direct child (but we'll want to
|
|
# 'swap' it later, the 'cif.expr' cexpr_t object must remain valid.)
|
|
cond = ida_hexrays.cexpr_t(cif.expr)
|
|
notcond = ida_hexrays.lnot(cond)
|
|
|
|
cif.expr.swap(notcond)
|
|
|
|
return True
|
|
|
|
def add_location(self, ea):
|
|
if ea in self.stored:
|
|
self.stored.remove(ea)
|
|
else:
|
|
self.stored.append(ea)
|
|
self.save()
|
|
return
|
|
|
|
def find_if_statement(self, vu):
|
|
|
|
vu.get_current_item(ida_hexrays.USE_KEYBOARD)
|
|
item = vu.item
|
|
|
|
if item.is_citem() and item.it.op == ida_hexrays.cit_if and item.it.to_specific_type.cif.ielse is not None:
|
|
return item.it.to_specific_type
|
|
|
|
if vu.tail.citype == ida_hexrays.VDI_TAIL and vu.tail.loc.itp == ida_hexrays.ITP_ELSE:
|
|
# for tail marks, we know only the corresponding ea,
|
|
# not the pointer to if-statement
|
|
# find it by walking the whole ctree
|
|
class if_finder_t(ida_hexrays.ctree_visitor_t):
|
|
def __init__(self, ea):
|
|
ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST | ida_hexrays.CV_INSNS)
|
|
|
|
self.ea = ea
|
|
self.found = None
|
|
return
|
|
|
|
def visit_insn(self, i):
|
|
if i.op == ida_hexrays.cit_if and i.ea == self.ea:
|
|
self.found = i
|
|
return 1 # stop enumeration
|
|
return 0
|
|
|
|
iff = if_finder_t(vu.tail.loc.ea)
|
|
if iff.apply_to(vu.cfunc.body, None):
|
|
return iff.found
|
|
|
|
return
|
|
|
|
def invert_if_event(self, vu):
|
|
|
|
i = self.find_if_statement(vu)
|
|
if not i:
|
|
return False
|
|
|
|
if self.invert_if(i):
|
|
vu.refresh_ctext()
|
|
self.add_location(i.ea)
|
|
|
|
return True
|
|
|
|
def restore(self, cfunc):
|
|
|
|
class visitor(ida_hexrays.ctree_visitor_t):
|
|
|
|
def __init__(self, inverter, cfunc):
|
|
ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST | ida_hexrays.CV_INSNS)
|
|
self.inverter = inverter
|
|
self.cfunc = cfunc
|
|
return
|
|
|
|
def visit_insn(self, i):
|
|
try:
|
|
if i.op == ida_hexrays.cit_if and i.ea in self.inverter.stored:
|
|
self.inverter.invert_if(i)
|
|
except:
|
|
traceback.print_exc()
|
|
return 0 # continue enumeration
|
|
|
|
visitor(self, cfunc).apply_to(cfunc.body, None)
|
|
|
|
return
|
|
|
|
|
|
class vds3_hooks_t(ida_hexrays.Hexrays_Hooks):
|
|
def __init__(self, i):
|
|
ida_hexrays.Hexrays_Hooks.__init__(self)
|
|
self.i = i
|
|
|
|
def populating_popup(self, widget, phandle, vu):
|
|
ida_kernwin.attach_action_to_popup(vu.ct, None, inverter_actname)
|
|
return 0
|
|
|
|
def maturity(self, cfunc, maturity):
|
|
if maturity == ida_hexrays.CMAT_FINAL:
|
|
self.i.restore(cfunc)
|
|
return 0
|
|
|
|
class idp_hooks_t(ida_idp.IDP_Hooks):
|
|
def __init__(self, i):
|
|
ida_idp.IDP_Hooks.__init__(self)
|
|
self.i = i
|
|
|
|
# 'node' refers to index of the named node, this index became invalid after
|
|
# privrange moving, so we recreate the node here to update nodeidx
|
|
def ev_privrange_changed(self, old_privrange, delta):
|
|
i.node.create(NETNODE_NAME)
|
|
|
|
|
|
# a plugin interface, boilerplate code
|
|
class my_plugin_t(ida_idaapi.plugin_t):
|
|
flags = ida_idaapi.PLUGIN_HIDE
|
|
wanted_name = "Hex-Rays if-inverter (IDAPython)"
|
|
wanted_hotkey = ""
|
|
comment = "Sample plugin3 for Hex-Rays decompiler"
|
|
help = ""
|
|
def init(self):
|
|
if ida_hexrays.init_hexrays_plugin():
|
|
i = hexrays_callback_info()
|
|
ida_kernwin.register_action(
|
|
ida_kernwin.action_desc_t(
|
|
inverter_actname,
|
|
"Invert then/else",
|
|
invert_action_handler_t(i),
|
|
"I"))
|
|
self.vds3_hooks = vds3_hooks_t(i)
|
|
self.vds3_hooks.hook()
|
|
# we need this hook to react to privrange moving event
|
|
self.idp_hooks = idp_hooks_t(i)
|
|
self.idp_hooks.hook()
|
|
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
|
|
def term(self):
|
|
self.vds3_hooks.unhook()
|
|
def run(self, arg):
|
|
pass
|
|
|
|
def PLUGIN_ENTRY():
|
|
return my_plugin_t()
|