From ac64bbface5299edc650c8537da4fdfa1c280a31 Mon Sep 17 00:00:00 2001 From: Vanellope Date: Sun, 21 Oct 2018 03:20:06 +0900 Subject: [PATCH] Segments implemented (#851) * Segments implemented * Not util anymore but common * Fixed the strings to be shown as the headers better * Quick Filter functionality is supported on both Section and Segment Widget * QuickFilter should basically be on but for some widgets, they should be off --- src/Cutter.cpp | 27 +++++ src/Cutter.h | 11 ++ src/Cutter.pro | 2 + src/MainWindow.cpp | 4 + src/MainWindow.h | 2 + src/MainWindow.ui | 12 ++ src/widgets/QuickFilterView.cpp | 6 +- src/widgets/QuickFilterView.h | 2 +- src/widgets/SectionsWidget.cpp | 33 +++++- src/widgets/SectionsWidget.h | 3 + src/widgets/SegmentsWidget.cpp | 191 ++++++++++++++++++++++++++++++++ src/widgets/SegmentsWidget.h | 72 ++++++++++++ 12 files changed, 358 insertions(+), 7 deletions(-) create mode 100644 src/widgets/SegmentsWidget.cpp create mode 100644 src/widgets/SegmentsWidget.h diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 0e04aee4..1132ee15 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -1652,6 +1652,33 @@ QList CutterCore::getAllSections() return ret; } +QList CutterCore::getAllSegments() +{ + CORE_LOCK(); + QList ret; + + QJsonArray segments = cmdj("iSSj").array(); + + for (QJsonValue value : segments) { + QJsonObject segmentObject = value.toObject(); + + QString name = segmentObject["name"].toString(); + if (name.isEmpty()) + continue; + + SegmentDescription segment; + segment.name = name; + segment.vaddr = segmentObject["vaddr"].toVariant().toULongLong(); + segment.paddr = segmentObject["paddr"].toVariant().toULongLong(); + segment.size = segmentObject["size"].toVariant().toULongLong(); + segment.vsize = segmentObject["vsize"].toVariant().toULongLong(); + segment.perm = segmentObject["perm"].toString(); + + ret << segment; + } + return ret; +} + QList CutterCore::getAllEntrypoint() { CORE_LOCK(); diff --git a/src/Cutter.h b/src/Cutter.h index 933ed673..39f76319 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -189,6 +189,15 @@ struct SectionDescription { QString entropy; }; +struct SegmentDescription { + RVA vaddr; + RVA paddr; + RVA size; + RVA vsize; + QString name; + QString perm; +}; + struct EntrypointDescription { RVA vaddr; RVA paddr; @@ -350,6 +359,7 @@ Q_DECLARE_METATYPE(HeaderDescription) Q_DECLARE_METATYPE(ZignatureDescription) Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SectionDescription) +Q_DECLARE_METATYPE(SegmentDescription) Q_DECLARE_METATYPE(MemoryMapDescription) Q_DECLARE_METATYPE(BreakpointDescription) Q_DECLARE_METATYPE(ProcessDescription) @@ -569,6 +579,7 @@ public: QList getAllFlagspaces(); QList getAllFlags(QString flagspace = NULL); QList getAllSections(); + QList getAllSegments(); QList getAllEntrypoint(); QList getAllClassesFromBin(); QList getAllClassesFromFlags(); diff --git a/src/Cutter.pro b/src/Cutter.pro index 8de8e26f..7707e927 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -138,6 +138,7 @@ SOURCES += \ widgets/RelocsWidget.cpp \ widgets/SdbDock.cpp \ widgets/SectionsWidget.cpp \ + widgets/SegmentsWidget.cpp \ widgets/Sidebar.cpp \ widgets/StringsWidget.cpp \ widgets/SymbolsWidget.cpp \ @@ -233,6 +234,7 @@ HEADERS += \ widgets/RelocsWidget.h \ widgets/SdbDock.h \ widgets/SectionsWidget.h \ + widgets/SegmentsWidget.h \ widgets/Sidebar.h \ widgets/StringsWidget.h \ widgets/SymbolsWidget.h \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 63169669..959e93c4 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -52,6 +52,7 @@ #include "widgets/GraphWidget.h" #include "widgets/FunctionsWidget.h" #include "widgets/SectionsWidget.h" +#include "widgets/SegmentsWidget.h" #include "widgets/CommentsWidget.h" #include "widgets/ImportsWidget.h" #include "widgets/ExportsWidget.h" @@ -201,6 +202,7 @@ void MainWindow::initUI() graphDock = new GraphWidget(this, ui->actionGraph); sectionsDock = new SectionsWidget(this, ui->actionSections); + segmentsDock = new SegmentsWidget(this, ui->actionSegments); entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints); functionsDock = new FunctionsWidget(this, ui->actionFunctions); importsDock = new ImportsWidget(this, ui->actionImports); @@ -573,9 +575,11 @@ void MainWindow::restoreDocks() // Console | Sections splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal); + splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal); // Tabs for center (must be applied after splitDockWidget()) tabifyDockWidget(sectionsDock, commentsDock); + tabifyDockWidget(segmentsDock, commentsDock); tabifyDockWidget(dashboardDock, disassemblyDock); tabifyDockWidget(dashboardDock, graphDock); tabifyDockWidget(dashboardDock, hexdumpDock); diff --git a/src/MainWindow.h b/src/MainWindow.h index 4a26ec3c..48670846 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -38,6 +38,7 @@ class QLineEdit; class SdbDock; class QAction; class SectionsWidget; +class SegmentsWidget; class ConsoleWidget; class EntrypointWidget; class DisassemblerGraphView; @@ -215,6 +216,7 @@ private: QLineEdit *gotoEntry = nullptr; SdbDock *sdbDock = nullptr; SectionsWidget *sectionsDock = nullptr; + SegmentsWidget *segmentsDock = nullptr; ZignaturesWidget *zignaturesDock = nullptr; ConsoleWidget *consoleDock = nullptr; ClassesWidget *classesDock = nullptr; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index adbce9df..24b3b53e 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -173,6 +173,7 @@ QToolTip { + @@ -452,6 +453,17 @@ QToolTip { Show/Hide Sections panel + + + true + + + Segments + + + Show/Hide Segments panel + + true diff --git a/src/widgets/QuickFilterView.cpp b/src/widgets/QuickFilterView.cpp index c410f30b..ff0ab050 100644 --- a/src/widgets/QuickFilterView.cpp +++ b/src/widgets/QuickFilterView.cpp @@ -2,7 +2,7 @@ #include "QuickFilterView.h" #include "ui_QuickFilterView.h" -QuickFilterView::QuickFilterView(QWidget *parent) : +QuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn) : QWidget(parent), ui(new Ui::QuickFilterView()) { @@ -13,6 +13,10 @@ QuickFilterView::QuickFilterView(QWidget *parent) : connect(ui->filterLineEdit, &QLineEdit::textChanged, this, [this](const QString & text) { emit filterTextChanged(text); }); + + if (!defaultOn) { + closeFilter(); + } } QuickFilterView::~QuickFilterView() {} diff --git a/src/widgets/QuickFilterView.h b/src/widgets/QuickFilterView.h index fd2cffd8..88097066 100644 --- a/src/widgets/QuickFilterView.h +++ b/src/widgets/QuickFilterView.h @@ -15,7 +15,7 @@ class QuickFilterView : public QWidget Q_OBJECT public: - explicit QuickFilterView(QWidget *parent = nullptr); + explicit QuickFilterView(QWidget *parent = nullptr, bool defaultOn = true); ~QuickFilterView(); public slots: diff --git a/src/widgets/SectionsWidget.cpp b/src/widgets/SectionsWidget.cpp index a212ea6a..d59c925e 100644 --- a/src/widgets/SectionsWidget.cpp +++ b/src/widgets/SectionsWidget.cpp @@ -3,6 +3,7 @@ #include "SectionsWidget.h" #include "MainWindow.h" +#include "QuickFilterView.h" #include "common/Helpers.h" SectionsModel::SectionsModel(QList *sections, QObject *parent) @@ -81,7 +82,7 @@ QVariant SectionsModel::headerData(int section, Qt::Orientation, int role) const case SectionsModel::AddressColumn: return tr("Address"); case SectionsModel::EndAddressColumn: - return tr("EndAddress"); + return tr("End Address"); case SectionsModel::EntropyColumn: return tr("Entropy"); default: @@ -135,19 +136,41 @@ SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) : auto proxyModel = new SectionsProxyModel(sectionsModel, this); sectionsTable->setModel(proxyModel); - sectionsTable->setIndentation(10); sectionsTable->setSortingEnabled(true); sectionsTable->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder); connect(sectionsTable, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(onSectionsDoubleClicked(const QModelIndex &))); - - setWidget(sectionsTable); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSections())); + quickFilterView = new QuickFilterView(this, false); + quickFilterView->setObjectName(QStringLiteral("quickFilterView")); + QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Maximum); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(quickFilterView->sizePolicy().hasHeightForWidth()); + quickFilterView->setSizePolicy(sizePolicy1); + + QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this); + connect(search_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::showFilter); + search_shortcut->setContext(Qt::WidgetWithChildrenShortcut); + + QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); + connect(clear_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::clearFilter); + clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut); + + connect(quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel, + SLOT(setFilterWildcard(const QString &))); + connect(quickFilterView, SIGNAL(filterClosed()), sectionsTable, SLOT(setFocus())); + dockWidgetContents = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(sectionsTable); + layout->addWidget(quickFilterView); + layout->setMargin(0); + dockWidgetContents->setLayout(layout); + setWidget(dockWidgetContents); } SectionsWidget::~SectionsWidget() {} diff --git a/src/widgets/SectionsWidget.h b/src/widgets/SectionsWidget.h index e6a6e5a3..4ca86b3f 100644 --- a/src/widgets/SectionsWidget.h +++ b/src/widgets/SectionsWidget.h @@ -13,6 +13,7 @@ class QTreeView; class QAbstractItemView; class MainWindow; class SectionsWidget; +class QuickFilterView; class SectionsModel : public QAbstractListModel { @@ -64,6 +65,8 @@ private: SectionsModel *sectionsModel; QTreeView *sectionsTable; MainWindow *main; + QWidget *dockWidgetContents; + QuickFilterView *quickFilterView; }; #endif // SECTIONSWIDGET_H diff --git a/src/widgets/SegmentsWidget.cpp b/src/widgets/SegmentsWidget.cpp new file mode 100644 index 00000000..1de7a921 --- /dev/null +++ b/src/widgets/SegmentsWidget.cpp @@ -0,0 +1,191 @@ +#include + +#include "SegmentsWidget.h" + +#include "MainWindow.h" +#include "QuickFilterView.h" +#include "common/Helpers.h" + +SegmentsModel::SegmentsModel(QList *segments, QObject *parent) + : QAbstractListModel(parent), + segments(segments) +{ +} + +int SegmentsModel::rowCount(const QModelIndex &) const +{ + return segments->count(); +} + +int SegmentsModel::columnCount(const QModelIndex &) const +{ + return SegmentsModel::ColumnCount; +} + +QVariant SegmentsModel::data(const QModelIndex &index, int role) const +{ + // TODO: create unique colors, e. g. use HSV color space and rotate in H for 360/size + static const QList colors = { QColor("#1ABC9C"), //TURQUOISE + QColor("#2ECC71"), //EMERALD + QColor("#3498DB"), //PETER RIVER + QColor("#9B59B6"), //AMETHYST + QColor("#34495E"), //WET ASPHALT + QColor("#F1C40F"), //SUN FLOWER + QColor("#E67E22"), //CARROT + QColor("#E74C3C"), //ALIZARIN + QColor("#ECF0F1"), //CLOUDS + QColor("#BDC3C7"), //SILVER + QColor("#95A5A6") //COBCRETE + }; + + if (index.row() >= segments->count()) + return QVariant(); + + const SegmentDescription &segment = segments->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case SegmentsModel::NameColumn: + return segment.name; + case SegmentsModel::SizeColumn: + return segment.size; + case SegmentsModel::AddressColumn: + return RAddressString(segment.vaddr); + case SegmentsModel::EndAddressColumn: + return RAddressString(segment.vaddr + segment.size); + case SegmentsModel::PermColumn: + return segment.perm; + default: + return QVariant(); + } + case Qt::DecorationRole: + if (index.column() == 0) + return colors[index.row() % colors.size()]; + return QVariant(); + case SegmentsModel::SegmentDescriptionRole: + return QVariant::fromValue(segment); + default: + return QVariant(); + } +} + +QVariant SegmentsModel::headerData(int segment, Qt::Orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (segment) { + case SegmentsModel::NameColumn: + return tr("Name"); + case SegmentsModel::SizeColumn: + return tr("Size"); + case SegmentsModel::AddressColumn: + return tr("Address"); + case SegmentsModel::EndAddressColumn: + return tr("End Address"); + case SegmentsModel::PermColumn: + return tr("Permissions"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +SegmentsProxyModel::SegmentsProxyModel(SegmentsModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSourceModel(sourceModel); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setSortCaseSensitivity(Qt::CaseInsensitive); +} + +bool SegmentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + auto leftSegment = left.data(SegmentsModel::SegmentDescriptionRole).value(); + auto rightSegment = right.data(SegmentsModel::SegmentDescriptionRole).value(); + switch (left.column()) { + case SegmentsModel::NameColumn: + return leftSegment.name < rightSegment.name; + case SegmentsModel::SizeColumn: + return leftSegment.size < rightSegment.size; + case SegmentsModel::AddressColumn: + case SegmentsModel::EndAddressColumn: + return leftSegment.vaddr < rightSegment.vaddr; + default: + break; + } + return false; +} + +SegmentsWidget::SegmentsWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + main(main) +{ + + setObjectName("SegmentsWidget"); + setWindowTitle(QStringLiteral("Segments")); + + segmentsTable = new QTreeView; + segmentsModel = new SegmentsModel(&segments, this); + auto proxyModel = new SegmentsProxyModel(segmentsModel, this); + + segmentsTable->setModel(proxyModel); + segmentsTable->setIndentation(10); + segmentsTable->setSortingEnabled(true); + segmentsTable->sortByColumn(SegmentsModel::NameColumn, Qt::AscendingOrder); + + connect(segmentsTable, SIGNAL(doubleClicked(const QModelIndex &)), + this, SLOT(onSegmentsDoubleClicked(const QModelIndex &))); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSegments())); + + quickFilterView = new QuickFilterView(this, false); + quickFilterView->setObjectName(QStringLiteral("quickFilterView")); + QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Maximum); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(quickFilterView->sizePolicy().hasHeightForWidth()); + quickFilterView->setSizePolicy(sizePolicy1); + + QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this); + connect(search_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::showFilter); + search_shortcut->setContext(Qt::WidgetWithChildrenShortcut); + + QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); + connect(clear_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::clearFilter); + clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut); + + connect(quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel, + SLOT(setFilterWildcard(const QString &))); + connect(quickFilterView, SIGNAL(filterClosed()), segmentsTable, SLOT(setFocus())); + + dockWidgetContents = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(segmentsTable); + layout->addWidget(quickFilterView); + layout->setMargin(0); + dockWidgetContents->setLayout(layout); + setWidget(dockWidgetContents); +} + +SegmentsWidget::~SegmentsWidget() {} + +void SegmentsWidget::refreshSegments() +{ + segmentsModel->beginResetModel(); + segments = Core()->getAllSegments(); + segmentsModel->endResetModel(); + + qhelpers::adjustColumns(segmentsTable, SegmentsModel::ColumnCount, 0); +} + +void SegmentsWidget::onSegmentsDoubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + auto segment = index.data(SegmentsModel::SegmentDescriptionRole).value(); + Core()->seek(segment.vaddr); +} diff --git a/src/widgets/SegmentsWidget.h b/src/widgets/SegmentsWidget.h new file mode 100644 index 00000000..ff723e3c --- /dev/null +++ b/src/widgets/SegmentsWidget.h @@ -0,0 +1,72 @@ +#ifndef SEGMENTSWIDGET_H +#define SEGMENTSWIDGET_H + +#include + +#include +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +class QTreeView; +class QAbstractItemView; +class MainWindow; +class SegmentsWidget; +class QuickFilterView; + +class SegmentsModel : public QAbstractListModel +{ + Q_OBJECT + + friend SegmentsWidget; + +private: + QList *segments; + +public: + enum Column { NameColumn = 0, SizeColumn, AddressColumn, EndAddressColumn, PermColumn, ColumnCount }; + enum Role { SegmentDescriptionRole = Qt::UserRole }; + + SegmentsModel(QList *segments, QObject *parent = nullptr); + + 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 segment, Qt::Orientation orientation, int role = Qt::DisplayRole) const; +}; + +class SegmentsProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + SegmentsProxyModel(SegmentsModel *sourceModel, QObject *parent = nullptr); + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; +}; + +class SegmentsWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit SegmentsWidget(MainWindow *main, QAction *action = nullptr); + ~SegmentsWidget(); + +private slots: + void refreshSegments(); + void onSegmentsDoubleClicked(const QModelIndex &index); + +private: + QList segments; + SegmentsModel *segmentsModel; + QTreeView *segmentsTable; + MainWindow *main; + QWidget *dockWidgetContents; + QuickFilterView *quickFilterView; +}; + +#endif // SEGMENTSWIDGET_H