From e8b024042b36e4c7559a658041e8ce0b3d7275a7 Mon Sep 17 00:00:00 2001 From: fcasal Date: Wed, 6 Jun 2018 12:05:20 +0100 Subject: [PATCH] Added widget to view register values (#509) * Added backtrace info * Make stack printing arch agnostic * Split into registers/stack/backtrace widgets * hide table idxs in stacktable * backtrace table header has register name instead of generic PC/SP * imports cleanup * add sorting to stack table * add Qheaderview import * Added button to set registers * added color to recently changed register * reorganized Windows tab and added stacking to debug widgets --- src/Cutter.cpp | 25 ++++++++ src/Cutter.h | 5 ++ src/Cutter.pro | 15 ++++- src/MainWindow.cpp | 8 +++ src/MainWindow.h | 6 ++ src/MainWindow.ui | 71 +++++++++++++++++----- src/widgets/BacktraceWidget.cpp | 63 +++++++++++++++++++ src/widgets/BacktraceWidget.h | 33 ++++++++++ src/widgets/BacktraceWidget.ui | 35 +++++++++++ src/widgets/RegistersWidget.cpp | 103 ++++++++++++++++++++++++++++++++ src/widgets/RegistersWidget.h | 37 ++++++++++++ src/widgets/RegistersWidget.ui | 35 +++++++++++ src/widgets/StackWidget.cpp | 58 ++++++++++++++++++ src/widgets/StackWidget.h | 33 ++++++++++ src/widgets/StackWidget.ui | 35 +++++++++++ 15 files changed, 543 insertions(+), 19 deletions(-) create mode 100644 src/widgets/BacktraceWidget.cpp create mode 100644 src/widgets/BacktraceWidget.h create mode 100644 src/widgets/BacktraceWidget.ui create mode 100644 src/widgets/RegistersWidget.cpp create mode 100644 src/widgets/RegistersWidget.h create mode 100644 src/widgets/RegistersWidget.ui create mode 100644 src/widgets/StackWidget.cpp create mode 100644 src/widgets/StackWidget.h create mode 100644 src/widgets/StackWidget.ui diff --git a/src/Cutter.cpp b/src/Cutter.cpp index df04012b..cacdb9aa 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -689,6 +689,31 @@ QJsonDocument CutterCore::getSignatureInfo() return cmdj("iCj"); } +QJsonDocument CutterCore::getStack(int size) +{ + return cmdj("pxrj " + QString::number(size) + " @ r:SP"); +} + +QJsonDocument CutterCore::getRegisterValues() +{ + return cmdj("drj"); +} + +QString CutterCore::getRegisterName(QString registerRole) +{ + return cmd("drn " + registerRole).trimmed(); +} + +void CutterCore::setRegister(QString regName, QString regValue) +{ + cmd("dr " + regName + "=" + regValue); +} + +QJsonDocument CutterCore::getBacktrace() +{ + return cmdj("dbtj"); +} + QStringList CutterCore::getStats() { QStringList stats; diff --git a/src/Cutter.h b/src/Cutter.h index 3ec84b2e..cf14af27 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -414,6 +414,11 @@ public: QList> get_exec_sections(); QString getOffsetInfo(QString addr); QJsonDocument getRegistersInfo(); + QJsonDocument getRegisterValues(); + QString getRegisterName(QString registerRole); + void setRegister(QString regName, QString regValue); + QJsonDocument getStack(int size = 0x40); + QJsonDocument getBacktrace(); RVA getOffsetJump(RVA addr); QString getDecompiledCode(RVA addr); QString getDecompiledCode(QString addr); diff --git a/src/Cutter.pro b/src/Cutter.pro index a00ea74d..660bfbd5 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -162,7 +162,10 @@ SOURCES += \ dialogs/VersionInfoDialog.cpp \ widgets/ZignaturesWidget.cpp \ utils/AsyncTask.cpp \ - dialogs/AsyncTaskDialog.cpp + dialogs/AsyncTaskDialog.cpp \ + widgets/StackWidget.cpp \ + widgets/RegistersWidget.cpp \ + widgets/BacktraceWidget.cpp HEADERS += \ Cutter.h \ @@ -239,7 +242,10 @@ HEADERS += \ dialogs/VersionInfoDialog.h \ widgets/ZignaturesWidget.h \ utils/AsyncTask.h \ - dialogs/AsyncTaskDialog.h + dialogs/AsyncTaskDialog.h \ + widgets/StackWidget.h \ + widgets/RegistersWidget.h \ + widgets/BacktraceWidget.h FORMS += \ dialogs/AboutDialog.ui \ @@ -283,7 +289,10 @@ FORMS += \ dialogs/R2PluginsDialog.ui \ dialogs/VersionInfoDialog.ui \ widgets/ZignaturesWidget.ui \ - dialogs/AsyncTaskDialog.ui + dialogs/AsyncTaskDialog.ui \ + widgets/StackWidget.ui \ + widgets/RegistersWidget.ui \ + widgets/BacktraceWidget.ui RESOURCES += \ resources.qrc \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index f8e6c704..5339bd11 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -178,6 +178,9 @@ void MainWindow::initUI() commentsDock = new CommentsWidget(this, ui->actionComments); stringsDock = new StringsWidget(this, ui->actionStrings); flagsDock = new FlagsWidget(this, ui->actionFlags); + stackDock = new StackWidget(this, ui->actionStack); + backtraceDock = new BacktraceWidget(this, ui->actionBacktrace); + registersDock = new RegistersWidget(this, ui->actionRegisters); #ifdef CUTTER_ENABLE_JUPYTER jupyterDock = new JupyterWidget(this, ui->actionJupyter); #else @@ -497,6 +500,11 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, vTablesDock); + + // Add Stack, Registers and Backtrace vertically stacked + addExtraWidget(stackDock); + splitDockWidget(stackDock, registersDock, Qt::Vertical); + splitDockWidget(stackDock, backtraceDock, Qt::Vertical); #ifdef CUTTER_ENABLE_JUPYTER tabifyDockWidget(dashboardDock, jupyterDock); #endif diff --git a/src/MainWindow.h b/src/MainWindow.h index 2bd13bee..4054959d 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -6,6 +6,9 @@ #include "Cutter.h" // only needed for ut64 #include "widgets/DisassemblyWidget.h" #include "widgets/SidebarWidget.h" +#include "widgets/StackWidget.h" +#include "widgets/RegistersWidget.h" +#include "widgets/BacktraceWidget.h" #include "widgets/HexdumpWidget.h" #include "widgets/PseudocodeWidget.h" #include "dialogs/NewFileDialog.h" @@ -211,6 +214,9 @@ private: DisassemblerGraphView *graphView = nullptr; QDockWidget *asmDock = nullptr; QDockWidget *calcDock = nullptr; + QDockWidget *stackDock = nullptr; + QDockWidget *registersDock = nullptr; + QDockWidget *backtraceDock = nullptr; NewFileDialog *newFileDialog = nullptr; #ifdef CUTTER_ENABLE_JUPYTER JupyterWidget *jupyterDock = nullptr; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index d22c22e2..e5c1f250 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -223,6 +223,33 @@ border-top: 0px; + + + Debug... + + + + + + + + Info... + + + + + + + + + + + + + + + + Windows @@ -233,26 +260,14 @@ border-top: 0px; + - - - - - - - - - - + - - - - - - + + @@ -1015,6 +1030,30 @@ background-color: palette(dark); Console + + + true + + + Stack + + + + + true + + + Registers + + + + + true + + + Backtrace + + true diff --git a/src/widgets/BacktraceWidget.cpp b/src/widgets/BacktraceWidget.cpp new file mode 100644 index 00000000..4c75c5af --- /dev/null +++ b/src/widgets/BacktraceWidget.cpp @@ -0,0 +1,63 @@ +#include "BacktraceWidget.h" +#include "ui_BacktraceWidget.h" +#include "utils/JsonModel.h" + +#include "MainWindow.h" + +BacktraceWidget::BacktraceWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + ui(new Ui::BacktraceWidget) +{ + ui->setupUi(this); + + // setup backtrace model + QString PC = Core()->getRegisterName("PC"); + QString SP = Core()->getRegisterName("SP"); + modelBacktrace->setHorizontalHeaderItem(0, new QStandardItem(PC)); + modelBacktrace->setHorizontalHeaderItem(1, new QStandardItem(SP)); + modelBacktrace->setHorizontalHeaderItem(2, new QStandardItem(tr("Frame Size"))); + modelBacktrace->setHorizontalHeaderItem(3, new QStandardItem(tr("Func Name"))); + modelBacktrace->setHorizontalHeaderItem(4, new QStandardItem(tr("Description"))); + viewBacktrace->setStyleSheet("QTableView {font-family: mono}"); + viewBacktrace->setModel(modelBacktrace); + ui->verticalLayout->addWidget(viewBacktrace); + + connect(Core(), &CutterCore::refreshAll, this, &BacktraceWidget::updateContents); + connect(Core(), &CutterCore::seekChanged, this, &BacktraceWidget::updateContents); +} + +BacktraceWidget::~BacktraceWidget() {} + +void BacktraceWidget::updateContents() +{ + setBacktraceGrid(); +} + +void BacktraceWidget::setBacktraceGrid() +{ + QJsonArray backtraceValues = Core()->getBacktrace().array(); + int i = 0; + for (QJsonValueRef value : backtraceValues) { + QJsonObject backtraceItem = value.toObject(); + QString progCounter = RAddressString(backtraceItem["pc"].toVariant().toULongLong()); + QString stackPointer = RAddressString(backtraceItem["sp"].toVariant().toULongLong()); + int frameSize = backtraceItem["frame_size"].toInt(); + QString funcName = backtraceItem["fname"].toString(); + QString desc = backtraceItem["desc"].toString(); + + QStandardItem *rowPC = new QStandardItem(progCounter); + QStandardItem *rowSP = new QStandardItem(stackPointer); + QStandardItem *rowFrameSize = new QStandardItem(frameSize); + QStandardItem *rowFuncName = new QStandardItem(funcName); + QStandardItem *rowDesc = new QStandardItem(desc); + + modelBacktrace->setItem(i, 0, rowPC); + modelBacktrace->setItem(i, 1, rowSP); + modelBacktrace->setItem(i, 2, rowFrameSize); + modelBacktrace->setItem(i, 3, rowFuncName); + modelBacktrace->setItem(i, 4, rowDesc); + i++; + } + viewBacktrace->setModel(modelBacktrace); + viewBacktrace->resizeColumnsToContents();; +} diff --git a/src/widgets/BacktraceWidget.h b/src/widgets/BacktraceWidget.h new file mode 100644 index 00000000..512663cb --- /dev/null +++ b/src/widgets/BacktraceWidget.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +class MainWindow; + +namespace Ui { +class BacktraceWidget; +} + +class BacktraceWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit BacktraceWidget(MainWindow *main, QAction *action = nullptr); + ~BacktraceWidget(); + +private slots: + void updateContents(); + void setBacktraceGrid(); + +private: + std::unique_ptr ui; + QStandardItemModel *modelBacktrace = new QStandardItemModel(1, 5, this); + QTableView *viewBacktrace = new QTableView; +}; \ No newline at end of file diff --git a/src/widgets/BacktraceWidget.ui b/src/widgets/BacktraceWidget.ui new file mode 100644 index 00000000..1bc65a42 --- /dev/null +++ b/src/widgets/BacktraceWidget.ui @@ -0,0 +1,35 @@ + + + BacktraceWidget + + + + 0 + 0 + 463 + 300 + + + + Backtrace + + + + + 10 + + + 0 + + + 0 + + + 0 + + + + + + + diff --git a/src/widgets/RegistersWidget.cpp b/src/widgets/RegistersWidget.cpp new file mode 100644 index 00000000..7d13633a --- /dev/null +++ b/src/widgets/RegistersWidget.cpp @@ -0,0 +1,103 @@ +#include "RegistersWidget.h" +#include "ui_RegistersWidget.h" +#include "utils/JsonModel.h" + +#include "MainWindow.h" +#include "QPushButton" + +RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + ui(new Ui::RegistersWidget) +{ + ui->setupUi(this); + + // setup register layout + ui->verticalLayout->addLayout(registerLayout); + + buttonSetRegisters = new QPushButton("Set registers", this); + connect(buttonSetRegisters, &QPushButton::clicked, this, &RegistersWidget::handleButton); + + ui->verticalLayout->addWidget(buttonSetRegisters); + connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents); + connect(Core(), &CutterCore::seekChanged, this, &RegistersWidget::updateContents); +} + +RegistersWidget::~RegistersWidget() {} + +void RegistersWidget::updateContents() +{ + setRegisterGrid(); +} + + void RegistersWidget::handleButton() + { + int j = 0; + int i = 0; + int col = 0; + for (j = 0; jitemAtPosition(i, col)->widget(); + QWidget *regValue = registerLayout->itemAtPosition(i, col + 1)->widget(); + QLabel *regLabel = qobject_cast(regName); + QLineEdit *regLine = qobject_cast(regValue); + QString regNameString = regLabel->text(); + QString regValueString = regLine->text(); + Core()->setRegister(regNameString, regValueString); + i++; + if (i >= registerLen/numCols + 1) { + i = 0; + col += 2; + } + } + setRegisterGrid(); + } + +void RegistersWidget::setRegisterGrid() +{ + int i = 0; + int col = 0; + QString regValue; + QLabel *registerLabel; + QLineEdit *registerEditValue; + QJsonObject registerValues = Core()->getRegisterValues().object(); + QStringList registerNames = registerValues.keys(); + registerLen = registerNames.size(); + for (const QString &key : registerNames) { + regValue = RAddressString(registerValues[key].toVariant().toULongLong()); + // check if we already filled this grid space with label/value + if (!registerLayout->itemAtPosition(i, col)) { + registerLabel = new QLabel; + registerLabel->setStyleSheet("font-weight: bold; font-family: mono"); + registerEditValue = new QLineEdit; + QFont font = registerEditValue->font(); + font.setWeight(QFont::Monospace); + registerEditValue->setFont(font); + // add label and register value to grid + registerLayout->addWidget(registerLabel, i, col); + registerLayout->addWidget(registerEditValue, i, col + 1); + } + else { + QWidget *regNameWidget = registerLayout->itemAtPosition(i, col)->widget(); + QWidget *regValueWidget = registerLayout->itemAtPosition(i, col + 1)->widget(); + registerLabel = qobject_cast(regNameWidget); + registerEditValue = qobject_cast(regValueWidget); + } + // decide to highlight QLine Box in case of change of register value + if (regValue != registerEditValue->text() && registerEditValue->text() != 0) { + registerEditValue->setStyleSheet("QLineEdit {border: 1px solid green;} QLineEdit:hover { border: 1px solid #3daee9; color: #eff0f1;}"); + } + else { + // reset stylesheet + registerEditValue->setStyleSheet(""); + } + // define register label and value + registerLabel->setText(key); + registerEditValue->setPlaceholderText(regValue); + registerEditValue->setText(regValue); + i++; + // decide if we should change column + if (i >= registerLen/numCols + 1) { + i = 0; + col += 2; + } + } +} diff --git a/src/widgets/RegistersWidget.h b/src/widgets/RegistersWidget.h new file mode 100644 index 00000000..d6970d74 --- /dev/null +++ b/src/widgets/RegistersWidget.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +class MainWindow; + +namespace Ui { +class RegistersWidget; +} + +class RegistersWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit RegistersWidget(MainWindow *main, QAction *action = nullptr); + ~RegistersWidget(); + +private slots: + void updateContents(); + void setRegisterGrid(); + void handleButton(); + +private: + std::unique_ptr ui; + QGridLayout *registerLayout = new QGridLayout; + int numCols = 2; + int registerLen = 0; + QPushButton *buttonSetRegisters; +}; \ No newline at end of file diff --git a/src/widgets/RegistersWidget.ui b/src/widgets/RegistersWidget.ui new file mode 100644 index 00000000..79dcd8fd --- /dev/null +++ b/src/widgets/RegistersWidget.ui @@ -0,0 +1,35 @@ + + + RegistersWidget + + + + 0 + 0 + 463 + 300 + + + + Registers + + + + + 10 + + + 0 + + + 0 + + + 0 + + + + + + + diff --git a/src/widgets/StackWidget.cpp b/src/widgets/StackWidget.cpp new file mode 100644 index 00000000..78429747 --- /dev/null +++ b/src/widgets/StackWidget.cpp @@ -0,0 +1,58 @@ +#include "StackWidget.h" +#include "ui_StackWidget.h" +#include "utils/JsonModel.h" +#include "utils/Helpers.h" + +#include "MainWindow.h" +#include "QHeaderView" + +StackWidget::StackWidget(MainWindow *main, QAction *action) : + CutterDockWidget(main, action), + ui(new Ui::StackWidget) +{ + ui->setupUi(this); + + // setup stack model + modelStack->setHorizontalHeaderItem(0, new QStandardItem(tr("Offset"))); + modelStack->setHorizontalHeaderItem(1, new QStandardItem(tr("Value"))); + modelStack->setHorizontalHeaderItem(2, new QStandardItem(tr("Reference"))); + viewStack->setStyleSheet("QTableView {font-family: mono}"); + viewStack->setModel(modelStack); + viewStack->verticalHeader()->hide(); + viewStack->setSortingEnabled(true); + ui->verticalLayout->addWidget(viewStack); + + connect(Core(), &CutterCore::refreshAll, this, &StackWidget::updateContents); + connect(Core(), &CutterCore::seekChanged, this, &StackWidget::updateContents); +} + +StackWidget::~StackWidget() {} + +void StackWidget::updateContents() +{ + setStackGrid(); +} + +void StackWidget::setStackGrid() +{ + QJsonArray stackValues = Core()->getStack().array(); + int i = 0; + for (QJsonValueRef value : stackValues) { + QJsonObject stackItem = value.toObject(); + QString addr = RAddressString(stackItem["addr"].toVariant().toULongLong()); + QString valueStack = RAddressString(stackItem["value"].toVariant().toULongLong()); + QStandardItem *rowOffset = new QStandardItem(addr); + QStandardItem *rowValue = new QStandardItem(valueStack); + modelStack->setItem(i, 0, rowOffset); + modelStack->setItem(i, 1, rowValue); + QJsonValue refObject = stackItem["ref"]; + if (!refObject.isUndefined()) { // check that the key exists + QString ref = refObject.toString(); + QStandardItem *rowRef = new QStandardItem(ref); + modelStack->setItem(i, 2, rowRef); + } + i++; + } + viewStack->setModel(modelStack); + viewStack->resizeColumnsToContents();; +} diff --git a/src/widgets/StackWidget.h b/src/widgets/StackWidget.h new file mode 100644 index 00000000..a83b9317 --- /dev/null +++ b/src/widgets/StackWidget.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +#include "Cutter.h" +#include "CutterDockWidget.h" + +class MainWindow; + +namespace Ui { +class StackWidget; +} + +class StackWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + explicit StackWidget(MainWindow *main, QAction *action = nullptr); + ~StackWidget(); + +private slots: + void updateContents(); + void setStackGrid(); + +private: + std::unique_ptr ui; + QTableView *viewStack = new QTableView; + QStandardItemModel *modelStack = new QStandardItemModel(1, 3, this); +}; \ No newline at end of file diff --git a/src/widgets/StackWidget.ui b/src/widgets/StackWidget.ui new file mode 100644 index 00000000..3ed194b7 --- /dev/null +++ b/src/widgets/StackWidget.ui @@ -0,0 +1,35 @@ + + + StackWidget + + + + 0 + 0 + 463 + 300 + + + + Stack + + + + + 10 + + + 0 + + + 0 + + + 0 + + + + + + +