diff --git a/src/Cutter.cpp b/src/Cutter.cpp index a3c97df9..bdaa19a3 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -1020,6 +1020,29 @@ QList CutterCore::getAllSymbols() return ret; } +QList CutterCore::getAllHeaders() +{ + CORE_LOCK(); + QList ret; + + QJsonArray headersArray = cmdj("ihj").array(); + + for (QJsonValue value : headersArray) { + QJsonObject headerObject = value.toObject(); + + HeaderDescription header; + + header.vaddr = headerObject["vaddr"].toVariant().toULongLong(); + header.paddr = headerObject["paddr"].toVariant().toULongLong(); + header.value = headerObject["comment"].toString(); + header.name = headerObject["name"].toString(); + + ret << header; + } + + return ret; +} + QList CutterCore::getAllComments(const QString &filterType) { CORE_LOCK(); diff --git a/src/Cutter.h b/src/Cutter.h index e1dccd8a..4a155f96 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -108,6 +108,14 @@ struct ExportDescription { QString flag_name; }; +struct HeaderDescription +{ + RVA vaddr; + RVA paddr; + QString value; + QString name; +}; + struct TypeDescription { QString type; int size; @@ -275,6 +283,7 @@ Q_DECLARE_METATYPE(const ClassFieldDescription *) Q_DECLARE_METATYPE(ResourcesDescription) Q_DECLARE_METATYPE(VTableDescription) Q_DECLARE_METATYPE(TypeDescription) +Q_DECLARE_METATYPE(HeaderDescription) Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SectionDescription) @@ -429,6 +438,7 @@ public: QList getAllImports(); QList getAllExports(); QList getAllSymbols(); + QList getAllHeaders(); QList getAllComments(const QString &filterType); QList getAllRelocs(); QList getAllStrings(); diff --git a/src/Cutter.pro b/src/Cutter.pro index c477817b..14cdabcf 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -146,6 +146,7 @@ SOURCES += \ widgets/ResourcesWidget.cpp \ widgets/VTablesWidget.cpp \ widgets/TypesWidget.cpp \ + widgets/HeadersWidget.cpp \ widgets/SearchWidget.cpp \ CutterApplication.cpp \ utils/JupyterConnection.cpp \ @@ -219,6 +220,7 @@ HEADERS += \ CutterApplication.h \ widgets/VTablesWidget.h \ widgets/TypesWidget.h \ + widgets/HeadersWidget.h \ widgets/SearchWidget.h \ utils/JupyterConnection.h \ widgets/JupyterWidget.h \ @@ -267,6 +269,7 @@ FORMS += \ widgets/ClassesWidget.ui \ widgets/VTablesWidget.ui \ widgets/TypesWidget.ui \ + widgets/HeadersWidget.ui \ widgets/SearchWidget.ui \ widgets/JupyterWidget.ui \ dialogs/R2PluginsDialog.ui \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index f8c964fe..3c83a0d9 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -68,6 +68,7 @@ #include "widgets/ResourcesWidget.h" #include "widgets/VTablesWidget.h" #include "widgets/JupyterWidget.h" +#include "widgets/HeadersWidget.h" // graphics #include @@ -167,6 +168,7 @@ void MainWindow::initUI() functionsDock = new FunctionsWidget(this, ui->actionFunctions); importsDock = new ImportsWidget(this, ui->actionImports); exportsDock = new ExportsWidget(this, ui->actionExports); + headersDock = new HeadersWidget(this, ui->actionHeaders); typesDock = new TypesWidget(this, ui->actionTypes); searchDock = new SearchWidget(this, ui->actionSearch); symbolsDock = new SymbolsWidget(this, ui->actionSymbols); @@ -461,6 +463,7 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, exportsDock); tabifyDockWidget(dashboardDock, typesDock); tabifyDockWidget(dashboardDock, searchDock); + tabifyDockWidget(dashboardDock, headersDock); tabifyDockWidget(dashboardDock, symbolsDock); tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, resourcesDock); diff --git a/src/MainWindow.h b/src/MainWindow.h index edf25141..69417135 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -40,6 +40,7 @@ class ClassesWidget; class ResourcesWidget; class VTablesWidget; class TypesWidget; +class HeadersWidget; class SearchWidget; #ifdef CUTTER_ENABLE_JUPYTER class JupyterWidget; @@ -183,8 +184,9 @@ private: FunctionsWidget *functionsDock = nullptr; ImportsWidget *importsDock = nullptr; ExportsWidget *exportsDock = nullptr; + HeadersWidget *headersDock = nullptr; TypesWidget *typesDock = nullptr; - SearchWidget *searchDock = nullptr; + SearchWidget *searchDock = nullptr; SymbolsWidget *symbolsDock = nullptr; RelocsWidget *relocsDock = nullptr; CommentsWidget *commentsDock = nullptr; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index ae6cf259..6c935204 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -250,6 +250,7 @@ border-top: 0px; + @@ -1061,6 +1062,17 @@ background-color: palette(dark); Show/Hide Search panel + + + true + + + Headers + + + Show/Hide Headers panel + + true diff --git a/src/widgets/HeadersWidget.cpp b/src/widgets/HeadersWidget.cpp new file mode 100644 index 00000000..5258eb1e --- /dev/null +++ b/src/widgets/HeadersWidget.cpp @@ -0,0 +1,146 @@ +#include "HeadersWidget.h" +#include "ui_HeadersWidget.h" +#include "MainWindow.h" +#include "utils/Helpers.h" + +HeadersModel::HeadersModel(QList *headers, QObject *parent) + : QAbstractListModel(parent), + headers(headers) +{ +} + +int HeadersModel::rowCount(const QModelIndex &) const +{ + return headers->count(); +} + +int HeadersModel::columnCount(const QModelIndex &) const +{ + return HeadersModel::ColumnCount; +} + +QVariant HeadersModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= headers->count()) + return QVariant(); + + const HeaderDescription &header = headers->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case OffsetColumn: + return RAddressString(header.vaddr); + case NameColumn: + return header.name; + case ValueColumn: + return header.value; + default: + return QVariant(); + } + case HeaderDescriptionRole: + return QVariant::fromValue(header); + default: + return QVariant(); + } +} + +QVariant HeadersModel::headerData(int section, Qt::Orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (section) { + case OffsetColumn: + return tr("Offset"); + case NameColumn: + return tr("Name"); + case ValueColumn: + return tr("Value"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +void HeadersModel::beginReloadHeaders() +{ + beginResetModel(); +} + +void HeadersModel::endReloadHeaders() +{ + endResetModel(); +} + +HeadersProxyModel::HeadersProxyModel(HeadersModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSourceModel(sourceModel); +} + +bool HeadersProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + QModelIndex index = sourceModel()->index(row, 0, parent); + HeaderDescription item = index.data(HeadersModel::HeaderDescriptionRole).value(); + return item.name.contains(filterRegExp()); +} + +bool HeadersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + HeaderDescription leftHeader = left.data(HeadersModel::HeaderDescriptionRole).value(); + HeaderDescription rightHeader = right.data(HeadersModel::HeaderDescriptionRole).value(); + + switch (left.column()) { + case HeadersModel::OffsetColumn: + return leftHeader.vaddr < rightHeader.vaddr; + case HeadersModel::NameColumn: + return leftHeader.name < rightHeader.name; + case HeadersModel::ValueColumn: + return leftHeader.value < rightHeader.value; + default: + break; + } + + return leftHeader.vaddr < rightHeader.vaddr; +} + +HeadersWidget::HeadersWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + ui(new Ui::HeadersWidget) +{ + ui->setupUi(this); + + headersModel = new HeadersModel(&headers, this); + headersProxyModel = new HeadersProxyModel(headersModel, this); + ui->headersTreeView->setModel(headersProxyModel); + ui->headersTreeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder); + + setScrollMode(); + + connect(Core(), &CutterCore::refreshAll, this, &HeadersWidget::refreshHeaders); +} + +HeadersWidget::~HeadersWidget() {} + +void HeadersWidget::refreshHeaders() +{ + headersModel->beginReloadHeaders(); + headers = Core()->getAllHeaders(); + headersModel->endReloadHeaders(); + + ui->headersTreeView->resizeColumnToContents(0); + ui->headersTreeView->resizeColumnToContents(1); +} + +void HeadersWidget::setScrollMode() +{ + qhelpers::setVerticalScrollMode(ui->headersTreeView); +} + +void HeadersWidget::on_headersTreeView_doubleClicked(const QModelIndex &index) +{ + HeaderDescription item = index.data(HeadersModel::HeaderDescriptionRole).value(); + Core()->seek(item.vaddr); +} diff --git a/src/widgets/HeadersWidget.h b/src/widgets/HeadersWidget.h new file mode 100644 index 00000000..a5e21892 --- /dev/null +++ b/src/widgets/HeadersWidget.h @@ -0,0 +1,88 @@ +#ifndef HEADERSWIDGET_H +#define HEADERSWIDGET_H + +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +#include +#include + +class MainWindow; +class QTreeWidget; + +namespace Ui +{ + class HeadersWidget; +} + + +class MainWindow; +class QTreeWidgetItem; + + +class HeadersModel: public QAbstractListModel +{ + Q_OBJECT + +private: + QList *headers; + +public: + enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount }; + enum Role { HeaderDescriptionRole = Qt::UserRole }; + + HeadersModel(QList *headers, QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void beginReloadHeaders(); + void endReloadHeaders(); +}; + + + +class HeadersProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + HeadersProxyModel(HeadersModel *sourceModel, QObject *parent = nullptr); + +protected: + bool filterAcceptsRow(int row, const QModelIndex &parent) const override; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; +}; + + + +class HeadersWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit HeadersWidget(MainWindow *main, QAction *action = nullptr); + ~HeadersWidget(); + +private slots: + void on_headersTreeView_doubleClicked(const QModelIndex &index); + + void refreshHeaders(); + +private: + std::unique_ptr ui; + + HeadersModel *headersModel; + HeadersProxyModel *headersProxyModel; + QList headers; + + void setScrollMode(); +}; + + +#endif // HEADERSWIDGET_H diff --git a/src/widgets/HeadersWidget.ui b/src/widgets/HeadersWidget.ui new file mode 100644 index 00000000..2f7360d4 --- /dev/null +++ b/src/widgets/HeadersWidget.ui @@ -0,0 +1,58 @@ + + + HeadersWidget + + + + 0 + 0 + 400 + 300 + + + + Headers + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QTreeView::item +{ + padding-top: 1px; + padding-bottom: 1px; +} + + + QFrame::NoFrame + + + 0 + + + 8 + + + true + + + + + + + + +