2017-10-12 19:55:15 +00:00
|
|
|
#include "HexdumpWidget.h"
|
|
|
|
#include "ui_HexdumpWidget.h"
|
2017-03-29 10:18:37 +00:00
|
|
|
|
2018-10-17 07:55:53 +00:00
|
|
|
#include "common/Helpers.h"
|
|
|
|
#include "common/Configuration.h"
|
|
|
|
#include "common/TempConfig.h"
|
2017-03-29 10:18:37 +00:00
|
|
|
|
2018-01-27 10:08:05 +00:00
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QElapsedTimer>
|
|
|
|
#include <QTextDocumentFragment>
|
2017-03-29 10:18:37 +00:00
|
|
|
#include <QMenu>
|
2018-01-27 10:08:05 +00:00
|
|
|
#include <QClipboard>
|
|
|
|
#include <QScrollBar>
|
2018-12-18 17:26:38 +00:00
|
|
|
#include <QInputDialog>
|
2019-04-25 11:38:53 +00:00
|
|
|
#include <QShortcut>
|
2017-11-15 21:42:39 +00:00
|
|
|
|
2018-03-16 21:46:57 +00:00
|
|
|
HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
2019-03-27 08:24:54 +00:00
|
|
|
MemoryDockWidget(CutterCore::MemoryWidgetType::Hexdump, main, action),
|
2018-05-25 14:30:59 +00:00
|
|
|
ui(new Ui::HexdumpWidget),
|
2019-01-13 14:20:07 +00:00
|
|
|
seekable(new CutterSeekable(this))
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
2017-07-11 11:05:42 +00:00
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
/*
|
|
|
|
* Ugly hack just for the layout issue
|
|
|
|
* QSettings saves the state with the object names
|
|
|
|
* By doing this hack,
|
|
|
|
* you can at least avoid some mess by dismissing all the Extra Widgets
|
|
|
|
*/
|
|
|
|
QString name = "Hexdump";
|
|
|
|
if (!action) {
|
|
|
|
name = "Extra Hexdump";
|
|
|
|
}
|
|
|
|
setObjectName(name);
|
|
|
|
|
2018-10-21 17:07:02 +00:00
|
|
|
ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg"));
|
|
|
|
ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg"));
|
2017-12-03 20:01:11 +00:00
|
|
|
|
2017-11-19 21:21:02 +00:00
|
|
|
|
2018-10-18 19:36:46 +00:00
|
|
|
ui->splitter->setChildrenCollapsible(false);
|
|
|
|
|
|
|
|
QToolButton *closeButton = new QToolButton;
|
2018-10-21 17:07:02 +00:00
|
|
|
QIcon closeIcon = QIcon(":/img/icons/delete.svg");
|
2018-10-18 19:36:46 +00:00
|
|
|
closeButton->setIcon(closeIcon);
|
2018-10-21 17:07:02 +00:00
|
|
|
closeButton->setAutoRaise(true);
|
2018-10-18 19:36:46 +00:00
|
|
|
ui->hexSideTab_2->setCornerWidget(closeButton);
|
|
|
|
|
|
|
|
ui->openSideViewB->hide(); // hide button at startup since side view is visible
|
|
|
|
|
2018-12-18 17:26:38 +00:00
|
|
|
connect(closeButton, &QToolButton::clicked, this, [this] {
|
|
|
|
ui->hexSideTab_2->hide();
|
|
|
|
ui->openSideViewB->show();
|
2018-10-18 19:36:46 +00:00
|
|
|
});
|
|
|
|
|
2018-12-18 17:26:38 +00:00
|
|
|
connect(ui->openSideViewB, &QToolButton::clicked, this, [this] {
|
2018-10-18 19:36:46 +00:00
|
|
|
ui->hexSideTab_2->show();
|
|
|
|
ui->openSideViewB->hide();
|
|
|
|
});
|
2018-03-05 14:10:17 +00:00
|
|
|
|
2018-10-21 17:07:02 +00:00
|
|
|
ui->bytesMD5->setPlaceholderText("Select bytes to display information");
|
|
|
|
ui->bytesEntropy->setPlaceholderText("Select bytes to display information");
|
|
|
|
ui->bytesSHA1->setPlaceholderText("Select bytes to display information");
|
|
|
|
ui->hexDisasTextEdit->setPlaceholderText("Select bytes to display information");
|
|
|
|
|
2017-11-20 10:10:31 +00:00
|
|
|
setupFonts();
|
2018-01-27 10:08:05 +00:00
|
|
|
|
2018-10-21 17:07:02 +00:00
|
|
|
ui->openSideViewB->setStyleSheet(""
|
|
|
|
"QToolButton {"
|
|
|
|
" border : 0px;"
|
|
|
|
" padding : 0px;"
|
|
|
|
" margin : 0px;"
|
|
|
|
"}"
|
|
|
|
"QToolButton:hover {"
|
|
|
|
" border : 1px solid;"
|
|
|
|
" border-width : 1px;"
|
|
|
|
" border-color : #3daee9"
|
|
|
|
"}");
|
|
|
|
|
2018-05-25 14:30:59 +00:00
|
|
|
this->setWindowTitle(tr("Hexdump"));
|
2019-01-13 15:00:07 +00:00
|
|
|
|
|
|
|
refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
|
|
|
|
refresh(offset ? *offset : RVA_INVALID);
|
|
|
|
});
|
|
|
|
|
2018-05-25 14:30:59 +00:00
|
|
|
connect(&syncAction, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
|
2019-05-10 11:15:57 +00:00
|
|
|
syncAction.setText(tr("Sync/unsync offset"));
|
|
|
|
this->ui->hexTextView->addAction(&syncAction);
|
2017-04-05 14:03:36 +00:00
|
|
|
|
2017-11-20 10:10:31 +00:00
|
|
|
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
|
|
|
|
|
2017-11-04 11:46:29 +00:00
|
|
|
connect(this, &QDockWidget::visibilityChanged, this, [](bool visibility) {
|
2018-03-21 20:32:32 +00:00
|
|
|
if (visibility) {
|
2017-11-04 11:46:29 +00:00
|
|
|
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Hexdump);
|
|
|
|
}
|
|
|
|
});
|
2017-05-13 18:09:36 +00:00
|
|
|
|
2017-11-19 12:56:10 +00:00
|
|
|
connect(Core(), &CutterCore::refreshAll, this, [this]() {
|
2018-06-27 15:33:21 +00:00
|
|
|
refresh(seekable->getOffset());
|
2017-11-19 12:56:10 +00:00
|
|
|
});
|
|
|
|
|
2019-01-12 17:02:51 +00:00
|
|
|
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &HexdumpWidget::onSeekChanged);
|
2019-05-10 11:15:57 +00:00
|
|
|
connect(ui->hexTextView, &HexTextView::positionChanged, this, [this](RVA addr) {
|
|
|
|
if (!sent_seek) {
|
|
|
|
sent_seek = true;
|
|
|
|
seekable->seek(addr);
|
|
|
|
sent_seek = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
connect(ui->hexTextView, &HexTextView::selectionChanged, this, &HexdumpWidget::selectionChanged);
|
2019-04-25 08:49:10 +00:00
|
|
|
|
2017-12-03 10:50:14 +00:00
|
|
|
initParsing();
|
2017-12-03 01:44:12 +00:00
|
|
|
selectHexPreview();
|
2017-04-28 13:09:40 +00:00
|
|
|
}
|
|
|
|
|
2019-05-10 11:15:57 +00:00
|
|
|
void HexdumpWidget::onSeekChanged(RVA addr)
|
2017-04-28 13:09:40 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (sent_seek) {
|
2018-01-27 10:08:05 +00:00
|
|
|
sent_seek = false;
|
|
|
|
return;
|
|
|
|
}
|
2019-05-10 11:15:57 +00:00
|
|
|
refresh(addr);
|
2017-11-17 12:41:30 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
HexdumpWidget::~HexdumpWidget() {}
|
|
|
|
|
|
|
|
void HexdumpWidget::refresh(RVA addr)
|
2017-04-09 19:55:06 +00:00
|
|
|
{
|
2019-01-13 15:00:07 +00:00
|
|
|
if (!refreshDeferrer->attemptRefresh(addr == RVA_INVALID ? nullptr : new RVA(addr))) {
|
2019-01-12 18:01:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-05-10 11:15:57 +00:00
|
|
|
sent_seek = true;
|
|
|
|
ui->hexTextView->refresh(addr);
|
|
|
|
sent_seek = false;
|
2017-11-17 12:41:30 +00:00
|
|
|
}
|
2017-11-15 21:56:10 +00:00
|
|
|
|
2017-11-19 21:21:02 +00:00
|
|
|
|
2017-12-03 10:50:14 +00:00
|
|
|
void HexdumpWidget::initParsing()
|
2017-10-12 19:55:15 +00:00
|
|
|
{
|
|
|
|
// Fill the plugins combo for the hexdump sidebar
|
2017-12-03 10:50:14 +00:00
|
|
|
ui->parseArchComboBox->insertItems(0, Core()->getAsmPluginNames());
|
|
|
|
|
|
|
|
ui->parseEndianComboBox->setCurrentIndex(Core()->getConfigb("cfg.bigendian") ? 1 : 0);
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-10 11:15:57 +00:00
|
|
|
void HexdumpWidget::selectionChanged(HexTextView::Selection selection)
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
2019-05-10 11:15:57 +00:00
|
|
|
if (selection.empty) {
|
|
|
|
clearParseWindow();
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2019-05-10 11:15:57 +00:00
|
|
|
updateParseWindow(selection.startAddress, selection.endAddress - selection.startAddress + 1);
|
2018-01-27 10:08:05 +00:00
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2017-12-03 10:50:14 +00:00
|
|
|
void HexdumpWidget::on_parseArchComboBox_currentTextChanged(const QString &/*arg1*/)
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
2019-05-10 11:15:57 +00:00
|
|
|
refreshSelectionInfo();
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
2017-12-03 10:50:14 +00:00
|
|
|
|
|
|
|
void HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString &/*arg1*/)
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
2019-05-10 11:15:57 +00:00
|
|
|
refreshSelectionInfo();
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
2017-11-18 14:33:52 +00:00
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
2018-05-25 14:30:59 +00:00
|
|
|
void HexdumpWidget::toggleSync()
|
|
|
|
{
|
|
|
|
QString windowTitle = tr("Hexdump");
|
2019-01-13 14:20:07 +00:00
|
|
|
seekable->toggleSynchronization();
|
|
|
|
if (seekable->isSynchronized()) {
|
2018-05-25 14:30:59 +00:00
|
|
|
setWindowTitle(windowTitle);
|
|
|
|
} else {
|
2019-01-13 14:20:07 +00:00
|
|
|
setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)"));
|
2018-05-25 14:30:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 12:41:30 +00:00
|
|
|
|
2017-11-20 10:10:31 +00:00
|
|
|
void HexdumpWidget::setupFonts()
|
2017-04-09 19:55:06 +00:00
|
|
|
{
|
2017-11-20 10:10:31 +00:00
|
|
|
QFont font = Config()->getFont();
|
2017-11-26 21:54:44 +00:00
|
|
|
ui->hexDisasTextEdit->setFont(font);
|
2019-05-10 11:15:57 +00:00
|
|
|
ui->hexTextView->setupFonts();
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-10 11:15:57 +00:00
|
|
|
void HexdumpWidget::refreshSelectionInfo()
|
2017-11-20 10:10:31 +00:00
|
|
|
{
|
2019-05-10 11:15:57 +00:00
|
|
|
selectionChanged(ui->hexTextView->getSelection());
|
2017-11-20 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
2019-05-10 11:15:57 +00:00
|
|
|
void HexdumpWidget::fontsUpdated()
|
2017-11-20 16:38:10 +00:00
|
|
|
{
|
2019-05-10 11:15:57 +00:00
|
|
|
setupFonts();
|
2018-01-27 10:08:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HexdumpWidget::clearParseWindow()
|
|
|
|
{
|
|
|
|
ui->hexDisasTextEdit->setPlainText("");
|
|
|
|
ui->bytesEntropy->setText("");
|
|
|
|
ui->bytesMD5->setText("");
|
|
|
|
ui->bytesSHA1->setText("");
|
|
|
|
}
|
|
|
|
|
|
|
|
void HexdumpWidget::updateParseWindow(RVA start_address, int size)
|
|
|
|
{
|
|
|
|
|
|
|
|
QString address = RAddressString(start_address);
|
|
|
|
|
2018-01-27 13:11:30 +00:00
|
|
|
QString argument = QString("%1@" + address).arg(size);
|
2018-01-27 10:08:05 +00:00
|
|
|
// Get selected combos
|
|
|
|
QString arch = ui->parseArchComboBox->currentText();
|
|
|
|
QString bits = ui->parseBitsComboBox->currentText();
|
|
|
|
bool bigEndian = ui->parseEndianComboBox->currentIndex() == 1;
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
{
|
|
|
|
// scope for TempConfig
|
2018-01-27 10:08:05 +00:00
|
|
|
TempConfig tempConfig;
|
|
|
|
tempConfig
|
2018-03-21 20:32:32 +00:00
|
|
|
.set("asm.arch", arch)
|
|
|
|
.set("asm.bits", bits)
|
|
|
|
.set("cfg.bigendian", bigEndian);
|
2018-01-27 10:08:05 +00:00
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
switch (ui->parseTypeComboBox->currentIndex()) {
|
|
|
|
case 0: // Disassembly
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pda " + argument));
|
|
|
|
break;
|
|
|
|
case 1: // String
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcs " + argument));
|
|
|
|
break;
|
|
|
|
case 2: // Assembler
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pca " + argument));
|
|
|
|
break;
|
|
|
|
case 3: // C byte array
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pc " + argument));
|
|
|
|
break;
|
|
|
|
case 4: // C half-word
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pch " + argument));
|
|
|
|
break;
|
|
|
|
case 5: // C word
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcw " + argument));
|
|
|
|
break;
|
|
|
|
case 6: // C dword
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcd " + argument));
|
|
|
|
break;
|
|
|
|
case 7: // Python
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcp " + argument));
|
|
|
|
break;
|
|
|
|
case 8: // JSON
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcj " + argument));
|
|
|
|
break;
|
|
|
|
case 9: // JavaScript
|
|
|
|
ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcJ " + argument));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ui->hexDisasTextEdit->setPlainText("");
|
2018-01-27 10:08:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill the information tab hashes and entropy
|
|
|
|
ui->bytesMD5->setText(Core()->cmd("ph md5 " + argument).trimmed());
|
|
|
|
ui->bytesSHA1->setText(Core()->cmd("ph sha1 " + argument).trimmed());
|
|
|
|
ui->bytesEntropy->setText(Core()->cmd("ph entropy " + argument).trimmed());
|
|
|
|
ui->bytesMD5->setCursorPosition(0);
|
|
|
|
ui->bytesSHA1->setCursorPosition(0);
|
2017-11-20 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
2017-11-20 10:10:31 +00:00
|
|
|
/*
|
|
|
|
* Actions callback functions
|
|
|
|
*/
|
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
void HexdumpWidget::on_actionHideHexdump_side_panel_triggered()
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ui->hexSideTab_2->isVisible()) {
|
2017-03-29 10:18:37 +00:00
|
|
|
ui->hexSideTab_2->hide();
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-03-29 10:18:37 +00:00
|
|
|
ui->hexSideTab_2->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-18 17:26:38 +00:00
|
|
|
|
2018-01-27 10:08:05 +00:00
|
|
|
void HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &)
|
2017-04-09 19:55:06 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ui->parseTypeComboBox->currentIndex() == 0) {
|
2018-01-27 10:08:05 +00:00
|
|
|
ui->hexSideFrame_2->show();
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2018-01-27 10:08:05 +00:00
|
|
|
ui->hexSideFrame_2->hide();
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
2019-05-10 11:15:57 +00:00
|
|
|
refreshSelectionInfo();
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-27 10:08:05 +00:00
|
|
|
void HexdumpWidget::on_parseEndianComboBox_currentTextChanged(const QString &)
|
2017-04-09 19:55:06 +00:00
|
|
|
{
|
2019-05-10 11:15:57 +00:00
|
|
|
refreshSelectionInfo();
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
void HexdumpWidget::on_hexSideTab_2_currentChanged(int /*index*/)
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
if (index == 2) {
|
|
|
|
// Add data to HTML Polar functions graph
|
|
|
|
QFile html(":/html/bar.html");
|
|
|
|
if(!html.open(QIODevice::ReadOnly)) {
|
|
|
|
QMessageBox::information(0,"error",html.errorString());
|
|
|
|
}
|
|
|
|
QString code = html.readAll();
|
|
|
|
html.close();
|
|
|
|
this->histoWebView->setHtml(code);
|
|
|
|
this->histoWebView->show();
|
|
|
|
} else {
|
|
|
|
this->histoWebView->hide();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2018-01-27 10:08:05 +00:00
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
void HexdumpWidget::resizeEvent(QResizeEvent *event)
|
2017-04-09 19:55:06 +00:00
|
|
|
{
|
2017-04-10 10:25:33 +00:00
|
|
|
QDockWidget::resizeEvent(event);
|
2018-01-27 10:08:05 +00:00
|
|
|
refresh();
|
2017-04-10 10:25:33 +00:00
|
|
|
}
|
|
|
|
|
2017-11-08 13:17:24 +00:00
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
void HexdumpWidget::on_copyMD5_clicked()
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
|
|
|
QString md5 = ui->bytesMD5->text();
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(md5);
|
2017-10-09 18:08:35 +00:00
|
|
|
// FIXME
|
|
|
|
// this->main->addOutput("MD5 copied to clipboard: " + md5);
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
void HexdumpWidget::on_copySHA1_clicked()
|
2017-03-29 10:18:37 +00:00
|
|
|
{
|
|
|
|
QString sha1 = ui->bytesSHA1->text();
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(sha1);
|
2017-10-09 18:08:35 +00:00
|
|
|
// FIXME
|
|
|
|
// this->main->addOutput("SHA1 copied to clipboard: " + sha1);
|
2017-03-29 10:18:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-27 10:08:05 +00:00
|
|
|
|
2017-10-12 19:55:15 +00:00
|
|
|
void HexdumpWidget::selectHexPreview()
|
2017-04-09 19:55:06 +00:00
|
|
|
{
|
2017-03-30 09:27:43 +00:00
|
|
|
// Pre-select arch and bits in the hexdump sidebar
|
2018-08-14 15:07:52 +00:00
|
|
|
QString arch = Core()->getConfig("asm.arch");
|
|
|
|
QString bits = Core()->getConfig("asm.bits");
|
2017-03-30 09:27:43 +00:00
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ui->parseArchComboBox->findText(arch) != -1) {
|
2017-12-03 10:50:14 +00:00
|
|
|
ui->parseArchComboBox->setCurrentIndex(ui->parseArchComboBox->findText(arch));
|
2017-03-30 09:27:43 +00:00
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ui->parseBitsComboBox->findText(bits) != -1) {
|
2017-12-03 10:50:14 +00:00
|
|
|
ui->parseBitsComboBox->setCurrentIndex(ui->parseBitsComboBox->findText(bits));
|
2017-03-30 09:27:43 +00:00
|
|
|
}
|
|
|
|
}
|