Write operation failure dialog (#695)

* Write operation failure dialog
* Option to reopen the file in write mode after write failure
This commit is contained in:
Vasco Franco 2018-09-14 18:20:54 +01:00 committed by xarkes
parent a40c1ce4bf
commit 387138908b
7 changed files with 87 additions and 29 deletions

View File

@ -373,6 +373,16 @@ void CutterCore::delFlag(const QString &name)
emit flagsChanged(); 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) void CutterCore::editInstruction(RVA addr, const QString &inst)
{ {
cmd("wa " + inst + " @ " + RAddressString(addr)); cmd("wa " + inst + " @ " + RAddressString(addr));

View File

@ -407,6 +407,8 @@ public:
void triggerFlagsChanged(); void triggerFlagsChanged();
/* Edition functions */ /* Edition functions */
QString getInstructionBytes(RVA addr);
QString getInstructionOpcode(RVA addr);
void editInstruction(RVA addr, const QString &inst); void editInstruction(RVA addr, const QString &inst);
void nopInstruction(RVA addr); void nopInstruction(RVA addr);
void jmpReverse(RVA addr); void jmpReverse(RVA addr);

View File

@ -2,14 +2,14 @@
#include "ui_EditInstructionDialog.h" #include "ui_EditInstructionDialog.h"
#include "Cutter.h" #include "Cutter.h"
EditInstructionDialog::EditInstructionDialog(QWidget *parent) : EditInstructionDialog::EditInstructionDialog(QWidget *parent, bool isEditingBytes) :
QDialog(parent), QDialog(parent),
ui(new Ui::EditInstructionDialog) ui(new Ui::EditInstructionDialog),
isEditingBytes(isEditingBytes)
{ {
ui->setupUi(this); ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
// Event filter for capturing Ctrl/Cmd+Return
ui->lineEdit->installEventFilter(this); ui->lineEdit->installEventFilter(this);
} }
@ -36,8 +36,15 @@ void EditInstructionDialog::setInstruction(const QString &instruction)
updatePreview(instruction); updatePreview(instruction);
} }
void EditInstructionDialog::updatePreview(const QString &hex) { void EditInstructionDialog::updatePreview(const QString &input)
QString result = Core()->disassemble(hex).trimmed(); {
QString result;
if (isEditingBytes) {
result = Core()->disassemble(input).trimmed();
} else {
result = Core()->assemble(input).trimmed();
}
if (result.isEmpty() || result.contains("\n")) { if (result.isEmpty() || result.contains("\n")) {
ui->instructionLabel->setText("Unknown Instruction"); ui->instructionLabel->setText("Unknown Instruction");
} else { } else {
@ -49,25 +56,17 @@ bool EditInstructionDialog::eventFilter(QObject *obj, QEvent *event)
{ {
Q_UNUSED(obj); Q_UNUSED(obj);
if (event -> type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast <QKeyEvent *>(event); 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;
}
// Update instruction preview // Update instruction preview
QString lineText = ui->lineEdit->text(); QString lineText = ui->lineEdit->text();
if (keyEvent -> key() == Qt::Key_Backspace) { if (keyEvent->key() == Qt::Key_Backspace) {
updatePreview(lineText.left(lineText.size() - 1)); updatePreview(lineText.left(lineText.size() - 1));
} else { } else {
updatePreview(lineText + keyEvent->text()); updatePreview(lineText + keyEvent->text());
} }
} }
return false; return false;
} }

View File

@ -14,13 +14,13 @@ class EditInstructionDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit EditInstructionDialog(QWidget *parent = nullptr); explicit EditInstructionDialog(QWidget *parent, bool isEditingBytes);
~EditInstructionDialog(); ~EditInstructionDialog();
QString getInstruction(); QString getInstruction();
void setInstruction(const QString &instruction); void setInstruction(const QString &instruction);
void updatePreview(const QString &hex); void updatePreview(const QString &input);
private slots: private slots:
void on_buttonBox_accepted(); void on_buttonBox_accepted();
@ -29,6 +29,7 @@ private slots:
private: private:
std::unique_ptr<Ui::EditInstructionDialog> ui; std::unique_ptr<Ui::EditInstructionDialog> ui;
bool isEditingBytes; // true if editing intruction **bytes**; false if editing instruction **text**
bool eventFilter(QObject *obj, QEvent *event); bool eventFilter(QObject *obj, QEvent *event);
}; };

View File

@ -355,24 +355,39 @@ QList<QKeySequence> DisassemblyContextMenu::getAddBPSequence() const
void DisassemblyContextMenu::on_actionEditInstruction_triggered() 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))); e->setWindowTitle(tr("Edit Instruction at %1").arg(RAddressString(offset)));
QString oldInstruction = Core()->cmdj("aoj").array().first().toObject()["opcode"].toString(); QString oldInstructionOpcode = Core()->getInstructionOpcode(offset);
e->setInstruction(oldInstruction); QString oldInstructionBytes = Core()->getInstructionBytes(offset);
e->setInstruction(oldInstructionOpcode);
if (e->exec()) if (e->exec())
{ {
QString instruction = e->getInstruction(); QString userInstructionOpcode = e->getInstruction();
if (instruction != oldInstruction) { if (userInstructionOpcode != oldInstructionOpcode) {
Core()->editInstruction(offset, instruction); Core()->editInstruction(offset, userInstructionOpcode);
// check if the write failed
auto newInstructionBytes = Core()->getInstructionBytes(offset);
if (newInstructionBytes == oldInstructionBytes) {
writeFailed();
}
} }
} }
} }
void DisassemblyContextMenu::on_actionNopInstruction_triggered() void DisassemblyContextMenu::on_actionNopInstruction_triggered()
{ {
QString oldBytes = Core()->getInstructionBytes(offset);
Core()->nopInstruction(offset); Core()->nopInstruction(offset);
QString newBytes = Core()->getInstructionBytes(offset);
if (oldBytes == newBytes) {
writeFailed();
}
} }
void DisassemblyContextMenu::showReverseJmpQuery() void DisassemblyContextMenu::showReverseJmpQuery()
@ -394,15 +409,22 @@ void DisassemblyContextMenu::showReverseJmpQuery()
void DisassemblyContextMenu::on_actionJmpReverse_triggered() void DisassemblyContextMenu::on_actionJmpReverse_triggered()
{ {
QString oldBytes = Core()->getInstructionBytes(offset);
Core()->jmpReverse(offset); Core()->jmpReverse(offset);
QString newBytes = Core()->getInstructionBytes(offset);
if (oldBytes == newBytes) {
writeFailed();
}
} }
void DisassemblyContextMenu::on_actionEditBytes_triggered() 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))); 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); e->setInstruction(oldBytes);
if (e->exec()) if (e->exec())
@ -410,10 +432,32 @@ void DisassemblyContextMenu::on_actionEditBytes_triggered()
QString bytes = e->getInstruction(); QString bytes = e->getInstruction();
if (bytes != oldBytes) { if (bytes != oldBytes) {
Core()->editBytes(offset, bytes); 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() void DisassemblyContextMenu::on_actionCopy_triggered()
{ {
emit copy(); emit copy();

View File

@ -26,8 +26,9 @@ private slots:
void on_actionEditInstruction_triggered(); void on_actionEditInstruction_triggered();
void on_actionNopInstruction_triggered(); void on_actionNopInstruction_triggered();
void on_actionJmpReverse_triggered(); void on_actionJmpReverse_triggered();
void showReverseJmpQuery();
void on_actionEditBytes_triggered(); void on_actionEditBytes_triggered();
void showReverseJmpQuery();
void writeFailed();
void on_actionCopy_triggered(); void on_actionCopy_triggered();
void on_actionCopyAddr_triggered(); void on_actionCopyAddr_triggered();

View File

@ -119,7 +119,8 @@ void StackWidget::editStack()
bool ok; bool ok;
int row = viewStack->selectionModel()->currentIndex().row(); int row = viewStack->selectionModel()->currentIndex().row();
QString offset = viewStack->selectionModel()->currentIndex().sibling(row, 0).data().toString(); 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)); e->setWindowTitle(tr("Edit stack at %1").arg(offset));
QString oldBytes = viewStack->selectionModel()->currentIndex().sibling(row, 1).data().toString(); QString oldBytes = viewStack->selectionModel()->currentIndex().sibling(row, 1).data().toString();