Add virtual tables panel (#340)

This commit is contained in:
Abdel-Rahman A 2018-02-27 00:26:18 +02:00 committed by xarkes
parent a9f669e27e
commit c1132aba0c
9 changed files with 402 additions and 2 deletions

View File

@ -65,6 +65,7 @@
#include "dialogs/SaveProjectDialog.h"
#include "widgets/ClassesWidget.h"
#include "widgets/ResourcesWidget.h"
#include "widgets/VTablesWidget.h"
// graphics
#include <QGraphicsEllipseItem>
@ -223,6 +224,7 @@ void MainWindow::initUI()
ADD_DOCK(SdbDock, sdbDock, ui->actionSDBBrowser);
ADD_DOCK(ClassesWidget, classesDock, ui->actionClasses);
ADD_DOCK(ResourcesWidget, resourcesDock, ui->actionResources);
ADD_DOCK(VTablesWidget, vTablesDock, ui->actionVTables);
#undef ADD_DOCK
@ -545,6 +547,7 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, notepadDock);
tabifyDockWidget(dashboardDock, classesDock);
tabifyDockWidget(dashboardDock, resourcesDock);
tabifyDockWidget(dashboardDock, vTablesDock);
updateDockActionsChecked();
}

View File

@ -39,6 +39,7 @@ class EntrypointWidget;
class DisassemblerGraphView;
class ClassesWidget;
class ResourcesWidget;
class VTablesWidget;
class QDockWidget;
@ -186,6 +187,7 @@ private:
ConsoleWidget *consoleDock = nullptr;
ClassesWidget *classesDock = nullptr;
ResourcesWidget *resourcesDock = nullptr;
VTablesWidget *vTablesDock = nullptr;
DisassemblerGraphView *graphView = nullptr;
QDockWidget *asmDock = nullptr;
QDockWidget *calcDock = nullptr;

View File

@ -250,6 +250,7 @@ border-top: 0px;
<addaction name="actionSymbols"/>
<addaction name="actionSDBBrowser"/>
<addaction name="actionResources"/>
<addaction name="actionVTables"/>
<addaction name="separator"/>
<addaction name="actionRelocs"/>
<addaction name="actionStrings"/>
@ -1050,6 +1051,17 @@ QToolButton:pressed {
<string>Resources</string>
</property>
</action>
<action name="actionVTables">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>VTables</string>
</property>
<property name="toolTip">
<string>Show/Hide VTables panel</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -1252,6 +1252,36 @@ QList<ResourcesDescription> CutterCore::getAllResources()
return ret;
}
QList<VTableDescription> CutterCore::getAllVTables()
{
CORE_LOCK();
QList<VTableDescription> ret;
QJsonArray vTablesArray = cmdj("avj").array();
for(QJsonValueRef vTableValue : vTablesArray)
{
QJsonObject vTableObject = vTableValue.toObject();
VTableDescription res;
res.addr = vTableObject["offset"].toVariant().toULongLong();
QJsonArray methodArray = vTableObject["methods"].toArray();
for(QJsonValueRef methodValue : methodArray)
{
QJsonObject methodObject = methodValue.toObject();
ClassMethodDescription method;
method.addr = methodObject["offset"].toVariant().toULongLong();
method.name = methodObject["name"].toString();
res.methods << method;
}
ret << res;
}
return ret;
}
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType)
{
QList<XrefDescription> ret = QList<XrefDescription>();

View File

@ -208,6 +208,12 @@ struct ResourcesDescription
QString lang;
};
struct VTableDescription
{
RVA addr;
QList<ClassMethodDescription> methods;
};
Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription)
@ -227,6 +233,7 @@ Q_DECLARE_METATYPE(const ClassDescription *)
Q_DECLARE_METATYPE(const ClassMethodDescription *)
Q_DECLARE_METATYPE(const ClassFieldDescription *)
Q_DECLARE_METATYPE(ResourcesDescription)
Q_DECLARE_METATYPE(VTableDescription)
class CutterCore: public QObject
{
@ -360,6 +367,7 @@ public:
QList<EntrypointDescription> getAllEntrypoint();
QList<ClassDescription> getAllClasses();
QList<ResourcesDescription> getAllResources();
QList<VTableDescription> getAllVTables();
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType = QString::null);

View File

@ -90,6 +90,7 @@ SOURCES += \
widgets/QuickFilterView.cpp \
widgets/ClassesWidget.cpp \
widgets/ResourcesWidget.cpp \
widgets/VTablesWidget.cpp \
CutterApplication.cpp
HEADERS += \
@ -150,7 +151,8 @@ HEADERS += \
widgets/QuickFilterView.h \
widgets/ClassesWidget.h \
widgets/ResourcesWidget.h \
CutterApplication.h
CutterApplication.h \
widgets/VTablesWidget.h
FORMS += \
dialogs/AboutDialog.ui \
@ -186,7 +188,8 @@ FORMS += \
dialogs/preferences/GraphOptionsWidget.ui \
widgets/QuickFilterView.ui \
widgets/PseudocodeWidget.ui \
widgets/ClassesWidget.ui
widgets/ClassesWidget.ui \
widgets/VTablesWidget.ui
RESOURCES += \
resources.qrc \

View File

@ -0,0 +1,179 @@
#include <QShortcut>
#include "VTablesWidget.h"
#include "ui_VTablesWidget.h"
VTableModel::VTableModel(QList<VTableDescription> *vtables, QObject *parent)
: QAbstractItemModel(parent),
vtables(vtables)
{
}
QModelIndex VTableModel::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column, (quintptr) parent.isValid()? parent.row() : -1);
}
QModelIndex VTableModel::parent(const QModelIndex &index) const
{
return index.isValid() && index.internalId() != (quintptr) -1 ?
this->index(index.internalId(), index.column()) : QModelIndex();
}
int VTableModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid()? (parent.parent().isValid()? 0 : vtables->at(parent.row()).methods.count()) : vtables->count();
}
int VTableModel::columnCount(const QModelIndex &) const
{
return Columns::COUNT;
}
QVariant VTableModel::data(const QModelIndex &index, int role) const
{
QModelIndex parent = index.parent();
if(parent.isValid())
{
const ClassMethodDescription &res = vtables->at(parent.row()).methods.at(index.row());
switch (role)
{
case Qt::DisplayRole:
switch(index.column())
{
case NAME:
return res.name;
case ADDRESS:
return RAddressString(res.addr);
default:
break;
}
default:
break;
}
}
else
switch(role)
{
case Qt::DisplayRole:
switch(index.column())
{
case NAME:
return tr("VTable ") + QString::number(index.row() + 1);
case ADDRESS:
return RAddressString(vtables->at(index.row()).addr);
default:
break;
}
default:
break;
}
return QVariant();
}
QVariant VTableModel::headerData(int section, Qt::Orientation, int role) const
{
switch(role)
{
case Qt::DisplayRole:
switch(section)
{
case NAME:
return tr("Name");
case ADDRESS:
return tr("Address");
default:
break;
}
default:
break;
}
return QVariant();
}
void VTableModel::beginReload()
{
beginResetModel();
}
void VTableModel::endReload()
{
endResetModel();
}
VTableSortFilterProxyModel::VTableSortFilterProxyModel(VTableModel *model, QObject* parent)
: QSortFilterProxyModel(parent)
{
setSourceModel(model);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
setFilterKeyColumn(VTableModel::NAME);
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
setRecursiveFilteringEnabled(true);
#endif
}
bool VTableSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if(QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent))
return true;
if(source_parent.isValid())
return QSortFilterProxyModel::filterAcceptsRow(source_parent.row(), QModelIndex());
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
else
{
QAbstractItemModel* const model = sourceModel();
const QModelIndex source = model->index(source_row, 0, QModelIndex());
const int rows = model->rowCount(source);
for(int i = 0; i < rows; ++i)
if(QSortFilterProxyModel::filterAcceptsRow(i, source))
return true;
}
#endif
return false;
}
VTablesWidget::VTablesWidget(QWidget *parent) :
QDockWidget(parent),
ui(new Ui::VTablesWidget)
{
ui->setupUi(this);
model = new VTableModel(&vtables, this);
proxy = new VTableSortFilterProxyModel(model);
ui->vTableTreeView->setModel(proxy);
ui->vTableTreeView->sortByColumn(VTableModel::ADDRESS, Qt::AscendingOrder);
// Esc to clear the filter entry
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
// Ctrl-F to show/hide the filter entry
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);
connect(search_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter);
search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString&)), proxy, SLOT(setFilterWildcard(const QString &)));
connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->vTableTreeView, SLOT(setFocus()));
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshVTables()));
}
VTablesWidget::~VTablesWidget()
{
}
void VTablesWidget::refreshVTables()
{
model->beginReload();
vtables = CutterCore::getInstance()->getAllVTables();
model->endReload();
ui->vTableTreeView->resizeColumnToContents(0);
ui->vTableTreeView->resizeColumnToContents(1);
ui->vTableTreeView->resizeColumnToContents(2);
ui->vTableTreeView->setColumnWidth(0, 200);
}

View File

@ -0,0 +1,70 @@
#ifndef VTABLESWIDGET_H
#define VTABLESWIDGET_H
#include <memory>
#include <QTreeView>
#include <QSortFilterProxyModel>
#include <QDockWidget>
#include "cutter.h"
namespace Ui
{
class VTablesWidget;
}
class VTableModel : public QAbstractItemModel
{
Q_OBJECT
private:
QList<VTableDescription> *vtables;
public:
enum Columns { NAME = 0, ADDRESS, COUNT };
VTableModel(QList<VTableDescription> *vtables, QObject* parent = nullptr);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void beginReload();
void endReload();
};
class VTableSortFilterProxyModel : public QSortFilterProxyModel
{
public:
VTableSortFilterProxyModel(VTableModel* model, QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
};
class VTablesWidget : public QDockWidget
{
Q_OBJECT
public:
explicit VTablesWidget(QWidget *parent = 0);
~VTablesWidget();
private slots:
void refreshVTables();
private:
std::unique_ptr<Ui::VTablesWidget> ui;
VTableModel *model;
QSortFilterProxyModel *proxy;
QList<VTableDescription> vtables;
};
#endif // VTABLESWIDGET_H

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VTablesWidget</class>
<widget class="QDockWidget" name="VTablesWidget">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="windowTitle">
<string notr="true">&amp;VTable</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTreeView" name="vTableTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="styleSheet">
<string notr="true">
QTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>QuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/QuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>