Fix Seek to VTable and add some docs

This commit is contained in:
Florian Märkl 2019-02-02 14:14:39 +01:00
parent dae04b8609
commit 1ee1d7d948
4 changed files with 101 additions and 24 deletions

View File

@ -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) {

View File

@ -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();

View File

@ -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<int>(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<RVA>();
if (vtableOffset != RVA_INVALID) {
Core()->seek(vtableOffset);
QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex();
QString className = index.parent().data(ClassesModel::NameRole).toString();
QList<AnalVTableDescription> 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()

View File

@ -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<QString> 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<QMap<QString, QVector<Attribute>>> attrs;
const QVector<Attribute> &getAttrs(const QString &cls) const;