Add a generic Decompiler interface and remove pdc (#1659)

This commit is contained in:
Florian Märkl 2019-07-15 14:08:44 +02:00 committed by GitHub
parent 736d3e1f1d
commit 4487564f7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 202 additions and 117 deletions

View File

@ -349,7 +349,8 @@ SOURCES += \
common/HighDpiPixmap.cpp \
widgets/GraphGridLayout.cpp \
widgets/HexWidget.cpp \
common/SelectionHighlight.cpp
common/SelectionHighlight.cpp \
common/Decompiler.cpp
HEADERS += \
core/Cutter.h \
@ -476,7 +477,8 @@ HEADERS += \
widgets/GraphLayout.h \
widgets/GraphGridLayout.h \
widgets/HexWidget.h \
common/SelectionHighlight.h
common/SelectionHighlight.h \
common/Decompiler.h
FORMS += \
dialogs/AboutDialog.ui \

View File

@ -3,6 +3,7 @@
#include "CutterApplication.h"
#include "plugins/PluginManager.h"
#include "CutterConfig.h"
#include "common/Decompiler.h"
#include <QApplication>
#include <QFileOpenEvent>
@ -118,6 +119,10 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
Config()->loadInitial();
Core()->loadCutterRC();
if (R2DecDecompiler::isAvailable()) {
Core()->registerDecompiler(new R2DecDecompiler(Core()));
}
bool analLevelSpecified = false;
int analLevel = 0;

67
src/common/Decompiler.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "Decompiler.h"
#include "Cutter.h"
#include <QJsonObject>
#include <QJsonArray>
Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
: QObject(parent),
id(id),
name(name)
{
}
R2DecDecompiler::R2DecDecompiler(QObject *parent)
: Decompiler("r2dec", "r2dec", parent)
{
}
bool R2DecDecompiler::isAvailable()
{
return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
}
DecompiledCode R2DecDecompiler::decompileAt(RVA addr)
{
DecompiledCode code;
QString s;
QJsonObject json = Core()->cmdj("pddj @ " + QString::number(addr)).object();
if (json.isEmpty()) {
return code;
}
for (const auto &line : json["log"].toArray()) {
if (!line.isString()) {
continue;
}
code.lines.append(DecompiledCode::Line(line.toString()));
}
auto linesArray = json["lines"].toArray();
code.lines.reserve(code.lines.size() + linesArray.size());
for (const auto &line : linesArray) {
QJsonObject lineObject = line.toObject();
if (lineObject.isEmpty()) {
continue;
}
DecompiledCode::Line codeLine;
codeLine.str = lineObject["str"].toString();
bool ok;
codeLine.addr = lineObject["offset"].toVariant().toULongLong(&ok);
if (!ok) {
codeLine.addr = RVA_INVALID;
}
code.lines.append(codeLine);
}
for (const auto &line : json["errors"].toArray()) {
if (!line.isString()) {
continue;
}
code.lines.append(DecompiledCode::Line(line.toString()));
}
return code;
}

71
src/common/Decompiler.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef DECOMPILER_H
#define DECOMPILER_H
#include "CutterCommon.h"
#include <QString>
#include <QObject>
/**
* Describes the result of a Decompilation Process with optional metadata
*/
struct DecompiledCode {
/**
* A single line of decompiled code
*/
struct Line
{
QString str;
/**
* Offset of the original instruction
*/
RVA addr;
Line()
{
this->addr = RVA_INVALID;
}
explicit Line(const QString &str, RVA addr = RVA_INVALID)
{
this->str = str;
this->addr = addr;
}
};
QList<Line> lines = {};
};
/**
* Implements a decompiler that can be registered using CutterCore::registerDecompiler()
*/
class Decompiler: public QObject
{
Q_OBJECT
private:
const QString id;
const QString name;
public:
Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);
virtual ~Decompiler() = default;
QString getId() const { return id; }
QString getName() const { return name; }
virtual DecompiledCode decompileAt(RVA addr) =0;
};
class R2DecDecompiler: public Decompiler
{
Q_OBJECT
public:
explicit R2DecDecompiler(QObject *parent = nullptr);
DecompiledCode decompileAt(RVA addr) override;
static bool isAvailable();
};
#endif //DECOMPILER_H

View File

@ -10,6 +10,7 @@
#include "common/R2Task.h"
#include "common/Json.h"
#include "core/Cutter.h"
#include "Decompiler.h"
#include "r_asm.h"
#include "sdb.h"
@ -932,67 +933,32 @@ RVA CutterCore::getOffsetJump(RVA addr)
return value;
}
DecompiledCode CutterCore::getDecompiledCodePDC(RVA addr)
QList<Decompiler *> CutterCore::getDecompilers()
{
DecompiledCode code;
auto lines = cmd("pdc @ " + QString::number(addr)).split('\n');
code.lines.reserve(lines.size());
for (const auto &line : lines) {
code.lines.append(DecompiledCode::Line(line));
}
return code;
return decompilers;
}
bool CutterCore::getR2DecAvailable()
Decompiler *CutterCore::getDecompilerById(const QString &id)
{
return cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
for (Decompiler *dec : decompilers) {
if (dec->getId() == id) {
return dec;
}
}
return nullptr;
}
DecompiledCode CutterCore::getDecompiledCodeR2Dec(RVA addr)
bool CutterCore::registerDecompiler(Decompiler *decompiler)
{
DecompiledCode code;
QString s;
QJsonObject json = cmdj("pddj @ " + QString::number(addr)).object();
if (json.isEmpty()) {
return code;
if (getDecompilerById(decompiler->getId())) {
return false;
}
for (const auto &line : json["log"].toArray()) {
if (!line.isString()) {
continue;
}
code.lines.append(DecompiledCode::Line(line.toString()));
}
auto linesArray = json["lines"].toArray();
code.lines.reserve(code.lines.size() + linesArray.size());
for (const auto &line : linesArray) {
QJsonObject lineObject = line.toObject();
if (lineObject.isEmpty()) {
continue;
}
DecompiledCode::Line codeLine;
codeLine.str = lineObject["str"].toString();
bool ok;
codeLine.addr = lineObject["offset"].toVariant().toULongLong(&ok);
if (!ok) {
codeLine.addr = RVA_INVALID;
}
code.lines.append(codeLine);
}
for (const auto &line : json["errors"].toArray()) {
if (!line.isString()) {
continue;
}
code.lines.append(DecompiledCode::Line(line.toString()));
}
return code;
decompiler->setParent(this);
decompilers.push_back(decompiler);
return true;
}
QJsonDocument CutterCore::getFileInfo()
{
return cmdj("ij");

View File

@ -14,6 +14,8 @@
class AsyncTaskManager;
class CutterCore;
class Decompiler;
#include "plugins/CutterPlugin.h"
#include "common/BasicBlockHighlighter.h"
@ -247,10 +249,19 @@ public:
int currentlyAttachedToPID = -1;
QString currentlyOpenFile;
/* Pseudocode */
DecompiledCode getDecompiledCodePDC(RVA addr);
bool getR2DecAvailable();
DecompiledCode getDecompiledCodeR2Dec(RVA addr);
/* Decompilers */
QList<Decompiler *> getDecompilers();
Decompiler *getDecompilerById(const QString &id);
/**
* Register a new decompiler
*
* The decompiler must have a unique id, otherwise this method will fail.
* The decompiler's parent will be set to this CutterCore instance, so it will automatically be freed later.
*
* @return whether the decompiler was registered successfully
*/
bool registerDecompiler(Decompiler *decompiler);
RVA getOffsetJump(RVA addr);
QJsonDocument getFileInfo();
@ -449,6 +460,8 @@ private:
RVA offsetPriorDebugging = RVA_INVALID;
QErrorMessage msgBox;
QList<Decompiler *> decompilers;
bool emptyGraph = false;
BasicBlockHighlighter *bbHighlighter;
};

View File

@ -304,35 +304,6 @@ struct VariableDescription {
QString type;
};
/**
* Describes the result of a Decompilation Process with optional metadata
*/
struct DecompiledCode {
/**
* A single line of decompiled code
*/
struct Line {
QString str;
/**
* Offset of the original instruction
*/
RVA addr;
Line()
{
this->addr = RVA_INVALID;
}
explicit Line(const QString &str, RVA addr = RVA_INVALID)
{
this->str = str;
this->addr = addr;
}
};
QList<Line> lines = {};
};
Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription)
@ -370,6 +341,5 @@ Q_DECLARE_METATYPE(BreakpointDescription)
Q_DECLARE_METATYPE(ProcessDescription)
Q_DECLARE_METATYPE(RegisterRefDescription)
Q_DECLARE_METATYPE(VariableDescription)
Q_DECLARE_METATYPE(DecompiledCode::Line)
#endif // DESCRIPTIONS_H

View File

@ -5,6 +5,7 @@
#include "common/Helpers.h"
#include "common/TempConfig.h"
#include "common/SelectionHighlight.h"
#include "common/Decompiler.h"
#include <QTextEdit>
#include <QPlainTextEdit>
@ -58,12 +59,16 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
doRefresh(Core()->getOffset());
});
if (Core()->getR2DecAvailable()) {
ui->decompilerComboBox->setEnabled(true);
ui->decompilerComboBox->setCurrentIndex(DecompilerCBR2Dec);
} else {
auto decompilers = Core()->getDecompilers();
for (auto dec : decompilers) {
ui->decompilerComboBox->addItem(dec->getName(), dec->getId());
}
if(decompilers.size() <= 1) {
ui->decompilerComboBox->setEnabled(false);
ui->decompilerComboBox->setCurrentIndex(DecompilerCBPdc);
if (decompilers.isEmpty()) {
ui->textEdit->setPlainText(tr("No Decompiler available."));
}
}
connectCursorPositionChanged(false);
@ -77,23 +82,21 @@ PseudocodeWidget::~PseudocodeWidget() = default;
void PseudocodeWidget::doRefresh(RVA addr)
{
if (ui->decompilerComboBox->currentIndex() < 0) {
return;
}
Decompiler *dec = Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());
if (!dec) {
return;
}
if (addr == RVA_INVALID) {
ui->textEdit->setPlainText(tr("Click Refresh to generate Pseudocode from current offset."));
return;
}
DecompiledCode decompiledCode;
switch (ui->decompilerComboBox->currentIndex()) {
case DecompilerCBR2Dec:
if (Core()->getR2DecAvailable()) {
decompiledCode = Core()->getDecompiledCodeR2Dec(addr);
break;
} // else fallthrough
case DecompilerCBPdc:
default:
decompiledCode = Core()->getDecompiledCodePDC(addr);
break;
}
DecompiledCode decompiledCode = dec->decompileAt(addr);
textLines = {};
textLines.reserve(decompiledCode.lines.size());

View File

@ -31,7 +31,6 @@ private slots:
void seekChanged();
private:
enum DecompilerComboBoxValues { DecompilerCBR2Dec, DecompilerCBPdc };
std::unique_ptr<Ui::PseudocodeWidget> ui;
QSyntaxHighlighter *syntaxHighlighter;

View File

@ -70,18 +70,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="decompilerComboBox">
<item>
<property name="text">
<string>r2dec</string>
</property>
</item>
<item>
<property name="text">
<string>pdc</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="decompilerComboBox" />
</item>
</layout>
</item>