BRIDGE+DBG+GUI: basics of analysis plugins
This commit is contained in:
parent
d0c755e3f8
commit
9138a3410e
|
|
@ -51,7 +51,15 @@ struct BridgeCFNode
|
|||
std::vector<duint> exits; //exits (including brtrue and brfalse)
|
||||
std::vector<BridgeCFInstruction> instrs; //block instructions
|
||||
|
||||
explicit BridgeCFNode(BridgeCFNodeList* nodeList, bool freedata = true)
|
||||
static void Free(const BridgeCFNodeList* nodeList)
|
||||
{
|
||||
if(!BridgeList<duint>::Free(&nodeList->exits))
|
||||
__debugbreak();
|
||||
if(!BridgeList<BridgeCFInstruction>::Free(&nodeList->instrs))
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
explicit BridgeCFNode(const BridgeCFNodeList* nodeList, bool freedata)
|
||||
{
|
||||
if(!nodeList)
|
||||
__debugbreak();
|
||||
|
|
@ -113,7 +121,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, bool freedata = true)
|
||||
static void Free(const BridgeCFGraphList* graphList)
|
||||
{
|
||||
if(!graphList || graphList->nodes.size != graphList->nodes.count * sizeof(BridgeCFNodeList))
|
||||
__debugbreak();
|
||||
auto data = (BridgeCFNodeList*)graphList->nodes.data;
|
||||
for(int i = 0; i < graphList->nodes.count; i++)
|
||||
BridgeCFNode::Free(&data[i]);
|
||||
BridgeFree(data);
|
||||
}
|
||||
|
||||
explicit BridgeCFGraph(const BridgeCFGraphList* graphList, bool freedata)
|
||||
{
|
||||
if(!graphList || graphList->nodes.size != graphList->nodes.count * sizeof(BridgeCFNodeList))
|
||||
__debugbreak();
|
||||
|
|
|
|||
|
|
@ -120,6 +120,14 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool Free(const ListInfo* listInfo)
|
||||
{
|
||||
if(!listInfo || listInfo->size != listInfo->count * sizeof(Type) || (listInfo->count && !listInfo->data))
|
||||
return false;
|
||||
BridgeFree(listInfo->data);
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -1463,9 +1463,9 @@ BRIDGE_IMPEXP void GuiLoadGraph(BridgeCFGraphList* graph, duint addr)
|
|||
_gui_sendmessage(GUI_LOAD_GRAPH, graph, (void*)addr);
|
||||
}
|
||||
|
||||
BRIDGE_IMPEXP bool GuiGraphAt(duint addr)
|
||||
BRIDGE_IMPEXP duint GuiGraphAt(duint addr)
|
||||
{
|
||||
return !!_gui_sendmessage(GUI_GRAPH_AT, (void*)addr, nullptr);
|
||||
return (duint)_gui_sendmessage(GUI_GRAPH_AT, (void*)addr, nullptr);
|
||||
}
|
||||
|
||||
BRIDGE_IMPEXP void GuiUpdateGraphView()
|
||||
|
|
|
|||
|
|
@ -1102,7 +1102,7 @@ BRIDGE_IMPEXP bool GuiIsUpdateDisabled();
|
|||
BRIDGE_IMPEXP void GuiUpdateEnable(bool updateNow);
|
||||
BRIDGE_IMPEXP void GuiUpdateDisable();
|
||||
BRIDGE_IMPEXP void GuiLoadGraph(BridgeCFGraphList* graph, duint addr);
|
||||
BRIDGE_IMPEXP bool GuiGraphAt(duint addr);
|
||||
BRIDGE_IMPEXP duint GuiGraphAt(duint addr);
|
||||
BRIDGE_IMPEXP void GuiUpdateGraphView();
|
||||
BRIDGE_IMPEXP void GuiDisableLog();
|
||||
BRIDGE_IMPEXP void GuiEnableLog();
|
||||
|
|
|
|||
|
|
@ -194,6 +194,11 @@ typedef struct
|
|||
duint VA;
|
||||
} PLUG_CB_SELCHANGED;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BridgeCFGraphList graph;
|
||||
} PLUG_CB_ANALYZE;
|
||||
|
||||
//enums
|
||||
typedef enum
|
||||
{
|
||||
|
|
@ -223,6 +228,7 @@ typedef enum
|
|||
CB_FILTERSYMBOL, //PLUG_CB_FILTERSYMBOL
|
||||
CB_TRACEEXECUTE, //PLUG_CB_TRACEEXECUTE
|
||||
CB_SELCHANGED, //PLUG_CB_SELCHANGED
|
||||
CB_ANALYZE, //PLUG_CB_ANALYZE
|
||||
CB_LAST
|
||||
} CBTYPE;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@
|
|||
#include "filehelper.h"
|
||||
#include "function.h"
|
||||
#include "xrefs.h"
|
||||
#include "plugin_loader.h"
|
||||
|
||||
RecursiveAnalysis::RecursiveAnalysis(duint base, duint size, duint entryPoint, duint maxDepth, bool dump)
|
||||
RecursiveAnalysis::RecursiveAnalysis(duint base, duint size, duint entryPoint, duint maxDepth, bool usePlugins, bool dump)
|
||||
: Analysis(base, size),
|
||||
mEntryPoint(entryPoint),
|
||||
mMaxDepth(maxDepth),
|
||||
mUsePlugins(usePlugins),
|
||||
mDump(dump)
|
||||
{
|
||||
}
|
||||
|
|
@ -200,5 +202,13 @@ void RecursiveAnalysis::analyzeFunction(duint entryPoint)
|
|||
addr += size;
|
||||
}
|
||||
}
|
||||
//fourth pass: allow plugins to manipulate the graph
|
||||
if(mUsePlugins && !plugincbempty(CB_ANALYZE))
|
||||
{
|
||||
PLUG_CB_ANALYZE info;
|
||||
info.graph = graph.ToGraphList();
|
||||
plugincbcall(CB_ANALYZE, &info);
|
||||
graph = BridgeCFGraph(&info.graph, true);
|
||||
}
|
||||
mFunctions.push_back(graph);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
class RecursiveAnalysis : public Analysis
|
||||
{
|
||||
public:
|
||||
explicit RecursiveAnalysis(duint base, duint size, duint entryPoint, duint maxDepth, bool dump = false);
|
||||
explicit RecursiveAnalysis(duint base, duint size, duint entryPoint, duint maxDepth, bool usePlugins, bool dump = false);
|
||||
void Analyse() override;
|
||||
void SetMarkers() override;
|
||||
|
||||
|
|
@ -83,6 +83,7 @@ protected:
|
|||
|
||||
private:
|
||||
duint mMaxDepth;
|
||||
bool mUsePlugins;
|
||||
bool mDump;
|
||||
|
||||
struct XREF
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ bool cbInstrAnalrecur(int argc, char* argv[])
|
|||
auto base = MemFindBaseAddr(entry, &size);
|
||||
if(!base)
|
||||
return false;
|
||||
RecursiveAnalysis analysis(base, size, entry, 0);
|
||||
RecursiveAnalysis analysis(base, size, entry, 0, true);
|
||||
analysis.Analyse();
|
||||
analysis.SetMarkers();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ bool cbDebugMemmapdump(int argc, char* argv[])
|
|||
|
||||
bool cbInstrGraph(int argc, char* argv[])
|
||||
{
|
||||
auto options = argc > 2 ? argv[2] : "";
|
||||
auto force = !!strstr(options, "force");
|
||||
auto silent = !!strstr(options, "silent");
|
||||
duint entry;
|
||||
if(argc < 2 || !valfromstring(argv[1], &entry))
|
||||
entry = GetContextDataEx(hActiveThread, UE_CIP);
|
||||
|
|
@ -103,21 +106,25 @@ bool cbInstrGraph(int argc, char* argv[])
|
|||
auto base = MemFindBaseAddr(entry, &size);
|
||||
if(!base || !MemIsValidReadPtr(entry))
|
||||
{
|
||||
if(argc <= 2) //undocumented silent option
|
||||
if(!silent)
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid address %p!\n"), entry);
|
||||
return false;
|
||||
}
|
||||
if(!GuiGraphAt(sel))
|
||||
auto curEntry = GuiGraphAt(sel);
|
||||
if(curEntry)
|
||||
entry = curEntry;
|
||||
if(!curEntry || force)
|
||||
{
|
||||
auto modbase = ModBaseFromAddr(base);
|
||||
if(modbase)
|
||||
base = modbase, size = ModSizeFromAddr(modbase);
|
||||
RecursiveAnalysis analysis(base, size, entry, 0);
|
||||
RecursiveAnalysis analysis(base, size, entry, 0, true);
|
||||
analysis.Analyse();
|
||||
auto graph = analysis.GetFunctionGraph(entry);
|
||||
if(!graph)
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "No graph generated..."));
|
||||
if(!silent)
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "No graph generated..."));
|
||||
return false;
|
||||
}
|
||||
auto graphList = graph->ToGraphList();
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ bool pluginload(const char* pluginName, bool loadall)
|
|||
regExport("CBSAVEDB", CB_SAVEDB);
|
||||
regExport("CBFILTERSYMBOL", CB_FILTERSYMBOL);
|
||||
regExport("CBTRACEEXECUTE", CB_TRACEEXECUTE);
|
||||
regExport("CBANALYZE", CB_ANALYZE);
|
||||
|
||||
//init plugin
|
||||
if(!pluginData.pluginit(&pluginData.initStruct))
|
||||
|
|
@ -489,15 +490,25 @@ bool pluginunregistercallback(int pluginHandle, CBTYPE cbType)
|
|||
*/
|
||||
void plugincbcall(CBTYPE cbType, void* callbackInfo)
|
||||
{
|
||||
SHARED_ACQUIRE(LockPluginCallbackList);
|
||||
if(pluginCallbackList[cbType].empty())
|
||||
return;
|
||||
SHARED_ACQUIRE(LockPluginCallbackList);
|
||||
auto cbList = pluginCallbackList[cbType]; //copy for thread-safety reasons
|
||||
SHARED_RELEASE();
|
||||
for(const auto & currentCallback : cbList)
|
||||
currentCallback.cbPlugin(cbType, callbackInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Checks if any callbacks are registered
|
||||
\param cbType The type of the callback.
|
||||
\return true if no callbacks are registered.
|
||||
*/
|
||||
bool plugincbempty(CBTYPE cbType)
|
||||
{
|
||||
return pluginCallbackList[cbType].empty();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Register a plugin command.
|
||||
\param pluginHandle Handle of the plugin to register a command for.
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ void pluginexprfuncunregisterall(int pluginHandle);
|
|||
void pluginregistercallback(int pluginHandle, CBTYPE cbType, CBPLUGIN cbPlugin);
|
||||
bool pluginunregistercallback(int pluginHandle, CBTYPE cbType);
|
||||
void plugincbcall(CBTYPE cbType, void* callbackInfo);
|
||||
bool plugincbempty(CBTYPE cbType);
|
||||
bool plugincmdregister(int pluginHandle, const char* command, CBPLUGINCOMMAND cbCommand, bool debugonly);
|
||||
bool plugincmdunregister(int pluginHandle, const char* command);
|
||||
int pluginmenuadd(int hMenu, const char* title);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget* parent)
|
|||
disasm(ConfigUint("Disassembler", "MaxModuleSize")),
|
||||
mCip(0),
|
||||
mGoto(nullptr),
|
||||
syncOrigin(false)
|
||||
syncOrigin(false),
|
||||
forceCenter(false)
|
||||
{
|
||||
this->status = "Loading...";
|
||||
|
||||
|
|
@ -606,7 +607,7 @@ void DisassemblerGraphView::mouseReleaseEvent(QMouseEvent* event)
|
|||
void DisassemblerGraphView::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
duint instr = this->getInstrForMouseEvent(event);
|
||||
DbgCmdExec(QString("graph dis.branchdest(%1), 1").arg(ToPtrString(instr)).toUtf8().constData());
|
||||
DbgCmdExec(QString("graph dis.branchdest(%1), silent").arg(ToPtrString(instr)).toUtf8().constData());
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::prepareGraphNode(DisassemblerBlock & block)
|
||||
|
|
@ -1199,7 +1200,10 @@ void DisassemblerGraphView::renderFunction(Function & func)
|
|||
this->verticalScrollBar()->setValue(this->desired_pos[1]);
|
||||
}
|
||||
else if(this->cur_instr != 0)
|
||||
this->show_cur_instr();
|
||||
{
|
||||
this->show_cur_instr(this->forceCenter);
|
||||
this->forceCenter = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Ensure start node is visible
|
||||
|
|
@ -1245,7 +1249,7 @@ void DisassemblerGraphView::updateTimerEvent()
|
|||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::show_cur_instr()
|
||||
void DisassemblerGraphView::show_cur_instr(bool force)
|
||||
{
|
||||
for(auto & blockIt : this->blocks)
|
||||
{
|
||||
|
|
@ -1261,8 +1265,8 @@ void DisassemblerGraphView::show_cur_instr()
|
|||
QRect viewportRect = this->viewport()->rect();
|
||||
QPoint translation(this->renderXOfs - xofs, this->renderYOfs - yofs);
|
||||
viewportRect.translate(-translation.x(), -translation.y());
|
||||
if(!viewportRect.contains(QRect(block.x + this->charWidth , block.y + this->charWidth,
|
||||
block.width - (2 * this->charWidth), block.height - (2 * this->charWidth))))
|
||||
if(force || !viewportRect.contains(QRect(block.x + this->charWidth , block.y + this->charWidth,
|
||||
block.width - (2 * this->charWidth), block.height - (2 * this->charWidth))))
|
||||
{
|
||||
auto x = block.x + int(block.width / 2);
|
||||
auto y = block.y + (2 * this->charWidth) + int((row + 0.5) * this->charHeight);
|
||||
|
|
@ -1347,7 +1351,7 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||
{
|
||||
const BridgeCFNode & node = nodeIt.second;
|
||||
Block block;
|
||||
block.entry = node.start;
|
||||
block.entry = node.instrs.empty() ? node.start : node.instrs[0].addr;
|
||||
block.exits = node.exits;
|
||||
block.false_path = node.brfalse;
|
||||
block.true_path = node.brtrue;
|
||||
|
|
@ -1439,16 +1443,17 @@ QString DisassemblerGraphView::getSymbolicName(duint addr)
|
|||
|
||||
void DisassemblerGraphView::loadGraphSlot(BridgeCFGraphList* graphList, duint addr)
|
||||
{
|
||||
currentGraph = BridgeCFGraph(graphList);
|
||||
currentGraph = BridgeCFGraph(graphList, true);
|
||||
currentBlockMap.clear();
|
||||
loadCurrentGraph();
|
||||
this->cur_instr = addr ? addr : this->function;
|
||||
this->forceCenter = true;
|
||||
loadCurrentGraph();
|
||||
Bridge::getBridge()->setResult();
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::graphAtSlot(duint addr)
|
||||
{
|
||||
Bridge::getBridge()->setResult(this->navigate(addr));
|
||||
Bridge::getBridge()->setResult(this->navigate(addr) ? this->currentGraph.entryPoint : 0);
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::updateGraphSlot()
|
||||
|
|
@ -1471,7 +1476,7 @@ void DisassemblerGraphView::setupContextMenu()
|
|||
|
||||
mMenuBuilder->addAction(mToggleOverview = makeShortcutAction(DIcon("graph.png"), tr("&Overview"), SLOT(toggleOverviewSlot()), "ActionGraphToggleOverview"));
|
||||
mMenuBuilder->addAction(mToggleSyncOrigin = makeShortcutAction(DIcon("lock.png"), tr("&Sync with origin"), SLOT(toggleSyncOriginSlot()), "ActionGraphSyncOrigin"));
|
||||
mMenuBuilder->addAction(makeShortcutAction(DIcon("sync.png"), tr("&Refresh"), SLOT(loadCurrentGraph()), "ActionGraphRefresh"));
|
||||
mMenuBuilder->addAction(makeShortcutAction(DIcon("sync.png"), tr("&Refresh"), SLOT(refreshSlot()), "ActionGraphRefresh"));
|
||||
MenuBuilder* gotoMenu = new MenuBuilder(this);
|
||||
gotoMenu->addAction(makeShortcutAction(DIcon("geolocation-goto.png"), tr("Expression"), SLOT(gotoExpressionSlot()), "ActionGotoExpression"));
|
||||
gotoMenu->addAction(makeShortcutAction(DIcon("cbp.png"), tr("Origin"), SLOT(gotoOriginSlot()), "ActionGotoOrigin"));
|
||||
|
|
@ -1486,15 +1491,15 @@ void DisassemblerGraphView::keyPressEvent(QKeyEvent* event)
|
|||
return;
|
||||
int key = event->key();
|
||||
if(key == Qt::Key_Up)
|
||||
DbgCmdExec(QString("graph dis.prev(%1), 1").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
DbgCmdExec(QString("graph dis.prev(%1), silent").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
else if(key == Qt::Key_Down)
|
||||
DbgCmdExec(QString("graph dis.next(%1), 1").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
DbgCmdExec(QString("graph dis.next(%1), silent").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
else if(key == Qt::Key_Left)
|
||||
DbgCmdExec(QString("graph dis.brtrue(%1), 1").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
DbgCmdExec(QString("graph dis.brtrue(%1), silent").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
else if(key == Qt::Key_Right)
|
||||
DbgCmdExec(QString("graph dis.brfalse(%1), 1").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
DbgCmdExec(QString("graph dis.brfalse(%1), silent").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
if(key == Qt::Key_Return || key == Qt::Key_Enter)
|
||||
DbgCmdExec(QString("graph dis.branchdest(%1), 1").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
DbgCmdExec(QString("graph dis.branchdest(%1), silent").arg(ToPtrString(cur_instr)).toUtf8().constData());
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::followDisassemblerSlot()
|
||||
|
|
@ -1575,13 +1580,13 @@ void DisassemblerGraphView::gotoExpressionSlot()
|
|||
if(mGoto->exec() == QDialog::Accepted)
|
||||
{
|
||||
duint value = DbgValFromString(mGoto->expressionText.toUtf8().constData());
|
||||
DbgCmdExec(QString().sprintf("graph %p, 1", value).toUtf8().constData());
|
||||
DbgCmdExec(QString().sprintf("graph %p, silent", value).toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::gotoOriginSlot()
|
||||
{
|
||||
DbgCmdExec("graph cip, 1");
|
||||
DbgCmdExec("graph cip, silent");
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::toggleSyncOriginSlot()
|
||||
|
|
@ -1592,3 +1597,8 @@ void DisassemblerGraphView::toggleSyncOriginSlot()
|
|||
if(syncOrigin)
|
||||
gotoOriginSlot();
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::refreshSlot()
|
||||
{
|
||||
DbgCmdExec(QString("graph %1, force").arg(ToPtrString(this->cur_instr)).toUtf8().constData());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ public:
|
|||
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();
|
||||
void show_cur_instr(bool force = false);
|
||||
bool navigate(duint addr);
|
||||
void fontChanged();
|
||||
QString getSymbolicName(duint addr);
|
||||
|
|
@ -270,6 +270,7 @@ public slots:
|
|||
void gotoExpressionSlot();
|
||||
void gotoOriginSlot();
|
||||
void toggleSyncOriginSlot();
|
||||
void refreshSlot();
|
||||
|
||||
private:
|
||||
QString status;
|
||||
|
|
@ -305,6 +306,7 @@ private:
|
|||
int overviewYOfs;
|
||||
qreal overviewScale;
|
||||
duint mCip;
|
||||
bool forceCenter;
|
||||
|
||||
QAction* mToggleOverview;
|
||||
QAction* mToggleSyncOrigin;
|
||||
|
|
|
|||
Loading…
Reference in New Issue