1
0
Fork 0

Merge branch 'graph' into development

# Conflicts:
#	src/gui/resource.qrc
This commit is contained in:
Mr. eXoDia 2016-07-17 23:23:22 +02:00
commit cff67aa8ff
27 changed files with 1905 additions and 128 deletions

View File

@ -12,14 +12,15 @@ typedef struct
bool terminal; //node is a RET
bool split; //node is a split (brtrue points to the next node)
void* userdata; //user data
ListOf(duint) exits; //exits (including brtrue and brfalse)
ListInfo exits; //exits (including brtrue and brfalse, duint)
ListInfo data; //block data
} BridgeCFNodeList;
typedef struct
{
duint entryPoint; //graph entry point
void* userdata; //user data
ListOf(BridgeCFNodeList) nodes; //graph nodes
ListInfo nodes; //graph nodes (BridgeCFNodeList)
} BridgeCFGraphList;
#ifdef __cplusplus
@ -27,6 +28,7 @@ typedef struct
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <utility>
struct BridgeCFNode
{
@ -40,10 +42,11 @@ struct BridgeCFNode
bool split; //node is a split (brtrue points to the next node)
void* userdata; //user data
std::vector<duint> exits; //exits (including brtrue and brfalse)
std::vector<unsigned char> data; //block data
explicit BridgeCFNode(BridgeCFNodeList* nodeList)
explicit BridgeCFNode(BridgeCFNodeList* nodeList, bool freedata = true)
{
if(!nodeList || !nodeList->exits || nodeList->exits->size != nodeList->exits->count * sizeof(duint))
if(!nodeList)
__debugbreak();
parentGraph = nodeList->parentGraph;
start = nodeList->start;
@ -54,10 +57,10 @@ struct BridgeCFNode
terminal = nodeList->terminal;
split = nodeList->split;
userdata = nodeList->userdata;
auto data = (duint*)nodeList->exits->data;
exits.resize(nodeList->exits->count);
for(int i = 0; i < nodeList->exits->count; i++)
exits[i] = data[i];
if(!BridgeList<duint>::ToVector(&nodeList->exits, exits, freedata))
__debugbreak();
if(!BridgeList<unsigned char>::ToVector(&nodeList->data, data, freedata))
__debugbreak();
}
explicit BridgeCFNode(duint parentGraph, duint start, duint end)
@ -77,6 +80,23 @@ struct BridgeCFNode
: BridgeCFNode(0, 0, 0)
{
}
BridgeCFNodeList ToNodeList() const
{
BridgeCFNodeList out;
out.parentGraph = parentGraph;
out.start = start;
out.end = end;
out.brtrue = brtrue;
out.brfalse = brfalse;
out.icount = icount;
out.terminal = terminal;
out.split = split;
out.userdata = userdata;
BridgeList<duint>::CopyData(&out.exits, exits);
BridgeList<unsigned char>::CopyData(&out.data, data);
return std::move(out);
}
};
struct BridgeCFGraph
@ -86,15 +106,17 @@ struct BridgeCFGraph
std::unordered_map<duint, BridgeCFNode> nodes; //CFNode.start -> CFNode
std::unordered_map<duint, std::unordered_set<duint>> parents; //CFNode.start -> parents
explicit BridgeCFGraph(BridgeCFGraphList* graphList)
explicit BridgeCFGraph(BridgeCFGraphList* graphList, bool freedata = true)
{
if(!graphList || !graphList->nodes || graphList->nodes->size != graphList->nodes->count * sizeof(BridgeCFNode))
if(!graphList || graphList->nodes.size != graphList->nodes.count * sizeof(BridgeCFNodeList))
__debugbreak();
entryPoint = graphList->entryPoint;
userdata = graphList->userdata;
auto data = (BridgeCFNode*)graphList->nodes->data;
for(int i = 0; i < graphList->nodes->count; i++)
AddNode(data[i]);
auto data = (BridgeCFNodeList*)graphList->nodes.data;
for(int i = 0; i < graphList->nodes.count; i++)
AddNode(BridgeCFNode(&data[i], freedata));
if(freedata && data)
BridgeFree(data);
}
explicit BridgeCFGraph(duint entryPoint)
@ -124,6 +146,19 @@ struct BridgeCFGraph
else
found->second.insert(parent);
}
BridgeCFGraphList ToGraphList() const
{
BridgeCFGraphList out;
out.entryPoint = entryPoint;
out.userdata = userdata;
std::vector<BridgeCFNodeList> nodeList;
nodeList.reserve(nodes.size());
for(const auto & nodeIt : nodes)
nodeList.push_back(nodeIt.second.ToNodeList());
BridgeList<BridgeCFNodeList>::CopyData(&out.nodes, nodeList);
return std::move(out);
}
};
#endif //__cplusplus

View File

@ -120,6 +120,18 @@ public:
return true;
}
static bool ToVector(const ListInfo* listInfo, std::vector<Type> & listData, bool freedata = true)
{
if(!listInfo || listInfo->size != listInfo->count * sizeof(Type) || (listInfo->count && !listInfo->data))
return false;
listData.resize(listInfo->count);
for(int i = 0; i < listInfo->count; i++)
listData[i] = ((Type*)listInfo->data)[i];
if(freedata && listInfo->data)
BridgeFree(listInfo->data);
return true;
}
private:
ListInfo _listInfo;
};

View File

@ -25,8 +25,6 @@ typedef unsigned long duint;
typedef signed long dsint;
#endif //_WIN64
#include "bridgegraph.h"
#ifndef BRIDGE_IMPEXP
#ifdef BUILD_BRIDGE
#define BRIDGE_IMPEXP __declspec(dllexport)
@ -57,6 +55,17 @@ BRIDGE_IMPEXP bool BridgeSettingFlush();
BRIDGE_IMPEXP bool BridgeSettingRead(int* errorLine);
BRIDGE_IMPEXP int BridgeGetDbgVersion();
#ifdef __cplusplus
}
#endif
#include "bridgegraph.h"
#ifdef __cplusplus
extern "C"
{
#endif
//Debugger defines
#define MAX_LABEL_SIZE 256
#define MAX_COMMENT_SIZE 512

View File

@ -33,6 +33,8 @@ void RecursiveAnalysis::SetMarkers()
duint icount = 0;
for(const auto & node : function.nodes)
{
if(!inRange(node.second.start))
continue;
icount += node.second.icount;
start = min(node.second.start, start);
end = max(node.second.end, end);
@ -64,11 +66,18 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
{
auto start = queue.front();
queue.pop();
if(visited.count(start) || !inRange(start)) //already visited or out of range
if(visited.count(start)) //already visited
continue;
visited.insert(start);
CFNode node(graph.entryPoint, start, start);
if(!inRange(start)) //out of range
{
graph.AddNode(node);
continue;
}
while(true)
{
node.icount++;
@ -151,7 +160,7 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
addr += size;
}
}
//third pass: correct the parents + add brtrue and brfalse to the exits
//third pass: correct the parents + add brtrue and brfalse to the exits + get data
graph.parents.clear();
for(auto & nodeIt : graph.nodes)
{
@ -162,6 +171,14 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
node.exits.push_back(node.brtrue);
if(node.brfalse)
node.exits.push_back(node.brfalse);
if(node.brtrue && !node.brfalse)
node.brtrue = 0;
if(!node.icount)
continue;
auto size = node.end - node.start + (mCp.Disassemble(node.end, translateAddr(node.end)) ? mCp.Size() : 1);
node.data.resize(size);
for(duint i = 0; i < size; i++)
node.data[i] = inRange(node.start + i) ? *translateAddr(node.start + i) : 0x90;
}
mFunctions.push_back(graph);
}

View File

@ -65,6 +65,14 @@ public:
return result;
}
const CFGraph* GetFunctionGraph(duint entry) const
{
for(const auto & function : mFunctions)
if(function.entryPoint == entry)
return &function;
return nullptr;
}
protected:
duint mEntryPoint;
std::vector<CFGraph> mFunctions;

View File

@ -27,10 +27,15 @@ static T callFunc(const T* argv, T(*cbFunction)(Ts...), seq<S...>)
template<typename... Ts>
static bool RegisterEasy(const String & name, duint(*cbFunction)(Ts...))
{
return ExpressionFunctions::Register(name, sizeof...(Ts), [cbFunction](int argc, duint * argv, void* userdata)
{
return callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
});
auto aliases = StringUtils::Split(name, '\1');
if(!ExpressionFunctions::Register(aliases[0], sizeof...(Ts), [cbFunction](int argc, duint * argv, void* userdata)
{
return callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
}))
return false;
for(size_t i = 1; i < aliases.size(); i++)
ExpressionFunctions::RegisterAlias(aliases[0], aliases[i]);
return true;
}
void ExpressionFunctions::Init()
@ -47,6 +52,10 @@ void ExpressionFunctions::Init()
RegisterEasy("mod.size", ModSizeFromAddr);
RegisterEasy("mod.hash", ModHashFromAddr);
RegisterEasy("mod.entry", ModEntryFromAddr);
RegisterEasy("disasm.sel\1dis.sel", disasmsel);
RegisterEasy("dump.sel", dumpsel);
RegisterEasy("stack.sel", stacksel);
}
bool ExpressionFunctions::Register(const String & name, int argc, CBEXPRESSIONFUNCTION cbFunction, void* userdata)
@ -65,13 +74,28 @@ bool ExpressionFunctions::Register(const String & name, int argc, CBEXPRESSIONFU
return true;
}
bool ExpressionFunctions::RegisterAlias(const String & name, const String & alias)
{
EXCLUSIVE_ACQUIRE(LockExpressionFunctions);
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
if(!Register(alias, found->second.argc, found->second.cbFunction, found->second.userdata))
return false;
found->second.aliases.push_back(alias);
return true;
}
bool ExpressionFunctions::Unregister(const String & name)
{
EXCLUSIVE_ACQUIRE(LockExpressionFunctions);
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
auto aliases = found->second.aliases;
mFunctions.erase(found);
for(const auto & alias : found->second.aliases)
Unregister(alias);
return true;
}

View File

@ -9,6 +9,7 @@ public:
static void Init();
static bool Register(const String & name, int argc, CBEXPRESSIONFUNCTION cbFunction, void* userdata = nullptr);
static bool RegisterAlias(const String & name, const String & alias);
static bool Unregister(const String & name);
static bool Call(const String & name, std::vector<duint> & argv, duint & result);
static bool GetArgc(const String & name, int & argc);
@ -20,6 +21,7 @@ private:
int argc;
CBEXPRESSIONFUNCTION cbFunction;
void* userdata;
std::vector<String> aliases;
};
static bool isValidName(const String & name);

View File

@ -24,4 +24,26 @@ namespace Exprfunc
{
return ModGetParty(addr);
}
static duint selstart(int hWindow)
{
SELECTIONDATA selection;
GuiSelectionGet(hWindow, &selection);
return selection.start;
}
duint disasmsel()
{
return selstart(GUI_DISASSEMBLY);
}
duint dumpsel()
{
return selstart(GUI_DUMP);
}
duint stacksel()
{
return selstart(GUI_STACK);
}
}

View File

@ -8,4 +8,8 @@ namespace Exprfunc
duint srcdisp(duint addr);
duint modparty(duint addr);
duint disasmsel();
duint dumpsel();
duint stacksel();
}

View File

@ -2817,3 +2817,32 @@ CMDRESULT cbInstrExinfo(int argc, char* argv[])
}
return STATUS_CONTINUE;
}
CMDRESULT cbInstrGraph(int argc, char* argv[])
{
duint entry;
if(argc < 2 || !valfromstring(argv[1], &entry))
entry = GetContextDataEx(hActiveThread, UE_CIP);
duint start, size, sel = entry;
if(FunctionGet(entry, &start))
entry = start;
auto base = MemFindBaseAddr(entry, &size);
if(!base || !MemIsValidReadPtr(entry))
{
dprintf("Invalid memory address " fhex "!\n", entry);
return STATUS_ERROR;
}
RecursiveAnalysis analysis(base, size, entry, 0);
analysis.Analyse();
auto graph = analysis.GetFunctionGraph(entry);
if(!graph)
{
dputs("No graph generated...");
return STATUS_ERROR;
}
auto graphList = graph->ToGraphList();
GuiGraphAt(sel);
GuiLoadGraph(&graphList);
GuiUpdateAllViews();
return STATUS_CONTINUE;
}

View File

@ -101,5 +101,6 @@ CMDRESULT cbInstrEnableGuiUpdate(int argc, char* argv[]);
CMDRESULT cbInstrExhandlers(int argc, char* argv[]);
CMDRESULT cbInstrInstrUndo(int argc, char* argv[]);
CMDRESULT cbInstrExinfo(int argc, char* argv[]);
CMDRESULT cbInstrGraph(int argc, char* argv[]);
#endif // _INSTRUCTION_H

View File

@ -308,6 +308,7 @@ static void registercommands()
dbgcmdnew("analrecur\1analr", cbInstrAnalrecur, true); //analyze a single function
dbgcmdnew("analxrefs\1analx", cbInstrAnalxrefs, true); //analyze xrefs
dbgcmdnew("analadv", cbInstrAnalyseadv, true); //analyze xref,function and data
dbgcmdnew("graph", cbInstrGraph, true); //graph function
}
static bool cbCommandProvider(char* cmd, int maxlen)

View File

@ -228,109 +228,8 @@ protected:
void invalidateCachedFont();
//action helpers
private:
struct ActionShortcut
{
QAction* action;
QString shortcut;
ActionShortcut(QAction* action, const char* shortcut)
: action(action),
shortcut(shortcut)
{
}
};
std::vector<ActionShortcut> actionShortcutPairs;
inline QAction* connectAction(QAction* action, const char* slot)
{
connect(action, SIGNAL(triggered(bool)), this, slot);
return action;
}
inline QAction* connectAction(QAction* action, QActionLambda::TriggerCallback callback)
{
auto lambda = new QActionLambda(action->parent(), callback);
connect(action, SIGNAL(triggered(bool)), lambda, SLOT(triggeredSlot()));
return action;
}
inline QAction* connectShortcutAction(QAction* action, const char* shortcut)
{
actionShortcutPairs.push_back(ActionShortcut(action, shortcut));
action->setShortcut(ConfigShortcut(shortcut));
action->setShortcutContext(Qt::WidgetShortcut);
addAction(action);
return action;
}
inline QAction* connectMenuAction(QMenu* menu, QAction* action)
{
menu->addAction(action);
return action;
}
protected:
inline QMenu* makeMenu(const QString & title)
{
return new QMenu(title, this);
}
inline QMenu* makeMenu(const QIcon & icon, const QString & title)
{
QMenu* menu = new QMenu(title, this);
menu->setIcon(icon);
return menu;
}
template<typename T>
inline QAction* makeAction(const QString & text, T slot)
{
return connectAction(new QAction(text, this), slot);
}
template<typename T>
inline QAction* makeAction(const QIcon & icon, const QString & text, T slot)
{
return connectAction(new QAction(icon, text, this), slot);
}
template<typename T>
inline QAction* makeShortcutAction(const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeAction(text, slot), shortcut);
}
template<typename T>
inline QAction* makeShortcutAction(const QIcon & icon, const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeAction(icon, text, slot), shortcut);
}
template<typename T>
inline QAction* makeMenuAction(QMenu* menu, const QString & text, T slot)
{
return connectMenuAction(menu, makeAction(text, slot));
}
template<typename T>
inline QAction* makeMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot)
{
return connectMenuAction(menu, makeAction(icon, text, slot));
}
template<typename T>
inline QAction* makeShortcutMenuAction(QMenu* menu, const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeMenuAction(menu, text, slot), shortcut);
}
template<typename T>
inline QAction* makeShortcutMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeMenuAction(menu, icon, text, slot), shortcut);
}
#include "ActionHelpers.h"
};
#endif // ABSTRACTTABLEVIEW_H

View File

@ -277,7 +277,7 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
byte_t wBuffer[16];
if(!DbgMemRead(parVA, wBuffer, 16))
return 0;
QBeaEngine disasm(-1);
QBeaEngine disasm(int(ConfigUint("Disassembler", "MaxModuleSize")));
Instruction_t instr = disasm.DisassembleAt(wBuffer, 16, 0, parVA);
RichTextPainter::List richText;
CapstoneTokenizer::TokenToRichText(instr.tokens, richText, 0);
@ -578,6 +578,22 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
case GUI_UPDATE_WATCH_VIEW:
emit updateWatch();
break;
case GUI_LOAD_GRAPH:
{
BridgeResult result;
emit loadGraph((BridgeCFGraphList*)param1);
result.Wait();
}
break;
case GUI_GRAPH_AT:
emit graphAt(duint(param1));
break;
case GUI_UPDATE_GRAPH_VIEW:
emit updateGraph();
break;
}
return nullptr;
}

View File

@ -124,6 +124,9 @@ signals:
void focusDump();
void focusStack();
void updateWatch();
void loadGraph(BridgeCFGraphList* graph);
void graphAt(duint addr);
void updateGraph();
private:
QMutex* mBridgeMutex;

View File

@ -327,6 +327,7 @@ void CPUDisassembly::setupRightClickContextMenu()
});
mMenuBuilder->addMenu(makeMenu(DIcon("snowman.png"), tr("Decompile")), decompileMenu);
mMenuBuilder->addAction(makeShortcutAction(DIcon("graph.png"), tr("Graph"), SLOT(graphSlot()), "ActionGraph"));
mMenuBuilder->addMenu(makeMenu(DIcon("help.png"), tr("Help on Symbolic Name")), [this](QMenu * menu)
{
@ -1653,3 +1654,9 @@ void CPUDisassembly::setEncodeTypeSlot()
mDisasm->getEncodeMap()->setDataType(rvaToVa(getSelectionStart()), (ENCODETYPE)pAction->data().toUInt());
GuiUpdateDisassemblyView();
}
void CPUDisassembly::graphSlot()
{
DbgCmdExec(QString("graph %1").arg(ToPtrString(rvaToVa(getSelectionStart()))).toUtf8().constData());
emit displayGraphWidget();
}

View File

@ -35,6 +35,7 @@ signals:
void decompileAt(dsint start, dsint end);
void displaySnowmanWidget();
void displayLogWidget();
void displayGraphWidget();
public slots:
void toggleInt3BPActionSlot();
@ -102,6 +103,7 @@ public slots:
void removeAnalysisModuleSlot();
void setEncodeTypeSlot();
void setEncodeTypeRangeSlot();
void graphSlot();
protected:
void paintEvent(QPaintEvent* event);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,289 @@
#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 <algorithm>
#include <QMutex>
#include "Bridge.h"
#include "QBeaEngine.h"
#include "CachedFontMetrics.h"
#include "MenuBuilder.h"
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;
point.row = row;
point.col = col;
point.index = 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)
{
RichTextPainter::List richText;
RichTextPainter::CustomRichText_t rt;
rt.highlight = false;
rt.flags = RichTextPainter::FlagColor;
rt.text = text;
rt.textColor = color;
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;
void print() const
{
puts("----BLOCK---");
printf("header_text: %s\n", header_text.ToQString().toUtf8().constData());
puts("exits:");
for(auto exit : exits)
printf("%X ", exit);
puts("\n--ENDBLOCK--");
}
};
struct DisassemblerBlock
{
DisassemblerBlock() {}
explicit DisassemblerBlock(Block & block)
: block(block) {}
void print() const
{
block.print();
}
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
};
DisassemblerGraphView(QWidget* parent = nullptr);
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 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();
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);
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 navigate(duint addr);
void fontChanged();
public slots:
void updateTimerEvent();
void loadGraphSlot(BridgeCFGraphList* graph);
void graphAtSlot(duint addr);
void updateGraphSlot();
void followDisassemblerSlot();
signals:
void showCpu();
private:
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;
protected:
#include "ActionHelpers.h"
};
#endif // DISASSEMBLERGRAPHVIEW_H

View File

@ -159,7 +159,11 @@ MainWindow::MainWindow(QWidget* parent)
mHandlesView = new HandlesView(this);
mHandlesView->setWindowTitle(tr("Handles"));
mHandlesView->setWindowIcon(DIcon("handles.png"));
mHandlesView->hide();
// Graph view
mGraphView = new DisassemblerGraphView(this);
mGraphView->setWindowTitle(tr("Graph"));
mGraphView->setWindowIcon(DIcon("graph.png"));
// Create the tab widget
mTabWidget = new MHTabWidget();
@ -167,6 +171,8 @@ MainWindow::MainWindow(QWidget* parent)
// Add all widgets to the list
mWidgetList.push_back(mCpuWidget);
mWidgetNativeNameList.push_back("CPUTab");
mWidgetList.push_back(mGraphView);
mWidgetNativeNameList.push_back("GraphTab");
mWidgetList.push_back(mLogView);
mWidgetNativeNameList.push_back("LogTab");
mWidgetList.push_back(mNotesManager);
@ -254,6 +260,7 @@ MainWindow::MainWindow(QWidget* parent)
connect(mSymbolView, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
connect(mSymbolView, SIGNAL(showReferences()), this, SLOT(displayReferencesWidget()));
connect(mReferenceManager, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
connect(mGraphView, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
connect(ui->actionReferences, SIGNAL(triggered()), this, SLOT(displayReferencesWidget()));
connect(ui->actionThreads, SIGNAL(triggered()), this, SLOT(displayThreadsWidget()));
connect(ui->actionSettings, SIGNAL(triggered()), this, SLOT(openSettings()));
@ -281,12 +288,14 @@ MainWindow::MainWindow(QWidget* parent)
connect(ui->actionNotes, SIGNAL(triggered()), this, SLOT(displayNotesWidget()));
connect(ui->actionSnowman, SIGNAL(triggered()), this, SLOT(displaySnowmanWidget()));
connect(ui->actionHandles, SIGNAL(triggered()), this, SLOT(displayHandlesWidget()));
connect(ui->actionGraph, SIGNAL(triggered()), this, SLOT(displayGraphWidget()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(updateWindowTitle(QString)), this, SLOT(updateWindowTitleSlot(QString)));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySourceManagerWidget()), this, SLOT(displaySourceViewWidget()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySnowmanWidget()), this, SLOT(displaySnowmanWidget()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayGraphWidget()), this, SLOT(displayGraphWidget()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(showPatches()), this, SLOT(patchWindow()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(decompileAt(dsint, dsint)), this, SLOT(decompileAt(dsint, dsint)));
@ -450,6 +459,7 @@ void MainWindow::refreshShortcuts()
setGlobalShortcut(ui->actionFunctions, ConfigShortcut("ViewFunctions"));
setGlobalShortcut(ui->actionSnowman, ConfigShortcut("ViewSnowman"));
setGlobalShortcut(ui->actionHandles, ConfigShortcut("ViewHandles"));
setGlobalShortcut(ui->actionGraph, ConfigShortcut("ViewGraph"));
setGlobalShortcut(ui->actionRun, ConfigShortcut("DebugRun"));
setGlobalShortcut(ui->actioneRun, ConfigShortcut("DebugeRun"));
@ -886,6 +896,11 @@ void MainWindow::displaySnowmanWidget()
showQWidgetTab(mSnowmanView);
}
void MainWindow::displayGraphWidget()
{
showQWidgetTab(mGraphView);
}
void MainWindow::hideDebugger()
{
DbgCmdExec("hide");

View File

@ -28,6 +28,7 @@
#include "TimeWastedCounter.h"
#include "NotesManager.h"
#include "SettingsDialog.h"
#include "DisassemblerGraphView.h"
namespace Ui
{
@ -91,6 +92,7 @@ public slots:
void displayReferencesWidget();
void displayThreadsWidget();
void displaySnowmanWidget();
void displayGraphWidget();
void hideDebugger();
void openSettings();
void openAppearance();
@ -167,6 +169,7 @@ private:
SnowmanView* mSnowmanView;
HandlesView* mHandlesView;
NotesManager* mNotesManager;
DisassemblerGraphView* mGraphView;
StatusLabel* mStatusLabel;
StatusLabel* mLastLogLabel;

View File

@ -70,6 +70,7 @@
<addaction name="actionFunctions"/>
<addaction name="actionSnowman"/>
<addaction name="actionHandles"/>
<addaction name="actionGraph"/>
</widget>
<widget class="QMenu" name="menuDebug">
<property name="title">
@ -954,6 +955,15 @@
<string>Step into (source)</string>
</property>
</action>
<action name="actionGraph">
<property name="icon">
<iconset resource="../../resource.qrc">
<normaloff>:/icons/images/graph.png</normaloff>:/icons/images/graph.png</iconset>
</property>
<property name="text">
<string>&amp;Graph</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -0,0 +1,101 @@
struct ActionShortcut
{
QAction* action;
QString shortcut;
ActionShortcut(QAction* action, const char* shortcut)
: action(action),
shortcut(shortcut)
{
}
};
std::vector<ActionShortcut> actionShortcutPairs;
inline QAction* connectAction(QAction* action, const char* slot)
{
connect(action, SIGNAL(triggered(bool)), this, slot);
return action;
}
inline QAction* connectAction(QAction* action, QActionLambda::TriggerCallback callback)
{
auto lambda = new QActionLambda(action->parent(), callback);
connect(action, SIGNAL(triggered(bool)), lambda, SLOT(triggeredSlot()));
return action;
}
inline QAction* connectShortcutAction(QAction* action, const char* shortcut)
{
actionShortcutPairs.push_back(ActionShortcut(action, shortcut));
action->setShortcut(ConfigShortcut(shortcut));
action->setShortcutContext(Qt::WidgetShortcut);
addAction(action);
return action;
}
inline QAction* connectMenuAction(QMenu* menu, QAction* action)
{
menu->addAction(action);
return action;
}
inline QMenu* makeMenu(const QString & title)
{
return new QMenu(title, this);
}
inline QMenu* makeMenu(const QIcon & icon, const QString & title)
{
QMenu* menu = new QMenu(title, this);
menu->setIcon(icon);
return menu;
}
template<typename T>
inline QAction* makeAction(const QString & text, T slot)
{
return connectAction(new QAction(text, this), slot);
}
template<typename T>
inline QAction* makeAction(const QIcon & icon, const QString & text, T slot)
{
return connectAction(new QAction(icon, text, this), slot);
}
template<typename T>
inline QAction* makeShortcutAction(const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeAction(text, slot), shortcut);
}
template<typename T>
inline QAction* makeShortcutAction(const QIcon & icon, const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeAction(icon, text, slot), shortcut);
}
template<typename T>
inline QAction* makeMenuAction(QMenu* menu, const QString & text, T slot)
{
return connectMenuAction(menu, makeAction(text, slot));
}
template<typename T>
inline QAction* makeMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot)
{
return connectMenuAction(menu, makeAction(icon, text, slot));
}
template<typename T>
inline QAction* makeShortcutMenuAction(QMenu* menu, const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeMenuAction(menu, text, slot), shortcut);
}
template<typename T>
inline QAction* makeShortcutMenuAction(QMenu* menu, const QIcon & icon, const QString & text, T slot, const char* shortcut)
{
return connectShortcutAction(makeMenuAction(menu, icon, text, slot), shortcut);
}

View File

@ -227,6 +227,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
QMap<QString, duint> tabOrderUint;
int curTab = 0;
tabOrderUint.insert("CPUTab", curTab++);
tabOrderUint.insert("GraphTab", curTab++);
tabOrderUint.insert("LogTab", curTab++);
tabOrderUint.insert("NotesTab", curTab++);
tabOrderUint.insert("BreakpointsTab", curTab++);
@ -278,6 +279,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultShortcuts.insert("ViewFunctions", Shortcut(tr("View -> Functions"), "Alt+F", true));
defaultShortcuts.insert("ViewSnowman", Shortcut(tr("View -> Snowman"), "", true));
defaultShortcuts.insert("ViewHandles", Shortcut(tr("View -> Handles"), "", true));
defaultShortcuts.insert("ViewGraph", Shortcut(tr("View -> Graph"), "Alt+G", true));
defaultShortcuts.insert("DebugRun", Shortcut(tr("Debug -> Run"), "F9", true));
defaultShortcuts.insert("DebugeRun", Shortcut(tr("Debug -> Run (skip exceptions)"), "Shift+F9", true));
@ -355,7 +357,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultShortcuts.insert("ActionFindPattern", Shortcut(tr("Actions -> Find Pattern"), "Ctrl+B"));
defaultShortcuts.insert("ActionFindReferences", Shortcut(tr("Actions -> Find References"), "Ctrl+R"));
defaultShortcuts.insert("ActionXrefs", Shortcut(tr("Actions -> xrefs..."), "X"));
defaultShortcuts.insert("ActionAnalyzeSingleFunction", Shortcut(tr("Actions -> Analyze Single Function"), "Ctrl+Shift+A"));
defaultShortcuts.insert("ActionAnalyzeSingleFunction", Shortcut(tr("Actions -> Analyze Single Function"), "A"));
defaultShortcuts.insert("ActionHelpOnMnemonic", Shortcut(tr("Actions -> Help on Mnemonic"), "Ctrl+F1"));
defaultShortcuts.insert("ActionToggleMnemonicBrief", Shortcut(tr("Actions -> Toggle Mnemonic Brief"), "Ctrl+Shift+F1"));
defaultShortcuts.insert("ActionHighlightingMode", Shortcut(tr("Actions -> Highlighting Mode"), "Ctrl+H"));
@ -410,6 +412,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultShortcuts.insert("ActionAbortScript", Shortcut(tr("Actions -> Abort Script"), "Esc"));
defaultShortcuts.insert("ActionExecuteCommandScript", Shortcut(tr("Actions -> Execute Script Command"), "X"));
defaultShortcuts.insert("ActionRefresh", Shortcut(tr("Actions -> Refresh"), "F5"));
defaultShortcuts.insert("ActionGraph", Shortcut(tr("Actions -> Graph"), "G"));
Shortcuts = defaultShortcuts;

BIN
src/gui/images/graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

View File

@ -249,5 +249,6 @@
<file>images/undo.png</file>
<file>images/binary_save.png</file>
<file>images/animal-dog.png</file>
<file>images/graph.png</file>
</qresource>
</RCC>

View File

@ -165,7 +165,8 @@ SOURCES += \
Src/Utils/CodeFolding.cpp \
Src/Gui/WatchView.cpp \
Src/Gui/FavouriteTools.cpp \
Src/Gui/BrowseDialog.cpp
Src/Gui/BrowseDialog.cpp \
Src/Gui/DisassemblerGraphView.cpp
HEADERS += \
@ -266,7 +267,9 @@ HEADERS += \
Src/Utils/CodeFolding.h \
Src/Gui/WatchView.h \
Src/Gui/FavouriteTools.h \
Src/Gui/BrowseDialog.h
Src/Gui/BrowseDialog.h \
Src/Gui/DisassemblerGraphView.h \
Src/Utils/ActionHelpers.h
FORMS += \