diff options
| author | kat <kat@vector35.com> | 2024-10-23 21:56:29 -0400 |
|---|---|---|
| committer | kat <kat@vector35.com> | 2024-10-23 21:58:47 -0400 |
| commit | 3006c68e108a18f9b8782bc937dc9ec7e2cb3b54 (patch) | |
| tree | f589bb38909349142c09b6215244205aef0a57ac /view/sharedcache/ui/dsctriage.cpp | |
| parent | 80fcccec8f686e0e5c89626b60af121a97baf856 (diff) | |
Initial commit of the alpha dyld_shared_cache view API Plugin.
This is an early release of our DSC processing plugin. We're still hard at work improving this feature. You should be able to just drop in a dyld_shared_cache and use the 'Shared Cache Triage' view to load and analyze images.
Diffstat (limited to 'view/sharedcache/ui/dsctriage.cpp')
| -rw-r--r-- | view/sharedcache/ui/dsctriage.cpp | 1012 |
1 files changed, 1012 insertions, 0 deletions
diff --git a/view/sharedcache/ui/dsctriage.cpp b/view/sharedcache/ui/dsctriage.cpp new file mode 100644 index 00000000..0dca96bf --- /dev/null +++ b/view/sharedcache/ui/dsctriage.cpp @@ -0,0 +1,1012 @@ +// +// Created by kat on 8/15/24. +// + +#include "dsctriage.h" +#include "ui/fontsettings.h" +#include <QPainter> +#include <QTextBrowser> +#include "tabwidget.h" +#include "globalarea.h" +#include "progresstask.h" + +#include <cmath> +#include <QMessageBox> + + +#define QSETTINGS_KEY_SELECTED_TAB "DSCTriage-SelectedTab" +#define QSETTINGS_KEY_TAB_LAYOUT "DSCTriage-TabLayout" +#define QSETTINGS_KEY_IMAGELOAD_TAB_LAYOUT "DSCTriage-ImageLoadTabLayout" +#define QSETTINGS_KEY_ALPHA_POPUP_SEEN "DSCTriage-AlphaPopupSeen" + + +DSCCacheBlocksView::DSCCacheBlocksView(QWidget* parent, BinaryViewRef data, SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache) + : QWidget(parent), m_data(data), m_cache(cache) +{ + setMouseTracking(true); + m_backingCacheCount = SharedCacheAPI::SharedCache::FastGetBackingCacheCount(data); + m_blockLuminance.resize(m_backingCacheCount, 128); + m_blockSizeRatios.resize(m_backingCacheCount, 1); + m_currentProgress = m_cache->GetLoadProgress(data); + m_targetBlockSizeForAnimation.resize(m_backingCacheCount, 0); + + m_blockWaveAnimation = Animation::create(this) + ->withDuration(1200) + ->withEasingCurve(QEasingCurve::Linear) + ->thenOnValueChanged([this](double v) + { + for (size_t i = 0; i < m_backingCacheCount; i++) + { + // Create a wave effect. + // We use sine to create the initial wave effect, and then cube it to make it more pronounced. + m_blockLuminance[i] = 128 + 95 * (pow((sin(v * 2 * M_PI + i * M_PI / m_backingCacheCount) + 1) / 2, 3)); + } + update(); + }) + ->thenOnEnd([this](QAbstractAnimation::Direction) + { + m_currentProgress = m_cache->GetLoadProgress(m_data); + if (m_currentProgress == BNDSCViewLoadProgress::LoadProgressFinished) + { + m_backingCaches = m_cache->GetBackingCaches(); + m_blockExpandAnimation->start(); + } + else + { + m_blockWaveAnimation->start(); + } + }); + m_blockExpandAnimation = Animation::create(this) + ->withDuration(600) + ->withEasingCurve(QEasingCurve::InOutCirc) + ->thenOnStart([this](QAbstractAnimation::Direction) + { + uint64_t totalSize = 0; + uint64_t sumCountForAvg = 0; + for (size_t i = 0; i < m_backingCacheCount; i++) + { + const auto& backingCache = m_backingCaches[i]; + double sizeSum = 0.0; + + for (const auto& mapping : backingCache.mappings) + { + sizeSum += mapping.size; + } + m_targetBlockSizeForAnimation[i] = sizeSum; + totalSize += sizeSum; + sumCountForAvg++; + } + + uint64_t avgSize = totalSize / sumCountForAvg; + + for (size_t i = 0; i < m_backingCacheCount; i++) + { + m_blockSizeRatios[i] = avgSize; + } + + m_averageBlockSizeForAnimationInterp = avgSize; + }) + ->thenOnValueChanged([this](double v) + { + for (size_t i = 0; i < m_backingCacheCount; i++) + { + m_blockSizeRatios[i] = m_averageBlockSizeForAnimationInterp + (v/2) * (m_targetBlockSizeForAnimation[i] - ((1.0 - (v/2)) * m_averageBlockSizeForAnimationInterp)); + + // Adjust luminance based on animation progress + m_blockLuminance[i] = 128 + (63 * v); + } + update(); + }) + ->thenOnEnd([this](QAbstractAnimation::Direction) + { + std::fill(m_blockLuminance.begin(), m_blockLuminance.end(), 191); + update(); + // wait 300, somehow + emit loadDone(); + m_selectedBlock = 0; + m_blockAutoselectAnimation->start(); + }); + + m_blockAutoselectAnimation = Animation::create(this) + ->withDuration(100) + ->withEasingCurve(QEasingCurve::InOutCirc) + ->thenOnValueChanged([this](double v){ + m_blockLuminance[0] = 191 + (64 * v); + update(); + }) + ->thenOnEnd([this](QAbstractAnimation::Direction) + { + emit selectionChanged(m_backingCaches[0], true); + }); + + m_blockWaveAnimation->setDirection(QAbstractAnimation::Backward); + m_blockWaveAnimation->start(); + +} + +DSCCacheBlocksView::~DSCCacheBlocksView() +{ + +} + +void DSCCacheBlocksView::mousePressEvent(QMouseEvent* event) +{ + if (m_currentProgress != BNDSCViewLoadProgress::LoadProgressFinished + || m_selectedBlock == -1) + { + return; + } + int blockIndex = getBlockIndexAtPosition(event->pos()); + blockSelected(blockIndex); + QWidget::mousePressEvent(event); +} + + +void DSCCacheBlocksView::mouseReleaseEvent(QMouseEvent* event) +{ + QWidget::mouseReleaseEvent(event); +} + + +void DSCCacheBlocksView::mouseDoubleClickEvent(QMouseEvent* event) +{ + QWidget::mouseDoubleClickEvent(event); +} + + +void DSCCacheBlocksView::mouseMoveEvent(QMouseEvent* event) +{ + if (m_selectedBlock == -1) + { + return; + } + uint64_t hoveredIndex = getBlockIndexAtPosition(event->pos()); + std::fill(m_blockLuminance.begin(), m_blockLuminance.end(), 191); + if (hoveredIndex != -1) + { + m_blockLuminance[hoveredIndex] = 255 - 32; + } + m_blockLuminance[m_selectedBlock] = 255; + update(); +} + + +void DSCCacheBlocksView::keyPressEvent(QKeyEvent* event) +{ + QWidget::keyPressEvent(event); +} + + +void DSCCacheBlocksView::keyReleaseEvent(QKeyEvent* event) +{ + QWidget::keyReleaseEvent(event); + if (m_selectedBlock == -1) + { + return; + } + + // left/right arrows, inc/dec m_selectedBlock + if (event->key() == Qt::Key_Left) + { + if (m_selectedBlock > 0) + { + blockSelected(m_selectedBlock - 1); + } + } + else if (event->key() == Qt::Key_Right) + { + if (m_selectedBlock < m_backingCacheCount - 1) + { + blockSelected(m_selectedBlock + 1); + } + } +} + + +void DSCCacheBlocksView::focusInEvent(QFocusEvent* event) +{ + QWidget::focusInEvent(event); +} + + +void DSCCacheBlocksView::focusOutEvent(QFocusEvent* event) +{ + QWidget::focusOutEvent(event); +} + + +void DSCCacheBlocksView::enterEvent(QEnterEvent* event) +{ + QWidget::enterEvent(event); +} + + +void DSCCacheBlocksView::leaveEvent(QEvent* event) +{ + QWidget::leaveEvent(event); +} + +void DSCCacheBlocksView::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + + // Initial X position and total width of the widget + int totalWidth = this->width(); + int totalHeight = 30; // Height of the rectangles + int totalSpacing = (m_blockSizeRatios.size() - 1) * 5; + int availableWidth = totalWidth - (50 * 2) - totalSpacing; // availableWidth minus the initial padding + + // Calculate the total ratio of block sizes + uint64_t totalRatio = 0; + for (const auto& ratio : m_blockSizeRatios) { + totalRatio += ratio; + } + + std::vector<int> originalWidths; + originalWidths.resize(m_blockSizeRatios.size(), (availableWidth / m_blockSizeRatios.size())); + + + // Calculate center points for each block + std::vector<int> centers; + centers.reserve(m_blockSizeRatios.size()); + int currentX = 50; + for (size_t i = 0; i < originalWidths.size(); ++i) { + centers.push_back(currentX + (originalWidths[i] / 2)); // Store the center point + currentX += originalWidths[i] + 5; // Update currentX for the next block + } + + // Now draw the blocks, adjusting the position to keep the center point constant + currentX = 50; + uint64_t lastBlockEnd = currentX - 5; + for (size_t i = 0; i < m_blockSizeRatios.size(); ++i) { + // Recalculate the width during animation + uint64_t adjustedAvailableWidth = availableWidth * m_blockSizeRatios[i]; + int blockWidth = std::max(10, static_cast<int>(adjustedAvailableWidth / totalRatio)); + + // Calculate the new X position to maintain the center + int newX = centers[i] - (blockWidth / 2); + if (newX > lastBlockEnd + 5) + { + int diff = newX - (lastBlockEnd + 5); + newX -= diff; + blockWidth += diff; + } + if (newX < lastBlockEnd + 5) + { + int diff = (lastBlockEnd + 5) - newX; + newX += diff; + blockWidth -= diff; + } + lastBlockEnd = newX + blockWidth; + + QRect blockRect(newX, (height() - totalHeight) / 2, blockWidth, totalHeight); + QColor blockColor(m_blockLuminance[i], m_blockLuminance[i], m_blockLuminance[i]); + painter.setBrush(blockColor); + painter.setPen(blockColor); + painter.drawRect(blockRect); + + currentX += blockWidth + 5; // Move to the next block's position + } +} + + +int DSCCacheBlocksView::getBlockIndexAtPosition(const QPoint& clickPosition) +{ + // Initial X position and total width of the widget + int totalWidth = this->width(); + int totalHeight = 50; // Height of the rectangles + int totalSpacing = (m_blockSizeRatios.size() - 1) * 5; + int availableWidth = totalWidth - (50 * 2) - totalSpacing; // availableWidth minus the initial padding + + // Calculate the total ratio of block sizes + uint64_t totalRatio = 0; + for (const auto& ratio : m_blockSizeRatios) + { + totalRatio += ratio; + } + + // Calculate center points for each block + std::vector<int> originalWidths; + originalWidths.resize(m_blockSizeRatios.size(), (availableWidth / m_blockSizeRatios.size())); + + std::vector<int> centers; + centers.reserve(m_blockSizeRatios.size()); + int currentX = 50; + for (size_t i = 0; i < originalWidths.size(); ++i) + { + centers.push_back(currentX + (originalWidths[i] / 2)); // Store the center point + currentX += originalWidths[i] + 5; // Update currentX for the next block + } + + // Now find the block that contains the click + currentX = 50; + uint64_t lastBlockEnd = currentX - 5; + for (size_t i = 0; i < m_blockSizeRatios.size(); ++i) + { + // Recalculate the width during animation + uint64_t adjustedAvailableWidth = availableWidth * m_blockSizeRatios[i]; + int blockWidth = std::max(10, static_cast<int>(adjustedAvailableWidth / totalRatio)); + + // Calculate the new X position to maintain the center + int newX = centers[i] - (blockWidth / 2); + if (newX > lastBlockEnd + 5) + { + int diff = newX - (lastBlockEnd + 5); + newX -= diff; + blockWidth += diff; + } + if (newX < lastBlockEnd + 5) + { + int diff = (lastBlockEnd + 5) - newX; + newX += diff; + blockWidth -= diff; + } + lastBlockEnd = newX + blockWidth; + + // Check if the clickPosition is inside the current block's rectangle + QRect blockRect(newX, (height() - totalHeight) / 2, blockWidth, totalHeight); + if (blockRect.contains(clickPosition)) + { + return static_cast<int>(i); // Return the index of the clicked block + } + + currentX += blockWidth + 5; // Move to the next block's position + } + + return -1; // Return -1 if no block was clicked +} + + +void DSCCacheBlocksView::blockSelected(int index) +{ + std::fill(m_blockLuminance.begin(), m_blockLuminance.end(), 191); + m_selectedBlock = index; + if (index != -1) + m_blockLuminance[index] = 255; + update(); + if (index != -1) + emit selectionChanged(m_backingCaches[index], false); +} + + +void DSCCacheBlocksView::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); +} + + +QSize DSCCacheBlocksView::sizeHint() const +{ + return QWidget::sizeHint(); +} + + +QSize DSCCacheBlocksView::minimumSizeHint() const +{ + return QWidget::minimumSizeHint(); +} + + +SymbolTableModel::SymbolTableModel(SymbolTableView* parent) + : QAbstractTableModel(parent), m_parent(parent) { +} + +int SymbolTableModel::rowCount(const QModelIndex& parent) const { + Q_UNUSED(parent); + return static_cast<int>(m_symbols.size()); +} + +int SymbolTableModel::columnCount(const QModelIndex& parent) const { + Q_UNUSED(parent); + // We have 3 columns: Address, Name, and Image + return 3; +} + +QVariant SymbolTableModel::data(const QModelIndex& index, int role) const { + if (!index.isValid() || role != Qt::DisplayRole) { + return QVariant(); + } + + const SharedCacheAPI::DSCSymbol& symbol = m_symbols.at(index.row()); + + switch (index.column()) { + case 0: // Address column + return QString("0x%1").arg(symbol.address, 0, 16); // Display address as hexadecimal + case 1: // Name column + return QString::fromStdString(symbol.name); + case 2: // Image column + return QString::fromStdString(symbol.image); + default: + return QVariant(); + } +} + +QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) { + return QVariant(); + } + + switch (section) { + case 0: + return QString("Address"); + case 1: + return QString("Name"); + case 2: + return QString("Image"); + default: + return QVariant(); + } +} + +void SymbolTableModel::updateSymbols() { + m_symbols = m_parent->m_symbols; + setFilter(m_filter); +} + +const SharedCacheAPI::DSCSymbol& SymbolTableModel::symbolAt(int row) const { + return m_symbols.at(row); +} + + +void SymbolTableModel::setFilter(std::string text) +{ + beginResetModel(); + + m_filter = text; + m_symbols.clear(); + + if (m_filter.empty()) + { + m_symbols = m_parent->m_symbols; + } + else + { + m_symbols.reserve(m_parent->m_symbols.size()); + for (const auto& symbol : m_parent->m_symbols) + { + if (symbol.name.find(m_filter) != std::string::npos) + { + m_symbols.push_back(symbol); + } + } + m_symbols.shrink_to_fit(); + } + + endResetModel(); +} + + +SymbolTableView::SymbolTableView(QWidget* parent, SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache) + : m_model(new SymbolTableModel(this)){ + + // Set up the filter model + setModel(m_model); + + // Configure view settings + horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + + BackgroundThread::create(this)->thenBackground([this, cache=cache](){ + // LogInfo("Symbol Search: Loading symbols..."); + m_symbols = cache->LoadAllSymbolsAndWait(); + // LogInfo("Symbol Search: Loaded 0x%zx symbols", m_symbols.size()); + })->thenMainThread([this](){ + m_model->updateSymbols(); + })->start(); +} + +SymbolTableView::~SymbolTableView() { + delete m_model; +} + +void SymbolTableView::setFilter(const std::string& filter) { + m_model->setFilter(filter); +} + + +DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), View(), m_data(data), m_cache(new SharedCacheAPI::SharedCache(data)) +{ + setBinaryDataNavigable(false); + setupView(this); + + m_triageCollection = new DockableTabCollection(); + m_triageTabs = new SplitTabWidget(m_triageCollection); + + auto triageTabStyle = new GlobalAreaTabStyle(); + m_triageTabs->setTabStyle(triageTabStyle); + + auto cacheInfoWidget = new QWidget; + auto cacheInfoLayout = new QVBoxLayout(cacheInfoWidget); + + QSplitter* containerWidget = new QSplitter; + containerWidget->setOrientation(Qt::Vertical); + + DSCCacheBlocksView* cacheBlocksView = new DSCCacheBlocksView(containerWidget, data, m_cache); + cacheBlocksView->setMinimumHeight(60); + + auto cacheInfo = new CollapsibleSection(this); + cacheInfo->setTitle(QString::fromStdString(data->GetFile()->GetOriginalFilename().substr(data->GetFile()->GetOriginalFilename().find_last_of('/') + 1))); + + auto cacheInfoSubwidget = new QWidget; + + auto mappingTable = new QTableView(cacheInfoSubwidget); + auto mappingModel = new QStandardItemModel(0, 3, mappingTable); + mappingModel->setHorizontalHeaderLabels({"VM Address", "File Address", "Size"}); + + mappingTable->setModel(mappingModel); + + mappingTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + mappingTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + mappingTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + + auto sectionTable = new QTableView(cacheInfoSubwidget); + auto sectionModel = new QStandardItemModel(0, 3, sectionTable); + sectionModel->setHorizontalHeaderLabels({"Name", "VM Address", "Size"}); + + sectionTable->setModel(sectionModel); + + sectionTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + sectionTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + sectionTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + + auto mappingLabel = new QLabel("Mappings"); + auto sectionLabel = new QLabel("Sections"); + + auto mappingLayout = new QVBoxLayout; + mappingLayout->addWidget(mappingLabel); + mappingLayout->addWidget(mappingTable); + + auto sectionLayout = new QVBoxLayout; + sectionLayout->addWidget(sectionLabel); + sectionLayout->addWidget(sectionTable); + + cacheInfoLayout->addLayout(mappingLayout); + cacheInfoLayout->addLayout(sectionLayout); + + cacheInfo->setContentWidget(cacheInfoSubwidget); + + cacheInfo->setMinimumHeight(170); + + connect(cacheBlocksView, &DSCCacheBlocksView::selectionChanged, [this, sectionModel, cacheInfo, cacheInfoWidget, mappingModel](const SharedCacheAPI::BackingCache& index, bool _auto) + { + if (!_auto) + m_triageTabs->selectWidget(cacheInfoWidget); + mappingModel->removeRows(0, mappingModel->rowCount()); + sectionModel->removeRows(0, sectionModel->rowCount()); + auto basename = index.path.substr(index.path.find_last_of('/') + 1); + cacheInfo->setTitle(QString::fromStdString(basename)); + size_t sizeInBits = 0; + for (const auto& mapping : index.mappings) + { + sizeInBits += mapping.size; + mappingModel->appendRow({ + new QStandardItem(QString("0x%1").arg(mapping.vmAddress, 0, 16)), + new QStandardItem(QString("0x%1").arg(mapping.fileOffset, 0, 16)), + new QStandardItem(QString("0x%1").arg(mapping.size, 0, 16))}); + } + + for (const auto& header : m_headers) + { + uint64_t i = 0; + for (const auto& section : header.sections) + { + for (const auto& mapping : index.mappings) + { + if (section.addr >= mapping.vmAddress && section.addr < mapping.vmAddress + mapping.size) + { + sectionModel->appendRow({ + new QStandardItem(QString::fromStdString(header.sectionNames[i])), + new QStandardItem(QString("0x%1").arg(section.addr, 0, 16)), + new QStandardItem(QString("0x%1").arg(section.size, 0, 16))}); + break; + } + } + i++; + } + continue; + } + + std::string sizeStr; + if (sizeInBits < 1024) + { + sizeStr = std::to_string(sizeInBits) + " B"; + } + else if (sizeInBits < 1024 * 1024) + { + sizeStr = std::to_string(sizeInBits / 1024) + " KB"; + } + else if (sizeInBits < 1024 * 1024 * 1024) + { + sizeStr = std::to_string(sizeInBits / (1024 * 1024)) + " MB"; + } + else + { + sizeStr = std::to_string(sizeInBits / (1024 * 1024 * 1024)) + " GB"; + } + + cacheInfo->setSubtitleRight(QString::fromStdString(sizeStr)); + }); + + containerWidget->addWidget(cacheInfo); + + QWidget* defaultWidget; + + // check for alpha popup qsetting + QSettings settings; + if (!(settings.contains(QSETTINGS_KEY_ALPHA_POPUP_SEEN) && settings.value(QSETTINGS_KEY_ALPHA_POPUP_SEEN).toBool())) + { + + QTextBrowser *tb = new QTextBrowser(this); + { + tb->setOpenExternalLinks(true); + auto alphaHtml = + R"( +<br> +<h1>Shared Cache Alpha</h1> + +<p> This is the alpha release of the sharedcache viewer! We are hard at work improving this and adding features, but we wanted +to make it available for users to play with as soon as possible. </p> + +<h2> Supported Platforms </h2> +<ul> + <li> iOS 11-17 (full) </li> + <li> iOS 18 (partial, Objective-C optimization parsing is not implemented yet.) </li> + <li> macOS x86/arm64e (partial) </li> +</ul> + +<p> iOS parsing should work well for now. macOS parsing should be usable, but is still a work in progress. </p> + +<h2> Getting the latest version of the plugin </h2> + +<p> We frequently release "dev" builds which will contain the latest version of the SharedCache plugin (and many other things). + +You can find instructions on how to install these builds <a href="https://docs.binary.ninja/guide/index.html#development-branch">here</a>. </p> + +<h3> Reading / building the source </h3> +<p>You can read the source and find instructions for building it <a href="https://github.com/Vector35/binaryninja-api/tree/dev/view/sharedcache">here</a>. + +Contributions are always welcome! </p> +)"; + tb->setHtml(alphaHtml); + + m_triageTabs->addTab(tb, "Shared Cache Alpha"); + + } + settings.setValue(QSETTINGS_KEY_ALPHA_POPUP_SEEN, true); + defaultWidget = tb; + } + + m_bottomRegionCollection = new DockableTabCollection(); + m_bottomRegionTabs = new SplitTabWidget(m_bottomRegionCollection); + m_bottomRegionTabs->setTabStyle(new GlobalAreaTabStyle()); + + auto loadImageTable = new FilterableTableView; + { + auto loadImageModel = new QStandardItemModel(0, 2, loadImageTable); + { + connect( + cacheBlocksView, &DSCCacheBlocksView::loadDone, [this, loadImageModel, cacheInfo]() + { + for (const auto& img : m_cache->GetImages()) + { + if (auto header = m_cache->GetMachOHeaderForAddress(img.headerAddress); header) + { + m_headers.push_back(*header); + } + loadImageModel->appendRow({ + new QStandardItem(QString::fromStdString(img.name)), + new QStandardItem(QString("0x%1").arg(img.headerAddress, 0, 16))}); + } + }); + loadImageModel->setHorizontalHeaderLabels({"Name", "VM Address"}); + } // loadImageModel + + auto loadImageButton = new CustomStyleFlatPushButton(); + { + connect(loadImageButton, &QPushButton::clicked, + [this, loadImageTable, cacheInfo, mappingModel, sectionModel](bool) { + auto selected = loadImageTable->selectionModel()->selectedRows(); + if (selected.size() == 0) + { + return; + } + + auto name = selected[0].data().toString().toStdString(); + WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); }); + }); + loadImageButton->setText("Load"); + + loadImageButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + loadImageButton->setMinimumWidth(100); + loadImageButton->setMinimumHeight(30); + + } // loadImageButton + loadImageTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + + auto loadImageFilterEdit = new FilterEdit(loadImageTable); + { + connect(loadImageFilterEdit, &FilterEdit::textChanged, [loadImageTable](const QString& filter) { + loadImageTable->setFilter(filter.toStdString()); + }); + } // loadImageFilterEdit + + connect(loadImageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) + { + auto name = loadImageModel->item(index.row(), 0)->text().toStdString(); + WorkerPriorityEnqueue([this, name]() + { + m_cache->LoadImageWithInstallName(name); + }); + }); + connect(loadImageTable, &FilterableTableView::doubleClicked, this, [=](const QModelIndex& index) + { + auto name = loadImageModel->item(index.row(), 0)->text().toStdString(); + WorkerPriorityEnqueue([this, name]() + { + m_cache->LoadImageWithInstallName(name); + }); + }); + + auto loadImageLayout = new QVBoxLayout; + loadImageLayout->addWidget(loadImageFilterEdit); + loadImageLayout->addWidget(loadImageTable); + loadImageLayout->addWidget(loadImageButton); + + auto loadImageWidget = new QWidget; + loadImageWidget->setLayout(loadImageLayout); + + m_bottomRegionTabs->addTab(loadImageWidget, "Load an Image"); + + loadImageTable->setModel(loadImageModel); + + loadImageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + loadImageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + + loadImageTable->setSelectionBehavior(QAbstractItemView::SelectRows); + loadImageTable->setSelectionMode(QAbstractItemView::SingleSelection); + + m_triageTabs->addTab(loadImageWidget, "Images"); + if (!defaultWidget) + defaultWidget = loadImageWidget; + m_triageTabs->setCanCloseTab(loadImageWidget, false); + } // loadImageTable + + auto symbolSearch = new SymbolTableView(this, m_cache); + { + auto symbolFilterEdit = new FilterEdit(symbolSearch); + { + connect(symbolFilterEdit, &FilterEdit::textChanged, [symbolSearch](const QString& filter) { + symbolSearch->setFilter(filter.toStdString()); + }); + } + + auto symbolLayout = new QVBoxLayout; + symbolLayout->addWidget(symbolFilterEdit); + symbolLayout->addWidget(symbolSearch); + + auto symbolWidget = new QWidget; + symbolWidget->setLayout(symbolLayout); + + symbolSearch->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // Address + symbolSearch->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); // Name + symbolSearch->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); // Image + + symbolSearch->setSelectionBehavior(QAbstractItemView::SelectRows); + symbolSearch->setSelectionMode(QAbstractItemView::SingleSelection); + + connect(symbolSearch, &SymbolTableView::activated, this, [=](const QModelIndex& index) + { + auto symbol = symbolSearch->getSymbolAtRow(index.row()); + auto dialog = new QMessageBox(this); + dialog->setText("Load " + QString::fromStdString(symbol.image) + "?"); + dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + + connect(dialog, &QMessageBox::buttonClicked, this, [=](QAbstractButton* button) + { + if (button == dialog->button(QMessageBox::Yes)) + { + WorkerPriorityEnqueue([this, symbol]() + { + m_cache->LoadImageWithInstallName(symbol.image); + }); + } + }); + dialog->exec(); + }); + + m_triageTabs->addTab(symbolWidget, "Symbol Search"); + m_triageTabs->setCanCloseTab(symbolWidget, false); + } // symbolSearch + + auto loadedRegions = new QTreeView; + { + auto loadedRegionsModel = new QStandardItemModel(0, 3, loadedRegions); + loadedRegionsModel->setHorizontalHeaderLabels({"VM Address", "Size", "Pretty Name"}); + + auto loadedRegionsLayout = new QVBoxLayout; + loadedRegionsLayout->addWidget(loadedRegions); + + auto loadedRegionsWidget = new QWidget; + loadedRegionsWidget->setLayout(loadedRegionsLayout); + + loadedRegions->setModel(loadedRegionsModel); + + loadedRegions->header()->setSectionResizeMode(QHeaderView::Stretch); + + loadedRegions->setSelectionBehavior(QAbstractItemView::SelectRows); + loadedRegions->setSelectionMode(QAbstractItemView::SingleSelection); + + connect(loadedRegions, &QTreeView::doubleClicked, this, [=](const QModelIndex& index) + { + auto addr = loadedRegionsModel->item(index.row(), 0)->text().toULongLong(nullptr, 16); + }); + + connect(loadedRegions, &QTreeView::activated, this, [=](const QModelIndex& index) + { + auto addr = loadedRegionsModel->item(index.row(), 0)->text().toULongLong(nullptr, 16); + }); + + // m_triageTabs->addTab(loadedRegionsWidget, "Loaded Regions"); + } // loadedRegions + + containerWidget->addWidget(m_bottomRegionTabs); + + m_triageTabs->addTab(cacheInfoWidget, "Cache Info"); + m_triageTabs->setCanCloseTab(cacheInfoWidget, false); + + m_layout = new QVBoxLayout(this); + m_layout->addWidget(cacheBlocksView); + m_layout->addWidget(m_triageTabs); + setLayout(m_layout); + + m_triageTabs->selectWidget(defaultWidget); +} + + +DSCTriageView::~DSCTriageView() {} + + +QFont DSCTriageView::getFont() +{ + return getMonospaceFont(this); +} + + +BinaryViewRef DSCTriageView::getData() +{ + return m_data; +} + + +bool DSCTriageView::navigate(uint64_t offset) +{ + return true; +} + + +uint64_t DSCTriageView::getCurrentOffset() +{ + return 0; +} + + +CollapsibleSection::CollapsibleSection(QWidget* parent) + : QWidget(parent) +{ + auto layout = new QVBoxLayout(this); + { + layout->setContentsMargins(0, 0, 0, 0); + + auto hLayout = new QHBoxLayout; + { + hLayout->setContentsMargins(0, 0, 0, 0); + + m_titleLabel = new QLabel; + m_titleLabel->setStyleSheet("font-weight: bold; font-size: 16px;"); + hLayout->addWidget(m_titleLabel, 1); + + m_subtitleRightLabel = new QLabel; + m_subtitleRightLabel->setStyleSheet("font-size: 12px;"); + hLayout->addWidget(m_subtitleRightLabel); + + m_collapseButton = new CustomStyleFlatPushButton; + m_collapseButton->setFlat(true); + m_collapseButton->setCheckable(true); + } + + layout->addLayout(hLayout); + } + + m_contentWidgetContainer = new QWidget; + { + layout->addWidget(m_contentWidgetContainer); + new QVBoxLayout(m_contentWidgetContainer); + } + +} + + +void CollapsibleSection::setTitle(const QString& title) +{ + m_titleLabel->setText(title); +} + + +void CollapsibleSection::setSubtitleRight(const QString& subtitle) +{ + m_subtitleRightLabel->setVisible(subtitle != ""); + m_subtitleRightLabel->setText(subtitle); +} + + +void CollapsibleSection::setContentWidget(QWidget* contentWidget) +{ + m_contentWidget = contentWidget; + m_contentWidgetContainer->layout()->addWidget(contentWidget); +} + + +QSize CollapsibleSection::sizeHint() const +{ + return QWidget::sizeHint(); +} + + +void CollapsibleSection::setCollapsed(bool collapsed, bool animated) +{ + if (collapsed == m_collapsed) + { + return; + } + + m_collapsed = collapsed; + + if (m_collapsed) + { + m_contentWidget->hide(); + } + else + { + m_contentWidget->show(); + } + + if (animated) + { + m_onContentAddedAnimation->start(); + } +} + + +DSCTriageViewType::DSCTriageViewType() + : ViewType("DSCTriage", "Shared Cache Triage") +{ + +} + + +int DSCTriageViewType::getPriority(BinaryViewRef data, const QString& filename) +{ + if (data->GetTypeName() == VIEW_NAME) + { + return 100; + } + return 1; +} + + +QWidget* DSCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame) +{ + if (data->GetTypeName() != VIEW_NAME) + { + return nullptr; + } + return new DSCTriageView(viewFrame, data); +} + + +void DSCTriageViewType::Register() +{ + ViewType::registerViewType(new DSCTriageViewType()); +} |
