added basic search (#367)

* Added basic search
This commit is contained in:
fcasal 2018-03-08 12:24:15 +00:00 committed by xarkes
parent 7b552694f0
commit d9592edd1b
9 changed files with 449 additions and 1 deletions

View File

@ -1305,6 +1305,30 @@ QList<TypeDescription> CutterCore::getAllTypes()
return ret;
}
QList<SearchDescription> CutterCore::getAllSearch(QString search_for, QString space)
{
CORE_LOCK();
QList<SearchDescription> ret;
QJsonArray searchArray = cmdj(space + QString(" ") + search_for).array();
foreach (QJsonValue value, searchArray)
{
QJsonObject searchObject = value.toObject();
SearchDescription exp;
exp.offset = searchObject["offset"].toVariant().toULongLong();
exp.size = searchObject["len"].toVariant().toULongLong();
exp.code = searchObject["code"].toString();
exp.data = searchObject["data"].toString();
ret << exp;
}
return ret;
}
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType)
{
QList<XrefDescription> ret = QList<XrefDescription>();

View File

@ -98,6 +98,14 @@ struct TypeDescription
QString format;
};
struct SearchDescription
{
RVA offset;
int size;
QString code;
QString data;
};
struct SymbolDescription
{
RVA vaddr;
@ -242,6 +250,7 @@ Q_DECLARE_METATYPE(const ClassFieldDescription *)
Q_DECLARE_METATYPE(ResourcesDescription)
Q_DECLARE_METATYPE(VTableDescription)
Q_DECLARE_METATYPE(TypeDescription)
Q_DECLARE_METATYPE(SearchDescription)
class CutterCore: public QObject
{
@ -378,6 +387,7 @@ public:
QList<ResourcesDescription> getAllResources();
QList<VTableDescription> getAllVTables();
QList<TypeDescription> getAllTypes();
QList<SearchDescription> getAllSearch(QString search_for, QString space);
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType = QString::null);

View File

@ -135,6 +135,7 @@ SOURCES += \
widgets/ResourcesWidget.cpp \
widgets/VTablesWidget.cpp \
widgets/TypesWidget.cpp \
widgets/SearchWidget.cpp \
CutterApplication.cpp \
utils/JupyterConnection.cpp \
widgets/JupyterWidget.cpp \
@ -201,6 +202,7 @@ HEADERS += \
CutterApplication.h \
widgets/VTablesWidget.h \
widgets/TypesWidget.h \
widgets/SearchWidget.h \
utils/JupyterConnection.h \
widgets/JupyterWidget.h \
utils/PythonAPI.h \
@ -242,6 +244,7 @@ FORMS += \
widgets/ClassesWidget.ui \
widgets/VTablesWidget.ui \
widgets/TypesWidget.ui \
widgets/SearchWidget.ui \
widgets/JupyterWidget.ui
RESOURCES += \

View File

@ -49,6 +49,7 @@
#include "widgets/ImportsWidget.h"
#include "widgets/ExportsWidget.h"
#include "widgets/TypesWidget.h"
#include "widgets/SearchWidget.h"
#include "widgets/SymbolsWidget.h"
#include "widgets/StringsWidget.h"
#include "widgets/SectionsDock.h"
@ -202,6 +203,7 @@ void MainWindow::initUI()
ADD_DOCK(ImportsWidget, importsDock, ui->actionImports);
ADD_DOCK(ExportsWidget, exportsDock, ui->actionExports);
ADD_DOCK(TypesWidget, typesDock, ui->actionTypes);
ADD_DOCK(SearchWidget, searchDock, ui->actionSearchInst);
ADD_DOCK(SymbolsWidget, symbolsDock, ui->actionSymbols);
ADD_DOCK(RelocsWidget, relocsDock, ui->actionRelocs);
ADD_DOCK(CommentsWidget, commentsDock, ui->actionComments);
@ -522,6 +524,7 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, importsDock);
tabifyDockWidget(dashboardDock, exportsDock);
tabifyDockWidget(dashboardDock, typesDock);
tabifyDockWidget(dashboardDock, searchDock);
tabifyDockWidget(dashboardDock, symbolsDock);
tabifyDockWidget(dashboardDock, classesDock);
tabifyDockWidget(dashboardDock, resourcesDock);

View File

@ -40,6 +40,7 @@ class ClassesWidget;
class ResourcesWidget;
class VTablesWidget;
class TypesWidget;
class SearchWidget;
#ifdef CUTTER_ENABLE_JUPYTER
class JupyterWidget;
#endif
@ -175,6 +176,7 @@ private:
ImportsWidget *importsDock = nullptr;
ExportsWidget *exportsDock = nullptr;
TypesWidget *typesDock = nullptr;
SearchWidget *searchDock = nullptr;
SymbolsWidget *symbolsDock = nullptr;
RelocsWidget *relocsDock = nullptr;
CommentsWidget *commentsDock = nullptr;

View File

@ -244,6 +244,7 @@ border-top: 0px;
<addaction name="actionResources"/>
<addaction name="actionVTables"/>
<addaction name="actionTypes"/>
<addaction name="actionSearchInst"/>
<addaction name="separator"/>
<addaction name="actionRelocs"/>
<addaction name="actionStrings"/>
@ -1054,6 +1055,17 @@ background-color: palette(dark);
<string>Show/Hide Types panel</string>
</property>
</action>
<action name="actionSearchInst">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Search</string>
</property>
<property name="toolTip">
<string>Show/Hide Search panel</string>
</property>
</action>
<action name="actionJupyter">
<property name="checkable">
<bool>true</bool>

View File

@ -0,0 +1,201 @@
#include <QDockWidget>
#include <QTreeWidget>
#include <QComboBox>
#include "SearchWidget.h"
#include "ui_SearchWidget.h"
#include "MainWindow.h"
#include "utils/Helpers.h"
SearchModel::SearchModel(QList<SearchDescription> *search, QObject *parent)
: QAbstractListModel(parent),
search(search)
{
}
int SearchModel::rowCount(const QModelIndex &) const
{
return search->count();
}
int SearchModel::columnCount(const QModelIndex &) const
{
return Columns::COUNT;
}
QVariant SearchModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= search->count())
return QVariant();
const SearchDescription &exp = search->at(index.row());
switch (role)
{
case Qt::DisplayRole:
switch (index.column())
{
case OFFSET:
return RAddressString(exp.offset);
case SIZE:
return RSizeString(exp.size);
case CODE:
return exp.code;
case DATA:
return exp.data;
default:
return QVariant();
}
case SearchDescriptionRole:
return QVariant::fromValue(exp);
default:
return QVariant();
}
}
QVariant SearchModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role)
{
case Qt::DisplayRole:
switch (section)
{
case SIZE:
return tr("Size");
case OFFSET:
return tr("Offset");
case CODE:
return tr("Code");
case DATA:
return tr("Data");
default:
return QVariant();
}
default:
return QVariant();
}
}
void SearchModel::beginReloadSearch()
{
beginResetModel();
}
void SearchModel::endReloadSearch()
{
endResetModel();
}
SearchSortFilterProxyModel::SearchSortFilterProxyModel(SearchModel *source_model, QObject *parent)
: QSortFilterProxyModel(parent)
{
setSourceModel(source_model);
}
bool SearchSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
QModelIndex index = sourceModel()->index(row, 0, parent);
SearchDescription search = index.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();
return search.code.contains(filterRegExp());
}
bool SearchSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
SearchDescription left_search = left.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();
SearchDescription right_search = right.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();
switch (left.column())
{
case SearchModel::SIZE:
return left_search.size < right_search.size;
case SearchModel::OFFSET:
return left_search.offset < right_search.offset;
case SearchModel::CODE:
return left_search.code < right_search.code;
case SearchModel::DATA:
return left_search.data < right_search.data;
default:
break;
}
return left_search.offset < right_search.offset;
}
SearchWidget::SearchWidget(MainWindow *main, QWidget *parent) :
QDockWidget(parent),
ui(new Ui::SearchWidget),
main(main)
{
ui->setupUi(this);
search_model = new SearchModel(&search, this);
search_proxy_model = new SearchSortFilterProxyModel(search_model, this);
ui->searchTreeView->setModel(search_proxy_model);
ui->searchTreeView->sortByColumn(SearchModel::OFFSET, Qt::AscendingOrder);
setScrollMode();
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSearchspaces()));
QShortcut *enter_press = new QShortcut(QKeySequence(Qt::Key_Return), this);
connect(enter_press, &QShortcut::activated, this, [this]() {
refreshSearch();
});
enter_press->setContext(Qt::WidgetWithChildrenShortcut);
connect(ui->searchButton, &QAbstractButton::clicked, this, [this]() {
refreshSearch();
});
}
SearchWidget::~SearchWidget() {}
void SearchWidget::on_searchTreeView_doubleClicked(const QModelIndex &index)
{
SearchDescription search = index.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();
Core()->seek(search.offset);
}
void SearchWidget::searchChanged()
{
refreshSearchspaces();
}
void SearchWidget::refreshSearchspaces()
{
int cur_idx = ui->searchspaceCombo->currentIndex();
if (cur_idx < 0)
cur_idx = 0;
ui->searchspaceCombo->clear();
ui->searchspaceCombo->addItem(tr("asm code"), QVariant("/cj"));
ui->searchspaceCombo->addItem(tr("string"), QVariant("/j"));
ui->searchspaceCombo->addItem(tr("hex string"), QVariant("/xj"));
if (cur_idx > 0)
ui->searchspaceCombo->setCurrentIndex(cur_idx);
refreshSearch();
}
void SearchWidget::refreshSearch()
{
QString search_for = ui->filterLineEdit->text();
QVariant searchspace_data = ui->searchspaceCombo->currentData();
QString searchspace = searchspace_data.toString();
search_model->beginReloadSearch();
search = Core()->getAllSearch(search_for, searchspace);
search_model->endReloadSearch();
ui->searchTreeView->resizeColumnToContents(0);
ui->searchTreeView->resizeColumnToContents(1);
ui->searchTreeView->resizeColumnToContents(2);
}
void SearchWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->searchTreeView);
}

View File

@ -0,0 +1,86 @@
#ifndef SEARCHWIDGET_H
#define SEARCHWIDGET_H
#include <memory>
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QDockWidget>
#include "Cutter.h"
class MainWindow;
class QTreeWidgetItem;
class SearchModel: public QAbstractListModel
{
Q_OBJECT
private:
QList<SearchDescription> *search;
public:
enum Columns { OFFSET = 0, SIZE, CODE, DATA, COUNT };
static const int SearchDescriptionRole = Qt::UserRole;
SearchModel(QList<SearchDescription> *search, QObject *parent = 0);
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 beginReloadSearch();
void endReloadSearch();
};
class SearchSortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
SearchSortFilterProxyModel(SearchModel *source_model, QObject *parent = 0);
protected:
bool filterAcceptsRow(int row, const QModelIndex &parent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
namespace Ui
{
class SearchWidget;
}
class SearchWidget : public QDockWidget
{
Q_OBJECT
public:
explicit SearchWidget(MainWindow *main, QWidget *parent = 0);
~SearchWidget();
private slots:
void on_searchTreeView_doubleClicked(const QModelIndex &index);
void searchChanged();
void refreshSearchspaces();
private:
std::unique_ptr<Ui::SearchWidget> ui;
MainWindow *main;
SearchModel *search_model;
SearchSortFilterProxyModel *search_proxy_model;
QList<SearchDescription> search;
void refreshSearch();
void setScrollMode();
};
#endif // SEARCHWIDGET_H

107
src/widgets/SearchWidget.ui Normal file
View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchWidget</class>
<widget class="QDockWidget" name="SearchWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>463</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Search</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<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="searchTreeView">
<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="dragEnabled">
<bool>true</bool>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="filterLineEdit">
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>jmp rax</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="searchButton">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="searchspaceLabel">
<property name="text">
<string>Search for:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="searchspaceCombo"/>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>