Skip to content

Commit

Permalink
Feature: flag for force watching clicks (#133)
Browse files Browse the repository at this point in the history
* Feature: flag for force watching clicks

* Fix angr api change
  • Loading branch information
mahaloz authored Nov 7, 2024
1 parent 434c2ca commit 650cf12
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 34 deletions.
4 changes: 3 additions & 1 deletion examples/change_watcher_plugin/bs_change_watcher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ def create_plugin(*args, **kwargs):
plugin_name="ArtifactChangeWatcher",
init_plugin=True,
decompiler_started_callbacks=decompiler_started_event_callbacks,
# passing the flag below forces click recording to start on decompiler startup
# force_click_recording = True,
gui_init_args=args,
gui_init_kwargs=kwargs
gui_init_kwargs=kwargs,
)
# create a function to print a string in the decompiler console
decompiler_printer = lambda *x, **y: deci.print(f"Changed {x}")
Expand Down
2 changes: 1 addition & 1 deletion libbs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "2.3.1"
__version__ = "2.4.0"


import logging
Expand Down
10 changes: 6 additions & 4 deletions libbs/api/decompiler_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def __init__(
undo_event_callbacks: Optional[List[Callable]] = None,
decompiler_started_callbacks: Optional[List[Callable]] = None,
thread_artifact_callbacks: bool = True,
force_click_recording: bool = False,
):
self.name = name
self.art_lifter = artifact_lifter
Expand All @@ -82,7 +83,8 @@ def __init__(
self._gui_ctx_menu_actions = []
self._plugin_name = plugin_name
self.gui_plugin = None
self._artifact_watchers_started = False
self.artifact_watchers_started = False
self.force_click_recording = force_click_recording

# locks
self.artifact_write_lock = threading.Lock()
Expand Down Expand Up @@ -155,7 +157,7 @@ def _init_gui_plugin(self, *args, **kwargs):

def shutdown(self):
self.config.save()
if self._artifact_watchers_started:
if self.artifact_watchers_started:
self.stop_artifact_watchers()

#
Expand Down Expand Up @@ -236,7 +238,7 @@ def start_artifact_watchers(self):
@return:
"""
self.debug("Starting BinSync artifact watchers...")
self._artifact_watchers_started = True
self.artifact_watchers_started = True

def stop_artifact_watchers(self):
"""
Expand All @@ -246,7 +248,7 @@ def stop_artifact_watchers(self):
react to them.
"""
self.debug("Stopping BinSync artifact watchers...")
self._artifact_watchers_started = False
self.artifact_watchers_started = False

@property
def binary_base_addr(self) -> int:
Expand Down
6 changes: 3 additions & 3 deletions libbs/decompilers/angr/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,12 @@ def _angr_management_decompile(self, func):

def decompile_function(self, func, refresh_gui=False):
# check for known decompilation
available = self.main_instance.project.kb.structured_code.available_flavors(func.addr)
available = self.main_instance.project.kb.decompilations.available_flavors(func.addr)
should_decompile = False
if self.headless or 'pseudocode' not in available:
should_decompile = True
else:
cached = self.main_instance.project.kb.structured_code[(func.addr, 'pseudocode')]
cached = self.main_instance.project.kb.decompilations[(func.addr, 'pseudocode')]
if isinstance(cached, DummyStructuredCodeGenerator):
should_decompile = True

Expand All @@ -420,7 +420,7 @@ def decompile_function(self, func, refresh_gui=False):

# grab newly cached pseudocode
if not self.headless:
decomp = self.main_instance.project.kb.structured_code[(func.addr, 'pseudocode')]
decomp = self.main_instance.project.kb.decompilations[(func.addr, 'pseudocode')]

# refresh the UI after decompiling
if refresh_gui and not self.headless:
Expand Down
4 changes: 2 additions & 2 deletions libbs/decompilers/binja/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def get_decompilation_object(self, function: Function, **kwargs) -> Optional[obj
return None

def start_artifact_watchers(self):
if not self._artifact_watchers_started:
if not self.artifact_watchers_started:
from .hooks import DataMonitor
if self.bv is None:
raise RuntimeError("Cannot start artifact watchers without a BinaryView.")
Expand All @@ -308,7 +308,7 @@ def start_artifact_watchers(self):
super().start_artifact_watchers()

def stop_artifact_watchers(self):
if self._artifact_watchers_started:
if self.artifact_watchers_started:
self.bv.unregister_notification(self._data_monitor)
self._data_monitor = None
super().stop_artifact_watchers()
Expand Down
4 changes: 2 additions & 2 deletions libbs/decompilers/ghidra/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def start_artifact_watchers(self):
return

from .hooks import create_data_monitor
if not self._artifact_watchers_started:
if not self.artifact_watchers_started:
if self.flat_api is None:
raise RuntimeError("Cannot start artifact watchers without Ghidra Bridge connection.")

Expand All @@ -145,7 +145,7 @@ def start_artifact_watchers(self):
super().start_artifact_watchers()

def stop_artifact_watchers(self):
if self._artifact_watchers_started:
if self.artifact_watchers_started:
self._data_monitor = None
# TODO: generalize superclass method?
super().stop_artifact_watchers()
Expand Down
12 changes: 10 additions & 2 deletions libbs/decompilers/ida/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,15 @@ def set_func_ret_type(ea, return_type_str):
#


@execute_write
def _get_ida_version():
return idaapi.get_kernel_version()


def get_ida_version():
global _IDA_VERSION
if _IDA_VERSION is None:
_IDA_VERSION = Version(idaapi.get_kernel_version())
_IDA_VERSION = Version(_get_ida_version())

return _IDA_VERSION

Expand Down Expand Up @@ -1627,6 +1632,7 @@ def xrefs_to(addr):
return list(idautils.XrefsTo(addr))


@execute_write
def wait_for_idc_initialization():
idc.auto_wait()

Expand All @@ -1648,11 +1654,13 @@ def has_older_hexrays_version():
return not vers.startswith("8.2")


@execute_write
def get_decompiler_version() -> typing.Optional[Version]:
wait_for_idc_initialization()
try:
_vers = ida_hexrays.get_hexrays_version()
except Exception:
except Exception as e:
_l.critical("Failed to get decompiler version: %s", e)
return None

try:
Expand Down
32 changes: 14 additions & 18 deletions libbs/decompilers/ida/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,29 +89,25 @@ def __init__(self, interface: "IDAInterface"):
super(ScreenHook, self).__init__()

def view_click(self, view, event):
if not self.interface._artifact_watchers_started:
return

ctx = compat.view_to_bs_context(view)
if ctx is None:
return

ctx = self.interface.art_lifter.lift(ctx)
self.interface._gui_active_context = ctx
self.interface.gui_context_changed(ctx)
self._handle_view_event(view, click=True)

def view_activated(self, view: "TWidget *"):
if not self.interface._artifact_watchers_started:
return
self._handle_view_event(view)

ctx = compat.view_to_bs_context(view)
if ctx is None:
return
def _handle_view_event(self, view, click=False):
if self.interface.force_click_recording or self.interface.artifact_watchers_started:
# drop ctx for speed when the artifact watches have not been officially started, and we are not clicking
if (self.interface.force_click_recording and not self.interface.artifact_watchers_started) and not click:
return

ctx = compat.view_to_bs_context(view)
if ctx is None:
return

ctx = self.interface.art_lifter.lift(ctx)
self.interface._gui_active_context = ctx
self.interface.gui_context_changed(ctx)
ctx = self.interface.art_lifter.lift(ctx)
self.interface._gui_active_context = ctx

self.interface.gui_context_changed(ctx)

class IDAHotkeyHook(ida_kernwin.UI_Hooks):
def __init__(self, keys_to_pass, uiptr):
Expand Down
2 changes: 1 addition & 1 deletion libbs/decompilers/ida/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def should_watch_artifacts(self) -> bool:
if not self._ida_analysis_finished:
self._ida_analysis_finished = ida_auto.auto_is_ok()

return self._ida_analysis_finished and self._artifact_watchers_started
return self._ida_analysis_finished and self.artifact_watchers_started

#
# Optional API
Expand Down

0 comments on commit 650cf12

Please sign in to comment.