1 #include "XrefsDialog.h"
2 #include "ui_XrefsDialog.h"
3 
4 #include "common/TempConfig.h"
5 #include "common/Helpers.h"
6 
7 #include "core/MainWindow.h"
8 
9 #include <QJsonArray>
10 
XrefsDialog(MainWindow * main,QWidget * parent,bool hideXrefFrom)11 XrefsDialog::XrefsDialog(MainWindow *main, QWidget *parent, bool hideXrefFrom) :
12     QDialog(parent),
13     addr(0),
14     toModel(this),
15     fromModel(this),
16     ui(new Ui::XrefsDialog)
17 {
18     ui->setupUi(this);
19     setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
20 
21     ui->toTreeWidget->setMainWindow(main);
22     ui->fromTreeWidget->setMainWindow(main);
23 
24     ui->toTreeWidget->setModel(&toModel);
25     ui->fromTreeWidget->setModel(&fromModel);
26 
27     // Modify the splitter's location to show more Disassembly instead of empty space. Not possible via Designer
28     ui->splitter->setSizes(QList<int>() << 300 << 400);
29 
30     // Increase asm text edit margin
31     QTextDocument *asm_docu = ui->previewTextEdit->document();
32     asm_docu->setDocumentMargin(10);
33 
34     setupPreviewColors();
35     setupPreviewFont();
36 
37     // Highlight current line
38     connect(ui->previewTextEdit, &QPlainTextEdit::cursorPositionChanged, this, &XrefsDialog::highlightCurrentLine);
39     connect(Config(), &Configuration::fontsUpdated, this, &XrefsDialog::setupPreviewFont);
40     connect(Config(), &Configuration::colorsUpdated, this, &XrefsDialog::setupPreviewColors);
41 
42     connect(ui->toTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
43             this, &XrefsDialog::onToTreeWidgetItemSelectionChanged);
44     connect(ui->fromTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
45             this, &XrefsDialog::onFromTreeWidgetItemSelectionChanged);
46 
47     // Don't create recursive xref dialogs
48     auto toContextMenu = ui->toTreeWidget->getItemContextMenu();
49     connect(toContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);
50     auto fromContextMenu = ui->fromTreeWidget->getItemContextMenu();
51     connect(fromContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);
52 
53     connect(ui->toTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);
54     connect(ui->fromTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);
55 
56     connect(Core(), &CutterCore::commentsChanged, this, [this]() {
57         qhelpers::emitColumnChanged(&toModel, XrefModel::COMMENT);
58         qhelpers::emitColumnChanged(&fromModel, XrefModel::COMMENT);
59     });
60 
61     if (hideXrefFrom) {
62         hideXrefFromSection();
63     }
64 }
65 
~XrefsDialog()66 XrefsDialog::~XrefsDialog() { }
67 
normalizeAddr(const QString & addr) const68 QString XrefsDialog::normalizeAddr(const QString &addr) const
69 {
70     QString ret = addr;
71     if (addr.length() < 10) {
72         ret = ret.mid(3).rightJustified(8, QLatin1Char('0'));
73         ret.prepend(QStringLiteral("0x"));
74     }
75     return ret;
76 }
77 
setupPreviewFont()78 void XrefsDialog::setupPreviewFont()
79 {
80     ui->previewTextEdit->setFont(Config()->getBaseFont());
81 }
82 
setupPreviewColors()83 void XrefsDialog::setupPreviewColors()
84 {
85     ui->previewTextEdit->setStyleSheet(QString("QPlainTextEdit { background-color: %1; color: %2; }")
86                                        .arg(ConfigColor("gui.background").name())
87                                        .arg(ConfigColor("btext").name()));
88 }
89 
highlightCurrentLine()90 void XrefsDialog::highlightCurrentLine()
91 {
92     QList<QTextEdit::ExtraSelection> extraSelections;
93 
94     if (ui->previewTextEdit->isReadOnly()) {
95         QTextEdit::ExtraSelection selection = QTextEdit::ExtraSelection();
96 
97         selection.format.setBackground(ConfigColor("lineHighlight"));
98         selection.format.setProperty(QTextFormat::FullWidthSelection, true);
99         selection.cursor = ui->previewTextEdit->textCursor();
100         selection.cursor.clearSelection();
101         extraSelections.append(selection);
102 
103         ui->previewTextEdit->setExtraSelections(extraSelections);
104     }
105 }
106 
onFromTreeWidgetItemSelectionChanged()107 void XrefsDialog::onFromTreeWidgetItemSelectionChanged()
108 {
109     auto index = ui->fromTreeWidget->currentIndex();
110     if (!ui->fromTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {
111         return;
112     }
113     ui->toTreeWidget->clearSelection();
114     updatePreview(fromModel.address(index));
115 }
116 
onToTreeWidgetItemSelectionChanged()117 void XrefsDialog::onToTreeWidgetItemSelectionChanged()
118 {
119     auto index = ui->toTreeWidget->currentIndex();
120     if (!ui->toTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {
121         return;
122     }
123     ui->fromTreeWidget->clearSelection();
124     updatePreview(toModel.address(index));
125 }
126 
updatePreview(RVA addr)127 void XrefsDialog::updatePreview(RVA addr)
128 {
129     TempConfig tempConfig;
130     tempConfig.set("scr.html", true);
131     tempConfig.set("scr.color", COLOR_MODE_16M);
132     tempConfig.set("asm.lines", false);
133     tempConfig.set("asm.bytes", false);
134 
135     // Use cmd because cmRaw cannot handle the output properly. Why?
136     QString disas = Core()->cmd("pd--20 @ " + QString::number(addr));
137     ui->previewTextEdit->document()->setHtml(disas);
138 
139     // Does it make any sense?
140     ui->previewTextEdit->find(normalizeAddr(RAddressString(addr)), QTextDocument::FindBackward);
141     ui->previewTextEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
142 }
143 
updateLabels(QString name)144 void XrefsDialog::updateLabels(QString name)
145 {
146     ui->label_xTo->setText(tr("X-Refs to %1 (%2 results):").arg(name).arg(toModel.rowCount()));
147     ui->label_xFrom->setText(tr("X-Refs from %1 (%2 results):").arg(name).arg(fromModel.rowCount()));
148 }
149 
updateLabelsForVariables(QString name)150 void XrefsDialog::updateLabelsForVariables(QString name)
151 {
152     ui->label_xTo->setText(tr("Writes to %1").arg(name));
153     ui->label_xFrom->setText(tr("Reads from %1").arg(name));
154 }
155 
hideXrefFromSection()156 void XrefsDialog::hideXrefFromSection()
157 {
158     ui->label_xFrom->hide();
159     ui->fromTreeWidget->hide();
160 }
161 
fillRefsForAddress(RVA addr,QString name,bool whole_function)162 void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function)
163 {
164     setWindowTitle(tr("X-Refs for %1").arg(name));
165 
166     toModel.readForOffset(addr, true, whole_function);
167     fromModel.readForOffset(addr, false, whole_function);
168 
169     updateLabels(name);
170 
171     // Adjust columns to content
172     qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
173     qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
174 
175     // Automatically select the first line
176     if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
177         qhelpers::selectFirstItem(ui->fromTreeWidget);
178     }
179 }
180 
fillRefsForVariable(QString nameOfVariable,RVA offset)181 void XrefsDialog::fillRefsForVariable(QString nameOfVariable, RVA offset)
182 {
183     setWindowTitle(tr("X-Refs for %1").arg(nameOfVariable));
184     updateLabelsForVariables(nameOfVariable);
185 
186     // Initialize Model
187     toModel.readForVariable(nameOfVariable, true, offset);
188     fromModel.readForVariable(nameOfVariable, false, offset);
189     // Hide irrelevant column 1: which shows type
190     ui->fromTreeWidget->hideColumn(XrefModel::Columns::TYPE);
191     ui->toTreeWidget->hideColumn(XrefModel::Columns::TYPE);
192     // Adjust columns to content
193     qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
194     qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
195 
196     // Automatically select the first line
197     if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
198         qhelpers::selectFirstItem(ui->fromTreeWidget);
199     }
200 }
201 
xrefTypeString(const QString & type)202 QString XrefModel::xrefTypeString(const QString &type)
203 {
204     if (type == "CODE") {
205         return QStringLiteral("Code");
206     } else if (type == "CALL") {
207         return QStringLiteral("Call");
208     } else if (type == "DATA") {
209         return QStringLiteral("Data");
210     } else if (type == "STRING") {
211         return QStringLiteral("String");
212     }
213     return type;
214 }
215 
216 
XrefModel(QObject * parent)217 XrefModel::XrefModel(QObject *parent)
218     : AddressableItemModel(parent)
219 {
220 }
221 
readForOffset(RVA offset,bool to,bool whole_function)222 void XrefModel::readForOffset(RVA offset, bool to, bool whole_function)
223 {
224     beginResetModel();
225     this->to = to;
226     xrefs = Core()->getXRefs(offset, to, whole_function);
227     endResetModel();
228 }
229 
readForVariable(QString nameOfVariable,bool write,RVA offset)230 void XrefModel::readForVariable(QString nameOfVariable, bool write, RVA offset)
231 {
232     beginResetModel();
233     this->to = write;
234     xrefs = Core()->getXRefsForVariable(nameOfVariable, write, offset);
235     endResetModel();
236 }
237 
rowCount(const QModelIndex & parent) const238 int XrefModel::rowCount(const QModelIndex &parent) const
239 {
240     Q_UNUSED(parent)
241     return xrefs.size();
242 }
243 
columnCount(const QModelIndex & parent) const244 int XrefModel::columnCount(const QModelIndex &parent) const
245 {
246     Q_UNUSED(parent)
247     return Columns::COUNT;
248 }
249 
data(const QModelIndex & index,int role) const250 QVariant XrefModel::data(const QModelIndex &index, int role) const
251 {
252     if (!index.isValid() || index.row() >= xrefs.count()) {
253         return QVariant();
254     }
255 
256     const XrefDescription &xref = xrefs.at(index.row());
257 
258     switch (role) {
259     case Qt::DisplayRole:
260         switch (index.column()) {
261         case OFFSET:
262             return to ? xref.from_str : xref.to_str;
263         case TYPE:
264             return xrefTypeString(xref.type);
265         case CODE:
266             if (to || xref.type != "DATA") {
267                 return Core()->disassembleSingleInstruction(xref.from);
268             } else {
269                 return QString();
270             }
271         case COMMENT:
272             return to ? Core()->getCommentAt(xref.from) : Core()->getCommentAt(xref.to);
273         }
274         return QVariant();
275     case FlagDescriptionRole:
276         return QVariant::fromValue(xref);
277     default:
278         break;
279     }
280     return QVariant();
281 }
282 
headerData(int section,Qt::Orientation orientation,int role) const283 QVariant XrefModel::headerData(int section, Qt::Orientation orientation, int role) const
284 {
285     Q_UNUSED(orientation)
286 
287     switch (role) {
288     case Qt::DisplayRole:
289         switch (section) {
290         case OFFSET:
291             return tr("Address");
292         case TYPE:
293             return tr("Type");
294         case CODE:
295             return tr("Code");
296         case COMMENT:
297             return tr("Comment");
298         default:
299             return QVariant();
300         }
301     default:
302         return QVariant();
303     }
304 }
305 
address(const QModelIndex & index) const306 RVA XrefModel::address(const QModelIndex &index) const
307 {
308     const auto &xref = xrefs.at(index.row());
309     return to ? xref.from : xref.to;
310 }
311