Add context menu entries for target address (#1712)

* Refactor "used here" logic.
* Add menu for showing instruction target.
This commit is contained in:
karliss 2019-08-14 21:47:30 +03:00 committed by Itay Cohen
parent 468100c52d
commit f50fecc57b
3 changed files with 104 additions and 31 deletions

View File

@ -915,7 +915,7 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
auto createAddNewWidgetAction = [this, menu, address](QString label, MemoryWidgetType type) {
QAction *action = new QAction(label, menu);
connect(action, &QAction::triggered, this, [this, address, type](){
addNewMemoryWidget(type, address, true);
addNewMemoryWidget(type, address, false);
});
menu->addAction(action);
};

View File

@ -18,7 +18,7 @@
#include <QApplication>
#include <QPushButton>
DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow* mainWindow)
DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *mainWindow)
: QMenu(parent),
offset(0),
canCopy(false),
@ -27,7 +27,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow* main
initAction(&actionCopy, tr("Copy"), SLOT(on_actionCopy_triggered()), getCopySequence());
addAction(&actionCopy);
initAction(&actionCopyAddr, tr("Copy address"), SLOT(on_actionCopyAddr_triggered()), getCopyAddressSequence());
initAction(&actionCopyAddr, tr("Copy address"), SLOT(on_actionCopyAddr_triggered()),
getCopyAddressSequence());
addAction(&actionCopyAddr);
initAction(&showInSubmenu, tr("Show in"), nullptr);
@ -112,9 +113,6 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow* main
DisassemblyContextMenu::~DisassemblyContextMenu()
{
for (QAction *action : anonymousActions) {
delete action;
}
}
void DisassemblyContextMenu::addSetBaseMenu()
@ -249,6 +247,65 @@ void DisassemblyContextMenu::addDebugMenu()
debugMenu->addAction(&actionSetPC);
}
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
{
QVector<ThingUsedHere> result;
const QJsonArray array = Core()->cmdj("anj @ " + QString::number(offset)).array();
result.reserve(array.size());
for (const auto &thing : array) {
auto obj = thing.toObject();
RVA offset = obj["offset"].toVariant().toULongLong();
QString name = obj["name"].toString();
QString typeString = obj["type"].toString();
ThingUsedHere::Type type = ThingUsedHere::Type::Address;
if (typeString == "var") {
type = ThingUsedHere::Type::Var;
} else if (typeString == "flag") {
type = ThingUsedHere::Type::Flag;
} else if (typeString == "function") {
type = ThingUsedHere::Type::Function;
} else if (typeString == "address") {
type = ThingUsedHere::Type::Address;
}
result.push_back(ThingUsedHere{name, offset, type});
}
return result;
}
void DisassemblyContextMenu::updateTargetMenuActions(const QVector<ThingUsedHere> &targets)
{
for (auto action : showTargetMenuActions) {
removeAction(action);
auto menu = action->menu();
if (menu) {
menu->deleteLater();
}
action->deleteLater();
}
showTargetMenuActions.clear();
for (auto &target : targets) {
QString name;
if (target.name.isEmpty()) {
name = tr("%1 (used here)").arg(RAddressString(target.offset));
} else {
name = tr("%1 (%2)").arg(target.name, RAddressString(target.offset));
}
auto action = new QAction(name, this);
showTargetMenuActions.append(action);
auto menu = mainWindow->createShowInMenu(this, target.offset);
action->setMenu(menu);
QAction *copyAddress = new QAction(tr("Copy address"), menu);
RVA offset = target.offset;
connect(copyAddress, &QAction::triggered, copyAddress, [offset]() {
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(RAddressString(offset));
});
menu->addSeparator();
menu->addAction(copyAddress);
}
insertActions(copySeparator, showTargetMenuActions);
}
void DisassemblyContextMenu::setOffset(RVA offset)
{
this->offset = offset;
@ -362,24 +419,22 @@ void DisassemblyContextMenu::aboutToShowSlot()
// Only show "rename X used here" if there is something to rename
QJsonArray thingUsedHereArray = Core()->cmdj("anj @ " + QString::number(offset)).array();
if (!thingUsedHereArray.isEmpty()) {
auto thingsUsedHere = getThingUsedHere(offset);
if (!thingsUsedHere.isEmpty()) {
actionRenameUsedHere.setVisible(true);
QJsonObject thingUsedHere = thingUsedHereArray.first().toObject();
if (thingUsedHere["type"] == "address") {
RVA offset = thingUsedHere["offset"].toVariant().toULongLong();
auto &thingUsedHere = thingsUsedHere.first();
if (thingUsedHere.type == ThingUsedHere::Type::Address) {
RVA offset = thingUsedHere.offset;
actionRenameUsedHere.setText(tr("Add flag at %1 (used here)").arg(RAddressString(offset)));
} else {
if (thingUsedHere["type"] == "function") {
actionRenameUsedHere.setText(tr("Rename \"%1\"").arg(thingUsedHere["name"].toString()));
}
else {
actionRenameUsedHere.setText(tr("Rename \"%1\" (used here)").arg(thingUsedHere["name"].toString()));
}
} else if (thingUsedHere.type == ThingUsedHere::Type::Function) {
actionRenameUsedHere.setText(tr("Rename \"%1\"").arg(thingUsedHere.name));
} else {
actionRenameUsedHere.setText(tr("Rename \"%1\" (used here)").arg(thingUsedHere.name));
}
} else {
actionRenameUsedHere.setVisible(false);
}
updateTargetMenuActions(thingsUsedHere);
// Decide to show Reverse jmp option
showReverseJmpQuery();
@ -686,38 +741,39 @@ void DisassemblyContextMenu::on_actionRename_triggered()
void DisassemblyContextMenu::on_actionRenameUsedHere_triggered()
{
QJsonArray array = Core()->cmdj("anj @ " + QString::number(offset)).array();
auto array = getThingUsedHere(offset);
if (array.isEmpty()) {
return;
}
QJsonObject thingUsedHere = array.first().toObject();
QString type = thingUsedHere.value("type").toString();
auto thingUsedHere = array.first();
RenameDialog dialog(this);
QString oldName;
auto type = thingUsedHere.type;
if (type == "address") {
RVA offset = thingUsedHere["offset"].toVariant().toULongLong();
if (type == ThingUsedHere::Type::Address) {
RVA offset = thingUsedHere.offset;
dialog.setWindowTitle(tr("Add flag at %1").arg(RAddressString(offset)));
dialog.setName("label." + QString::number(offset, 16));
} else {
oldName = thingUsedHere.value("name").toString();
oldName = thingUsedHere.name;
dialog.setWindowTitle(tr("Rename %1").arg(oldName));
dialog.setName(oldName);
}
if (dialog.exec()) {
QString newName = dialog.getName().trimmed();
if (!newName.isEmpty()) {
Core()->cmd("an " + newName + " @ " + QString::number(offset));
if (type == "address" || type == "flag") {
if (type == ThingUsedHere::Type::Address || type == ThingUsedHere::Type::Address) {
Core()->triggerFlagsChanged();
} else if (type == "var") {
} else if (type == ThingUsedHere::Type::Var) {
Core()->triggerVarsChanged();
} else if (type == "function") {
} else if (type == ThingUsedHere::Type::Function) {
Core()->triggerFunctionRenamed(oldName, newName);
}
}
@ -729,7 +785,8 @@ void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
RAnalFunction *fcn = Core()->functionAt(offset);
if (!fcn) {
QMessageBox::critical(this, tr("Re-type function local vars"), tr("You must be in a function to define variable types."));
QMessageBox::critical(this, tr("Re-type function local vars"),
tr("You must be in a function to define variable types."));
return;
}
@ -873,7 +930,7 @@ void DisassemblyContextMenu::setToData(int size, int repeat)
QAction *DisassemblyContextMenu::addAnonymousAction(QString name, const char *slot,
QKeySequence keySequence)
{
auto action = new QAction();
auto action = new QAction(this);
addAction(action);
anonymousActions.append(action);
initAction(action, name, slot, keySequence);

View File

@ -10,7 +10,7 @@ class DisassemblyContextMenu : public QMenu
Q_OBJECT
public:
DisassemblyContextMenu(QWidget *parent, MainWindow* mainWindow);
DisassemblyContextMenu(QWidget *parent, MainWindow *mainWindow);
~DisassemblyContextMenu();
signals:
@ -101,7 +101,7 @@ private:
RVA offset;
bool canCopy;
QString curHighlightedWord; // The current highlighted word
MainWindow* mainWindow;
MainWindow *mainWindow;
QList<QAction *> anonymousActions;
@ -166,6 +166,7 @@ private:
QAction actionSetToDataQword;
QAction showInSubmenu;
QList<QAction*> showTargetMenuActions;
// For creating anonymous entries (that are always visible)
QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut);
@ -184,5 +185,20 @@ private:
void addSetToDataMenu();
void addEditMenu();
void addDebugMenu();
struct ThingUsedHere {
QString name;
RVA offset;
enum class Type {
Var,
Function,
Flag,
Address
};
Type type;
};
QVector<ThingUsedHere> getThingUsedHere(RVA offset);
void updateTargetMenuActions(const QVector<ThingUsedHere> &targets);
};
#endif // DISASSEMBLYCONTEXTMENU_H