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