Skip to content

Commit

Permalink
Fixes rizinorg#2247 - Improve try/catch analysis [WIP]
Browse files Browse the repository at this point in the history
Your checklist for this pull request

- [x] I've read the guidelines for contributing to this repository
- [x] I made sure to follow the project's coding style
- [ ] I've documented or updated the documentation of every function and struct this PR changes. If not so I've explained why.
- [ ] I've added tests that prove my fix is effective or that my feature works (if possible)
- [ ] I've updated the rizin book with the relevant information (if needed)

Detailed description

Bin

- Load exception information by default

Analysis

-  Make analyzing exception scopes the default
-  Analyze all exception sources as functions
-  Do not hack basic blocks flow to add try/catch information

Graph

- Add an edge pointing to the catch block for each basic block inside the try scope
-  Add configuration graph.trycatch to control if graphing the exception blocks is enabled

Problems

    8ff06e9 Is basically copy-pasted logic from block.c
    This can make graphs a lot noisier
    Cutter will have to implement its own version of the logic if it wants to copy rizin in showing the connection between the blocks and its catch blocks
    I didn't add tests yet

Test plan

Open bins\pe\microsoft_seh_tests\x64\xcpt4.exe for example
Optional: idp to load debug info for binary
aaa
Optional: .iw to show try catch scopes as flags
s main
Enter visual graph mode, make sure all edges, lines are painted correctly, and that there area now new edges pointing each basic block to its catch block if it is inside a try scope

Open file from rizinorg#2247
aaa
s main
Enter visual graph mode, make sure issue is fixed

Closing issues

Closes rizinorg#2247
  • Loading branch information
j-barnak committed Feb 28, 2024
1 parent 183571c commit df494ed
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 55 deletions.
8 changes: 8 additions & 0 deletions librz/analysis/analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ static void global_kv_free(HtPPKv *kv) {
rz_analysis_var_global_free(kv->value);
}

static void exception_scope_kv_free(HtUPKv *kv) {
rz_list_free(kv->value);
}

RZ_API RzAnalysis *rz_analysis_new(void) {
int i;
RzAnalysis *analysis = RZ_NEW0(RzAnalysis);
Expand Down Expand Up @@ -102,6 +106,7 @@ RZ_API RzAnalysis *rz_analysis_new(void) {
analysis->cpp_abi = RZ_ANALYSIS_CPP_ABI_ITANIUM;
analysis->opt.depth = 32;
analysis->opt.noncode = false; // do not analyze data by default
analysis->exception_scopes_ht = ht_up_new(NULL, exception_scope_kv_free, NULL);
rz_spaces_init(&analysis->meta_spaces, "CS");
rz_event_hook(analysis->meta_spaces.event, RZ_SPACE_EVENT_UNSET, meta_unset_for, NULL);
rz_event_hook(analysis->meta_spaces.event, RZ_SPACE_EVENT_COUNT, meta_count_for, NULL);
Expand Down Expand Up @@ -192,6 +197,9 @@ RZ_API RzAnalysis *rz_analysis_free(RzAnalysis *a) {
ht_pp_free(a->ht_global_var);
rz_list_free(a->plugins);
rz_analysis_debug_info_free(a->debug_info);
// TODO (Jared): Implement
rz_rbtree_itv_free(&a->exception_scopes_tree);
ht_up_free(a->exception_scopes_ht);
free(a);
return NULL;
}
Expand Down
43 changes: 12 additions & 31 deletions librz/analysis/fcn.c
Original file line number Diff line number Diff line change
Expand Up @@ -773,37 +773,6 @@ static RzAnalysisBBEndCause run_basic_block_analysis(RzAnalysisTaskItem *item, R
rz_analysis_block_set_size(bb, newbbsize);
fcn->ninstr++;
}
if (analysis->opt.trycatch) {
const char *name = analysis->coreb.getName(analysis->coreb.core, at);
if (name) {
if (rz_str_startswith(name, "try.") && rz_str_endswith(name, ".from")) {
char *handle = strdup(name);
// handle = rz_str_replace (handle, ".from", ".to", 0);
ut64 from_addr = analysis->coreb.numGet(analysis->coreb.core, handle);
handle = rz_str_replace(handle, ".from", ".catch", 0);
ut64 handle_addr = analysis->coreb.numGet(analysis->coreb.core, handle);
handle = rz_str_replace(handle, ".catch", ".filter", 0);
ut64 filter_addr = analysis->coreb.numGet(analysis->coreb.core, handle);
if (filter_addr) {
rz_analysis_xrefs_set(analysis, op.addr, filter_addr, RZ_ANALYSIS_XREF_TYPE_CALL);
}
bb->jump = at + oplen;
if (from_addr != bb->addr) {
bb->fail = handle_addr;
ret = analyze_function_locally(analysis, fcn, handle_addr);
if (bb->size == 0) {
rz_analysis_function_remove_block(fcn, bb);
}
rz_analysis_block_update_hash(bb);
rz_analysis_block_unref(bb);
bb = fcn_append_basic_block(analysis, fcn, bb->jump);
if (!bb) {
gotoBeach(RZ_ANALYSIS_RET_ERROR);
}
}
}
}
}
idx += oplen;
delay.un_idx = idx;
if (analysis->opt.delay && op.delay > 0 && !delay.pending) {
Expand Down Expand Up @@ -1633,6 +1602,18 @@ RZ_API int rz_analysis_fcn(RzAnalysis *analysis, RzAnalysisFunction *fcn, ut64 a
RzVector tasks;
rz_vector_init(&tasks, sizeof(RzAnalysisTaskItem), NULL, NULL);
rz_analysis_task_item_new(analysis, &tasks, fcn, NULL, addr, 0);
if (analysis->opt.trycatch) {
RzBinTrycatch *tc;
RzListIter *it;
RzList *scopes = ht_up_find(analysis->exception_scopes_ht, fcn->addr, NULL);
rz_list_foreach (scopes, it, tc) {
if (tc->filter) {
rz_analysis_xrefs_set(analysis, tc->from, tc->filter, RZ_ANALYSIS_XREF_TYPE_CALL);
}
// TODO (Jared): Fix Function
rz_analysis_task_item_new(analysis, &tasks, fcn, NULL, tc->handler, fcn->stack);
}
}
int ret = rz_analysis_run_tasks(&tasks);
rz_vector_fini(&tasks);
return ret;
Expand Down
95 changes: 80 additions & 15 deletions librz/core/agraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,10 @@ static void create_dummy_nodes(RzAGraph *g) {
dummy->layer = from->layer + i;
dummy->is_reversed = is_reversed(g, e);
dummy->w = 1;
dummy->is_unconditional_jmp = to->is_unconditional_jmp;
dummy->address = to->address;
dummy->jump = to->jump;
dummy->fail = to->fail;
rz_agraph_add_edge_at(g, prev, dummy, nth);
rz_list_append(g->dummy_nodes, dummy);

Expand Down Expand Up @@ -1754,6 +1758,9 @@ static void fix_back_edge_dummy_nodes(RzAGraph *g, RzANode *from, RzANode *to) {
}

static int get_edge_number(const RzAGraph *g, RzANode *src, RzANode *dst, bool outgoing) {
if (g->is_callgraph) {
return 0;
}
RzListIter *itn;
RzGraphNode *gv;
int cur_nth = 0;
Expand All @@ -1768,6 +1775,9 @@ static int get_edge_number(const RzAGraph *g, RzANode *src, RzANode *dst, bool o
? rz_graph_get_neighbours(g->graph, src->gnode)
: rz_graph_innodes(g->graph, dst->gnode);
const int exit_edges = rz_list_length(neighbours);
if (exit_edges == 1) {
return -1;
}
rz_list_foreach (neighbours, itn, gv) {
if (!(v = gv->data)) {
break;
Expand Down Expand Up @@ -2346,6 +2356,7 @@ static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
RzListIter *iter;
bool emu = rz_config_get_i(core->config, "asm.emu");
bool few = rz_config_get_i(core->config, "graph.few");
bool trycatch = rz_config_get_i(core->config, "graph.trycatch");
int ret = false;
ut64 saved_gp = core->analysis->gp;
ut8 *saved_arena = NULL;
Expand Down Expand Up @@ -2384,6 +2395,9 @@ static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
char *title = get_title(bb->addr);

RzANode *node = rz_agraph_add_node(g, title, body);
node->address = bb->addr;
node->jump = bb->jump;
node->fail = bb->fail;
if (shortcuts) {
rz_core_agraph_add_shortcut(core, g, node, bb->addr, title);
}
Expand All @@ -2395,6 +2409,15 @@ static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
core->keep_asmqjmps = true;
}

RzList *exception_scopes = NULL;
if (trycatch) {
// RzInterval itv = { rz_analysis_function_min_addr(fcn),
RzInterval itv = { rz_analysis_function_min_addr(fcn),
rz_analysis_function_max_addr(fcn) - rz_analysis_function_min_addr(fcn) };
// TODO (Jared): Implement exception_scopes_tree
exception_scopes = rz_rbtree_itv_all_intersect(fcn->analysis->exception_scopes_tree, itv);
}

rz_list_foreach (fcn->bbs, iter, bb) {
if (bb->addr == UT64_MAX) {
continue;
Expand All @@ -2406,6 +2429,28 @@ static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
char *title = get_title(bb->addr);
RzANode *u = rz_agraph_get_node(g, title);
RzANode *v;

RzListIter *it;
RzBinTrycatch *tc;
rz_list_foreach (exception_scopes, it, tc) {
if (bb->addr == tc->handler) {
continue;
}
if (!rz_itv_overlap2((RzInterval){ bb->addr, bb->size }, tc->from, tc->to - 1)) {
continue;
}
RzAnalysisBlock *catch_bb = rz_analysis_get_block_at(bb->analysis, tc->handler);
if (!catch_bb || catch_bb->addr == bb->jump || catch_bb->addr == bb->fail) {
continue;
}
title = get_title(catch_bb->addr);
v = rz_agraph_get_node(g, title);
free(title);
if (v) {
v->is_unconditional_jmp = true;
rz_agraph_add_edge(g, u, v);
}
}
free(title);
if (bb->jump != UT64_MAX) {
title = get_title(bb->jump);
Expand All @@ -2430,7 +2475,7 @@ static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
}
}
}

rz_list_free(exception_scopes);
delete_dup_edges(g);
ret = true;

Expand Down Expand Up @@ -2861,6 +2906,14 @@ static int first_x_cmp(const void *_a, const void *_b, void *user) {
return 0;
}

static inline bool is_true_edge(RzANode *src, RzANode *dst) {
return src->jump != UT64_MAX && src->jump == dst->address;
}

static inline bool is_false_edge(RzANode *src, RzANode *dst) {
return src->fail != UT64_MAX && src->fail == dst->address;
}

static void agraph_print_edges(RzAGraph *g) {
if (!g->edgemode) {
return;
Expand All @@ -2869,12 +2922,20 @@ static void agraph_print_edges(RzAGraph *g) {
agraph_print_edges_simple(g);
return;
}

int out_nth, in_nth, bendpoint;
RzListIter *itn, *itm, *ito;
RzCanvasLineStyle style = { 0 };
const RzList *nodes = rz_graph_get_nodes(g->graph);
RzGraphNode *ga;
RzANode *a;
// RzANode *real_parent = a;
// if (a->is_dummy) {
// real_parent = (RzANode *)(((RzGraphNode *)rz_list_first(ga->in_nodes))->data);
// while (real_parent && real_parent->is_dummy) {
// real_parent = (RzANode *)(((RzGraphNode *)rz_list_first((real_parent->gnode)->in_nodes))->data);
// }
// }

RzList *lyr = rz_list_new();
RzList *bckedges = rz_list_new();
Expand Down Expand Up @@ -2938,28 +2999,29 @@ static void agraph_print_edges(RzAGraph *g) {
rz_list_sort(neighbours, first_x_cmp, NULL);
}

RzANode *real_parent = a;
if (a->is_dummy) {
real_parent = (RzANode *)(((RzGraphNode *)rz_list_first(ga->in_nodes))->data);
while (real_parent && real_parent->is_dummy) {
real_parent = (RzANode *)(((RzGraphNode *)rz_list_first((real_parent->gnode)->in_nodes))->data);
}
}

rz_list_foreach (neighbours, itn, gb) {
if (!(b = gb->data)) {
break;
}
out_nth = get_edge_number(g, a, b, true);
in_nth = get_edge_number(g, a, b, false);

bool parent_many = false;
if (a->is_dummy) {
RzANode *in = (RzANode *)(((RzGraphNode *)rz_list_first(ga->in_nodes))->data);
while (in && in->is_dummy) {
in = (RzANode *)(((RzGraphNode *)rz_list_first((in->gnode)->in_nodes))->data);
}
if (in && in->gnode) {
parent_many = rz_list_length(in->gnode->out_nodes) > 2;
} else {
parent_many = false;
}
}

style.dot_style = DOT_STYLE_NORMAL;
if (many || parent_many || g->is_il) {
if (is_true_edge(real_parent, b) && out_nth != -1) {
style.color = LINE_TRUE;
style.dot_style = DOT_STYLE_CONDITIONAL;
} else if (is_false_edge(real_parent, b)) {
style.color = LINE_FALSE;
style.dot_style = DOT_STYLE_CONDITIONAL;
} else if (many || b->is_unconditional_jmp) {
style.color = LINE_UNCJMP;
} else {
switch (out_nth) {
Expand Down Expand Up @@ -3783,6 +3845,9 @@ RZ_API RzANode *rz_agraph_add_node(const RzAGraph *g, const char *title, const c
rz_strf(buf, "agraph.nodes.%s.body", res->title);
sdb_set_owned(g->db, buf, s, 0);
}
res->address = UT64_MAX;
res->jump = UT64_MAX;
res->fail = UT64_MAX;
return res;
}

Expand Down
23 changes: 23 additions & 0 deletions librz/core/canalysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -1487,10 +1487,24 @@ static bool is_skippable_addr(RzCore *core, ut64 addr) {
if (fcn->addr == addr) {
return true;
}
if (ht_up_find(core->analysis->exception_scopes_ht, addr, NULL)) {
return false;
}
const RzList *flags = rz_flag_get_list(core->flags, addr);
return !(flags && rz_list_find(flags, fcn, find_sym_flag, NULL));
}

static bool analyze_exception_source(void *user, const ut64 key, const void *value) {
RzCore *core = user;
const RzList *scopes = value;
RzListIter *it;
RzBinTrycatch *trycatch;
rz_list_foreach (scopes, it, trycatch) {
rz_core_analysis_fcn(core, trycatch->source, UT64_MAX, RZ_ANALYSIS_XREF_TYPE_NULL, core->analysis->opt.depth);
}
return true;
}

// XXX: This function takes sometimes forever
/* analyze a RzAnalysisFunction at the address 'at'.
* If the function has been already analyzed, it adds a
Expand Down Expand Up @@ -4003,8 +4017,17 @@ RZ_API bool rz_core_analysis_everything(RzCore *core, bool experimental, char *d
return false;
}

if (core->analysis->exception_scopes_ht->count) {
notify = "Analyze exception sources as functions";
rz_core_notify_begin(core, "%s", notify);
ht_up_foreach(core->analysis->exception_scopes_ht, analyze_exception_source, core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
}

notify = "Analyze function calls";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_calls(core, false); // "aac"
(void)rz_core_analysis_calls(core, false); // "aac"
rz_core_seek(core, curseek, true);
rz_core_notify_done(core, "%s", notify);
Expand Down
51 changes: 44 additions & 7 deletions librz/core/cbin.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@

#define LOAD_BSS_MALLOC 0

#define IS_MODE_SET(mode) ((mode)&RZ_MODE_SET)
#define IS_MODE_SIMPLE(mode) ((mode)&RZ_MODE_SIMPLE)
#define IS_MODE_SIMPLEST(mode) ((mode)&RZ_MODE_SIMPLEST)
#define IS_MODE_JSON(mode) ((mode)&RZ_MODE_JSON)
#define IS_MODE_RZCMD(mode) ((mode)&RZ_MODE_RIZINCMD)
#define IS_MODE_EQUAL(mode) ((mode)&RZ_MODE_EQUAL)
#define IS_MODE_SET(mode) ((mode) & RZ_MODE_SET)
#define IS_MODE_SIMPLE(mode) ((mode) & RZ_MODE_SIMPLE)
#define IS_MODE_SIMPLEST(mode) ((mode) & RZ_MODE_SIMPLEST)
#define IS_MODE_JSON(mode) ((mode) & RZ_MODE_JSON)
#define IS_MODE_RZCMD(mode) ((mode) & RZ_MODE_RIZINCMD)
#define IS_MODE_EQUAL(mode) ((mode) & RZ_MODE_EQUAL)
#define IS_MODE_NORMAL(mode) (!(mode))
#define IS_MODE_CLASSDUMP(mode) ((mode)&RZ_MODE_CLASSDUMP)
#define IS_MODE_CLASSDUMP(mode) ((mode) & RZ_MODE_CLASSDUMP)

// dup from cmd_info
#define PAIR_WIDTH "9"
Expand Down Expand Up @@ -279,6 +279,9 @@ RZ_API bool rz_core_bin_apply_info(RzCore *r, RzBinFile *binfile, ut32 mask) {
if (mask & RZ_CORE_BIN_ACC_RESOURCES) {
rz_core_bin_apply_resources(r, binfile);
}
if (mask & RZ_CORE_BIN_ACC_TRYCATCH) {
rz_core_bin_apply_trycatch(r, binfile);
}

return true;
}
Expand Down Expand Up @@ -1668,6 +1671,40 @@ RZ_API bool rz_core_bin_apply_resources(RzCore *core, RzBinFile *binfile) {
return true;
}

RZ_API bool rz_core_bin_apply_trycatch(RzCore *core, RzBinFile *binfile) {
rz_return_val_if_fail(core && binfile, false);
RzListIter *it;
RzPVector *vec = rz_bin_file_get_trycatch(binfile);
RzBinTrycatch *trycatch = NULL;
rz_pvector_foreach (vec, it) {
RzList *scopes = ht_up_find(core->analysis->exception_scopes_ht, trycatch->source, NULL);
if (!scopes) {
scopes = rz_list_newf(free);
if (!scopes) {
return false;
}
if (!ht_up_insert(core->analysis->exception_scopes_ht, trycatch->source, scopes)) {
rz_list_free(scopes);
return false;
}
}
RzBinTrycatch *tc = RZ_NEW(RzBinTrycatch);
if (!tc) {
return false;
}
*tc = *trycatch;
if (!rz_list_append(scopes, tc)) {
free(tc);
return false;
}
RzInterval itv = { tc->from, tc->to - tc->from - 1 };
if (!rz_rbtree_itv_insert(&core->analysis->exception_scopes_tree, itv, tc)) {
return false;
}
}
return true;
}

static void digests_ht_free(HtPPKv *kv) {
free(kv->key);
free(kv->value);
Expand Down
Loading

0 comments on commit df494ed

Please sign in to comment.