1 #include "DisassemblyContextMenu.h"
2 #include "dialogs/preferences/PreferencesDialog.h"
3 #include "dialogs/EditInstructionDialog.h"
4 #include "dialogs/CommentsDialog.h"
5 #include "dialogs/FlagDialog.h"
6 #include "dialogs/XrefsDialog.h"
7 #include "dialogs/EditVariablesDialog.h"
8 #include "dialogs/SetToDataDialog.h"
9 #include "dialogs/EditFunctionDialog.h"
10 #include "dialogs/LinkTypeDialog.h"
11 #include "dialogs/EditStringDialog.h"
12 #include "dialogs/BreakpointsDialog.h"
13 #include "MainWindow.h"
14 
15 #include <QtCore>
16 #include <QShortcut>
17 #include <QJsonArray>
18 #include <QClipboard>
19 #include <QApplication>
20 #include <QPushButton>
21 #include <QInputDialog>
22 
DisassemblyContextMenu(QWidget * parent,MainWindow * mainWindow)23 DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *mainWindow)
24     :   QMenu(parent),
25         offset(0),
26         canCopy(false),
27         mainWindow(mainWindow),
28         actionEditInstruction(this),
29         actionNopInstruction(this),
30         actionJmpReverse(this),
31         actionEditBytes(this),
32         actionCopy(this),
33         actionCopyAddr(this),
34         actionAddComment(this),
35         actionAnalyzeFunction(this),
36         actionEditFunction(this),
37         actionRename(this),
38         actionSetFunctionVarTypes(this),
39         actionXRefs(this),
40         actionXRefsForVariables(this),
41         actionDisplayOptions(this),
42         actionDeleteComment(this),
43         actionDeleteFlag(this),
44         actionDeleteFunction(this),
45         actionLinkType(this),
46         actionSetBaseBinary(this),
47         actionSetBaseOctal(this),
48         actionSetBaseDecimal(this),
49         actionSetBaseHexadecimal(this),
50         actionSetBasePort(this),
51         actionSetBaseIPAddr(this),
52         actionSetBaseSyscall(this),
53         actionSetBaseString(this),
54         actionSetBits16(this),
55         actionSetBits32(this),
56         actionSetBits64(this),
57         actionContinueUntil(this),
58         actionSetPC(this),
59         actionAddBreakpoint(this),
60         actionAdvancedBreakpoint(this),
61         actionSetToCode(this),
62         actionSetAsStringAuto(this),
63         actionSetAsStringRemove(this),
64         actionSetAsStringAdvanced(this),
65         actionSetToDataEx(this),
66         actionSetToDataByte(this),
67         actionSetToDataWord(this),
68         actionSetToDataDword(this),
69         actionSetToDataQword(this),
70         showInSubmenu(this)
71 {
72     initAction(&actionCopy, tr("Copy"), SLOT(on_actionCopy_triggered()), getCopySequence());
73     addAction(&actionCopy);
74 
75     initAction(&actionCopyAddr, tr("Copy address"), SLOT(on_actionCopyAddr_triggered()),
76                getCopyAddressSequence());
77     addAction(&actionCopyAddr);
78 
79     initAction(&showInSubmenu, tr("Show in"), nullptr);
80     addAction(&showInSubmenu);
81 
82     copySeparator = addSeparator();
83 
84     initAction(&actionAddComment, tr("Add Comment"),
85                SLOT(on_actionAddComment_triggered()), getCommentSequence());
86     addAction(&actionAddComment);
87 
88     initAction(&actionRename, tr("Rename or add flag"),
89                SLOT(on_actionRename_triggered()), getRenameSequence());
90     addAction(&actionRename);
91 
92     initAction(&actionSetFunctionVarTypes, tr("Re-type Local Variables"),
93                SLOT(on_actionSetFunctionVarTypes_triggered()), getRetypeSequence());
94     addAction(&actionSetFunctionVarTypes);
95 
96     initAction(&actionEditFunction, tr("Edit function"),
97                SLOT(on_actionEditFunction_triggered()), getEditFunctionSequence());
98     addAction(&actionEditFunction);
99 
100     initAction(&actionDeleteComment, tr("Delete comment"), SLOT(on_actionDeleteComment_triggered()));
101     addAction(&actionDeleteComment);
102 
103     initAction(&actionDeleteFlag, tr("Delete flag"), SLOT(on_actionDeleteFlag_triggered()));
104     addAction(&actionDeleteFlag);
105 
106     initAction(&actionDeleteFunction, tr("Undefine function"),
107                SLOT(on_actionDeleteFunction_triggered()), getUndefineFunctionSequence());
108     addAction(&actionDeleteFunction);
109 
110     initAction(&actionAnalyzeFunction, tr("Define function here"),
111                SLOT(on_actionAnalyzeFunction_triggered()), getDefineNewFunctionSequence());
112     addAction(&actionAnalyzeFunction);
113 
114     addSeparator();
115 
116     addSetBaseMenu();
117 
118     addSetBitsMenu();
119 
120     structureOffsetMenu = addMenu(tr("Structure offset"));
121     connect(structureOffsetMenu, &QMenu::triggered,
122             this, &DisassemblyContextMenu::on_actionStructureOffsetMenu_triggered);
123 
124     initAction(&actionLinkType, tr("Link Type to Address"),
125                SLOT(on_actionLinkType_triggered()), getLinkTypeSequence());
126     addAction(&actionLinkType);
127 
128     addSetAsMenu();
129 
130     addSeparator();
131 
132     initAction(&actionXRefs, tr("Show X-Refs"),
133                SLOT(on_actionXRefs_triggered()), getXRefSequence());
134     addAction(&actionXRefs);
135 
136     initAction(&actionXRefsForVariables, tr("X-Refs for local variables"),
137                 SLOT(on_actionXRefsForVariables_triggered()), QKeySequence({Qt::SHIFT + Qt::Key_X}));
138     addAction(&actionXRefsForVariables);
139 
140     initAction(&actionDisplayOptions, tr("Show Options"),
141                SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence());
142 
143     addSeparator();
144 
145     addEditMenu();
146 
147     addSeparator();
148 
149     addBreakpointMenu();
150     addDebugMenu();
151 
152     addSeparator();
153 
154     if (mainWindow) {
155         pluginMenu = mainWindow->getContextMenuExtensions(MainWindow::ContextMenuType::Disassembly);
156         pluginActionMenuAction = addMenu(pluginMenu);
157     }
158 
159     addSeparator();
160 
161     connect(this, &DisassemblyContextMenu::aboutToShow,
162             this, &DisassemblyContextMenu::aboutToShowSlot);
163     connect(this, &DisassemblyContextMenu::aboutToHide,
164             this, &DisassemblyContextMenu::aboutToHideSlot);
165 }
166 
~DisassemblyContextMenu()167 DisassemblyContextMenu::~DisassemblyContextMenu()
168 {
169 }
170 
addSetBaseMenu()171 void DisassemblyContextMenu::addSetBaseMenu()
172 {
173     setBaseMenu = addMenu(tr("Set Immediate Base to..."));
174 
175     initAction(&actionSetBaseBinary, tr("Binary"));
176     setBaseMenu->addAction(&actionSetBaseBinary);
177     connect(&actionSetBaseBinary, &QAction::triggered, this, [this] { setBase("b"); });
178 
179     initAction(&actionSetBaseOctal, tr("Octal"));
180     setBaseMenu->addAction(&actionSetBaseOctal);
181     connect(&actionSetBaseOctal, &QAction::triggered, this, [this] { setBase("o"); });
182 
183     initAction(&actionSetBaseDecimal, tr("Decimal"));
184     setBaseMenu->addAction(&actionSetBaseDecimal);
185     connect(&actionSetBaseDecimal, &QAction::triggered, this, [this] { setBase("d"); });
186 
187     initAction(&actionSetBaseHexadecimal, tr("Hexadecimal"));
188     setBaseMenu->addAction(&actionSetBaseHexadecimal);
189     connect(&actionSetBaseHexadecimal, &QAction::triggered, this, [this] { setBase("h"); });
190 
191     initAction(&actionSetBasePort, tr("Network Port"));
192     setBaseMenu->addAction(&actionSetBasePort);
193     connect(&actionSetBasePort, &QAction::triggered, this, [this] { setBase("p"); });
194 
195     initAction(&actionSetBaseIPAddr, tr("IP Address"));
196     setBaseMenu->addAction(&actionSetBaseIPAddr);
197     connect(&actionSetBaseIPAddr, &QAction::triggered, this, [this] { setBase("i"); });
198 
199     initAction(&actionSetBaseSyscall, tr("Syscall"));
200     setBaseMenu->addAction(&actionSetBaseSyscall);
201     connect(&actionSetBaseSyscall, &QAction::triggered, this, [this] { setBase("S"); });
202 
203     initAction(&actionSetBaseString, tr("String"));
204     setBaseMenu->addAction(&actionSetBaseString);
205     connect(&actionSetBaseString, &QAction::triggered, this, [this] { setBase("s"); });
206 }
207 
addSetBitsMenu()208 void DisassemblyContextMenu::addSetBitsMenu()
209 {
210     setBitsMenu = addMenu(tr("Set current bits to..."));
211 
212     initAction(&actionSetBits16, "16");
213     setBitsMenu->addAction(&actionSetBits16);
214     connect(&actionSetBits16, &QAction::triggered, this, [this] { setBits(16); });
215 
216     initAction(&actionSetBits32, "32");
217     setBitsMenu->addAction(&actionSetBits32);
218     connect(&actionSetBits32, &QAction::triggered, this, [this] { setBits(32); });
219 
220     initAction(&actionSetBits64, "64");
221     setBitsMenu->addAction(&actionSetBits64);
222     connect(&actionSetBits64, &QAction::triggered, this, [this] { setBits(64); });
223 }
224 
225 
addSetAsMenu()226 void DisassemblyContextMenu::addSetAsMenu()
227 {
228     setAsMenu = addMenu(tr("Set as..."));
229 
230     initAction(&actionSetToCode, tr("Code"),
231                SLOT(on_actionSetToCode_triggered()), getSetToCodeSequence());
232     setAsMenu->addAction(&actionSetToCode);
233 
234     setAsString = setAsMenu->addMenu(tr("String..."));
235 
236     initAction(&actionSetAsStringAuto, tr("Auto-detect"),
237                SLOT(on_actionSetAsString_triggered()), getSetAsStringSequence());
238     initAction(&actionSetAsStringRemove, tr("Remove"),
239                SLOT(on_actionSetAsStringRemove_triggered()));
240     initAction(&actionSetAsStringAdvanced, tr("Advanced"),
241                SLOT(on_actionSetAsStringAdvanced_triggered()), getSetAsStringAdvanced());
242 
243 
244     setAsString->addAction(&actionSetAsStringAuto);
245     setAsString->addAction(&actionSetAsStringRemove);
246     setAsString->addAction(&actionSetAsStringAdvanced);
247 
248     addSetToDataMenu();
249 }
250 
addSetToDataMenu()251 void DisassemblyContextMenu::addSetToDataMenu()
252 {
253     setToDataMenu = setAsMenu->addMenu(tr("Data..."));
254 
255     initAction(&actionSetToDataByte, tr("Byte"));
256     setToDataMenu->addAction(&actionSetToDataByte);
257     connect(&actionSetToDataByte, &QAction::triggered, this, [this] { setToData(1); });
258 
259     initAction(&actionSetToDataWord, tr("Word"));
260     setToDataMenu->addAction(&actionSetToDataWord);
261     connect(&actionSetToDataWord, &QAction::triggered, this, [this] { setToData(2); });
262 
263     initAction(&actionSetToDataDword, tr("Dword"));
264     setToDataMenu->addAction(&actionSetToDataDword);
265     connect(&actionSetToDataDword, &QAction::triggered, this, [this] { setToData(4); });
266 
267     initAction(&actionSetToDataQword, tr("Qword"));
268     setToDataMenu->addAction(&actionSetToDataQword);
269     connect(&actionSetToDataQword, &QAction::triggered, this, [this] { setToData(8); });
270 
271     initAction(&actionSetToDataEx, "...",
272                SLOT(on_actionSetToDataEx_triggered()), getSetToDataExSequence());
273     setToDataMenu->addAction(&actionSetToDataEx);
274 
275     auto switchAction = new QAction(this);
276     initAction(switchAction, "Switch Data",
277                SLOT(on_actionSetToData_triggered()), getSetToDataSequence());
278 }
279 
addEditMenu()280 void DisassemblyContextMenu::addEditMenu()
281 {
282     editMenu = addMenu(tr("Edit"));
283 
284     initAction(&actionEditInstruction, tr("Instruction"), SLOT(on_actionEditInstruction_triggered()));
285     editMenu->addAction(&actionEditInstruction);
286 
287     initAction(&actionNopInstruction, tr("Nop Instruction"), SLOT(on_actionNopInstruction_triggered()));
288     editMenu->addAction(&actionNopInstruction);
289 
290     initAction(&actionEditBytes, tr("Bytes"), SLOT(on_actionEditBytes_triggered()));
291     editMenu->addAction(&actionEditBytes);
292 
293     initAction(&actionJmpReverse, tr("Reverse Jump"), SLOT(on_actionJmpReverse_triggered()));
294     editMenu->addAction(&actionJmpReverse);
295 }
296 
addBreakpointMenu()297 void DisassemblyContextMenu::addBreakpointMenu()
298 {
299     breakpointMenu = addMenu(tr("Breakpoint"));
300 
301     initAction(&actionAddBreakpoint, tr("Add/remove breakpoint"),
302                SLOT(on_actionAddBreakpoint_triggered()), getAddBPSequence());
303     breakpointMenu->addAction(&actionAddBreakpoint);
304     initAction(&actionAdvancedBreakpoint, tr("Advanced breakpoint"),
305                SLOT(on_actionAdvancedBreakpoint_triggered()), QKeySequence(Qt::CTRL+Qt::Key_F2));
306     breakpointMenu->addAction(&actionAdvancedBreakpoint);
307 }
308 
addDebugMenu()309 void DisassemblyContextMenu::addDebugMenu()
310 {
311     debugMenu = addMenu(tr("Debug"));
312 
313     initAction(&actionContinueUntil, tr("Continue until line"),
314                SLOT(on_actionContinueUntil_triggered()));
315     debugMenu->addAction(&actionContinueUntil);
316 
317     initAction(&actionSetPC, "Set PC", SLOT(on_actionSetPC_triggered()));
318     debugMenu->addAction(&actionSetPC);
319 }
320 
getThingUsedHere(RVA offset)321 QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
322 {
323     QVector<ThingUsedHere> result;
324     const QJsonArray array = Core()->cmdj("anj @ " + QString::number(offset)).array();
325     result.reserve(array.size());
326     for (const auto &thing : array) {
327         auto obj = thing.toObject();
328         RVA offset = obj["offset"].toVariant().toULongLong();
329         QString name;
330 
331         // If real names display is enabled, show flag's real name instead of full flag name
332         if (Config()->getConfigBool("asm.flags.real") && obj.contains("realname")) {
333             name = obj["realname"].toString();
334         } else {
335             name = obj["name"].toString();
336         }
337 
338         QString typeString = obj["type"].toString();
339         ThingUsedHere::Type type = ThingUsedHere::Type::Address;
340         if (typeString == "var") {
341             type = ThingUsedHere::Type::Var;
342         } else if (typeString == "flag") {
343             type = ThingUsedHere::Type::Flag;
344         } else if (typeString == "function") {
345             type = ThingUsedHere::Type::Function;
346         } else if (typeString == "address") {
347             type = ThingUsedHere::Type::Address;
348         }
349         result.push_back(ThingUsedHere{name, offset, type});
350     }
351     return result;
352 }
353 
setOffset(RVA offset)354 void DisassemblyContextMenu::setOffset(RVA offset)
355 {
356     this->offset = offset;
357     this->actionSetFunctionVarTypes.setVisible(true);
358 }
359 
setCanCopy(bool enabled)360 void DisassemblyContextMenu::setCanCopy(bool enabled)
361 {
362     this->canCopy = enabled;
363 }
364 
setCurHighlightedWord(const QString & text)365 void DisassemblyContextMenu::setCurHighlightedWord(const QString &text)
366 {
367     this->curHighlightedWord = text;
368     // Update the renaming options only when a new word is selected
369     setupRenaming();
370 }
371 
getThingAt(ut64 address)372 DisassemblyContextMenu::ThingUsedHere DisassemblyContextMenu::getThingAt(ut64 address)
373 {
374     ThingUsedHere tuh;
375     RAnalFunction *fcn = Core()->functionAt(address);
376     RFlagItem *flag = r_flag_get_i(Core()->core()->flags, address);
377 
378     // We will lookup through existing r2 types to find something relevant
379 
380     if (fcn != nullptr) {
381         // It is a function
382         tuh.type = ThingUsedHere::Type::Function;
383         tuh.name = fcn->name;
384     } else if (flag != nullptr) {
385         // It is a flag
386         tuh.type = ThingUsedHere::Type::Flag;
387         if (Config()->getConfigBool("asm.flags.real") && flag->realname) {
388             tuh.name = flag->realname;
389         } else {
390             tuh.name = flag->name;
391         }
392     } else {
393         // Consider it an address
394         tuh.type = ThingUsedHere::Type::Address;
395     }
396 
397     tuh.offset = address;
398     return tuh;
399 }
400 
buildRenameMenu(ThingUsedHere * tuh)401 void DisassemblyContextMenu::buildRenameMenu(ThingUsedHere* tuh)
402 {
403     if (!tuh) {
404         qWarning() << "Unexpected behavior null pointer passed to DisassemblyContextMenu::buildRenameMenu";
405         doRenameAction = RENAME_DO_NOTHING;
406         return;
407     }
408 
409     actionDeleteFlag.setVisible(false);
410     if (tuh->type == ThingUsedHere::Type::Address) {
411         doRenameAction = RENAME_ADD_FLAG;
412         doRenameInfo.name = RAddressString(tuh->offset);
413         doRenameInfo.addr = tuh->offset;
414         actionRename.setText(tr("Add flag at %1 (used here)").arg(doRenameInfo.name));
415     } else if (tuh->type == ThingUsedHere::Type::Function) {
416         doRenameAction = RENAME_FUNCTION;
417         doRenameInfo.name = tuh->name;
418         doRenameInfo.addr = tuh->offset;
419         actionRename.setText(tr("Rename \"%1\"").arg(doRenameInfo.name));
420     } else if (tuh->type == ThingUsedHere::Type::Var) {
421         doRenameAction = RENAME_LOCAL;
422         doRenameInfo.name = tuh->name;
423         doRenameInfo.addr = tuh->offset;
424         actionRename.setText(tr("Rename local \"%1\"").arg(tuh->name));
425     } else if (tuh->type == ThingUsedHere::Type::Flag) {
426         doRenameAction = RENAME_FLAG;
427         doRenameInfo.name = tuh->name;
428         doRenameInfo.addr = tuh->offset;
429         actionRename.setText(tr("Rename flag \"%1\" (used here)").arg(doRenameInfo.name));
430         actionDeleteFlag.setVisible(true);
431     } else {
432         qWarning() << "Unexpected renaming type";
433         doRenameAction = RENAME_DO_NOTHING;
434     }
435 }
436 
setupRenaming()437 void DisassemblyContextMenu::setupRenaming()
438 {
439     // We parse our highlighted word as an address
440     ut64 selection = Core()->num(curHighlightedWord);
441 
442     // First, let's try to see if current line (offset) contains a local variable or a function
443     ThingUsedHere *tuh = nullptr;
444     ThingUsedHere thingAt;
445     auto things = getThingUsedHere(offset);
446     for (auto& thing : things) {
447         if (thing.offset == selection || thing.name == curHighlightedWord) {
448             // We matched something on current line
449             tuh = &thing;
450             break;
451         }
452     }
453 
454     if (!tuh) {
455         // Nothing matched on current line, is there anything valid coming from our selection?
456         thingAt = getThingAt(selection);
457 
458         if (thingAt.offset == 0) {
459             // We parsed something which resolved to 0, it's very likely nothing interesting was selected
460             // So we fallback on current line offset
461             thingAt = getThingAt(offset);
462         }
463 
464         // However, since for the moment selection selects *every* lines which match a specific offset,
465         // make sure we didn't want to select a local variable rather than the function itself
466         if (thingAt.type == ThingUsedHere::Type::Function) {
467             auto vars = Core()->getVariables(offset);
468             for (auto v : vars) {
469                 if (v.name == curHighlightedWord) {
470                     // This is a local variable
471                     thingAt.type = ThingUsedHere::Type::Var;
472                     thingAt.name = v.name;
473                     break;
474                 }
475             }
476         }
477 
478         // In any case, thingAt will contain something we can rename
479         tuh = &thingAt;
480     }
481 
482     // Now, build the renaming menu and show it
483     buildRenameMenu(tuh);
484     actionRename.setVisible(true);
485 }
486 
aboutToShowSlot()487 void DisassemblyContextMenu::aboutToShowSlot()
488 {
489     // check if set immediate base menu makes sense
490     QJsonObject instObject = Core()->cmdj("aoj @ " + QString::number(
491                                               offset)).array().first().toObject();
492     auto keys = instObject.keys();
493     bool immBase = keys.contains("val") || keys.contains("ptr");
494     setBaseMenu->menuAction()->setVisible(immBase);
495     setBitsMenu->menuAction()->setVisible(true);
496 
497     // Create structure offset menu if it makes sense
498     QString memBaseReg; // Base register
499     QVariant memDisp; // Displacement
500     if (instObject.contains("opex") && instObject["opex"].toObject().contains("operands")) {
501         // Loop through both the operands of the instruction
502         for (const QJsonValue value: instObject["opex"].toObject()["operands"].toArray()) {
503             QJsonObject operand = value.toObject();
504             if (operand.contains("type") && operand["type"].toString() == "mem" &&
505                     operand.contains("base") && !operand["base"].toString().contains("bp") &&
506                     operand.contains("disp") && operand["disp"].toVariant().toLongLong() > 0) {
507 
508                     // The current operand is the one which has an immediate displacement
509                     memBaseReg = operand["base"].toString();
510                     memDisp = operand["disp"].toVariant();
511                     break;
512 
513             }
514         }
515     }
516     if (memBaseReg.isEmpty()) {
517         // hide structure offset menu
518         structureOffsetMenu->menuAction()->setVisible(false);
519     } else {
520         // show structure offset menu
521         structureOffsetMenu->menuAction()->setVisible(true);
522         structureOffsetMenu->clear();
523 
524         // Get the possible offsets using the "ahts" command
525         // TODO: add ahtj command to radare2 and then use it here
526         QStringList ret = Core()->cmdList("ahts " + memDisp.toString());
527         for (const QString &val : ret) {
528             if (val.isEmpty()) {
529                 continue;
530             }
531             structureOffsetMenu->addAction("[" + memBaseReg + " + " + val + "]")->setData(val);
532         }
533         if (structureOffsetMenu->isEmpty()) {
534             // No possible offset was found so hide the menu
535             structureOffsetMenu->menuAction()->setVisible(false);
536         }
537     }
538 
539     actionAnalyzeFunction.setVisible(true);
540 
541     // Show the option to remove a defined string only if a string is defined in this address
542     QString stringDefinition = Core()->cmdRawAt("Cs.", offset);
543     actionSetAsStringRemove.setVisible(!stringDefinition.isEmpty());
544 
545     QString comment = Core()->cmdRawAt("CC.", offset);
546 
547     if (comment.isNull() || comment.isEmpty()) {
548         actionDeleteComment.setVisible(false);
549         actionAddComment.setText(tr("Add Comment"));
550     } else {
551         actionDeleteComment.setVisible(true);
552         actionAddComment.setText(tr("Edit Comment"));
553     }
554 
555     actionCopy.setVisible(canCopy);
556     copySeparator->setVisible(canCopy);
557 
558     // Handle renaming of variable, function, flag, ...
559     // Note: This might be useless if we consider setCurrentHighlightedWord is always called before
560     setupRenaming();
561 
562     // Only show retype for local vars if in a function
563     RAnalFunction *in_fcn = Core()->functionIn(offset);
564     if (in_fcn) {
565         auto vars = Core()->getVariables(offset);
566         actionSetFunctionVarTypes.setVisible(!vars.empty());
567         actionEditFunction.setVisible(true);
568         actionEditFunction.setText(tr("Edit function \"%1\"").arg(in_fcn->name));
569     } else {
570         actionSetFunctionVarTypes.setVisible(false);
571         actionEditFunction.setVisible(false);
572     }
573 
574     // Decide to show Reverse jmp option
575     showReverseJmpQuery();
576 
577     if (showInSubmenu.menu() != nullptr) {
578         showInSubmenu.menu()->deleteLater();
579     }
580     showInSubmenu.setMenu(mainWindow->createShowInMenu(this, offset));
581 
582     // Only show debug options if we are currently debugging
583     debugMenu->menuAction()->setVisible(Core()->currentlyDebugging);
584     bool hasBreakpoint = Core()->breakpointIndexAt(offset) > -1;
585     actionAddBreakpoint.setText(hasBreakpoint ?
586                                      tr("Remove breakpoint") : tr("Add breakpoint"));
587     actionAdvancedBreakpoint.setText(hasBreakpoint ?
588                                      tr("Edit breakpoint") : tr("Advanced breakpoint"));
589     QString progCounterName = Core()->getRegisterName("PC").toUpper();
590     actionSetPC.setText("Set " + progCounterName + " here");
591 
592     if (pluginMenu) {
593         pluginActionMenuAction->setVisible(!pluginMenu->isEmpty());
594         for (QAction *pluginAction : pluginMenu->actions()) {
595             pluginAction->setData(QVariant::fromValue(offset));
596         }
597     }
598 
599     bool isLocalVar = isHighlightedWordLocalVar();
600     actionXRefsForVariables.setVisible(isLocalVar);
601     if (isLocalVar) {
602         actionXRefsForVariables.setText(tr("X-Refs for %1").arg(curHighlightedWord));
603     }
604 }
605 
aboutToHideSlot()606 void DisassemblyContextMenu::aboutToHideSlot()
607 {
608     actionXRefsForVariables.setVisible(true);
609 }
610 
getCopySequence() const611 QKeySequence DisassemblyContextMenu::getCopySequence() const
612 {
613     return QKeySequence::Copy;
614 }
615 
getCommentSequence() const616 QKeySequence DisassemblyContextMenu::getCommentSequence() const
617 {
618     return {Qt::Key_Semicolon};
619 }
620 
getCopyAddressSequence() const621 QKeySequence DisassemblyContextMenu::getCopyAddressSequence() const
622 {
623     return {Qt::CTRL | Qt::SHIFT | Qt::Key_C};
624 }
625 
getSetToCodeSequence() const626 QKeySequence DisassemblyContextMenu::getSetToCodeSequence() const
627 {
628     return {Qt::Key_C};
629 }
630 
getSetAsStringSequence() const631 QKeySequence DisassemblyContextMenu::getSetAsStringSequence() const
632 {
633     return {Qt::Key_A};
634 }
635 
getSetAsStringAdvanced() const636 QKeySequence DisassemblyContextMenu::getSetAsStringAdvanced() const
637 {
638     return {Qt::SHIFT | Qt::Key_A};
639 }
640 
getSetToDataSequence() const641 QKeySequence DisassemblyContextMenu::getSetToDataSequence() const
642 {
643     return {Qt::Key_D};
644 }
645 
getSetToDataExSequence() const646 QKeySequence DisassemblyContextMenu::getSetToDataExSequence() const
647 {
648     return {Qt::Key_Asterisk};
649 }
650 
getRenameSequence() const651 QKeySequence DisassemblyContextMenu::getRenameSequence() const
652 {
653     return {Qt::Key_N};
654 }
655 
getRetypeSequence() const656 QKeySequence DisassemblyContextMenu::getRetypeSequence() const
657 {
658     return {Qt::Key_Y};
659 }
660 
getXRefSequence() const661 QKeySequence DisassemblyContextMenu::getXRefSequence() const
662 {
663     return {Qt::Key_X};
664 }
665 
getDisplayOptionsSequence() const666 QKeySequence DisassemblyContextMenu::getDisplayOptionsSequence() const
667 {
668     return {}; //TODO insert correct sequence
669 }
670 
getLinkTypeSequence() const671 QKeySequence DisassemblyContextMenu::getLinkTypeSequence() const
672 {
673     return {Qt::Key_L};
674 }
675 
getAddBPSequence() const676 QList<QKeySequence> DisassemblyContextMenu::getAddBPSequence() const
677 {
678     return {Qt::Key_F2, Qt::CTRL + Qt::Key_B};
679 }
680 
getDefineNewFunctionSequence() const681 QKeySequence DisassemblyContextMenu::getDefineNewFunctionSequence() const
682 {
683     return {Qt::Key_P};
684 }
685 
getEditFunctionSequence() const686 QKeySequence DisassemblyContextMenu::getEditFunctionSequence() const
687 {
688     return {Qt::SHIFT + Qt::Key_P};
689 }
690 
getUndefineFunctionSequence() const691 QKeySequence DisassemblyContextMenu::getUndefineFunctionSequence() const
692 {
693     return {Qt::Key_U};
694 }
695 
696 
on_actionEditInstruction_triggered()697 void DisassemblyContextMenu::on_actionEditInstruction_triggered()
698 {
699     if (!ioModesController.prepareForWriting()) {
700         return;
701     }
702     EditInstructionDialog e(EDIT_TEXT, this);
703     e.setWindowTitle(tr("Edit Instruction at %1").arg(RAddressString(offset)));
704 
705     QString oldInstructionOpcode = Core()->getInstructionOpcode(offset);
706     QString oldInstructionBytes = Core()->getInstructionBytes(offset);
707 
708     e.setInstruction(oldInstructionOpcode);
709 
710     if (e.exec()) {
711         QString userInstructionOpcode = e.getInstruction();
712         if (userInstructionOpcode != oldInstructionOpcode) {
713             Core()->editInstruction(offset, userInstructionOpcode);
714         }
715     }
716 }
717 
on_actionNopInstruction_triggered()718 void DisassemblyContextMenu::on_actionNopInstruction_triggered()
719 {
720     if (!ioModesController.prepareForWriting()) {
721         return;
722     }
723     Core()->nopInstruction(offset);
724 }
725 
showReverseJmpQuery()726 void DisassemblyContextMenu::showReverseJmpQuery()
727 {
728     QString type;
729 
730     QJsonArray array = Core()->cmdj("pdj 1 @ " + RAddressString(offset)).array();
731     if (array.isEmpty()) {
732         return;
733     }
734 
735     type = array.first().toObject()["type"].toString();
736     if (type == "cjmp") {
737         actionJmpReverse.setVisible(true);
738     } else {
739         actionJmpReverse.setVisible(false);
740     }
741 }
742 
on_actionJmpReverse_triggered()743 void DisassemblyContextMenu::on_actionJmpReverse_triggered()
744 {
745     if (!ioModesController.prepareForWriting()) {
746         return;
747     }
748     Core()->jmpReverse(offset);
749 }
750 
on_actionEditBytes_triggered()751 void DisassemblyContextMenu::on_actionEditBytes_triggered()
752 {
753     if (!ioModesController.prepareForWriting()) {
754         return;
755     }
756     EditInstructionDialog e(EDIT_BYTES, this);
757     e.setWindowTitle(tr("Edit Bytes at %1").arg(RAddressString(offset)));
758 
759     QString oldBytes = Core()->getInstructionBytes(offset);
760     e.setInstruction(oldBytes);
761 
762     if (e.exec()) {
763         QString bytes = e.getInstruction();
764         if (bytes != oldBytes) {
765             Core()->editBytes(offset, bytes);
766         }
767     }
768 }
769 
on_actionCopy_triggered()770 void DisassemblyContextMenu::on_actionCopy_triggered()
771 {
772     emit copy();
773 }
774 
on_actionCopyAddr_triggered()775 void DisassemblyContextMenu::on_actionCopyAddr_triggered()
776 {
777     QClipboard *clipboard = QApplication::clipboard();
778     clipboard->setText(RAddressString(offset));
779 }
780 
on_actionAddBreakpoint_triggered()781 void DisassemblyContextMenu::on_actionAddBreakpoint_triggered()
782 {
783     Core()->toggleBreakpoint(offset);
784 }
785 
on_actionAdvancedBreakpoint_triggered()786 void DisassemblyContextMenu::on_actionAdvancedBreakpoint_triggered()
787 {
788     int index = Core()->breakpointIndexAt(offset);
789     if (index >= 0) {
790         BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), this);
791     } else {
792         BreakpointsDialog::createNewBreakpoint(offset, this);
793     }
794 }
795 
on_actionContinueUntil_triggered()796 void DisassemblyContextMenu::on_actionContinueUntil_triggered()
797 {
798     Core()->continueUntilDebug(RAddressString(offset));
799 }
800 
on_actionSetPC_triggered()801 void DisassemblyContextMenu::on_actionSetPC_triggered()
802 {
803     QString progCounterName = Core()->getRegisterName("PC");
804     Core()->setRegister(progCounterName, RAddressString(offset).toUpper());
805 }
806 
on_actionAddComment_triggered()807 void DisassemblyContextMenu::on_actionAddComment_triggered()
808 {
809     CommentsDialog::addOrEditComment(offset, this);
810 }
811 
on_actionAnalyzeFunction_triggered()812 void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered()
813 {
814     bool ok;
815     // Create dialog
816     QString functionName = QInputDialog::getText(this, tr("New function %1").arg(RAddressString(offset)),
817                             tr("Function name:"), QLineEdit::Normal, QString(), &ok);
818 
819     // If user accepted
820     if (ok && !functionName.isEmpty()) {
821         Core()->createFunctionAt(offset, functionName);
822     }
823 }
824 
on_actionRename_triggered()825 void DisassemblyContextMenu::on_actionRename_triggered()
826 {
827     bool ok = false;
828     if (doRenameAction == RENAME_FUNCTION) {
829         QString newName = QInputDialog::getText(this->mainWindow, tr("Rename function %2").arg(doRenameInfo.name),
830                                             tr("Function name:"), QLineEdit::Normal, doRenameInfo.name, &ok);
831         if (ok && !newName.isEmpty()) {
832             Core()->renameFunction(doRenameInfo.addr, newName);
833         }
834     } else if (doRenameAction == RENAME_FLAG || doRenameAction == RENAME_ADD_FLAG) {
835         FlagDialog dialog(doRenameInfo.addr, this->mainWindow);
836         ok = dialog.exec();
837     } else if (doRenameAction == RENAME_LOCAL) {
838         RAnalFunction *fcn = Core()->functionIn(offset);
839         if (fcn) {
840             EditVariablesDialog dialog(fcn->addr, curHighlightedWord, this->mainWindow);
841             if (!dialog.empty()) {
842                 // Don't show the dialog if there are no variables
843                 ok = dialog.exec();
844             }
845         }
846     } else if (doRenameAction == RENAME_DO_NOTHING) {
847         // Do nothing
848     } else {
849         qWarning() << "Unhandled renaming action: " << doRenameAction;
850         assert(false);
851     }
852 
853     if (ok) {
854         // Rebuild menu in case the user presses the rename shortcut directly before clicking
855         setupRenaming();
856     }
857 }
858 
on_actionSetFunctionVarTypes_triggered()859 void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
860 {
861     RAnalFunction *fcn = Core()->functionIn(offset);
862 
863     if (!fcn) {
864         QMessageBox::critical(this, tr("Re-type Local Variables"),
865                               tr("You must be in a function to define variable types."));
866         return;
867     }
868 
869     EditVariablesDialog dialog(fcn->addr, curHighlightedWord, this);
870     if (dialog.empty()) { // don't show the dialog if there are no variables
871         return;
872     }
873     dialog.exec();
874 }
875 
on_actionXRefs_triggered()876 void DisassemblyContextMenu::on_actionXRefs_triggered()
877 {
878     XrefsDialog dialog(mainWindow, nullptr);
879     dialog.fillRefsForAddress(offset, RAddressString(offset), false);
880     dialog.exec();
881 }
882 
on_actionXRefsForVariables_triggered()883 void DisassemblyContextMenu::on_actionXRefsForVariables_triggered()
884 {
885     if (isHighlightedWordLocalVar()) {
886         XrefsDialog dialog(mainWindow, nullptr);
887         dialog.fillRefsForVariable(curHighlightedWord, offset);
888         dialog.exec();
889     }
890 }
891 
on_actionDisplayOptions_triggered()892 void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
893 {
894     PreferencesDialog dialog(this->window());
895     dialog.showSection(PreferencesDialog::Section::Disassembly);
896     dialog.exec();
897 }
898 
on_actionSetToCode_triggered()899 void DisassemblyContextMenu::on_actionSetToCode_triggered()
900 {
901     Core()->setToCode(offset);
902 }
903 
on_actionSetAsString_triggered()904 void DisassemblyContextMenu::on_actionSetAsString_triggered()
905 {
906     Core()->setAsString(offset);
907 }
908 
on_actionSetAsStringRemove_triggered()909 void DisassemblyContextMenu::on_actionSetAsStringRemove_triggered()
910 {
911     Core()->removeString(offset);
912 }
913 
on_actionSetAsStringAdvanced_triggered()914 void DisassemblyContextMenu::on_actionSetAsStringAdvanced_triggered()
915 {
916     EditStringDialog dialog(parentWidget());
917     const int predictedStrSize = Core()->getString(offset).size();
918     dialog.setStringSizeValue(predictedStrSize);
919     dialog.setStringStartAddress(offset);
920 
921     if(!dialog.exec())
922     {
923         return;
924     }
925 
926     uint64_t strAddr = 0U;
927     if( !dialog.getStringStartAddress(strAddr) ) {
928         QMessageBox::critical(this->window(), tr("Wrong address"), tr("Can't edit string at this address"));
929         return;
930     }
931     CutterCore::StringTypeFormats coreStringType = CutterCore::StringTypeFormats::None;
932 
933     const auto strSize = dialog.getStringSizeValue();
934     const auto strType = dialog.getStringType();
935     switch(strType)
936     {
937     case EditStringDialog::StringType::Auto:
938         coreStringType = CutterCore::StringTypeFormats::None;
939         break;
940     case EditStringDialog::StringType::ASCII_LATIN1:
941         coreStringType = CutterCore::StringTypeFormats::ASCII_LATIN1;
942         break;
943     case EditStringDialog::StringType::UTF8:
944         coreStringType = CutterCore::StringTypeFormats::UTF8;
945         break;
946     };
947 
948     Core()->setAsString(strAddr, strSize, coreStringType);
949 }
950 
on_actionSetToData_triggered()951 void DisassemblyContextMenu::on_actionSetToData_triggered()
952 {
953     int size = Core()->sizeofDataMeta(offset);
954     if (size > 8 || (size && (size & (size - 1)))) {
955         return;
956     }
957     if (size == 0 || size == 8) {
958         size = 1;
959     } else {
960         size *= 2;
961     }
962     setToData(size);
963 }
964 
on_actionSetToDataEx_triggered()965 void DisassemblyContextMenu::on_actionSetToDataEx_triggered()
966 {
967     SetToDataDialog dialog(offset, this->window());
968     if (!dialog.exec()) {
969         return;
970     }
971     setToData(dialog.getItemSize(), dialog.getItemCount());
972 }
973 
on_actionStructureOffsetMenu_triggered(QAction * action)974 void DisassemblyContextMenu::on_actionStructureOffsetMenu_triggered(QAction *action)
975 {
976     Core()->applyStructureOffset(action->data().toString(), offset);
977 }
978 
on_actionLinkType_triggered()979 void DisassemblyContextMenu::on_actionLinkType_triggered()
980 {
981     LinkTypeDialog dialog(mainWindow);
982     if (!dialog.setDefaultAddress(curHighlightedWord)) {
983         dialog.setDefaultAddress(RAddressString(offset));
984     }
985     dialog.exec();
986 }
987 
on_actionDeleteComment_triggered()988 void DisassemblyContextMenu::on_actionDeleteComment_triggered()
989 {
990     Core()->delComment(offset);
991 }
992 
on_actionDeleteFlag_triggered()993 void DisassemblyContextMenu::on_actionDeleteFlag_triggered()
994 {
995     Core()->delFlag(offset);
996 }
997 
on_actionDeleteFunction_triggered()998 void DisassemblyContextMenu::on_actionDeleteFunction_triggered()
999 {
1000     Core()->delFunction(offset);
1001 }
1002 
on_actionEditFunction_triggered()1003 void DisassemblyContextMenu::on_actionEditFunction_triggered()
1004 {
1005     RCore *core = Core()->core();
1006     EditFunctionDialog dialog(mainWindow);
1007     RAnalFunction *fcn = r_anal_get_fcn_in(core->anal, offset, 0);
1008 
1009     if (fcn) {
1010         dialog.setWindowTitle(tr("Edit function %1").arg(fcn->name));
1011         dialog.setNameText(fcn->name);
1012 
1013         QString startAddrText = "0x" + QString::number(fcn->addr, 16);
1014         dialog.setStartAddrText(startAddrText);
1015 
1016         dialog.setStackSizeText(QString::number(fcn->stack));
1017 
1018         QStringList callConList = Core()->cmdRaw("afcl").split("\n");
1019         callConList.removeLast();
1020         dialog.setCallConList(callConList);
1021         dialog.setCallConSelected(fcn->cc);
1022 
1023 
1024         if (dialog.exec()) {
1025             QString new_name = dialog.getNameText();
1026             Core()->renameFunction(fcn->addr, new_name);
1027             QString new_start_addr = dialog.getStartAddrText();
1028             fcn->addr = Core()->math(new_start_addr);
1029             QString new_stack_size = dialog.getStackSizeText();
1030             fcn->stack = int(Core()->math(new_stack_size));
1031             Core()->cmdRaw("afc " + dialog.getCallConSelected());
1032             emit Core()->functionsChanged();
1033         }
1034     }
1035 }
1036 
setBase(QString base)1037 void DisassemblyContextMenu::setBase(QString base)
1038 {
1039     Core()->setImmediateBase(base, offset);
1040 }
1041 
setBits(int bits)1042 void DisassemblyContextMenu::setBits(int bits)
1043 {
1044     Core()->setCurrentBits(bits, offset);
1045 }
1046 
setToData(int size,int repeat)1047 void DisassemblyContextMenu::setToData(int size, int repeat)
1048 {
1049     Core()->setToData(offset, size, repeat);
1050 }
1051 
addAnonymousAction(QString name,const char * slot,QKeySequence keySequence)1052 QAction *DisassemblyContextMenu::addAnonymousAction(QString name, const char *slot,
1053                                                     QKeySequence keySequence)
1054 {
1055     auto action = new QAction(this);
1056     addAction(action);
1057     anonymousActions.append(action);
1058     initAction(action, name, slot, keySequence);
1059     return action;
1060 }
1061 
initAction(QAction * action,QString name,const char * slot)1062 void DisassemblyContextMenu::initAction(QAction *action, QString name, const char *slot)
1063 {
1064     action->setParent(this);
1065     parentWidget()->addAction(action);
1066     action->setText(name);
1067     if (slot) {
1068         connect(action, SIGNAL(triggered(bool)), this, slot);
1069     }
1070 }
1071 
initAction(QAction * action,QString name,const char * slot,QKeySequence keySequence)1072 void DisassemblyContextMenu::initAction(QAction *action, QString name,
1073                                         const char *slot, QKeySequence keySequence)
1074 {
1075     initAction(action, name, slot);
1076     if (keySequence.isEmpty()) {
1077         return;
1078     }
1079     action->setShortcut(keySequence);
1080     action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
1081 }
1082 
initAction(QAction * action,QString name,const char * slot,QList<QKeySequence> keySequenceList)1083 void DisassemblyContextMenu::initAction(QAction *action, QString name,
1084                                         const char *slot, QList<QKeySequence> keySequenceList)
1085 {
1086     initAction(action, name, slot);
1087     if (keySequenceList.empty()) {
1088         return;
1089     }
1090     action->setShortcuts(keySequenceList);
1091     action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
1092 }
1093 
isHighlightedWordLocalVar()1094 bool DisassemblyContextMenu::isHighlightedWordLocalVar()
1095 {
1096     QList<VariableDescription> variables = Core()->getVariables(offset);
1097     for (const VariableDescription &var : variables) {
1098         if (var.name == curHighlightedWord) {
1099             return true;
1100         }
1101     }
1102     return false;
1103 }
1104