1
0
Fork 0

GUI: close patch dialog when debugging stops + do not show patch dialog when not debugging + fixed a bug with the checking of patches + added patch import/export (with undo support and option to not apply patches with a different oldbyte value)

This commit is contained in:
Mr. eXoDia 2014-07-06 23:44:43 +02:00
parent e4101a0d79
commit 704994df12
5 changed files with 257 additions and 14 deletions

View File

@ -749,6 +749,14 @@ void MainWindow::getStrWindow(const QString title, QString *text)
void MainWindow::patchWindow()
{
if(!DbgIsDebugging())
{
QMessageBox msg(QMessageBox::Critical, "Error!", QString("Patches cannot be shown when not debugging..."));
msg.setWindowIcon(QIcon(":/icons/images/compile-error.png"));
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
return;
}
GuiUpdatePatches();
mPatchDialog->showNormal();
mPatchDialog->setFocus();

View File

@ -12,6 +12,7 @@ PatchDialog::PatchDialog(QWidget *parent) :
setModal(false); //non-modal window
connect(Bridge::getBridge(), SIGNAL(updatePatches()), this, SLOT(updatePatches()));
connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(dbgStateChanged(DBGSTATE)));
mGroupSelector = new PatchDialogGroupSelector(parent);
mGroupSelector->setGroup(0);
@ -74,6 +75,15 @@ int_t PatchDialog::getGroupAddress(const PatchInfoList & patchList, int group)
return -1;
}
void PatchDialog::dbgStateChanged(DBGSTATE state)
{
if(state==stopped)
{
mGroupSelector->hide();
reject();
}
}
void PatchDialog::updatePatches()
{
if(this->isVisible())
@ -104,12 +114,14 @@ void PatchDialog::updatePatches()
//fill the patch list
STATUSINFO defaultStatus;
defaultStatus.group=0;
defaultStatus.checked=false;
defaultStatus.checked=true;
for(int i=0; i<numPatches; i++)
{
if(!patches[i].addr)
continue;
QString mod(patches[i].mod);
if(!*patches[i].mod)
continue;
QString mod = patches[i].mod;
PatchMap::iterator found = mPatches->find(mod);
if(found != mPatches->end()) //found
(*mPatches)[mod].append(PatchPair(patches[i], defaultStatus));
@ -238,7 +250,8 @@ void PatchDialog::on_listModules_itemSelectionChanged()
QString addrText = QString("%1").arg(curPatch.addr, sizeof(int_t)*2, 16, QChar('0')).toUpper();
QListWidgetItem* item = new QListWidgetItem(QString().sprintf("%d", patchList.at(i).second.group).rightJustified(4, ' ', true) + "|" + addrText + QString().sprintf(":%.2X->%.2X", curPatch.oldbyte, curPatch.newbyte), ui->listPatches);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
Qt::CheckState state = patchList.at(i).second.checked ? Qt::Checked : Qt::Unchecked;
item->setCheckState(state);
}
mIsWorking=false;
}
@ -452,3 +465,194 @@ void PatchDialog::on_btnPatchFile_clicked()
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
}
void PatchDialog::on_btnImport_clicked()
{
QString filename = QFileDialog::getOpenFileName(this, tr("Open patch"), QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation), tr("Patch files (*.1337)"));
if(!filename.length())
return;
filename=QDir::toNativeSeparators(filename); //convert to native path format (with backlashes)
QFile file(filename);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&file);
QString patch=in.readAll();
file.close();
patch=patch.replace("\r\n", "\n");
QStringList lines=patch.split("\n", QString::SkipEmptyParts);
if(!lines.size())
{
QMessageBox msg(QMessageBox::Critical, "Error!", QString("The patch file is empty..."));
msg.setWindowIcon(QIcon(":/icons/images/compile-error.png"));
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
return;
}
typedef struct IMPORTSTATUS
{
bool nomatchoriginal;
bool matchold;
};
QList<QPair<DBGPATCHINFO, IMPORTSTATUS>> patchList;
DBGPATCHINFO curPatch;
int_t modbase=0;
bool bBadOriginal = false;
bool bAlreadyDone = false;
for(int i=0; i<lines.size(); i++)
{
ULONGLONG rva;
unsigned int oldbyte;
unsigned int newbyte;
QString curLine=lines.at(i);
if(curLine.startsWith(">")) //module
{
strcpy(curPatch.mod, curLine.toUtf8().constData()+1);
modbase=DbgFunctions()->ModBaseFromName(curPatch.mod);
continue;
}
if(!modbase)
continue;
curLine=curLine.replace(" ", "");
if(sscanf(curLine.toUtf8().constData(), "%llX:%X->%X", &rva, &oldbyte, &newbyte)!=3)
{
QMessageBox msg(QMessageBox::Critical, "Error!", QString("Patch file format is incorrect..."));
msg.setWindowIcon(QIcon(":/icons/images/compile-error.png"));
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
return;
}
oldbyte&=0xFF;
newbyte&=0xFF;
curPatch.addr=rva+modbase;
if(!DbgMemIsValidReadPtr(curPatch.addr))
continue;
unsigned char checkbyte=0;
DbgMemRead(curPatch.addr, &checkbyte, sizeof(checkbyte));
if(checkbyte==newbyte)
bAlreadyDone=true;
else if(checkbyte!=oldbyte)
bBadOriginal=true;
curPatch.oldbyte=oldbyte;
curPatch.newbyte=newbyte;
IMPORTSTATUS status = {checkbyte!=oldbyte && !checkbyte==newbyte, checkbyte==newbyte};
patchList.push_back(QPair<DBGPATCHINFO, IMPORTSTATUS>(curPatch, status));
}
if(!patchList.size())
{
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("No patches to apply in the current process."));
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
msg.setParent(this, Qt::Dialog);
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
return;
}
bool bUndoPatched = false;
if(bAlreadyDone)
{
QMessageBox msg(QMessageBox::Question, "Question", "Some patches are already applied.\n\nDo you want to remove these patches?", QMessageBox::Yes|QMessageBox::No);
msg.setWindowIcon(QIcon(":/icons/images/question.png"));
msg.setParent(this, Qt::Dialog);
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
if(msg.exec()==QMessageBox::Yes)
bUndoPatched = true;
}
bool bPatchBadOriginals = false;
if(bBadOriginal)
{
QMessageBox msg(QMessageBox::Question, "Question", "Some bytes do not match the original in the patch file.\n\nDo you want to apply these patches anyway?", QMessageBox::Yes|QMessageBox::No);
msg.setWindowIcon(QIcon(":/icons/images/question.png"));
msg.setParent(this, Qt::Dialog);
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
if(msg.exec()==QMessageBox::Yes)
bPatchBadOriginals = true;
}
int patched=0;
for(int i=0; i<patchList.size(); i++)
{
if(!bPatchBadOriginals && patchList.at(i).second.nomatchoriginal)
continue;
curPatch=patchList.at(i).first;
if(bUndoPatched && patchList.at(i).second.matchold)
{
GuiAddStatusBarMessage("undo!");
if(DbgFunctions()->MemPatch(curPatch.addr, &curPatch.oldbyte, 1))
patched++;
}
else
{
if(DbgFunctions()->MemPatch(curPatch.addr, &curPatch.newbyte, 1))
patched++;
}
}
updatePatches();
GuiUpdateAllViews();
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("%d/%d patch(es) applied!", patched, patchList.size()));
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
msg.setParent(this, Qt::Dialog);
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
}
void PatchDialog::on_btnExport_clicked()
{
if(!mPatches->size())
return;
QString filename = QFileDialog::getSaveFileName(this, tr("Save patch"), QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation), tr("Patch files (*.1337)"));
if(!filename.length())
return;
filename=QDir::toNativeSeparators(filename); //convert to native path format (with backlashes)
QStringList lines;
int patches=0;
for(PatchMap::iterator i=mPatches->begin(); i!=mPatches->end(); ++i)
{
const PatchInfoList & curPatchList = i.value();
bool bModPlaced=false;
int_t modbase=DbgFunctions()->ModBaseFromName(i.key().toUtf8().constData());
if(!modbase)
continue;
for(int j=0; j<curPatchList.size(); j++)
{
if(!curPatchList.at(j).second.checked) //skip unchecked patches
continue;
if(!bModPlaced)
{
lines.push_back(">" + i.key());
bModPlaced=true;
}
QString addrText = QString("%1").arg(curPatchList.at(j).first.addr-modbase, sizeof(int_t)*2, 16, QChar('0')).toUpper();
lines.push_back(addrText+QString().sprintf(":%.2X->%.2X", curPatchList.at(j).first.oldbyte, curPatchList.at(j).first.newbyte));
patches++;
}
}
if(!lines.size())
{
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("No patches to export."));
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
msg.setParent(this, Qt::Dialog);
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
return;
}
QFile file(filename);
file.open(QFile::WriteOnly | QFile::Text);
QString text = lines.join("\n");
file.write(text.toUtf8().constData(), text.length());
file.close();
QMessageBox msg(QMessageBox::Information, "Information", QString().sprintf("%d patch(es) exported!", patches));
msg.setWindowIcon(QIcon(":/icons/images/information.png"));
msg.setParent(this, Qt::Dialog);
msg.setWindowFlags(msg.windowFlags()&(~Qt::WindowContextHelpButtonHint));
msg.exec();
}

View File

@ -45,6 +45,7 @@ private:
int_t getGroupAddress(const PatchInfoList & patchList, int group);
private slots:
void dbgStateChanged(DBGSTATE state);
void updatePatches();
void groupToggle();
void groupPrevious();
@ -57,6 +58,8 @@ private slots:
void on_listPatches_itemSelectionChanged();
void on_btnPickGroups_clicked();
void on_btnPatchFile_clicked();
void on_btnImport_clicked();
void on_btnExport_clicked();
};
#endif // PATCHDIALOG_H

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>511</width>
<height>452</height>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
@ -51,7 +51,7 @@
<x>10</x>
<y>10</y>
<width>211</width>
<height>431</height>
<height>391</height>
</rect>
</property>
<property name="title">
@ -63,7 +63,7 @@
<x>10</x>
<y>20</y>
<width>191</width>
<height>401</height>
<height>361</height>
</rect>
</property>
<property name="font">
@ -76,8 +76,8 @@
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>230</x>
<y>380</y>
<x>231</x>
<y>381</y>
<width>271</width>
<height>58</height>
</rect>
@ -88,21 +88,21 @@
<item>
<widget class="QPushButton" name="btnSelectAll">
<property name="text">
<string>Select All</string>
<string>&amp;Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDeselectAll">
<property name="text">
<string>Deselect All</string>
<string>&amp;Deselect All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRestoreSelected">
<property name="text">
<string>Restore Selected</string>
<string>&amp;Restore Selected</string>
</property>
</widget>
</item>
@ -116,7 +116,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Pick Groups</string>
<string>Pick &amp;Groups</string>
</property>
</widget>
</item>
@ -126,7 +126,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Patch File</string>
<string>&amp;Patch File</string>
</property>
</widget>
</item>
@ -134,6 +134,32 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>7</x>
<y>410</y>
<width>211</width>
<height>25</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="btnImport">
<property name="text">
<string>&amp;Import</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnExport">
<property name="text">
<string>&amp;Export</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>listModules</tabstop>

View File

@ -20,7 +20,9 @@ bool MemoryPage::read(byte_t* parDest, uint_t parRVA, uint_t parSize)
bool MemoryPage::write(const void* parDest, uint_t parRVA, uint_t parSize)
{
return DbgFunctions()->MemPatch(mBase + parRVA, (unsigned char*)parDest, parSize);
bool ret = DbgFunctions()->MemPatch(mBase + parRVA, (unsigned char*)parDest, parSize);
GuiUpdatePatches();
return ret;
}
bool MemoryPage::write(const byte_t* parDest, uint_t parRVA, uint_t parSize)