Add Tools > BaseFind + command line options (#3198)

* Add Tools > BaseFind
* Rewritten how cutter parses and stores the initial options
* Update docs and fixed comments
* Add missing endianness option
This commit is contained in:
Giovanni 2023-06-25 11:42:23 +08:00 committed by GitHub
parent 579ac236b6
commit cf14fd1006
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1362 additions and 21 deletions

View File

@ -40,21 +40,45 @@ Options
**2**
aaaa (experimental)
.. option:: -a, --arch <arch>
Sets a specific architecture name.
.. option:: -b, --bits <bits>
Sets a specific architecture bits.
.. option:: -c, --cpu <cpu>
Sets a specific CPU.
.. option:: -o, --os <os>
Sets a specific operating system.
.. option:: -e, --endian <big|little>
Sets the endianness (big or little).
.. option:: -F, --format <name>
Force using a specific file format (bin plugin)
Force using a specific file format (bin plugin).
.. option:: -B, --base <base address>
Load binary at a specific base address
Load binary at a specific base address.
.. option:: -m, --map <map address>
Map the binary at a specific address.
.. option:: -i <file>
Run script file
Run script file.
.. option:: -p, --project <file>
Load project file
Load project file.
.. option:: -w, --writemode
@ -62,9 +86,13 @@ Options
When used together with -A/--analysis <level>, it will open a file directly
in write mode without any further dialog or confirmation.
.. option:: -P, --phymode
Disables virtual addressing.
.. option:: --pythonhome <PYTHONHOME>
PYTHONHOME to use for the embedded python interpreter
PYTHONHOME to use for the embedded python interpreter.
.. option:: --no-output-redirect

2
rizin

@ -1 +1 @@
Subproject commit 9c6feafd4733903fca0cdad50b3bd213ab2b6228
Subproject commit 6bfc67a2868e07ab89ff931b96dfd3bc65e7371e

View File

@ -6,6 +6,7 @@ set(SOURCES
core/Cutter.cpp
core/CutterJson.cpp
core/RizinCpp.cpp
core/Basefind.cpp
dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp
@ -149,6 +150,9 @@ set(SOURCES
dialogs/GlibcHeapBinsDialog.cpp
widgets/HeapBinsGraphView.cpp
dialogs/ArenaInfoDialog.cpp
tools/basefind/BaseFindDialog.cpp
tools/basefind/BaseFindSearchDialog.cpp
tools/basefind/BaseFindResultsDialog.cpp
)
set(HEADER_FILES
core/Cutter.h
@ -156,6 +160,7 @@ set(HEADER_FILES
core/CutterDescriptions.h
core/CutterJson.h
core/RizinCpp.h
core/Basefind.h
dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h
@ -308,6 +313,9 @@ set(HEADER_FILES
dialogs/GlibcHeapBinsDialog.h
widgets/HeapBinsGraphView.h
dialogs/ArenaInfoDialog.h
tools/basefind/BaseFindDialog.h
tools/basefind/BaseFindSearchDialog.h
tools/basefind/BaseFindResultsDialog.h
)
set(UI_FILES
dialogs/AboutDialog.ui
@ -377,6 +385,9 @@ set(UI_FILES
widgets/GlibcHeapWidget.ui
dialogs/GlibcHeapBinsDialog.ui
dialogs/ArenaInfoDialog.ui
tools/basefind/BaseFindDialog.ui
tools/basefind/BaseFindSearchDialog.ui
tools/basefind/BaseFindResultsDialog.ui
)
set(QRC_FILES
resources.qrc

View File

@ -296,6 +296,89 @@ bool CutterApplication::loadTranslations()
return false;
}
QStringList CutterApplication::getArgs() const
{
auto &options = clOptions.fileOpenOptions;
QStringList args;
switch (clOptions.analysisLevel) {
case AutomaticAnalysisLevel::None:
args.push_back("-A");
args.push_back("0");
break;
case AutomaticAnalysisLevel::AAA:
args.push_back("-A");
args.push_back("1");
break;
case AutomaticAnalysisLevel::AAAA:
args.push_back("-A");
args.push_back("2");
break;
default:
break;
}
if (!options.useVA) {
args.push_back("-P");
}
if (options.writeEnabled) {
args.push_back("-w");
}
if (!options.script.isEmpty()) {
args.push_back("-i");
args.push_back(options.script);
}
if (!options.projectFile.isEmpty()) {
args.push_back("-p");
args.push_back(options.projectFile);
}
if (!options.arch.isEmpty()) {
args.push_back("-a");
args.push_back(options.arch);
}
if (options.bits > 0) {
args.push_back("-b");
args.push_back(QString::asprintf("%d", options.bits));
}
if (!options.cpu.isEmpty()) {
args.push_back("-c");
args.push_back(options.cpu);
}
if (!options.os.isEmpty()) {
args.push_back("-o");
args.push_back(options.os);
}
switch (options.endian) {
case InitialOptions::Endianness::Little:
args.push_back("-e");
args.push_back("little");
break;
case InitialOptions::Endianness::Big:
args.push_back("-e");
args.push_back("big");
break;
default:
break;
}
if (!options.forceBinPlugin.isEmpty()) {
args.push_back("-F");
args.push_back(options.forceBinPlugin);
}
if (options.binLoadAddr != RVA_INVALID) {
args.push_back("-B");
args.push_back(RzAddressString(options.binLoadAddr));
}
if (options.mapAddr != RVA_INVALID) {
args.push_back("-m");
args.push_back(RzAddressString(options.mapAddr));
}
if (!options.filename.isEmpty()) {
args.push_back(options.filename);
}
return args;
}
bool CutterApplication::parseCommandLineOptions()
{
// Keep this function in sync with documentation
@ -315,6 +398,27 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("level"));
cmd_parser.addOption(analOption);
QCommandLineOption archOption({ "a", "arch" }, QObject::tr("Sets a specific architecture name"),
QObject::tr("arch"));
cmd_parser.addOption(archOption);
QCommandLineOption bitsOption({ "b", "bits" }, QObject::tr("Sets a specific architecture bits"),
QObject::tr("bits"));
cmd_parser.addOption(bitsOption);
QCommandLineOption cpuOption({ "c", "cpu" }, QObject::tr("Sets a specific CPU"),
QObject::tr("cpu"));
cmd_parser.addOption(cpuOption);
QCommandLineOption osOption({ "o", "os" }, QObject::tr("Sets a specific operating system"),
QObject::tr("os"));
cmd_parser.addOption(osOption);
QCommandLineOption endianOption({ "e", "endian" },
QObject::tr("Sets the endianness (big or little)"),
QObject::tr("big|little"));
cmd_parser.addOption(endianOption);
QCommandLineOption formatOption({ "F", "format" },
QObject::tr("Force using a specific file format (bin plugin)"),
QObject::tr("name"));
@ -325,6 +429,11 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("base address"));
cmd_parser.addOption(baddrOption);
QCommandLineOption maddrOption({ "m", "map" },
QObject::tr("Map the binary at a specific address"),
QObject::tr("map address"));
cmd_parser.addOption(maddrOption);
QCommandLineOption scriptOption("i", QObject::tr("Run script file"), QObject::tr("file"));
cmd_parser.addOption(scriptOption);
@ -336,6 +445,10 @@ bool CutterApplication::parseCommandLineOptions()
QObject::tr("Open file in write mode"));
cmd_parser.addOption(writeModeOption);
QCommandLineOption phyModeOption({ "P", "phymode" },
QObject::tr("Disables virtual addressing"));
cmd_parser.addOption(phyModeOption);
QCommandLineOption pythonHomeOption(
"pythonhome", QObject::tr("PYTHONHOME to use for embedded python interpreter"),
"PYTHONHOME");
@ -397,15 +510,21 @@ bool CutterApplication::parseCommandLineOptions()
return false;
}
InitialOptions options;
if (!opts.args.isEmpty()) {
opts.fileOpenOptions.filename = opts.args[0];
opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption);
if (cmd_parser.isSet(baddrOption)) {
bool ok;
bool ok = false;
RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0);
if (ok) {
options.binLoadAddr = baddr;
opts.fileOpenOptions.binLoadAddr = baddr;
}
}
if (cmd_parser.isSet(maddrOption)) {
bool ok = false;
RVA maddr = cmd_parser.value(maddrOption).toULongLong(&ok, 0);
if (ok) {
opts.fileOpenOptions.mapAddr = maddr;
}
}
switch (opts.analysisLevel) {
@ -422,8 +541,36 @@ bool CutterApplication::parseCommandLineOptions()
break;
}
opts.fileOpenOptions.script = cmd_parser.value(scriptOption);
opts.fileOpenOptions.arch = cmd_parser.value(archOption);
opts.fileOpenOptions.cpu = cmd_parser.value(cpuOption);
opts.fileOpenOptions.os = cmd_parser.value(osOption);
if (cmd_parser.isSet(bitsOption)) {
bool ok = false;
int bits = cmd_parser.value(bitsOption).toInt(&ok, 10);
if (ok && bits > 0) {
opts.fileOpenOptions.bits = bits;
}
}
if (cmd_parser.isSet(endianOption)) {
QString endian = cmd_parser.value(endianOption).toLower();
opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;
if (endian == "little") {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Little;
} else if (endian == "big") {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Big;
} else {
fprintf(stderr, "%s\n",
QObject::tr("Invalid Endianness. You can only set it to `big` or `little`.")
.toLocal8Bit()
.constData());
return false;
}
} else {
opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;
}
opts.fileOpenOptions.writeEnabled = cmd_parser.isSet(writeModeOption);
opts.fileOpenOptions.useVA = !cmd_parser.isSet(phyModeOption);
}
opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption);

View File

@ -33,6 +33,10 @@ public:
void launchNewInstance(const QStringList &args = {});
InitialOptions getInitialOptions() const { return clOptions.fileOpenOptions; }
void setInitialOptions(const InitialOptions &options) { clOptions.fileOpenOptions = options; }
QStringList getArgs() const;
protected:
bool event(QEvent *e);

109
src/core/Basefind.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "Basefind.h"
bool Basefind::threadCallback(const RzBaseFindThreadInfo *info, void *user)
{
auto th = reinterpret_cast<Basefind *>(user);
return th->updateProgress(info);
}
Basefind::Basefind(CutterCore *core)
: core(core),
scores(nullptr),
continue_run(true)
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
,
mutex(QMutex::Recursive)
#endif
{
memset(&options, 0, sizeof(RzBaseFindOpt));
}
Basefind::~Basefind()
{
cancel();
wait();
rz_list_free(scores);
}
bool Basefind::setOptions(const RzBaseFindOpt *opts)
{
mutex.lock();
options.max_threads = opts->max_threads;
options.pointer_size = opts->pointer_size;
options.start_address = opts->start_address;
options.end_address = opts->end_address;
options.alignment = opts->alignment;
options.min_score = opts->min_score;
options.min_string_len = opts->min_string_len;
mutex.unlock();
if (options.start_address >= options.end_address) {
qWarning() << "Start address is >= end address";
return false;
} else if (options.alignment < RZ_BASEFIND_BASE_ALIGNMENT) {
qWarning() << "Alignment must be at least "
<< QString::asprintf("0x%x", RZ_BASEFIND_BASE_ALIGNMENT);
return false;
} else if (options.min_score < 1) {
qWarning() << "Min score must be at least 1";
return false;
} else if (options.min_string_len < 1) {
qWarning() << "Min string length must be at least 1";
return false;
}
return true;
}
void Basefind::run()
{
qRegisterMetaType<BasefindCoreStatusDescription>();
mutex.lock();
rz_list_free(scores);
scores = nullptr;
continue_run = true;
mutex.unlock();
core->coreMutex.lock();
options.callback = threadCallback;
options.user = this;
scores = rz_basefind(core->core_, &options);
core->coreMutex.unlock();
emit complete();
}
void Basefind::cancel()
{
mutex.lock();
continue_run = false;
mutex.unlock();
}
QList<BasefindResultDescription> Basefind::results()
{
QList<BasefindResultDescription> pairs;
RzListIter *it;
RzBaseFindScore *pair;
CutterRzListForeach (scores, it, RzBaseFindScore, pair) {
BasefindResultDescription desc;
desc.candidate = pair->candidate;
desc.score = pair->score;
pairs.push_back(desc);
}
return pairs;
}
bool Basefind::updateProgress(const RzBaseFindThreadInfo *info)
{
mutex.lock();
BasefindCoreStatusDescription status;
status.index = info->thread_idx;
status.percentage = info->percentage;
emit progress(status);
bool ret = continue_run;
mutex.unlock();
return ret;
}

47
src/core/Basefind.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef CUTTER_BASEFIND_CORE_H
#define CUTTER_BASEFIND_CORE_H
#include <QThread>
#include <QMutex>
#include "Cutter.h"
#include "CutterDescriptions.h"
#include <rz_basefind.h>
class CutterCore;
class Basefind : public QThread
{
Q_OBJECT
public:
explicit Basefind(CutterCore *core);
virtual ~Basefind();
void run();
bool setOptions(const RzBaseFindOpt *opts);
QList<BasefindResultDescription> results();
public slots:
void cancel();
signals:
void progress(BasefindCoreStatusDescription status);
void complete();
private:
CutterCore *const core;
RzList *scores;
bool continue_run;
RzBaseFindOpt options;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QMutex mutex;
#else
QRecursiveMutex mutex;
#endif
bool updateProgress(const RzBaseFindThreadInfo *info);
static bool threadCallback(const RzBaseFindThreadInfo *info, void *user);
};
#endif // CUTTER_BASEFIND_CORE_H

View File

@ -4577,3 +4577,9 @@ void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGr
}
}
}
bool CutterCore::rebaseBin(RVA base_address)
{
CORE_LOCK();
return rz_core_bin_rebase(core, base_address);
}

View File

@ -4,6 +4,7 @@
#include "core/CutterCommon.h"
#include "core/CutterDescriptions.h"
#include "core/CutterJson.h"
#include "core/Basefind.h"
#include "common/BasicInstructionHighlighter.h"
#include <QMap>
@ -69,6 +70,7 @@ class CUTTER_EXPORT CutterCore : public QObject
friend class RzCoreLocked;
friend class RizinTask;
friend class Basefind;
public:
explicit CutterCore(QObject *parent = nullptr);
@ -556,6 +558,8 @@ public:
void setGraphEmpty(bool empty);
bool isGraphEmpty();
bool rebaseBin(RVA base_address);
void getRegs();
QList<QString> regs;
void setSettings();

View File

@ -385,6 +385,18 @@ struct Arena
ut64 max_system_mem;
};
struct BasefindCoreStatusDescription
{
size_t index;
ut32 percentage;
};
struct BasefindResultDescription
{
RVA candidate;
ut32 score;
};
Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription)
@ -423,5 +435,7 @@ Q_DECLARE_METATYPE(BreakpointDescription::PositionType)
Q_DECLARE_METATYPE(ProcessDescription)
Q_DECLARE_METATYPE(RefDescription)
Q_DECLARE_METATYPE(VariableDescription)
Q_DECLARE_METATYPE(BasefindCoreStatusDescription)
Q_DECLARE_METATYPE(BasefindResultDescription)
#endif // DESCRIPTIONS_H

View File

@ -115,6 +115,9 @@
#include <QGraphicsScene>
#include <QGraphicsView>
// Tools
#include "tools/basefind/BaseFindDialog.h"
#define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)")
template<class T>
@ -1087,11 +1090,11 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr
memoryWidget = new DecompilerWidget(this);
break;
case MemoryWidgetType::CallGraph:
memoryWidget = new CallGraphWidget(this, false);
break;
memoryWidget = new CallGraphWidget(this, false);
break;
case MemoryWidgetType::GlobalCallGraph:
memoryWidget = new CallGraphWidget(this, true);
break;
memoryWidget = new CallGraphWidget(this, true);
break;
}
auto seekable = memoryWidget->getSeekable();
seekable->setSynchronization(synchronized);
@ -1637,6 +1640,12 @@ void MainWindow::on_actionTabs_triggered()
setTabLocation();
}
void MainWindow::on_actionBaseFind_triggered()
{
auto dialog = new BaseFindDialog(this);
dialog->show();
}
void MainWindow::on_actionAbout_triggered()
{
AboutDialog *a = new AboutDialog(this);
@ -1774,12 +1783,10 @@ void MainWindow::on_actionExport_as_code_triggered()
return;
}
auto string = fromOwned(
dialog.selectedNameFilter() != instructionsInComments
? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])
: rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size));
dialog.selectedNameFilter() != instructionsInComments
? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])
: rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size));
fileOut << string.get();
}

View File

@ -152,6 +152,7 @@ public slots:
void toggleOverview(bool visibility, GraphWidget *targetGraph);
private slots:
void on_actionBaseFind_triggered();
void on_actionAbout_triggered();
void on_actionIssue_triggered();
void documentationClicked();

View File

@ -128,6 +128,12 @@
<addaction name="actionSaveLayout"/>
<addaction name="menuLayouts"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Tools</string>
</property>
<addaction name="actionBaseFind"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
@ -184,6 +190,7 @@
<addaction name="menuView"/>
<addaction name="menuWindows"/>
<addaction name="menuDebug"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
@ -233,6 +240,11 @@
<string>Zen mode</string>
</property>
</action>
<action name="actionBaseFind">
<property name="text">
<string>BaseFind</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>

View File

@ -1,6 +1,8 @@
#include "CommentsDialog.h"
#include "ui_CommentsDialog.h"
#include <QErrorMessage>
#include "core/Cutter.h"
CommentsDialog::CommentsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CommentsDialog)

View File

@ -14,6 +14,7 @@
#include "core/Cutter.h"
#include "common/AnalysisTask.h"
#include "CutterApplication.h"
InitialOptionsDialog::InitialOptionsDialog(MainWindow *main)
: QDialog(nullptr), // parent must not be main
@ -179,9 +180,56 @@ void InitialOptionsDialog::loadOptions(const InitialOptions &options)
ui->entry_loadOffset->setText(RzAddressString(options.binLoadAddr));
}
if (options.mapAddr != RVA_INVALID) {
ui->entry_mapOffset->setText(RzAddressString(options.mapAddr));
}
ui->vaCheckBox->setChecked(options.useVA);
ui->writeCheckBox->setChecked(options.writeEnabled);
// TODO: all other options should also be applied to the ui
if (!options.arch.isNull() && !options.arch.isEmpty()) {
ui->archComboBox->setCurrentText(options.arch);
}
if (!options.cpu.isNull() && !options.cpu.isEmpty()) {
ui->cpuComboBox->setCurrentText(options.cpu);
}
if (options.bits > 0) {
ui->bitsComboBox->setCurrentText(QString::asprintf("%d", options.bits));
}
if (!options.os.isNull() && !options.os.isEmpty()) {
ui->kernelComboBox->setCurrentText(options.os);
}
if (!options.forceBinPlugin.isNull() && !options.forceBinPlugin.isEmpty()) {
ui->formatComboBox->setCurrentText(options.forceBinPlugin);
}
if (!options.loadBinInfo) {
ui->binCheckBox->setChecked(false);
}
ui->writeCheckBox->setChecked(options.writeEnabled);
switch (options.endian) {
case InitialOptions::Endianness::Little:
ui->endiannessComboBox->setCurrentIndex(1);
break;
case InitialOptions::Endianness::Big:
ui->endiannessComboBox->setCurrentIndex(2);
break;
default:
break;
}
ui->demangleCheckBox->setChecked(options.demangle);
if (!options.pdbFile.isNull() && !options.pdbFile.isEmpty()) {
ui->pdbCheckBox->setChecked(true);
ui->pdbLineEdit->setText(options.pdbFile);
}
}
void InitialOptionsDialog::setTooltipWithConfigHelp(QWidget *w, const char *config)
@ -246,7 +294,7 @@ QList<CommandDescription> InitialOptionsDialog::getSelectedAdvancedAnalCmds() co
return advanced;
}
void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList<QString> advanced*/)
void InitialOptionsDialog::setupAndStartAnalysis()
{
InitialOptions options;
@ -322,6 +370,8 @@ void InitialOptionsDialog::setupAndStartAnalysis(/*int level, QList<QString> adv
Core()->getAsyncTaskManager()->start(analysisTaskPtr);
done(0);
static_cast<CutterApplication *>(qApp)->setInitialOptions(options);
}
void InitialOptionsDialog::on_okButton_clicked()

View File

@ -20,7 +20,7 @@ public:
explicit InitialOptionsDialog(MainWindow *main);
~InitialOptionsDialog();
void setupAndStartAnalysis(/*int level, QList<QString> advanced*/);
void setupAndStartAnalysis();
private slots:
void on_okButton_clicked();

View File

@ -0,0 +1,97 @@
#include "BaseFindDialog.h"
#include "ui_BaseFindDialog.h"
#include "BaseFindSearchDialog.h"
#include <core/Cutter.h>
#include <rz_th.h>
BaseFindDialog::BaseFindDialog(QWidget *parent) : QDialog(parent), ui(new Ui::BaseFindDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
// Fill in N-thread Combo
size_t n_cores = rz_th_physical_core_number();
ui->nCoresCombo->clear();
for (size_t i = n_cores; i > 0; i--) {
if (n_cores == i) {
ui->nCoresCombo->addItem("All Cores");
continue;
}
ui->nCoresCombo->addItem(QString::number(i));
}
ui->startAddressEdit->setText(Core()->getConfig("basefind.search.start"));
ui->endAddressEdit->setText(Core()->getConfig("basefind.search.end"));
ui->alignmentEdit->setText(Core()->getConfig("basefind.alignment"));
ui->minStrLenEdit->setValue(Core()->getConfigut64("basefind.min.string"));
ui->minScoreEdit->setValue(Core()->getConfigut64("basefind.min.score"));
size_t selected_n_cores = Core()->getConfigut64("basefind.max.threads");
if (selected_n_cores < n_cores && selected_n_cores > 0) {
ui->nCoresCombo->setCurrentIndex(n_cores - selected_n_cores);
}
}
BaseFindDialog::~BaseFindDialog() {}
size_t BaseFindDialog::getNCores() const
{
size_t n_cores = rz_th_physical_core_number();
return n_cores - ui->nCoresCombo->currentIndex();
}
ut32 BaseFindDialog::getPointerSize() const
{
auto index = ui->pointerSizeCombo->currentIndex();
QString value = ui->pointerSizeCombo->itemText(index);
return value.toULong(nullptr, 0);
}
RVA BaseFindDialog::getStartAddress() const
{
QString value = ui->startAddressEdit->text();
return value.toULongLong(nullptr, 0);
}
RVA BaseFindDialog::getEndAddress() const
{
QString value = ui->endAddressEdit->text();
return value.toULongLong(nullptr, 0);
}
RVA BaseFindDialog::getAlignment() const
{
QString value = ui->alignmentEdit->text();
return value.toULongLong(nullptr, 0);
}
ut32 BaseFindDialog::getMinStrLen() const
{
return ui->minStrLenEdit->value();
}
ut32 BaseFindDialog::getMinScore() const
{
return ui->minScoreEdit->value();
}
void BaseFindDialog::on_buttonBox_accepted()
{
RzBaseFindOpt options = {};
options.max_threads = getNCores();
options.pointer_size = getPointerSize();
options.start_address = getStartAddress();
options.end_address = getEndAddress();
options.alignment = getAlignment();
options.min_score = getMinScore();
options.min_string_len = getMinStrLen();
options.callback = nullptr;
options.user = nullptr;
BaseFindSearchDialog *bfs = new BaseFindSearchDialog(parentWidget());
bfs->show(&options);
}
void BaseFindDialog::on_buttonBox_rejected() {}

View File

@ -0,0 +1,38 @@
#ifndef BASEFIND_DIALOG_H
#define BASEFIND_DIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <memory>
#include <core/Cutter.h>
namespace Ui {
class BaseFindDialog;
}
class BaseFindDialog : public QDialog
{
Q_OBJECT
public:
explicit BaseFindDialog(QWidget *parent = nullptr);
~BaseFindDialog();
size_t getNCores() const;
ut32 getPointerSize() const;
RVA getStartAddress() const;
RVA getEndAddress() const;
RVA getAlignment() const;
ut32 getMinStrLen() const;
ut32 getMinScore() const;
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
private:
std::unique_ptr<Ui::BaseFindDialog> ui;
};
#endif // BASEFIND_DIALOG_H

View File

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseFindDialog</class>
<widget class="QDialog" name="BaseFindDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>373</width>
<height>348</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">BaseFind</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="nCoresLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cores:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="nCoresCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="pointerSizeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Pointer Size:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="pointerSizeCombo">
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="startAddressLabel">
<property name="text">
<string>Start Address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="startAddressEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="endAddressLabel">
<property name="text">
<string>End Address:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="endAddressEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="alignmentLabel">
<property name="text">
<string>Alignment:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="alignmentEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="minStrLenLabel">
<property name="text">
<string>Min String Length:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="minStrLenEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>4</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="minScoreLabel">
<property name="text">
<string>Min Score:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="minScoreEdit">
<property name="maximumSize">
<size>
<width>382</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<action name="actionRemoveItem">
<property name="text">
<string>Remove item</string>
</property>
</action>
<action name="actionRemoveAll">
<property name="text">
<string>Remove all</string>
</property>
<property name="toolTip">
<string>Remove all</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BaseFindDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>234</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BaseFindDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,161 @@
#include "BaseFindResultsDialog.h"
#include "ui_BaseFindResultsDialog.h"
#include <QClipboard>
#include <QMessageBox>
#include <core/Cutter.h>
#include <CutterApplication.h>
BaseFindResultsModel::BaseFindResultsModel(QList<BasefindResultDescription> *list, QObject *parent)
: QAbstractListModel(parent), list(list)
{
}
int BaseFindResultsModel::rowCount(const QModelIndex &) const
{
return list->count();
}
int BaseFindResultsModel::columnCount(const QModelIndex &) const
{
return BaseFindResultsModel::ColumnCount;
}
QVariant BaseFindResultsModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= list->count())
return QVariant();
const BasefindResultDescription &entry = list->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case ScoreColumn:
return QString::asprintf("%u", entry.score);
case CandidateColumn:
return QString::asprintf("%#010llx", entry.candidate);
default:
return QVariant();
}
case Qt::ToolTipRole: {
return QString::asprintf("%#010llx", entry.candidate);
}
default:
return QVariant();
}
}
QVariant BaseFindResultsModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case ScoreColumn:
return tr("Score");
case CandidateColumn:
return tr("Address");
default:
return QVariant();
}
default:
return QVariant();
}
}
BaseFindResultsDialog::BaseFindResultsDialog(QList<BasefindResultDescription> results,
QWidget *parent)
: QDialog(parent), list(results), ui(new Ui::BaseFindResultsDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
model = new BaseFindResultsModel(&list, this);
ui->tableView->setModel(model);
ui->tableView->sortByColumn(BaseFindResultsModel::ScoreColumn, Qt::AscendingOrder);
ui->tableView->verticalHeader()->hide();
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
blockMenu = new QMenu(this);
actionCopyCandidate = new QAction(tr("Copy %1"), this);
actionSetLoadAddr = new QAction(tr("Reopen Cutter with base address as %1"), this);
actionSetMapAddr = new QAction(tr("Reopen Cutter with map address as %1"), this);
connect(ui->tableView, &QWidget::customContextMenuRequested, this,
&BaseFindResultsDialog::showItemContextMenu);
connect(actionCopyCandidate, &QAction::triggered, this,
&BaseFindResultsDialog::onActionCopyLine);
connect(actionSetLoadAddr, &QAction::triggered, this,
&BaseFindResultsDialog::onActionSetLoadAddr);
connect(actionSetMapAddr, &QAction::triggered, this,
&BaseFindResultsDialog::onActionSetMapAddr);
blockMenu->addAction(actionSetLoadAddr);
blockMenu->addAction(actionSetMapAddr);
blockMenu->addAction(actionCopyCandidate);
addActions(blockMenu->actions());
}
void BaseFindResultsDialog::showItemContextMenu(const QPoint &pt)
{
auto index = ui->tableView->currentIndex();
if (index.isValid()) {
const BasefindResultDescription &entry = list.at(index.row());
candidate = entry.candidate;
auto addr = QString::asprintf("%#010llx", candidate);
actionCopyCandidate->setText(tr("Copy %1").arg(addr));
actionSetLoadAddr->setText(tr("Reopen Cutter with base address as %1").arg(addr));
actionSetMapAddr->setText(tr("Reopen Cutter with map address as %1").arg(addr));
blockMenu->exec(this->mapToGlobal(pt));
}
}
void BaseFindResultsDialog::onActionCopyLine()
{
auto clipboard = QApplication::clipboard();
clipboard->setText(QString::asprintf("%#010llx", candidate));
}
void BaseFindResultsDialog::onActionSetLoadAddr()
{
auto cutter = static_cast<CutterApplication *>(qApp);
auto options = cutter->getInitialOptions();
auto oldValue = options.binLoadAddr;
// override options to generate correct args
options.binLoadAddr = candidate;
cutter->setInitialOptions(options);
auto args = cutter->getArgs();
// revert back options
options.binLoadAddr = oldValue;
cutter->setInitialOptions(options);
cutter->launchNewInstance(args);
}
void BaseFindResultsDialog::onActionSetMapAddr()
{
auto cutter = static_cast<CutterApplication *>(qApp);
auto options = cutter->getInitialOptions();
auto oldValue = options.mapAddr;
// override options to generate correct args
options.mapAddr = candidate;
cutter->setInitialOptions(options);
auto args = cutter->getArgs();
// revert back options
options.mapAddr = oldValue;
cutter->setInitialOptions(options);
cutter->launchNewInstance(args);
}
BaseFindResultsDialog::~BaseFindResultsDialog() {}
void BaseFindResultsDialog::on_buttonBox_rejected() {}

View File

@ -0,0 +1,68 @@
#ifndef BASEFIND_RESULTS_DIALOG_H
#define BASEFIND_RESULTS_DIALOG_H
#include <QDialog>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <memory>
#include <core/Cutter.h>
class BaseFindResultsDialog;
namespace Ui {
class BaseFindResultsDialog;
}
class BaseFindResultsModel : public QAbstractListModel
{
Q_OBJECT
friend BaseFindResultsDialog;
public:
enum Column { ScoreColumn = 0, CandidateColumn, ColumnCount };
BaseFindResultsModel(QList<BasefindResultDescription> *list, QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
private:
QList<BasefindResultDescription> *list;
};
class BaseFindResultsDialog : public QDialog
{
Q_OBJECT
public:
explicit BaseFindResultsDialog(QList<BasefindResultDescription> results,
QWidget *parent = nullptr);
~BaseFindResultsDialog();
public slots:
void showItemContextMenu(const QPoint &pt);
private slots:
void on_buttonBox_rejected();
private:
void onActionCopyLine();
void onActionSetLoadAddr();
void onActionSetMapAddr();
QList<BasefindResultDescription> list;
std::unique_ptr<Ui::BaseFindResultsDialog> ui;
BaseFindResultsModel *model;
QMenu *blockMenu;
QAction *actionCopyCandidate;
QAction *actionSetLoadAddr;
QAction *actionSetMapAddr;
RVA candidate;
};
#endif // BASEFIND_RESULTS_DIALOG_H

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseFindResultsDialog</class>
<widget class="QDialog" name="BaseFindResultsDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>582</width>
<height>382</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">BaseFind Results</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QTableView" name="tableView"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BaseFindResultsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,66 @@
#include "BaseFindSearchDialog.h"
#include "ui_BaseFindSearchDialog.h"
#include "BaseFindResultsDialog.h"
#include <QLabel>
#include <QFormLayout>
#include <core/Cutter.h>
#include <rz_th.h>
BaseFindSearchDialog::BaseFindSearchDialog(QWidget *parent)
: QDialog(parent), basefind(new Basefind(Core())), ui(new Ui::BaseFindSearchDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
}
BaseFindSearchDialog::~BaseFindSearchDialog() {}
void BaseFindSearchDialog::show(RzBaseFindOpt *opts)
{
size_t n_cores = rz_th_physical_core_number();
if (opts->max_threads > n_cores || opts->max_threads < 1) {
opts->max_threads = n_cores;
}
QFormLayout *layout = new QFormLayout();
ui->scrollAreaWidgetContents->setLayout(layout);
for (ut32 i = 0; i < opts->max_threads; ++i) {
QString label = QString::asprintf("Core %u", i);
QProgressBar *pbar = new QProgressBar(nullptr);
layout->addRow(label, pbar);
pbar->setRange(0, 100);
bars.push_back(pbar);
}
if (!basefind->setOptions(opts)) {
return;
}
connect(this, &BaseFindSearchDialog::cancelSearch, basefind.get(), &Basefind::cancel);
connect(basefind.get(), &Basefind::progress, this, &BaseFindSearchDialog::onProgress);
connect(basefind.get(), &Basefind::complete, this, &BaseFindSearchDialog::onCompletion);
basefind->start();
this->QDialog::show();
}
void BaseFindSearchDialog::onProgress(BasefindCoreStatusDescription status)
{
bars[status.index]->setValue(status.percentage);
}
void BaseFindSearchDialog::onCompletion()
{
auto results = basefind->results();
BaseFindResultsDialog *table = new BaseFindResultsDialog(results, parentWidget());
table->show();
this->close();
}
void BaseFindSearchDialog::on_buttonBox_rejected()
{
emit cancelSearch();
}

View File

@ -0,0 +1,41 @@
#ifndef BASEFIND_SEARCH_DIALOG_H
#define BASEFIND_SEARCH_DIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <QProgressBar>
#include <memory>
#include <core/Cutter.h>
namespace Ui {
class BaseFindSearchDialog;
}
class BaseFindSearchDialog : public QDialog
{
Q_OBJECT
public:
explicit BaseFindSearchDialog(QWidget *parent = nullptr);
~BaseFindSearchDialog();
void show(RzBaseFindOpt *opts);
public slots:
void onProgress(BasefindCoreStatusDescription status);
void onCompletion();
signals:
void cancelSearch();
private slots:
void on_buttonBox_rejected();
private:
std::vector<QProgressBar *> bars;
std::unique_ptr<Basefind> basefind;
std::unique_ptr<Ui::BaseFindSearchDialog> ui;
};
#endif // BASEFIND_SEARCH_DIALOG_H

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseFindSearchDialog</class>
<widget class="QDialog" name="BaseFindSearchDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>582</width>
<height>382</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Searching for base address</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>564</width>
<height>320</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<action name="actionRemoveItem">
<property name="text">
<string>Remove item</string>
</property>
</action>
<action name="actionRemoveAll">
<property name="text">
<string>Remove all</string>
</property>
<property name="toolTip">
<string>Remove all</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BaseFindSearchDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>