diff --git a/src/dialogs/EditMethodDialog.cpp b/src/dialogs/EditMethodDialog.cpp index 41056216..37828ae2 100644 --- a/src/dialogs/EditMethodDialog.cpp +++ b/src/dialogs/EditMethodDialog.cpp @@ -59,6 +59,7 @@ bool EditMethodDialog::inputValid() if (ui->nameEdit->text().isEmpty()) { return false; } + // TODO: do more checks here, for example for name clashes return true; } @@ -128,9 +129,9 @@ AnalMethodDescription EditMethodDialog::getMethod() return ret; } -bool EditMethodDialog::showDialog(const QString &title, QString *className, AnalMethodDescription *desc, QWidget *parent) +bool EditMethodDialog::showDialog(const QString &title, bool classFixed, QString *className, AnalMethodDescription *desc, QWidget *parent) { - auto dialog = new EditMethodDialog(parent); + auto dialog = new EditMethodDialog(classFixed, parent); dialog->setWindowTitle(title); dialog->setClass(*className); dialog->setMethod(*desc); @@ -147,7 +148,7 @@ void EditMethodDialog::newMethod(QString className, const QString &meth, QWidget desc.vtableOffset = -1; desc.addr = Core()->getOffset(); - if (!showDialog(tr("Create Method"), &className, &desc, parent)) { + if (!showDialog(tr("Create Method"), false, &className, &desc, parent)) { return; } @@ -162,7 +163,7 @@ void EditMethodDialog::editMethod(const QString &className, const QString &meth, } QString classNameCopy = className; - if (!showDialog(tr("Edit Method"), &classNameCopy, &desc, parent)) { + if (!showDialog(tr("Edit Method"), false, &classNameCopy, &desc, parent)) { return; } if (desc.name != meth) { diff --git a/src/dialogs/EditMethodDialog.h b/src/dialogs/EditMethodDialog.h index 9365bda3..74ac8bca 100644 --- a/src/dialogs/EditMethodDialog.h +++ b/src/dialogs/EditMethodDialog.h @@ -17,6 +17,9 @@ class EditMethodDialog : public QDialog Q_OBJECT public: + /*! + * \param classFixed whether the user should be able to change the class. If false, a QComboBox will be shown, otherwise a plain QLabel. + */ explicit EditMethodDialog(bool classFixed, QWidget *parent = nullptr); ~EditMethodDialog(); @@ -26,8 +29,25 @@ public: QString getClass(); AnalMethodDescription getMethod(); - static bool showDialog(const QString &title, QString *className, AnalMethodDescription *desc, QWidget *parent = nullptr); + /*! + * \brief Helper function to display the dialog + * + * \param title title of the dialog + * \param classFixed whether the user should be able to change the class + * \param className initial class name, will be overwritten if the user changed the class + * \param desc initial data for the method information + * \return whether the dialog was accepted by the user + */ + static bool showDialog(const QString &title, bool classFixed, QString *className, AnalMethodDescription *desc, QWidget *parent = nullptr); + + /*! + * \brief Show the dialog to add a new method a given class + */ static void newMethod(QString className = nullptr, const QString &meth = QString(), QWidget *parent = nullptr); + + /*! + * \brief Show the dialog to edit a given method of a given class + */ static void editMethod(const QString &className, const QString &meth, QWidget *parent = nullptr); private slots: @@ -42,6 +62,9 @@ private: QComboBox *classComboBox = nullptr; QLabel *classLabel = nullptr; + /*! + * This will only be used when the dialog was created with classFixed = true in order to remember the class name. + */ QString fixedClass; bool inputValid(); diff --git a/src/widgets/ClassesWidget.cpp b/src/widgets/ClassesWidget.cpp index 47f47bbf..db4d3ecf 100644 --- a/src/widgets/ClassesWidget.cpp +++ b/src/widgets/ClassesWidget.cpp @@ -132,14 +132,10 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const } case OffsetRole: return QVariant::fromValue(meth->addr); - case VTableOffsetRole: - return QVariant::fromValue(index.parent().data(VTableOffsetRole).toULongLong() + meth->vtableOffset); case NameRole: return meth->name; case TypeRole: return QVariant::fromValue(RowType::Method); - case DataRole: - return QVariant::fromValue(*meth); default: return QVariant(); } @@ -203,8 +199,6 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const } case OffsetRole: return QVariant::fromValue(cls->addr); - case VTableOffsetRole: - return QVariant::fromValue(cls->vtableAddr); case NameRole: return cls->name; case TypeRole: @@ -383,8 +377,6 @@ QVariant AnalClassesModel::data(const QModelIndex &index, int role) const return QVariant(); case OffsetRole: return QVariant::fromValue(meth.addr); - case VTableOffsetRole: - return QVariant::fromValue(index.parent().data(VTableOffsetRole).toULongLong() + meth.vtableOffset); case NameRole: return meth.name; case TypeRole: @@ -555,6 +547,11 @@ void ClassesWidget::on_classesTreeView_doubleClicked(const QModelIndex &index) void ClassesWidget::showContextMenu(const QPoint &pt) { + if(!anal_model) { + // no context menu for bin classes + return; + } + QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); if (!index.isValid()) { return; @@ -562,15 +559,19 @@ void ClassesWidget::showContextMenu(const QPoint &pt) QMenu menu(ui->classesTreeView); - QVariant vtableOffsetVariant = index.data(ClassesModel::VTableOffsetRole); - if (vtableOffsetVariant.isValid() && vtableOffsetVariant.toULongLong() != RVA_INVALID) { - menu.addAction(ui->seekToVTableAction); - } - menu.addAction(ui->addMethodAction); if (index.data(ClassesModel::TypeRole).toInt() == static_cast(ClassesModel::RowType::Method)) { menu.addAction(ui->editMethodAction); + + QString className = index.parent().data(ClassesModel::NameRole).toString(); + QString methodName = index.data(ClassesModel::NameRole).toString(); + AnalMethodDescription desc; + if (Core()->getAnalMethod(className, methodName, &desc)) { + if (desc.vtableOffset >= 0) { + menu.addAction(ui->seekToVTableAction); + } + } } menu.exec(ui->classesTreeView->mapToGlobal(pt)); @@ -578,11 +579,22 @@ void ClassesWidget::showContextMenu(const QPoint &pt) void ClassesWidget::on_seekToVTableAction_triggered() { - RVA vtableOffset = ui->classesTreeView->selectionModel()->currentIndex() - .data(ClassesModel::VTableOffsetRole).value(); - if (vtableOffset != RVA_INVALID) { - Core()->seek(vtableOffset); + QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); + QString className = index.parent().data(ClassesModel::NameRole).toString(); + + QList vtables = Core()->getAnalClassVTables(className); + if (vtables.isEmpty()) { + QMessageBox::warning(this, tr("Missing VTable in class"), tr("The class %1 does not have any VTable!").arg(className)); + return; } + + QString methodName = index.data(ClassesModel::NameRole).toString(); + AnalMethodDescription desc; + if (!Core()->getAnalMethod(className, methodName, &desc) || desc.vtableOffset < 0) { + return; + } + + Core()->seek(vtables[0].addr + desc.vtableOffset); } void ClassesWidget::on_addMethodAction_triggered() diff --git a/src/widgets/ClassesWidget.h b/src/widgets/ClassesWidget.h index cf58ac3e..6fb8c2ce 100644 --- a/src/widgets/ClassesWidget.h +++ b/src/widgets/ClassesWidget.h @@ -18,17 +18,40 @@ class QTreeWidgetItem; class MainWindow; class ClassesWidget; +/*! + * \brief Common abstract base class for Bin and Anal classes models + */ class ClassesModel: public QAbstractItemModel { public: enum Columns { NAME = 0, TYPE, OFFSET, VTABLE, COUNT }; + + /*! + * \brief values for TypeRole data + */ enum class RowType { Class = 0, Base, VTable, Method, Field }; + /*! + * \brief Offset role of data for QModelIndex + * + * will contain values of type RVA + */ static const int OffsetRole = Qt::UserRole; + + /*! + * \brief Name role of data for QModelIndex + * + * will contain values of QString, used for sorting, + * as well as identifying classes and methods + */ static const int NameRole = Qt::UserRole + 1; + + /*! + * \brief Type role of data for QModelIndex + * + * will contain values of RowType + */ static const int TypeRole = Qt::UserRole + 2; - static const int VTableOffsetRole = Qt::UserRole + 3; - static const int DataRole = Qt::UserRole + 4; explicit ClassesModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {} @@ -64,6 +87,13 @@ class AnalClassesModel: public ClassesModel Q_OBJECT private: + /*! + * \brief List entry below a class + * + * This roughly corresponds to attributes of r2 anal classes, which means it is not an attribute in the sense of + * a class member variable, but any kind of sub-info associated with the class. + * This struct in particular is used to provide a model for the list entries below a class. + */ struct Attribute { enum class Type { VTable, Base, Method }; @@ -75,6 +105,17 @@ private: }; QList classes; + + /*! + * \brief Cache for class attributes + * + * Maps class names to a list of Attributes. + * This is filled only when the attributes of a specific class are requested. + * (i.e. the user expands the class in the QTreeView) + * + * This must be a pointer instead of just a QMap, because it has to be modified + * in methods that are defined as const by QAbstractItemModel. + */ std::unique_ptr>> attrs; const QVector &getAttrs(const QString &cls) const;