diff options
Diffstat (limited to 'python/examples')
| -rw-r--r-- | python/examples/bin_info.py | 38 | ||||
| -rw-r--r-- | python/examples/raw_binary_base_detection.py | 101 | ||||
| -rw-r--r-- | python/examples/typelibexplorer.py | 355 |
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() |
