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