mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-22 12:56:11 +00:00
3eeb631c68
* Add SHA256 and CRC32 to hexdump widget * Fix layout * Add copied-to-clipboard messages
389 lines
11 KiB
C++
389 lines
11 KiB
C++
#include "HexdumpWidget.h"
|
|
#include "ui_HexdumpWidget.h"
|
|
|
|
#include "common/Helpers.h"
|
|
#include "common/Configuration.h"
|
|
#include "common/TempConfig.h"
|
|
#include "common/SyntaxHighlighter.h"
|
|
#include "core/MainWindow.h"
|
|
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
#include <QElapsedTimer>
|
|
#include <QTextDocumentFragment>
|
|
#include <QMenu>
|
|
#include <QClipboard>
|
|
#include <QScrollBar>
|
|
#include <QInputDialog>
|
|
#include <QShortcut>
|
|
|
|
HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
|
MemoryDockWidget(MemoryWidgetType::Hexdump, main, action),
|
|
ui(new Ui::HexdumpWidget)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
setObjectName(main
|
|
? main->getUniqueObjectName(getWidgetType())
|
|
: getWidgetType());
|
|
|
|
ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg"));
|
|
ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg"));
|
|
ui->copySHA256->setIcon(QIcon(":/img/icons/copy.svg"));
|
|
ui->copyCRC32->setIcon(QIcon(":/img/icons/copy.svg"));
|
|
|
|
|
|
ui->splitter->setChildrenCollapsible(false);
|
|
|
|
QToolButton *closeButton = new QToolButton;
|
|
QIcon closeIcon = QIcon(":/img/icons/delete.svg");
|
|
closeButton->setIcon(closeIcon);
|
|
closeButton->setAutoRaise(true);
|
|
|
|
ui->hexSideTab_2->setCornerWidget(closeButton);
|
|
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->hexDisasTextEdit->document());
|
|
|
|
ui->openSideViewB->hide(); // hide button at startup since side view is visible
|
|
|
|
connect(closeButton, &QToolButton::clicked, this, [this] {
|
|
showSidePanel(false);
|
|
});
|
|
|
|
connect(ui->openSideViewB, &QToolButton::clicked, this, [this] {
|
|
showSidePanel(true);
|
|
});
|
|
|
|
// Set placeholders for the line-edit components
|
|
QString placeholder = tr("Select bytes to display information");
|
|
ui->bytesMD5->setPlaceholderText(placeholder);
|
|
ui->bytesEntropy->setPlaceholderText(placeholder);
|
|
ui->bytesSHA1->setPlaceholderText(placeholder);
|
|
ui->bytesSHA256->setPlaceholderText(placeholder);
|
|
ui->bytesCRC32->setPlaceholderText(placeholder);
|
|
ui->hexDisasTextEdit->setPlaceholderText(placeholder);
|
|
|
|
setupFonts();
|
|
|
|
ui->openSideViewB->setStyleSheet(""
|
|
"QToolButton {"
|
|
" border : 0px;"
|
|
" padding : 0px;"
|
|
" margin : 0px;"
|
|
"}"
|
|
"QToolButton:hover {"
|
|
" border : 1px solid;"
|
|
" border-width : 1px;"
|
|
" border-color : #3daee9"
|
|
"}");
|
|
|
|
setWindowTitle(getWindowTitle());
|
|
|
|
refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
|
|
refresh(offset ? *offset : RVA_INVALID);
|
|
});
|
|
|
|
this->ui->hexTextView->addAction(&syncAction);
|
|
|
|
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
|
|
connect(Core(), &CutterCore::refreshAll, this, [this]() { refresh(); });
|
|
connect(Core(), &CutterCore::instructionChanged, this, [this]() { refresh(); });
|
|
connect(Core(), &CutterCore::stackChanged, this, [this]() { refresh(); });
|
|
connect(Core(), &CutterCore::registersChanged, this, [this]() { refresh(); });
|
|
|
|
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &HexdumpWidget::onSeekChanged);
|
|
connect(ui->hexTextView, &HexWidget::positionChanged, this, [this](RVA addr) {
|
|
if (!sent_seek) {
|
|
sent_seek = true;
|
|
seekable->seek(addr);
|
|
sent_seek = false;
|
|
}
|
|
});
|
|
connect(ui->hexTextView, &HexWidget::selectionChanged, this, &HexdumpWidget::selectionChanged);
|
|
connect(ui->hexSideTab_2, &QTabWidget::currentChanged, this, &HexdumpWidget::refreshSelectionInfo);
|
|
ui->hexTextView->installEventFilter(this);
|
|
|
|
initParsing();
|
|
selectHexPreview();
|
|
|
|
// apply initial offset
|
|
refresh(seekable->getOffset());
|
|
}
|
|
|
|
void HexdumpWidget::onSeekChanged(RVA addr)
|
|
{
|
|
if (sent_seek) {
|
|
sent_seek = false;
|
|
return;
|
|
}
|
|
refresh(addr);
|
|
}
|
|
|
|
HexdumpWidget::~HexdumpWidget() {}
|
|
|
|
QString HexdumpWidget::getWidgetType()
|
|
{
|
|
return "Hexdump";
|
|
}
|
|
|
|
void HexdumpWidget::refresh()
|
|
{
|
|
refresh(RVA_INVALID);
|
|
}
|
|
|
|
void HexdumpWidget::refresh(RVA addr)
|
|
{
|
|
if (!refreshDeferrer->attemptRefresh(addr == RVA_INVALID ? nullptr : new RVA(addr))) {
|
|
return;
|
|
}
|
|
sent_seek = true;
|
|
if (addr != RVA_INVALID) {
|
|
ui->hexTextView->seek(addr);
|
|
} else {
|
|
ui->hexTextView->refresh();
|
|
refreshSelectionInfo();
|
|
}
|
|
sent_seek = false;
|
|
}
|
|
|
|
|
|
void HexdumpWidget::initParsing()
|
|
{
|
|
// Fill the plugins combo for the hexdump sidebar
|
|
ui->parseArchComboBox->insertItems(0, Core()->getAsmPluginNames());
|
|
|
|
ui->parseEndianComboBox->setCurrentIndex(Core()->getConfigb("cfg.bigendian") ? 1 : 0);
|
|
}
|
|
|
|
void HexdumpWidget::selectionChanged(HexWidget::Selection selection)
|
|
{
|
|
if (selection.empty) {
|
|
clearParseWindow();
|
|
} else {
|
|
updateParseWindow(selection.startAddress, selection.endAddress - selection.startAddress + 1);
|
|
}
|
|
}
|
|
|
|
void HexdumpWidget::on_parseArchComboBox_currentTextChanged(const QString &/*arg1*/)
|
|
{
|
|
refreshSelectionInfo();
|
|
}
|
|
|
|
void HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString &/*arg1*/)
|
|
{
|
|
refreshSelectionInfo();
|
|
}
|
|
|
|
void HexdumpWidget::setupFonts()
|
|
{
|
|
QFont font = Config()->getFont();
|
|
ui->hexDisasTextEdit->setFont(font);
|
|
ui->hexTextView->setMonospaceFont(font);
|
|
}
|
|
|
|
void HexdumpWidget::refreshSelectionInfo()
|
|
{
|
|
selectionChanged(ui->hexTextView->getSelection());
|
|
}
|
|
|
|
void HexdumpWidget::fontsUpdated()
|
|
{
|
|
setupFonts();
|
|
}
|
|
|
|
void HexdumpWidget::clearParseWindow()
|
|
{
|
|
ui->hexDisasTextEdit->setPlainText("");
|
|
ui->bytesEntropy->setText("");
|
|
ui->bytesMD5->setText("");
|
|
ui->bytesSHA1->setText("");
|
|
ui->bytesSHA256->setText("");
|
|
ui->bytesCRC32->setText("");
|
|
}
|
|
|
|
void HexdumpWidget::showSidePanel(bool show)
|
|
{
|
|
ui->hexSideTab_2->setVisible(show);
|
|
ui->openSideViewB->setHidden(show);
|
|
if (show) {
|
|
refreshSelectionInfo();
|
|
}
|
|
}
|
|
|
|
QString HexdumpWidget::getWindowTitle() const
|
|
{
|
|
return tr("Hexdump");
|
|
}
|
|
|
|
void HexdumpWidget::updateParseWindow(RVA start_address, int size)
|
|
{
|
|
if (!ui->hexSideTab_2->isVisible()) {
|
|
return;
|
|
}
|
|
|
|
QString address = RAddressString(start_address);
|
|
QString argument = QString("%1@" + address).arg(size);
|
|
|
|
if (ui->hexSideTab_2->currentIndex() == 0) {
|
|
// scope for TempConfig
|
|
|
|
// Get selected combos
|
|
QString arch = ui->parseArchComboBox->currentText();
|
|
QString bits = ui->parseBitsComboBox->currentText();
|
|
QString selectedCommand = "";
|
|
QString commandResult = "";
|
|
bool bigEndian = ui->parseEndianComboBox->currentIndex() == 1;
|
|
|
|
TempConfig tempConfig;
|
|
tempConfig
|
|
.set("asm.arch", arch)
|
|
.set("asm.bits", bits)
|
|
.set("cfg.bigendian", bigEndian);
|
|
|
|
switch (ui->parseTypeComboBox->currentIndex()) {
|
|
case 0: // Disassembly
|
|
selectedCommand = "pda";
|
|
break;
|
|
case 1: // String
|
|
selectedCommand = "pcs";
|
|
break;
|
|
case 2: // Assembler
|
|
selectedCommand = "pca";
|
|
break;
|
|
case 3: // C byte array
|
|
selectedCommand = "pc";
|
|
break;
|
|
case 4: // C half-word
|
|
selectedCommand = "pch";
|
|
break;
|
|
case 5: // C word
|
|
selectedCommand = "pcw";
|
|
break;
|
|
case 6: // C dword
|
|
selectedCommand = "pcd";
|
|
break;
|
|
case 7: // Python
|
|
selectedCommand = "pcp";
|
|
break;
|
|
case 8: // JSON
|
|
selectedCommand = "pcj";
|
|
break;
|
|
case 9: // JavaScript
|
|
selectedCommand = "pcJ";
|
|
break;
|
|
case 10: // Yara
|
|
selectedCommand = "pcy";
|
|
break;
|
|
}
|
|
ui->hexDisasTextEdit->setPlainText(selectedCommand != "" ? Core()->cmd(selectedCommand + " " + argument) : "");
|
|
} else {
|
|
// 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->bytesSHA256->setText(Core()->cmd("ph sha256 " + argument).trimmed());
|
|
ui->bytesCRC32->setText(Core()->cmd("ph crc32 " + argument).trimmed());
|
|
ui->bytesEntropy->setText(Core()->cmd("ph entropy " + argument).trimmed());
|
|
ui->bytesMD5->setCursorPosition(0);
|
|
ui->bytesSHA1->setCursorPosition(0);
|
|
ui->bytesSHA256->setCursorPosition(0);
|
|
ui->bytesCRC32->setCursorPosition(0);
|
|
}
|
|
}
|
|
|
|
void HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &)
|
|
{
|
|
if (ui->parseTypeComboBox->currentIndex() == 0) {
|
|
ui->hexSideFrame_2->show();
|
|
} else {
|
|
ui->hexSideFrame_2->hide();
|
|
}
|
|
refreshSelectionInfo();
|
|
}
|
|
|
|
void HexdumpWidget::on_parseEndianComboBox_currentTextChanged(const QString &)
|
|
{
|
|
refreshSelectionInfo();
|
|
}
|
|
|
|
void HexdumpWidget::on_hexSideTab_2_currentChanged(int /*index*/)
|
|
{
|
|
/*
|
|
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();
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
void HexdumpWidget::resizeEvent(QResizeEvent *event)
|
|
{
|
|
// Heuristics to hide sidebar when it hides the content of the hexdump. 600px looks just "okay"
|
|
// Only applied when widget width is decreased to avoid unwanted behavior
|
|
if (event->oldSize().width() > event->size().width() && event->size().width() < 600) {
|
|
showSidePanel(false);
|
|
}
|
|
QDockWidget::resizeEvent(event);
|
|
refresh();
|
|
}
|
|
|
|
QWidget *HexdumpWidget::widgetToFocusOnRaise()
|
|
{
|
|
return ui->hexTextView;
|
|
}
|
|
|
|
void HexdumpWidget::on_copyMD5_clicked()
|
|
{
|
|
QString md5 = ui->bytesMD5->text();
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(md5);
|
|
Core()->message("MD5 copied to clipboard: " + md5);
|
|
}
|
|
|
|
void HexdumpWidget::on_copySHA1_clicked()
|
|
{
|
|
QString sha1 = ui->bytesSHA1->text();
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(sha1);
|
|
Core()->message("SHA1 copied to clipboard: " + sha1);
|
|
}
|
|
|
|
void HexdumpWidget::on_copySHA256_clicked()
|
|
{
|
|
QString sha256 = ui->bytesSHA256->text();
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(sha256);
|
|
Core()->message("SHA256 copied to clipboard: " + sha256);
|
|
}
|
|
|
|
void HexdumpWidget::on_copyCRC32_clicked()
|
|
{
|
|
QString crc32 = ui->bytesCRC32->text();
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(crc32);
|
|
Core()->message("CRC32 copied to clipboard: " + crc32);
|
|
}
|
|
|
|
void HexdumpWidget::selectHexPreview()
|
|
{
|
|
// Pre-select arch and bits in the hexdump sidebar
|
|
QString arch = Core()->getConfig("asm.arch");
|
|
QString bits = Core()->getConfig("asm.bits");
|
|
|
|
if (ui->parseArchComboBox->findText(arch) != -1) {
|
|
ui->parseArchComboBox->setCurrentIndex(ui->parseArchComboBox->findText(arch));
|
|
}
|
|
|
|
if (ui->parseBitsComboBox->findText(bits) != -1) {
|
|
ui->parseBitsComboBox->setCurrentIndex(ui->parseBitsComboBox->findText(bits));
|
|
}
|
|
}
|