Added new graph view (WIP)

* This known unstable just a PoC
This commit is contained in:
xarkes 2017-09-28 23:53:59 +02:00
parent 97ee9f17b6
commit 25a4d06697
11 changed files with 2814 additions and 16 deletions

View File

@ -18,7 +18,7 @@ QT += core gui widgets webengine webenginewidgets
QT_CONFIG -= no-pkg-config
CONFIG += c++11
CONFIG += debug c++11
# Define the preprocessor macro to get the application version in our application.
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
@ -56,7 +56,6 @@ SOURCES += \
widgets/commentswidget.cpp \
widgets/stringswidget.cpp \
widgets/flagswidget.cpp \
widgets/memorywidget.cpp \
widgets/exportswidget.cpp \
widgets/sdbdock.cpp \
analthread.cpp \
@ -71,7 +70,10 @@ SOURCES += \
widgets/consolewidget.cpp \
radarewebserver.cpp \
widgets/entrypointwidget.cpp \
dialogs/flagdialog.cpp
dialogs/flagdialog.cpp \
widgets/DisassemblerGraphView.cpp \
widgets/MemoryWidget.cpp \
utils/RichTextPainter.cpp
HEADERS += \
mainwindow.h \
@ -94,7 +96,6 @@ HEADERS += \
widgets/commentswidget.h \
widgets/stringswidget.h \
widgets/flagswidget.h \
widgets/memorywidget.h \
widgets/exportswidget.h \
widgets/sdbdock.h \
analthread.h \
@ -112,7 +113,11 @@ HEADERS += \
settings.h \
widgets/entrypointwidget.h \
cutter.h \
dialogs/flagdialog.h
dialogs/flagdialog.h \
widgets/DisassemblerGraphView.h \
widgets/MemoryWidget.h \
utils/RichTextPainter.h \
utils/CachedFontMetrics.h
FORMS += \
mainwindow.ui \
newfiledialog.ui \
@ -128,7 +133,6 @@ FORMS += \
widgets/commentswidget.ui \
widgets/stringswidget.ui \
widgets/flagswidget.ui \
widgets/memorywidget.ui \
widgets/exportswidget.ui \
widgets/sdbdock.ui \
dialogs/commentsdialog.ui \
@ -138,7 +142,8 @@ FORMS += \
widgets/sectionsdock.ui \
widgets/consolewidget.ui \
widgets/entrypointwidget.ui \
dialogs/flagdialog.ui
dialogs/flagdialog.ui \
widgets/MemoryWidget.ui
RESOURCES += \
resources.qrc

View File

@ -39,7 +39,7 @@
#include "newfiledialog.h"
#include "helpers.h"
#include "widgets/memorywidget.h"
#include "widgets/MemoryWidget.h"
#include "widgets/functionswidget.h"
#include "widgets/sectionswidget.h"
#include "widgets/commentswidget.h"
@ -60,6 +60,7 @@
#include "settings.h"
#include "optionsdialog.h"
#include "widgets/entrypointwidget.h"
#include "widgets/DisassemblerGraphView.h"
// graphics
#include <QGraphicsEllipseItem>
@ -204,7 +205,7 @@ void MainWindow::initUI()
/*
* Dock Widgets
*/
dockWidgets.reserve(11);
dockWidgets.reserve(12);
// Add Memory DockWidget
this->memoryDock = new MemoryWidget(this);

View File

@ -5,7 +5,7 @@
#include "helpers.h"
// TODO: remove us
#include "widgets/memorywidget.h"
#include "widgets/MemoryWidget.h"
#include "widgets/notepad.h"
#include "settings.h"

View File

@ -0,0 +1,63 @@
#ifndef CACHEDFONTMETRICS_H
#define CACHEDFONTMETRICS_H
#include <QObject>
#include <QFont>
#include <QFontMetrics>
class CachedFontMetrics : public QObject
{
Q_OBJECT
public:
explicit CachedFontMetrics(QObject* parent, const QFont & font)
: QObject(parent),
mFontMetrics(font)
{
memset(mWidths, 0, sizeof(mWidths));
mHeight = mFontMetrics.height();
}
int width(const QChar & ch)
{
auto unicode = ch.unicode();
if(unicode >= 0xD800)
{
if(unicode >= 0xE000)
unicode -= 0xE000 - 0xD800;
else
// is lonely surrogate
return mFontMetrics.width(ch);
}
if(!mWidths[unicode])
return mWidths[unicode] = mFontMetrics.width(ch);
return mWidths[unicode];
}
int width(const QString & text)
{
int result = 0;
QChar temp;
for(const QChar & ch : text)
{
if(ch.isHighSurrogate())
temp = ch;
else if(ch.isLowSurrogate())
result += mFontMetrics.width(QString(temp) + ch);
else
result += width(ch);
}
return result;
}
int height()
{
return mHeight;
}
private:
QFontMetrics mFontMetrics;
uchar mWidths[0x10000 - 0xE000 + 0xD800];
int mHeight;
};
#endif // CACHEDFONTMETRICS_H

View File

@ -0,0 +1,103 @@
/* x64dbg RichTextPainter */
#include "RichTextPainter.h"
#include "CachedFontMetrics.h"
#include <QPainter>
//TODO: fix performance (possibly use QTextLayout?)
void RichTextPainter::paintRichText(QPainter* painter, int x, int y, int w, int h, int xinc, const List & richText, CachedFontMetrics* fontMetrics)
{
QPen pen;
QPen highlightPen;
QBrush brush(Qt::cyan);
for(const CustomRichText_t & curRichText : richText)
{
int textWidth = fontMetrics->width(curRichText.text);
int backgroundWidth = textWidth;
if(backgroundWidth + xinc > w)
backgroundWidth = w - xinc;
if(backgroundWidth <= 0) //stop drawing when going outside the specified width
break;
switch(curRichText.flags)
{
case FlagNone: //defaults
break;
case FlagColor: //color only
pen.setColor(curRichText.textColor);
painter->setPen(pen);
break;
case FlagBackground: //background only
if(backgroundWidth > 0 && curRichText.textBackground.alpha())
{
brush.setColor(curRichText.textBackground);
painter->fillRect(QRect(x + xinc, y, backgroundWidth, h), brush);
}
break;
case FlagAll: //color+background
if(backgroundWidth > 0 && curRichText.textBackground.alpha())
{
brush.setColor(curRichText.textBackground);
painter->fillRect(QRect(x + xinc, y, backgroundWidth, h), brush);
}
pen.setColor(curRichText.textColor);
painter->setPen(pen);
break;
}
painter->drawText(QRect(x + xinc, y, w - xinc, h), Qt::TextBypassShaping, curRichText.text);
if(curRichText.highlight && curRichText.highlightColor.alpha())
{
highlightPen.setColor(curRichText.highlightColor);
highlightPen.setWidth(curRichText.highlightWidth);
painter->setPen(highlightPen);
int highlightOffsetX = curRichText.highlightConnectPrev ? -1 : 1;
painter->drawLine(x + xinc + highlightOffsetX, y + h - 1, x + xinc + backgroundWidth - 1, y + h - 1);
}
xinc += textWidth;
}
}
/**
* @brief RichTextPainter::htmlRichText Convert rich text in x64dbg to HTML, for use by other applications
* @param richText The rich text to be converted to HTML format
* @param textHtml The HTML source. Any previous content will be preserved and new content will be appended at the end.
* @param textPlain The plain text. Any previous content will be preserved and new content will be appended at the end.
*/
void RichTextPainter::htmlRichText(const List & richText, QString & textHtml, QString & textPlain)
{
for(const CustomRichText_t & curRichText : richText)
{
if(curRichText.text == " ") //blank
{
textHtml += " ";
textPlain += " ";
continue;
}
switch(curRichText.flags)
{
case FlagNone: //defaults
textHtml += "<span>";
break;
case FlagColor: //color only
textHtml += QString("<span style=\"color:%1\">").arg(curRichText.textColor.name());
break;
case FlagBackground: //background only
if(curRichText.textBackground != Qt::transparent) // QColor::name() returns "#000000" for transparent color. That's not desired. Leave it blank.
textHtml += QString("<span style=\"background-color:%1\">").arg(curRichText.textBackground.name());
else
textHtml += QString("<span>");
break;
case FlagAll: //color+background
if(curRichText.textBackground != Qt::transparent) // QColor::name() returns "#000000" for transparent color. That's not desired. Leave it blank.
textHtml += QString("<span style=\"color:%1; background-color:%2\">").arg(curRichText.textColor.name(), curRichText.textBackground.name());
else
textHtml += QString("<span style=\"color:%1\">").arg(curRichText.textColor.name());
break;
}
if(curRichText.highlight) //Underline highlighted token
textHtml += "<u>";
textHtml += curRichText.text.toHtmlEscaped();
if(curRichText.highlight)
textHtml += "</u>";
textHtml += "</span>"; //Close the tag
textPlain += curRichText.text;
}
}

View File

@ -0,0 +1,43 @@
/* x64dbg RichTextPainter */
#ifndef RICHTEXTPAINTER_H
#define RICHTEXTPAINTER_H
#include <QString>
#include <QColor>
#include <vector>
class CachedFontMetrics;
class QPainter;
class RichTextPainter
{
public:
//structures
enum CustomRichTextFlags
{
FlagNone,
FlagColor,
FlagBackground,
FlagAll
};
struct CustomRichText_t
{
QString text;
QColor textColor;
QColor textBackground;
CustomRichTextFlags flags;
bool highlight;
QColor highlightColor;
int highlightWidth = 2;
bool highlightConnectPrev = false;
};
typedef std::vector<CustomRichText_t> List;
//functions
static void paintRichText(QPainter* painter, int x, int y, int w, int h, int xinc, const List & richText, CachedFontMetrics* fontMetrics);
static void htmlRichText(const List & richText, QString & textHtml, QString & textPlain);
};
#endif // RICHTEXTPAINTER_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,363 @@
/* x64dbg DisassemblerGraphView */
#ifndef DISASSEMBLERGRAPHVIEW_H
#define DISASSEMBLERGRAPHVIEW_H
#include <QObject>
#include <QWidget>
#include <QAbstractScrollArea>
#include <QPaintEvent>
#include <QTimer>
#include <QSize>
#include <QResizeEvent>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <QMutex>
#include <QMenu>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include "cutter.h"
#include "utils/RichTextPainter.h"
#include "utils/CachedFontMetrics.h"
#define duint ut64
class MenuBuilder;
class CachedFontMetrics;
class GotoDialog;
class XrefBrowseDialog;
class DisassemblerGraphView : public QAbstractScrollArea
{
Q_OBJECT
public:
struct DisassemblerBlock;
struct Point
{
int row; //point[0]
int col; //point[1]
int index; //point[2]
};
struct DisassemblerEdge
{
QColor color;
DisassemblerBlock* dest;
std::vector<Point> points;
int start_index = 0;
QPolygonF polyline;
QPolygonF arrow;
void addPoint(int row, int col, int index = 0)
{
Point point = {row, col, 0};
this->points.push_back(point);
if(int(this->points.size()) > 1)
this->points[this->points.size() - 2].index = index;
}
};
struct Token
{
int start; //token[0]
int length; //token[1]
QString type; //token[2]
duint addr; //token[3]
QString name; //token[4]
};
struct HighlightToken
{
QString type; //highlight_token[0]
duint addr; //highlight_token[1]
QString name; //highlight_token[2]
bool equalsToken(const Token & token)
{
return this->type == token.type &&
this->addr == token.addr &&
this->name == token.name;
}
static HighlightToken* fromToken(const Token & token)
{
//TODO: memory leaks
auto result = new HighlightToken();
result->type = token.type;
result->addr = token.addr;
result->name = token.name;
return result;
}
};
struct Text
{
std::vector<RichTextPainter::List> lines;
Text() {}
Text(const QString & text, QColor color, QColor background)
{
RichTextPainter::List richText;
RichTextPainter::CustomRichText_t rt;
rt.highlight = false;
rt.text = text;
rt.textColor = color;
rt.textBackground = background;
rt.flags = rt.textBackground.alpha() ? RichTextPainter::FlagAll : RichTextPainter::FlagColor;
richText.push_back(rt);
lines.push_back(richText);
}
Text(const RichTextPainter::List & richText)
{
lines.push_back(richText);
}
QString ToQString() const
{
QString result;
for(auto & line : lines)
{
for(auto & t : line)
{
result += t.text;
}
}
return std::move(result);
}
};
struct Instr
{
duint addr = 0;
Text text;
std::vector<unsigned char> opcode; //instruction bytes
};
struct Block
{
Text header_text;
std::vector<Instr> instrs;
std::vector<duint> exits;
duint entry = 0;
duint true_path = 0;
duint false_path = 0;
bool terminal = false;
bool indirectcall = false;
};
struct DisassemblerBlock
{
DisassemblerBlock() {}
explicit DisassemblerBlock(Block & block)
: block(block) {}
Block block;
std::vector<DisassemblerEdge> edges;
std::vector<duint> incoming;
std::vector<duint> new_exits;
qreal x = 0.0;
qreal y = 0.0;
int width = 0;
int height = 0;
int col = 0;
int col_count = 0;
int row = 0;
int row_count = 0;
};
struct Function
{
bool ready;
duint entry;
duint update_id;
std::vector<Block> blocks;
};
struct Analysis
{
duint entry = 0;
std::unordered_map<duint, Function> functions;
bool ready = false;
duint update_id = 0;
QString status = "Analyzing...";
bool find_instr(duint addr, duint & func, duint & instr)
{
//TODO implement
Q_UNUSED(addr);
Q_UNUSED(func);
Q_UNUSED(instr);
return false;
}
//dummy class
};
enum class LayoutType
{
Wide,
Medium,
Narrow,
};
DisassemblerGraphView(QWidget *parent, CutterCore *core);
~DisassemblerGraphView();
void initFont();
void adjustSize(int width, int height);
void resizeEvent(QResizeEvent* event);
duint get_cursor_pos();
void set_cursor_pos(duint addr);
std::tuple<duint, duint> get_selection_range();
void set_selection_range(std::tuple<duint, duint> range);
void copy_address();
//void analysis_thread_proc();
//void closeRequest();
void paintNormal(QPainter & p, QRect & viewportRect, int xofs, int yofs);
void paintOverview(QPainter & p, QRect & viewportRect, int xofs, int yofs);
void paintEvent(QPaintEvent* event);
bool isMouseEventInBlock(QMouseEvent* event);
duint getInstrForMouseEvent(QMouseEvent* event);
bool getTokenForMouseEvent(QMouseEvent* event, Token & token);
bool find_instr(duint addr, Instr & instr);
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void prepareGraphNode(DisassemblerBlock & block);
void adjustGraphLayout(DisassemblerBlock & block, int col, int row);
void computeGraphLayout(DisassemblerBlock & block);
void setupContextMenu();
void keyPressEvent(QKeyEvent* event);
template<typename T>
using Matrix = std::vector<std::vector<T>>;
using EdgesVector = Matrix<std::vector<bool>>;
bool isEdgeMarked(EdgesVector & edges, int row, int col, int index);
void markEdge(EdgesVector & edges, int row, int col, int index, bool used = true);
int findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int max_col);
int findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int max_row);
DisassemblerEdge routeEdge(EdgesVector & horiz_edges, EdgesVector & vert_edges, Matrix<bool> & edge_valid, DisassemblerBlock & start, DisassemblerBlock & end, QColor color);
void renderFunction(Function & func);
void show_cur_instr(bool force = false);
bool navigate(duint addr);
void fontChanged();
void setGraphLayout(LayoutType layout);
//VaHistory mHistory;
signals:
void displaySnowmanWidget();
public slots:
void updateTimerEvent();
//void loadGraphSlot(BridgeCFGraphList* graph, duint addr);
void graphAtSlot(duint addr);
void updateGraphSlot();
void followDisassemblerSlot();
void colorsUpdatedSlot();
void fontsUpdatedSlot();
void shortcutsUpdatedSlot();
void toggleOverviewSlot();
void toggleSummarySlot();
//void selectionGetSlot(SELECTIONDATA* selection);
void tokenizerConfigUpdatedSlot();
void loadCurrentGraph();
//void disassembleAtSlot(dsint va, dsint cip);
void gotoExpressionSlot();
void gotoOriginSlot();
void gotoPreviousSlot();
void gotoNextSlot();
void toggleSyncOriginSlot();
void followActionSlot();
void refreshSlot();
void saveImageSlot();
void setCommentSlot();
void setLabelSlot();
void xrefSlot();
void decompileSlot();
private:
CutterCore *mCore;
QString status;
Analysis analysis;
duint function;
QTimer* updateTimer;
int baseline;
qreal charWidth;
int charHeight;
int charOffset;
int width;
int height;
int renderWidth;
int renderHeight;
int renderXOfs;
int renderYOfs;
duint cur_instr;
int scroll_base_x;
int scroll_base_y;
duint update_id;
bool scroll_mode;
bool ready;
int* desired_pos;
std::unordered_map<duint, DisassemblerBlock> blocks;
HighlightToken* highlight_token;
std::vector<int> col_edge_x;
std::vector<int> row_edge_y;
CachedFontMetrics* mFontMetrics;
MenuBuilder* mMenuBuilder;
bool drawOverview;
bool onlySummary;
bool syncOrigin;
int overviewXOfs;
int overviewYOfs;
qreal overviewScale;
duint mCip;
bool forceCenter;
bool saveGraph;
bool mHistoryLock; //Don't add a history while going to previous/next
LayoutType layoutType;
QAction* mToggleOverview;
QAction* mToggleSummary;
QAction* mToggleSyncOrigin;
QColor disassemblyBackgroundColor;
QColor disassemblySelectionColor;
QColor disassemblyTracedColor;
QColor disassemblyTracedSelectionColor;
QColor jmpColor;
QColor brtrueColor;
QColor brfalseColor;
QColor retShadowColor;
QColor indirectcallShadowColor;
QColor backgroundColor;
QColor mAutoCommentColor;
QColor mAutoCommentBackgroundColor;
QColor mCommentColor;
QColor mCommentBackgroundColor;
QColor mLabelColor;
QColor mLabelBackgroundColor;
QColor graphNodeColor;
QColor mAddressColor;
QColor mAddressBackgroundColor;
QColor mCipColor;
QColor mBreakpointColor;
QColor mDisabledBreakpointColor;
//BridgeCFGraph currentGraph;
std::unordered_map<duint, duint> currentBlockMap;
//QBeaEngine disasm;
GotoDialog* mGoto;
XrefBrowseDialog* mXrefDlg;
void addReferenceAction(QMenu* menu, duint addr);
};
#endif // DISASSEMBLERGRAPHVIEW_H

View File

@ -1,5 +1,6 @@
#include "memorywidget.h"
#include "ui_memorywidget.h"
#include "MemoryWidget.h"
#include "ui_MemoryWidget.h"
#include "DisassemblerGraphView.h"
#include "mainwindow.h"
#include "helpers.h"
@ -159,6 +160,11 @@ MemoryWidget::MemoryWidget(MainWindow *main) :
connect(new QShortcut(Qt::SHIFT + Qt::Key_X, ui->disasTextEdit_2),
SIGNAL(activated()), this, SLOT(showXrefsDialog()));
// Create Graph View
ui->tabGraph->setLayout(new QGridLayout);
mGraphView = new DisassemblerGraphView(ui->tabGraph, main->core);
ui->tabGraph->layout()->addWidget(mGraphView);
// Space to switch between disassembly and graph
QShortcut *graph_shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this->main);
connect(graph_shortcut, SIGNAL(activated()), this, SLOT(cycleViews()));
@ -1136,7 +1142,7 @@ void MemoryWidget::on_actionSettings_menu_1_triggered()
bool ok = true;
// QFont font = QFont("Monospace", 8);
// TODO Use global configuration
QFont font = QFontDialog::getFont(&ok, ui->disasTextEdit_2->font(), this);
if (ok)
@ -1267,6 +1273,12 @@ void MemoryWidget::on_graphButton_2_clicked()
ui->memSideTabWidget_2->setCurrentIndex(0);
}
void MemoryWidget::on_graphButton_clicked()
{
ui->memTabWidget->setCurrentIndex(3);
ui->memSideTabWidget_2->setCurrentIndex(3);
}
void MemoryWidget::on_actionSend_to_Notepad_triggered()
{
QTextCursor cursor = ui->disasTextEdit_2->textCursor();

View File

@ -15,6 +15,7 @@
#include "hexascii_highlighter.h"
#include "hexhighlighter.h"
#include "dashboard.h"
#include "widgets/DisassemblerGraphView.h"
class MainWindow;
@ -46,6 +47,7 @@ public:
QTabWidget *memTabWidget;
QWebEngineView *graphWebView;
QWebEngineView *histoWebView;
DisassemblerGraphView *mGraphView;
Highlighter *highlighter;
Highlighter *highlighter_5;
@ -139,6 +141,7 @@ private slots:
void on_disButton_2_clicked();
void on_hexButton_2_clicked();
void on_graphButton_2_clicked();
void on_graphButton_clicked();
void showDisasContextMenu(const QPoint &pt);
void showHexdumpContextMenu(const QPoint &pt);
void showHexASCIIContextMenu(const QPoint &pt);

View File

@ -178,7 +178,7 @@ QToolTip {
}</string>
</property>
<property name="text">
<string/>
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
@ -255,6 +255,63 @@ QToolTip {
</attribute>
</widget>
</item>
<item>
<widget class="QToolButton" name="graphButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Graph</string>
</property>
<property name="styleSheet">
<string notr="true">QToolButton { /* all types of tool button */
border: 5px solid #333;
border-left: 10px solid #333;
border-right: 10px solid #333;
border-radius: 0px;
background-color: #333;
}
QToolButton:hover {
border: 5px solid #444;
border-radius: 0px;
background-color: #444;
}
QToolButton:checked {
border: 5px solid #2180a9;
border-radius: 0px;
background-color: #2180a9;
}
QToolTip {
background-color: #444;
border: 3px solid #444;
color: rgb(232, 232, 232);
}</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/img/icons/graph_light.svg</normaloff>
<normalon>:/img/icons/graph_white.svg</normalon>:/img/icons/graph_light.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<spacer name="verticalSpacer_6">
<property name="orientation">
@ -456,7 +513,7 @@ border-top: 0px;
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<property name="documentMode">
<bool>false</bool>
@ -1249,6 +1306,12 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
<widget class="QWidget" name="tabGraph">
<attribute name="title">
<string>Page</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_19"/>
</widget>
</widget>
</item>
<item>
@ -3046,8 +3109,8 @@ QToolTip {
</resources>
<connections/>
<buttongroups>
<buttongroup name="buttonGroup_3"/>
<buttongroup name="buttonGroup_2"/>
<buttongroup name="buttonGroup_3"/>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>