summaryrefslogtreecommitdiff
path: root/python/examples
diff options
context:
space:
mode:
Diffstat (limited to 'python/examples')
-rw-r--r--python/examples/bin_info.py38
-rw-r--r--python/examples/raw_binary_base_detection.py101
-rw-r--r--python/examples/typelibexplorer.py355
3 files changed, 443 insertions, 51 deletions
diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py
index bcf5adcd..b2fd194e 100644
--- a/python/examples/bin_info.py
+++ b/python/examples/bin_info.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2015-2024 Vector 35 Inc
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -19,17 +19,19 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
-import sys
import os
+import sys
+from glob import glob
-from binaryninja.log import log_warn, log_to_stdout
-import binaryninja.interaction as interaction
-from binaryninja.plugin import PluginCommand
-from binaryninja import load
+from binaryninja import LogLevel, PluginCommand, interaction, load, log, log_to_stdout, log_warn
-def get_bininfo(bv):
+def get_bininfo(bv, filename=None):
if bv is None:
+ if not (os.path.isfile(filename) and os.access(filename, os.R_OK)):
+ return("Cannot read {}\n".format(filename))
+ bv = load(filename, options={'analysis.mode': 'basic', 'analysis.linearSweep.autorun' : False})
+ else:
filename = ""
if len(sys.argv) > 1:
filename = sys.argv[1]
@@ -40,7 +42,7 @@ def get_bininfo(bv):
sys.exit(1)
bv = load(filename)
- log_to_stdout(True)
+ log_to_stdout(LogLevel.InfoLog)
contents = "## %s ##\n" % os.path.basename(bv.file.filename)
contents += "- START: 0x%x\n\n" % bv.start
@@ -62,6 +64,13 @@ def get_bininfo(bv):
length = bv.strings[i].length
string = bv.strings[i].value
contents += "| 0x%x |%d | %s |\n" % (start, length, string)
+
+ # Note that we need to close BV file handles that we opened to prevent a
+ # memory leak due to a circular reference between BinaryViews and the
+ # FileMetadata that backs them
+
+ if filename != "":
+ bv.file.close()
return contents
@@ -70,6 +79,15 @@ def display_bininfo(bv):
if __name__ == "__main__":
- print(get_bininfo(None))
+ if len(sys.argv) == 1:
+ filename = interaction.get_open_filename_input("Filename:")
+ if filename is None:
+ log.log_warn("No file specified")
+ else:
+ print(get_bininfo(None, filename=filename))
+ else:
+ for pattern in sys.argv[1:]:
+ for filename in glob(pattern):
+ print(get_bininfo(None, filename=filename))
else:
- PluginCommand.register("Binary Info", "Display basic info about the binary", display_bininfo)
+ PluginCommand.register("Binary Info", "Display basic info about the binary using minimal analysis modes", display_bininfo)
diff --git a/python/examples/raw_binary_base_detection.py b/python/examples/raw_binary_base_detection.py
new file mode 100644
index 00000000..76268fbe
--- /dev/null
+++ b/python/examples/raw_binary_base_detection.py
@@ -0,0 +1,101 @@
+# Copyright (c) 2015-2024 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+"""Headless script for demonstrating Binary Ninja automated base address detection for
+raw position-dependent firmware binaries
+"""
+
+import argparse
+import json
+from os import walk, path
+from binaryninja import BaseAddressDetection, log_to_stderr, LogLevel, log_info, log_error
+
+
+def _get_directory_listing(_path: str) -> list[str]:
+ if path.isfile(_path):
+ return [_path]
+
+ if not path.isdir(_path):
+ raise FileNotFoundError(f"Path '{_path}' is not a file or directory")
+
+ files = []
+ for dirpath, _, filenames in walk(_path):
+ for filename in filenames:
+ files.append(path.join(dirpath, filename))
+ return files
+
+
+def _parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(description="detect base address of position-dependent raw firmware binaries")
+ parser.add_argument("path", help="path to the position-dependent raw firmware binary or directory")
+ parser.add_argument("--debug", action="store_true", help="enable debug logging")
+ parser.add_argument("--reasons", action="store_true", help="show reasons for base address selection")
+ parser.add_argument("--analysis", type=str, help="analysis level", default="basic")
+ parser.add_argument("--arch", type=str, default="", help="architecture of the binary")
+ return parser.parse_args()
+
+
+def _setup_logger(debug: bool) -> None:
+ if debug:
+ log_to_stderr(LogLevel.DebugLog)
+ else:
+ log_to_stderr(LogLevel.InfoLog)
+
+
+def main() -> None:
+ """Run the program"""
+ args = _parse_args()
+ _setup_logger(args.debug)
+
+ files = _get_directory_listing(args.path)
+ for _file in files:
+ log_info(f"Running base address detection analysis on '{_file}'...")
+ bad = BaseAddressDetection(_file)
+ if not bad.detect_base_address(analysis=args.analysis, arch=args.arch):
+ log_error("Base address detection analysis failed")
+ continue
+
+ json_dict = dict()
+ json_dict["filename"] = path.basename(_file)
+ json_dict["preferred_candidate"] = dict()
+ json_dict["preferred_candidate"]["address"] = f"0x{bad.preferred_base_address:x}"
+ json_dict["preferred_candidate"]["confidence"] = bad.confidence
+ json_dict["aborted"] = bad.aborted
+ json_dict["last_tested"] = f"0x{bad.last_tested_base_address:x}"
+ json_dict["candidates"] = dict()
+ for baseaddr, score in bad.scores:
+ json_dict["candidates"][f"0x{baseaddr:x}"] = dict()
+ json_dict["candidates"][f"0x{baseaddr:x}"]["score"] = score
+ json_dict["candidates"][f"0x{baseaddr:x}"]["function hits"] = bad.get_function_hits(baseaddr)
+ json_dict["candidates"][f"0x{baseaddr:x}"]["string hits"] = bad.get_string_hits(baseaddr)
+ json_dict["candidates"][f"0x{baseaddr:x}"]["data hits"] = bad.get_data_hits(baseaddr)
+ if args.reasons:
+ json_dict["candidates"][f"0x{baseaddr:x}"]["reasons"] = dict()
+ for reason in bad.get_reasons(baseaddr):
+ json_dict["candidates"][f"0x{baseaddr:x}"]["reasons"][f"0x{reason.pointer:x}"] = {
+ "poi_offset": f"0x{reason.offset:x}",
+ "poi_type": reason.type,
+ }
+
+ print(json.dumps(json_dict, indent=4))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/python/examples/typelibexplorer.py b/python/examples/typelibexplorer.py
index 8d1d863b..28899003 100644
--- a/python/examples/typelibexplorer.py
+++ b/python/examples/typelibexplorer.py
@@ -21,21 +21,45 @@
# This is an example UI plugin which demonstrates how to add sidebar widgets to Binary Ninja.
# See .../api/ui/sidebar.h for interface details.
-from binaryninjaui import SidebarWidget, SidebarWidgetType, Sidebar, UIActionHandler
-from PySide6.QtCore import Qt, QRectF
-from PySide6.QtWidgets import QVBoxLayout, QLabel, QComboBox, QTableWidget, QTableWidgetItem, QTextEdit, QApplication, QLineEdit, QHBoxLayout
-from PySide6.QtGui import QImage, QPainter, QFont, QColor
-from binaryninja import Platform, BinaryView, TypeLibrary, log
+from binaryninjaui import SidebarWidget, SidebarWidgetType, Sidebar, UIActionHandler, FilterEdit, FilteredView, \
+ FilterTarget, DockableTabWidget, GlobalAreaTabStyle, DockableTabCollection, View, ViewType, ViewFrame, ViewPane
+from PySide6.QtCore import Qt, QRectF, QModelIndex
+from PySide6.QtWidgets import QVBoxLayout, QLabel, QComboBox, QTableWidget, QTableWidgetItem, QTextEdit, QApplication, \
+ QLineEdit, QHBoxLayout, QWidget, QAbstractItemView, QFrame
+from PySide6.QtGui import QImage, QPainter, QFont, QColor, QPalette
+from binaryninja import Platform, BinaryView, TypeLibrary, log, QualifiedName
from typing import Optional
from re import search
instance_id = 0
-class TypelibTypeTableWidget(QTableWidget):
+g_typelib_explorer_viewtype = None
+
+
+class TypelibTypeTableWidget(QTableWidget, FilterTarget):
def __init__(self, parent):
QTableWidget.__init__(self, parent)
+ FilterTarget.__init__(self)
self.typelib = None
+ def setFilter(self, filter_text: str):
+ self.setFilterRegExp(filter_text)
+
+ def scrollToFirstItem(self):
+ self.scrollToTop()
+
+ def scrollToCurrentItem(self):
+ self.scrollTo(self.currentIndex())
+
+ def selectFirstItem(self):
+ self.setCurrentIndex(self.model().index(0, 0, QModelIndex()))
+
+ def activateFirstItem(self):
+ self.setCurrentIndex(self.model().index(0, 0, QModelIndex()))
+
+ def closeFilter(self):
+ self.setFocus(Qt.OtherFocusReason)
+
def setFilterRegExp(self, pattern):
if pattern == "":
for i in range(self.rowCount()):
@@ -65,16 +89,35 @@ class TypelibTypeTableWidget(QTableWidget):
if data is None:
self.setItem(i, 2, QTableWidgetItem(str(type)))
else:
- lines = type.get_lines(data, name)
+ lines = type.get_lines(data, str(name))
self.setItem(i, 2, QTableWidgetItem("".join([str(l) for l in lines])))
-class TypelibObjectTableWidget(QTableWidget):
+class TypelibObjectTableWidget(QTableWidget, FilterTarget):
def __init__(self, parent):
QTableWidget.__init__(self, parent)
+ FilterTarget.__init__(self)
self.typelib = None
self.ordinals = None
+ def setFilter(self, filterText):
+ self.setFilterRegExp(filterText)
+
+ def scrollToFirstItem(self):
+ self.scrollToTop()
+
+ def scrollToCurrentItem(self):
+ self.scrollTo(self.currentIndex())
+
+ def selectFirstItem(self):
+ self.setCurrentIndex(self.model().index(0, 0, QModelIndex()))
+
+ def activateFirstItem(self):
+ self.setCurrentIndex(self.model().index(0, 0, QModelIndex()))
+
+ def closeFilter(self):
+ self.setFocus(Qt.OtherFocusReason)
+
def lookup_ordinal(self, name: str) -> str:
assert self.typelib is not None
if md := self.typelib.query_metadata("ordinals"):
@@ -137,19 +180,33 @@ class TypelibObjectTableWidget(QTableWidget):
# Sidebar widgets must derive from SidebarWidget, not QWidget. SidebarWidget is a QWidget but
# provides callbacks for sidebar events, and must be created with a title.
-class TypelibExplorerWidget(SidebarWidget):
+class TypelibExplorerWidget(SidebarWidget, FilterTarget):
def __init__(self, name, frame, data):
- global instance_id
SidebarWidget.__init__(self, name)
+ FilterTarget.__init__(self)
+ self.setBackgroundRole(QPalette.ColorRole.Window)
+ self.setAutoFillBackground(True)
+ self.setObjectName("TypelibExplorerWidget")
+
self.actionHandler = UIActionHandler()
self.actionHandler.setupActionHandler(self)
- layout = QVBoxLayout()
+
self.previous_typelib_index = -1
self.previous_platform_index = -1
+ self.orientation = None
self.data = None
self.platform = None
self.typelib = None
+ self.setLayout(QVBoxLayout())
+ self.layout().setContentsMargins(0, 0, 0, 0)
+ self.primary_wrapper = QWidget()
+ self.primary_wrapper.setLayout(QVBoxLayout())
+ self.primary_wrapper.setBackgroundRole(QPalette.ColorRole.Window)
+ self.primary_wrapper.setAutoFillBackground(True)
+
+ self.layout().addWidget(self.primary_wrapper)
+
# platform selector
self.platform_selector = QComboBox(self)
self.platform_selector.setEditable(True)
@@ -158,69 +215,151 @@ class TypelibExplorerWidget(SidebarWidget):
self.platform_selector.addItem(platform.name, platform)
if data is not None:
self.platform_selector.setCurrentIndex(self.platform_selector.findText(data.platform.name))
- layout.addWidget(self.platform_selector)
# typelib selector
self.typelib_selector = QComboBox(self)
self.typelib_selector.setEditable(True)
self.typelib_selector.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
- layout.addWidget(self.typelib_selector)
# guid
- layout.addWidget(QLabel("GUID", self))
self.guid = QLineEdit(self)
self.guid.setReadOnly(True)
- layout.addWidget(self.guid)
# alternate names
- layout.addWidget(QLabel("Alternate Names", self))
self.alternate_names = QTextEdit(self)
self.alternate_names.setReadOnly(True)
- self.alternate_names.setMaximumHeight(64)
- layout.addWidget(self.alternate_names)
# title and table of objects
+ self.object_table = TypelibObjectTableWidget(self)
+ self.object_table.verticalHeader().hide()
+ self.object_table.horizontalHeader().setStretchLastSection(True)
+ self.object_table.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.object_label = QLabel("Objects", self)
- self.object_filter = QLineEdit(self)
+ self.object_filter = FilterEdit(self.object_table)
self.object_filter.setPlaceholderText("Filter (regex)...")
- object_layout = QHBoxLayout()
- object_layout.addWidget(self.object_label)
- object_layout.addWidget(self.object_filter)
- layout.addLayout(object_layout)
+ self.object_layout = QHBoxLayout()
+ self.object_layout.addWidget(self.object_label)
+ self.object_layout.addWidget(self.object_filter)
+ self.object_layout_widget = QWidget()
+ self.object_layout_widget.setLayout(self.object_layout)
- self.object_table = TypelibObjectTableWidget(self)
- self.object_table.verticalHeader().hide()
self.object_table.setSortingEnabled(True)
- layout.addWidget(self.object_table)
+ self.object_filter.textChanged.connect(self.object_table.setFilter)
# title and table of types
self.table_label = QLabel("Types", self)
- self.types_filter = QLineEdit(self)
- self.types_filter.setPlaceholderText("Filter (regex)...")
- type_layout = QHBoxLayout()
- type_layout.addWidget(self.table_label)
- type_layout.addWidget(self.types_filter)
- layout.addLayout(type_layout)
self.type_table = TypelibTypeTableWidget(self)
+ self.type_table.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.type_table.verticalHeader().hide()
self.type_table.setSortingEnabled(True)
- layout.addWidget(self.type_table)
- self.setLayout(layout)
-
- # initialize
- instance_id += 1
+ self.type_table.horizontalHeader().setStretchLastSection(True)
+ self.types_filter = FilterEdit(self.type_table)
+ self.types_filter.setPlaceholderText("Filter (regex)...")
+ self.types_filter.textChanged.connect(self.type_table.setFilter)
+ self.type_layout = QHBoxLayout()
+ self.type_layout.addWidget(self.table_label)
+ self.type_layout.addWidget(self.types_filter)
+ self.type_layout_widget = QWidget()
+ self.type_layout_widget.setLayout(self.type_layout)
self.typelib_selector.currentIndexChanged.connect(self.on_typelib_selector_changed)
self.platform_selector.currentIndexChanged.connect(self.on_platform_selector_changed)
self.object_filter.textChanged.connect(self.object_table.setFilterRegExp)
- self.types_filter.textChanged.connect(self.type_table.setFilterRegExp)
- self.initialize_data(data)
+
+ # Whenever we're in Horizontal mode, we hide the two table-specific filters and show a singular one to the right
+ # of tabs.
+ # We'll set the filter target to this class, and manually forward the signals to the appropriate FilterEdit.
+ # This approach ensures that whenever we transition back to Vertical mode, the FilterEdits maintain their
+ # respective contents, and allows us to easily swap out contents when tab changes.
+ self.shared_filter = FilterEdit(self)
+ self.shared_filter.setPlaceholderText("Filter (regex)...")
+ self.shared_filter.textChanged.connect(self.setFilter)
+ shared_filter_container = QWidget()
+ shared_filter_layout = QHBoxLayout()
+ shared_filter_layout.setContentsMargins(0, 2, 0, 2)
+ shared_filter_layout.addWidget(self.shared_filter)
+ shared_filter_container.setLayout(shared_filter_layout)
+
+ # We *must* hold a reference to the DockableTabCollection as long as this widget is alive
+ self.tab_collection = DockableTabCollection()
+ self.horizontal_tabs = DockableTabWidget(self.tab_collection)
+ self.horizontal_tabs.setCornerWidget(shared_filter_container, Qt.TopRightCorner, True)
+ # Whenever we swap tabs, we want to pull the filter text from the currently active tab's FilterEdit
+ self.horizontal_tabs.currentChanged.connect(self.resetSharedFilterForTabIdx)
+
+ self.horizontal_tabs.setTabStyle(GlobalAreaTabStyle())
+
+ # We're setting up wrapper widgets for the contents of each tab, as we want to set up our tab system
+ # before we know if we actually want to put the widgets in it.
+ self.tab_object_container = QWidget()
+ self.tab_object_layout = QVBoxLayout()
+ self.tab_object_layout.setContentsMargins(0, 0, 0, 0)
+ self.tab_object_container.setLayout(self.tab_object_layout)
+ self.horizontal_tabs.addTab(self.tab_object_container, "Objects")
+ self.tab_type_container = QWidget()
+ self.tab_type_layout = QVBoxLayout()
+ self.tab_type_layout.setContentsMargins(0, 0, 0, 0)
+ self.tab_type_container.setLayout(self.tab_type_layout)
+ self.horizontal_tabs.addTab(self.tab_type_container, "Types")
+
+ self.horizontal_tabs.setCanSplit(False)
+ self.horizontal_tabs.setCanCloseTab(0, False)
+ self.horizontal_tabs.setCanCloseTab(1, False)
+ self.horizontal_tabs.setCanCreateNewWindow(False)
+
+ # initialize
+ global instance_id
+ instance_id += 1
+ self.initialize_data(self.data)
+
+ def resetSharedFilterForTabIdx(self, idx):
+ if idx == 0:
+ self.shared_filter.setText(self.object_filter.text())
+ else:
+ self.shared_filter.setText(self.types_filter.text())
+
+ def setFilter(self, filterText):
+ if self.horizontal_tabs.currentIndex() == 0:
+ self.object_filter.setText(filterText)
+ else:
+ self.types_filter.setText(filterText)
+
+ def scrollToFirstItem(self):
+ if self.horizontal_tabs.currentIndex() == 0:
+ self.object_table.scrollToFirstItem()
+ else:
+ self.type_table.scrollToFirstItem()
+
+ def scrollToCurrentItem(self):
+ if self.horizontal_tabs.currentIndex() == 0:
+ self.object_table.scrollToCurrentItem()
+ else:
+ self.type_table.scrollToCurrentItem()
+
+ def selectFirstItem(self):
+ if self.horizontal_tabs.currentIndex() == 0:
+ self.object_table.selectFirstItem()
+ else:
+ self.type_table.selectFirstItem()
+
+ def activateFirstItem(self):
+ if self.horizontal_tabs.currentIndex() == 0:
+ self.object_table.activateFirstItem()
+ else:
+ self.type_table.activateFirstItem()
+
+ def closeFilter(self):
+ if self.horizontal_tabs.currentIndex() == 0:
+ self.object_table.closeFilter()
+ else:
+ self.type_table.closeFilter()
def setDisabled(self, disabled):
self.typelib_selector.setDisabled(disabled)
self.object_table.setDisabled(disabled)
self.type_table.setDisabled(disabled)
- def initialize_data(self, data: Optional[BinaryView], platform: Optional[Platform]=None, typelib: Optional[TypeLibrary]=None):
+ def initialize_data(self, data: Optional[BinaryView], platform: Optional[Platform] = None,
+ typelib: Optional[TypeLibrary] = None):
# first ensure we have valid platform and typelib
data_changed = data != self.data
platform_changed = platform != self.platform
@@ -319,12 +458,133 @@ class TypelibExplorerWidget(SidebarWidget):
self.initialize_data(data, data.platform)
def notifyViewChanged(self, view_frame):
- data = view_frame.getCurrentViewInterface().getData() if view_frame is not None else None
+ data = None
+ if view_frame is not None:
+ data = view_frame.getCurrentViewInterface().getData()
self.on_binaryview_changed(data)
def contextMenuEvent(self, event):
self.m_contextMenuManager.show(self.m_menu, self.actionHandler)
+ def updateLayout(self):
+ self.layout().removeWidget(self.primary_wrapper)
+
+ # Deparent all of our top level widgets before deleting the primary_wrapper so that they don't get deleted,
+ # and we can reparent them to a new wrapper.
+ self.horizontal_tabs.setParent(None)
+ self.platform_selector.setParent(None)
+ self.typelib_selector.setParent(None)
+ self.guid.setParent(None)
+ self.alternate_names.setParent(None)
+ self.object_layout_widget.setParent(None)
+ self.object_table.setParent(None)
+ self.type_layout_widget.setParent(None)
+ self.type_table.setParent(None)
+
+ # Recreate our primary wrapper so we can use a new layout.
+ self.primary_wrapper.setParent(None)
+ self.primary_wrapper.deleteLater()
+ self.primary_wrapper = QWidget()
+ self.primary_wrapper.setBackgroundRole(QPalette.ColorRole.Window)
+ self.primary_wrapper.setAutoFillBackground(True)
+
+ if self.orientation == Qt.Orientation.Vertical:
+ self.alternate_names.setMaximumHeight(64)
+ layout = QVBoxLayout()
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.addWidget(self.platform_selector)
+ layout.addWidget(self.typelib_selector)
+ layout.addWidget(QLabel("GUID", self))
+ layout.addWidget(self.guid)
+ layout.addWidget(QLabel("Alternate Names", self))
+ layout.addWidget(self.alternate_names)
+ layout.addWidget(self.object_layout_widget)
+ layout.addWidget(self.object_table)
+ layout.addWidget(self.type_layout_widget)
+ layout.addWidget(self.type_table)
+ self.primary_wrapper.setLayout(layout)
+ else: # Horizontal
+ self.alternate_names.setMaximumHeight(2048)
+ layout = QHBoxLayout()
+ left_hand_layout = QVBoxLayout()
+ left_hand_layout.setContentsMargins(0, 0, 0, 0)
+ left_hand_layout.addWidget(self.platform_selector)
+ left_hand_layout.addWidget(self.typelib_selector)
+ left_hand_layout.addWidget(QLabel("GUID", self))
+ left_hand_layout.addWidget(self.guid)
+ left_hand_layout.addWidget(QLabel("Alternate Names", self))
+ left_hand_layout.addWidget(self.alternate_names, 1)
+ layout.addLayout(left_hand_layout)
+ right_hand_layout = QVBoxLayout()
+ right_hand_layout.setContentsMargins(0, 0, 0, 0)
+ right_hand_layout.addWidget(self.horizontal_tabs)
+ active_tab = self.horizontal_tabs.currentIndex()
+ filter_text = self.object_filter.text() if active_tab == 0 else self.types_filter.text()
+ self.shared_filter.setText(filter_text)
+ self.tab_object_layout.addWidget(self.object_table)
+ self.tab_type_layout.addWidget(self.type_table)
+ layout.addLayout(right_hand_layout)
+ self.primary_wrapper.setLayout(layout)
+
+ self.layout().addWidget(self.primary_wrapper)
+
+ def setPrimaryOrientation(self, orientation):
+ if orientation == self.orientation:
+ return
+ self.orientation = orientation
+ self.updateLayout()
+
+
+class TypelibExplorerView(QFrame, View):
+ def __init__(self, parent, data):
+ self.data = data
+ QFrame.__init__(self)
+ View.__init__(self)
+ View.setBinaryDataNavigable(self, False)
+ self.setupView(self)
+
+ self.setParent(parent)
+ self.setObjectName("TypelibExplorerView")
+
+ self.typelib_explorer_widget = TypelibExplorerWidget("Typelib Explorer", self, data)
+ self.typelib_explorer_widget.initialize_data(data, data.platform)
+ self.typelib_explorer_widget.setPrimaryOrientation(Qt.Orientation.Vertical)
+ self.setLayout(QVBoxLayout())
+ self.layout().addWidget(self.typelib_explorer_widget)
+
+ def getData(self):
+ try:
+ return self.data
+ except AttributeError:
+ return None
+
+ def getCurrentOffset(self):
+ return 0
+
+ def navigate(self, offset):
+ return False
+
+ def resizeEvent(self, event) -> None:
+ self.typelib_explorer_widget.setPrimaryOrientation(Qt.Orientation.Horizontal if self.width() > self.height() else Qt.Orientation.Vertical)
+ QFrame.resizeEvent(self, event)
+
+
+class TypelibExplorerViewType(ViewType):
+ def __init__(self):
+ ViewType.__init__(self, "Typelib Explorer", "Typelib Explorer")
+
+ def getPriority(self, data, filename):
+ return 1
+
+ def create(self, data: 'BinaryView', view_frame: ViewFrame):
+ return TypelibExplorerView(view_frame, data)
+
+ @classmethod
+ def init(cls):
+ global g_typelib_explorer_viewtype
+ g_typelib_explorer_viewtype = cls()
+ ViewType.registerViewType(g_typelib_explorer_viewtype)
+
class TypelibExplorerWidgetType(SidebarWidgetType):
def __init__(self):
@@ -350,7 +610,20 @@ class TypelibExplorerWidgetType(SidebarWidgetType):
# widget is visible and the BinaryView becomes active.
return TypelibExplorerWidget("Typelib Explorer", frame, data)
+ def canUseAsPane(self, split_pane_widget: 'binaryninjaui.SplitPaneWidget', data: 'BinaryView'):
+ return True
+
+ def createPane(self, split_pane_widget: 'binaryninjaui.SplitPaneWidget', data: 'BinaryView') -> 'binaryninjaui.Pane':
+ # We've already registered the View Type, so we request that here by name.
+ _type = "Typelib Explorer:" + data.view_type
+ frame = ViewFrame(split_pane_widget, split_pane_widget.fileContext(), _type)
+ if not frame.getCurrentBinaryView():
+ del frame
+ return None
+ return ViewPane(frame)
+
# Register the sidebar widget type with Binary Ninja. This will make it appear as an icon in the
# sidebar and the `createWidget` method will be called when a widget is required.
Sidebar.addSidebarWidgetType(TypelibExplorerWidgetType())
+TypelibExplorerViewType.init()