From a1b5a41e56bb0db7cca94d1081192373464c1c0c Mon Sep 17 00:00:00 2001 From: yossizap Date: Thu, 30 Jan 2020 19:40:27 +0200 Subject: [PATCH] Register refs widget improvements (#2038) * Move register refs to the new telescoping function and add an addressable context menu --- src/core/Cutter.cpp | 100 +++++++++++++++++++++++------ src/core/Cutter.h | 11 +++- src/core/CutterDescriptions.h | 8 +-- src/widgets/RegisterRefsWidget.cpp | 66 +++++++++++++++---- src/widgets/RegisterRefsWidget.h | 18 ++++-- src/widgets/StackWidget.cpp | 55 +--------------- src/widgets/StackWidget.h | 3 +- 7 files changed, 164 insertions(+), 97 deletions(-) diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index ffa5b61a..862508fd 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1156,6 +1156,86 @@ QJsonDocument CutterCore::getSignatureInfo() return cmdj("iCj"); } +// Utility function to check if a telescoped item exists and add it with prefixes to the desc +static inline const QString appendVar(QString &dst, const QString val, const QString prepend_val, + const QString append_val) +{ + if (!val.isEmpty()) { + dst += prepend_val + val + append_val; + } + return val; +} + +RefDescription CutterCore::formatRefDesc(QJsonObject refItem) +{ + RefDescription desc; + + // Ignore empty refs and refs that only contain addr + if (refItem.size() <= 1) { + return desc; + } + + QString str = refItem["string"].toVariant().toString(); + if (!str.isEmpty()) { + desc.ref = str; + desc.refColor = ConfigColor("comment"); + } else { + QString type, string; + do { + desc.ref += " ->"; + appendVar(desc.ref, refItem["reg"].toVariant().toString(), " @", ""); + appendVar(desc.ref, refItem["mapname"].toVariant().toString(), " (", ")"); + appendVar(desc.ref, refItem["section"].toVariant().toString(), " (", ")"); + appendVar(desc.ref, refItem["func"].toVariant().toString(), " ", ""); + type = appendVar(desc.ref, refItem["type"].toVariant().toString(), " ", ""); + appendVar(desc.ref, refItem["perms"].toVariant().toString(), " ", ""); + appendVar(desc.ref, refItem["asm"].toVariant().toString(), " \"", "\""); + string = appendVar(desc.ref, refItem["string"].toVariant().toString(), " ", ""); + if (!string.isNull()) { + // There is no point in adding ascii and addr info after a string + break; + } + if (!refItem["value"].isNull()) { + appendVar(desc.ref, RAddressString(refItem["value"].toVariant().toULongLong()), " ", ""); + } + refItem = refItem["ref"].toObject(); + } while (!refItem.empty()); + + // Set the ref's color according to the last item type + if (type == "ascii" || !string.isEmpty()) { + desc.refColor = ConfigColor("comment"); + } else if (type == "program") { + desc.refColor = ConfigColor("fname"); + } else if (type == "library") { + desc.refColor = ConfigColor("floc"); + } else if (type == "stack") { + desc.refColor = ConfigColor("offset"); + } + } + + return desc; +} + +QList CutterCore::getRegisterRefs(int depth) +{ + QList ret; + if (!currentlyDebugging) { + return ret; + } + + QJsonObject registers = cmdj("drj").object(); + + for (const QString &key : registers.keys()) { + QJsonObject reg; + reg["value"] = registers.value(key); + reg["ref"] = getAddrRefs(registers.value(key).toVariant().toULongLong(), depth); + reg["name"] = key; + ret.append(reg); + } + + return ret; +} + QList CutterCore::getStack(int size, int depth) { QList stack; @@ -1321,26 +1401,6 @@ QJsonDocument CutterCore::getRegisterValues() return cmdj("drj"); } -QList CutterCore::getRegisterRefs() -{ - QList ret; - QJsonArray registerRefArray = cmdj("drrj").array(); - - for (const QJsonValue &value : registerRefArray) { - QJsonObject regRefObject = value.toObject(); - - RegisterRefDescription regRef; - - regRef.reg = regRefObject[RJsonKey::reg].toString(); - regRef.value = regRefObject[RJsonKey::value].toString(); - regRef.ref = regRefObject[RJsonKey::ref].toString(); - - ret << regRef; - } - - return ret; -} - QList CutterCore::getVariables(RVA at) { QList ret; diff --git a/src/core/Cutter.h b/src/core/Cutter.h index b265bbbb..b5eed6c6 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -312,6 +312,11 @@ public: * @param depth telescoping depth */ QJsonObject getAddrRefs(RVA addr, int depth); + /** + * @brief return a RefDescription with a formatted ref string and configured colors + * @param ref the "ref" JSON node from getAddrRefs + */ + RefDescription formatRefDesc(QJsonObject ref); /** * @brief Get a list of a given process's threads * @param pid The pid of the process, -1 for the currently debugged process @@ -525,7 +530,11 @@ public: BlockStatistics getBlockStatistics(unsigned int blocksCount); QList getBreakpoints(); QList getAllProcesses(); - QList getRegisterRefs(); + /** + * @brief returns a list of reg values and their telescoped references + * @param depth telescoping depth + */ + QList getRegisterRefs(int depth = 6); QJsonObject getRegisterJson(); QList getVariables(RVA at); diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 23e97621..2087640e 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "core/CutterCommon.h" struct FunctionDescription { @@ -308,10 +309,9 @@ struct ProcessDescription { QString path; }; -struct RegisterRefDescription { - QString reg; - QString value; +struct RefDescription { QString ref; + QColor refColor; }; struct VariableDescription { @@ -357,7 +357,7 @@ Q_DECLARE_METATYPE(MemoryMapDescription) Q_DECLARE_METATYPE(BreakpointDescription) Q_DECLARE_METATYPE(BreakpointDescription::PositionType) Q_DECLARE_METATYPE(ProcessDescription) -Q_DECLARE_METATYPE(RegisterRefDescription) +Q_DECLARE_METATYPE(RefDescription) Q_DECLARE_METATYPE(VariableDescription) #endif // DESCRIPTIONS_H diff --git a/src/widgets/RegisterRefsWidget.cpp b/src/widgets/RegisterRefsWidget.cpp index c7016372..eeac36e7 100644 --- a/src/widgets/RegisterRefsWidget.cpp +++ b/src/widgets/RegisterRefsWidget.cpp @@ -3,6 +3,7 @@ #include "core/MainWindow.h" #include "common/Helpers.h" +#include #include #include #include @@ -38,7 +39,14 @@ QVariant RegisterRefModel::data(const QModelIndex &index, int role) const case ValueColumn: return registerRef.value; case RefColumn: - return registerRef.ref; + return registerRef.refDesc.ref; + default: + return QVariant(); + } + case Qt::ForegroundRole: + switch (index.column()) { + case RefColumn: + return registerRef.refDesc.refColor; default: return QVariant(); } @@ -93,7 +101,7 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex case RegisterRefModel::RegColumn: return leftRegRef.reg < rightRegRef.reg; case RegisterRefModel::RefColumn: - return leftRegRef.ref < rightRegRef.ref; + return leftRegRef.refDesc.ref < rightRegRef.refDesc.ref; case RegisterRefModel::ValueColumn: return leftRegRef.value < rightRegRef.value; default: @@ -106,7 +114,8 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) : CutterDockWidget(main, action), ui(new Ui::RegisterRefsWidget), - tree(new CutterTreeWidget(this)) + tree(new CutterTreeWidget(this)), + addressableItemContextMenu(this, main) { ui->setupUi(this); @@ -122,6 +131,13 @@ RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) : actionCopyValue = new QAction(tr("Copy register value"), this); actionCopyRef = new QAction(tr("Copy register reference"), this); + addressableItemContextMenu.addAction(actionCopyValue); + addressableItemContextMenu.addAction(actionCopyRef); + addActions(addressableItemContextMenu.actions()); + + connect(ui->registerRefTreeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &RegisterRefsWidget::onCurrentChanged); + refreshDeferrer = createRefreshDeferrer([this](){ refreshRegisterRef(); }); @@ -144,8 +160,8 @@ RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) : copyClip(RegisterRefModel::RefColumn); }); ui->registerRefTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->registerRefTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(showRegRefContextMenu(const QPoint &))); + connect(ui->registerRefTreeView, &QMenu::customContextMenuRequested, + this, &RegisterRefsWidget::customMenuRequested); connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { tree->showItemsNumber(registerRefProxyModel->rowCount()); @@ -161,7 +177,19 @@ void RegisterRefsWidget::refreshRegisterRef() } registerRefModel->beginResetModel(); - registerRefs = Core()->getRegisterRefs(); + + QList regRefs = Core()->getRegisterRefs(); + registerRefs.clear(); + for (const QJsonObject ® : regRefs) { + RegisterRefDescription desc; + + desc.value = RAddressString(reg["value"].toVariant().toULongLong()); + desc.reg = reg["name"].toVariant().toString(); + desc.refDesc = Core()->formatRefDesc(reg["ref"].toObject()); + + registerRefs.push_back(desc); + } + registerRefModel->endResetModel(); ui->registerRefTreeView->resizeColumnToContents(0); @@ -183,15 +211,27 @@ void RegisterRefsWidget::on_registerRefTreeView_doubleClicked(const QModelIndex Core()->seekAndShow(item.value); } -void RegisterRefsWidget::showRegRefContextMenu(const QPoint &pt) +void RegisterRefsWidget::customMenuRequested(QPoint pos) { - QMenu *menu = new QMenu(ui->registerRefTreeView); - menu->clear(); - menu->addAction(actionCopyValue); - menu->addAction(actionCopyRef); + addressableItemContextMenu.exec(ui->registerRefTreeView->viewport()->mapToGlobal(pos)); +} - menu->exec(ui->registerRefTreeView->viewport()->mapToGlobal(pt)); - delete menu; +void RegisterRefsWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + Q_UNUSED(current) + Q_UNUSED(previous) + auto currentIndex = ui->registerRefTreeView->selectionModel()->currentIndex(); + + // Use the value column as the offset + QString offsetString; + if (currentIndex.column() != RegisterRefModel::RefColumn) { + offsetString = currentIndex.data().toString(); + } else { + offsetString = currentIndex.sibling(currentIndex.row(), RegisterRefModel::ValueColumn).data().toString(); + } + + RVA offset = Core()->math(offsetString); + addressableItemContextMenu.setTarget(offset); } void RegisterRefsWidget::copyClip(int column) diff --git a/src/widgets/RegisterRefsWidget.h b/src/widgets/RegisterRefsWidget.h index 9615eab7..44531f1c 100644 --- a/src/widgets/RegisterRefsWidget.h +++ b/src/widgets/RegisterRefsWidget.h @@ -5,6 +5,7 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" #include "CutterTreeWidget.h" +#include "menus/AddressableItemContextMenu.h" #include #include @@ -21,6 +22,12 @@ class RegisterRefsWidget; class MainWindow; class QTreeWidgetItem; +struct RegisterRefDescription { + QString reg; + QString value; + RefDescription refDesc; +}; +Q_DECLARE_METATYPE(RegisterRefDescription) class RegisterRefModel: public QAbstractListModel { @@ -44,8 +51,6 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; - - class RegisterRefProxyModel : public QSortFilterProxyModel { Q_OBJECT @@ -71,7 +76,8 @@ private slots: void on_registerRefTreeView_doubleClicked(const QModelIndex &index); void refreshRegisterRef(); void copyClip(int column); - void showRegRefContextMenu(const QPoint &pt); + void customMenuRequested(QPoint pos); + void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); private: std::unique_ptr ui; @@ -79,10 +85,12 @@ private: RegisterRefModel *registerRefModel; RegisterRefProxyModel *registerRefProxyModel; QList registerRefs; - QAction *actionCopyValue; - QAction *actionCopyRef; CutterTreeWidget *tree; void setScrollMode(); RefreshDeferrer *refreshDeferrer; + + QAction *actionCopyValue; + QAction *actionCopyRef; + AddressableItemContextMenu addressableItemContextMenu; }; diff --git a/src/widgets/StackWidget.cpp b/src/widgets/StackWidget.cpp index 93e5a44b..933f10bb 100644 --- a/src/widgets/StackWidget.cpp +++ b/src/widgets/StackWidget.cpp @@ -144,16 +144,6 @@ StackModel::StackModel(QObject *parent) { } -// Utility function to check if a telescoped item exists and add it with prefixes to the desc -static inline const QString append_var(QString &dst, const QString val, const QString prepend_val, - const QString append_val) -{ - if (!val.isEmpty()) { - dst += prepend_val + val + append_val; - } - return val; -} - void StackModel::reload() { QList stackItems = Core()->getStack(); @@ -165,47 +155,8 @@ void StackModel::reload() item.offset = stackItem["addr"].toVariant().toULongLong(); item.value = RAddressString(stackItem["value"].toVariant().toULongLong()); + item.refDesc = Core()->formatRefDesc(stackItem["ref"].toObject()); - QJsonObject refItem = stackItem["ref"].toObject(); - if (!refItem.empty()) { - QString str = refItem["string"].toVariant().toString(); - if (!str.isEmpty()) { - item.description = str; - item.descriptionColor = ConfigColor("comment"); - } else { - QString type, string; - do { - item.description += " ->"; - append_var(item.description, refItem["reg"].toVariant().toString(), " @", ""); - append_var(item.description, refItem["mapname"].toVariant().toString(), " (", ")"); - append_var(item.description, refItem["section"].toVariant().toString(), " (", ")"); - append_var(item.description, refItem["func"].toVariant().toString(), " ", ""); - type = append_var(item.description, refItem["type"].toVariant().toString(), " ", ""); - append_var(item.description, refItem["perms"].toVariant().toString(), " ", ""); - append_var(item.description, refItem["asm"].toVariant().toString(), " \"", "\""); - string = append_var(item.description, refItem["string"].toVariant().toString(), " ", ""); - if (!string.isNull()) { - // There is no point in adding ascii and addr info after a string - break; - } - if (!refItem["value"].isNull()) { - append_var(item.description, RAddressString(refItem["value"].toVariant().toULongLong()), " ", ""); - } - refItem = refItem["ref"].toObject(); - } while (!refItem.empty()); - - // Set the description's color according to the last item type - if (type == "ascii" || !string.isEmpty()) { - item.descriptionColor = ConfigColor("comment"); - } else if (type == "program") { - item.descriptionColor = ConfigColor("fname"); - } else if (type == "library") { - item.descriptionColor = ConfigColor("floc"); - } else if (type == "stack") { - item.descriptionColor = ConfigColor("offset"); - } - } - } values.push_back(item); } endResetModel(); @@ -236,14 +187,14 @@ QVariant StackModel::data(const QModelIndex &index, int role) const case ValueColumn: return item.value; case DescriptionColumn: - return item.description; + return item.refDesc.ref; default: return QVariant(); } case Qt::ForegroundRole: switch (index.column()) { case DescriptionColumn: - return item.descriptionColor; + return item.refDesc.refColor; default: return QVariant(); } diff --git a/src/widgets/StackWidget.h b/src/widgets/StackWidget.h index 42f3a768..b9517286 100644 --- a/src/widgets/StackWidget.h +++ b/src/widgets/StackWidget.h @@ -22,8 +22,7 @@ public: struct Item { RVA offset; QString value; - QString description; - QVariant descriptionColor; + RefDescription refDesc; }; enum Column { OffsetColumn = 0, ValueColumn, DescriptionColumn, ColumnCount};