summaryrefslogtreecommitdiff
path: root/view/kernelcache
diff options
context:
space:
mode:
authorAlexander Taylor <alex@vector35.com>2025-04-09 12:47:00 -0400
committerAlexander Taylor <alex@vector35.com>2025-04-09 17:16:23 -0400
commite3772366504d6d05b4809013cb4f4149ca64c4d4 (patch)
treee3fcb0818bfede1a5f8fff0b1edd59422b517b67 /view/kernelcache
parentf78345462ebc25e0241d838f04104e5c6bfae863 (diff)
Shared cache and kernel cache UI cleanup.
One day, these will share more code, but today is not quite that day.
Diffstat (limited to 'view/kernelcache')
-rw-r--r--view/kernelcache/ui/kctriage.cpp548
-rw-r--r--view/kernelcache/ui/kctriage.h79
2 files changed, 255 insertions, 372 deletions
diff --git a/view/kernelcache/ui/kctriage.cpp b/view/kernelcache/ui/kctriage.cpp
index 3fa70065..7b3cf9f0 100644
--- a/view/kernelcache/ui/kctriage.cpp
+++ b/view/kernelcache/ui/kctriage.cpp
@@ -1,59 +1,65 @@
-//
-// Created by kat on 8/15/24.
-//
-
+#include "globalarea.h"
#include "kctriage.h"
+#include "progresstask.h"
#include "ui/fontsettings.h"
+#include <QMessageBox>
#include <QPainter>
-#include <QTextBrowser>
-#include "tabwidget.h"
-#include "globalarea.h"
-#include "progresstask.h"
-
#include <cmath>
-#include <QMessageBox>
-
-#define QSETTINGS_KEY_SELECTED_TAB "KCTriage-SelectedTab"
-#define QSETTINGS_KEY_TAB_LAYOUT "KCTriage-TabLayout"
-#define QSETTINGS_KEY_IMAGELOAD_TAB_LAYOUT "KCTriage-ImageLoadTabLayout"
-#define QSETTINGS_KEY_ALPHA_POPUP_SEEN "KCTriage-AlphaPopupSeenV2"
+using namespace BinaryNinja;
+using namespace KernelCacheAPI;
SymbolTableModel::SymbolTableModel(SymbolTableView* parent)
: QAbstractTableModel(parent), m_parent(parent) {
+ // TODO: Need to implement updating this font if it is changed by the user
+ m_font = getMonospaceFont(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) {
+ if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::FontRole)) {
return QVariant();
}
- const KernelCacheAPI::KCSymbol& symbol = m_symbols.at(index.row());
+ const KCSymbol& 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);
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ {
+ 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();
+ }
+ }
+ case Qt::FontRole:
+ return m_font;
default:
return QVariant();
}
}
+
QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole || orientation != Qt::Horizontal) {
return QVariant();
@@ -71,12 +77,14 @@ QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation,
}
}
+
void SymbolTableModel::updateSymbols() {
m_symbols = m_parent->m_symbols;
setFilter(m_filter);
}
-const KernelCacheAPI::KCSymbol& SymbolTableModel::symbolAt(int row) const {
+
+const KCSymbol& SymbolTableModel::symbolAt(int row) const {
return m_symbols.at(row);
}
@@ -88,39 +96,38 @@ void SymbolTableModel::setFilter(std::string text)
m_filter = text;
m_symbols.clear();
- if (m_filter.empty())
- {
- m_symbols = m_parent->m_symbols;
- }
- else
+ if (!m_filter.empty())
{
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();
}
+ else
+ {
+ m_symbols = m_parent->m_symbols;
+ }
endResetModel();
}
-SymbolTableView::SymbolTableView(QWidget* parent, Ref<KernelCacheAPI::KernelCache> cache)
- : m_model(new SymbolTableModel(this)) {
+SymbolTableView::SymbolTableView(QWidget* parent, Ref<KernelCache> cache)
+ : QTableView(parent), m_model(new SymbolTableModel(this)) {
// Set up the filter model
setModel(m_model);
// Configure view settings
horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::SingleSelection);
- BackgroundThread::create(this)->thenBackground([this, cache=cache](){
+ setSortingEnabled(true);
+
+ BackgroundThread::create(this)->thenBackground([this, cache](){
// LogInfo("Symbol Search: Loading symbols...");
m_symbols = cache->LoadAllSymbolsAndWait();
// LogInfo("Symbol Search: Loaded 0x%zx symbols", m_symbols.size());
@@ -129,386 +136,267 @@ SymbolTableView::SymbolTableView(QWidget* parent, Ref<KernelCacheAPI::KernelCach
})->start();
}
+
SymbolTableView::~SymbolTableView() {
delete m_model;
}
+
void SymbolTableView::setFilter(const std::string& filter) {
m_model->setFilter(filter);
}
-KCTriageView::KCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), View(), m_data(data), m_cache(new KernelCacheAPI::KernelCache(data))
-{
- setBinaryDataNavigable(true);
- setupView(this);
-
- m_triageCollection = new DockableTabCollection();
- m_triageTabs = new SplitTabWidget(m_triageCollection);
-
- auto triageTabStyle = new GlobalAreaTabStyle();
- m_triageTabs->setTabStyle(triageTabStyle);
-
- QSplitter* containerWidget = new QSplitter;
- containerWidget->setOrientation(Qt::Vertical);
-
- QWidget* defaultWidget = nullptr;
-
- m_bottomRegionCollection = new DockableTabCollection();
- m_bottomRegionTabs = new SplitTabWidget(m_bottomRegionCollection);
- m_bottomRegionTabs->setTabStyle(new GlobalAreaTabStyle());
+KCTriageViewType::KCTriageViewType()
+ : ViewType("KCTriage", "Kernel Cache Triage")
+{}
- auto loadImageTable = new FilterableTableView;
- {
- auto loadImageModel = new QStandardItemModel(0, 2, loadImageTable);
- {
- loadImageModel->setHorizontalHeaderLabels({"Name", "VM Address"});
- BackgroundThread::create(loadImageTable)->thenBackground([this](QVariant var)
- {
- QVariantList rows;
- auto images = m_cache->GetImages();
+int KCTriageViewType::getPriority(BinaryViewRef data, const QString& filename)
+{
+ if (data->GetTypeName() == KC_VIEW_NAME)
+ return 100;
+ return 0;
+}
- auto newHeaders = std::make_shared<std::vector<KernelCacheAPI::KernelCacheMachOHeader>>();
- newHeaders->reserve(images.size());
- for (const auto& img : images)
- {
- if (auto header = m_cache->GetMachOHeaderForImage(img.name); header)
- {
- newHeaders->push_back(*header);
- rows.push_back(QList<QVariant>{
- QString::fromStdString(img.name),
- QString("0x%1").arg(header->textBase, 0, 16)
- });
- }
- }
+QWidget* KCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame)
+{
+ if (data->GetTypeName() != KC_VIEW_NAME)
+ return nullptr;
+ return new KCTriageView(viewFrame, data);
+}
- {
- std::unique_lock<std::mutex> lock(m_headersMutex);
- m_headers.swap(newHeaders);
- }
- return QVariant(rows);
- })->thenMainThread([loadImageModel, loadImageTable](QVariant var){
- QVariantList rows = var.toList();
+void KCTriageViewType::Register()
+{
+ registerViewType(new KCTriageViewType());
+}
- if (loadImageModel->rowCount() > 0)
- {
- loadImageModel->removeRows(0, loadImageModel->rowCount());
- }
- for (const QVariant &rowVariant : rows) {
- QVariantList row = rowVariant.toList();
+KCTriageView::KCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), View(), m_data(data), m_cache(new KernelCache(data))
+{
+ setBinaryDataNavigable(true);
+ setupView(this);
- QList<QStandardItem*> items;
- for (const QVariant &cellValue : row) {
- items.append(new QStandardItem(cellValue.toString()));
- }
+ UIContext::registerNotification(this);
- loadImageModel->appendRow(items);
- loadImageTable->resizeColumnsToContents();
- }
- })->start();
- } // loadImageModel
+ m_triageCollection = new DockableTabCollection();
+ m_triageTabs = new SplitTabWidget(m_triageCollection);
- auto loadImageButton = new CustomStyleFlatPushButton();
- {
- connect(loadImageButton, &QPushButton::clicked,
- [this, loadImageTable](bool) {
- auto selected = loadImageTable->selectionModel()->selectedRows();
- if (selected.size() == 0)
- {
- return;
- }
+ auto triageTabStyle = new GlobalAreaTabStyle();
+ m_triageTabs->setTabStyle(triageTabStyle);
- for (const auto& selection : selected)
- {
- auto name = selection.data().toString().toStdString();
- WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
- }
- });
- loadImageButton->setText("Load");
+ QWidget* defaultWidget = initImageTable();
+ initSymbolTable();
- loadImageButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- loadImageButton->setMinimumWidth(100);
- loadImageButton->setMinimumHeight(30);
+ m_layout = new QVBoxLayout(this);
+ m_layout->addWidget(m_triageTabs);
+ setLayout(m_layout);
- } // loadImageButton
- loadImageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ // XXX: RefreshData
- auto loadImageFilterEdit = new FilterEdit(loadImageTable);
- {
- connect(loadImageFilterEdit, &FilterEdit::textChanged, [loadImageTable](const QString& filter) {
- loadImageTable->setFilter(filter.toStdString());
- });
- } // loadImageFilterEdit
+ m_triageTabs->selectWidget(defaultWidget);
+}
- connect(loadImageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index)
- {
- auto selected = loadImageTable->selectionModel()->selectedRows();
- if (selected.size() == 0)
- {
- return;
- }
- for (const auto& selection : selected)
- {
- auto name = selection.data().toString().toStdString();
- WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
- }
- });
+KCTriageView::~KCTriageView()
+{
+ UIContext::unregisterNotification(this);
+}
- auto loadImageLayout = new QVBoxLayout;
- loadImageLayout->addWidget(loadImageFilterEdit);
- loadImageLayout->addWidget(loadImageTable);
- loadImageLayout->addWidget(loadImageButton);
- auto loadImageWidget = new QWidget;
- loadImageWidget->setLayout(loadImageLayout);
+QWidget* KCTriageView::initImageTable()
+{
+ m_imageTable = new FilterableTableView(this);
- m_bottomRegionTabs->addTab(loadImageWidget, "Load an Image");
+ m_imageModel = new QStandardItemModel(0, 2, m_imageTable);
+ m_imageModel->setHorizontalHeaderLabels({"VM Address", "Name"});
- loadImageTable->setModel(loadImageModel);
+ // Apply custom column styling
+ m_imageTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_imageTable));
- loadImageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
- loadImageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ BackgroundThread::create(m_imageTable)->thenBackground([this](const QVariant var) {
+ QVariantList rows;
- loadImageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ auto images = m_cache->GetImages();
- m_triageTabs->addTab(loadImageWidget, "Images");
- defaultWidget = loadImageWidget;
- m_triageTabs->setCanCloseTab(loadImageWidget, false);
- } // loadImageTable
+ auto newHeaders = std::make_shared<std::vector<KernelCacheMachOHeader>>();
+ newHeaders->reserve(images.size());
- auto symbolSearch = new SymbolTableView(this, m_cache);
- {
- auto symbolFilterEdit = new FilterEdit(symbolSearch);
+ for (const auto& img : images)
{
- 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);
-
- std::function<void(uint64_t)> navigateToAddress = [=](uint64_t addr){
- ExecuteOnMainThread([addr, this](){
- if (BinaryNinja::Settings::Instance()->Get<bool>("ui.view.graph.preferred"))
- m_data->Navigate("Graph:KCView", addr);
- else
- m_data->Navigate("Linear:KCView", addr);
- });
- };
-
- connect(symbolSearch, &SymbolTableView::activated, this, [=](const QModelIndex& index)
+ if (auto header = m_cache->GetMachOHeaderForImage(img.name); header)
{
- auto symbol = symbolSearch->getSymbolAtRow(index.row());
- WorkerPriorityEnqueue([this, symbol, navigateToAddress]()
- {
- if (m_data->IsValidOffset(symbol.address))
- navigateToAddress(symbol.address);
- else
- {
- m_cache->LoadImageWithInstallName(symbol.image);
- navigateToAddress(symbol.address);
- }
- });
- });
-
- m_triageTabs->addTab(symbolWidget, "Symbol Search");
- m_triageTabs->setCanCloseTab(symbolWidget, false);
- } // symbolSearch
-
- { // Doc tabs
-
- QTextBrowser *mainDocBrowser = new QTextBrowser(this);
- {
- mainDocBrowser->setOpenExternalLinks(true);
- auto alphaHtml =
- R"(
-<h1>KernelCache Parser</h1>
-
-<p> This parser has support for MH_FILESET (>= macOS 11, >= iOS 14) kernels. </p>
-
-<h2> Getting the latest version of the plugin </h2>
-<p> We frequently release "dev" builds which will contain the latest version of the KernelCache plugin (and many other things). </p>
-<p> 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> Like most of our platforms, architectures, debug information parsing, and the entire API and documentation, this plugin is open source. </p>
-<p> You can read the source and find instructions for building it <a href="https://github.com/Vector35/binaryninja-api/tree/dev/view/kernelcache">here</a>. </p>
-<p> Contributions are always welcome! </p>
-)";
- mainDocBrowser->setHtml(alphaHtml);
-
- m_triageTabs->addTab(mainDocBrowser, "KernelCache Parser");
- m_triageTabs->setCanCloseTab(mainDocBrowser, false);
-
+ newHeaders->push_back(*header);
+ rows.push_back(QList<QVariant>{
+ QString("0x%1").arg(header->textBase, 0, 16),
+ QString::fromStdString(img.name)
+ });
+ }
}
- }
- containerWidget->addWidget(m_bottomRegionTabs);
+ std::unique_lock<std::mutex> lock(m_headersMutex);
+ m_headers.swap(newHeaders);
- m_layout = new QVBoxLayout(this);
- m_layout->addWidget(m_triageTabs);
- setLayout(m_layout);
+ return QVariant(rows);
+ })->thenMainThread([this](const QVariant var) {
+ QVariantList rows = var.toList();
- m_triageTabs->selectWidget(defaultWidget);
-}
+ if (m_imageModel->rowCount() > 0)
+ m_imageModel->removeRows(0, m_imageModel->rowCount());
+ for (const QVariant &rowVariant : rows) {
+ QVariantList row = rowVariant.toList();
-QFont KCTriageView::getFont()
-{
- return getMonospaceFont(this);
-}
+ QList<QStandardItem*> items;
+ for (const QVariant &cellValue : row)
+ items.append(new QStandardItem(cellValue.toString()));
+ m_imageModel->appendRow(items);
+ m_imageTable->resizeColumnsToContents();
+ }
+ })->start();
-BinaryViewRef KCTriageView::getData()
-{
- return m_data;
-}
+ auto loadImageButton = new QPushButton();
+ connect(loadImageButton, &QPushButton::clicked, [this](bool) {
+ // Collect only visible selected rows
+ QModelIndexList visibleSelectedRows;
+ for (const auto& index : m_imageTable->selectionModel()->selectedRows()) {
+ if (!m_imageTable->isRowHidden(index.row())) {
+ visibleSelectedRows.append(index);
+ }
+ }
+ if (visibleSelectedRows.empty())
+ return;
-bool KCTriageView::navigate(uint64_t offset)
-{
- return false;
-}
+ for (const auto& selection : visibleSelectedRows) {
+ auto name = selection.data().toString().toStdString();
+ WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
+ }
+ });
+ loadImageButton->setText("Load Selected");
+ auto loadImageFilterEdit = new FilterEdit(m_imageTable);
+ connect(loadImageFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
+ m_imageTable->setFilter(filter.toStdString());
+ });
-uint64_t KCTriageView::getCurrentOffset()
-{
- return 0;
-}
+ connect(m_imageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) {
+ auto selected = m_imageModel->item(index.row(), 0);
+ auto name = selected->text().toStdString();
+ WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
+ });
+ auto loadImageLayout = new QVBoxLayout;
+ loadImageLayout->addWidget(loadImageFilterEdit);
+ loadImageLayout->addWidget(m_imageTable);
+ loadImageLayout->addWidget(loadImageButton);
-CollapsibleSection::CollapsibleSection(QWidget* parent)
- : QWidget(parent)
-{
- auto layout = new QVBoxLayout(this);
- {
- layout->setContentsMargins(0, 0, 0, 0);
+ auto buttonLayout = new QHBoxLayout;
+ buttonLayout->addWidget(loadImageButton);
+ buttonLayout->setAlignment(Qt::AlignLeft);
+ loadImageLayout->addLayout(buttonLayout);
- auto hLayout = new QHBoxLayout;
- {
- hLayout->setContentsMargins(0, 0, 0, 0);
+ auto loadImageWidget = new QWidget;
+ loadImageWidget->setLayout(loadImageLayout);
- m_titleLabel = new QLabel;
- m_titleLabel->setStyleSheet("font-weight: bold; font-size: 16px;");
- hLayout->addWidget(m_titleLabel, 1);
+ m_imageTable->setModel(m_imageModel);
- m_subtitleRightLabel = new QLabel;
- m_subtitleRightLabel->setStyleSheet("font-size: 12px;");
- hLayout->addWidget(m_subtitleRightLabel);
+ m_imageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_collapseButton = new CustomStyleFlatPushButton;
- m_collapseButton->setFlat(true);
- m_collapseButton->setCheckable(true);
- }
+ m_imageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ m_imageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
- layout->addLayout(hLayout);
- }
+ m_imageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ m_imageTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
- m_contentWidgetContainer = new QWidget;
- {
- layout->addWidget(m_contentWidgetContainer);
- new QVBoxLayout(m_contentWidgetContainer);
- }
+ m_imageTable->setSortingEnabled(true);
-}
+ m_imageTable->verticalHeader()->setVisible(false);
+ m_triageTabs->addTab(loadImageWidget, "Images");
+ m_triageTabs->setCanCloseTab(loadImageWidget, false);
-void CollapsibleSection::setTitle(const QString& title)
-{
- m_titleLabel->setText(title);
+ return loadImageWidget; // For use as the default widget
}
-void CollapsibleSection::setSubtitleRight(const QString& subtitle)
+void KCTriageView::initSymbolTable()
{
- m_subtitleRightLabel->setVisible(subtitle != "");
- m_subtitleRightLabel->setText(subtitle);
-}
+ m_symbolTable = new SymbolTableView(this, m_cache);
+ auto symbolFilterEdit = new FilterEdit(m_symbolTable);
+ connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
+ m_symbolTable->setFilter(filter.toStdString());
+ });
-void CollapsibleSection::setContentWidget(QWidget* contentWidget)
-{
- m_contentWidget = contentWidget;
- m_contentWidgetContainer->layout()->addWidget(contentWidget);
-}
+ // Apply custom column styling
+ m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
+ auto symbolLayout = new QVBoxLayout;
+ symbolLayout->addWidget(symbolFilterEdit);
+ symbolLayout->addWidget(m_symbolTable);
-QSize CollapsibleSection::sizeHint() const
-{
- return QWidget::sizeHint();
-}
+ auto symbolWidget = new QWidget;
+ symbolWidget->setLayout(symbolLayout);
+ m_symbolTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // Address
+ m_symbolTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); // Name
+ m_symbolTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); // Image
-void CollapsibleSection::setCollapsed(bool collapsed, bool animated)
-{
- if (collapsed == m_collapsed)
- {
- return;
- }
+ m_symbolTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ m_symbolTable->setSelectionMode(QAbstractItemView::SingleSelection);
- m_collapsed = collapsed;
+ std::function<void(uint64_t)> navigateToAddress = [=](uint64_t addr) {
+ ExecuteOnMainThread([addr, this](){
+ if (Settings::Instance()->Get<bool>("ui.view.graph.preferred"))
+ m_data->Navigate("Graph:KCView", addr);
+ else
+ m_data->Navigate("Linear:KCView", addr);
+ });
+ };
- if (m_collapsed)
- {
- m_contentWidget->hide();
- }
- else
+ connect(m_symbolTable, &SymbolTableView::activated, this, [=](const QModelIndex& index)
{
- m_contentWidget->show();
- }
+ auto symbol = m_symbolTable->getSymbolAtRow(index.row());
+ WorkerPriorityEnqueue([this, symbol, navigateToAddress]() {
+ if (m_data->IsValidOffset(symbol.address))
+ navigateToAddress(symbol.address);
+ else
+ {
+ m_cache->LoadImageWithInstallName(symbol.image);
+ navigateToAddress(symbol.address);
+ }
+ });
+ });
- if (animated)
- {
- m_onContentAddedAnimation->start();
- }
+ m_triageTabs->addTab(symbolWidget, "Symbols");
+ m_triageTabs->setCanCloseTab(symbolWidget, false);
}
-KCTriageViewType::KCTriageViewType()
- : ViewType("KCTriage", "Kernel Cache Triage")
+QFont KCTriageView::getFont()
{
-
+ return getMonospaceFont(this);
}
-int KCTriageViewType::getPriority(BinaryViewRef data, const QString& filename)
+BinaryViewRef KCTriageView::getData()
{
- if (data->GetTypeName() == KC_VIEW_NAME)
- {
- return 100;
- }
- return 0;
+ return m_data;
}
-QWidget* KCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame)
+bool KCTriageView::navigate(uint64_t offset)
{
- if (data->GetTypeName() != KC_VIEW_NAME)
- {
- return nullptr;
- }
- return new KCTriageView(viewFrame, data);
+ // TODO: We have to set this to true otherwise view restore does not pickup this view.
+ return true;
}
-void KCTriageViewType::Register()
+uint64_t KCTriageView::getCurrentOffset()
{
- ViewType::registerViewType(new KCTriageViewType());
+ return 0;
}
diff --git a/view/kernelcache/ui/kctriage.h b/view/kernelcache/ui/kctriage.h
index a6b7f7ac..07c24fad 100644
--- a/view/kernelcache/ui/kctriage.h
+++ b/view/kernelcache/ui/kctriage.h
@@ -1,7 +1,3 @@
-//
-// Created by kat on 8/15/24.
-//
-
#include <kernelcacheapi.h>
#include <binaryninjaapi.h>
#include "uitypes.h"
@@ -14,37 +10,29 @@
#include <QSortFilterProxyModel>
#include <QHeaderView>
#include "filter.h"
+#include <QStyledItemDelegate>
+
+#include "ui/fontsettings.h"
#ifndef BINARYNINJA_KCTRIAGE_H
#define BINARYNINJA_KCTRIAGE_H
-class CollapsibleSection : public QWidget
+class AddressColorDelegate : public QStyledItemDelegate
{
- Q_OBJECT
-
- QLabel* m_titleLabel;
- QLabel* m_subtitleRightLabel;
- QPushButton* m_collapseButton;
-
- bool m_collapsed = true;
-
- Animation* m_onContentAddedAnimation;
-
- QWidget* m_contentWidgetContainer;
- QWidget* m_contentWidget;
-
-protected:
- QSize sizeHint() const override;
public:
- CollapsibleSection(QWidget* parent);
- void setTitle(const QString& title);
- void setSubtitleRight(const QString& subtitle);
+ explicit AddressColorDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
- void setContentWidget(QWidget* contentWidget);
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
+ {
+ QStyleOptionViewItem opt = option;
+ initStyleOption(&opt, index);
- void setCollapsed(bool collapsed, bool animated = true);
- bool isCollapsed() const { return m_collapsed; }
+ opt.palette.setColor(QPalette::Text, getThemeColor(BNThemeColor::AddressColor));
+ opt.displayAlignment = Qt::AlignCenter | Qt::AlignVCenter;
+
+ QStyledItemDelegate::paint(painter, opt, index);
+ }
};
@@ -54,12 +42,13 @@ class FilterableTableView : public QTableView, public FilterTarget {
bool m_filterByHiding;
public:
- FilterableTableView(QWidget* parent = nullptr, bool filterByHiding = true)
+ explicit FilterableTableView(QWidget* parent = nullptr, bool filterByHiding = true)
: QTableView(parent), m_filterByHiding(filterByHiding) {
viewport()->installEventFilter(this);
+ setFont(getMonospaceFont(parent));
}
- ~FilterableTableView() override {}
+ ~FilterableTableView() override = default;
void setFilter(const std::string& filter) override {
if (!m_filterByHiding)
@@ -112,7 +101,7 @@ public:
bool eventFilter(QObject* obj, QEvent* event) override {
if (event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ auto keyEvent = dynamic_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Escape) {
clearSelection();
return true;
@@ -135,19 +124,18 @@ class SymbolTableModel : public QAbstractTableModel {
Q_OBJECT
SymbolTableView* m_parent;
+ QFont m_font;
std::string m_filter;
std::vector<KernelCacheAPI::KCSymbol> m_symbols;
public:
explicit SymbolTableModel(SymbolTableView* parent);
- int rowCount(const QModelIndex& parent = QModelIndex()) const override;
- int columnCount(const QModelIndex& parent = QModelIndex()) const override;
- QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
-
+ int rowCount(const QModelIndex& parent) const override;
+ int columnCount(const QModelIndex& parent) const override;
+ QVariant data(const QModelIndex& index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void updateSymbols();
-
void setFilter(std::string text);
const KernelCacheAPI::KCSymbol& symbolAt(int row) const;
@@ -165,7 +153,7 @@ class SymbolTableView : public QTableView, public FilterTarget
public:
SymbolTableView(QWidget* parent, Ref<KernelCacheAPI::KernelCache> cache);
- virtual ~SymbolTableView() override;
+ ~SymbolTableView() override;
void scrollToFirstItem() override {
if (model()->rowCount() > 0) {
@@ -204,29 +192,36 @@ public:
};
-class KCTriageView : public QWidget, public View
+class KCTriageView : public QWidget, public View, public UIContextNotification
{
BinaryViewRef m_data;
QVBoxLayout* m_layout;
- Ref<KernelCacheAPI::KernelCache> m_cache;
- std::mutex m_headersMutex;
- std::shared_ptr<std::vector<KernelCacheAPI::KernelCacheMachOHeader>> m_headers;
+ Ref<KernelCacheAPI::KernelCache> m_cache;
SplitTabWidget* m_triageTabs;
DockableTabCollection* m_triageCollection;
- SplitTabWidget* m_bottomRegionTabs;
- DockableTabCollection* m_bottomRegionCollection;
+ FilterableTableView* m_imageTable;
+ QStandardItemModel* m_imageModel;
+ SymbolTableView* m_symbolTable;
+
+ std::mutex m_headersMutex;
+ std::shared_ptr<std::vector<KernelCacheAPI::KernelCacheMachOHeader>> m_headers;
public:
KCTriageView(QWidget* parent, BinaryViewRef data);
+ ~KCTriageView() override;
BinaryViewRef getData() override;
void setSelectionOffsets(BNAddressRange range) override {};
QFont getFont() override;
bool navigate(uint64_t offset) override;
uint64_t getCurrentOffset() override;
+
+private:
+ QWidget* initImageTable();
+ void initSymbolTable();
};