Register refs widget improvements (#2038)

* Move register refs to the new telescoping function and add an addressable context menu
This commit is contained in:
yossizap 2020-01-30 19:40:27 +02:00 committed by GitHub
parent 969ce5ac30
commit a1b5a41e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 164 additions and 97 deletions

View File

@ -1156,6 +1156,86 @@ QJsonDocument CutterCore::getSignatureInfo()
return cmdj("iCj");
}
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
static inline const QString appendVar(QString &dst, const QString val, const QString prepend_val,
const QString append_val)
{
if (!val.isEmpty()) {
dst += prepend_val + val + append_val;
}
return val;
}
RefDescription CutterCore::formatRefDesc(QJsonObject refItem)
{
RefDescription desc;
// Ignore empty refs and refs that only contain addr
if (refItem.size() <= 1) {
return desc;
}
QString str = refItem["string"].toVariant().toString();
if (!str.isEmpty()) {
desc.ref = str;
desc.refColor = ConfigColor("comment");
} else {
QString type, string;
do {
desc.ref += " ->";
appendVar(desc.ref, refItem["reg"].toVariant().toString(), " @", "");
appendVar(desc.ref, refItem["mapname"].toVariant().toString(), " (", ")");
appendVar(desc.ref, refItem["section"].toVariant().toString(), " (", ")");
appendVar(desc.ref, refItem["func"].toVariant().toString(), " ", "");
type = appendVar(desc.ref, refItem["type"].toVariant().toString(), " ", "");
appendVar(desc.ref, refItem["perms"].toVariant().toString(), " ", "");
appendVar(desc.ref, refItem["asm"].toVariant().toString(), " \"", "\"");
string = appendVar(desc.ref, refItem["string"].toVariant().toString(), " ", "");
if (!string.isNull()) {
// There is no point in adding ascii and addr info after a string
break;
}
if (!refItem["value"].isNull()) {
appendVar(desc.ref, RAddressString(refItem["value"].toVariant().toULongLong()), " ", "");
}
refItem = refItem["ref"].toObject();
} while (!refItem.empty());
// Set the ref's color according to the last item type
if (type == "ascii" || !string.isEmpty()) {
desc.refColor = ConfigColor("comment");
} else if (type == "program") {
desc.refColor = ConfigColor("fname");
} else if (type == "library") {
desc.refColor = ConfigColor("floc");
} else if (type == "stack") {
desc.refColor = ConfigColor("offset");
}
}
return desc;
}
QList<QJsonObject> CutterCore::getRegisterRefs(int depth)
{
QList<QJsonObject> ret;
if (!currentlyDebugging) {
return ret;
}
QJsonObject registers = cmdj("drj").object();
for (const QString &key : registers.keys()) {
QJsonObject reg;
reg["value"] = registers.value(key);
reg["ref"] = getAddrRefs(registers.value(key).toVariant().toULongLong(), depth);
reg["name"] = key;
ret.append(reg);
}
return ret;
}
QList<QJsonObject> CutterCore::getStack(int size, int depth)
{
QList<QJsonObject> stack;
@ -1321,26 +1401,6 @@ QJsonDocument CutterCore::getRegisterValues()
return cmdj("drj");
}
QList<RegisterRefDescription> CutterCore::getRegisterRefs()
{
QList<RegisterRefDescription> ret;
QJsonArray registerRefArray = cmdj("drrj").array();
for (const QJsonValue &value : registerRefArray) {
QJsonObject regRefObject = value.toObject();
RegisterRefDescription regRef;
regRef.reg = regRefObject[RJsonKey::reg].toString();
regRef.value = regRefObject[RJsonKey::value].toString();
regRef.ref = regRefObject[RJsonKey::ref].toString();
ret << regRef;
}
return ret;
}
QList<VariableDescription> CutterCore::getVariables(RVA at)
{
QList<VariableDescription> ret;

View File

@ -312,6 +312,11 @@ public:
* @param depth telescoping depth
*/
QJsonObject getAddrRefs(RVA addr, int depth);
/**
* @brief return a RefDescription with a formatted ref string and configured colors
* @param ref the "ref" JSON node from getAddrRefs
*/
RefDescription formatRefDesc(QJsonObject ref);
/**
* @brief Get a list of a given process's threads
* @param pid The pid of the process, -1 for the currently debugged process
@ -525,7 +530,11 @@ public:
BlockStatistics getBlockStatistics(unsigned int blocksCount);
QList<BreakpointDescription> getBreakpoints();
QList<ProcessDescription> getAllProcesses();
QList<RegisterRefDescription> getRegisterRefs();
/**
* @brief returns a list of reg values and their telescoped references
* @param depth telescoping depth
*/
QList<QJsonObject> getRegisterRefs(int depth = 6);
QJsonObject getRegisterJson();
QList<VariableDescription> getVariables(RVA at);

View File

@ -9,6 +9,7 @@
#include <QList>
#include <QStringList>
#include <QMetaType>
#include <QColor>
#include "core/CutterCommon.h"
struct FunctionDescription {
@ -308,10 +309,9 @@ struct ProcessDescription {
QString path;
};
struct RegisterRefDescription {
QString reg;
QString value;
struct RefDescription {
QString ref;
QColor refColor;
};
struct VariableDescription {
@ -357,7 +357,7 @@ Q_DECLARE_METATYPE(MemoryMapDescription)
Q_DECLARE_METATYPE(BreakpointDescription)
Q_DECLARE_METATYPE(BreakpointDescription::PositionType)
Q_DECLARE_METATYPE(ProcessDescription)
Q_DECLARE_METATYPE(RegisterRefDescription)
Q_DECLARE_METATYPE(RefDescription)
Q_DECLARE_METATYPE(VariableDescription)
#endif // DESCRIPTIONS_H

View File

@ -3,6 +3,7 @@
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include <QJsonObject>
#include <QMenu>
#include <QClipboard>
#include <QShortcut>
@ -38,7 +39,14 @@ QVariant RegisterRefModel::data(const QModelIndex &index, int role) const
case ValueColumn:
return registerRef.value;
case RefColumn:
return registerRef.ref;
return registerRef.refDesc.ref;
default:
return QVariant();
}
case Qt::ForegroundRole:
switch (index.column()) {
case RefColumn:
return registerRef.refDesc.refColor;
default:
return QVariant();
}
@ -93,7 +101,7 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex
case RegisterRefModel::RegColumn:
return leftRegRef.reg < rightRegRef.reg;
case RegisterRefModel::RefColumn:
return leftRegRef.ref < rightRegRef.ref;
return leftRegRef.refDesc.ref < rightRegRef.refDesc.ref;
case RegisterRefModel::ValueColumn:
return leftRegRef.value < rightRegRef.value;
default:
@ -106,7 +114,8 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex
RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::RegisterRefsWidget),
tree(new CutterTreeWidget(this))
tree(new CutterTreeWidget(this)),
addressableItemContextMenu(this, main)
{
ui->setupUi(this);
@ -122,6 +131,13 @@ RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) :
actionCopyValue = new QAction(tr("Copy register value"), this);
actionCopyRef = new QAction(tr("Copy register reference"), this);
addressableItemContextMenu.addAction(actionCopyValue);
addressableItemContextMenu.addAction(actionCopyRef);
addActions(addressableItemContextMenu.actions());
connect(ui->registerRefTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &RegisterRefsWidget::onCurrentChanged);
refreshDeferrer = createRefreshDeferrer([this](){
refreshRegisterRef();
});
@ -144,8 +160,8 @@ RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) :
copyClip(RegisterRefModel::RefColumn);
});
ui->registerRefTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->registerRefTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showRegRefContextMenu(const QPoint &)));
connect(ui->registerRefTreeView, &QMenu::customContextMenuRequested,
this, &RegisterRefsWidget::customMenuRequested);
connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] {
tree->showItemsNumber(registerRefProxyModel->rowCount());
@ -161,7 +177,19 @@ void RegisterRefsWidget::refreshRegisterRef()
}
registerRefModel->beginResetModel();
registerRefs = Core()->getRegisterRefs();
QList<QJsonObject> regRefs = Core()->getRegisterRefs();
registerRefs.clear();
for (const QJsonObject &reg : regRefs) {
RegisterRefDescription desc;
desc.value = RAddressString(reg["value"].toVariant().toULongLong());
desc.reg = reg["name"].toVariant().toString();
desc.refDesc = Core()->formatRefDesc(reg["ref"].toObject());
registerRefs.push_back(desc);
}
registerRefModel->endResetModel();
ui->registerRefTreeView->resizeColumnToContents(0);
@ -183,15 +211,27 @@ void RegisterRefsWidget::on_registerRefTreeView_doubleClicked(const QModelIndex
Core()->seekAndShow(item.value);
}
void RegisterRefsWidget::showRegRefContextMenu(const QPoint &pt)
void RegisterRefsWidget::customMenuRequested(QPoint pos)
{
QMenu *menu = new QMenu(ui->registerRefTreeView);
menu->clear();
menu->addAction(actionCopyValue);
menu->addAction(actionCopyRef);
addressableItemContextMenu.exec(ui->registerRefTreeView->viewport()->mapToGlobal(pos));
}
menu->exec(ui->registerRefTreeView->viewport()->mapToGlobal(pt));
delete menu;
void RegisterRefsWidget::onCurrentChanged(const QModelIndex &current, const QModelIndex &previous)
{
Q_UNUSED(current)
Q_UNUSED(previous)
auto currentIndex = ui->registerRefTreeView->selectionModel()->currentIndex();
// Use the value column as the offset
QString offsetString;
if (currentIndex.column() != RegisterRefModel::RefColumn) {
offsetString = currentIndex.data().toString();
} else {
offsetString = currentIndex.sibling(currentIndex.row(), RegisterRefModel::ValueColumn).data().toString();
}
RVA offset = Core()->math(offsetString);
addressableItemContextMenu.setTarget(offset);
}
void RegisterRefsWidget::copyClip(int column)

View File

@ -5,6 +5,7 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "CutterTreeWidget.h"
#include "menus/AddressableItemContextMenu.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@ -21,6 +22,12 @@ class RegisterRefsWidget;
class MainWindow;
class QTreeWidgetItem;
struct RegisterRefDescription {
QString reg;
QString value;
RefDescription refDesc;
};
Q_DECLARE_METATYPE(RegisterRefDescription)
class RegisterRefModel: public QAbstractListModel
{
@ -44,8 +51,6 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
};
class RegisterRefProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
@ -71,7 +76,8 @@ private slots:
void on_registerRefTreeView_doubleClicked(const QModelIndex &index);
void refreshRegisterRef();
void copyClip(int column);
void showRegRefContextMenu(const QPoint &pt);
void customMenuRequested(QPoint pos);
void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
private:
std::unique_ptr<Ui::RegisterRefsWidget> ui;
@ -79,10 +85,12 @@ private:
RegisterRefModel *registerRefModel;
RegisterRefProxyModel *registerRefProxyModel;
QList<RegisterRefDescription> registerRefs;
QAction *actionCopyValue;
QAction *actionCopyRef;
CutterTreeWidget *tree;
void setScrollMode();
RefreshDeferrer *refreshDeferrer;
QAction *actionCopyValue;
QAction *actionCopyRef;
AddressableItemContextMenu addressableItemContextMenu;
};

View File

@ -144,16 +144,6 @@ StackModel::StackModel(QObject *parent)
{
}
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
static inline const QString append_var(QString &dst, const QString val, const QString prepend_val,
const QString append_val)
{
if (!val.isEmpty()) {
dst += prepend_val + val + append_val;
}
return val;
}
void StackModel::reload()
{
QList<QJsonObject> stackItems = Core()->getStack();
@ -165,47 +155,8 @@ void StackModel::reload()
item.offset = stackItem["addr"].toVariant().toULongLong();
item.value = RAddressString(stackItem["value"].toVariant().toULongLong());
item.refDesc = Core()->formatRefDesc(stackItem["ref"].toObject());
QJsonObject refItem = stackItem["ref"].toObject();
if (!refItem.empty()) {
QString str = refItem["string"].toVariant().toString();
if (!str.isEmpty()) {
item.description = str;
item.descriptionColor = ConfigColor("comment");
} else {
QString type, string;
do {
item.description += " ->";
append_var(item.description, refItem["reg"].toVariant().toString(), " @", "");
append_var(item.description, refItem["mapname"].toVariant().toString(), " (", ")");
append_var(item.description, refItem["section"].toVariant().toString(), " (", ")");
append_var(item.description, refItem["func"].toVariant().toString(), " ", "");
type = append_var(item.description, refItem["type"].toVariant().toString(), " ", "");
append_var(item.description, refItem["perms"].toVariant().toString(), " ", "");
append_var(item.description, refItem["asm"].toVariant().toString(), " \"", "\"");
string = append_var(item.description, refItem["string"].toVariant().toString(), " ", "");
if (!string.isNull()) {
// There is no point in adding ascii and addr info after a string
break;
}
if (!refItem["value"].isNull()) {
append_var(item.description, RAddressString(refItem["value"].toVariant().toULongLong()), " ", "");
}
refItem = refItem["ref"].toObject();
} while (!refItem.empty());
// Set the description's color according to the last item type
if (type == "ascii" || !string.isEmpty()) {
item.descriptionColor = ConfigColor("comment");
} else if (type == "program") {
item.descriptionColor = ConfigColor("fname");
} else if (type == "library") {
item.descriptionColor = ConfigColor("floc");
} else if (type == "stack") {
item.descriptionColor = ConfigColor("offset");
}
}
}
values.push_back(item);
}
endResetModel();
@ -236,14 +187,14 @@ QVariant StackModel::data(const QModelIndex &index, int role) const
case ValueColumn:
return item.value;
case DescriptionColumn:
return item.description;
return item.refDesc.ref;
default:
return QVariant();
}
case Qt::ForegroundRole:
switch (index.column()) {
case DescriptionColumn:
return item.descriptionColor;
return item.refDesc.refColor;
default:
return QVariant();
}

View File

@ -22,8 +22,7 @@ public:
struct Item {
RVA offset;
QString value;
QString description;
QVariant descriptionColor;
RefDescription refDesc;
};
enum Column { OffsetColumn = 0, ValueColumn, DescriptionColumn, ColumnCount};