From 141bcd61bb0564cbb18ec83bf034e0c65be59c0d Mon Sep 17 00:00:00 2001 From: Arnaud Fontaine <151835536+af-airbus@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:33:22 +0200 Subject: [PATCH] IDA 9.0 compatibility --- python/idabincat/analyzer_conf.py | 36 +++++++++------------ python/idabincat/bcplugin.py | 40 +++++++++++------------- python/idabincat/hexview/hexview.py | 28 ++++++++++++++--- python/idabincat/hexview/hexview.ui | 38 ---------------------- python/idabincat/hexview/hexview_auto.py | 33 ------------------- python/idabincat/npkgen.py | 5 +-- 6 files changed, 61 insertions(+), 119 deletions(-) delete mode 100644 python/idabincat/hexview/hexview.ui delete mode 100644 python/idabincat/hexview/hexview_auto.py diff --git a/python/idabincat/analyzer_conf.py b/python/idabincat/analyzer_conf.py index a33b6325..2658f298 100644 --- a/python/idabincat/analyzer_conf.py +++ b/python/idabincat/analyzer_conf.py @@ -34,8 +34,8 @@ import idautils import ida_segment import ida_kernwin +import ida_ida import idabincat.netnode -from builtins import bytes from idabincat.plugin_options import PluginOptions # Python 2/3 compat if sys.version_info > (2, 8): @@ -86,8 +86,7 @@ class ConfigHelpers(object): @staticmethod def get_file_type(): - ida_db_info_structure = idaapi.get_inf_structure() - f_type = ida_db_info_structure.filetype + f_type = ida_ida.inf_get_filetype() if f_type in ConfigHelpers.ftypes: return ConfigHelpers.ftypes[f_type] else: @@ -136,17 +135,15 @@ def guess_file_path(): @staticmethod def get_memory_model(): - ida_db_info_structure = idaapi.get_inf_structure() - compiler_info = ida_db_info_structure.cc - if compiler_info.cm & idaapi.C_PC_FLAT == idaapi.C_PC_FLAT: + cm = ida_ida.inf_get_cc_cm() + if cm & idaapi.C_PC_FLAT == idaapi.C_PC_FLAT: return "flat" else: return "segmented" @staticmethod def get_call_convention(): - ida_db_info_structure = idaapi.get_inf_structure() - compiler_info = ida_db_info_structure.cc + cm = ida_ida.inf_get_cc_cm() cc = { idaapi.CM_CC_INVALID: "invalid", idaapi.CM_CC_UNKNOWN: "unknown", @@ -157,7 +154,7 @@ def get_call_convention(): idaapi.CM_CC_PASCAL: "pascal", idaapi.CM_CC_FASTCALL: "fastcall", idaapi.CM_CC_THISCALL: "thiscall", - }[compiler_info.cm & idaapi.CM_CC_MASK] + }[cm & idaapi.CM_CC_MASK] # XXX if ConfigHelpers.get_arch().startswith("powerpc") and ida_db_info_structure.abiname == "sysv": return "svr" @@ -183,17 +180,15 @@ def get_bitness(ea): @staticmethod def get_endianness(): - ida_db_info_structure = idaapi.get_inf_structure() - return "big" if ida_db_info_structure.is_be() else "little" + return "big" if ida_ida.inf_is_be() else "little" @staticmethod def get_stack_width(): - ida_db_info_structure = idaapi.get_inf_structure() - if ida_db_info_structure.is_64bit(): + if ida_ida.inf_is_64bit(): return 8*8 else: - if ida_db_info_structure.is_32bit(): + if ida_ida.inf_is_32bit_exactly(): return 4*8 else: return 2*8 @@ -405,20 +400,19 @@ def get_initial_mem(arch=None): @staticmethod def get_arch(): - info = idaapi.get_inf_structure() - procname = info.procname.lower() + procname = ida_ida.inf_get_procname().lower() if procname == "metapc": - if info.is_64bit(): + if ida_ida.inf_is_64bit(): return "x64" else: return "x86" if procname == "ppc": - if info.is_64bit(): + if ida_ida.inf_is_64bit(): return "powerpc64" else: return "powerpc" elif procname.startswith("arm"): - if info.is_64bit(): + if ida_ida.inf_is_64bit(): return "armv8" else: return "armv7" @@ -649,7 +643,7 @@ def nops(self): def remap(self): if not self._config.has_option('IDA', 'remap_binary'): return False - return self._config.get('IDA', 'remap_binary').lower() == "true"; + return self._config.get('IDA', 'remap_binary').lower() == "true" @remap.setter def remap(self, value): @@ -778,7 +772,7 @@ def load_from_str(string): sio = StringIO(string) parser = ConfigParser.RawConfigParser() parser.optionxform = str - parser.readfp(sio) + parser.read_file(sio) return AnalyzerConfig(parser) def write(self, filepath): diff --git a/python/idabincat/bcplugin.py b/python/idabincat/bcplugin.py index 21a03411..4c844ce5 100644 --- a/python/idabincat/bcplugin.py +++ b/python/idabincat/bcplugin.py @@ -45,8 +45,8 @@ # log message will be displayed later pass +import ida_ida import idaapi -from idaapi import NW_OPENIDB, NW_CLOSEIDB, NW_TERMIDA, NW_REMOVE import idabincat.netnode import idabincat.npkgen from idabincat.plugin_options import PluginOptions @@ -87,13 +87,13 @@ def flush_staging(): if not loglines: flush_staging() break - l = loglines.pop() - if l == staging: + line = loglines.pop() + if line == staging: n += 1 else: if flush_staging(): break - staging = l + staging = line n = 1 res.reverse() return res @@ -118,8 +118,7 @@ def __init__(self): # IDA API methods: init, run, term def init(self): - info = idaapi.get_inf_structure() - procname = info.procname + procname = ida_ida.inf_get_procname() if procname != 'metapc' and procname != 'ARM' and procname != 'ARMB' and procname != 'PPC': bc_log.info("CPU '%s' is not supported, not loading BinCAT", procname) return idaapi.PLUGIN_SKIP @@ -240,20 +239,13 @@ def __call__(self): def destroy(self): idaapi.unregister_timer(self.timer) - -class LocalAnalyzer(Analyzer, QtCore.QProcess): +class LocalAnalyzer(Analyzer): """ Runs BinCAT locally using QProcess. """ def __init__(self, *args, **kwargs): - QtCore.QProcess.__init__(self) Analyzer.__init__(self, *args, **kwargs) - # Qprocess signal handlers - self.error.connect(self.procanalyzer_on_error) - self.stateChanged.connect(self.procanalyzer_on_state_change) - self.started.connect(self.procanalyzer_on_start) - self.finished.connect(self.procanalyzer_on_finish) def generate_tnpk(self, fname=None, destfname=None): if fname: @@ -272,8 +264,15 @@ def generate_tnpk(self, fname=None, destfname=None): def run(self): idaapi.show_wait_box("Running analysis") + self.process = QtCore.QProcess() + # Qprocess signal handlers + self.process.error.connect(self.procanalyzer_on_error) + self.process.stateChanged.connect(self.procanalyzer_on_state_change) + self.process.started.connect(self.procanalyzer_on_start) + self.process.finished.connect(self.procanalyzer_on_finish) + # Create a timer to allow for cancellation - self.timer = LocalAnalyzerTimer(self) + self.timer = LocalAnalyzerTimer(self.process) cmdline = ["bincat", [self.initfname, self.outfname, self.logfname]] @@ -281,7 +280,7 @@ def run(self): bc_log.debug( "Analyzer cmdline: [%s %s]", cmdline[0], " ".join(cmdline[1])) try: - self.start(*cmdline) + self.process.start(*cmdline) except Exception as e: bc_log.error("BinCAT failed to launch the analyzer.py") bc_log.warning("Exception: %s\n%s", str(e), traceback.format_exc()) @@ -296,7 +295,7 @@ def procanalyzer_on_error(self, err): except IndexError: errtxt = "Unspecified error %s" % err bc_log.error("Analyzer error: %s", errtxt) - self.process_output() + self.process.process_output() def procanalyzer_on_state_change(self, new_state): states = ["Not running", "Starting", "Running"] @@ -307,7 +306,7 @@ def procanalyzer_on_start(self): def procanalyzer_on_finish(self): bc_log.info("Analyzer process finished") - exitcode = self.exitCode() + exitcode = self.process.exitCode() if exitcode != 0: bc_log.error("analyzer returned exit code=%i", exitcode) try: @@ -322,9 +321,9 @@ def process_output(self): """ self.timer.destroy() bc_log.info("---- stdout ----------------") - bc_log.info(str(self.readAllStandardOutput())) + bc_log.info(str(self.process.readAllStandardOutput())) bc_log.info("---- stderr ----------------") - bc_log.info(str(self.readAllStandardError())) + bc_log.info(str(self.process.readAllStandardError())) bc_log.info("---- logfile ---------------") if os.path.exists(self.logfname): with open(self.logfname, 'r') as f: @@ -450,7 +449,6 @@ def run(self): if not files["out.ini"]: # try to parse out.ini if it exists return with open(self.outfname, 'w') as outfp: - outfp.write(self.download_file(files["out.ini"])) analyzer_log_contents = self.download_file(files["analyzer.log"]) with open(self.logfname, 'w') as logfp: diff --git a/python/idabincat/hexview/hexview.py b/python/idabincat/hexview/hexview.py index 087f6c16..e2761bbc 100644 --- a/python/idabincat/hexview/hexview.py +++ b/python/idabincat/hexview/hexview.py @@ -48,6 +48,7 @@ from PyQt5.QtCore import QItemSelectionModel from PyQt5.QtCore import QRectF from PyQt5.QtCore import QAbstractTableModel +import PyQt5.QtWidgets as QtWidgets from PyQt5.QtWidgets import QMenu from PyQt5.QtWidgets import QStyle from PyQt5.QtWidgets import QAction @@ -60,7 +61,6 @@ from PyQt5.QtWidgets import QStyledItemDelegate from PyQt5.QtWidgets import QAbstractItemView -from .hexview_auto import Ui_Form as HexViewBase from .common import h from .common import LoggingObject @@ -465,8 +465,7 @@ def _handle_mouse_released(self, qindex): self._update_selection(self._start_qindex, qindex) self._start_qindex = None - -class HexTableView(QTableView, LoggingObject): +class HexTableView(QTableView): """ table view that handles click events for better selection handling """ leftMousePressed = pyqtSignal([QMouseEvent]) leftMousePressedIndex = pyqtSignal([QModelIndex]) @@ -603,7 +602,7 @@ def _handle_mouse_release(self, key_event): Origin = namedtuple("Origin", ["offset", "name"]) -class HexViewWidget(QWidget, HexViewBase, LoggingObject): +class HexViewWidget(QWidget): originsChanged = pyqtSignal() newOverride = pyqtSignal(int, int, bool) @@ -672,6 +671,27 @@ def __init__(self, meminfo, parent=None): self.statusLabel.setText("") + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(400, 300) + self.verticalLayout = QtWidgets.QVBoxLayout(Form) + self.verticalLayout.setObjectName("verticalLayout") + self.mainLayout = QtWidgets.QVBoxLayout() + self.mainLayout.setObjectName("mainLayout") + self.statusLabel = QtWidgets.QLabel(Form) + self.statusLabel.setMaximumSize(QtCore.QSize(16777215, 15)) + self.statusLabel.setObjectName("statusLabel") + self.mainLayout.addWidget(self.statusLabel) + self.verticalLayout.addLayout(self.mainLayout) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.statusLabel.setText(_translate("Form", "TextLabel")) + def setNewMem(self, meminfo): self._model.beginResetModel() self._meminfo = meminfo diff --git a/python/idabincat/hexview/hexview.ui b/python/idabincat/hexview/hexview.ui deleted file mode 100644 index 369bb159..00000000 --- a/python/idabincat/hexview/hexview.ui +++ /dev/null @@ -1,38 +0,0 @@ - - - Form - - - - 0 - 0 - 400 - 300 - - - - Form - - - - - - - - - 16777215 - 15 - - - - TextLabel - - - - - - - - - - diff --git a/python/idabincat/hexview/hexview_auto.py b/python/idabincat/hexview/hexview_auto.py deleted file mode 100644 index a02d53a1..00000000 --- a/python/idabincat/hexview/hexview_auto.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'hexview/hexview.ui' -# -# Created by: PyQt5 UI code generator 5.5 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_Form(object): - def setupUi(self, Form): - Form.setObjectName("Form") - Form.resize(400, 300) - self.verticalLayout = QtWidgets.QVBoxLayout(Form) - self.verticalLayout.setObjectName("verticalLayout") - self.mainLayout = QtWidgets.QVBoxLayout() - self.mainLayout.setObjectName("mainLayout") - self.statusLabel = QtWidgets.QLabel(Form) - self.statusLabel.setMaximumSize(QtCore.QSize(16777215, 15)) - self.statusLabel.setObjectName("statusLabel") - self.mainLayout.addWidget(self.statusLabel) - self.verticalLayout.addLayout(self.mainLayout) - - self.retranslateUi(Form) - QtCore.QMetaObject.connectSlotsByName(Form) - - def retranslateUi(self, Form): - _translate = QtCore.QCoreApplication.translate - Form.setWindowTitle(_translate("Form", "Form")) - self.statusLabel.setText(_translate("Form", "TextLabel")) - diff --git a/python/idabincat/npkgen.py b/python/idabincat/npkgen.py index 77aef402..1b2abc05 100644 --- a/python/idabincat/npkgen.py +++ b/python/idabincat/npkgen.py @@ -24,6 +24,7 @@ try: import idaapi from ida_typeinf import PRTYPE_1LINE + import idc except ImportError: # imported outside ida - for instance, from wsgi.py pass @@ -167,7 +168,7 @@ def import_name(self, type_name): helper do import a type by name into local types """ # print "import_name : "+type_name - idaapi.import_type(idaapi.cvar.idati, -1, type_name, 0) + idc.import_type(-1, type_name) def imp_cb(self, ea, name, ord_nb): """ @@ -251,7 +252,7 @@ def analyze_type(self, tinfo): def add_types(self): local_type = idaapi.tinfo_t() count = 0 - for ordinal in range(1, idaapi.get_ordinal_qty(idaapi.cvar.idati)): + for ordinal in range(1, idaapi.get_ordinal_limit(idaapi.cvar.idati)): local_type.get_numbered_type(idaapi.cvar.idati, ordinal) if self.analyze_type(local_type): count += 1