From 342fe7788694e9e74964831a913921bddba29850 Mon Sep 17 00:00:00 2001 From: fcasal Date: Sun, 1 Jul 2018 22:29:38 +0100 Subject: [PATCH] Added emulation support (#553) * emulation start button * add emul continue until syscall * reopen file after stopping debug * show debug context menu only when debugging * updated r2 * Attach to running process * fix bps issue in debug and attach mode * renamed signal and added attach icon --- src/Cutter.cpp | 143 +++++++++++++++++---- src/Cutter.h | 14 +- src/Cutter.pro | 9 +- src/dialogs/AttachProcDialog.cpp | 178 ++++++++++++++++++++++++++ src/dialogs/AttachProcDialog.h | 82 ++++++++++++ src/dialogs/AttachProcDialog.ui | 116 +++++++++++++++++ src/img/icons/play_light_attach.svg | 5 + src/img/icons/play_light_debug.svg | 5 + src/img/icons/play_light_emul.svg | 5 + src/menus/DisassemblyContextMenu.cpp | 10 +- src/resources.qrc | 4 +- src/widgets/BreakpointWidget.cpp | 2 +- src/widgets/DebugToolbar.cpp | 69 +++++++++- src/widgets/DebugToolbar.h | 5 +- src/widgets/DisassemblerGraphView.cpp | 2 +- src/widgets/DisassemblyWidget.cpp | 2 +- 16 files changed, 607 insertions(+), 44 deletions(-) create mode 100644 src/dialogs/AttachProcDialog.cpp create mode 100644 src/dialogs/AttachProcDialog.h create mode 100644 src/dialogs/AttachProcDialog.ui create mode 100644 src/img/icons/play_light_attach.svg create mode 100644 src/img/icons/play_light_debug.svg create mode 100644 src/img/icons/play_light_emul.svg diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 6fe8325a..322e0740 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -777,65 +777,137 @@ void CutterCore::startDebug() cmd("ood"); emit registersChanged(); if (!currentlyDebugging) { + setConfig("asm.flags", false); emit changeDebugView(); + emit flagsChanged(); + emit refreshCodeViews(); + currentlyDebugging = true; + } +} + +void CutterCore::startEmulation() +{ + if (!currentlyDebugging) { + offsetPriorDebugging = getOffset(); + } + // clear registers, init esil state, stack, progcounter at current seek + cmd("ar0; aei; aeim; aeip"); + emit registersChanged(); + if (!currentlyDebugging || !currentlyEmulating) { + // prevent register flags from appearing during debug/emul + setConfig("asm.flags", false); + // allows to view self-modifying code changes or other binary changes + setConfig("io.cache", true); + emit changeDebugView(); + emit flagsChanged(); + emit refreshCodeViews(); + currentlyDebugging = true; + currentlyEmulating = true; + } +} + +void CutterCore::attachDebug(int pid) +{ + if (!currentlyDebugging) { + offsetPriorDebugging = getOffset(); + } + // attach to process with dbg plugin + cmd("o-*; e cfg.debug = true; o+ dbg://" + QString::number(pid)); + QString programCounterValue = cmd("dr?`drn PC`").trimmed(); + seek(programCounterValue); + emit registersChanged(); + if (!currentlyDebugging || !currentlyEmulating) { + // prevent register flags from appearing during debug/emul + setConfig("asm.flags", false); + emit changeDebugView(); + emit flagsChanged(); currentlyDebugging = true; } } void CutterCore::stopDebug() { - // @TODO should first obtain correct signal to send. - // Also, we do a ds since otherwise the process does not die. if (currentlyDebugging) { - cmd("dk 9; ds; e cfg.debug = false; oo"); + if (currentlyEmulating) { + cmd("aeim-; aei-; wcr; .ar-"); + currentlyEmulating = false; + } else { + // we do a ds since otherwise the process does not die. + cmd("dk 9; ds; oo; .ar-"); + } seek(offsetPriorDebugging); - emit changeDefinedView(); + setConfig("asm.flags", true); + setConfig("io.cache", false); currentlyDebugging = false; + emit changeDefinedView(); + emit flagsChanged(); } } void CutterCore::continueDebug() { - cmd("dc"); - emit registersChanged(); + if (currentlyDebugging) { + cmd("dc"); + emit registersChanged(); + emit refreshCodeViews(); + } } void CutterCore::continueUntilDebug(QString offset) { - cmd("dcu " + offset); - emit registersChanged(); + if (currentlyDebugging) { + if (!currentlyEmulating) { + cmd("dcu " + offset); + } else { + cmd("aecu " + offset); + } + emit registersChanged(); + emit refreshCodeViews(); + } } void CutterCore::continueUntilCall() { - cmd("dcc"); - QString programCounterValue = cmd("dr?`drn PC`").trimmed(); - seek(programCounterValue); - emit registersChanged(); + if (currentlyDebugging) { + cmd("dcc"); + QString programCounterValue = cmd("dr?`drn PC`").trimmed(); + seek(programCounterValue); + emit registersChanged(); + } } void CutterCore::continueUntilSyscall() { - cmd("dcs"); - QString programCounterValue = cmd("dr?`drn PC`").trimmed(); - seek(programCounterValue); - emit registersChanged(); + if (currentlyDebugging) { + if (currentlyEmulating) { + cmd("aecs"); + } else { + cmd("dcs"); + } + QString programCounterValue = cmd("dr?`drn PC`").trimmed(); + seek(programCounterValue); + emit registersChanged(); + } } void CutterCore::stepDebug() { - cmd("ds"); - QString programCounterValue = cmd("dr?`drn PC`").trimmed(); - seek(programCounterValue); - emit registersChanged(); + if (currentlyDebugging) { + cmd("ds"); + QString programCounterValue = cmd("dr?`drn PC`").trimmed(); + seek(programCounterValue); + emit registersChanged(); + } } void CutterCore::stepOverDebug() { - cmd("dso"); - QString programCounterValue = cmd("dr?`drn PC`").trimmed(); - seek(programCounterValue); - emit registersChanged(); + if (currentlyDebugging) { + cmd("dso"); + QString programCounterValue = cmd("dr?`drn PC`").trimmed(); + seek(programCounterValue); + emit registersChanged(); + } } QStringList CutterCore::getDebugPlugins() @@ -885,7 +957,7 @@ void CutterCore::delBreakpoint(RVA addr) void CutterCore::delAllBreakpoints() { cmd("db-*"); - emit deletedAllBreakpoints(); + emit refreshCodeViews(); } void CutterCore::enableBreakpoint(RVA addr) @@ -930,6 +1002,27 @@ QJsonDocument CutterCore::getBacktrace() return cmdj("dbtj"); } +QList CutterCore::getAllProcesses() +{ + QList ret; + QJsonArray ProcessArray = cmdj("dplj").array(); + + for (QJsonValue value : ProcessArray) { + QJsonObject procObject = value.toObject(); + + ProcessDescription proc; + + proc.pid = procObject["pid"].toVariant().toInt(); + proc.uid = procObject["uid"].toVariant().toInt(); + proc.status = procObject["status"].toString(); + proc.path = procObject["path"].toString(); + + ret << proc; + } + + return ret; +} + QList CutterCore::getMemoryMap() { QList ret; diff --git a/src/Cutter.h b/src/Cutter.h index 647dcbc7..5c41c7b5 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -312,6 +312,13 @@ struct BreakpointDescription { bool enabled; }; +struct ProcessDescription { + int pid; + int uid; + QString status; + QString path; +}; + Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ExportDescription) @@ -342,6 +349,7 @@ Q_DECLARE_METATYPE(SearchDescription) Q_DECLARE_METATYPE(SectionDescription) Q_DECLARE_METATYPE(MemoryMapDescription) Q_DECLARE_METATYPE(BreakpointDescription) +Q_DECLARE_METATYPE(ProcessDescription) class CutterCore: public QObject { @@ -480,6 +488,8 @@ public: QJsonDocument getStack(int size = 0x40); QJsonDocument getBacktrace(); void startDebug(); + void startEmulation(); + void attachDebug(int pid); void stopDebug(); void continueDebug(); void continueUntilCall(); @@ -497,6 +507,7 @@ public: QStringList getDebugPlugins(); void setDebugPlugin(QString plugin); bool currentlyDebugging = false; + bool currentlyEmulating = false; RVA getOffsetJump(RVA addr); QString getDecompiledCode(RVA addr); @@ -554,6 +565,7 @@ public: QList getAllSearch(QString search_for, QString space); BlockStatistics getBlockStatistics(unsigned int blocksCount); QList getBreakpoints(); + QList getAllProcesses(); QList getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType = QString::null); @@ -584,7 +596,7 @@ signals: void registersChanged(); void instructionChanged(RVA offset); void breakpointsChanged(); - void deletedAllBreakpoints(); + void refreshCodeViews(); void notesChanged(const QString ¬es); void projectSaved(const QString &name); diff --git a/src/Cutter.pro b/src/Cutter.pro index a56cfb0d..c5dc7242 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -180,7 +180,8 @@ SOURCES += \ widgets/MemoryMapWidget.cpp \ dialogs/preferences/DebugOptionsWidget.cpp \ widgets/BreakpointWidget.cpp \ - dialogs/BreakpointsDialog.cpp + dialogs/BreakpointsDialog.cpp \ + dialogs/AttachProcDialog.cpp HEADERS += \ Cutter.h \ @@ -272,7 +273,8 @@ HEADERS += \ widgets/MemoryMapWidget.h \ dialogs/preferences/DebugOptionsWidget.h \ widgets/BreakpointWidget.h \ - dialogs/BreakpointsDialog.h + dialogs/BreakpointsDialog.h \ + dialogs/AttachProcDialog.h FORMS += \ dialogs/AboutDialog.ui \ @@ -325,7 +327,8 @@ FORMS += \ widgets/MemoryMapWidget.ui \ dialogs/preferences/DebugOptionsWidget.ui \ widgets/BreakpointWidget.ui \ - dialogs/BreakpointsDialog.ui + dialogs/BreakpointsDialog.ui \ + dialogs/AttachProcDialog.ui RESOURCES += \ resources.qrc \ diff --git a/src/dialogs/AttachProcDialog.cpp b/src/dialogs/AttachProcDialog.cpp new file mode 100644 index 00000000..8a2c1f42 --- /dev/null +++ b/src/dialogs/AttachProcDialog.cpp @@ -0,0 +1,178 @@ +#include "MainWindow.h" +#include "Cutter.h" +#include "AttachProcDialog.h" +#include "ui_AttachProcDialog.h" + +#include "utils/Helpers.h" + +ProcessModel::ProcessModel(QList *processes, QObject *parent) + : QAbstractListModel(parent), + processes(processes) +{ +} + +int ProcessModel::rowCount(const QModelIndex &) const +{ + return processes->count(); +} + +int ProcessModel::columnCount(const QModelIndex &) const +{ + return ProcessModel::ColumnCount; +} + +QVariant ProcessModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= processes->count()) + return QVariant(); + + const ProcessDescription &proc = processes->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case PidColumn: + return proc.pid; + case UidColumn: + return proc.uid; + case StatusColumn: + return proc.status; + case PathColumn: + return proc.path; + default: + return QVariant(); + } + case ProcDescriptionRole: + return QVariant::fromValue(proc); + default: + return QVariant(); + } +} + +QVariant ProcessModel::headerData(int section, Qt::Orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (section) { + case PidColumn: + return tr("PID"); + case UidColumn: + return tr("UID"); + case StatusColumn: + return tr("Status"); + case PathColumn: + return tr("Path"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +void ProcessModel::beginReloadProcess() +{ + beginResetModel(); +} + +void ProcessModel::endReloadProcess() +{ + endResetModel(); +} + +ProcessProxyModel::ProcessProxyModel(ProcessModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSourceModel(sourceModel); +} + +bool ProcessProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + QModelIndex index = sourceModel()->index(row, 0, parent); + ProcessDescription item = index.data(ProcessModel::ProcDescriptionRole).value(); + return item.path.contains(filterRegExp()); +} + +bool ProcessProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + ProcessDescription leftProc = left.data(ProcessModel::ProcDescriptionRole).value(); + ProcessDescription rightProc = right.data(ProcessModel::ProcDescriptionRole).value(); + + switch (left.column()) { + case ProcessModel::PidColumn: + return leftProc.pid < rightProc.pid; + case ProcessModel::UidColumn: + return leftProc.uid < rightProc.uid; + case ProcessModel::StatusColumn: + return leftProc.status < rightProc.status; + case ProcessModel::PathColumn: + return leftProc.path < rightProc.path; + default: + break; + } + + return leftProc.pid < rightProc.pid; +} + +AttachProcDialog::AttachProcDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AttachProcDialog) +{ + ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + + processes = Core()->getAllProcesses(); + processModel = new ProcessModel(&processes, this); + processProxyModel = new ProcessProxyModel(processModel, this); + ui->procTreeView->setModel(processProxyModel); + ui->procTreeView->sortByColumn(ProcessModel::PidColumn, Qt::AscendingOrder); + connect(ui->filterLineEdit, SIGNAL(textChanged(const QString &)), processProxyModel, + SLOT(setFilterWildcard(const QString &))); + qhelpers::setVerticalScrollMode(ui->procTreeView); + + // focus on filter line + ui->filterLineEdit->setFocus(); + // Event filter for capturing Ctrl/Cmd+Return + ui->filterLineEdit->installEventFilter(this); +} + +AttachProcDialog::~AttachProcDialog() {} + +void AttachProcDialog::on_buttonBox_accepted() +{ +} + +void AttachProcDialog::on_buttonBox_rejected() +{ + close(); +} + +int AttachProcDialog::getPID() +{ + ProcessDescription proc = ui->procTreeView->selectionModel()->currentIndex().data( + ProcessModel::ProcDescriptionRole).value(); + return proc.pid; +} + +bool AttachProcDialog::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj); + if (event -> type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast (event); + + // Confirm comment by pressing Ctrl/Cmd+Return + if ((keyEvent -> modifiers() & Qt::ControlModifier) && + ((keyEvent -> key() == Qt::Key_Enter) || (keyEvent -> key() == Qt::Key_Return))) { + this->accept(); + return true; + } + } + + return false; +} + +void AttachProcDialog::on_procTreeView_doubleClicked(const QModelIndex &index) +{ + ProcessDescription proc = index.data(ProcessModel::ProcDescriptionRole).value(); + accept(); +} \ No newline at end of file diff --git a/src/dialogs/AttachProcDialog.h b/src/dialogs/AttachProcDialog.h new file mode 100644 index 00000000..28123a99 --- /dev/null +++ b/src/dialogs/AttachProcDialog.h @@ -0,0 +1,82 @@ +#pragma once + +#include "Cutter.h" +#include +#include +#include +#include + +namespace Ui { +class AttachProcDialog; +} + +class MainWindow; +class QTreeWidget; +class QTreeWidgetItem; + + +class ProcessModel: public QAbstractListModel +{ + Q_OBJECT + +private: + QList *processes; + +public: + enum Column { PidColumn = 0, UidColumn, StatusColumn, PathColumn, ColumnCount }; + enum Role { ProcDescriptionRole = Qt::UserRole }; + + ProcessModel(QList *processes, 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 beginReloadProcess(); + void endReloadProcess(); +}; + + + +class ProcessProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ProcessProxyModel(ProcessModel *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 AttachProcDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AttachProcDialog(QWidget *parent = nullptr); + ~AttachProcDialog(); + + int getPID(); + +private slots: + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + void on_procTreeView_doubleClicked(const QModelIndex &index); + +signals: + void attachProcess(int pid); +private: + std::unique_ptr ui; + bool eventFilter(QObject *obj, QEvent *event); + + ProcessModel *processModel; + ProcessProxyModel *processProxyModel; + QList processes; + +}; diff --git a/src/dialogs/AttachProcDialog.ui b/src/dialogs/AttachProcDialog.ui new file mode 100644 index 00000000..be5980af --- /dev/null +++ b/src/dialogs/AttachProcDialog.ui @@ -0,0 +1,116 @@ + + + AttachProcDialog + + + + 0 + 0 + 800 + 600 + + + + Select process to attach... + + + + 2 + + + 2 + + + 5 + + + 2 + + + 2 + + + + + 0 + + + + + QTreeView::item +{ + padding-top: 1px; + padding-bottom: 1px; +} + + + QFrame::NoFrame + + + 0 + + + 8 + + + true + + + + + + + + + Quick Filter + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AttachProcDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AttachProcDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/img/icons/play_light_attach.svg b/src/img/icons/play_light_attach.svg new file mode 100644 index 00000000..b3bd9944 --- /dev/null +++ b/src/img/icons/play_light_attach.svg @@ -0,0 +1,5 @@ + + + + A + diff --git a/src/img/icons/play_light_debug.svg b/src/img/icons/play_light_debug.svg new file mode 100644 index 00000000..6a387a1c --- /dev/null +++ b/src/img/icons/play_light_debug.svg @@ -0,0 +1,5 @@ + + + + D + diff --git a/src/img/icons/play_light_emul.svg b/src/img/icons/play_light_emul.svg new file mode 100644 index 00000000..174bca74 --- /dev/null +++ b/src/img/icons/play_light_emul.svg @@ -0,0 +1,5 @@ + + + + E + diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 76aec277..9d65ab73 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -237,10 +237,12 @@ void DisassemblyContextMenu::aboutToShowSlot() // decide to show Reverse jmp option showReverseJmpQuery(); - // show debug options - // @TODO determine if we are being debugged and only show the menu in those cases - // maybe using dpt command - debugMenuAction->setVisible(true); + // only show debug options if we are currently debugging + debugMenuAction->setVisible(Core()->currentlyDebugging); + // currently there are is no breakpoint support in ESIL so + // we dont show the option in case we are emulating + actionAddBreakpoint.setVisible(!Core()->currentlyEmulating); + } QKeySequence DisassemblyContextMenu::getCopySequence() const diff --git a/src/resources.qrc b/src/resources.qrc index e5d9ba16..37240963 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -19,7 +19,9 @@ img/icons/spin.svg img/icons/plus.svg img/icons/play.svg - img/icons/play_light.svg + img/icons/play_light_debug.svg + img/icons/play_light_emul.svg + img/icons/play_light_attach.svg img/icons/media-stop_light.svg img/icons/media-skip-forward_light.svg img/icons/continue_until_main.svg diff --git a/src/widgets/BreakpointWidget.cpp b/src/widgets/BreakpointWidget.cpp index 7023fa55..ca8ebc8b 100644 --- a/src/widgets/BreakpointWidget.cpp +++ b/src/widgets/BreakpointWidget.cpp @@ -136,7 +136,7 @@ BreakpointWidget::BreakpointWidget(MainWindow *main, QAction *action) : connect(actionToggleBreakpoint, &QAction::triggered, this, &BreakpointWidget::toggleBreakpoint); connect(Core(), &CutterCore::refreshAll, this, &BreakpointWidget::refreshBreakpoint); connect(Core(), &CutterCore::breakpointsChanged, this, &BreakpointWidget::refreshBreakpoint); - connect(Core(), &CutterCore::deletedAllBreakpoints, this, &BreakpointWidget::refreshBreakpoint); + connect(Core(), &CutterCore::refreshCodeViews, this, &BreakpointWidget::refreshBreakpoint); connect(ui->addBreakpoint, &QAbstractButton::clicked, this, &BreakpointWidget::addBreakpointDialog); connect(ui->delBreakpoint, &QAbstractButton::clicked, this, &BreakpointWidget::delBreakpoint); connect(ui->delAllBreakpoints, &QAbstractButton::clicked, Core(), &CutterCore::delAllBreakpoints); diff --git a/src/widgets/DebugToolbar.cpp b/src/widgets/DebugToolbar.cpp index 6ceeef73..d8679970 100644 --- a/src/widgets/DebugToolbar.cpp +++ b/src/widgets/DebugToolbar.cpp @@ -1,5 +1,6 @@ #include "DebugToolbar.h" #include "MainWindow.h" +#include "dialogs/AttachProcDialog.cpp" #include #include @@ -11,7 +12,9 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) : main(main) { setObjectName("debugToolbar"); - QIcon startIcon = QIcon(":/img/icons/play_light.svg"); + QIcon startDebugIcon = QIcon(":/img/icons/play_light_debug.svg"); + QIcon startEmulIcon = QIcon(":/img/icons/play_light_emul.svg"); + QIcon startAttachIcon = QIcon(":/img/icons/play_light_attach.svg"); QIcon stopIcon = QIcon(":/img/icons/media-stop_light.svg"); QIcon continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg"); QIcon continueUntilMainIcon = QIcon(":/img/icons/continue_until_main.svg"); @@ -20,7 +23,9 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) : QIcon stepIcon = QIcon(":/img/icons/step_light.svg"); QIcon stepOverIcon = QIcon(":/img/icons/step_over_light.svg"); - QAction *actionStart = new QAction(startIcon, tr("Start debug"), parent); + actionStart = new QAction(startDebugIcon, tr("Start debug"), parent); + actionStartEmul = new QAction(startEmulIcon, tr("Start emulation"), parent); + QAction *actionAttach = new QAction(startAttachIcon, tr("Attach to process"), parent); QAction *actionStop = new QAction(stopIcon, tr("Stop debug"), parent); QAction *actionContinue = new QAction(continueIcon, tr("Continue"), parent); QAction *actionContinueUntilMain = new QAction(continueUntilMainIcon, tr("Continue until main"), parent); @@ -29,10 +34,19 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) : QAction *actionStep = new QAction(stepIcon, tr("Step"), parent); QAction *actionStepOver = new QAction(stepOverIcon, tr("Step over"), parent); + QToolButton *startButton = new QToolButton; + startButton->setPopupMode(QToolButton::MenuButtonPopup); + connect(startButton, &QToolButton::triggered, startButton, &QToolButton::setDefaultAction); + QMenu *startMenu = new QMenu; + startMenu->addAction(actionStart); + startMenu->addAction(actionStartEmul); + startMenu->addAction(actionAttach); + startButton->setDefaultAction(actionStart); + startButton->setMenu(startMenu); + QToolButton *continueUntilButton = new QToolButton; continueUntilButton->setPopupMode(QToolButton::MenuButtonPopup); connect(continueUntilButton, &QToolButton::triggered, continueUntilButton, &QToolButton::setDefaultAction); - QMenu *continueUntilMenu = new QMenu; continueUntilMenu->addAction(actionContinueUntilMain); continueUntilMenu->addAction(actionContinueUntilCall); @@ -40,7 +54,7 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) : continueUntilButton->setMenu(continueUntilMenu); continueUntilButton->setDefaultAction(actionContinueUntilMain); - addAction(actionStart); + addWidget(startButton); addAction(actionStop); addAction(actionContinue); addWidget(continueUntilButton); @@ -48,10 +62,33 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) : addAction(actionStepOver); connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug); - connect(actionStop, &QAction::triggered, [=](){ this->colorToolbar(false); }); + connect(actionStop, &QAction::triggered, [=](){ + actionContinue->setVisible(true); + actionStart->setVisible(true); + actionStartEmul->setVisible(true); + actionAttach->setVisible(true); + actionContinueUntilMain->setVisible(true); + actionContinueUntilCall->setVisible(true); + this->colorToolbar(false); + }); connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug); connect(actionStart, &QAction::triggered, Core(), &CutterCore::startDebug); - connect(actionStart, &QAction::triggered, [=](){ this->colorToolbar(true); }); + connect(actionStart, &QAction::triggered, [=](){ + this->colorToolbar(true); + actionAttach->setVisible(false); + actionStartEmul->setVisible(false); + }); + connect(actionAttach, &QAction::triggered, this, &DebugToolbar::attachProcessDialog); + connect(actionStartEmul, &QAction::triggered, Core(), &CutterCore::startEmulation); + connect(actionStartEmul, &QAction::triggered, [=](){ + actionContinue->setVisible(false); + actionStart->setVisible(false); + actionAttach->setVisible(false); + actionContinueUntilMain->setVisible(false); + actionContinueUntilCall->setVisible(false); + continueUntilButton->setDefaultAction(actionContinueUntilSyscall); + this->colorToolbar(true); + }); connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug); connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug); connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain); @@ -71,4 +108,24 @@ void DebugToolbar::colorToolbar(bool p) } else { this->setStyleSheet(""); } +} + +void DebugToolbar::attachProcessDialog() +{ + AttachProcDialog *dialog = new AttachProcDialog(this); + + if (dialog->exec()) { + int pid = dialog->getPID(); + attachProcess(pid); + } +} + +void DebugToolbar::attachProcess(int pid) +{ + // hide unwanted buttons + this->colorToolbar(true); + this->actionStart->setVisible(false); + this->actionStartEmul->setVisible(false); + // attach + Core()->attachDebug(pid); } \ No newline at end of file diff --git a/src/widgets/DebugToolbar.h b/src/widgets/DebugToolbar.h index b87a2deb..01a0356a 100644 --- a/src/widgets/DebugToolbar.h +++ b/src/widgets/DebugToolbar.h @@ -14,9 +14,12 @@ public: private: MainWindow *main; + QAction *actionStart; + QAction *actionStartEmul; private slots: void continueUntilMain(); void colorToolbar(bool p); - + void attachProcessDialog(); + void attachProcess(int pid); }; \ No newline at end of file diff --git a/src/widgets/DisassemblerGraphView.cpp b/src/widgets/DisassemblerGraphView.cpp index ce182601..22c45aeb 100644 --- a/src/widgets/DisassemblerGraphView.cpp +++ b/src/widgets/DisassemblerGraphView.cpp @@ -36,7 +36,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent) connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(graphOptionsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(asmOptionsChanged()), this, SLOT(refreshView())); - connect(Core(), SIGNAL(deletedAllBreakpoints()), this, SLOT(refreshView())); + connect(Core(), SIGNAL(refreshCodeViews()), this, SLOT(refreshView())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot())); diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index c583db75..8fb2483a 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -119,7 +119,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action) refreshDisasm(); } }); - connect(Core(), SIGNAL(deletedAllBreakpoints()), this, SLOT(refreshDisasm())); + connect(Core(), SIGNAL(refreshCodeViews()), this, SLOT(refreshDisasm())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));