mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-22 04:46:11 +00:00
75146d63f9
Fixes #1528
365 lines
10 KiB
C++
365 lines
10 KiB
C++
#include "HexdumpWidget.h"
|
|
#include "ui_HexdumpWidget.h"
|
|
|
|
#include "common/Helpers.h"
|
|
#include "common/Configuration.h"
|
|
#include "common/TempConfig.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(CutterCore::MemoryWidgetType::Hexdump, main, action),
|
|
ui(new Ui::HexdumpWidget),
|
|
seekable(new CutterSeekable(this))
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg"));
|
|
ui->copySHA1->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);
|
|
|
|
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);
|
|
});
|
|
|
|
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");
|
|
|
|
setupFonts();
|
|
|
|
ui->openSideViewB->setStyleSheet(""
|
|
"QToolButton {"
|
|
" border : 0px;"
|
|
" padding : 0px;"
|
|
" margin : 0px;"
|
|
"}"
|
|
"QToolButton:hover {"
|
|
" border : 1px solid;"
|
|
" border-width : 1px;"
|
|
" border-color : #3daee9"
|
|
"}");
|
|
|
|
this->setWindowTitle(tr("Hexdump"));
|
|
|
|
refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
|
|
refresh(offset ? *offset : RVA_INVALID);
|
|
});
|
|
|
|
connect(&syncAction, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
|
|
syncAction.setText(tr("Sync/unsync offset"));
|
|
this->ui->hexTextView->addAction(&syncAction);
|
|
|
|
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
|
|
|
|
connect(this, &QDockWidget::visibilityChanged, this, [](bool visibility) {
|
|
if (visibility) {
|
|
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Hexdump);
|
|
}
|
|
});
|
|
|
|
connect(Core(), &CutterCore::refreshAll, 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);
|
|
|
|
initParsing();
|
|
selectHexPreview();
|
|
}
|
|
|
|
void HexdumpWidget::onSeekChanged(RVA addr)
|
|
{
|
|
if (sent_seek) {
|
|
sent_seek = false;
|
|
return;
|
|
}
|
|
refresh(addr);
|
|
}
|
|
|
|
HexdumpWidget::~HexdumpWidget() {}
|
|
|
|
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::toggleSync()
|
|
{
|
|
QString windowTitle = tr("Hexdump");
|
|
seekable->toggleSynchronization();
|
|
if (seekable->isSynchronized()) {
|
|
setWindowTitle(windowTitle);
|
|
} else {
|
|
setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)"));
|
|
}
|
|
}
|
|
|
|
|
|
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("");
|
|
}
|
|
|
|
void HexdumpWidget::showSidePanel(bool show)
|
|
{
|
|
ui->hexSideTab_2->setVisible(show);
|
|
ui->openSideViewB->setHidden(show);
|
|
if (show) {
|
|
refreshSelectionInfo();
|
|
}
|
|
}
|
|
|
|
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();
|
|
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
|
|
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("");
|
|
}
|
|
} 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->bytesEntropy->setText(Core()->cmd("ph entropy " + argument).trimmed());
|
|
ui->bytesMD5->setCursorPosition(0);
|
|
ui->bytesSHA1->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)
|
|
{
|
|
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);
|
|
// FIXME
|
|
// this->main->addOutput("MD5 copied to clipboard: " + md5);
|
|
}
|
|
|
|
void HexdumpWidget::on_copySHA1_clicked()
|
|
{
|
|
QString sha1 = ui->bytesSHA1->text();
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(sha1);
|
|
// FIXME
|
|
// this->main->addOutput("SHA1 copied to clipboard: " + sha1);
|
|
}
|
|
|
|
|
|
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));
|
|
}
|
|
}
|