diff --git a/libbs/artifacts/context.py b/libbs/artifacts/context.py index 5feee99..f9cd452 100644 --- a/libbs/artifacts/context.py +++ b/libbs/artifacts/context.py @@ -7,17 +7,27 @@ class Context(Artifact): __slots__ = Artifact.__slots__ + ( "addr", "func_addr", - "screen_name" + "screen_name", + "variable" ) - def __init__(self, addr: int = None, func_addr: Optional[int] = None, screen_name: str = None, **kwargs): - self.addr: Optional[int] = addr - self.func_addr: Optional[int] = func_addr - self.screen_name: str = screen_name + def __init__( + self, + addr: Optional[int] = None, + func_addr: Optional[int] = None, + screen_name: Optional[str] = None, + variable: Optional[str] = None, + **kwargs + ): + self.addr = addr + self.func_addr = func_addr + self.screen_name = screen_name + self.variable = variable super().__init__(**kwargs) def __str__(self): post_text = f" screen={self.screen_name}" if self.screen_name else "" + post_text += f" var={self.variable}" if self.variable else "" if self.func_addr is not None: post_text = f"@{hex(self.func_addr)}" + post_text if self.addr is not None: diff --git a/libbs/decompilers/ida/compat.py b/libbs/decompilers/ida/compat.py index 84d0df1..bcae2f8 100644 --- a/libbs/decompilers/ida/compat.py +++ b/libbs/decompilers/ida/compat.py @@ -20,7 +20,7 @@ import libbs from libbs.artifacts import ( - Struct, FunctionHeader, FunctionArgument, StackVariable, Function, GlobalVariable, Enum, Artifact + Struct, FunctionHeader, FunctionArgument, StackVariable, Function, GlobalVariable, Enum, Artifact, Context ) from PyQt5.Qt import QObject @@ -30,6 +30,16 @@ _l = logging.getLogger(__name__) +FORM_TYPE_TO_NAME = { + idaapi.BWN_PSEUDOCODE: "decompilation", + idaapi.BWN_DISASM: "disassembly", + idaapi.BWN_FUNCS: "functions", + idaapi.BWN_STRUCTS: "structs", + idaapi.BWN_ENUMS: "enums", +} + +FUNC_FORMS = {"decompilation", "disassembly"} + # # Wrappers for IDA Main thread r/w operations # a special note about these functions: @@ -1080,6 +1090,28 @@ def has_older_hexrays_version(): return not vers.startswith("8.2") +def view_to_bs_context(view, get_var=True) -> Optional[Context]: + form_type = idaapi.get_widget_type(view) + if form_type is None: + return None + + view_name = FORM_TYPE_TO_NAME.get(form_type, "unknown") + ctx = Context(screen_name=view_name) + if view_name in FUNC_FORMS: + ctx.addr = idaapi.get_screen_ea() + func = idaapi.get_func(ctx.addr) + if func is not None: + ctx.func_addr = func.start_ea + if get_var and view_name == "decompilation": + # get lvar info at cursor + vu = idaapi.get_widget_vdui(view) + if vu and vu.item: + lvar = vu.item.get_lvar() + if lvar: + ctx.variable = lvar.name + + return ctx + # # IDA Classes @@ -1108,25 +1140,22 @@ def term(self): class GenericAction(idaapi.action_handler_t): - def __init__(self, action_target, action_function): + def __init__(self, action_target, action_function, deci=None): idaapi.action_handler_t.__init__(self) self.action_target = action_target self.action_function = action_function + self.deci: IDAInterface = deci def activate(self, ctx): if ctx is None or ctx.action != self.action_target: return - dec_view = ida_hexrays.get_widget_vdui(ctx.widget) - # show a thing while we work - #prg = QProgressDialog("Querying...", "Stop", 0, 1, None) - #prg.show() - - self.action_function() + ctx = view_to_bs_context(ctx.widget) + if ctx is None: + return - # close the panel we showed while running - #prg.setValue(1) - #prg.close() + dec_view = ida_hexrays.get_widget_vdui(ctx.widget) + self.action_function(ctx, deci=self.deci) if dec_view is not None: dec_view.refresh_view(False) diff --git a/libbs/decompilers/ida/hooks.py b/libbs/decompilers/ida/hooks.py index 8f14da7..b75e27c 100644 --- a/libbs/decompilers/ida/hooks.py +++ b/libbs/decompilers/ida/hooks.py @@ -56,16 +56,6 @@ IDA_EXTRA_CMT = "extra" IDA_CMT_TYPES = {IDA_CMT_CMT, IDA_EXTRA_CMT, IDA_RANGE_CMT} -FORM_TYPE_TO_NAME = { - idaapi.BWN_PSEUDOCODE: "decompilation", - idaapi.BWN_DISASM: "disassembly", - idaapi.BWN_FUNCS: "functions", - idaapi.BWN_STRUCTS: "structs", - idaapi.BWN_ENUMS: "enums", -} - -FUNC_FORMS = {"decompilation", "disassembly"} - def while_should_watch(func): @functools.wraps(func) @@ -103,19 +93,10 @@ def view_click(self, view, event): if not self.interface._artifact_watchers_started: return - form_type = idaapi.get_widget_type(view) - #decomp_view = idaapi.get_widget_vdui(view) - if not form_type: + ctx = compat.view_to_bs_context(view) + if ctx is None: return - view_name = FORM_TYPE_TO_NAME.get(form_type, "unknown") - ctx = Context(screen_name=view_name) - if view_name in FUNC_FORMS: - ctx.addr = idaapi.get_screen_ea() - func = idaapi.get_func(ctx.addr) - if func is not None: - ctx.func_addr = func.start_ea - ctx = self.interface.art_lifter.lift(ctx) self.interface._gui_active_context = ctx self.interface.gui_context_changed(ctx) diff --git a/libbs/decompilers/ida/interface.py b/libbs/decompilers/ida/interface.py index 388387e..0425664 100755 --- a/libbs/decompilers/ida/interface.py +++ b/libbs/decompilers/ida/interface.py @@ -71,16 +71,15 @@ def gui_ask_for_choice(self, question: str, choices: list, title="Plugin Questio return compat.ask_choice(question, choices, title=title) def gui_register_ctx_menu(self, name, action_string, callback_func, category=None) -> bool: - # Function explaining action - explain_action = idaapi.action_desc_t( + action = idaapi.action_desc_t( name, action_string, - compat.GenericAction(name, callback_func), + compat.GenericAction(name, callback_func, deci=self), "", action_string, 199 ) - idaapi.register_action(explain_action) + idaapi.register_action(action) idaapi.attach_action_to_menu( f"Edit/{category}/{name}" if category else f"Edit/{name}", name,