pe-bear/pe-bear/PEDockedWidget.cpp
2023-09-22 11:59:27 +08:00

412 lines
13 KiB
C++

#include "PEDockedWidget.h"
#include "gui_base/AddressInputDialog.h"
PEDockedWidget::PEDockedWidget(PeHandler *peHndl, QWidget *parent)
: PeViewItem(peHndl), QDockWidget(parent),
mainSplitter(Qt::Horizontal, this), tagBrowser(peHndl, this),
cntntSplitter(NULL),
contentPrev(NULL),
tabWidget(NULL),
toolBar(NULL)
{
if (!myPeHndl || !m_PE) return;
this->setWidget(&dockWindow);
dockWindow.setCentralWidget(&mainSplitter);
this->setWindowTitle(myPeHndl->getShortName());
this->setAutoFillBackground(true);
cntntSplitter = new QSplitter(Qt::Vertical, &mainSplitter);
contentPrev = new ContentPreview(peHndl, cntntSplitter);
tabWidget = new DetailsTab(peHndl, cntntSplitter);
diagramModel = new SecDiagramModel(this->myPeHndl);
diagram = new SelectableSecDiagram(diagramModel, true, cntntSplitter);
tabWidget->setFocusPolicy(Qt::StrongFocus);
tabWidget->setAutoFillBackground(true);
setupActionsToolbar(cntntSplitter);
cntntSplitter->addWidget(contentPrev);
cntntSplitter->addWidget(tabWidget);
mainSplitter.addWidget(cntntSplitter);
mainSplitter.addWidget(diagram);
setupDiagram();
connect(this, SIGNAL(signalChangeHexViewSettings(HexViewSettings &)),
contentPrev, SLOT(changeHexViewSettings(HexViewSettings &)) );
connect(this, SIGNAL(signalChangeDisasmViewSettings(DisasmViewSettings &)),
&tabWidget->disasmView, SLOT(changeDisasmViewSettings(DisasmViewSettings &)) );
}
void PEDockedWidget::setScaledIcons()
{
if (!toolBar) return;
const int iconDim = ViewSettings::getSmallIconDim(QApplication::font());
toolBar->setIconSize(QSize(iconDim, iconDim));
toolBar->layout()->setSpacing(iconDim);
//tool bar icons:
QIcon previewIco = ViewSettings::makeScaledIcon(":/icons/arrow-right.ico", iconDim, iconDim);
this->goToEntryPointA->setIcon(previewIco);
QIcon goToRvaIco = ViewSettings::makeScaledIcon(":/icons/go_to_rva.ico", iconDim, iconDim);
this->goToRvaAction->setIcon(goToRvaIco);
QIcon goToRawIco = ViewSettings::makeScaledIcon(":/icons/go_to_raw.ico", iconDim, iconDim);
this->goToRawAction->setIcon(goToRawIco);
QIcon backIco = ViewSettings::makeScaledIcon(":/icons/undo.ico", iconDim, iconDim);
backAction->setIcon(backIco);
QIcon pinIco = ViewSettings::makeScaledIcon(":/icons/red_pin.ico", iconDim, iconDim);
this->goToModifAction->setIcon(pinIco);
QIcon umodIco = ViewSettings::makeScaledIcon(":/icons/unmodify.ico", iconDim, iconDim);
this->unModifyAction->setIcon(umodIco);
QIcon tagIco = ViewSettings::makeScaledIcon(":/icons/star.ico", iconDim, iconDim);
this->tagsAction->setIcon(tagIco);
}
void PEDockedWidget::setupActionsToolbar(QSplitter* owner)
{
if (!owner) return;
this->toolBar = new QToolBar(&dockWindow);
dockWindow.addToolBar(Qt::TopToolBarArea, toolBar);;
//const Qt::ShortcutContext context = Qt::WidgetWithChildrenShortcut;
this->goToEntryPointA = new QAction(tr("&Preview Entry Point\n[CTRL + E]"), &dockWindow);
//goToEntryPointA->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
//goToEntryPointA->setShortcutContext(context);
connect(goToEntryPointA, SIGNAL(triggered()), this, SLOT(goToEntryPoint()) );
this->goToRvaAction = new QAction(tr("&Go to RVA/VA\n[CTRL + R]"), &dockWindow);
//goToRvaAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
//goToRvaAction->setShortcutContext(context);
connect(goToRvaAction, SIGNAL(triggered()), this, SLOT(goToRVA()) );
this->goToRawAction = new QAction(tr("&Go to raw+\n[CTRL + G]"), &dockWindow);
//goToRawAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_G));
//goToRawAction->setShortcutContext(context);
connect(goToRawAction, SIGNAL(triggered()), this, SLOT(goToOffset()) );
this->backAction = new QAction(tr("&Back to last visited offset\n[CTRL + B]"), &dockWindow);
//backAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B));
//backAction->setShortcutContext(context);
connect(backAction, SIGNAL(triggered()), this, SLOT(undoOffset()) );
this->goToModifAction = new QAction(tr("&Go to last modification"), &dockWindow);
connect(goToModifAction, SIGNAL(triggered()), this, SLOT(goToLastModif()) );
this->unModifyAction = new QAction( tr("&Undo last modifications\n[CTRL + Z]"), &dockWindow);
//unModifyAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z));
//unModifyAction->setShortcutContext(context);
connect(unModifyAction, SIGNAL(triggered()), this, SLOT(unModify()) );
this->tagsAction = new QAction(tr("&Tag"), &dockWindow);
//tagsAction->setShortcut(QKeySequence(Qt::CTRL + ';'));
//tagsAction->setShortcutContext(context);
connect(tagsAction, SIGNAL(triggered()), this, SLOT(browseTags()) );
toolBar->addAction(goToEntryPointA);
toolBar->addAction(goToRvaAction);
toolBar->addAction(goToRawAction);
toolBar->addAction(backAction);
toolBar->addAction(goToModifAction);
toolBar->addAction(unModifyAction);
toolBar->addAction(tagsAction);
const size_t marginSize = 5;
toolBar->layout()->setContentsMargins(marginSize, marginSize, marginSize, marginSize);
toolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setScaledIcons();
updateModifActions();
updateNavigActions();
connect(this->myPeHndl, SIGNAL(modified()), this, SLOT(updateModifActions()));
connect(this->myPeHndl, SIGNAL(pageOffsetModified(offset_t, bufsize_t)), this, SLOT( updateNavigActions() ));
}
void PEDockedWidget::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Undo)) {
return myPeHndl->unModify();
}
switch (event->key()) {
case Qt::Key_PageUp:
myPeHndl->advanceOffset(- PREVIEW_SIZE);
break;
case Qt::Key_PageDown:
myPeHndl->advanceOffset(PREVIEW_SIZE);
break;
case Qt::Key_Up:
myPeHndl->advanceOffset(-1);
break;
case Qt::Key_Down:
myPeHndl->advanceOffset(1);
break;
case Qt::Key_G:
goToOffset();
break;
case Qt::Key_E:
goToEntryPoint();
break;
case Qt::Key_R:
goToRVA();
break;
case Qt::Key_B:
myPeHndl->undoDisplayOffset();
break;
}
QDockWidget::keyPressEvent(event);
}
void PEDockedWidget::updateNavigActions()
{
bool hasModif = this->myPeHndl->prevOffsets.size() > 0;
QString num;
if (hasModif) {
const offset_t off = this->myPeHndl->prevOffsets.top();
num = ": " + QString::number(off, 16);
}
QString text = tr("&Back to last visited offset") + num + "\n[CTRL + B]";
backAction->setText(text);
backAction->setEnabled(hasModif);
}
void PEDockedWidget::updateModifActions()
{
bool hasModif = this->myPeHndl->modifHndl.countOperations() ? true : false;
goToModifAction->setEnabled(hasModif);
unModifyAction->setEnabled(hasModif);
}
void PEDockedWidget::setupDiagram()
{
this->diagram->setBackgroundColor(Qt::black);
this->diagram->contourColor = Qt::lightGray;
this->diagram->setMaximumWidth(this->diagram->minimumSizeHint().width());
this->diagram->setMinimumWidth(this->diagram->minimumSizeHint().width());
connect(this->myPeHndl, SIGNAL(pageOffsetModified(offset_t, bufsize_t)),
this->diagramModel, SLOT(setSelectedArea(offset_t, bufsize_t)) );
}
void PEDockedWidget::goToLastModif()
{
offset_t modS = this->myPeHndl->modifHndl.getLastModifiedOffset();
if (modS == INVALID_ADDR) {
QMessageBox::warning(0, tr("Cannot go"), tr("No modifications!"));
return;
}
myPeHndl->setDisplayed(false, modS);
}
void PEDockedWidget::goToEntryPoint()
{
if (!m_PE) return;
offset_t raw = 0;
try {
raw = m_PE->rvaToRaw(m_PE->getEntryPoint());
} catch (CustomException e) {
QMessageBox::warning(0, tr("Warning!"), tr("Wrong RVA supplied!")+ QString::fromStdString(e.what()));
return;
}
myPeHndl->setDisplayed(false, raw);
}
void PEDockedWidget::goToOffset()
{
goToAddress(true);
}
void PEDockedWidget::goToRVA()
{
goToAddress(false);
}
void PEDockedWidget::goToAddress(bool isRaw)
{
if (!m_PE) return;
long long number = myPeHndl->displayedOffset;
if (!isRaw) {
try {
number = m_PE->rawToRva(number);
} catch (CustomException e) {
number = 0;
}
}
AddressInputDialog dialog(m_PE, isRaw, this->addrColors, this);
dialog.setDefaultValue(number);
int dialogRet = dialog.exec();
bool isOk = false;
number = dialog.getNumValue(NULL);
if (dialogRet != QDialog::Accepted) {
return;
}
//----
Executable::addr_type aT = dialog.getAddrType();
try {
offset_t raw = m_PE->toRaw(number, aT, true);
if (raw == INVALID_ADDR) {
QMessageBox::warning(0, tr("Warning!"), tr("Wrong address supplied!"));
return;
}
myPeHndl->setDisplayed(false, raw);
} catch (CustomException e) {
QMessageBox::warning(0, tr("Warning!"), tr("Wrong address supplied: ")+ QString::fromStdString(e.what()));
}
}
//--------------------------------------------------------------------------------------
SectionMenu::SectionMenu(MainSettings &settings, QWidget *parent)
: QMenu(parent),
mainSettings(settings), peHndl(NULL), selectedSection(NULL)
{
createActions();
}
void SectionMenu::createActions()
{
this->dumpSelSecAction = new QAction(tr("&Save the content as..."), this);
connect(this->dumpSelSecAction, SIGNAL(triggered()), this, SLOT(dumpSelectedSection()) );
QIcon clearContentIco(":/icons/eraser.ico");
this->clearSelSecAction = new QAction(clearContentIco, tr("&Clear the content"), this);
connect(this->clearSelSecAction, SIGNAL(triggered()), this, SLOT(clearSelectedSection()) );
this->loadSelSecAction = new QAction(tr("Substitute the content"), this);
connect(this->loadSelSecAction, SIGNAL(triggered()), this, SLOT(loadSelectedSection()) );
QIcon disasmIco(":/icons/disasm.ico");
this->dumpDisasmAction = new QAction(disasmIco, tr("&Export section disassembly as..."), this);
connect(dumpDisasmAction, SIGNAL(triggered()), this, SLOT(exportSectionDisasm()) );
addAction(this->dumpSelSecAction);
addAction(this->loadSelSecAction);
addAction(this->clearSelSecAction);
addSeparator();
addAction(this->dumpDisasmAction);
}
void SectionMenu::sectionSelected(PeHandler *pe, SectionHdrWrapper *sec)
{
this->peHndl = pe;
this->selectedSection = sec;
bool isEnabled = sec != NULL ? true : false;
if (!pe) isEnabled = false;
this->setEnabled(isEnabled);
this->setTitle(tr("No section selected"));
if (!isEnabled) return;
this->setTitle(tr("Section: [") + sec->mappedName + "]");
}
void SectionMenu::dumpSelectedSection()
{
if (!peHndl) return;
PEFile *pe = peHndl->getPe();
if (!pe) return;
if (!selectedSection) return;
QString outDir = mainSettings.dirDump;
if (outDir == "") outDir = peHndl->getDirPath();
QString defaultPath = outDir + QDir::separator() + peHndl->getShortName() + "[" + selectedSection->mappedName + "]";
QString path = QFileDialog::getSaveFileName(this, tr("Save as..."), defaultPath);
if (path.size() == 0) return;
if (pe->dumpSection(selectedSection, path)) {
QMessageBox::information(this, tr("Done!"), tr("Dumped section: ")+ selectedSection->mappedName +"\n"+ tr("into: ") + path);
return;
}
QMessageBox::warning(this, tr("Error"), tr("Dumping section failed!"));
}
void SectionMenu::exportSectionDisasm()
{
if (!peHndl || !selectedSection) return;
QString outDir = mainSettings.dirDump;
if (outDir == "") outDir = peHndl->getDirPath();
QString defaultPath = outDir + QDir::separator() + peHndl->getShortName() + "[" + selectedSection->mappedName + "].txt";
QString path = QFileDialog::getSaveFileName(this, tr("Save disasembly as..."), defaultPath);
if (path.size() == 0) return;
const offset_t startOff = selectedSection->getContentOffset(Executable::RAW, true);
const size_t previewSize = selectedSection->getContentSize(Executable::RAW, true);
if (!peHndl->exportDisasm(path, startOff, previewSize)) {
QMessageBox::warning(this, tr("Error"), tr("Dumping section failed!"));
return;
}
QMessageBox::information(this, tr("Done!"), tr("Dumped section disasembly: ")+ selectedSection->mappedName +"\n"+ tr("into: ") + path);
return;
}
void SectionMenu::clearSelectedSection()
{
if (!peHndl) return;
PEFile *pe = peHndl->getPe();
if (!pe) return;
if (!selectedSection) return;
int answer = QMessageBox::warning(0, tr("Clearing section"), tr("Do you really want to clear content of ") + selectedSection->mappedName + "?",
QMessageBox::Yes | QMessageBox::No);
if (answer != QMessageBox::Yes) return;
const offset_t secOffset = selectedSection->getContentOffset(Executable::RAW, true);
const bufsize_t secSize = selectedSection->getContentSize(Executable::RAW, true);
peHndl->backupModification(secOffset, secSize);
bool isOk = pe->clearContent(selectedSection);
if (isOk) {
peHndl->setDisplayed(false, secOffset, secSize);
peHndl->setBlockModified(secOffset, secSize);
}
if (!isOk) {
QMessageBox::warning(this, tr("Failed"), tr("Cannot clear the section content"));
}
}
void SectionMenu::loadSelectedSection()
{
if (!peHndl) return;
PEFile *pe = peHndl->getPe();
if (!pe) return;
if (!selectedSection) return;
QString path = QFileDialog::getOpenFileName(this, tr("Open"), peHndl->getFullName(), "*");
if (path.length() == 0) return;
QFile fIn(path);
if (fIn.open(QFile::ReadOnly) == false) {
QMessageBox::warning(this, tr("Failed"), tr("Cannot open file"));
return;
}
size_t loaded = peHndl->loadSectionContent(selectedSection, fIn);
fIn.close();
QMessageBox::information(this, tr("Loaded"), tr("Loaded from file: 0x") + QString::number(loaded, 16) + " bytes");
}