Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dev_scripts/test_headless.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"boombox.exe"
))

print "Opening '%s' headlessly..." % target
print(f"Opening '{target}' headlessly...")
x = binaryninja.BinaryViewType["PE"].open(target)
x.update_analysis_and_wait()
print "DONE!"
print("DONE!")
40 changes: 34 additions & 6 deletions plugins/lighthouse/integration/binja_integration.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import ctypes
import logging

from binaryninja import PluginCommand
from binaryninjaui import UIAction, UIActionHandler, Menu
from binaryninja import PluginCommand, BinaryView
from binaryninjaui import UIAction, UIActionHandler, Menu, Sidebar

from lighthouse.context import LighthouseContext
from lighthouse.integration.core import LighthouseCore
from lighthouse.util.disassembler import disassembler
from lighthouse.util.disassembler.binja_api import set_integration_hack, LighthouseWidgetType # Import new hook

logger = logging.getLogger("Lighthouse.Binja.Integration")

Expand All @@ -22,13 +23,30 @@ class LighthouseBinja(LighthouseCore):
def __init__(self):
super(LighthouseBinja, self).__init__()

# Overload the internal load function to set up the widget hack
def load(self):
# Inject our instance into the global scope so the SidebarWidget can access it
set_integration_hack(self)

# Manually register the SidebarWidgetType here, since the abstract
# register_dockable call is now a no-op (due to the hack).
Sidebar.addSidebarWidgetType(LighthouseWidgetType())

super(LighthouseBinja, self).load()

def get_context(self, dctx, startup=True):
"""
Get the LighthouseContext object for a given database context.

In Binary Ninja, a dctx is a BinaryView (BV).
"""
dctx_id = ctypes.addressof(dctx.handle.contents)
# If dctx is a BinaryView, use the address of its handle content as a unique ID
if isinstance(dctx, BinaryView):
dctx_id = ctypes.addressof(dctx.handle.contents)
else:
# Fallback if the object is not a BinaryView but should still be tracked
dctx_id = str(id(dctx))


#
# create a new LighthouseContext if this is the first time a context
Expand Down Expand Up @@ -73,7 +91,10 @@ def binja_close_context(self, dctx):

In Binary Ninja, a dctx is a BinaryView (BV).
"""
dctx_id = ctypes.addressof(dctx.handle.contents)
if isinstance(dctx, BinaryView):
dctx_id = ctypes.addressof(dctx.handle.contents)
else:
dctx_id = str(id(dctx))

# fetch the LighthouseContext for the closing BNDB
try:
Expand All @@ -94,7 +115,7 @@ def binja_close_context(self, dctx):
#--------------------------------------------------------------------------
# UI Integration (Internal)
#--------------------------------------------------------------------------

#
# TODO / HACK / XXX / V35 / 2021: Some of Binja's UI elements (such as the
# terminal) do not get assigned a BV, even if there is only one open.
Expand All @@ -114,6 +135,10 @@ def binja_close_context(self, dctx):
# and re-factored at some point point.. sorry if it's hard to follow
#

# NOTE: The implementations below remain mostly the same, relying on the
# original Core/Director logic. The UI stability is now handled by the
# SidebarWidget implementation.

def _interactive_load_file(self, context):
dctx = disassembler.binja_get_bv_from_dock()
if not dctx:
Expand Down Expand Up @@ -207,6 +232,7 @@ def _open_coverage_overview(self, context):
if not dctx:
disassembler.warning("Lighthouse requires an open BNDB to open the overview.")
return
# NOTE: open_coverage_overview in core.py handles the rest
super(LighthouseBinja, self).open_coverage_overview(dctx)

def _stub(self, context):
Expand Down Expand Up @@ -249,6 +275,8 @@ def _install_open_coverage_overview(self):
action = self.ACTION_COVERAGE_OVERVIEW
UIAction.registerAction(action)
UIActionHandler.globalActions().bindAction(action, UIAction(self._open_coverage_overview))
# Note: This menu entry might be redundant if the sidebar button is present,
# but we keep it for consistency with other disassemblers.
Menu.mainMenu("Plugins").addAction(action, "Windows", 0)
logger.info("Installed the 'Open Coverage Overview' menu entry")

Expand All @@ -260,4 +288,4 @@ def _uninstall_load_batch(self):
def _uninstall_open_coverage_xref(self):
pass
def _uninstall_open_coverage_overview(self):
pass
pass
15 changes: 6 additions & 9 deletions plugins/lighthouse/integration/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,9 @@ def load(self):
self.palette = LighthousePalette()
self.palette.theme_changed(self.refresh_theme)

def create_coverage_overview(name, parent, dctx):
lctx = self.get_context(dctx, startup=False)
widget = disassembler.create_dockable_widget(parent, name)
overview = CoverageOverview(lctx, widget)
return widget
# NOTE: create_coverage_overview removed as it's now handled by the SidebarWidget creation callback

# the coverage overview widget
disassembler.register_dockable("Coverage Overview", create_coverage_overview)
# NOTE: disassembler.register_dockable removed here - now handled by Sidebar.addSidebarWidgetType

# install disassembler UI
self._install_ui()
Expand Down Expand Up @@ -190,7 +185,9 @@ def refresh_theme(self):
"""
for lctx in self.lighthouse_contexts.values():
lctx.director.refresh_theme()
lctx.coverage_overview.refresh_theme()
# Check if overview is initialized before calling refresh_theme
if lctx.coverage_overview:
lctx.coverage_overview.refresh_theme()
lctx.painter.force_repaint()

def open_coverage_overview(self, dctx=None):
Expand Down Expand Up @@ -408,4 +405,4 @@ def check_for_update(self):

# kick off the async update check
check_for_update(self.PLUGIN_VERSION, callback)
self._update_checked = True
self._update_checked = True
14 changes: 11 additions & 3 deletions plugins/lighthouse/ui/coverage_overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ def name(self):

@property
def visible(self):
# FIX: Replace .visible property access with the Qt standard .isVisible() method
if not self.widget:
return False
return self.widget.visible
return self.widget.isVisible()

def terminate(self):
"""
Expand Down Expand Up @@ -288,7 +289,13 @@ def eventFilter(self, source, event):
if disassembler.NAME == "BINJA":
lctx = self._target.lctx
core = lctx.core
core.binja_close_context(lctx.dctx)
# FIX: In SidebarWidget, the 'widget' is the sidebar itself (source),
# which is a SidebarWidget and has its own close/cleanup notifications.
# However, the core logic expects the dctx, not widget.
# We need to manually determine the dctx from the lctx before passing it.
if hasattr(lctx, 'dctx'):
core.binja_close_context(lctx.dctx)


# cleanup the UI / qt references for the CoverageOverview elements
self._target.terminate()
Expand Down Expand Up @@ -322,6 +329,7 @@ def eventFilter(self, source, event):

elif int(event.type()) == self.EventUpdateLater:

# FIX: Use the fixed 'visible' property (which calls isVisible())
if self._target.visible and self._first_hit:
self._first_hit = False

Expand Down Expand Up @@ -361,4 +369,4 @@ def eventFilter(self, source, event):
idaapi.set_dock_pos(self._target._title, "Structures", idaapi.DP_TAB)
debugger_docked = True

return False
return False
Loading