diff --git a/docs/source/user-docs/shortcuts.rst b/docs/source/user-docs/shortcuts.rst index 9f95a1856..e6f06a576 100644 --- a/docs/source/user-docs/shortcuts.rst +++ b/docs/source/user-docs/shortcuts.rst @@ -76,6 +76,8 @@ Disassembly View Shortcuts +-------------+----------------------------------+ | Ctrl/Cmd+= | Reset zoom | +-------------+----------------------------------+ +| H | Toggle immediate base | ++-------------+----------------------------------+ Graph View Shortcuts -------------------- diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 6ade8e806..5ee722dfa 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -699,14 +699,19 @@ void CutterCore::delFlag(const QString &name) emit flagsChanged(); } +QJsonObject CutterCore::getInstructionObject(RVA addr) +{ + return Core()->cmdj("aoj @ " + QString::number(addr)).array().first().toObject(); +} + QString CutterCore::getInstructionBytes(RVA addr) { - return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()[RJsonKey::bytes].toString(); + return getInstructionObject(addr)[RJsonKey::bytes].toString(); } QString CutterCore::getInstructionOpcode(RVA addr) { - return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()[RJsonKey::opcode].toString(); + return getInstructionObject(addr)[RJsonKey::opcode].toString(); } void CutterCore::editInstruction(RVA addr, const QString &inst) @@ -842,6 +847,18 @@ void CutterCore::setImmediateBase(const QString &r2BaseName, RVA offset) emit instructionChanged(offset); } +int CutterCore::getImmediateBase(RVA offset) +{ + QJsonArray result = this->cmdj(QString("ahj %1").arg(offset)).array(); + for (const auto& n: result) { + QJsonValue immbase = n.toObject()["immbase"]; + if (immbase.isDouble()) { + return immbase.toInt(); + } + } + return 0; +} + void CutterCore::setCurrentBits(int bits, RVA offset) { if (offset == RVA_INVALID) { @@ -1229,8 +1246,7 @@ QJsonDocument CutterCore::getRegistersInfo() RVA CutterCore::getOffsetJump(RVA addr) { bool ok; - RVA value = cmdj("aoj @" + QString::number( - addr)).array().first().toObject().value(RJsonKey::jump).toVariant().toULongLong(&ok); + RVA value = getInstructionObject(addr).value(RJsonKey::jump).toVariant().toULongLong(&ok); if (!ok) { return RVA_INVALID; diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 790a648f9..2568df9e5 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -191,9 +191,17 @@ class CUTTER_EXPORT CutterCore: public QObject QString nearestFlag(RVA offset, RVA *flagOffsetOut); void triggerFlagsChanged(); + /** + * @brief Returns json data of an instruction + * @param addr Address of the instruction + * @returns Instruction data + */ + QJsonObject getInstructionObject(RVA addr); + /* Edition functions */ QString getInstructionBytes(RVA addr); QString getInstructionOpcode(RVA addr); + void editInstruction(RVA addr, const QString &inst); void nopInstruction(RVA addr); void jmpReverse(RVA addr); @@ -232,6 +240,7 @@ class CUTTER_EXPORT CutterCore: public QObject void delComment(RVA addr); QString getCommentAt(RVA addr); void setImmediateBase(const QString &r2BaseName, RVA offset = RVA_INVALID); + int getImmediateBase(RVA offset = RVA_INVALID); void setCurrentBits(int bits, RVA offset = RVA_INVALID); /** diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index f5fc4da3a..ddfd81d29 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -125,6 +125,10 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main addSetBaseMenu(); + initAction(&actionToggleBase, tr("Toggle Immediate Base (Hex/Dec)"), + SLOT(on_actionToggleBase_triggered()), getToggleBaseSequence()); + addAction(&actionToggleBase); + addSetBitsMenu(); structureOffsetMenu = addMenu(tr("Structure offset")); @@ -400,6 +404,7 @@ void DisassemblyContextMenu::setOffset(RVA offset) this->offset = offset; this->actionSetFunctionVarTypes.setVisible(true); + actionToggleBase.setVisible(checkImmediateBaseMenu(Core()->getInstructionObject(offset))); } void DisassemblyContextMenu::setCanCopy(bool enabled) @@ -412,14 +417,17 @@ void DisassemblyContextMenu::setCurHighlightedWord(const QString &text) this->curHighlightedWord = text; } +// Check if it makes sense to show the immediate base menu +bool DisassemblyContextMenu::checkImmediateBaseMenu(const QJsonObject& instObject) { + auto keys = instObject.keys(); + return keys.contains("val") || keys.contains("ptr"); +} + void DisassemblyContextMenu::aboutToShowSlot() { - // check if set immediate base menu makes sense - QJsonObject instObject = Core()->cmdj("aoj @ " + QString::number( - offset)).array().first().toObject(); - auto keys = instObject.keys(); - bool immBase = keys.contains("val") || keys.contains("ptr"); - setBaseMenu->menuAction()->setVisible(immBase); + QJsonObject instObject = Core()->getInstructionObject(offset); + + setBaseMenu->menuAction()->setVisible(checkImmediateBaseMenu(instObject)); setBitsMenu->menuAction()->setVisible(true); // Create structure offset menu if it makes sense @@ -674,6 +682,11 @@ QKeySequence DisassemblyContextMenu::getUndefineFunctionSequence() const return {Qt::Key_U}; } +QKeySequence DisassemblyContextMenu::getToggleBaseSequence() const +{ + return {Qt::Key_H}; +} + void DisassemblyContextMenu::on_actionEditInstruction_triggered() { @@ -803,6 +816,17 @@ void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered() } } +// TODO: requires the user to trigger the action twice if the number is in decimals +void DisassemblyContextMenu::on_actionToggleBase_triggered() +{ + int base = Core()->getImmediateBase(offset); + if (base == 10) { + setBase("h"); + } else { + setBase("d"); + } +} + void DisassemblyContextMenu::on_actionAddFlag_triggered() { FlagDialog dialog(offset, this->parentWidget()); diff --git a/src/menus/DisassemblyContextMenu.h b/src/menus/DisassemblyContextMenu.h index bfffe9128..d113ce1fd 100644 --- a/src/menus/DisassemblyContextMenu.h +++ b/src/menus/DisassemblyContextMenu.h @@ -68,6 +68,8 @@ private slots: void on_actionSetToData_triggered(); void on_actionSetToDataEx_triggered(); + void on_actionToggleBase_triggered(); + /** * @brief Executed on selecting an offset from the structureOffsetMenu * Uses the applyStructureOffset() function of CutterCore to apply the @@ -101,6 +103,7 @@ private slots: QKeySequence getDefineNewFunctionSequence() const; QKeySequence getUndefineFunctionSequence() const; QKeySequence getEditFunctionSequence() const; + QKeySequence getToggleBaseSequence() const; QList getAddBPSequence() const; /** @@ -156,6 +159,7 @@ private slots: QAction actionSetBaseIPAddr; QAction actionSetBaseSyscall; QAction actionSetBaseString; + QAction actionToggleBase; QMenu *setBitsMenu; QAction actionSetBits16; @@ -229,6 +233,14 @@ private slots: }; QVector getThingUsedHere(RVA offset); + /** + * @brief Checks if it makes sense to display the immediate base menu + * @param instObject Object with instruction data + * @returns Return true if the it makes sense to display the set immediate base menu + * return false otherwise. + */ + bool checkImmediateBaseMenu(const QJsonObject& instObject); + void updateTargetMenuActions(const QVector &targets); }; #endif // DISASSEMBLYCONTEXTMENU_H