mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-19 02:48:49 +00:00
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:
parent
a40c1ce4bf
commit
387138908b
@ -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));
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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,8 +432,30 @@ 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()
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user