Add graph diffing and better function diffing (#1131)

* Added function diffing and graph diffing (json and dot only)
* Added tests for new functions and graph diffing
* diff graph more clear
* enforce same colors everywhere.
* using realsize for calculating function size
* renaming unconditional jump color name
* fixed memory issues and weird behaviour
This commit is contained in:
Giovanni 2021-05-18 14:49:53 +02:00 committed by GitHub
parent 52ca5f8093
commit 1348c247df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 762 additions and 348 deletions

View file

@ -51,17 +51,17 @@ RZ_API int rz_analysis_diff_fingerprint_bb(RzAnalysis *analysis, RzAnalysisBlock
int oplen, idx = 0;
if (!analysis) {
return false;
return -1;
}
if (analysis->cur && analysis->cur->fingerprint_bb) {
return (analysis->cur->fingerprint_bb(analysis, bb));
}
if (!(bb->fingerprint = malloc(1 + bb->size))) {
return false;
return -1;
}
if (!(buf = malloc(bb->size + 1))) {
free(bb->fingerprint);
return false;
return -1;
}
if (analysis->iob.read_at(analysis->iob.io, bb->addr, buf, bb->size)) {
memcpy(bb->fingerprint, buf, bb->size);
@ -69,7 +69,7 @@ RZ_API int rz_analysis_diff_fingerprint_bb(RzAnalysis *analysis, RzAnalysisBlock
if (!(op = rz_analysis_op_new())) {
free(bb->fingerprint);
free(buf);
return false;
return -1;
}
while (idx < bb->size) {
if ((oplen = rz_analysis_op(analysis, op, 0, buf + idx, bb->size - idx, RZ_ANALYSIS_OP_MASK_BASIC)) < 1) {
@ -156,6 +156,7 @@ RZ_API bool rz_analysis_diff_bb(RzAnalysis *analysis, RzAnalysisFunction *fcn, R
fcn->diff->type = fcn2->diff->type =
RZ_ANALYSIS_DIFF_TYPE_UNMATCH;
}
mbb->diff->dist = mbb2->diff->dist = ot;
RZ_FREE(mbb->fingerprint);
RZ_FREE(mbb2->fingerprint);
mbb->diff->addr = mbb2->addr;
@ -193,7 +194,7 @@ RZ_API int rz_analysis_diff_fcn(RzAnalysis *analysis, RzList *fcns, RzList *fcns
rz_diff_levenstein_distance(fcn->fingerprint, fcn->fingerprint_size,
fcn2->fingerprint, fcn2->fingerprint_size, NULL, &t);
/* Set flag in matched functions */
fcn->diff->type = fcn2->diff->type = (t >= 1)
fcn->diff->type = fcn2->diff->type = (t >= 1.0)
? RZ_ANALYSIS_DIFF_TYPE_MATCH
: RZ_ANALYSIS_DIFF_TYPE_UNMATCH;
fcn->diff->dist = fcn2->diff->dist = t;
@ -201,8 +202,8 @@ RZ_API int rz_analysis_diff_fcn(RzAnalysis *analysis, RzList *fcns, RzList *fcns
RZ_FREE(fcn2->fingerprint);
fcn->diff->addr = fcn2->addr;
fcn2->diff->addr = fcn->addr;
fcn->diff->size = rz_analysis_function_linear_size(fcn2);
fcn2->diff->size = rz_analysis_function_linear_size(fcn);
fcn->diff->size = rz_analysis_function_realsize(fcn2);
fcn2->diff->size = rz_analysis_function_realsize(fcn);
RZ_FREE(fcn->diff->name);
if (fcn2->name) {
fcn->diff->name = strdup(fcn2->name);
@ -218,21 +219,14 @@ RZ_API int rz_analysis_diff_fcn(RzAnalysis *analysis, RzList *fcns, RzList *fcns
}
/* Compare remaining functions */
rz_list_foreach (fcns, iter, fcn) {
/*
if ((fcn->type != RZ_ANALYSIS_FCN_TYPE_FCN &&
fcn->type != RZ_ANALYSIS_FCN_TYPE_SYM) ||
fcn->diff->type != RZ_ANALYSIS_DIFF_TYPE_NULL) {
continue;
}
*/
if (fcn->diff->type != RZ_ANALYSIS_DIFF_TYPE_NULL) {
continue;
}
ot = 0;
mfcn = mfcn2 = NULL;
rz_list_foreach (fcns2, iter2, fcn2) {
ut64 fcn_size = rz_analysis_function_linear_size(fcn);
ut64 fcn2_size = rz_analysis_function_linear_size(fcn2);
ut64 fcn_size = rz_analysis_function_realsize(fcn);
ut64 fcn2_size = rz_analysis_function_realsize(fcn2);
if (fcn_size > fcn2_size) {
maxsize = fcn_size;
minsize = fcn2_size;
@ -241,11 +235,9 @@ RZ_API int rz_analysis_diff_fcn(RzAnalysis *analysis, RzList *fcns, RzList *fcns
minsize = fcn_size;
}
if (maxsize * analysis->diff_thfcn > minsize) {
eprintf("Exceeded analysis threshold while diffing %s and %s\n", fcn->name, fcn2->name);
continue;
}
if (fcn2->diff->type != RZ_ANALYSIS_DIFF_TYPE_NULL) {
eprintf("Function %s already diffed\n", fcn2->name);
continue;
}
if ((fcn2->type != RZ_ANALYSIS_FCN_TYPE_FCN && fcn2->type != RZ_ANALYSIS_FCN_TYPE_SYM)) {
@ -272,8 +264,8 @@ RZ_API int rz_analysis_diff_fcn(RzAnalysis *analysis, RzList *fcns, RzList *fcns
RZ_FREE(mfcn2->fingerprint);
mfcn->diff->addr = mfcn2->addr;
mfcn2->diff->addr = mfcn->addr;
mfcn->diff->size = rz_analysis_function_linear_size(mfcn2);
mfcn2->diff->size = rz_analysis_function_linear_size(mfcn);
mfcn->diff->size = rz_analysis_function_realsize(mfcn2);
mfcn2->diff->size = rz_analysis_function_realsize(mfcn);
RZ_FREE(mfcn->diff->name);
if (mfcn2->name) {
mfcn->diff->name = strdup(mfcn2->name);

View file

@ -1757,7 +1757,7 @@ RZ_API RZ_OWN char *rz_analysis_function_get_signature(RzAnalysisFunction *funct
char *args = strdup("");
for (i = 0; i < argc; i++) {
const char *arg_name = rz_type_func_args_name(a->typedb, realname, i);
const char *arg_type = rz_type_func_args_type(a->typedb, realname, i);
char *arg_type = rz_type_func_args_type(a->typedb, realname, i);
// Here we check if the type is a pointer, in this case we don't put
// the space between type and name for the style reasons
// "char *var" looks much better than "char * var"
@ -1766,6 +1766,7 @@ RZ_API RZ_OWN char *rz_analysis_function_get_signature(RzAnalysisFunction *funct
? rz_str_newf("%s%s%s%s", args, arg_type, maybe_space, arg_name)
: rz_str_newf("%s%s%s%s, ", args, arg_type, maybe_space, arg_name);
free(args);
free(arg_type);
args = new_args;
}
char *signature = rz_str_newf("%s %s (%s);", ret_type ? ret_type : "void", realname, args);

View file

@ -47,10 +47,17 @@ RZ_API bool rz_analysis_function_rebase_vars(RzAnalysis *a, RzAnalysisFunction *
// remove all other vars that are overlapped by var and are at the offset of one of its struct members
static void shadow_var_struct_members(RzAnalysisVar *var) {
RzBaseType *btype = rz_type_db_get_base_type(var->fcn->analysis->typedb, var->type);
if (!btype || btype->kind != RZ_BASE_TYPE_KIND_STRUCT) {
if (!btype) {
return;
}
if (btype->kind != RZ_BASE_TYPE_KIND_STRUCT) {
rz_type_base_type_free(btype);
return;
}
if (rz_vector_empty(&btype->struct_data.members)) {
rz_type_base_type_free(btype);
return;
}
RzTypeStructMember *member;
@ -62,6 +69,7 @@ static void shadow_var_struct_members(RzAnalysisVar *var) {
}
}
}
rz_type_base_type_free(btype);
}
RZ_API RzAnalysisVar *rz_analysis_function_set_var(RzAnalysisFunction *fcn, int delta, char kind, RZ_NULLABLE const char *type, int size, bool isarg, RZ_NONNULL const char *name) {

View file

@ -53,7 +53,7 @@ static void apply_line_style(RzConsCanvas *c, int x, int y, int x2, int y2,
RzCons *cons = rz_cons_singleton();
switch (style->color) {
case LINE_UNCJMP:
c->attr = cons->context->pal.graph_trufae;
c->attr = cons->context->pal.graph_ujump;
break;
case LINE_TRUE:
c->attr = cons->context->pal.graph_true;
@ -63,7 +63,7 @@ static void apply_line_style(RzConsCanvas *c, int x, int y, int x2, int y2,
break;
case LINE_NONE:
default:
c->attr = cons->context->pal.graph_trufae;
c->attr = cons->context->pal.graph_ujump;
break;
}
if (!c->color) {

View file

@ -24,7 +24,7 @@ ec graph.box4 rgb:66d9ef
ec graph.current rgb:fff
ec graph.false rgb:f07178 # false line on visual graph mode
ec graph.true rgb:bae67e # true line on visual graph mode
ec graph.trufae rgb:55b4d4 # lines in graph that does not have true or false
ec graph.ujump rgb:55b4d4 # lines in graph that does not have true or false
ec help rgb:c2d94c # color of help explanation text
ec input rgb:888
ec jmp rgb:c2d94c # jmp instructions

View file

@ -43,7 +43,7 @@ ec pop yellow
ec linehl rgb:004
ec graph.true green
ec graph.false red
ec graph.trufae blue
ec graph.ujump blue
ec graph.current blue
ec graph.traced blue
ec graph.box gray

View file

@ -52,7 +52,7 @@ ec graph.box3 rgb:af87ff
ec graph.box4 rgb:ff005f
ec graph.true rgb:00ff5f
ec graph.false rgb:ff005f
ec graph.trufae rgb:5f87ff
ec graph.ujump rgb:5f87ff
ec graph.current rgb:5f87ff
ec graph.traced rgb:ffff5f
ec linehl rgb:1f1f1f

View file

@ -52,7 +52,7 @@ ec push white
ec ret rgb:777
ec swi black red bold
ec trap red
ec graph.trufae rgb:ddd
ec graph.ujump rgb:ddd
ec graph.current rgb:f72
ec graph.traced rgb:f00
ec gui.cflow rgb:f72

View file

@ -70,6 +70,6 @@ ec graph.box3 rgb:ff00ff
ec graph.box4 rgb:808080
ec graph.true rgb:00ff00
ec graph.false rgb:ff0000
ec graph.trufae rgb:0000ff # single jump
ec graph.ujump rgb:0000ff # single jump
ec graph.traced rgb:ffff00
ec graph.current rgb:0000ff

View file

@ -51,7 +51,7 @@ ec graph.box3 rgb:f0f
ec graph.box4 rgb:f0f
ec graph.true rgb:f0f
ec graph.false rgb:0ff
ec graph.trufae rgb:f0f
ec graph.ujump rgb:f0f
ec graph.current rgb:0ff
ec graph.traced rgb:f0f

View file

@ -49,7 +49,7 @@ ec graph.box3 rgb:af2
ec graph.box4 rgb:af2
ec graph.true rgb:4f2
ec graph.false rgb:d41
ec graph.trufae rgb:4cf
ec graph.ujump rgb:4cf
ec graph.current rgb:af2
ec graph.traced rgb:090

View file

@ -56,7 +56,7 @@ ec graph.box3 rgb:e03030
ec graph.box4 rgb:005f87
ec graph.true rgb:5f8700
ec graph.false rgb:e03030
ec graph.trufae rgb:005f87
ec graph.ujump rgb:005f87
ec graph.current rgb:00f0f0
ec graph.traced rgb:e03030
ec gui.cflow rgb:ffff00

View file

@ -45,7 +45,7 @@ ec graph.box3 rgb:040
ec graph.box4 rgb:040
ec graph.true rgb:06c
ec graph.false rgb:035
ec graph.trufae rgb:06c
ec graph.ujump rgb:06c
ec graph.current rgb:99a
ec graph.traced rgb:bbb

View file

@ -47,7 +47,7 @@ ec graph.box3 rgb:66d9ef
ec graph.box4 rgb:66d9ef
ec graph.true rgb:009100
ec graph.false rgb:bc0000
ec graph.trufae rgb:0043cb
ec graph.ujump rgb:0043cb
ec graph.current rgb:fff
ec func_var rgb:99a

View file

@ -49,7 +49,7 @@ ec graph.box=rgb:9e9e9e
ec graph.box2 rgb:5f87d7
ec graph.box3 rgb:af5f87
ec graph.box4 rgb:303030
ec graph.trufae rgb:005fff
ec graph.ujump rgb:005fff
ec graph.current rgb:c68e00
ec graph.true rgb:009100
ec graph.false rgb:bc0000

View file

@ -46,7 +46,7 @@ ec graph.box3 blue
ec graph.box4 blue
ec graph.true green
ec graph.false red
ec graph.trufae blue
ec graph.ujump blue
ec graph.current blue
ec graph.traced red

View file

@ -50,7 +50,7 @@ ec graph.box3 rgb:86c06c
ec graph.box4 rgb:86c06c
ec graph.true rgb:86c06c
ec graph.false rgb:e0f8cf
ec graph.trufae rgb:86c06c
ec graph.ujump rgb:86c06c
ec graph.current rgb:e0f8cf
ec graph.traced rgb:86c06c

View file

@ -56,7 +56,7 @@ ec graph.box3 rgb:30f030
ec graph.box4 rgb:30f030
ec graph.true rgb:30f030
ec graph.false rgb:f03030
ec graph.trufae rgb:3030f0
ec graph.ujump rgb:3030f0
ec graph.current rgb:f03030
ec graph.traced rgb:f00000
ec gui.cflow rgb:f03030

View file

@ -50,7 +50,7 @@ ec graph.box3 rgb:444
ec graph.box4 rgb:444
ec graph.true rgb:ef2
ec graph.false rgb:5d5
ec graph.trufae rgb:af2
ec graph.ujump rgb:af2
ec graph.current rgb:af2
ec graph.traced rgb:090

View file

@ -46,7 +46,7 @@ ec graph.box3 rgb:ff0000
ec graph.box4 rgb:0000ff
ec graph.true green
ec graph.false rgb:050
ec graph.trufae green
ec graph.ujump green
ec graph.current green
ec graph.traced rgb:060

View file

@ -48,7 +48,7 @@ ec graph.box3 rgb:66d9ef
ec graph.box4 rgb:66d9ef
ec graph.true rgb:A6df2E
ec graph.false rgb:f92672
ec graph.trufae rgb:a398e5
ec graph.ujump rgb:a398e5
ec graph.current rgb:fff
ec func_var cyan

View file

@ -39,7 +39,7 @@ ec ucall rgb:f72 rgb:333
ec ujmp rgb:f72
ec graph.true rgb:f72
ec graph.false rgb:777
ec graph.trufae rgb:ddd
ec graph.ujump rgb:ddd
ec graph.current rgb:f72
ec graph.traced rgb:f00
ec graph.box rgb:fff

View file

@ -61,7 +61,7 @@ ec graph.box3 rgb:c678dd
ec graph.box4 rgb:000000
ec graph.true rgb:98c379
ec graph.false rgb:e06c75
ec graph.trufae rgb:61afef
ec graph.ujump rgb:61afef
ec graph.current rgb:61afef
ec graph.traced rgb:d19a66
ec linehl rgb:5c6370

View file

@ -40,7 +40,7 @@ ec crypto rgb:75d
ec linehl rgb:fdf
ec graph.true rgb:f5d
ec graph.false rgb:715
ec graph.trufae rgb:f5d
ec graph.ujump rgb:f5d
ec graph.current rgb:f5d
ec graph.traced rgb:a7f
ec graph.box rgb:f5d

View file

@ -50,7 +50,7 @@ ec graph.box3 red
ec graph.box4 red
ec graph.true green
ec graph.false red
ec graph.trufae green
ec graph.ujump green
ec graph.current green
ec graph.traced yellow

View file

@ -46,7 +46,7 @@ ec wordhl rgb:ff0000
ec linehl rgb:210
ec graph.true rgb:fd9
ec graph.false rgb:431
ec graph.trufae rgb:fd9
ec graph.ujump rgb:fd9
ec graph.current rgb:0000ff
ec graph.traced rgb:0000ff
ec graph.box rgb:431

View file

@ -50,7 +50,7 @@ ec graph.box3 rgb:9df
ec graph.box4 rgb:9df
ec graph.true rgb:9c4
ec graph.false rgb:d66
ec graph.trufae rgb:9c4
ec graph.ujump rgb:9c4
ec graph.current rgb:fff
ec func_var rgb:9df

View file

@ -45,7 +45,7 @@ ec graph.box3 rgb:033
ec graph.box4 rgb:033
ec graph.true rgb:890
ec graph.false rgb:c41
ec graph.trufae rgb:899
ec graph.ujump rgb:899
ec graph.current rgb:f00
ec graph.traced red

View file

@ -59,7 +59,7 @@ ec linehl rgb:004
ec graph.true rgb:370
ec graph.false rgb:a00
ec graph.trufae rgb:bbb
ec graph.ujump rgb:bbb
ec graph.current rgb:7d1
ec graph.traced rgb:f00
ec graph.box rgb:950

View file

@ -45,7 +45,7 @@ ec flag rgb:ffd
ec linehl rgb:004
ec graph.true rgb:9b7
ec graph.false rgb:b97
ec graph.trufae rgb:64c
ec graph.ujump rgb:64c
ec graph.current blue
ec graph.traced blue
ec graph.box rgb:9b7

View file

@ -44,7 +44,7 @@ ec graph.box3 red
ec graph.box4 blue
ec graph.true green
ec graph.false red
ec graph.trufae blue
ec graph.ujump blue
ec graph.current rgb:0ff
ec graph.traced red

View file

@ -27,7 +27,7 @@ ec graph.box4 rgb:66d9ef
ec graph.current rgb:fff
ec graph.false rgb:990000 # false line on visual graph mode
ec graph.true rgb:376B4C # true line on visual graph mode
ec graph.trufae rgb:007D96 # lines in graph that does not have true or false
ec graph.ujump rgb:007D96 # lines in graph that does not have true or false
ec help rgb:2F6F9F 0 italic # color of help explanation text
ec input rgb:888
ec jmp rgb:376B4C 0 bold underline # jmp instructions

View file

@ -53,7 +53,7 @@ ec graph.box3 magenta
ec graph.box4 rgb:000000
ec graph.true green
ec graph.false red
ec graph.trufae blue
ec graph.ujump blue
ec graph.current blue
ec graph.traced yellow
ec gui.cflow yellow

View file

@ -49,7 +49,7 @@ ec graph.box3 rgb:aaa
ec graph.box4 rgb:aaa
ec graph.true rgb:aaa
ec graph.false rgb:d52
ec graph.trufae rgb:aaa
ec graph.ujump rgb:aaa
ec graph.current blue
ec graph.traced red

View file

@ -70,7 +70,7 @@ static struct {
{ "graph.box4", rz_offsetof(RzConsPrintablePalette, graph_box4), rz_offsetof(RzConsPalette, graph_box4) },
{ "graph.true", rz_offsetof(RzConsPrintablePalette, graph_true), rz_offsetof(RzConsPalette, graph_true) },
{ "graph.false", rz_offsetof(RzConsPrintablePalette, graph_false), rz_offsetof(RzConsPalette, graph_false) },
{ "graph.trufae", rz_offsetof(RzConsPrintablePalette, graph_trufae), rz_offsetof(RzConsPalette, graph_trufae) },
{ "graph.ujump", rz_offsetof(RzConsPrintablePalette, graph_ujump), rz_offsetof(RzConsPalette, graph_ujump) },
{ "graph.current", rz_offsetof(RzConsPrintablePalette, graph_current), rz_offsetof(RzConsPalette, graph_current) },
{ "graph.traced", rz_offsetof(RzConsPrintablePalette, graph_traced), rz_offsetof(RzConsPalette, graph_traced) },
@ -229,7 +229,7 @@ RZ_API void rz_cons_pal_init(RzConsContext *ctx) {
ctx->cpal.graph_box4 = (RzColor)RzColor_GRAY;
ctx->cpal.graph_true = (RzColor)RzColor_GREEN;
ctx->cpal.graph_false = (RzColor)RzColor_RED;
ctx->cpal.graph_trufae = (RzColor)RzColor_BLUE; // single jump
ctx->cpal.graph_ujump = (RzColor)RzColor_BLUE; // single jump
ctx->cpal.graph_traced = (RzColor)RzColor_YELLOW;
ctx->cpal.graph_current = (RzColor)RzColor_BLUE;
ctx->cpal.graph_diff_unknown = (RzColor)RzColor_MAGENTA;

View file

@ -2060,7 +2060,7 @@ static int core_analysis_graph_construct_edges(RzCore *core, RzAnalysisFunction
int is_html = rz_cons_singleton()->is_html;
char *pal_jump = palColorFor("graph.true");
char *pal_fail = palColorFor("graph.false");
char *pal_trfa = palColorFor("graph.trufae");
char *pal_trfa = palColorFor("graph.ujump");
int nodes = 0;
rz_list_foreach (fcn->bbs, iter, bbi) {
if (bbi->jump != UT64_MAX) {
@ -2429,7 +2429,7 @@ static int core_analysis_graph_nodes(RzCore *core, RzAnalysisFunction *fcn, int
Sdb *DB = NULL;
char *pal_jump = palColorFor("graph.true");
char *pal_fail = palColorFor("graph.false");
char *pal_trfa = palColorFor("graph.trufae");
char *pal_trfa = palColorFor("graph.ujump");
char *pal_curr = palColorFor("graph.current");
char *pal_traced = palColorFor("graph.traced");
char *pal_box4 = palColorFor("graph.box4");
@ -4217,7 +4217,7 @@ RZ_API RzList *rz_core_analysis_graph_to(RzCore *core, ut64 addr, int n) {
return paths;
}
RZ_API int rz_core_analysis_graph(RzCore *core, ut64 addr, int opts) {
RZ_API bool rz_core_analysis_graph(RzCore *core, ut64 addr, int opts) {
ut64 from = rz_config_get_i(core->config, "graph.from");
ut64 to = rz_config_get_i(core->config, "graph.to");
const char *font = rz_config_get(core->config, "graph.font");

View file

@ -7510,19 +7510,19 @@ static void cmd_analysis_graph(RzCore *core, const char *input) {
switch (input[1]) {
case 'j': { // "agdj"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_fcn(core, addr, core->offset);
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt | RZ_CORE_ANALYSIS_JSON);
break;
}
case 'J': { // "agdJ"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_fcn(core, addr, core->offset);
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt | RZ_CORE_ANALYSIS_JSON | RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM);
break;
}
case '*': { // "agd*"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_fcn(core, addr, core->offset);
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt | RZ_CORE_ANALYSIS_STAR);
break;
}
@ -7539,7 +7539,7 @@ static void cmd_analysis_graph(RzCore *core, const char *input) {
}
case 'd': { // "agdd"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_fcn(core, addr, core->offset);
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt);
break;
}

View file

@ -765,7 +765,7 @@ RZ_IPI int rz_cmd_cmp(void *data, const char *input) {
case 'f': // "cgf"
eprintf("TODO: agf is experimental\n");
rz_analysis_diff_setup(core->analysis, true, -1, -1);
rz_core_gdiff_fcn(core, core->offset,
rz_core_gdiff_function_1_file(core, core->offset,
rz_num_math(core->num, input + 2));
return false;
case ' ':
@ -809,8 +809,8 @@ RZ_IPI int rz_cmd_cmp(void *data, const char *input) {
rz_core_bin_load(core2, file2,
rz_config_get_i(core->config, "bin.baddr"));
rz_core_gdiff(core, core2);
rz_core_diff_show(core, core2);
rz_core_gdiff_2_files(core, core2);
rz_core_diff_show(core, core2, false);
/* exchange a segfault with a memleak */
core2->config = NULL;
rz_core_free(core2);

View file

@ -3467,7 +3467,7 @@ static bool cmd_print_blocks(RzCore *core, const char *input) {
if (use_color) {
if (s) {
if (s->perm & RZ_PERM_X) {
rz_cons_print(rz_cons_singleton()->context->pal.graph_trufae);
rz_cons_print(rz_cons_singleton()->context->pal.graph_ujump);
} else {
rz_cons_print(rz_cons_singleton()->context->pal.graph_true);
}

View file

@ -1615,7 +1615,7 @@ RZ_API void rz_core_autocomplete(RZ_NULLABLE RzCore *core, RzLineCompletion *com
ADDARG("graph.box4")
ADDARG("graph.true")
ADDARG("graph.false")
ADDARG("graph.trufae")
ADDARG("graph.ujump")
ADDARG("graph.current")
ADDARG("graph.traced")
ADDARG("gui.cflow")

View file

@ -9,7 +9,12 @@
#include <rz_util.h>
#include <rz_core.h>
RZ_API int rz_core_gdiff_fcn(RzCore *c, ut64 addr, ut64 addr2) {
/**
* \brief Calculates basic block differences of 2 functions within the same file
*
* Calculates basic block differences of 2 functions within the same file
* */
RZ_API bool rz_core_gdiff_function_1_file(RzCore *c, ut64 addr, ut64 addr2) {
RzList *la, *lb;
RzAnalysisFunction *fa = rz_analysis_get_function_at(c->analysis, addr);
RzAnalysisFunction *fb = rz_analysis_get_function_at(c->analysis, addr2);
@ -34,8 +39,55 @@ RZ_API int rz_core_gdiff_fcn(RzCore *c, ut64 addr, ut64 addr2) {
return true;
}
/* Fingerprint functions and blocks, then diff. */
RZ_API int rz_core_gdiff(RzCore *c, RzCore *c2) {
/**
* \brief Calculates basic block differences of 2 functions within 2 files
*
* Calculates basic block differences of 2 functions within 2 files
* */
RZ_API bool rz_core_gdiff_function_2_files(RzCore *c, RzCore *c2, ut64 addr, ut64 addr2) {
rz_return_val_if_fail(c && c2, false);
RzList *la, *lb;
RzAnalysisFunction *fa = rz_analysis_get_function_at(c->analysis, addr);
RzAnalysisFunction *fb = rz_analysis_get_function_at(c2->analysis, addr2);
if (!fa || !fb) {
eprintf("cannot get functions at 0x%" PFMT64x " or at 0x%" PFMT64x "\n", addr, addr2);
return false;
}
RzAnalysisBlock *bb;
RzListIter *iter;
rz_list_foreach (fa->bbs, iter, bb) {
if (rz_analysis_diff_fingerprint_bb(c->analysis, bb) < 0) {
eprintf("cannot fingerprint 0x%" PFMT64x "\n", addr);
return false;
}
}
rz_list_foreach (fb->bbs, iter, bb) {
if (rz_analysis_diff_fingerprint_bb(c2->analysis, bb) < 0) {
eprintf("cannot fingerprint 0x%" PFMT64x "\n", addr2);
return false;
}
}
rz_analysis_diff_fingerprint_fcn(c->analysis, fa);
rz_analysis_diff_fingerprint_fcn(c2->analysis, fb);
la = rz_list_new();
rz_list_append(la, fa);
lb = rz_list_new();
rz_list_append(lb, fb);
rz_analysis_diff_fcn(c->analysis, la, lb);
rz_list_free(la);
rz_list_free(lb);
return true;
}
/**
* \brief Calculates basic block differences of all functions within 2 files
*
* Calculates basic block differences of all functions within 2 files.
* */
RZ_API bool rz_core_gdiff_2_files(RzCore *c, RzCore *c2) {
rz_return_val_if_fail(c && c2, false);
RzCore *cores[2] = { c, c2 };
RzAnalysisFunction *fcn;
RzAnalysisBlock *bb;
@ -69,45 +121,76 @@ RZ_API int rz_core_gdiff(RzCore *c, RzCore *c2) {
return true;
}
/* copypasta from rz_diff */
static void diffrow(ut64 addr, const char *name, ut32 size, int maxnamelen,
int digits, ut64 addr2, const char *name2, ut32 size2,
const char *match, double dist, int bare) {
double dist, bool is_new, bool bare, bool color) {
const char *type = NULL;
const char *prefix = NULL;
const char *suffix = color ? Color_RESET : "";
if (dist == 1.0) {
prefix = color ? Color_BGREEN : "";
type = color ? Color_BGREEN "MATCH " Color_RESET : "MATCH ";
} else if (dist >= 0.5) {
prefix = color ? Color_BYELLOW : "";
type = color ? Color_BYELLOW "SIMILAR" Color_RESET : "SIMILAR";
} else if (is_new) {
dist = 0.0;
prefix = color ? Color_BBLUE : "";
type = color ? Color_BBLUE "NEW " Color_RESET : "NEW ";
} else {
prefix = color ? Color_BRED : "";
type = color ? Color_BRED "UNMATCH" Color_RESET : "UNMATCH";
}
if (bare) {
if (addr2 == UT64_MAX || !name2) {
printf("0x%016" PFMT64x " |%8s (%f)\n", addr, match, dist);
printf("0x%016" PFMT64x " | %7s (%s%f%s)\n", addr, type, prefix, dist, suffix);
} else {
printf("0x%016" PFMT64x " |%8s (%f) | 0x%016" PFMT64x "\n", addr, match, dist, addr2);
printf("0x%016" PFMT64x " | %7s (%s%f%s) | 0x%016" PFMT64x "\n", addr, type, prefix, dist, suffix, addr2);
}
} else {
if (addr2 == UT64_MAX || !name2) {
printf("%*s %*d 0x%" PFMT64x " |%8s (%f)\n",
maxnamelen, name, digits, size, addr, match, dist);
printf("%*s %*d 0x%016" PFMT64x " | %7s (%s%f%s)\n",
maxnamelen, name, digits, size, addr, type, prefix, dist, suffix);
} else {
printf("%*s %*d 0x%" PFMT64x " |%8s (%f) | 0x%" PFMT64x " %*d %s\n",
maxnamelen, name, digits, size, addr, match, dist, addr2,
printf("%*s %*d 0x%016" PFMT64x " | %7s (%s%f%s) | 0x%016" PFMT64x " %*d %s\n",
maxnamelen, name, digits, size, addr, type, prefix, dist, suffix, addr2,
digits, size2, name2);
}
}
}
RZ_API void rz_core_diff_show(RzCore *c, RzCore *c2) {
bool bare = rz_config_get_i(c->config, "diff.bare") || rz_config_get_i(c2->config, "diff.bare");
RZ_API void rz_core_diff_show(RzCore *c, RzCore *c2, bool json) {
rz_return_if_fail(c && c2);
bool color = rz_config_get_i(c->config, "scr.color") > 0 || rz_config_get_i(c2->config, "scr.color") > 0;
bool bare = rz_config_get_b(c->config, "diff.bare") || rz_config_get_b(c2->config, "diff.bare");
bool is_new = false;
RzList *fcns = rz_analysis_get_fcns(c->analysis);
const char *match;
RzListIter *iter;
RzAnalysisFunction *f;
int maxnamelen = 0;
ut64 maxsize = 0;
int digits = 1;
int len;
PJ *pj = NULL;
if (json) {
pj = pj_new();
if (!pj) {
eprintf("cannot alocate json\n");
return;
}
pj_a(pj);
}
rz_list_foreach (fcns, iter, f) {
if (f->name && (len = strlen(f->name)) > maxnamelen) {
maxnamelen = len;
}
if (rz_analysis_function_linear_size(f) > maxsize) {
maxsize = rz_analysis_function_linear_size(f);
if (rz_analysis_function_realsize(f) > maxsize) {
maxsize = rz_analysis_function_realsize(f);
}
}
fcns = rz_analysis_get_fcns(c2->analysis);
@ -115,8 +198,8 @@ RZ_API void rz_core_diff_show(RzCore *c, RzCore *c2) {
if (f->name && (len = strlen(f->name)) > maxnamelen) {
maxnamelen = len;
}
if (rz_analysis_function_linear_size(f) > maxsize) {
maxsize = rz_analysis_function_linear_size(f);
if (rz_analysis_function_realsize(f) > maxsize) {
maxsize = rz_analysis_function_realsize(f);
}
}
while (maxsize > 9) {
@ -126,7 +209,7 @@ RZ_API void rz_core_diff_show(RzCore *c, RzCore *c2) {
fcns = rz_analysis_get_fcns(c->analysis);
if (rz_list_empty(fcns)) {
eprintf("No functions found, try running with -A or load a project\n");
eprintf("functions list is empty. analyze the binary first\n");
return;
}
rz_list_sort(fcns, c->analysis->columnSort);
@ -137,18 +220,37 @@ RZ_API void rz_core_diff_show(RzCore *c, RzCore *c2) {
case RZ_ANALYSIS_FCN_TYPE_SYM:
switch (f->diff->type) {
case RZ_ANALYSIS_DIFF_TYPE_MATCH:
match = "MATCH";
break;
case RZ_ANALYSIS_DIFF_TYPE_UNMATCH:
match = "UNMATCH";
is_new = false;
break;
default:
match = "NEW";
f->diff->dist = 0;
is_new = true;
}
if (json) {
double dist = f->diff->dist;
pj_o(pj);
pj_kd(pj, "distance", f->diff->dist);
pj_ks(pj, "type", dist >= 1.0 ? "MATCH" : (dist >= 0.5 ? "SIMILAR" : (is_new ? "NEW" : "UNMATCH")));
if (f->name) {
pj_ko(pj, "original");
pj_ks(pj, "name", f->name);
pj_kn(pj, "addr", f->addr);
pj_kn(pj, "size", rz_analysis_function_realsize(f));
pj_end(pj);
}
if (f->diff->name) {
pj_ko(pj, "modified");
pj_ks(pj, "name", f->diff->name);
pj_kn(pj, "addr", f->diff->addr);
pj_kn(pj, "size", f->diff->size);
pj_end(pj);
}
pj_end(pj);
} else {
diffrow(f->addr, f->name, rz_analysis_function_realsize(f), maxnamelen, digits,
f->diff->addr, f->diff->name, f->diff->size,
f->diff->dist, is_new, bare, color);
}
diffrow(f->addr, f->name, rz_analysis_function_linear_size(f), maxnamelen, digits,
f->diff->addr, f->diff->name, f->diff->size,
match, f->diff->dist, bare);
break;
}
}
@ -159,11 +261,320 @@ RZ_API void rz_core_diff_show(RzCore *c, RzCore *c2) {
case RZ_ANALYSIS_FCN_TYPE_FCN:
case RZ_ANALYSIS_FCN_TYPE_SYM:
if (f->diff->type == RZ_ANALYSIS_DIFF_TYPE_NULL) {
diffrow(f->addr, f->name, rz_analysis_function_linear_size(f), maxnamelen,
digits, f->diff->addr, f->diff->name, f->diff->size,
"NEW", 0, bare); //f->diff->dist, bare);
if (json) {
pj_o(pj);
pj_kd(pj, "distance", 0.0);
pj_ks(pj, "type", "NEW");
if (f->name) {
pj_ko(pj, "original");
pj_ks(pj, "name", f->name);
pj_kn(pj, "addr", f->addr);
pj_kn(pj, "size", rz_analysis_function_realsize(f));
pj_end(pj);
}
if (f->diff->name) {
pj_ko(pj, "modified");
pj_ks(pj, "name", f->diff->name);
pj_kn(pj, "addr", f->diff->addr);
pj_kn(pj, "size", f->diff->size);
pj_end(pj);
}
pj_end(pj);
} else {
diffrow(f->addr, f->name, rz_analysis_function_realsize(f), maxnamelen,
digits, f->diff->addr, f->diff->name, f->diff->size,
0.0, true, bare, color);
}
}
break;
}
}
if (json) {
pj_end(pj);
printf("%s\n", pj_string(pj));
pj_free(pj);
}
}
static const char *diff_color(RzAnalysisBlock *bbi) {
if (!bbi->diff) {
return "white";
}
switch (bbi->diff->type) {
case RZ_ANALYSIS_DIFF_TYPE_MATCH:
return "lightgray";
case RZ_ANALYSIS_DIFF_TYPE_UNMATCH:
return bbi->diff->dist >= 0.5 ? "yellow" : "red";
default:
return "turquoise";
}
}
static void print_color_node(RzCore *core, RzAnalysisBlock *bbi) {
bool color_current = rz_config_get_i(core->config, "graph.gv.current");
bool current = rz_analysis_block_contains(bbi, core->offset);
if (current && color_current) {
printf("\t\"0x%08" PFMT64x "\" ", bbi->addr);
printf("\t[fillcolor=gray style=filled shape=box];\n");
}
}
static int graph_construct_nodes(RzCore *core, RzCore *core2, RzAnalysisFunction *fcn, PJ *pj) {
char addr_a[32], addr_b[32];
RzAnalysisBlock *bbi;
RzListIter *iter;
int is_json = pj != NULL;
const char *font = rz_config_get(core->config, "graph.font");
int nodes = 0;
snprintf(addr_a, sizeof(addr_a), "0x%08" PFMT64x, fcn->addr);
snprintf(addr_b, sizeof(addr_b), "0x%08" PFMT64x, fcn->diff->addr);
const char *norig = fcn->name ? fcn->name : addr_a;
const char *nmodi = fcn->diff->name ? fcn->diff->name : addr_b;
rz_list_foreach (fcn->bbs, iter, bbi) {
if (is_json) {
RzDebugTracepoint *t = rz_debug_trace_get(core->dbg, bbi->addr);
ut8 *buf = malloc(bbi->size);
pj_o(pj);
pj_kn(pj, "offset", bbi->addr);
pj_kn(pj, "size", bbi->size);
if (bbi->jump != UT64_MAX) {
pj_kn(pj, "jump", bbi->jump);
}
if (bbi->fail != -1) {
pj_kn(pj, "fail", bbi->fail);
}
if (bbi->switch_op) {
RzAnalysisSwitchOp *op = bbi->switch_op;
pj_k(pj, "switchop");
pj_o(pj);
pj_kn(pj, "offset", op->addr);
pj_kn(pj, "defval", op->def_val);
pj_kn(pj, "maxval", op->max_val);
pj_kn(pj, "minval", op->min_val);
pj_k(pj, "cases");
pj_a(pj);
RzAnalysisCaseOp *case_op;
RzListIter *case_iter;
rz_list_foreach (op->cases, case_iter, case_op) {
pj_o(pj);
pj_kn(pj, "offset", case_op->addr);
pj_kn(pj, "value", case_op->value);
pj_kn(pj, "jump", case_op->jump);
pj_end(pj);
}
pj_end(pj);
pj_end(pj);
}
if (t) {
pj_k(pj, "trace");
pj_o(pj);
pj_ki(pj, "count", t->count);
pj_ki(pj, "times", t->times);
pj_end(pj);
}
pj_kn(pj, "colorize", bbi->colorize);
pj_k(pj, "ops");
pj_a(pj);
if (buf) {
rz_io_read_at(core->io, bbi->addr, buf, bbi->size);
rz_core_print_disasm_json(core, bbi->addr, buf, bbi->size, 0, pj);
free(buf);
} else {
eprintf("cannot allocate %" PFMT64u " byte(s)\n", bbi->size);
}
pj_end(pj);
pj_end(pj);
continue;
} else {
const char *fillcolor = diff_color(bbi);
nodes++;
RzConfigHold *hc = rz_config_hold_new(core->config);
rz_config_hold_i(hc, "scr.color", "scr.utf8", "asm.offset", "asm.lines",
"asm.cmt.right", "asm.lines.fcn", "asm.bytes", "asm.comments", NULL);
rz_config_set_i(core->config, "scr.utf8", 0);
rz_config_set_i(core->config, "asm.offset", 0);
rz_config_set_i(core->config, "asm.lines", 0);
rz_config_set_i(core->config, "asm.cmt.right", 0);
rz_config_set_i(core->config, "asm.lines.fcn", 0);
rz_config_set_i(core->config, "asm.bytes", 0);
rz_config_set_i(core->config, "asm.comments", 0);
rz_config_set_i(core->config, "scr.color", COLOR_MODE_DISABLED);
char *original = rz_core_cmd_strf(core, "pdb @ 0x%08" PFMT64x, bbi->addr);
if (bbi->diff && bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_UNMATCH) {
RzConfig *oc = core2->config;
core2->config = core->config;
char *modified = rz_core_cmd_strf(core2, "pdb @ 0x%08" PFMT64x, bbi->diff->addr);
RzDiff *dff = rz_diff_lines_new(original, modified, NULL);
char *diffstr = rz_diff_unified_text(dff, norig, nmodi, false, false);
rz_diff_free(dff);
rz_str_replace_char(diffstr, '"', '\'');
diffstr = rz_str_replace(diffstr, "\n", "\\l", 1);
printf("\t\"0x%08" PFMT64x "\" [fillcolor=\"%s\","
"color=\"black\", fontname=\"%s\","
" label=\"%s\", URL=\"%s/0x%08" PFMT64x "\"]\n",
bbi->addr, fillcolor, font, diffstr, fcn->name,
bbi->addr);
free(diffstr);
free(modified);
core2->config = oc;
} else {
rz_str_replace_char(original, '"', '\'');
original = rz_str_replace(original, "\n", "\\l", 1);
printf("\t\"0x%08" PFMT64x "\" [fillcolor=\"%s\","
"color=\"black\", fontname=\"%s\","
" label=\"%s\", URL=\"%s/0x%08" PFMT64x "\"]\n",
bbi->addr, fillcolor, font, original, fcn->name, bbi->addr);
}
free(original);
rz_config_set_i(core->config, "scr.color", 1);
rz_config_hold_restore(hc);
rz_config_hold_free(hc);
}
}
return nodes;
}
static int graph_construct_edges(RzCore *core, RzAnalysisFunction *fcn) {
RzAnalysisBlock *bbi;
RzListIter *iter;
const char *pal_jump = "#0037da";
const char *pal_fail = "#c50f1f";
const char *pal_true = "#13a10e";
int nodes = 0;
rz_list_foreach (fcn->bbs, iter, bbi) {
if (bbi->jump != UT64_MAX) {
nodes++;
printf("\t\"0x%08" PFMT64x "\" -> \"0x%08" PFMT64x "\" [color=\"%s\"];\n",
bbi->addr, bbi->jump,
bbi->fail != UT64_MAX ? pal_true : pal_jump);
print_color_node(core, bbi);
}
if (bbi->fail != UT64_MAX) {
nodes++;
printf("\t\"0x%08" PFMT64x "\" -> \"0x%08" PFMT64x "\" [color=\"%s\"];\n",
bbi->addr, bbi->fail, pal_fail);
print_color_node(core, bbi);
}
if (bbi->switch_op) {
RzAnalysisCaseOp *caseop;
RzListIter *iter;
if (bbi->fail != UT64_MAX) {
printf("\t\"0x%08" PFMT64x "\" -> \"0x%08" PFMT64x "\" [color=\"%s\"];\n",
bbi->addr, bbi->fail, pal_fail);
print_color_node(core, bbi);
}
rz_list_foreach (bbi->switch_op->cases, iter, caseop) {
nodes++;
printf("\t\"0x%08" PFMT64x "\" -> \"0x%08" PFMT64x "\" [color2=\"%s\"];\n",
caseop->addr, caseop->jump, pal_fail);
print_color_node(core, bbi);
}
}
}
return nodes;
}
static int draw_graph_nodes(RzCore *core, RzCore *core2, RzAnalysisFunction *fcn, PJ *pj) {
rz_return_val_if_fail(fcn && fcn->bbs, -1);
int nodes = 0;
if (pj) {
char *fcn_name_escaped = rz_str_escape_utf8_for_json(fcn->name, -1);
pj_o(pj);
pj_ks(pj, "name", rz_str_get_null(fcn_name_escaped));
free(fcn_name_escaped);
pj_kn(pj, "offset", fcn->addr);
pj_ki(pj, "ninstr", fcn->ninstr);
pj_ki(pj, "nargs",
rz_analysis_var_count(core->analysis, fcn, 'r', 1) +
rz_analysis_var_count(core->analysis, fcn, 's', 1) +
rz_analysis_var_count(core->analysis, fcn, 'b', 1));
pj_ki(pj, "nlocals",
rz_analysis_var_count(core->analysis, fcn, 'r', 0) +
rz_analysis_var_count(core->analysis, fcn, 's', 0) +
rz_analysis_var_count(core->analysis, fcn, 'b', 0));
pj_kn(pj, "size", rz_analysis_function_linear_size(fcn));
pj_ki(pj, "stack", fcn->maxstack);
pj_ks(pj, "type", rz_analysis_fcntype_tostring(fcn->type));
pj_k(pj, "blocks");
pj_a(pj);
}
nodes += graph_construct_nodes(core, core2, fcn, pj);
if (!pj) {
nodes += graph_construct_edges(core, fcn);
}
if (pj) {
pj_end(pj);
pj_end(pj);
}
return nodes;
}
/**
* \brief Generate a json or dot output of the graph and its data.
*
* Each node that doesn't match 100% with the other function will include
* a unified diff of the assembly of the same basic block.
* */
RZ_API bool rz_core_diff_show_function(RzCore *core, RzCore *core2, ut64 addr1, bool json) {
rz_return_val_if_fail(core && core2, false);
const char *font = rz_config_get(core->config, "graph.font");
int nodes = 0;
PJ *pj = NULL;
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr1);
if (!fcn) {
eprintf("cannot get functions at 0x%" PFMT64x "\n", addr1);
return false;
}
if (!json) {
const char *gv_edge = rz_config_get(core->config, "graph.gv.edge");
const char *gv_node = rz_config_get(core->config, "graph.gv.node");
const char *gv_spline = rz_config_get(core->config, "graph.gv.spline");
if (!gv_edge || !*gv_edge) {
gv_edge = "arrowhead=\"normal\"";
}
if (!gv_node || !*gv_node) {
gv_node = "fillcolor=gray style=filled shape=box";
}
if (!gv_spline || !*gv_spline) {
gv_spline = "splines=\"ortho\"";
}
printf("digraph code {\n"
"\tgraph [bgcolor=azure fontsize=8 fontname=\"%s\" %s];\n"
"\tnode [%s];\n"
"\tedge [%s];\n",
font, gv_spline, gv_node, gv_edge);
} else {
pj = pj_new();
if (!pj) {
return false;
}
pj_a(pj);
}
nodes += draw_graph_nodes(core, core2, fcn, pj);
if (nodes < 1 && !json) {
printf("\t\"0x%08" PFMT64x "\";\n", addr1);
}
if (json) {
pj_end(pj);
printf("%s\n", pj_string(pj));
pj_free(pj);
} else {
printf("}\n");
}
return true;
}

View file

@ -62,7 +62,7 @@ static inline void diff_unified_append_data(RzDiff *diff, const void *array, st3
rz_strbuf_appendf(sb, "%s%c", bcol, prefix);
for (st32 i = beg; i < end; ++i) {
if (newline || (is_bytes && count > 0 && !FAST_MOD64(count))) {
rz_strbuf_appendf(sb, "%s\n%s%c", bcol, ecol, prefix);
rz_strbuf_appendf(sb, "%s\n%s%c", ecol, bcol, prefix);
newline = false;
}
rz_strbuf_init(&tmp);

View file

@ -263,7 +263,7 @@ typedef struct rz_cons_palette_t {
RzColor graph_box4;
RzColor graph_true;
RzColor graph_false;
RzColor graph_trufae;
RzColor graph_ujump;
RzColor graph_traced;
RzColor graph_current;
RzColor graph_diff_match;
@ -343,7 +343,7 @@ typedef struct rz_cons_printable_palette_t {
char *graph_diff_new;
char *graph_true;
char *graph_false;
char *graph_trufae;
char *graph_ujump;
char *graph_traced;
char *graph_current;
char **rainbow; // rainbow

View file

@ -669,7 +669,7 @@ RZ_API ut64 rz_core_analysis_fcn_list_size(RzCore *core);
RZ_API int rz_core_analysis_fcn_clean(RzCore *core, ut64 addr);
RZ_API int rz_core_print_bb_custom(RzCore *core, RzAnalysisFunction *fcn);
RZ_API int rz_core_print_bb_gml(RzCore *core, RzAnalysisFunction *fcn);
RZ_API int rz_core_analysis_graph(RzCore *core, ut64 addr, int opts);
RZ_API bool rz_core_analysis_graph(RzCore *core, ut64 addr, int opts);
RZ_API RzList *rz_core_analysis_graph_to(RzCore *core, ut64 addr, int n);
RZ_API int rz_core_analysis_all(RzCore *core);
RZ_API bool rz_core_analysis_everything(RzCore *core, bool experimental, char *dh_orig);
@ -763,8 +763,9 @@ RZ_API int rz_core_pseudo_code(RzCore *core, const char *input);
/* gdiff.c */
RZ_API int rz_core_zdiff(RzCore *c, RzCore *c2);
RZ_API int rz_core_gdiff(RzCore *core1, RzCore *core2);
RZ_API int rz_core_gdiff_fcn(RzCore *c, ut64 addr, ut64 addr2);
RZ_API bool rz_core_gdiff_2_files(RzCore *core1, RzCore *core2);
RZ_API bool rz_core_gdiff_function_1_file(RzCore *c, ut64 addr, ut64 addr2);
RZ_API bool rz_core_gdiff_function_2_files(RzCore *core1, RzCore *core2, ut64 addr, ut64 addr2);
RZ_API char *rz_core_sysenv_begin(RzCore *core, const char *cmd);
RZ_API void rz_core_sysenv_end(RzCore *core, const char *cmd);
@ -899,7 +900,8 @@ RZ_API RzList * /*<RzIOMap*>*/ rz_core_get_boundaries_prot(RzCore *core, int pro
RZ_API void rz_core_hack_help(const RzCore *core);
RZ_API int rz_core_hack(RzCore *core, const char *op);
RZ_API bool rz_core_dump(RzCore *core, const char *file, ut64 addr, ut64 size, int append);
RZ_API void rz_core_diff_show(RzCore *core, RzCore *core2);
RZ_API void rz_core_diff_show(RzCore *core, RzCore *core2, bool json);
RZ_API bool rz_core_diff_show_function(RzCore *core, RzCore *core2, ut64 addr, bool json);
RZ_API void rz_core_clippy(RzCore *core, const char *msg);
/* watchers */

View file

@ -46,6 +46,7 @@ typedef enum {
DIFF_TYPE_SECTIONS,
DIFF_TYPE_STRINGS,
DIFF_TYPE_SYMBOLS,
DIFF_TYPE_PLOTDIFF,
} DiffType;
typedef enum {
@ -56,6 +57,7 @@ typedef enum {
DIFF_OPT_VERSION,
DIFF_OPT_DISTANCE,
DIFF_OPT_UNIFIED,
DIFF_OPT_GRAPH,
} DiffOption;
typedef struct diff_context_t {
@ -68,8 +70,8 @@ typedef struct diff_context_t {
bool show_time;
bool colors;
const char *architecture;
const char *command_a;
const char *command_b;
const char *input_a;
const char *input_b;
const char *file_a;
const char *file_b;
} DiffContext;
@ -162,9 +164,9 @@ static void rz_diff_show_help(bool usage_only) {
" -A compare virtual and physical addresses\n"
" -C show colors\n"
" -T show timestamp information\n"
" -0 [cmd] command to execute for file0 when option -t 'commands' is given.\n"
" if -1 is not set, it will execute the same cmd for file1.\n"
" -1 [cmd] command to execute for file1 when option -t 'commands' is given.\n"
" -0 [cmd] input for file0 when option -t 'commands' is given.\n"
" the same value will be set for file1, if -1 is not set.\n"
" -1 [cmd] input for file1 when option -t 'commands' is given.\n"
" -t [type] compute the difference between two files based on its type:\n"
" bytes | compares raw bytes in the files (only for small files)\n"
" lines | compares text files\n"
@ -174,6 +176,8 @@ static void rz_diff_show_help(bool usage_only) {
" | requires -0 <cmd> and -1 <cmd> is optional\n"
" entries | compares entries found in the files\n"
" fields | compares fields found in the files\n"
" graphs | compares 2 functions and outputs in graphviz/dot format\n"
" | requires -0 <fcn name|offset> and -1 <fcn name|offset> is optional\n"
" imports | compares imports found in the files\n"
" libraries | compares libraries found in the files\n"
" sections | compares sections found in the files\n"
@ -202,8 +206,8 @@ static void rz_diff_parse_arguments(int argc, const char **argv, DiffContext *ct
rz_getopt_init(&opt, argc, argv, "hjqvACTa:b:d:t:0:1:");
while ((c = rz_getopt_next(&opt)) != -1) {
switch (c) {
case '0': rz_diff_ctx_set_def(ctx, command_a, NULL, opt.arg); break;
case '1': rz_diff_ctx_set_def(ctx, command_b, NULL, opt.arg); break;
case '0': rz_diff_ctx_set_def(ctx, input_a, NULL, opt.arg); break;
case '1': rz_diff_ctx_set_def(ctx, input_b, NULL, opt.arg); break;
case 'A': rz_diff_ctx_set_def(ctx, compare_addresses, false, true); break;
case 'C': rz_diff_ctx_set_def(ctx, colors, false, true); break;
case 'T': rz_diff_ctx_set_def(ctx, show_time, false, true); break;
@ -262,21 +266,35 @@ static void rz_diff_parse_arguments(int argc, const char **argv, DiffContext *ct
} else if (!strcmp(type, "lines")) {
rz_diff_ctx_set_type(ctx, DIFF_TYPE_LINES);
} else if (!strcmp(type, "functions")) {
if (ctx->input_a) {
rz_diff_error_opt(ctx, DIFF_OPT_ERROR, "option -t '%s' does not support -0.\n", type);
} else if (ctx->input_b) {
rz_diff_error_opt(ctx, DIFF_OPT_ERROR, "option -t '%s' does not support -1.\n", type);
}
ctx->option = DIFF_OPT_GRAPH;
rz_diff_ctx_set_type(ctx, DIFF_TYPE_FUNCTIONS);
} else if (!strcmp(type, "classes")) {
rz_diff_ctx_set_type(ctx, DIFF_TYPE_CLASSES);
} else if (!strcmp(type, "command")) {
if (!ctx->command_a) {
if (!ctx->input_a) {
rz_diff_error_opt(ctx, DIFF_OPT_ERROR, "option -t '%s' requires -0 <command>.\n", type);
}
if (!ctx->command_b) {
ctx->command_b = ctx->command_a;
if (!ctx->input_b) {
ctx->input_b = ctx->input_a;
}
rz_diff_ctx_set_type(ctx, DIFF_TYPE_COMMAND);
} else if (!strcmp(type, "entries")) {
rz_diff_ctx_set_type(ctx, DIFF_TYPE_ENTRIES);
} else if (!strcmp(type, "fields")) {
rz_diff_ctx_set_type(ctx, DIFF_TYPE_FIELDS);
} else if (!strcmp(type, "graphs")) {
if (!ctx->input_a) {
rz_diff_error_opt(ctx, DIFF_OPT_ERROR, "option -t '%s' requires -0 <fcn name|address>.\n", type);
} else if (ctx->input_a && !ctx->input_b) {
ctx->input_b = ctx->input_a;
}
ctx->option = DIFF_OPT_GRAPH;
rz_diff_ctx_set_type(ctx, DIFF_TYPE_PLOTDIFF);
} else if (!strcmp(type, "imports")) {
rz_diff_ctx_set_type(ctx, DIFF_TYPE_IMPORTS);
} else if (!strcmp(type, "libraries")) {
@ -409,7 +427,7 @@ static inline RzBinFile *core_get_file(RzCoreFile *cfile) {
return rz_pvector_at(&cfile->binfiles, 0);
}
static RzCoreFile *rz_diff_load_file_with_core(const char *filename, const char *architecture, ut32 arch_bits) {
static RzCoreFile *rz_diff_load_file_with_core(const char *filename, const char *architecture, ut32 arch_bits, bool colors) {
RzCore *core = NULL;
RzCoreFile *cfile = NULL;
RzBinFile *bfile = NULL;
@ -421,7 +439,7 @@ static RzCoreFile *rz_diff_load_file_with_core(const char *filename, const char
}
rz_core_loadlibs(core, RZ_CORE_LOADLIBS_ALL, NULL);
rz_config_set_i(core->config, "scr.color", 0);
rz_config_set_i(core->config, "scr.color", colors ? 1 : 0);
rz_config_set_b(core->config, "scr.interactive", false);
rz_config_set_b(core->config, "cfg.debug", false);
core->print->scr_prompt = false;
@ -1120,160 +1138,10 @@ static RzDiff *rz_diff_fields_new(DiffFile *dfile_a, DiffFile *dfile_b, bool com
return rz_diff_generic_new(list_a, rz_list_length(list_a), list_b, rz_list_length(list_b), &methods);
}
/**************************************** functions ***************************************/
static ut32 func_hash_addr(const DiffFunction *elem) {
ut32 hash = rz_diff_hash_data((const ut8 *)elem->name, strlen(elem->name));
hash ^= (ut32)(elem->address >> 32);
hash ^= (ut32)elem->address;
hash ^= (ut32)elem->bits;
hash ^= (ut32)elem->n_instructions;
return hash;
}
static int func_compare_addr(const DiffFunction *a, const DiffFunction *b) {
st64 ret;
IF_STRCMP_S(ret, a->name, b->name);
ret = ((st64)b->address) - ((st64)a->address);
if (ret) {
return ret;
}
ret = ((st64)b->n_instructions) - ((st64)a->n_instructions);
if (ret) {
return ret;
}
return ((st64)b->bits) - ((st64)a->bits);
}
static void func_stringify_addr(const DiffFunction *elem, RzStrBuf *sb) {
rz_strbuf_setf(sb, "0x%016" PFMT64x " instrs: %-4d bits: %-2d %s\n",
elem->address, elem->n_instructions, elem->bits, elem->name);
}
static ut32 func_hash(const DiffFunction *elem) {
ut32 hash = rz_diff_hash_data((const ut8 *)elem->name, strlen(elem->name));
hash ^= (ut32)elem->bits;
hash ^= (ut32)elem->n_instructions;
return hash;
}
static int func_compare(const DiffFunction *a, const DiffFunction *b) {
st64 ret;
IF_STRCMP_S(ret, a->name, b->name);
ret = ((st64)b->n_instructions) - ((st64)a->n_instructions);
if (ret) {
return ret;
}
return ((st64)b->bits) - ((st64)a->bits);
}
static void func_stringify(const DiffFunction *elem, RzStrBuf *sb) {
rz_strbuf_setf(sb, "instrs: %-4d bits: %-2d %s\n", elem->n_instructions, elem->bits, elem->name);
}
static DiffFunction *func_new(RzAnalysisFunction *function) {
DiffFunction *df = RZ_NEW(DiffFunction);
if (!df) {
return NULL;
}
df->name /* */ = strdup(function->name);
df->bits /* */ = function->bits;
df->address /* */ = function->addr;
df->n_instructions = function->ninstr;
return df;
}
static void func_free(DiffFunction *func) {
if (!func) {
return;
}
free(func->name);
free(func);
}
static RzList *func_get_all_functions(const char *filename, const char *architecture, ut32 arch_bits) {
RzList *list = NULL;
RzList *functions = NULL;
RzCoreFile *cfile = NULL;
RzListIter *it = NULL;
RzAnalysisFunction *fcn;
DiffFunction *df = NULL;
cfile = rz_diff_load_file_with_core(filename, architecture, arch_bits);
if (!cfile) {
return NULL;
}
functions = rz_analysis_function_list(cfile->core->analysis);
if (!functions) {
rz_diff_error("cannot get function list\n");
goto func_get_all_functions_fail;
}
list = rz_list_newf((RzListFree)func_free);
if (!list) {
rz_diff_error("cannot allocate list for functions\n");
goto func_get_all_functions_fail;
}
rz_list_foreach (functions, it, fcn) {
if (!fcn) {
continue;
}
df = func_new(fcn);
if (!df) {
rz_diff_error("cannot allocate function\n");
goto func_get_all_functions_fail;
}
if (!rz_list_append(list, df)) {
func_free(df);
rz_diff_error("cannot insert function in list\n");
goto func_get_all_functions_fail;
}
}
rz_core_free(cfile->core);
return list;
func_get_all_functions_fail:
if (cfile) {
rz_core_free(cfile->core);
}
return NULL;
}
static RzDiff *rz_diff_functions_new(DiffContext *ctx) {
RzList *list_a = NULL;
RzList *list_b = NULL;
list_a = func_get_all_functions(ctx->file_a, ctx->architecture, ctx->arch_bits);
if (!list_a) {
return NULL;
}
list_b = func_get_all_functions(ctx->file_b, ctx->architecture, ctx->arch_bits);
if (!list_b) {
return NULL;
}
rz_list_sort(list_a, (RzListComparator)func_compare);
rz_list_sort(list_b, (RzListComparator)func_compare);
RzDiffMethods methods = {
.elem_at = (RzDiffMethodElemAt)rz_diff_list_elem_at,
.elem_hash = (RzDiffMethodElemHash)(ctx->compare_addresses ? func_hash_addr : func_hash),
.compare = (RzDiffMethodCompare)(ctx->compare_addresses ? func_compare_addr : func_compare),
.stringify = (RzDiffMethodStringify)(ctx->compare_addresses ? func_stringify_addr : func_stringify),
.ignore = NULL,
};
return rz_diff_generic_new(list_a, rz_list_length(list_a), list_b, rz_list_length(list_b), &methods);
}
/**************************************** commands ***************************************/
static char *execute_command(const char *command, const char *filename, const char *architecture, ut32 arch_bits) {
RzCoreFile *cfile = rz_diff_load_file_with_core(filename, architecture, arch_bits);
static char *execute_command(const char *command, const char *filename, DiffContext *ctx) {
RzCoreFile *cfile = rz_diff_load_file_with_core(filename, ctx->architecture, ctx->arch_bits, ctx->colors);
if (!cfile) {
return NULL;
}
@ -1287,15 +1155,15 @@ static RzDiff *rz_diff_command_new(DiffContext *ctx) {
char *output_a = NULL;
char *output_b = NULL;
output_a = execute_command(ctx->command_a, ctx->file_a, ctx->architecture, ctx->arch_bits);
output_a = execute_command(ctx->input_a, ctx->file_a, ctx);
if (!output_a) {
rz_diff_error_ret(NULL, "cannot execute command '%s' on file '%s'\n", ctx->command_a, ctx->file_a);
rz_diff_error_ret(NULL, "cannot execute command '%s' on file '%s'\n", ctx->input_a, ctx->file_a);
}
output_b = execute_command(ctx->command_b, ctx->file_b, ctx->architecture, ctx->arch_bits);
output_b = execute_command(ctx->input_b, ctx->file_b, ctx);
if (!output_b) {
free(output_a);
rz_diff_error_ret(NULL, "cannot execute command '%s' on file '%s'\n", ctx->command_b, ctx->file_b);
rz_diff_error_ret(NULL, "cannot execute command '%s' on file '%s'\n", ctx->input_b, ctx->file_b);
}
RzDiff *diff = rz_diff_lines_new(output_a, output_b, NULL);
@ -1304,7 +1172,7 @@ static RzDiff *rz_diff_command_new(DiffContext *ctx) {
return diff;
}
/**************************************** rz-diff ***************************************/
/**************************************** unified ***************************************/
static bool rz_diff_unified_files(DiffContext *ctx) {
size_t a_size = 0;
@ -1350,9 +1218,6 @@ static bool rz_diff_unified_files(DiffContext *ctx) {
case DIFF_TYPE_FIELDS:
diff = rz_diff_fields_new(&dfile_a, &dfile_b, ctx->compare_addresses);
break;
case DIFF_TYPE_FUNCTIONS:
diff = rz_diff_functions_new(ctx);
break;
case DIFF_TYPE_IMPORTS:
diff = rz_diff_imports_new(&dfile_a, &dfile_b);
break;
@ -1400,11 +1265,6 @@ static bool rz_diff_unified_files(DiffContext *ctx) {
result = true;
rz_diff_unified_files_bad:
if (ctx->type == DIFF_TYPE_FUNCTIONS) {
rz_list_free((RzList *)rz_diff_get_a(diff));
rz_list_free((RzList *)rz_diff_get_b(diff));
}
rz_diff_free(diff);
rz_diff_file_close(&dfile_a);
rz_diff_file_close(&dfile_b);
@ -1413,6 +1273,72 @@ rz_diff_unified_files_bad:
return result;
}
/**************************************** graphs ***************************************/
static bool convert_offset_from_input(RzCore *core, const char *input, ut64 *offset) {
if (rz_num_is_valid_input(NULL, input)) {
*offset = rz_num_get_input_value(NULL, input);
return true;
}
RzFlagItem *fi = rz_flag_get(core->flags, input);
if (fi) {
*offset = fi->offset;
return true;
}
return false;
}
static bool rz_diff_graphs_files(DiffContext *ctx) {
bool success = false;
RzCoreFile *a = NULL;
RzCoreFile *b = NULL;
a = rz_diff_load_file_with_core(ctx->file_a, ctx->architecture, ctx->arch_bits, ctx->colors);
if (!a) {
goto rz_diff_graphs_files_bad;
}
b = rz_diff_load_file_with_core(ctx->file_b, ctx->architecture, ctx->arch_bits, ctx->colors);
if (!b) {
goto rz_diff_graphs_files_bad;
}
if (ctx->type == DIFF_TYPE_PLOTDIFF) {
ut64 offset_a = 0;
ut64 offset_b = 0;
if (!convert_offset_from_input(a->core, ctx->input_a, &offset_a)) {
rz_diff_error("cannot convert '%s' into an offset\n", ctx->input_a);
goto rz_diff_graphs_files_bad;
}
if (!convert_offset_from_input(b->core, ctx->input_b, &offset_b)) {
rz_diff_error("cannot convert '%s' into an offset\n", ctx->input_b);
goto rz_diff_graphs_files_bad;
}
if (!rz_core_gdiff_function_2_files(a->core, b->core, offset_a, offset_b)) {
rz_diff_error("cannot diff graphs with inputs '%s' with '%s'\n", ctx->input_a, ctx->input_b);
goto rz_diff_graphs_files_bad;
}
rz_core_diff_show_function(a->core, b->core, offset_a, ctx->mode == DIFF_MODE_JSON);
} else {
if (!rz_core_gdiff_2_files(a->core, b->core)) {
rz_diff_error("cannot diff all graphs\n");
goto rz_diff_graphs_files_bad;
}
rz_core_diff_show(a->core, b->core, ctx->mode == DIFF_MODE_JSON);
}
success = true;
rz_diff_graphs_files_bad:
rz_core_free(a ? a->core : NULL);
rz_core_free(b ? b->core : NULL);
return success;
}
RZ_API int rz_main_rz_diff(int argc, const char **argv) {
bool success = false;
DiffContext ctx;
@ -1426,6 +1352,9 @@ RZ_API int rz_main_rz_diff(int argc, const char **argv) {
case DIFF_OPT_UNIFIED:
success = rz_diff_unified_files(&ctx);
break;
case DIFF_OPT_GRAPH:
success = rz_diff_graphs_files(&ctx);
break;
case DIFF_OPT_VERSION:
rz_main_version_print("rz-diff");
break;

View file

@ -277,6 +277,7 @@ RZ_API RzBaseType *rz_type_db_get_base_type(RzTypeDB *typedb, const char *name)
}
if (base_type) {
free(base_type->name);
base_type->name = sname;
} else {
free(sname);

View file

@ -60,7 +60,7 @@ ec graph.box3 rgb:c0a060
ec graph.box4 rgb:c0a060
ec graph.true rgb:f0d090
ec graph.false rgb:403010
ec graph.trufae rgb:f0d090
ec graph.ujump rgb:f0d090
ec graph.current rgb:0000ff
ec graph.traced rgb:0000ff
ec graph.diff.unknown rgb:881798
@ -172,7 +172,7 @@ EXPECT=<<EOF
## graph.box4
## graph.true
## graph.false
## graph.trufae
## graph.ujump
## graph.current
## graph.traced
## graph.diff.unknown
@ -242,7 +242,7 @@ EXPECT=<<EOF
## graph.box4
## graph.true
## graph.false
## graph.trufae
## graph.ujump
## graph.current
## graph.traced
## graph.diff.unknown

File diff suppressed because one or more lines are too long

View file

@ -53,7 +53,7 @@ ec graph.box3 rgb:881798
ec graph.box4 rgb:767676
ec graph.true rgb:13a10e
ec graph.false rgb:c50f1f
ec graph.trufae rgb:0037da
ec graph.ujump rgb:0037da
ec graph.current rgb:0037da
ec graph.traced rgb:c19c00
ec gui.cflow rgb:c19c00