Add new Analysis section to the Preferences widget (#2332)

This commit is contained in:
Oriol Castejón 2020-08-10 18:12:38 +02:00 committed by GitHub
parent 724d3f26b4
commit 9e346275c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 402 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -7,6 +7,7 @@ Most configuration is done using the Preferences dialog. It can be opened by cli
:maxdepth: 1
:glob:
preferences/analysis
preferences/initialization-script
preferences/layout
preferences/*

View File

@ -0,0 +1,74 @@
Analysis Options
================
Cutter will use the underlying radare2 analysis options to analyze a binary. These options are usually
configured when the binary is first loaded. However, they can be later changed using the Analysis
dialog, and a new analysis that takes these options into account can then be performed.
Analysis Dialog
---------------
.. image:: ../../images/analysis_dialog.png
:alt: Analysis dialog
**Description:** The Analysis dialog allows setting some radare2's analysis options. The supported options are described
below.
Clicking on the "Analyze Program" button will trigger an analysis of the current binary with radare2's ``aaa``
command, taking into account the configured values of the analysis options.
**Steps to open:** ``Edit -> Preferences -> Analysis``
Speculatively set a name for the functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Try to name functions without symbols by using artifacts in the functions such as API calls and strings.
**Configuration variable:** ``anal.autoname``
Search for new functions following already defined functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cutter will check if there is a candidate for a new function following an already defined one, as the compiler usually
state them together. This option is taking the advantages of both Recursive Analysis and Linear Sweep into some kind of a hybrid mode. For each discovered function, the analysis will try to check for a function-prologue, usually following a gap of null bytes or ``cc`` bytes. This will help with discovering functions which are not referenced in the program. As such, it will make the analysis slower and prone to false-positives.
**Configuration variable:** ``anal.hasnext``
Create references for unconditional jumps
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When encountering unconditional jumps during the analysis, Cutter will add a code-reference even if it is not guaranteed
that the jump will take place.
**Configuration variable:** ``anal.jmp.ref``
Analyze jump tables in switch statements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When encountering switch statements during analysis, continue and analyze the target blocks of the jump tables.
**Configuration variable:** ``anal.jmp.tbl``
Analyze ``push`` + ``ret`` as ``jmp``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When performing analysis of a function, treat the sequence of ``push`` followed by ``ret`` instruction as a ``jmp``.
This can help Cutter to continue the analysis to target of the ``jmp``.
**Configuration variable:** ``anal.pushret``
Show verbose information when performing analysis
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When enabled, Cutter will print warnings it encountered while preforming analysis. These warnings can help the user
understand if anything went wrong in the analysis. This function is not only helpful when trying to perform a full
analysis of the program, but also when trying to analyze and define new functions.
**Configuration variable:** ``anal.verbose``
Verbose output from type analysis
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Print warnings encountered while preforming type analysis. These warnings can help the user understand if anything went
wrong in the analysis.
**Configuration variable:** ``anal.types.verbose``

View File

@ -435,7 +435,8 @@ SOURCES += \
widgets/SimpleTextGraphView.cpp \
widgets/R2GraphWidget.cpp \
widgets/CallGraph.cpp \
widgets/AddressableDockWidget.cpp
widgets/AddressableDockWidget.cpp \
dialogs/preferences/AnalOptionsWidget.cpp
GRAPHVIZ_SOURCES = \
widgets/GraphvizLayout.cpp
@ -592,7 +593,8 @@ HEADERS += \
widgets/SimpleTextGraphView.h \
widgets/R2GraphWidget.h \
widgets/CallGraph.h \
widgets/AddressableDockWidget.h
widgets/AddressableDockWidget.h \
dialogs/preferences/AnalOptionsWidget.h
GRAPHVIZ_HEADERS = widgets/GraphvizLayout.h
@ -660,7 +662,8 @@ FORMS += \
dialogs/preferences/ColorThemeEditDialog.ui \
widgets/ListDockWidget.ui \
dialogs/LayoutManager.ui \
widgets/R2GraphWidget.ui
widgets/R2GraphWidget.ui \
dialogs/preferences/AnalOptionsWidget.ui
RESOURCES += \
resources.qrc \

View File

@ -21,11 +21,17 @@ void AnalTask::interrupt()
r_cons_singleton()->context->breaked = true;
}
QString AnalTask::getTitle() {
// If no file is loaded we consider it's Initial Analysis
QJsonArray openedFiles = Core()->getOpenedFiles();
if (!openedFiles.size()) {
return tr("Initial Analysis");
}
return tr("Analyzing Program");
}
void AnalTask::runTask()
{
log(tr("Loading the file..."));
openFailed = false;
int perms = R_PERM_RX;
if (options.writeEnabled) {
perms |= R_PERM_W;
@ -39,6 +45,8 @@ void AnalTask::runTask()
// Do not reload the file if already loaded
QJsonArray openedFiles = Core()->getOpenedFiles();
if (!openedFiles.size() && options.filename.length()) {
log(tr("Loading the file..."));
openFailed = false;
bool fileLoaded = Core()->loadFile(options.filename,
options.binLoadAddr,
options.mapAddr,

View File

@ -17,7 +17,7 @@ public:
explicit AnalTask();
~AnalTask();
QString getTitle() override { return tr("Initial Analysis"); }
QString getTitle() override;
void setOptions(const InitialOptions &options) { this->options = options; }

View File

@ -2,6 +2,7 @@
#include "ui_MainWindow.h"
// Common Headers
#include "common/AnalTask.h"
#include "common/BugReporting.h"
#include "common/Highlighter.h"
#include "common/Helpers.h"
@ -243,6 +244,10 @@ void MainWindow::initUI()
enableDebugWidgetsMenu(false);
readSettings();
// Display tooltip for the Analyze Program action
ui->actionAnalyze->setToolTip("Analyze the program using radare2's \"aaa\" command");
ui->menuFile->setToolTipsVisible(true);
}
void MainWindow::initToolBar()
@ -1579,9 +1584,24 @@ void MainWindow::on_actionRefresh_Panels_triggered()
this->refreshAll();
}
/**
* @brief A signal that creates an AsyncTask to re-analyze the current file
*/
void MainWindow::on_actionAnalyze_triggered()
{
// TODO: implement this, but do NOT open InitialOptionsDialog!!
auto *analTask = new AnalTask();
InitialOptions options;
options.analCmd = { {"aaa", "Auto analysis"} };
analTask->setOptions(options);
AsyncTask::Ptr analTaskPtr(analTask);
auto *taskDialog = new AsyncTaskDialog(analTaskPtr);
taskDialog->setInterruptOnClose(true);
taskDialog->setAttribute(Qt::WA_DeleteOnClose);
taskDialog->show();
connect(analTask, &AnalTask::finished, this, &MainWindow::refreshAll);
Core()->getAsyncTaskManager()->start(analTaskPtr);
}
void MainWindow::on_actionImportPDB_triggered()

View File

@ -150,6 +150,8 @@ public slots:
void on_actionTabs_triggered();
void on_actionAnalyze_triggered();
void lockUnlock_Docks(bool what);
void on_actionRun_Script_triggered();
@ -192,8 +194,6 @@ private slots:
void on_actionPreferences_triggered();
void on_actionAnalyze_triggered();
void on_actionImportPDB_triggered();
void on_actionExport_as_code_triggered();

View File

@ -75,6 +75,7 @@
<addaction name="actionMap"/>
<addaction name="separator"/>
<addaction name="actionImportPDB"/>
<addaction name="actionAnalyze"/>
<addaction name="separator"/>
<addaction name="menuSetMode"/>
<addaction name="actionCommitChanges"/>
@ -741,7 +742,7 @@
</action>
<action name="actionAnalyze">
<property name="text">
<string>Analyze</string>
<string>Analyze Program</string>
</property>
</action>
<action name="actionExport_as_code">

View File

@ -0,0 +1,52 @@
#include "AnalOptionsWidget.h"
#include "ui_AnalOptionsWidget.h"
#include "PreferencesDialog.h"
#include "common/Helpers.h"
#include "common/Configuration.h"
#include "core/MainWindow.h"
AnalOptionsWidget::AnalOptionsWidget(PreferencesDialog *dialog)
: QDialog(dialog),
ui(new Ui::AnalOptionsWidget)
{
ui->setupUi(this);
checkboxes = {
{ ui->autonameCheckbox, "anal.autoname" },
{ ui->hasnextCheckbox, "anal.hasnext" },
{ ui->jmpRefCheckbox, "anal.jmp.ref" },
{ ui->jmpTblCheckbox, "anal.jmp.tbl" },
{ ui->pushRetCheckBox, "anal.pushret" },
{ ui->typesVerboseCheckBox, "anal.types.verbose" },
{ ui->verboseCheckBox, "anal.verbose" }
};
// Connect each checkbox from "checkboxes" to the generic signal "checkboxEnabler"
for (ConfigCheckbox &confCheckbox : checkboxes) {
QString val = confCheckbox.config;
QCheckBox &cb = *confCheckbox.checkBox;
connect(confCheckbox.checkBox, &QCheckBox::stateChanged, this, [this, val, &cb]() { checkboxEnabler(&cb, val); });
}
ui->analyzePushButton->setToolTip("Analyze the program using radare2's \"aaa\" command");
auto *mainWindow = new MainWindow(this);
connect(ui->analyzePushButton, &QPushButton::clicked, mainWindow, &MainWindow::on_actionAnalyze_triggered);
updateAnalOptionsFromVars();
}
AnalOptionsWidget::~AnalOptionsWidget() {}
void AnalOptionsWidget::checkboxEnabler(QCheckBox *checkBox, const QString &config)
{
Config()->setConfig(config, checkBox->isChecked());
}
void AnalOptionsWidget::updateAnalOptionsFromVars()
{
for (ConfigCheckbox &confCheckbox : checkboxes) {
qhelpers::setCheckedWithoutSignals(confCheckbox.checkBox, Core()->getConfigb(confCheckbox.config));
}
}

View File

@ -0,0 +1,46 @@
#ifndef ANALOPTIONSWIDGET_H
#define ANALOPTIONSWIDGET_H
#include <QDialog>
#include <memory>
#include "core/Cutter.h"
class PreferencesDialog;
namespace Ui {
class AnalOptionsWidget;
}
class AnalOptionsWidget : public QDialog
{
Q_OBJECT
public:
explicit AnalOptionsWidget(PreferencesDialog *dialog);
~AnalOptionsWidget();
private:
std::unique_ptr<Ui::AnalOptionsWidget> ui;
struct ConfigCheckbox {
QCheckBox *checkBox;
QString config;
};
QList<ConfigCheckbox> checkboxes;
private slots:
/**
* @brief A slot to display the options in the dialog according to the current anal.* configuration
*/
void updateAnalOptionsFromVars();
/**
* @brief A generic slot to handle the simple cases where a checkbox is toggled
* while it's only responsible for a single independent boolean configuration eval.
* @param checkBox - The checkbox which is responsible for the signal
* @param config - the configuration string to be toggled
*/
static void checkboxEnabler(QCheckBox *checkbox, const QString &config);
};
#endif // ANALOPTIONSWIDGET_H

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AnalOptionsWidget</class>
<widget class="QWidget" name="AnalOptionsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>633</width>
<height>689</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Analysis</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>611</width>
<height>610</height>
</rect>
</property>
<widget class="QCheckBox" name="verboseCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>190</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Show verbose information when performing analysis (anal.verbose)</string>
</property>
</widget>
<widget class="QCheckBox" name="pushRetCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Analyze push+ret as jmp (anal.pushret)</string>
</property>
</widget>
<widget class="QCheckBox" name="typesVerboseCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Verbose output from type analysis (anal.types.verbose)</string>
</property>
</widget>
<widget class="QCheckBox" name="autonameCheckbox">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Speculatively set a name for the functions (anal.autoname)</string>
</property>
</widget>
<widget class="QCheckBox" name="hasnextCheckbox">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Search for new functions following already defined functions (anal.hasnext)</string>
</property>
</widget>
<widget class="QCheckBox" name="jmpRefCheckbox">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Create references for unconditional jumps (anal.jmp.ref)</string>
</property>
</widget>
<widget class="QCheckBox" name="jmpTblCheckbox">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>601</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Analyze jump tables in switch statements (anal.jmp.tbl)</string>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</item>
<item alignment="Qt::AlignRight">
<widget class="QPushButton" name="analyzePushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Analyze program</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -7,6 +7,7 @@
#include "DebugOptionsWidget.h"
#include "PluginsOptionsWidget.h"
#include "InitializationFileEditor.h"
#include "AnalOptionsWidget.h"
#include "PreferenceCategory.h"
@ -57,6 +58,11 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)
tr("Initialization Script"),
new InitializationFileEditor(this),
QIcon(":/img/icons/initialization.svg")
},
{
tr("Analysis"),
new AnalOptionsWidget(this),
QIcon(":/img/icons/cog_light.svg")
}
};
@ -119,6 +125,7 @@ void PreferencesDialog::chooseThemeIcons()
{ QStringLiteral("Appearance"), QStringLiteral("polar.svg") },
{ QStringLiteral("Plugins"), QStringLiteral("plugins.svg") },
{ QStringLiteral("Initialization Script"), QStringLiteral("initialization.svg") },
{ QStringLiteral("Analysis"), QStringLiteral("cog_light.svg") },
};
QList<QPair<void*, QString>> supportedIconsNames;

View File

@ -0,0 +1,4 @@
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg style="enable-background:new 0 0 32 32" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="32px" width="32px" version="1.1" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">
<path d="m32 18v-4l-4.8-2c-0.13-0.38-0.27-0.74-0.44-1.1l1.9-4.8-3-3-4.8 2c-0.36-0.18-0.73-0.32-1.1-0.46l-2-4.7h-4l-2 4.7c-0.4 0.14-0.78 0.29-1.2 0.47l-4.8-1.9-3 2.8 1.9 4.7c-0.1 0-0.3 1-0.4 1l-4.7 2v4l4.7 2c0.14 0.41 0.3 0.8 0.49 1.2l-1.9 4.7 2.8 2.8 4.7-1.9c0.38 0.18 0.77 0.32 1.2 0.46l2 5h4l2-4.8c0.38-0.14 0.75-0.29 1.1-0.46l4.8 1.9 2.8-2.8-2-4.8c0.17-0.36 0.3-0.72 0.44-1.1l5-2zm-16 4c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-3 6-6 6z" fill="#039be5"/>
</svg>

After

Width:  |  Height:  |  Size: 785 B

View File

@ -63,6 +63,7 @@
<file>img/icons/bug.svg</file>
<file>img/icons/light/bug.svg</file>
<file>img/icons/cog_light.svg</file>
<file>img/icons/light/cog_light.svg</file>
<file>img/icons/cog_white.svg</file>
<file>img/icons/new_light.svg</file>
<file>img/icons/run_light.svg</file>