diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 56a6947f..6465ddb2 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -373,6 +373,16 @@ void CutterCore::delFlag(const QString &name) emit flagsChanged(); } +QString CutterCore::getInstructionBytes(RVA addr) +{ + return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()["bytes"].toString(); +} + +QString CutterCore::getInstructionOpcode(RVA addr) +{ + return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()["opcode"].toString(); +} + void CutterCore::editInstruction(RVA addr, const QString &inst) { cmd("wa " + inst + " @ " + RAddressString(addr)); diff --git a/src/Cutter.h b/src/Cutter.h index 9044e023..524dbbc3 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -407,6 +407,8 @@ public: void triggerFlagsChanged(); /* Edition functions */ + QString getInstructionBytes(RVA addr); + QString getInstructionOpcode(RVA addr); void editInstruction(RVA addr, const QString &inst); void nopInstruction(RVA addr); void jmpReverse(RVA addr); diff --git a/src/dialogs/EditInstructionDialog.cpp b/src/dialogs/EditInstructionDialog.cpp index 2fa22fe8..58405903 100644 --- a/src/dialogs/EditInstructionDialog.cpp +++ b/src/dialogs/EditInstructionDialog.cpp @@ -2,14 +2,14 @@ #include "ui_EditInstructionDialog.h" #include "Cutter.h" -EditInstructionDialog::EditInstructionDialog(QWidget *parent) : +EditInstructionDialog::EditInstructionDialog(QWidget *parent, bool isEditingBytes) : QDialog(parent), - ui(new Ui::EditInstructionDialog) + ui(new Ui::EditInstructionDialog), + isEditingBytes(isEditingBytes) { ui->setupUi(this); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); - // Event filter for capturing Ctrl/Cmd+Return ui->lineEdit->installEventFilter(this); } @@ -36,8 +36,15 @@ void EditInstructionDialog::setInstruction(const QString &instruction) updatePreview(instruction); } -void EditInstructionDialog::updatePreview(const QString &hex) { - QString result = Core()->disassemble(hex).trimmed(); +void EditInstructionDialog::updatePreview(const QString &input) +{ + QString result; + if (isEditingBytes) { + result = Core()->disassemble(input).trimmed(); + } else { + result = Core()->assemble(input).trimmed(); + } + if (result.isEmpty() || result.contains("\n")) { ui->instructionLabel->setText("Unknown Instruction"); } else { @@ -49,25 +56,17 @@ bool EditInstructionDialog::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); - if (event -> type() == QEvent::KeyPress) { + 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; - } - // Update instruction preview QString lineText = ui->lineEdit->text(); - if (keyEvent -> key() == Qt::Key_Backspace) { + if (keyEvent->key() == Qt::Key_Backspace) { updatePreview(lineText.left(lineText.size() - 1)); } else { updatePreview(lineText + keyEvent->text()); } } - return false; -} +} \ No newline at end of file diff --git a/src/dialogs/EditInstructionDialog.h b/src/dialogs/EditInstructionDialog.h index 4115a007..8a082961 100644 --- a/src/dialogs/EditInstructionDialog.h +++ b/src/dialogs/EditInstructionDialog.h @@ -14,13 +14,13 @@ class EditInstructionDialog : public QDialog Q_OBJECT public: - explicit EditInstructionDialog(QWidget *parent = nullptr); + explicit EditInstructionDialog(QWidget *parent, bool isEditingBytes); ~EditInstructionDialog(); QString getInstruction(); void setInstruction(const QString &instruction); - - void updatePreview(const QString &hex); + + void updatePreview(const QString &input); private slots: void on_buttonBox_accepted(); @@ -29,6 +29,7 @@ private slots: private: std::unique_ptr ui; + bool isEditingBytes; // true if editing intruction **bytes**; false if editing instruction **text** bool eventFilter(QObject *obj, QEvent *event); }; diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 9d30fc94..a0710bae 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -355,24 +355,39 @@ QList DisassemblyContextMenu::getAddBPSequence() const void DisassemblyContextMenu::on_actionEditInstruction_triggered() { - EditInstructionDialog *e = new EditInstructionDialog(this); + EditInstructionDialog *e = new EditInstructionDialog(this, false); e->setWindowTitle(tr("Edit Instruction at %1").arg(RAddressString(offset))); - QString oldInstruction = Core()->cmdj("aoj").array().first().toObject()["opcode"].toString(); - e->setInstruction(oldInstruction); + QString oldInstructionOpcode = Core()->getInstructionOpcode(offset); + QString oldInstructionBytes = Core()->getInstructionBytes(offset); + + e->setInstruction(oldInstructionOpcode); if (e->exec()) { - QString instruction = e->getInstruction(); - if (instruction != oldInstruction) { - Core()->editInstruction(offset, instruction); + QString userInstructionOpcode = e->getInstruction(); + if (userInstructionOpcode != oldInstructionOpcode) { + Core()->editInstruction(offset, userInstructionOpcode); + + // check if the write failed + auto newInstructionBytes = Core()->getInstructionBytes(offset); + if (newInstructionBytes == oldInstructionBytes) { + writeFailed(); + } } } } void DisassemblyContextMenu::on_actionNopInstruction_triggered() { + QString oldBytes = Core()->getInstructionBytes(offset); + Core()->nopInstruction(offset); + + QString newBytes = Core()->getInstructionBytes(offset); + if (oldBytes == newBytes) { + writeFailed(); + } } void DisassemblyContextMenu::showReverseJmpQuery() @@ -394,15 +409,22 @@ void DisassemblyContextMenu::showReverseJmpQuery() void DisassemblyContextMenu::on_actionJmpReverse_triggered() { + QString oldBytes = Core()->getInstructionBytes(offset); + Core()->jmpReverse(offset); + + QString newBytes = Core()->getInstructionBytes(offset); + if (oldBytes == newBytes) { + writeFailed(); + } } void DisassemblyContextMenu::on_actionEditBytes_triggered() { - EditInstructionDialog *e = new EditInstructionDialog(this); + EditInstructionDialog *e = new EditInstructionDialog(this, true); e->setWindowTitle(tr("Edit Bytes at %1").arg(RAddressString(offset))); - QString oldBytes = Core()->cmdj("aoj").array().first().toObject()["bytes"].toString(); + QString oldBytes = Core()->getInstructionBytes(offset); e->setInstruction(oldBytes); if (e->exec()) @@ -410,10 +432,32 @@ void DisassemblyContextMenu::on_actionEditBytes_triggered() QString bytes = e->getInstruction(); if (bytes != oldBytes) { Core()->editBytes(offset, bytes); + + QString newBytes = Core()->getInstructionBytes(offset); + if (oldBytes == newBytes) { + writeFailed(); + } } } } +void DisassemblyContextMenu::writeFailed() +{ + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Icon::Critical); + msgBox.setWindowTitle(tr("Write error")); + msgBox.setText(tr("Unable to complete write operation. Consider opening in write mode.")); + msgBox.addButton(tr("OK"), QMessageBox::NoRole); + QAbstractButton* reopenButton = msgBox.addButton(tr("Reopen in write mode"), QMessageBox::YesRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == reopenButton) { + QMessageBox::warning(this, "File reopened in write mode", "WARNING: Any chages will now be commited to disk"); + Core()->cmd("oo+"); + } +} + void DisassemblyContextMenu::on_actionCopy_triggered() { emit copy(); diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index 7abdaf10..bc72fb47 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -26,8 +26,9 @@ private slots: void on_actionEditInstruction_triggered(); void on_actionNopInstruction_triggered(); void on_actionJmpReverse_triggered(); - void showReverseJmpQuery(); void on_actionEditBytes_triggered(); + void showReverseJmpQuery(); + void writeFailed(); void on_actionCopy_triggered(); void on_actionCopyAddr_triggered(); diff --git a/src/widgets/StackWidget.cpp b/src/widgets/StackWidget.cpp index d7ef3927..f0b0ea25 100644 --- a/src/widgets/StackWidget.cpp +++ b/src/widgets/StackWidget.cpp @@ -119,7 +119,8 @@ void StackWidget::editStack() bool ok; int row = viewStack->selectionModel()->currentIndex().row(); QString offset = viewStack->selectionModel()->currentIndex().sibling(row, 0).data().toString(); - EditInstructionDialog *e = new EditInstructionDialog(this); + // FIXME: This is not correct because there should be no preview of anything + EditInstructionDialog *e = new EditInstructionDialog(this, false); e->setWindowTitle(tr("Edit stack at %1").arg(offset)); QString oldBytes = viewStack->selectionModel()->currentIndex().sibling(row, 1).data().toString();