Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add entry functions to triage UI #5665

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 281 additions & 0 deletions examples/triage/entry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
#include <cstring>
#include <algorithm>
#include "entry.h"
#include "view.h"
#include "fontsettings.h"


const int AddressColumn = 0;
const int NameColumn = 1;
const int ColumnCount = 2;


GenericEntryModel::GenericEntryModel(QWidget* parent, BinaryViewRef data): QAbstractItemModel(parent)
{
m_data = data;
m_sortOrder = Qt::AscendingOrder;
m_allEntries = data->GetAllEntryFunctions();
m_entries = m_allEntries;
}


int GenericEntryModel::columnCount(const QModelIndex&) const
{
return ColumnCount;
}


int GenericEntryModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return (int)m_entries.size();
}


QVariant GenericEntryModel::data(const QModelIndex& index, int role) const
{
switch (role)
{
case Qt::DisplayRole:
if (!index.isValid() || index.row() >= (int)m_entries.size())
return QVariant();
if (index.column() == AddressColumn)
return QString("0x") + QString::number(m_entries[index.row()]->GetStart(), 16);
if (index.column() == NameColumn)
return QString::fromStdString(m_entries[index.row()]->GetSymbol()->GetFullName());
break;
case Qt::ForegroundRole:
if (index.column() == AddressColumn)
return getThemeColor(AddressColor);
if (index.column() == NameColumn)
return getThemeColor(ExportColor);
break;
default:
break;
}

return QVariant();
}


QVariant GenericEntryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical)
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
if (section == AddressColumn)
return QString("Address");
if (section == NameColumn)
return QString("Name");
return QVariant();
}


QModelIndex GenericEntryModel::index(int row, int col, const QModelIndex& parent) const
{
if (parent.isValid())
return QModelIndex();
if (row >= (int)m_entries.size())
return QModelIndex();
if (col >= ColumnCount)
return QModelIndex();
return createIndex(row, col);
}


QModelIndex GenericEntryModel::parent(const QModelIndex&) const
{
return QModelIndex();
}


FunctionRef GenericEntryModel::getEntry(const QModelIndex& index)
{
if (!index.isValid() || index.row() >= (int)m_entries.size())
return nullptr;
return m_entries[index.row()];
}


void GenericEntryModel::performSort(int col, Qt::SortOrder order)
{
std::sort(m_entries.begin(), m_entries.end(), [&](FunctionRef a, FunctionRef b) {
if (col == AddressColumn)
{
if (a->GetStart() != b->GetStart())
{
if (order == Qt::AscendingOrder)
return a->GetStart() < b->GetStart();
else
return a->GetStart() > b->GetStart();
}
if (order == Qt::AscendingOrder)
return a->GetSymbol()->GetFullName() < b->GetSymbol()->GetFullName();
else
return a->GetSymbol()->GetFullName() > b->GetSymbol()->GetFullName();
}
else if (col == NameColumn)
{
if (order == Qt::AscendingOrder)
return a->GetSymbol()->GetFullName() < b->GetSymbol()->GetFullName();
else
return a->GetSymbol()->GetFullName() > b->GetSymbol()->GetFullName();
}
return false;
});
}


void GenericEntryModel::sort(int col, Qt::SortOrder order)
{
beginResetModel();
m_sortCol = col;
m_sortOrder = order;
performSort(col, order);
endResetModel();
}


void GenericEntryModel::setFilter(const std::string& filterText)
{
beginResetModel();
m_entries.clear();
for (auto& entry : m_allEntries)
{
if (FilteredView::match(entry->GetSymbol()->GetFullName(), filterText))
m_entries.push_back(entry);
}
performSort(m_sortCol, m_sortOrder);
endResetModel();
}


EntryTreeView::EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRef data) : QTreeView(parent)
{
m_data = data;
m_parent = parent;
m_view = view;

// Allow view-specific shortcuts when imports are focused
m_actionHandler.setupActionHandler(this);
m_actionHandler.setActionContext([=]() { return m_view->actionContext(); });

m_model = new GenericEntryModel(this, m_data);
setModel(m_model);
setRootIsDecorated(false);
setUniformRowHeights(true);
setSortingEnabled(true);
sortByColumn(AddressColumn, Qt::AscendingOrder);

setColumnWidth(AddressColumn, 90);

setFont(getMonospaceFont(this));

connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &EntryTreeView::entrySelected);
connect(this, &QTreeView::doubleClicked, this, &EntryTreeView::entryDoubleClicked);
}


void EntryTreeView::entrySelected(const QModelIndex& cur, const QModelIndex&)
{
FunctionRef func = m_model->getEntry(cur);
if (func)
m_view->setCurrentOffset(func->GetStart());
}


void EntryTreeView::entryDoubleClicked(const QModelIndex& cur)
{
FunctionRef func = m_model->getEntry(cur);
if (func)
{
ViewFrame* viewFrame = ViewFrame::viewFrameForWidget(this);
if (viewFrame)
{
if (BinaryNinja::Settings::Instance()->Get<bool>("ui.view.graph.preferred") &&
viewFrame->getCurrentBinaryView() &&
func->GetStart() > 0)
{
viewFrame->navigate("Graph:" + viewFrame->getCurrentDataType(), func->GetStart());
}
else
{
viewFrame->navigate("Linear:" + viewFrame->getCurrentDataType(), func->GetStart());
}
}
}
}


void EntryTreeView::setFilter(const std::string& filterText)
{
m_model->setFilter(filterText);
}


void EntryTreeView::scrollToFirstItem()
{
scrollToTop();
}


void EntryTreeView::scrollToCurrentItem()
{
scrollTo(currentIndex());
}


void EntryTreeView::selectFirstItem()
{
setCurrentIndex(m_model->index(0, 0, QModelIndex()));
}


void EntryTreeView::activateFirstItem()
{
entryDoubleClicked(m_model->index(0, 0, QModelIndex()));
}


void EntryTreeView::closeFilter()
{
setFocus(Qt::OtherFocusReason);
}


void EntryTreeView::keyPressEvent(QKeyEvent* event)
{
if ((event->text().size() == 1) && (event->text()[0] > ' ') && (event->text()[0] <= '~'))
{
m_parent->showFilter(event->text());
event->accept();
}
else if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter))
{
QList<QModelIndex> sel = selectionModel()->selectedIndexes();
if (sel.size() != 0)
entryDoubleClicked(sel[0]);
}
QTreeView::keyPressEvent(event);
}


EntryWidget::EntryWidget(QWidget* parent, TriageView* view, BinaryViewRef data) : QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
EntryTreeView* entry = new EntryTreeView(this, view, data);
m_filter = new FilteredView(this, entry, entry);
m_filter->setFilterPlaceholderText("Search entry functions");
layout->addWidget(m_filter, 1);
setLayout(layout);
setMinimumSize(UIContext::getScaledWindowSize(100, 196));
}


void EntryWidget::showFilter(const QString& filter)
{
m_filter->showFilter(filter);
}
70 changes: 70 additions & 0 deletions examples/triage/entry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#pragma once

#include <QtCore/QAbstractItemModel>
#include <QtWidgets/QTreeView>
#include "filter.h"


class GenericEntryModel : public QAbstractItemModel
{
BinaryViewRef m_data;
std::vector<FunctionRef> m_allEntries, m_entries;
Qt::SortOrder m_sortOrder;
int m_sortCol;

void performSort(int col, Qt::SortOrder order);

public:
GenericEntryModel(QWidget* parent, BinaryViewRef data);

virtual int columnCount(const QModelIndex& parent) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& index) const override;
virtual void sort(int col, Qt::SortOrder order) override;
void setFilter(const std::string& filterText);

FunctionRef getEntry(const QModelIndex& index);
};


class TriageView;
class EntryWidget;

class EntryTreeView : public QTreeView, public FilterTarget
{
BinaryViewRef m_data;
EntryWidget* m_parent;
TriageView* m_view;
UIActionHandler m_actionHandler;
GenericEntryModel* m_model;

public:
EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRef data);

virtual void setFilter(const std::string& filterText) override;
virtual void scrollToFirstItem() override;
virtual void scrollToCurrentItem() override;
virtual void selectFirstItem() override;
virtual void activateFirstItem() override;
virtual void closeFilter() override;

protected:
virtual void keyPressEvent(QKeyEvent* event) override;

private Q_SLOTS:
void entrySelected(const QModelIndex& cur, const QModelIndex& prev);
void entryDoubleClicked(const QModelIndex& cur);
};


class EntryWidget : public QWidget
{
FilteredView* m_filter;

public:
EntryWidget(QWidget* parent, TriageView* view, BinaryViewRef data);
void showFilter(const QString& filter);
};
8 changes: 8 additions & 0 deletions examples/triage/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <QtWidgets/QSplitter>
#include "view.h"
#include "entropy.h"
#include "entry.h"
#include "imports.h"
#include "exports.h"
#include "sections.h"
Expand Down Expand Up @@ -87,6 +88,12 @@ TriageView::TriageView(QWidget* parent, BinaryViewRef data) : QScrollArea(parent

layout->addWidget(importExportSplitter);

QGroupBox* entryGroup = new QGroupBox("Entry Functions", container);
QVBoxLayout* entryLayout = new QVBoxLayout();
entryLayout->addWidget(new EntryWidget(entryGroup, this, m_data));
entryGroup->setLayout(entryLayout);
layout->addWidget(entryGroup);

if (m_data->GetTypeName() != "PE")
{
QGroupBox* segmentsGroup = new QGroupBox("Segments", container);
Expand All @@ -108,6 +115,7 @@ TriageView::TriageView(QWidget* parent, BinaryViewRef data) : QScrollArea(parent
if (sectionsWidget->GetSections().size() == 0)
sectionsGroup->hide();


QGroupBox* stringsGroup = new QGroupBox("Strings", container);
QVBoxLayout* stringsLayout = new QVBoxLayout();
stringsLayout->addWidget(new StringsWidget(stringsGroup, this, m_data));
Expand Down