1
0
Fork 0
x64dbg/src/gui/Src/BasicView/ReferenceView.cpp

425 lines
16 KiB
C++

#include <QHBoxLayout>
#include <QMessageBox>
#include <QLabel>
#include <QTabWidget>
#include "ReferenceView.h"
#include "Configuration.h"
#include "Bridge.h"
#include "MiscUtil.h"
ReferenceView::ReferenceView(bool sourceView, QWidget* parent) : StdSearchListView(parent, true, false), mParent(dynamic_cast<QTabWidget*>(parent))
{
// Setup SearchListView settings
mSearchStartCol = 1;
// Widget container for progress
QWidget* progressWidget = new QWidget();
// Create the layout for the progress bars
QHBoxLayout* layoutProgress = new QHBoxLayout();
progressWidget->setLayout(layoutProgress);
layoutProgress->setContentsMargins(2, 0, 0, 0);
layoutProgress->setSpacing(4);
// Create current task search progress bar
mSearchCurrentTaskProgress = new QProgressBar();
mSearchCurrentTaskProgress->setRange(0, 100);
mSearchCurrentTaskProgress->setTextVisible(true);
mSearchCurrentTaskProgress->setMaximumHeight(15);
layoutProgress->addWidget(mSearchCurrentTaskProgress);
// Create total search progress bar
mSearchTotalProgress = new QProgressBar();
mSearchTotalProgress->setRange(0, 100);
mSearchTotalProgress->setTextVisible(true);
mSearchTotalProgress->setMaximumHeight(15);
layoutProgress->addWidget(mSearchTotalProgress);
// Label for the number of references
mCountTotalLabel = new QLabel("");
mCountTotalLabel->setAlignment(Qt::AlignCenter);
mCountTotalLabel->setMaximumHeight(16);
mCountTotalLabel->setMinimumWidth(40);
mCountTotalLabel->setContentsMargins(2, 0, 5, 0);
layoutProgress->addWidget(mCountTotalLabel);
if(!sourceView)
{
// Add the progress bar and label to the main layout
layout()->addWidget(progressWidget);
}
connect(this, SIGNAL(listContextMenuSignal(QMenu*)), this, SLOT(referenceContextMenu(QMenu*)));
connect(this, SIGNAL(enterPressedSignal()), this, SLOT(followGenericAddress()));
setupContextMenu();
}
void ReferenceView::setupContextMenu()
{
QIcon disassembler = DIcon(ArchValue("processor32.png", "processor64.png"));
mFollowAddress = new QAction(disassembler, tr("&Follow in Disassembler"), this);
connect(mFollowAddress, SIGNAL(triggered()), this, SLOT(followAddress()));
mFollowDumpAddress = new QAction(DIcon("dump.png"), tr("Follow in &Dump"), this);
connect(mFollowDumpAddress, SIGNAL(triggered()), this, SLOT(followDumpAddress()));
mFollowApiAddress = new QAction(tr("Follow &API Address"), this);
connect(mFollowApiAddress, SIGNAL(triggered()), this, SLOT(followApiAddress()));
mToggleBreakpoint = new QAction(DIcon("breakpoint_toggle.png"), tr("Toggle Breakpoint"), this);
mToggleBreakpoint->setShortcutContext(Qt::WidgetWithChildrenShortcut);
addAction(mToggleBreakpoint);
StdSearchListView::addAction(mToggleBreakpoint);
connect(mToggleBreakpoint, SIGNAL(triggered()), this, SLOT(toggleBreakpoint()));
mToggleBookmark = new QAction(DIcon("bookmark_toggle.png"), tr("Toggle Bookmark"), this);
mToggleBookmark->setShortcutContext(Qt::WidgetWithChildrenShortcut);
addAction(mToggleBookmark);
StdSearchListView::addAction(mToggleBookmark);
connect(mToggleBookmark, SIGNAL(triggered()), this, SLOT(toggleBookmark()));
mSetBreakpointOnAllCommands = new QAction(DIcon("breakpoint_seton_all_commands.png"), tr("Set breakpoint on all commands"), this);
connect(mSetBreakpointOnAllCommands, SIGNAL(triggered()), this, SLOT(setBreakpointOnAllCommands()));
mRemoveBreakpointOnAllCommands = new QAction(DIcon("breakpoint_remove_all_commands.png"), tr("Remove breakpoint on all commands"), this);
connect(mRemoveBreakpointOnAllCommands, SIGNAL(triggered()), this, SLOT(removeBreakpointOnAllCommands()));
mSetBreakpointOnAllApiCalls = new QAction(tr("Set breakpoint on all api calls"), this);
connect(mSetBreakpointOnAllApiCalls, SIGNAL(triggered()), this, SLOT(setBreakpointOnAllApiCalls()));
mRemoveBreakpointOnAllApiCalls = new QAction(tr("Remove breakpoint on all api calls"), this);
connect(mRemoveBreakpointOnAllApiCalls, SIGNAL(triggered()), this, SLOT(removeBreakpointOnAllApiCalls()));
refreshShortcutsSlot();
connect(Config(), SIGNAL(shortcutsUpdated()), this, SLOT(refreshShortcutsSlot()));
}
void ReferenceView::connectBridge()
{
connect(Bridge::getBridge(), SIGNAL(referenceAddColumnAt(int, QString)), this, SLOT(addColumnAtRef(int, QString)));
connect(Bridge::getBridge(), SIGNAL(referenceSetRowCount(dsint)), this, SLOT(setRowCount(dsint)));
connect(Bridge::getBridge(), SIGNAL(referenceSetCellContent(int, int, QString)), this, SLOT(setCellContent(int, int, QString)));
connect(Bridge::getBridge(), SIGNAL(referenceReloadData()), this, SLOT(reloadData()));
connect(Bridge::getBridge(), SIGNAL(referenceSetSingleSelection(int, bool)), this, SLOT(setSingleSelection(int, bool)));
connect(Bridge::getBridge(), SIGNAL(referenceSetProgress(int)), this, SLOT(referenceSetProgressSlot(int)));
connect(Bridge::getBridge(), SIGNAL(referenceSetCurrentTaskProgress(int, QString)), this, SLOT(referenceSetCurrentTaskProgressSlot(int, QString)));
connect(Bridge::getBridge(), SIGNAL(referenceSetSearchStartCol(int)), this, SLOT(setSearchStartCol(int)));
connect(Bridge::getBridge(), SIGNAL(referenceAddCommand(QString, QString)), this, SLOT(addCommand(QString, QString)));
connect(stdSearchList(), SIGNAL(selectionChangedSignal(int)), this, SLOT(searchSelectionChanged(int)));
connect(stdList(), SIGNAL(selectionChangedSignal(int)), this, SLOT(searchSelectionChanged(int)));
}
void ReferenceView::disconnectBridge()
{
disconnect(Bridge::getBridge(), SIGNAL(referenceAddColumnAt(int, QString)), this, SLOT(addColumnAtRef(int, QString)));
disconnect(Bridge::getBridge(), SIGNAL(referenceSetRowCount(dsint)), this, SLOT(setRowCount(dsint)));
disconnect(Bridge::getBridge(), SIGNAL(referenceSetCellContent(int, int, QString)), this, SLOT(setCellContent(int, int, QString)));
disconnect(Bridge::getBridge(), SIGNAL(referenceReloadData()), this, SLOT(reloadData()));
disconnect(Bridge::getBridge(), SIGNAL(referenceSetSingleSelection(int, bool)), this, SLOT(setSingleSelection(int, bool)));
disconnect(Bridge::getBridge(), SIGNAL(referenceSetProgress(int)), mSearchTotalProgress, SLOT(setValue(int)));
disconnect(Bridge::getBridge(), SIGNAL(referenceSetCurrentTaskProgress(int, QString)), this, SLOT(referenceSetCurrentTaskProgressSlot(int, QString)));
disconnect(Bridge::getBridge(), SIGNAL(referenceSetSearchStartCol(int)), this, SLOT(setSearchStartCol(int)));
disconnect(Bridge::getBridge(), SIGNAL(referenceAddCommand(QString, QString)), this, SLOT(addCommand(QString, QString)));
disconnect(stdSearchList(), SIGNAL(selectionChangedSignal(int)), this, SLOT(searchSelectionChanged(int)));
disconnect(stdList(), SIGNAL(selectionChangedSignal(int)), this, SLOT(searchSelectionChanged(int)));
}
void ReferenceView::refreshShortcutsSlot()
{
mToggleBreakpoint->setShortcut(ConfigShortcut("ActionToggleBreakpoint"));
mToggleBookmark->setShortcut(ConfigShortcut("ActionToggleBookmark"));
}
void ReferenceView::referenceSetProgressSlot(int progress)
{
mSearchTotalProgress->setValue(progress);
mSearchTotalProgress->setAlignment(Qt::AlignCenter);
mSearchTotalProgress->setFormat(tr("Total Progress %1%").arg(QString::number(progress)));
}
void ReferenceView::referenceSetCurrentTaskProgressSlot(int progress, QString taskTitle)
{
mSearchCurrentTaskProgress->setValue(progress);
mSearchCurrentTaskProgress->setAlignment(Qt::AlignCenter);
mSearchCurrentTaskProgress->setFormat(taskTitle + " " + QString::number(progress) + "%");
}
void ReferenceView::searchSelectionChanged(int index)
{
DbgValToString("$__disasm_refindex", index);
DbgValToString("$__dump_refindex", index);
}
void ReferenceView::addColumnAtRef(int width, QString title)
{
int charwidth = getCharWidth();
if(width)
width = charwidth * width + 8;
else
width = 0;
mSearchBox->setText("");
if(title.toLower() == "&data&")
title = "Data";
StdSearchListView::addColumnAt(width, title, true);
}
void ReferenceView::setRowCount(dsint count)
{
if(!stdList()->getRowCount() && count) //from zero to N rows
searchSelectionChanged(0);
emit mCountTotalLabel->setText(QString("%1").arg(count));
StdSearchListView::setRowCount(count);
}
void ReferenceView::setSingleSelection(int index, bool scroll)
{
mSearchBox->setText("");
stdList()->setSingleSelection(index);
if(scroll) //TODO: better scrolling
stdList()->setTableOffset(index);
}
void ReferenceView::addCommand(QString title, QString command)
{
mCommandTitles.append(title);
mCommands.append(command);
}
void ReferenceView::referenceContextMenu(QMenu* wMenu)
{
if(!mCurList->getRowCount())
return;
QString text = mCurList->getCellContent(mCurList->getInitialSelection(), 0);
duint addr;
if(!DbgFunctions()->ValFromString(text.toUtf8().constData(), &addr))
return;
if(DbgMemIsValidReadPtr(addr))
{
wMenu->addAction(mFollowAddress);
wMenu->addAction(mFollowDumpAddress);
dsint apiaddr = apiAddressFromString(mCurList->getCellContent(mCurList->getInitialSelection(), 1));
if(apiaddr)
wMenu->addAction(mFollowApiAddress);
wMenu->addSeparator();
wMenu->addAction(mToggleBreakpoint);
wMenu->addAction(mSetBreakpointOnAllCommands);
wMenu->addAction(mRemoveBreakpointOnAllCommands);
if(apiaddr)
{
char label[MAX_LABEL_SIZE] = "";
if(DbgGetLabelAt(apiaddr, SEG_DEFAULT, label))
{
wMenu->addSeparator();
mSetBreakpointOnAllApiCalls->setText(tr("Set breakpoint on all calls to %1").arg(label));
wMenu->addAction(mSetBreakpointOnAllApiCalls);
mRemoveBreakpointOnAllApiCalls->setText(tr("Remove breakpoint on all calls to %1").arg(label));
wMenu->addAction(mRemoveBreakpointOnAllApiCalls);
}
}
wMenu->addSeparator();
wMenu->addAction(mToggleBookmark);
}
if(this->mCommands.size() > 0)
{
wMenu->addSeparator();
for(auto i = 0; i < this->mCommandTitles.size(); i++)
{
QAction* newCommandAction = new QAction(this->mCommandTitles.at(i), wMenu);
newCommandAction->setData(QVariant(mCommands.at(i)));
connect(newCommandAction, SIGNAL(triggered()), this, SLOT(referenceExecCommand()));
wMenu->addAction(newCommandAction);
}
}
}
void ReferenceView::followAddress()
{
DbgCmdExecDirect(QString("disasm " + mCurList->getCellContent(mCurList->getInitialSelection(), 0)).toUtf8().constData());
}
void ReferenceView::followDumpAddress()
{
DbgCmdExecDirect(QString("dump " + mCurList->getCellContent(mCurList->getInitialSelection(), 0)).toUtf8().constData());
}
void ReferenceView::followApiAddress()
{
dsint apiValue = apiAddressFromString(mCurList->getCellContent(mCurList->getInitialSelection(), 1));
DbgCmdExecDirect(QString("disasm " + ToPtrString(apiValue)).toUtf8().constData());
}
void ReferenceView::followGenericAddress()
{
auto addr = DbgValFromString(mCurList->getCellContent(mCurList->getInitialSelection(), 0).toUtf8().constData());
if(!addr)
return;
if(DbgFunctions()->MemIsCodePage(addr, false))
followAddress();
else
{
followDumpAddress();
emit Bridge::getBridge()->getDumpAttention();
}
}
void ReferenceView::setBreakpointAt(int row, BPSetAction action)
{
if(!DbgIsDebugging())
return;
if(!mCurList->getRowCount())
return;
QString addrText = mCurList->getCellContent(row, 0).toUtf8().constData();
duint wVA;
if(!DbgFunctions()->ValFromString(addrText.toUtf8().constData(), &wVA))
return;
if(!DbgMemIsValidReadPtr(wVA))
return;
BPXTYPE wBpType = DbgGetBpxTypeAt(wVA);
QString wCmd;
if((wBpType & bp_normal) == bp_normal)
{
if(action == Toggle || action == Remove)
wCmd = "bc " + ToPtrString(wVA);
else if(action == Disable)
wCmd = "bpd " + ToPtrString(wVA);
else if(action == Enable)
wCmd = "bpe " + ToPtrString(wVA);
}
else if(wBpType == bp_none && (action == Toggle || action == Enable))
{
wCmd = "bp " + ToPtrString(wVA);
}
DbgCmdExecDirect(wCmd.toUtf8().constData());
}
void ReferenceView::toggleBreakpoint()
{
if(!DbgIsDebugging())
return;
if(!mCurList->getRowCount())
return;
setBreakpointAt(mCurList->getInitialSelection(), Toggle);
}
void ReferenceView::setBreakpointOnAllCommands()
{
GuiUpdateDisable();
for(int i = 0; i < mCurList->getRowCount(); i++)
setBreakpointAt(i, Enable);
GuiUpdateEnable(true);
}
void ReferenceView::removeBreakpointOnAllCommands()
{
GuiUpdateDisable();
for(int i = 0; i < mCurList->getRowCount(); i++)
setBreakpointAt(i, Remove);
GuiUpdateEnable(true);
}
void ReferenceView::setBreakpointOnAllApiCalls()
{
if(!mCurList->getRowCount())
return;
dsint apiaddr = apiAddressFromString(mCurList->getCellContent(mCurList->getInitialSelection(), 1));
if(!apiaddr)
return;
QString apiText = mCurList->getCellContent(mCurList->getInitialSelection(), 1);
GuiUpdateDisable();
for(int i = 0; i < mCurList->getRowCount(); i++)
if(mCurList->getCellContent(i, 1) == apiText)
setBreakpointAt(i, Enable);
GuiUpdateEnable(true);
}
void ReferenceView::removeBreakpointOnAllApiCalls()
{
if(!mCurList->getRowCount())
return;
dsint apiaddr = apiAddressFromString(mCurList->getCellContent(mCurList->getInitialSelection(), 1));
if(!apiaddr)
return;
QString apiText = mCurList->getCellContent(mCurList->getInitialSelection(), 1);
GuiUpdateDisable();
for(int i = 0; i < mCurList->getRowCount(); i++)
if(mCurList->getCellContent(i, 1) == apiText)
setBreakpointAt(i, Remove);
GuiUpdateEnable(true);
}
void ReferenceView::toggleBookmark()
{
if(!DbgIsDebugging())
return;
if(!mCurList->getRowCount())
return;
QString addrText = mCurList->getCellContent(mCurList->getInitialSelection(), 0);
duint wVA;
if(!DbgFunctions()->ValFromString(addrText.toUtf8().constData(), &wVA))
return;
if(!DbgMemIsValidReadPtr(wVA))
return;
bool result;
if(DbgGetBookmarkAt(wVA))
result = DbgSetBookmarkAt(wVA, false);
else
result = DbgSetBookmarkAt(wVA, true);
if(!result)
SimpleErrorBox(this, tr("Error!"), tr("DbgSetBookmarkAt failed!"));
GuiUpdateAllViews();
}
dsint ReferenceView::apiAddressFromString(const QString & s)
{
QRegExp regEx("call.+<(.+)>");
regEx.indexIn(s);
QStringList list = regEx.capturedTexts();
if(list.length() < 2)
return 0;
QString match = list[1];
if(match[0] == QChar('&'))
match.remove(0, 1);
duint value;
return DbgFunctions()->ValFromString(match.toUtf8().constData(), &value) && DbgMemIsValidReadPtr(value) ? value : 0;
}
void ReferenceView::referenceExecCommand()
{
QAction* act = qobject_cast<QAction*>(sender());
if(act != nullptr)
{
QString command = act->data().toString();
for(int selected : mCurList->getSelection()) //to do: enable multi-selection
{
QString specializedCommand = command;
for(int i = 0; i < mCurList->getColumnCount(); i++)
{
QString token = "$" + QString::number(i);
if(specializedCommand.contains(token))
specializedCommand.replace(token, mCurList->getCellContent(selected, i));
}
DbgCmdExec(specializedCommand);
}
}
}
void ReferenceView::mouseReleaseEvent(QMouseEvent* event)
{
if(mParent)
{
if(event->button() == Qt::ForwardButton)
mParent->setCurrentIndex(std::min(mParent->currentIndex() + 1, mParent->count()));
else if(event->button() == Qt::BackButton)
mParent->setCurrentIndex(std::max(mParent->currentIndex() - 1, 0));
}
}