Xref for function variables in disassembly view (#2297)

This commit is contained in:
NIRMAL MANOJ C 2020-07-29 01:19:50 +05:30 committed by GitHub
parent cf96775b74
commit f2a867ca28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 148 additions and 6 deletions

View File

@ -28,6 +28,7 @@ Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
namespace RJsonKey { namespace RJsonKey {
R_JSON_KEY(addr); R_JSON_KEY(addr);
R_JSON_KEY(addrs);
R_JSON_KEY(addr_end); R_JSON_KEY(addr_end);
R_JSON_KEY(arrow); R_JSON_KEY(arrow);
R_JSON_KEY(baddr); R_JSON_KEY(baddr);
@ -476,6 +477,18 @@ QJsonDocument CutterCore::cmdj(const char *str)
return doc; return doc;
} }
QJsonDocument CutterCore::cmdjAt(const char *str, RVA address)
{
QJsonDocument res;
RVA oldOffset = getOffset();
seekSilent(address);
res = cmdj(str);
seekSilent(oldOffset);
return res;
}
QString CutterCore::cmdTask(const QString &str) QString CutterCore::cmdTask(const QString &str)
{ {
R2Task task(str); R2Task task(str);
@ -3459,6 +3472,37 @@ BlockStatistics CutterCore::getBlockStatistics(unsigned int blocksCount)
return blockStats; return blockStats;
} }
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites, RVA offset)
{
QList<XrefDescription> xrefList = QList<XrefDescription>();
QJsonArray xrefsArray;
if (findWrites) {
xrefsArray = cmdjAt("afvWj", offset).array();
} else {
xrefsArray = cmdjAt("afvRj", offset).array();
}
for (const QJsonValue &value : xrefsArray) {
QJsonObject xrefObject = value.toObject();
QString name = xrefObject[RJsonKey::name].toString();
if (name == variableName) {
QJsonArray addressArray = xrefObject[RJsonKey::addrs].toArray();
for (const QJsonValue &address : addressArray) {
XrefDescription xref;
RVA addr = address.toVariant().toULongLong();
xref.from = addr;
xref.to = addr;
if (findWrites) {
xref.from_str = RAddressString(addr);
} else {
xref.to_str = RAddressString(addr);
}
xrefList << xref;
}
}
}
return xrefList;
}
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function, QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType) const QString &filterType)
{ {

View File

@ -109,6 +109,7 @@ public:
QJsonDocument cmdj(const char *str); QJsonDocument cmdj(const char *str);
QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); } QJsonDocument cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
QJsonDocument cmdjAt(const char *str, RVA address);
QStringList cmdList(const char *str) { return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS); } QStringList cmdList(const char *str) { return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS); }
QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); } QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); }
QString cmdTask(const QString &str); QString cmdTask(const QString &str);
@ -567,7 +568,17 @@ public:
QList<QJsonObject> getRegisterRefs(int depth = 6); QList<QJsonObject> getRegisterRefs(int depth = 6);
QVector<RegisterRefValueDescription> getRegisterRefValues(); QVector<RegisterRefValueDescription> getRegisterRefValues();
QList<VariableDescription> getVariables(RVA at); QList<VariableDescription> getVariables(RVA at);
/**
* @brief Fetches all the writes or reads to the specified local variable 'variableName'
* in the function in which the specified offset is a part of.
* @param variableName Name of the local variable.
* @param findWrites If this is true, then locations at which modification happen to the specified
* local variable is fetched. Else, the locations at which the local is variable is read is fetched.
* @param offset An offset in the function in which the specified local variable exist.
* @return A list of XrefDescriptions that contains details of all the writes or reads that happen to the
* variable 'variableName'.
*/
QList<XrefDescription> getXRefsForVariable(QString variableName, bool findWrites, RVA offset);
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType = QString()); const QString &filterType = QString());

View File

@ -138,12 +138,14 @@ void XrefsDialog::updateLabels(QString name)
ui->label_xFrom->setText(tr("X-Refs from %1:").arg(name)); ui->label_xFrom->setText(tr("X-Refs from %1:").arg(name));
} }
void XrefsDialog::updateLabelsForVariables(QString name)
{
ui->label_xTo->setText(tr("Writes to %1").arg(name));
ui->label_xFrom->setText(tr("Reads from %1").arg(name));
}
void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function) void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function)
{ {
TempConfig tempConfig;
tempConfig.set("scr.html", false);
tempConfig.set("scr.color", COLOR_MODE_DISABLED);
setWindowTitle(tr("X-Refs for %1").arg(name)); setWindowTitle(tr("X-Refs for %1").arg(name));
updateLabels(name); updateLabels(name);
@ -160,6 +162,27 @@ void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function
} }
} }
void XrefsDialog::fillRefsForVariable(QString nameOfVariable, RVA offset)
{
setWindowTitle(tr("X-Refs for %1").arg(nameOfVariable));
updateLabelsForVariables(nameOfVariable);
// Initialize Model
toModel.readForVariable(nameOfVariable, true, offset);
fromModel.readForVariable(nameOfVariable, false, offset);
// Hide irrelevant column 1: which shows type
ui->fromTreeWidget->hideColumn(XrefModel::Columns::TYPE);
ui->toTreeWidget->hideColumn(XrefModel::Columns::TYPE);
// Adjust columns to content
qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
// Automatically select the first line
if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
qhelpers::selectFirstItem(ui->fromTreeWidget);
}
}
QString XrefModel::xrefTypeString(const QString &type) QString XrefModel::xrefTypeString(const QString &type)
{ {
if (type == "CODE") { if (type == "CODE") {
@ -188,6 +211,14 @@ void XrefModel::readForOffset(RVA offset, bool to, bool whole_function)
endResetModel(); endResetModel();
} }
void XrefModel::readForVariable(QString nameOfVariable, bool write, RVA offset)
{
beginResetModel();
this->to = write;
xrefs = Core()->getXRefsForVariable(nameOfVariable, write, offset);
endResetModel();
}
int XrefModel::rowCount(const QModelIndex &parent) const int XrefModel::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent) Q_UNUSED(parent)

View File

@ -19,7 +19,7 @@ public:
XrefModel(QObject *parent = nullptr); XrefModel(QObject *parent = nullptr);
void readForOffset(RVA offset, bool to, bool whole_function); void readForOffset(RVA offset, bool to, bool whole_function);
void readForVariable(QString nameOfVariable, bool write, RVA offset);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
@ -48,6 +48,13 @@ public:
~XrefsDialog(); ~XrefsDialog();
void fillRefsForAddress(RVA addr, QString name, bool whole_function); void fillRefsForAddress(RVA addr, QString name, bool whole_function);
/**
* @brief Initializes toModel and fromModel with the details about the references to the specified
* local variable 'nameOfVariable'.
* @param nameOfVarible Name of the local variable for which the references are being initialized.
* @param offset An offset in the function in which the specified local variable exist.
*/
void fillRefsForVariable(QString nameOfVariable, RVA offset);
private slots: private slots:
QString normalizeAddr(const QString &addr) const; QString normalizeAddr(const QString &addr) const;
@ -69,6 +76,7 @@ private:
std::unique_ptr<Ui::XrefsDialog> ui; std::unique_ptr<Ui::XrefsDialog> ui;
void updateLabels(QString name); void updateLabels(QString name);
void updateLabelsForVariables(QString name);
void updatePreview(RVA addr); void updatePreview(RVA addr);
}; };

View File

@ -39,6 +39,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
actionRenameUsedHere(this), actionRenameUsedHere(this),
actionSetFunctionVarTypes(this), actionSetFunctionVarTypes(this),
actionXRefs(this), actionXRefs(this),
actionXRefsForVariables(this),
actionDisplayOptions(this), actionDisplayOptions(this),
actionDeleteComment(this), actionDeleteComment(this),
actionDeleteFlag(this), actionDeleteFlag(this),
@ -142,6 +143,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
SLOT(on_actionXRefs_triggered()), getXRefSequence()); SLOT(on_actionXRefs_triggered()), getXRefSequence());
addAction(&actionXRefs); addAction(&actionXRefs);
initAction(&actionXRefsForVariables, tr("X-Refs for local variables"),
SLOT(on_actionXRefsForVariables_triggered()), QKeySequence({Qt::SHIFT + Qt::Key_X}));
addAction(&actionXRefsForVariables);
initAction(&actionDisplayOptions, tr("Show Options"), initAction(&actionDisplayOptions, tr("Show Options"),
SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence()); SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence());
@ -165,6 +170,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
connect(this, &DisassemblyContextMenu::aboutToShow, connect(this, &DisassemblyContextMenu::aboutToShow,
this, &DisassemblyContextMenu::aboutToShowSlot); this, &DisassemblyContextMenu::aboutToShowSlot);
connect(this, &DisassemblyContextMenu::aboutToHide,
this, &DisassemblyContextMenu::aboutToHideSlot);
} }
DisassemblyContextMenu::~DisassemblyContextMenu() DisassemblyContextMenu::~DisassemblyContextMenu()
@ -559,6 +566,17 @@ void DisassemblyContextMenu::aboutToShowSlot()
pluginAction->setData(QVariant::fromValue(offset)); pluginAction->setData(QVariant::fromValue(offset));
} }
} }
bool isLocalVar = isHighlightedWordLocalVar();
actionXRefsForVariables.setVisible(isLocalVar);
if (isLocalVar) {
actionXRefsForVariables.setText(tr("X-Refs for %1").arg(curHighlightedWord));
}
}
void DisassemblyContextMenu::aboutToHideSlot()
{
actionXRefsForVariables.setVisible(true);
} }
QKeySequence DisassemblyContextMenu::getCopySequence() const QKeySequence DisassemblyContextMenu::getCopySequence() const
@ -880,6 +898,15 @@ void DisassemblyContextMenu::on_actionXRefs_triggered()
dialog.exec(); dialog.exec();
} }
void DisassemblyContextMenu::on_actionXRefsForVariables_triggered()
{
if (isHighlightedWordLocalVar()) {
XrefsDialog dialog(mainWindow, nullptr);
dialog.fillRefsForVariable(curHighlightedWord, offset);
dialog.exec();
}
}
void DisassemblyContextMenu::on_actionDisplayOptions_triggered() void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
{ {
PreferencesDialog dialog(this->window()); PreferencesDialog dialog(this->window());
@ -1081,3 +1108,14 @@ void DisassemblyContextMenu::initAction(QAction *action, QString name,
action->setShortcuts(keySequenceList); action->setShortcuts(keySequenceList);
action->setShortcutContext(Qt::WidgetWithChildrenShortcut); action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
} }
bool DisassemblyContextMenu::isHighlightedWordLocalVar()
{
QList<VariableDescription> variables = Core()->getVariables(offset);
for (const VariableDescription &var : variables) {
if (var.name == curHighlightedWord) {
return true;
}
}
return false;
}

View File

@ -29,6 +29,7 @@ public slots:
private slots: private slots:
void aboutToShowSlot(); void aboutToShowSlot();
void aboutToHideSlot();
void on_actionEditFunction_triggered(); void on_actionEditFunction_triggered();
void on_actionEditInstruction_triggered(); void on_actionEditInstruction_triggered();
@ -46,6 +47,7 @@ private slots:
void on_actionRenameUsedHere_triggered(); void on_actionRenameUsedHere_triggered();
void on_actionSetFunctionVarTypes_triggered(); void on_actionSetFunctionVarTypes_triggered();
void on_actionXRefs_triggered(); void on_actionXRefs_triggered();
void on_actionXRefsForVariables_triggered();
void on_actionDisplayOptions_triggered(); void on_actionDisplayOptions_triggered();
void on_actionDeleteComment_triggered(); void on_actionDeleteComment_triggered();
@ -131,6 +133,7 @@ private:
QAction actionRenameUsedHere; QAction actionRenameUsedHere;
QAction actionSetFunctionVarTypes; QAction actionSetFunctionVarTypes;
QAction actionXRefs; QAction actionXRefs;
QAction actionXRefsForVariables;
QAction actionDisplayOptions; QAction actionDisplayOptions;
QAction actionDeleteComment; QAction actionDeleteComment;
@ -203,6 +206,13 @@ private:
void addBreakpointMenu(); void addBreakpointMenu();
void addDebugMenu(); void addDebugMenu();
/**
* @brief Checks if the currently highlighted word in the disassembly widget
* is a local variable or function paramter.
* @return Return true if the highlighted word is the name of a local variable or function parameter,
* return false otherwise.
*/
bool isHighlightedWordLocalVar();
struct ThingUsedHere { struct ThingUsedHere {
QString name; QString name;
RVA offset; RVA offset;