mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-19 02:48:49 +00:00
Add a generic Decompiler interface and remove pdc (#1659)
This commit is contained in:
parent
736d3e1f1d
commit
4487564f7d
@ -349,7 +349,8 @@ SOURCES += \
|
|||||||
common/HighDpiPixmap.cpp \
|
common/HighDpiPixmap.cpp \
|
||||||
widgets/GraphGridLayout.cpp \
|
widgets/GraphGridLayout.cpp \
|
||||||
widgets/HexWidget.cpp \
|
widgets/HexWidget.cpp \
|
||||||
common/SelectionHighlight.cpp
|
common/SelectionHighlight.cpp \
|
||||||
|
common/Decompiler.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
core/Cutter.h \
|
core/Cutter.h \
|
||||||
@ -476,7 +477,8 @@ HEADERS += \
|
|||||||
widgets/GraphLayout.h \
|
widgets/GraphLayout.h \
|
||||||
widgets/GraphGridLayout.h \
|
widgets/GraphGridLayout.h \
|
||||||
widgets/HexWidget.h \
|
widgets/HexWidget.h \
|
||||||
common/SelectionHighlight.h
|
common/SelectionHighlight.h \
|
||||||
|
common/Decompiler.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
dialogs/AboutDialog.ui \
|
dialogs/AboutDialog.ui \
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "CutterApplication.h"
|
#include "CutterApplication.h"
|
||||||
#include "plugins/PluginManager.h"
|
#include "plugins/PluginManager.h"
|
||||||
#include "CutterConfig.h"
|
#include "CutterConfig.h"
|
||||||
|
#include "common/Decompiler.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFileOpenEvent>
|
#include <QFileOpenEvent>
|
||||||
@ -118,6 +119,10 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
|
|||||||
Config()->loadInitial();
|
Config()->loadInitial();
|
||||||
Core()->loadCutterRC();
|
Core()->loadCutterRC();
|
||||||
|
|
||||||
|
if (R2DecDecompiler::isAvailable()) {
|
||||||
|
Core()->registerDecompiler(new R2DecDecompiler(Core()));
|
||||||
|
}
|
||||||
|
|
||||||
bool analLevelSpecified = false;
|
bool analLevelSpecified = false;
|
||||||
int analLevel = 0;
|
int analLevel = 0;
|
||||||
|
|
||||||
|
67
src/common/Decompiler.cpp
Normal file
67
src/common/Decompiler.cpp
Normal 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
71
src/common/Decompiler.h
Normal 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
|
@ -10,6 +10,7 @@
|
|||||||
#include "common/R2Task.h"
|
#include "common/R2Task.h"
|
||||||
#include "common/Json.h"
|
#include "common/Json.h"
|
||||||
#include "core/Cutter.h"
|
#include "core/Cutter.h"
|
||||||
|
#include "Decompiler.h"
|
||||||
#include "r_asm.h"
|
#include "r_asm.h"
|
||||||
#include "sdb.h"
|
#include "sdb.h"
|
||||||
|
|
||||||
@ -932,67 +933,32 @@ RVA CutterCore::getOffsetJump(RVA addr)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecompiledCode CutterCore::getDecompiledCodePDC(RVA addr)
|
|
||||||
|
QList<Decompiler *> CutterCore::getDecompilers()
|
||||||
{
|
{
|
||||||
DecompiledCode code;
|
return decompilers;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
if (getDecompilerById(decompiler->getId())) {
|
||||||
QString s;
|
return false;
|
||||||
|
|
||||||
QJsonObject json = cmdj("pddj @ " + QString::number(addr)).object();
|
|
||||||
if (json.isEmpty()) {
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
decompiler->setParent(this);
|
||||||
for (const auto &line : json["log"].toArray()) {
|
decompilers.push_back(decompiler);
|
||||||
if (!line.isString()) {
|
return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QJsonDocument CutterCore::getFileInfo()
|
QJsonDocument CutterCore::getFileInfo()
|
||||||
{
|
{
|
||||||
return cmdj("ij");
|
return cmdj("ij");
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
class AsyncTaskManager;
|
class AsyncTaskManager;
|
||||||
class CutterCore;
|
class CutterCore;
|
||||||
|
class Decompiler;
|
||||||
|
|
||||||
#include "plugins/CutterPlugin.h"
|
#include "plugins/CutterPlugin.h"
|
||||||
#include "common/BasicBlockHighlighter.h"
|
#include "common/BasicBlockHighlighter.h"
|
||||||
|
|
||||||
@ -247,10 +249,19 @@ public:
|
|||||||
int currentlyAttachedToPID = -1;
|
int currentlyAttachedToPID = -1;
|
||||||
QString currentlyOpenFile;
|
QString currentlyOpenFile;
|
||||||
|
|
||||||
/* Pseudocode */
|
/* Decompilers */
|
||||||
DecompiledCode getDecompiledCodePDC(RVA addr);
|
QList<Decompiler *> getDecompilers();
|
||||||
bool getR2DecAvailable();
|
Decompiler *getDecompilerById(const QString &id);
|
||||||
DecompiledCode getDecompiledCodeR2Dec(RVA addr);
|
|
||||||
|
/**
|
||||||
|
* 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);
|
RVA getOffsetJump(RVA addr);
|
||||||
QJsonDocument getFileInfo();
|
QJsonDocument getFileInfo();
|
||||||
@ -449,6 +460,8 @@ private:
|
|||||||
RVA offsetPriorDebugging = RVA_INVALID;
|
RVA offsetPriorDebugging = RVA_INVALID;
|
||||||
QErrorMessage msgBox;
|
QErrorMessage msgBox;
|
||||||
|
|
||||||
|
QList<Decompiler *> decompilers;
|
||||||
|
|
||||||
bool emptyGraph = false;
|
bool emptyGraph = false;
|
||||||
BasicBlockHighlighter *bbHighlighter;
|
BasicBlockHighlighter *bbHighlighter;
|
||||||
};
|
};
|
||||||
|
@ -304,35 +304,6 @@ struct VariableDescription {
|
|||||||
QString type;
|
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(FunctionDescription)
|
||||||
Q_DECLARE_METATYPE(ImportDescription)
|
Q_DECLARE_METATYPE(ImportDescription)
|
||||||
Q_DECLARE_METATYPE(ExportDescription)
|
Q_DECLARE_METATYPE(ExportDescription)
|
||||||
@ -370,6 +341,5 @@ Q_DECLARE_METATYPE(BreakpointDescription)
|
|||||||
Q_DECLARE_METATYPE(ProcessDescription)
|
Q_DECLARE_METATYPE(ProcessDescription)
|
||||||
Q_DECLARE_METATYPE(RegisterRefDescription)
|
Q_DECLARE_METATYPE(RegisterRefDescription)
|
||||||
Q_DECLARE_METATYPE(VariableDescription)
|
Q_DECLARE_METATYPE(VariableDescription)
|
||||||
Q_DECLARE_METATYPE(DecompiledCode::Line)
|
|
||||||
|
|
||||||
#endif // DESCRIPTIONS_H
|
#endif // DESCRIPTIONS_H
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "common/Helpers.h"
|
#include "common/Helpers.h"
|
||||||
#include "common/TempConfig.h"
|
#include "common/TempConfig.h"
|
||||||
#include "common/SelectionHighlight.h"
|
#include "common/SelectionHighlight.h"
|
||||||
|
#include "common/Decompiler.h"
|
||||||
|
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
@ -58,12 +59,16 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
|||||||
doRefresh(Core()->getOffset());
|
doRefresh(Core()->getOffset());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Core()->getR2DecAvailable()) {
|
auto decompilers = Core()->getDecompilers();
|
||||||
ui->decompilerComboBox->setEnabled(true);
|
for (auto dec : decompilers) {
|
||||||
ui->decompilerComboBox->setCurrentIndex(DecompilerCBR2Dec);
|
ui->decompilerComboBox->addItem(dec->getName(), dec->getId());
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if(decompilers.size() <= 1) {
|
||||||
ui->decompilerComboBox->setEnabled(false);
|
ui->decompilerComboBox->setEnabled(false);
|
||||||
ui->decompilerComboBox->setCurrentIndex(DecompilerCBPdc);
|
if (decompilers.isEmpty()) {
|
||||||
|
ui->textEdit->setPlainText(tr("No Decompiler available."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectCursorPositionChanged(false);
|
connectCursorPositionChanged(false);
|
||||||
@ -77,23 +82,21 @@ PseudocodeWidget::~PseudocodeWidget() = default;
|
|||||||
|
|
||||||
void PseudocodeWidget::doRefresh(RVA addr)
|
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) {
|
if (addr == RVA_INVALID) {
|
||||||
ui->textEdit->setPlainText(tr("Click Refresh to generate Pseudocode from current offset."));
|
ui->textEdit->setPlainText(tr("Click Refresh to generate Pseudocode from current offset."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecompiledCode decompiledCode;
|
DecompiledCode decompiledCode = dec->decompileAt(addr);
|
||||||
switch (ui->decompilerComboBox->currentIndex()) {
|
|
||||||
case DecompilerCBR2Dec:
|
|
||||||
if (Core()->getR2DecAvailable()) {
|
|
||||||
decompiledCode = Core()->getDecompiledCodeR2Dec(addr);
|
|
||||||
break;
|
|
||||||
} // else fallthrough
|
|
||||||
case DecompilerCBPdc:
|
|
||||||
default:
|
|
||||||
decompiledCode = Core()->getDecompiledCodePDC(addr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
textLines = {};
|
textLines = {};
|
||||||
textLines.reserve(decompiledCode.lines.size());
|
textLines.reserve(decompiledCode.lines.size());
|
||||||
|
@ -31,7 +31,6 @@ private slots:
|
|||||||
void seekChanged();
|
void seekChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum DecompilerComboBoxValues { DecompilerCBR2Dec, DecompilerCBPdc };
|
|
||||||
std::unique_ptr<Ui::PseudocodeWidget> ui;
|
std::unique_ptr<Ui::PseudocodeWidget> ui;
|
||||||
|
|
||||||
QSyntaxHighlighter *syntaxHighlighter;
|
QSyntaxHighlighter *syntaxHighlighter;
|
||||||
|
@ -70,18 +70,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="decompilerComboBox">
|
<widget class="QComboBox" name="decompilerComboBox" />
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>r2dec</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>pdc</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user