diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 5ebba6a0..613920ad 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -1046,6 +1046,37 @@ QList CutterCore::getAllHeaders() return ret; } +QList CutterCore::getAllZignatures() +{ + CORE_LOCK(); + QList ret; + + QJsonArray zignaturesArray = cmdj("zj").array(); + + for (QJsonValue value : zignaturesArray) { + QJsonObject zignatureObject = value.toObject(); + + ZignatureDescription zignature; + + zignature.name = zignatureObject["name"].toString(); + zignature.bytes = zignatureObject["bytes"].toString(); + zignature.offset = zignatureObject["offset"].toVariant().toULongLong(); + for (QJsonValue ref : zignatureObject["refs"].toArray()) { + zignature.refs << ref.toString(); + } + + QJsonObject graphObject = zignatureObject["graph"].toObject(); + zignature.cc = graphObject["cc"].toVariant().toULongLong(); + zignature.nbbs = graphObject["nbbs"].toVariant().toULongLong(); + zignature.edges = graphObject["edges"].toVariant().toULongLong(); + zignature.ebbs = graphObject["ebbs"].toVariant().toULongLong(); + + ret << zignature; + } + + return ret; +} + QList CutterCore::getAllComments(const QString &filterType) { CORE_LOCK(); diff --git a/src/Cutter.h b/src/Cutter.h index 4a155f96..48793c1e 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -116,6 +116,18 @@ struct HeaderDescription QString name; }; +struct ZignatureDescription +{ + QString name; + QString bytes; + RVA cc; + RVA nbbs; + RVA edges; + RVA ebbs; + RVA offset; + QStringList refs; +}; + struct TypeDescription { QString type; int size; @@ -284,6 +296,7 @@ Q_DECLARE_METATYPE(ResourcesDescription) Q_DECLARE_METATYPE(VTableDescription) Q_DECLARE_METATYPE(TypeDescription) Q_DECLARE_METATYPE(HeaderDescription) +Q_DECLARE_METATYPE(ZignatureDescription) Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SectionDescription) @@ -439,6 +452,7 @@ public: QList getAllExports(); QList getAllSymbols(); QList getAllHeaders(); + QList getAllZignatures(); QList getAllComments(const QString &filterType); QList getAllRelocs(); QList getAllStrings(); diff --git a/src/Cutter.pro b/src/Cutter.pro index 14cdabcf..f45d000b 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -158,7 +158,8 @@ SOURCES += \ widgets/GraphWidget.cpp \ utils/JsonTreeItem.cpp \ utils/JsonModel.cpp \ - dialogs/VersionInfoDialog.cpp + dialogs/VersionInfoDialog.cpp \ + widgets/ZignaturesWidget.cpp HEADERS += \ Cutter.h \ @@ -231,7 +232,8 @@ HEADERS += \ widgets/GraphWidget.h \ utils/JsonTreeItem.h \ utils/JsonModel.h \ - dialogs/VersionInfoDialog.h + dialogs/VersionInfoDialog.h \ + widgets/ZignaturesWidget.h FORMS += \ dialogs/AboutDialog.ui \ @@ -273,7 +275,8 @@ FORMS += \ widgets/SearchWidget.ui \ widgets/JupyterWidget.ui \ dialogs/R2PluginsDialog.ui \ - dialogs/VersionInfoDialog.ui + dialogs/VersionInfoDialog.ui \ + widgets/ZignaturesWidget.ui RESOURCES += \ resources.qrc \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3c83a0d9..f98fc565 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -69,6 +69,7 @@ #include "widgets/VTablesWidget.h" #include "widgets/JupyterWidget.h" #include "widgets/HeadersWidget.h" +#include "widgets/ZignaturesWidget.h" // graphics #include @@ -169,6 +170,7 @@ void MainWindow::initUI() importsDock = new ImportsWidget(this, ui->actionImports); exportsDock = new ExportsWidget(this, ui->actionExports); headersDock = new HeadersWidget(this, ui->actionHeaders); + zignaturesDock = new ZignaturesWidget(this, ui->actionZignatures); typesDock = new TypesWidget(this, ui->actionTypes); searchDock = new SearchWidget(this, ui->actionSearch); symbolsDock = new SymbolsWidget(this, ui->actionSymbols); @@ -464,6 +466,7 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, typesDock); tabifyDockWidget(dashboardDock, searchDock); tabifyDockWidget(dashboardDock, headersDock); + tabifyDockWidget(dashboardDock, zignaturesDock); tabifyDockWidget(dashboardDock, symbolsDock); tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, resourcesDock); diff --git a/src/MainWindow.h b/src/MainWindow.h index 69417135..6490ffad 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -41,6 +41,7 @@ class ResourcesWidget; class VTablesWidget; class TypesWidget; class HeadersWidget; +class ZignaturesWidget; class SearchWidget; #ifdef CUTTER_ENABLE_JUPYTER class JupyterWidget; @@ -196,6 +197,7 @@ private: QLineEdit *gotoEntry = nullptr; SdbDock *sdbDock = nullptr; SectionsWidget *sectionsDock = nullptr; + ZignaturesWidget *zignaturesDock = nullptr; ConsoleWidget *consoleDock = nullptr; ClassesWidget *classesDock = nullptr; ResourcesWidget *resourcesDock = nullptr; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 6c935204..ded96d79 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -251,6 +251,7 @@ border-top: 0px; + @@ -1073,6 +1074,17 @@ background-color: palette(dark); Show/Hide Headers panel + + + true + + + Zignatures + + + Show/Hide Zignatures panel + + true diff --git a/src/widgets/ZignaturesWidget.cpp b/src/widgets/ZignaturesWidget.cpp new file mode 100644 index 00000000..98c37709 --- /dev/null +++ b/src/widgets/ZignaturesWidget.cpp @@ -0,0 +1,161 @@ +#include "ZignaturesWidget.h" +#include "ui_ZignaturesWidget.h" +#include "MainWindow.h" +#include "utils/Helpers.h" + +ZignaturesModel::ZignaturesModel(QList *zignatures, QObject *parent) + : QAbstractListModel(parent), + zignatures(zignatures) +{ +} + +int ZignaturesModel::rowCount(const QModelIndex &) const +{ + return zignatures->count(); +} + +int ZignaturesModel::columnCount(const QModelIndex &) const +{ + return ZignaturesModel::ColumnCount; +} + +QVariant ZignaturesModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= zignatures->count()) + return QVariant(); + + const ZignatureDescription &zignature = zignatures->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case OffsetColumn: + return RAddressString(zignature.offset); + case NameColumn: + return zignature.name; + case ValueColumn: + return zignature.bytes; + default: + return QVariant(); + } + + case ZignatureDescriptionRole: + return QVariant::fromValue(zignature); + + case Qt::ToolTipRole: { + QString tmp = QString("Graph:\n\n Cyclomatic complexity: " + RSizeString(zignature.cc) + + "\n Nodes / basicblocks: " + RSizeString(zignature.nbbs) + + "\n Edges: " + RSizeString(zignature.edges) + + "\n Ebbs: " + RSizeString(zignature.ebbs) + + "\n\nRefs:\n"); + for (QString ref : zignature.refs) { + tmp.append("\n " + ref); + } + return tmp; + } + + default: + return QVariant(); + } +} + +QVariant ZignaturesModel::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("Bytes"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +void ZignaturesModel::beginReloadZignatures() +{ + beginResetModel(); +} + +void ZignaturesModel::endReloadZignatures() +{ + endResetModel(); +} + +ZignaturesProxyModel::ZignaturesProxyModel(ZignaturesModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSourceModel(sourceModel); +} + +bool ZignaturesProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + QModelIndex index = sourceModel()->index(row, 0, parent); + ZignatureDescription item = index.data(ZignaturesModel::ZignatureDescriptionRole).value(); + return item.name.contains(filterRegExp()); +} + +bool ZignaturesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + ZignatureDescription leftZignature = left.data(ZignaturesModel::ZignatureDescriptionRole).value(); + ZignatureDescription rightZignature = right.data(ZignaturesModel::ZignatureDescriptionRole).value(); + + switch (left.column()) { + case ZignaturesModel::OffsetColumn: + return leftZignature.offset < rightZignature.offset; + case ZignaturesModel::NameColumn: + return leftZignature.name < rightZignature.name; + case ZignaturesModel::ValueColumn: + return leftZignature.bytes < rightZignature.bytes; + default: + break; + } + + return leftZignature.offset < rightZignature.offset; +} + +ZignaturesWidget::ZignaturesWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + ui(new Ui::ZignaturesWidget) +{ + ui->setupUi(this); + + zignaturesModel = new ZignaturesModel(&zignatures, this); + zignaturesProxyModel = new ZignaturesProxyModel(zignaturesModel, this); + ui->zignaturesTreeView->setModel(zignaturesProxyModel); + ui->zignaturesTreeView->sortByColumn(ZignaturesModel::OffsetColumn, Qt::AscendingOrder); + + setScrollMode(); + + connect(Core(), &CutterCore::refreshAll, this, &ZignaturesWidget::refreshZignatures); +} + +ZignaturesWidget::~ZignaturesWidget() {} + +void ZignaturesWidget::refreshZignatures() +{ + zignaturesModel->beginReloadZignatures(); + zignatures = Core()->getAllZignatures(); + zignaturesModel->endReloadZignatures(); + + ui->zignaturesTreeView->resizeColumnToContents(0); + ui->zignaturesTreeView->resizeColumnToContents(1); + ui->zignaturesTreeView->resizeColumnToContents(2); +} + +void ZignaturesWidget::setScrollMode() +{ + qhelpers::setVerticalScrollMode(ui->zignaturesTreeView); +} + +void ZignaturesWidget::on_zignaturesTreeView_doubleClicked(const QModelIndex &index) +{ + ZignatureDescription item = index.data(ZignaturesModel::ZignatureDescriptionRole).value(); + Core()->seek(item.offset); +} diff --git a/src/widgets/ZignaturesWidget.h b/src/widgets/ZignaturesWidget.h new file mode 100644 index 00000000..cb357407 --- /dev/null +++ b/src/widgets/ZignaturesWidget.h @@ -0,0 +1,83 @@ +#ifndef ZIGNATURESWIDGET_H +#define ZIGNATURESWIDGET_H + +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +#include +#include + +class MainWindow; +class QTreeWidget; +class QTreeWidgetItem; + +namespace Ui +{ + class ZignaturesWidget; +} + +class ZignaturesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount }; + enum Role { ZignatureDescriptionRole = Qt::UserRole }; + + ZignaturesModel(QList *zignatures, QObject *parent = 0); + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + void beginReloadZignatures(); + void endReloadZignatures(); + +private: + QList *zignatures; +}; + + + +class ZignaturesProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ZignaturesProxyModel(ZignaturesModel *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 ZignaturesWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit ZignaturesWidget(MainWindow *main, QAction *action = nullptr); + ~ZignaturesWidget(); + +private slots: + void on_zignaturesTreeView_doubleClicked(const QModelIndex &index); + + void refreshZignatures(); + +private: + std::unique_ptr ui; + + ZignaturesModel *zignaturesModel; + ZignaturesProxyModel *zignaturesProxyModel; + QList zignatures; + + void setScrollMode(); +}; + +#endif // ZIGNATURESWIDGET_H diff --git a/src/widgets/ZignaturesWidget.ui b/src/widgets/ZignaturesWidget.ui new file mode 100644 index 00000000..e9280792 --- /dev/null +++ b/src/widgets/ZignaturesWidget.ui @@ -0,0 +1,70 @@ + + + ZignaturesWidget + + + + 0 + 0 + 400 + 300 + + + + Zignatures + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QTreeView::item +{ + padding-top: 1px; + padding-bottom: 1px; +} + + + QFrame::NoFrame + + + 0 + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + true + + + QAbstractItemView::ScrollPerPixel + + + 8 + + + true + + + + + + + + +