1 #include "HexdumpWidget.h"
2 #include "ui_HexdumpWidget.h"
3 
4 #include "common/Helpers.h"
5 #include "common/Configuration.h"
6 #include "common/TempConfig.h"
7 #include "common/SyntaxHighlighter.h"
8 #include "core/MainWindow.h"
9 
10 #include <QJsonObject>
11 #include <QJsonArray>
12 #include <QElapsedTimer>
13 #include <QTextDocumentFragment>
14 #include <QMenu>
15 #include <QClipboard>
16 #include <QScrollBar>
17 #include <QInputDialog>
18 #include <QShortcut>
19 
HexdumpWidget(MainWindow * main)20 HexdumpWidget::HexdumpWidget(MainWindow *main) :
21     MemoryDockWidget(MemoryWidgetType::Hexdump, main),
22     ui(new Ui::HexdumpWidget)
23 {
24     ui->setupUi(this);
25 
26     setObjectName(main
27                   ? main->getUniqueObjectName(getWidgetType())
28                   : getWidgetType());
29     updateWindowTitle();
30 
31     ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg"));
32     ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg"));
33     ui->copySHA256->setIcon(QIcon(":/img/icons/copy.svg"));
34     ui->copyCRC32->setIcon(QIcon(":/img/icons/copy.svg"));
35 
36 
37     ui->splitter->setChildrenCollapsible(false);
38 
39     QToolButton *closeButton = new QToolButton;
40     QIcon closeIcon = QIcon(":/img/icons/delete.svg");
41     closeButton->setIcon(closeIcon);
42     closeButton->setAutoRaise(true);
43 
44     ui->hexSideTab_2->setCornerWidget(closeButton);
45     syntaxHighLighter = Config()->createSyntaxHighlighter(ui->hexDisasTextEdit->document());
46 
47     ui->openSideViewB->hide();  // hide button at startup since side view is visible
48 
49     connect(closeButton, &QToolButton::clicked, this, [this] {
50         showSidePanel(false);
51     });
52 
53     connect(ui->openSideViewB, &QToolButton::clicked, this, [this] {
54         showSidePanel(true);
55     });
56 
57     // Set placeholders for the line-edit components
58     QString placeholder = tr("Select bytes to display information");
59     ui->bytesMD5->setPlaceholderText(placeholder);
60     ui->bytesEntropy->setPlaceholderText(placeholder);
61     ui->bytesSHA1->setPlaceholderText(placeholder);
62     ui->bytesSHA256->setPlaceholderText(placeholder);
63     ui->bytesCRC32->setPlaceholderText(placeholder);
64     ui->hexDisasTextEdit->setPlaceholderText(placeholder);
65 
66     setupFonts();
67 
68     ui->openSideViewB->setStyleSheet(""
69                                      "QToolButton {"
70                                      "   border : 0px;"
71                                      "   padding : 0px;"
72                                      "   margin : 0px;"
73                                      "}"
74                                      "QToolButton:hover {"
75                                      "  border : 1px solid;"
76                                      "  border-width : 1px;"
77                                      "  border-color : #3daee9"
78                                      "}");
79 
80     refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
81         refresh(offset ? *offset : RVA_INVALID);
82     });
83 
84     this->ui->hexTextView->addAction(&syncAction);
85 
86     connect(Config(), &Configuration::fontsUpdated, this, &HexdumpWidget::fontsUpdated);
87     connect(Core(), &CutterCore::refreshAll, this, [this]() { refresh(); });
88     connect(Core(), &CutterCore::refreshCodeViews, this, [this]() { refresh(); });
89     connect(Core(), &CutterCore::instructionChanged, this, [this]() { refresh(); });
90     connect(Core(), &CutterCore::stackChanged, this, [this]() { refresh(); });
91     connect(Core(), &CutterCore::registersChanged, this, [this]() { refresh(); });
92 
93     connect(seekable, &CutterSeekable::seekableSeekChanged, this, &HexdumpWidget::onSeekChanged);
94     connect(ui->hexTextView, &HexWidget::positionChanged, this, [this](RVA addr) {
95         if (!sent_seek) {
96             sent_seek = true;
97             seekable->seek(addr);
98             sent_seek = false;
99         }
100     });
101     connect(ui->hexTextView, &HexWidget::selectionChanged, this, &HexdumpWidget::selectionChanged);
102     connect(ui->hexSideTab_2, &QTabWidget::currentChanged, this, &HexdumpWidget::refreshSelectionInfo);
103     ui->hexTextView->installEventFilter(this);
104 
105     initParsing();
106     selectHexPreview();
107 
108     // apply initial offset
109     refresh(seekable->getOffset());
110 }
111 
onSeekChanged(RVA addr)112 void HexdumpWidget::onSeekChanged(RVA addr)
113 {
114     if (sent_seek) {
115         sent_seek = false;
116         return;
117     }
118     refresh(addr);
119 }
120 
~HexdumpWidget()121 HexdumpWidget::~HexdumpWidget() {}
122 
getWidgetType()123 QString HexdumpWidget::getWidgetType()
124 {
125     return "Hexdump";
126 }
127 
refresh()128 void HexdumpWidget::refresh()
129 {
130     refresh(RVA_INVALID);
131 }
132 
refresh(RVA addr)133 void HexdumpWidget::refresh(RVA addr)
134 {
135     if (!refreshDeferrer->attemptRefresh(addr == RVA_INVALID ? nullptr : new RVA(addr))) {
136         return;
137     }
138     sent_seek = true;
139     if (addr != RVA_INVALID) {
140         ui->hexTextView->seek(addr);
141     } else {
142         ui->hexTextView->refresh();
143         refreshSelectionInfo();
144     }
145     sent_seek = false;
146 }
147 
148 
initParsing()149 void HexdumpWidget::initParsing()
150 {
151     // Fill the plugins combo for the hexdump sidebar
152     ui->parseTypeComboBox->addItem(tr("Disassembly"), "pda");
153     ui->parseTypeComboBox->addItem(tr("String"), "pcs");
154     ui->parseTypeComboBox->addItem(tr("Assembler"), "pca");
155     ui->parseTypeComboBox->addItem(tr("C bytes"), "pc");
156     ui->parseTypeComboBox->addItem(tr("C bytes with instructions"), "pci");
157     ui->parseTypeComboBox->addItem(tr("C half-words (2 byte)"), "pch");
158     ui->parseTypeComboBox->addItem(tr("C words (4 byte)"), "pcw");
159     ui->parseTypeComboBox->addItem(tr("C dwords (8 byte)"), "pcd");
160     ui->parseTypeComboBox->addItem(tr("Python"), "pcp");
161     ui->parseTypeComboBox->addItem(tr("JSON"), "pcj");
162     ui->parseTypeComboBox->addItem(tr("JavaScript"), "pcJ");
163     ui->parseTypeComboBox->addItem(tr("Yara"), "pcy");
164 
165     ui->parseArchComboBox->insertItems(0, Core()->getAsmPluginNames());
166 
167     ui->parseEndianComboBox->setCurrentIndex(Core()->getConfigb("cfg.bigendian") ? 1 : 0);
168 }
169 
selectionChanged(HexWidget::Selection selection)170 void HexdumpWidget::selectionChanged(HexWidget::Selection selection)
171 {
172     if (selection.empty) {
173         clearParseWindow();
174     } else {
175         updateParseWindow(selection.startAddress, selection.endAddress - selection.startAddress + 1);
176     }
177 }
178 
on_parseArchComboBox_currentTextChanged(const QString &)179 void HexdumpWidget::on_parseArchComboBox_currentTextChanged(const QString &/*arg1*/)
180 {
181     refreshSelectionInfo();
182 }
183 
on_parseBitsComboBox_currentTextChanged(const QString &)184 void HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString &/*arg1*/)
185 {
186     refreshSelectionInfo();
187 }
188 
setupFonts()189 void HexdumpWidget::setupFonts()
190 {
191     QFont font = Config()->getFont();
192     ui->hexDisasTextEdit->setFont(font);
193     ui->hexTextView->setMonospaceFont(font);
194 }
195 
refreshSelectionInfo()196 void HexdumpWidget::refreshSelectionInfo()
197 {
198     selectionChanged(ui->hexTextView->getSelection());
199 }
200 
fontsUpdated()201 void HexdumpWidget::fontsUpdated()
202 {
203     setupFonts();
204 }
205 
clearParseWindow()206 void HexdumpWidget::clearParseWindow()
207 {
208     ui->hexDisasTextEdit->setPlainText("");
209     ui->bytesEntropy->setText("");
210     ui->bytesMD5->setText("");
211     ui->bytesSHA1->setText("");
212     ui->bytesSHA256->setText("");
213     ui->bytesCRC32->setText("");
214 }
215 
showSidePanel(bool show)216 void HexdumpWidget::showSidePanel(bool show)
217 {
218     ui->hexSideTab_2->setVisible(show);
219     ui->openSideViewB->setHidden(show);
220     if (show) {
221         refreshSelectionInfo();
222     }
223 }
224 
getWindowTitle() const225 QString HexdumpWidget::getWindowTitle() const
226 {
227     return tr("Hexdump");
228 }
229 
updateParseWindow(RVA start_address,int size)230 void HexdumpWidget::updateParseWindow(RVA start_address, int size)
231 {
232     if (!ui->hexSideTab_2->isVisible()) {
233         return;
234     }
235 
236     if (ui->hexSideTab_2->currentIndex() == 0) {
237         // scope for TempConfig
238 
239         // Get selected combos
240         QString arch = ui->parseArchComboBox->currentText();
241         QString bits = ui->parseBitsComboBox->currentText();
242         QString selectedCommand = ui->parseTypeComboBox->currentData().toString();
243         QString commandResult = "";
244         bool bigEndian = ui->parseEndianComboBox->currentIndex() == 1;
245 
246         TempConfig tempConfig;
247         tempConfig
248         .set("asm.arch", arch)
249         .set("asm.bits", bits)
250         .set("cfg.bigendian", bigEndian);
251 
252         ui->hexDisasTextEdit->setPlainText(selectedCommand != "" ? Core()->cmdRawAt(QString("%1 %2")
253                                                                     .arg(selectedCommand)
254                                                                     .arg(size)
255                                                                     , start_address) : "");
256     } else {
257         // Fill the information tab hashes and entropy
258         ui->bytesMD5->setText(Core()->cmdRawAt(QString("ph md5 %1").arg(size), start_address).trimmed());
259         ui->bytesSHA1->setText(Core()->cmdRawAt(QString("ph sha1 %1").arg(size), start_address).trimmed());
260         ui->bytesSHA256->setText(Core()->cmdRawAt(QString("ph sha256 %1").arg(size), start_address).trimmed());
261         ui->bytesCRC32->setText(Core()->cmdRawAt(QString("ph crc32 %1").arg(size), start_address).trimmed());
262         ui->bytesEntropy->setText(Core()->cmdRawAt(QString("ph entropy %1").arg(size), start_address).trimmed());
263         ui->bytesMD5->setCursorPosition(0);
264         ui->bytesSHA1->setCursorPosition(0);
265         ui->bytesSHA256->setCursorPosition(0);
266         ui->bytesCRC32->setCursorPosition(0);
267     }
268 }
269 
on_parseTypeComboBox_currentTextChanged(const QString &)270 void HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &)
271 {
272     QString currentParseTypeText = ui->parseTypeComboBox->currentData().toString();
273     if (currentParseTypeText == "pda" || currentParseTypeText == "pci") {
274         ui->hexSideFrame_2->show();
275     } else {
276         ui->hexSideFrame_2->hide();
277     }
278     refreshSelectionInfo();
279 }
280 
on_parseEndianComboBox_currentTextChanged(const QString &)281 void HexdumpWidget::on_parseEndianComboBox_currentTextChanged(const QString &)
282 {
283     refreshSelectionInfo();
284 }
285 
on_hexSideTab_2_currentChanged(int)286 void HexdumpWidget::on_hexSideTab_2_currentChanged(int /*index*/)
287 {
288     /*
289     if (index == 2) {
290         // Add data to HTML Polar functions graph
291         QFile html(":/html/bar.html");
292         if(!html.open(QIODevice::ReadOnly)) {
293             QMessageBox::information(0,"error",html.errorString());
294         }
295         QString code = html.readAll();
296         html.close();
297         this->histoWebView->setHtml(code);
298         this->histoWebView->show();
299     } else {
300         this->histoWebView->hide();
301     }
302     */
303 }
304 
305 
resizeEvent(QResizeEvent * event)306 void HexdumpWidget::resizeEvent(QResizeEvent *event)
307 {
308     // Heuristics to hide sidebar when it hides the content of the hexdump. 600px looks just "okay"
309     // Only applied when widget width is decreased to avoid unwanted behavior
310     if (event->oldSize().width() > event->size().width() && event->size().width() < 600) {
311         showSidePanel(false);
312     }
313     QDockWidget::resizeEvent(event);
314     refresh();
315 }
316 
widgetToFocusOnRaise()317 QWidget *HexdumpWidget::widgetToFocusOnRaise()
318 {
319     return ui->hexTextView;
320 }
321 
on_copyMD5_clicked()322 void HexdumpWidget::on_copyMD5_clicked()
323 {
324     QString md5 = ui->bytesMD5->text();
325     QClipboard *clipboard = QApplication::clipboard();
326     clipboard->setText(md5);
327     Core()->message("MD5 copied to clipboard: " + md5);
328 }
329 
on_copySHA1_clicked()330 void HexdumpWidget::on_copySHA1_clicked()
331 {
332     QString sha1 = ui->bytesSHA1->text();
333     QClipboard *clipboard = QApplication::clipboard();
334     clipboard->setText(sha1);
335     Core()->message("SHA1 copied to clipboard: " + sha1);
336 }
337 
on_copySHA256_clicked()338 void HexdumpWidget::on_copySHA256_clicked()
339 {
340     QString sha256 = ui->bytesSHA256->text();
341     QClipboard *clipboard = QApplication::clipboard();
342     clipboard->setText(sha256);
343     Core()->message("SHA256 copied to clipboard: " + sha256);
344 }
345 
on_copyCRC32_clicked()346 void HexdumpWidget::on_copyCRC32_clicked()
347 {
348     QString crc32 = ui->bytesCRC32->text();
349     QClipboard *clipboard = QApplication::clipboard();
350     clipboard->setText(crc32);
351     Core()->message("CRC32 copied to clipboard: " + crc32);
352 }
353 
selectHexPreview()354 void HexdumpWidget::selectHexPreview()
355 {
356     // Pre-select arch and bits in the hexdump sidebar
357     QString arch = Core()->getConfig("asm.arch");
358     QString bits = Core()->getConfig("asm.bits");
359 
360     if (ui->parseArchComboBox->findText(arch) != -1) {
361         ui->parseArchComboBox->setCurrentIndex(ui->parseArchComboBox->findText(arch));
362     }
363 
364     if (ui->parseBitsComboBox->findText(bits) != -1) {
365         ui->parseBitsComboBox->setCurrentIndex(ui->parseBitsComboBox->findText(bits));
366     }
367 }
368