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
This commit is contained in:
fcasal 2018-07-01 22:29:38 +01:00 committed by xarkes
parent 975e4c4a3d
commit 342fe77886
16 changed files with 607 additions and 44 deletions

View File

@ -777,66 +777,138 @@ void CutterCore::startDebug()
cmd("ood"); cmd("ood");
emit registersChanged(); emit registersChanged();
if (!currentlyDebugging) { if (!currentlyDebugging) {
setConfig("asm.flags", false);
emit changeDebugView(); 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; currentlyDebugging = true;
} }
} }
void CutterCore::stopDebug() 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) { 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); seek(offsetPriorDebugging);
emit changeDefinedView(); setConfig("asm.flags", true);
setConfig("io.cache", false);
currentlyDebugging = false; currentlyDebugging = false;
emit changeDefinedView();
emit flagsChanged();
} }
} }
void CutterCore::continueDebug() void CutterCore::continueDebug()
{ {
if (currentlyDebugging) {
cmd("dc"); cmd("dc");
emit registersChanged(); emit registersChanged();
emit refreshCodeViews();
}
} }
void CutterCore::continueUntilDebug(QString offset) void CutterCore::continueUntilDebug(QString offset)
{ {
if (currentlyDebugging) {
if (!currentlyEmulating) {
cmd("dcu " + offset); cmd("dcu " + offset);
} else {
cmd("aecu " + offset);
}
emit registersChanged(); emit registersChanged();
emit refreshCodeViews();
}
} }
void CutterCore::continueUntilCall() void CutterCore::continueUntilCall()
{ {
if (currentlyDebugging) {
cmd("dcc"); cmd("dcc");
QString programCounterValue = cmd("dr?`drn PC`").trimmed(); QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seek(programCounterValue); seek(programCounterValue);
emit registersChanged(); emit registersChanged();
} }
}
void CutterCore::continueUntilSyscall() void CutterCore::continueUntilSyscall()
{ {
if (currentlyDebugging) {
if (currentlyEmulating) {
cmd("aecs");
} else {
cmd("dcs"); cmd("dcs");
}
QString programCounterValue = cmd("dr?`drn PC`").trimmed(); QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seek(programCounterValue); seek(programCounterValue);
emit registersChanged(); emit registersChanged();
} }
}
void CutterCore::stepDebug() void CutterCore::stepDebug()
{ {
if (currentlyDebugging) {
cmd("ds"); cmd("ds");
QString programCounterValue = cmd("dr?`drn PC`").trimmed(); QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seek(programCounterValue); seek(programCounterValue);
emit registersChanged(); emit registersChanged();
} }
}
void CutterCore::stepOverDebug() void CutterCore::stepOverDebug()
{ {
if (currentlyDebugging) {
cmd("dso"); cmd("dso");
QString programCounterValue = cmd("dr?`drn PC`").trimmed(); QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seek(programCounterValue); seek(programCounterValue);
emit registersChanged(); emit registersChanged();
} }
}
QStringList CutterCore::getDebugPlugins() QStringList CutterCore::getDebugPlugins()
{ {
@ -885,7 +957,7 @@ void CutterCore::delBreakpoint(RVA addr)
void CutterCore::delAllBreakpoints() void CutterCore::delAllBreakpoints()
{ {
cmd("db-*"); cmd("db-*");
emit deletedAllBreakpoints(); emit refreshCodeViews();
} }
void CutterCore::enableBreakpoint(RVA addr) void CutterCore::enableBreakpoint(RVA addr)
@ -930,6 +1002,27 @@ QJsonDocument CutterCore::getBacktrace()
return cmdj("dbtj"); return cmdj("dbtj");
} }
QList<ProcessDescription> CutterCore::getAllProcesses()
{
QList<ProcessDescription> 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<MemoryMapDescription> CutterCore::getMemoryMap() QList<MemoryMapDescription> CutterCore::getMemoryMap()
{ {
QList<MemoryMapDescription> ret; QList<MemoryMapDescription> ret;

View File

@ -312,6 +312,13 @@ struct BreakpointDescription {
bool enabled; bool enabled;
}; };
struct ProcessDescription {
int pid;
int uid;
QString status;
QString path;
};
Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription) Q_DECLARE_METATYPE(ExportDescription)
@ -342,6 +349,7 @@ Q_DECLARE_METATYPE(SearchDescription)
Q_DECLARE_METATYPE(SectionDescription) Q_DECLARE_METATYPE(SectionDescription)
Q_DECLARE_METATYPE(MemoryMapDescription) Q_DECLARE_METATYPE(MemoryMapDescription)
Q_DECLARE_METATYPE(BreakpointDescription) Q_DECLARE_METATYPE(BreakpointDescription)
Q_DECLARE_METATYPE(ProcessDescription)
class CutterCore: public QObject class CutterCore: public QObject
{ {
@ -480,6 +488,8 @@ public:
QJsonDocument getStack(int size = 0x40); QJsonDocument getStack(int size = 0x40);
QJsonDocument getBacktrace(); QJsonDocument getBacktrace();
void startDebug(); void startDebug();
void startEmulation();
void attachDebug(int pid);
void stopDebug(); void stopDebug();
void continueDebug(); void continueDebug();
void continueUntilCall(); void continueUntilCall();
@ -497,6 +507,7 @@ public:
QStringList getDebugPlugins(); QStringList getDebugPlugins();
void setDebugPlugin(QString plugin); void setDebugPlugin(QString plugin);
bool currentlyDebugging = false; bool currentlyDebugging = false;
bool currentlyEmulating = false;
RVA getOffsetJump(RVA addr); RVA getOffsetJump(RVA addr);
QString getDecompiledCode(RVA addr); QString getDecompiledCode(RVA addr);
@ -554,6 +565,7 @@ public:
QList<SearchDescription> getAllSearch(QString search_for, QString space); QList<SearchDescription> getAllSearch(QString search_for, QString space);
BlockStatistics getBlockStatistics(unsigned int blocksCount); BlockStatistics getBlockStatistics(unsigned int blocksCount);
QList<BreakpointDescription> getBreakpoints(); QList<BreakpointDescription> getBreakpoints();
QList<ProcessDescription> getAllProcesses();
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType = QString::null); const QString &filterType = QString::null);
@ -584,7 +596,7 @@ signals:
void registersChanged(); void registersChanged();
void instructionChanged(RVA offset); void instructionChanged(RVA offset);
void breakpointsChanged(); void breakpointsChanged();
void deletedAllBreakpoints(); void refreshCodeViews();
void notesChanged(const QString &notes); void notesChanged(const QString &notes);
void projectSaved(const QString &name); void projectSaved(const QString &name);

View File

@ -180,7 +180,8 @@ SOURCES += \
widgets/MemoryMapWidget.cpp \ widgets/MemoryMapWidget.cpp \
dialogs/preferences/DebugOptionsWidget.cpp \ dialogs/preferences/DebugOptionsWidget.cpp \
widgets/BreakpointWidget.cpp \ widgets/BreakpointWidget.cpp \
dialogs/BreakpointsDialog.cpp dialogs/BreakpointsDialog.cpp \
dialogs/AttachProcDialog.cpp
HEADERS += \ HEADERS += \
Cutter.h \ Cutter.h \
@ -272,7 +273,8 @@ HEADERS += \
widgets/MemoryMapWidget.h \ widgets/MemoryMapWidget.h \
dialogs/preferences/DebugOptionsWidget.h \ dialogs/preferences/DebugOptionsWidget.h \
widgets/BreakpointWidget.h \ widgets/BreakpointWidget.h \
dialogs/BreakpointsDialog.h dialogs/BreakpointsDialog.h \
dialogs/AttachProcDialog.h
FORMS += \ FORMS += \
dialogs/AboutDialog.ui \ dialogs/AboutDialog.ui \
@ -325,7 +327,8 @@ FORMS += \
widgets/MemoryMapWidget.ui \ widgets/MemoryMapWidget.ui \
dialogs/preferences/DebugOptionsWidget.ui \ dialogs/preferences/DebugOptionsWidget.ui \
widgets/BreakpointWidget.ui \ widgets/BreakpointWidget.ui \
dialogs/BreakpointsDialog.ui dialogs/BreakpointsDialog.ui \
dialogs/AttachProcDialog.ui
RESOURCES += \ RESOURCES += \
resources.qrc \ resources.qrc \

View File

@ -0,0 +1,178 @@
#include "MainWindow.h"
#include "Cutter.h"
#include "AttachProcDialog.h"
#include "ui_AttachProcDialog.h"
#include "utils/Helpers.h"
ProcessModel::ProcessModel(QList<ProcessDescription> *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<ProcessDescription>();
return item.path.contains(filterRegExp());
}
bool ProcessProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
ProcessDescription leftProc = left.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
ProcessDescription rightProc = right.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
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<ProcessDescription>();
return proc.pid;
}
bool AttachProcDialog::eventFilter(QObject *obj, QEvent *event)
{
Q_UNUSED(obj);
if (event -> type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast <QKeyEvent *> (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<ProcessDescription>();
accept();
}

View File

@ -0,0 +1,82 @@
#pragma once
#include "Cutter.h"
#include <QDialog>
#include <memory>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
namespace Ui {
class AttachProcDialog;
}
class MainWindow;
class QTreeWidget;
class QTreeWidgetItem;
class ProcessModel: public QAbstractListModel
{
Q_OBJECT
private:
QList<ProcessDescription> *processes;
public:
enum Column { PidColumn = 0, UidColumn, StatusColumn, PathColumn, ColumnCount };
enum Role { ProcDescriptionRole = Qt::UserRole };
ProcessModel(QList<ProcessDescription> *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::AttachProcDialog> ui;
bool eventFilter(QObject *obj, QEvent *event);
ProcessModel *processModel;
ProcessProxyModel *processProxyModel;
QList<ProcessDescription> processes;
};

View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AttachProcDialog</class>
<widget class="QDialog" name="AttachProcDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Select process to attach...</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QTreeView" name="procTreeView">
<property name="styleSheet">
<string notr="true">QTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLineEdit" name="filterLineEdit">
<property name="placeholderText">
<string>Quick Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AttachProcDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AttachProcDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,5 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 24 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="24px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 32">
<polygon points="0 0 24 16 0 32" fill="#aaacaf"/>
<text x="15" y="10" font-family="Helvetica, Arial, sans-serif" font-size="13" stroke-width="2" stroke="#aaacaf" fill="#aaacaf" >A</text>
</svg>

After

Width:  |  Height:  |  Size: 521 B

View File

@ -0,0 +1,5 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 24 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="24px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 32">
<polygon points="0 0 24 16 0 32" fill="#aaacaf"/>
<text x="15" y="10" font-family="Helvetica, Arial, sans-serif" font-size="13" stroke-width="2" stroke="#aaacaf" fill="#aaacaf" >D</text>
</svg>

After

Width:  |  Height:  |  Size: 521 B

View File

@ -0,0 +1,5 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 24 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="24px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 32">
<polygon points="0 0 24 16 0 32" fill="#aaacaf"/>
<text x="15" y="10" font-family="Helvetica, Arial, sans-serif" font-size="13" stroke-width="2" stroke="#aaacaf" fill="#aaacaf" >E</text>
</svg>

After

Width:  |  Height:  |  Size: 521 B

View File

@ -237,10 +237,12 @@ void DisassemblyContextMenu::aboutToShowSlot()
// decide to show Reverse jmp option // decide to show Reverse jmp option
showReverseJmpQuery(); showReverseJmpQuery();
// show debug options // only show debug options if we are currently debugging
// @TODO determine if we are being debugged and only show the menu in those cases debugMenuAction->setVisible(Core()->currentlyDebugging);
// maybe using dpt command // currently there are is no breakpoint support in ESIL so
debugMenuAction->setVisible(true); // we dont show the option in case we are emulating
actionAddBreakpoint.setVisible(!Core()->currentlyEmulating);
} }
QKeySequence DisassemblyContextMenu::getCopySequence() const QKeySequence DisassemblyContextMenu::getCopySequence() const

View File

@ -19,7 +19,9 @@
<file>img/icons/spin.svg</file> <file>img/icons/spin.svg</file>
<file>img/icons/plus.svg</file> <file>img/icons/plus.svg</file>
<file>img/icons/play.svg</file> <file>img/icons/play.svg</file>
<file>img/icons/play_light.svg</file> <file>img/icons/play_light_debug.svg</file>
<file>img/icons/play_light_emul.svg</file>
<file>img/icons/play_light_attach.svg</file>
<file>img/icons/media-stop_light.svg</file> <file>img/icons/media-stop_light.svg</file>
<file>img/icons/media-skip-forward_light.svg</file> <file>img/icons/media-skip-forward_light.svg</file>
<file>img/icons/continue_until_main.svg</file> <file>img/icons/continue_until_main.svg</file>

View File

@ -136,7 +136,7 @@ BreakpointWidget::BreakpointWidget(MainWindow *main, QAction *action) :
connect(actionToggleBreakpoint, &QAction::triggered, this, &BreakpointWidget::toggleBreakpoint); connect(actionToggleBreakpoint, &QAction::triggered, this, &BreakpointWidget::toggleBreakpoint);
connect(Core(), &CutterCore::refreshAll, this, &BreakpointWidget::refreshBreakpoint); connect(Core(), &CutterCore::refreshAll, this, &BreakpointWidget::refreshBreakpoint);
connect(Core(), &CutterCore::breakpointsChanged, 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->addBreakpoint, &QAbstractButton::clicked, this, &BreakpointWidget::addBreakpointDialog);
connect(ui->delBreakpoint, &QAbstractButton::clicked, this, &BreakpointWidget::delBreakpoint); connect(ui->delBreakpoint, &QAbstractButton::clicked, this, &BreakpointWidget::delBreakpoint);
connect(ui->delAllBreakpoints, &QAbstractButton::clicked, Core(), &CutterCore::delAllBreakpoints); connect(ui->delAllBreakpoints, &QAbstractButton::clicked, Core(), &CutterCore::delAllBreakpoints);

View File

@ -1,5 +1,6 @@
#include "DebugToolbar.h" #include "DebugToolbar.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "dialogs/AttachProcDialog.cpp"
#include <QAction> #include <QAction>
#include <QPainter> #include <QPainter>
@ -11,7 +12,9 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
main(main) main(main)
{ {
setObjectName("debugToolbar"); 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 stopIcon = QIcon(":/img/icons/media-stop_light.svg");
QIcon continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg"); QIcon continueIcon = QIcon(":/img/icons/media-skip-forward_light.svg");
QIcon continueUntilMainIcon = QIcon(":/img/icons/continue_until_main.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 stepIcon = QIcon(":/img/icons/step_light.svg");
QIcon stepOverIcon = QIcon(":/img/icons/step_over_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 *actionStop = new QAction(stopIcon, tr("Stop debug"), parent);
QAction *actionContinue = new QAction(continueIcon, tr("Continue"), parent); QAction *actionContinue = new QAction(continueIcon, tr("Continue"), parent);
QAction *actionContinueUntilMain = new QAction(continueUntilMainIcon, tr("Continue until main"), 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 *actionStep = new QAction(stepIcon, tr("Step"), parent);
QAction *actionStepOver = new QAction(stepOverIcon, tr("Step over"), 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; QToolButton *continueUntilButton = new QToolButton;
continueUntilButton->setPopupMode(QToolButton::MenuButtonPopup); continueUntilButton->setPopupMode(QToolButton::MenuButtonPopup);
connect(continueUntilButton, &QToolButton::triggered, continueUntilButton, &QToolButton::setDefaultAction); connect(continueUntilButton, &QToolButton::triggered, continueUntilButton, &QToolButton::setDefaultAction);
QMenu *continueUntilMenu = new QMenu; QMenu *continueUntilMenu = new QMenu;
continueUntilMenu->addAction(actionContinueUntilMain); continueUntilMenu->addAction(actionContinueUntilMain);
continueUntilMenu->addAction(actionContinueUntilCall); continueUntilMenu->addAction(actionContinueUntilCall);
@ -40,7 +54,7 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
continueUntilButton->setMenu(continueUntilMenu); continueUntilButton->setMenu(continueUntilMenu);
continueUntilButton->setDefaultAction(actionContinueUntilMain); continueUntilButton->setDefaultAction(actionContinueUntilMain);
addAction(actionStart); addWidget(startButton);
addAction(actionStop); addAction(actionStop);
addAction(actionContinue); addAction(actionContinue);
addWidget(continueUntilButton); addWidget(continueUntilButton);
@ -48,10 +62,33 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
addAction(actionStepOver); addAction(actionStepOver);
connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug); 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(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
connect(actionStart, &QAction::triggered, Core(), &CutterCore::startDebug); 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(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug); connect(actionContinue, &QAction::triggered, Core(), &CutterCore::continueDebug);
connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain); connect(actionContinueUntilMain, &QAction::triggered, this, &DebugToolbar::continueUntilMain);
@ -72,3 +109,23 @@ void DebugToolbar::colorToolbar(bool p)
this->setStyleSheet(""); 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);
}

View File

@ -14,9 +14,12 @@ public:
private: private:
MainWindow *main; MainWindow *main;
QAction *actionStart;
QAction *actionStartEmul;
private slots: private slots:
void continueUntilMain(); void continueUntilMain();
void colorToolbar(bool p); void colorToolbar(bool p);
void attachProcessDialog();
void attachProcess(int pid);
}; };

View File

@ -36,7 +36,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshView()));
connect(Core(), SIGNAL(graphOptionsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(graphOptionsChanged()), this, SLOT(refreshView()));
connect(Core(), SIGNAL(asmOptionsChanged()), 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(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));

View File

@ -119,7 +119,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
refreshDisasm(); 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(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));