Add agfm command to print cfg graphs using mermaid syntax ##analysis

This commit is contained in:
Dennis Goodlett 2022-02-18 06:31:32 -05:00 committed by GitHub
parent f59b7dfb56
commit eccee9beb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 106 additions and 0 deletions

View File

@ -9703,6 +9703,62 @@ R_API void cmd_agfb2(RCore *core, const char *s) {
free (pix);
}
static bool cmd_graph_mermaid(RCore *core) {
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
if (!fcn || !fcn->bbs) {
return false;
}
bool ret = true;
// for info on mermaid syntax: https://mermaid-js.github.io/mermaid/#/stateDiagram
RStrBuf *nodes = r_strbuf_new ("stateDiagram-v2\n");
RStrBuf *edges = r_strbuf_new ("");
// TODO: add themeing to nodes buff here -> https://mermaid-js.github.io/mermaid/#/theming
RAnalBlock *b;
RListIter *iter;
r_list_sort (fcn->bbs, bb_cmp);
r_list_foreach (fcn->bbs, iter, b) {
// node names start with _0x b/c 0x makes mermaids mad somehow
if (b->addr != fcn->addr) {
ret &= r_strbuf_appendf (nodes, "\tstate \"0x%" PFMT64x "\" as _0x%" PFMT64x "\n", b->addr, b->addr);
} else {
ret &= r_strbuf_appendf (nodes, "\tstate \"ENTRY: 0x%" PFMT64x "\" as _0x%" PFMT64x "\n", b->addr, b->addr);
}
// TODO: make body contain assembly, this needs to be done with some care so characters are not misinterpreted
if (b->jump != UT64_MAX) {
if (b->fail != UT64_MAX) {
ret &= r_strbuf_appendf (edges, "\t_0x%" PFMT64x " --> _0x%" PFMT64x ": true\n", b->addr, b->jump);
ret &= r_strbuf_appendf (edges, "\t_0x%" PFMT64x " --> _0x%" PFMT64x ": false\n", b->addr, b->fail);
} else {
ret &= r_strbuf_appendf (edges, "\t_0x%" PFMT64x " --> _0x%" PFMT64x "\n", b->addr, b->jump);
}
} else if (b->fail != UT64_MAX) {
ret &= r_strbuf_appendf (edges, "\t_0x%" PFMT64x " --> _0x%" PFMT64x "\n", b->addr, b->fail);
}
if (!ret) {
break;
}
}
if (ret) {
char *n = r_strbuf_drain_nofree (nodes);
char *e = r_strbuf_drain_nofree (edges);
if (n && e) {
r_cons_print (n);
r_cons_print (e);
}
free (n);
free (e);
}
r_strbuf_free (nodes);
r_strbuf_free (edges);
return ret;
}
static void cmd_anal_graph(RCore *core, const char *input) {
core->graph->show_node_titles = r_config_get_i (core->config, "graph.ntitles");
r_cons_enable_highlight (false);
@ -9715,6 +9771,9 @@ static void cmd_anal_graph(RCore *core, const char *input) {
case 'b': // "agfb" // braile
cmd_agfb (core);
break;
case 'm': /// "agfm" // mermaid
cmd_graph_mermaid (core);
break;
case ' ': { // "agf "
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
r_core_visual_graph (core, NULL, fcn, false);

View File

@ -61,3 +61,50 @@ digraph code {
}
EOF
RUN
NAME=agfm basic mermaid output
FILE=bins/elf/ls
CMDS=<<EOF
s sym._obstack_newchunk
af
agfm
EOF
EXPECT=<<EOF
stateDiagram-v2
state "ENTRY: 0x15cc0" as _0x15cc0
state "0x15d06" as _0x15d06
state "0x15d12" as _0x15d12
state "0x15d29" as _0x15d29
state "0x15d67" as _0x15d67
state "0x15d7e" as _0x15d7e
state "0x15da0" as _0x15da0
state "0x15db5" as _0x15db5
state "0x15dc9" as _0x15dc9
state "0x15dd0" as _0x15dd0
state "0x15dd5" as _0x15dd5
state "0x15dda" as _0x15dda
state "0x15de3" as _0x15de3
state "0x15df0" as _0x15df0
state "0x15df6" as _0x15df6
_0x15cc0 --> _0x15db5: true
_0x15cc0 --> _0x15d06: false
_0x15d06 --> _0x15db5: true
_0x15d06 --> _0x15d12: false
_0x15d12 --> _0x15db5: true
_0x15d12 --> _0x15d29: false
_0x15d29 --> _0x15d7e: true
_0x15d29 --> _0x15d67: false
_0x15d67 --> _0x15da0: true
_0x15d67 --> _0x15d7e: false
_0x15da0 --> _0x15d7e
_0x15db5 --> _0x15df6: true
_0x15db5 --> _0x15dc9: false
_0x15dc9 --> _0x15dd0
_0x15dd0 --> _0x15dda: true
_0x15dd0 --> _0x15dd5: false
_0x15dd5 --> _0x15df0: true
_0x15dd5 --> _0x15dda: false
_0x15dda --> _0x15dd0: true
_0x15dda --> _0x15de3: false
EOF
RUN