From 61b4bb24e06aa955484293d35fa926c07887544b Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Mon, 7 Jun 2021 09:59:54 -0400 Subject: Add type hints to basicblock.py, lowlevelil.py, architecture.py --- python/examples/bin_info.py | 8 +- python/examples/mappedview.py | 21 +- python/examples/nds.py | 13 +- python/examples/nes.py | 42 ++-- python/examples/nsf.py | 24 +- python/examples/snippets/__init__.py | 444 +++++++++++++++++++++++++++++++++++ 6 files changed, 497 insertions(+), 55 deletions(-) create mode 100644 python/examples/snippets/__init__.py (limited to 'python/examples') diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py index 1af59a31..850a97b8 100644 --- a/python/examples/bin_info.py +++ b/python/examples/bin_info.py @@ -28,9 +28,6 @@ import binaryninja.interaction as interaction from binaryninja.plugin import PluginCommand from binaryninja import Settings -# 2-3 compatibility -from binaryninja import range - def get_bininfo(bv): if bv is None: @@ -54,8 +51,9 @@ def get_bininfo(bv): contents += "| Start | Name |\n" contents += "|------:|:-------|\n" - for i in range(min(10, len(bv.functions))): - contents += "| 0x%x | %s |\n" % (bv.functions[i].start, bv.functions[i].symbol.full_name) + functions = list(bv.functions) + for i in range(min(10, len(functions))): + contents += "| 0x%x | %s |\n" % (functions[i].start, functions[i].symbol.full_name) contents += "### First 10 Strings ###\n" contents += "| Start | Length | String |\n" diff --git a/python/examples/mappedview.py b/python/examples/mappedview.py index 6d060235..bf7f2d11 100644 --- a/python/examples/mappedview.py +++ b/python/examples/mappedview.py @@ -43,8 +43,8 @@ class MappedView(BinaryView): def __init__(self, data): BinaryView.__init__(self, parent_view = data, file_metadata = data.file) - @classmethod - def is_valid_for_data(cls, data): + @staticmethod + def is_valid_for_data(data): # Insert code that looks for a magic identifier and return True if this BinaryViewType can handle/parse the binary return True @@ -64,12 +64,14 @@ class MappedView(BinaryView): # Optionally, perform light-weight parsing of the 'Raw' BinaryView to extract required information for load settings generation. # This allows finer control of the settings provided as well as their default values. # For example, the view.relocatable property could be used to control the read-only attribute of "loader.imageBase" - view = cls.registered_view_type.parse(data) - + registered_view = cls.registered_view_type + assert registered_view is not None + view = registered_view.parse(data) + assert view is not None # Populate settings container with default load settings # Note: `get_default_load_settings_for_data` automatically tries to parse the input if the `data` BinaryViewType name does not match the # cls BinaryViewType name. In this case a parsed view is already being passed. - load_settings = cls.registered_view_type.get_default_load_settings_for_data(view) + load_settings = registered_view.get_default_load_settings_for_data(view) # Specify default load settings that can be overridden (from the UI). overrides = ["loader.architecture", "loader.platform", "loader.entryPointOffset", "loader.imageBase", "loader.segments", "loader.sections"] @@ -113,8 +115,9 @@ class MappedView(BinaryView): load_settings = self.get_load_settings(self.name) if load_settings is None: if self.parse_only is True: - self.arch = Architecture['x86'] - self.platform = Architecture['x86'].standalone_platform + self.arch = Architecture['x86'] # type: ignore + self.platform = Architecture['x86'].standalone_platform # type: ignore + assert self.parent_view is not None self.add_auto_segment(0, len(self.parent_view), 0, len(self.parent_view), SegmentFlag.SegmentReadable) return True else: @@ -123,8 +126,8 @@ class MappedView(BinaryView): load_settings = self.__class__.get_load_settings_for_data(self.parent_view) arch = load_settings.get_string("loader.architecture", self) - self.arch = Architecture[arch] - self.platform = Architecture[arch].standalone_platform + self.arch = Architecture[arch] # type: ignore + self.platform = Architecture[arch].standalone_platform # type: ignore self.load_address = load_settings.get_integer("loader.imageBase", self) self.add_auto_segment(self.load_address, len(self.parent_view), 0, len(self.parent_view), SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) if load_settings.contains("loader.entryPointOffset"): diff --git a/python/examples/nds.py b/python/examples/nds.py index 4cf7c24d..18001d50 100644 --- a/python/examples/nds.py +++ b/python/examples/nds.py @@ -27,9 +27,6 @@ from binaryninja.log import log_error import struct import traceback -# 2-3 compatibility -from binaryninja import range - def crc16(data): crc = 0xffff @@ -48,8 +45,8 @@ class DSView(BinaryView): BinaryView.__init__(self, file_metadata = data.file, parent_view = data) self.raw = data - @classmethod - def is_valid_for_data(self, data): + @staticmethod + def is_valid_for_data(data): hdr = data.read(0, 0x160) if len(hdr) < 0x160: return False @@ -60,7 +57,7 @@ class DSView(BinaryView): return True def init_common(self): - self.platform = Architecture["armv7"].standalone_platform + self.platform = Architecture["armv7"].standalone_platform # type: ignore self.hdr = self.raw.read(0, 0x160) def init_arm9(self): @@ -72,7 +69,7 @@ class DSView(BinaryView): self.arm9_size = struct.unpack(" 0: + oldSelection = self.files.filePath(old.indexes()[0]) + if not QFileInfo(oldSelection).isDir() and self.snippetChanged(): + question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?")) + if question != QMessageBox.StandardButton.Yes: + self.resetting = True + self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) + return False + + self.currentFile = newSelection + self.loadSnippet() + + def loadSnippet(self): + self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName()) + (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) + self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("") + self.keySequenceEdit.setKeySequence(snippetKeys) if snippetKeys else self.keySequenceEdit.setKeySequence(QKeySequence("")) + self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("") + self.readOnly(False) + + def newFileDialog(self): + (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: ")) + if ok and snippetName: + if not snippetName.endswith(".py"): + snippetName += ".py" + index = self.tree.selectionModel().currentIndex() + selection = self.files.filePath(index) + if QFileInfo(selection).isDir(): + path = os.path.join(selection, snippetName) + else: + path = os.path.join(snippetPath, snippetName) + self.readOnly(False) + open(path, "w").close() + self.tree.setCurrentIndex(self.files.index(path)) + log_debug("Snippet %s created." % snippetName) + + def readOnly(self, flag): + self.keySequenceEdit.setEnabled(not flag) + self.snippetDescription.setReadOnly(flag) + self.edit.setReadOnly(flag) + if flag: + self.snippetDescription.setDisabled(True) + self.edit.setDisabled(True) + else: + self.snippetDescription.setEnabled(True) + self.edit.setEnabled(True) + + def deleteSnippet(self): + selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row + snippetName = self.files.fileName(selection) + question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName) + if (question == QMessageBox.StandardButton.Yes): + log_debug("Deleting snippet %s." % snippetName) + self.clearSelection() + self.files.remove(selection) + self.registerAllSnippets() + + def snippetChanged(self): + if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()): + return False + (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) + if snippetKeys == None and not self.keySequenceEdit.keySequence().isEmpty(): + return True + if snippetKeys != None and snippetKeys != self.keySequenceEdit.keySequence().toString(): + return True + return self.edit.toPlainText() != snippetCode or \ + self.snippetDescription.text() != snippetDescription + + def save(self): + log_debug("Saving snippet %s" % self.currentFile) + outputSnippet = codecs.open(self.currentFile, "w", "utf-8") + outputSnippet.write("#" + self.snippetDescription.text() + "\n") + outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") + outputSnippet.write(self.edit.toPlainText()) + outputSnippet.close() + self.registerAllSnippets() + + def run(self): + if self.context == None: + log_warn("Cannot run snippets outside of the UI at this time.") + return + if self.snippetChanged(): + question = QMessageBox.question(self, self.tr("Confirm"), self.tr("You have unsaved changes, must save first. Save?")) + if (question == QMessageBox.StandardButton.No): + return + else: + self.save() + actionText = actionFromSnippet(self.currentFile, self.snippetDescription.text()) + UIActionHandler.globalActions().executeAction(actionText, self.context) + + log_debug("Saving snippet %s" % self.currentFile) + outputSnippet = codecs.open(self.currentFile, "w", "utf-8") + outputSnippet.write("#" + self.snippetDescription.text() + "\n") + outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") + outputSnippet.write(self.edit.toPlainText()) + outputSnippet.close() + self.registerAllSnippets() + + def clearHotkey(self): + self.keySequenceEdit.clear() + + +def launchPlugin(context): + snippets = Snippets(context) + snippets.exec_() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + snippets = Snippets(None) + snippets.show() + sys.exit(app.exec_()) +else: + Snippets.registerAllSnippets() + UIAction.registerAction("Snippets\\Snippet Editor...") + UIActionHandler.globalActions().bindAction("Snippets\\Snippet Editor...", UIAction(launchPlugin)) + Menu.mainMenu("Tools").addAction("Snippets\\Snippet Editor...", "Snippet") -- cgit v1.3.1