From 3c02fa5618c700b9e674551143ccfb3ce6f37478 Mon Sep 17 00:00:00 2001 From: Naren Sirigere Date: Sat, 4 Jul 2026 13:16:13 +0530 Subject: [PATCH] Fix x64 and x86 SEH analysis (#6558) --- librz/arch/fcn.c | 107 ++++++++++++++++++------- librz/bin/bfile.c | 24 ++++-- librz/bin/bin.c | 7 ++ librz/bin/bobj.c | 1 + librz/core/cbin.c | 2 - librz/core/cconfig.c | 2 +- librz/include/rz_bin.h | 5 +- test/db/abi/compilers/pelles_c | 2 +- test/db/analysis/x86_32 | 137 ++++++++++++++++++++++++++++++++- test/db/analysis/x86_64 | 89 +++++++++++++++++++++ 10 files changed, 339 insertions(+), 37 deletions(-) diff --git a/librz/arch/fcn.c b/librz/arch/fcn.c index af3392ed46..fa4eeb3108 100644 --- a/librz/arch/fcn.c +++ b/librz/arch/fcn.c @@ -583,6 +583,49 @@ static bool is_unknown_call_from_plt(RzAnalysis *analysis, ut64 op_address) { RZ_STR_EQ(s->name, ".plt"); } +typedef struct rz_analysis_trycatch_ctx_t { + RzAnalysis *analysis; + RzVector /**/ *tasks; + RzAnalysisFunction *fcn; + RzStackAddr sp; + ut64 addr; + bool overlapped; + RzAnalysisBlock *bb; +} RzAnalysisTrycatchCtx; + +static bool add_trycatch_handlers(RZ_NONNULL RzAnalysisTrycatchCtx *ctx) { + if (!ctx->analysis->binb.get_trycatch || !ctx->analysis->binb.bin) { + return false; + } + + RzPVector *tcs = ctx->analysis->binb.get_trycatch(ctx->analysis->binb.bin); + if (!tcs) { + return false; + } + + bool found = false; + void **it; + RzBinTrycatch *tc; + rz_pvector_foreach (tcs, it) { + tc = *it; + if (ctx->addr >= tc->from && ctx->addr < tc->to && tc->handler) { + if (!ctx->overlapped && !found && ctx->bb) { + ctx->bb->jump = tc->handler; + } + rz_analysis_task_item_new(ctx->analysis, ctx->tasks, ctx->fcn, NULL, tc->handler, ctx->sp); + found = true; + } + } + return found; +} + +static inline bool is_x86_seh(RzAnalysis *analysis, bool is_x86, RzAnalysisOp *op, ut64 last_push_addr) { + return analysis->opt.trycatch && is_x86 && op->dst && op->dst->seg && + RZ_STR_EQ(op->dst->seg->name, "fs") && op->dst->delta == 0 && + op->dst->memref && last_push_addr != UT64_MAX && + analysis->iob.is_valid_offset(analysis->iob.io, last_push_addr, 0); +} + /** * \brief Analyses the given task item \p item for branches. * @@ -843,34 +886,21 @@ 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 = rz_str_dup(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); + if (analysis->opt.trycatch && analysis->binb.get_trycatch) { + RzPVector *tcs = analysis->binb.get_trycatch(analysis->binb.bin); + if (tcs) { + void **it; + RzBinTrycatch *tc; + rz_pvector_foreach (tcs, it) { + tc = *it; + if (tc->from != at) { + continue; } - 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); - } + if (tc->filter) { + rz_analysis_xrefs_set(analysis, op.addr, tc->filter, RZ_ANALYSIS_XREF_TYPE_CALL); } + rz_analysis_xrefs_set(analysis, op.addr, tc->handler, RZ_ANALYSIS_XREF_TYPE_CODE); + rz_analysis_task_item_new(analysis, tasks, fcn, NULL, tc->handler, sp); } } } @@ -992,6 +1022,11 @@ static RzAnalysisBBEndCause run_basic_block_analysis(RzAnalysisTaskItem *item, R gotoBeach(RZ_ANALYSIS_RET_END); } } + if (is_x86_seh(analysis, is_x86, &op, last_push_addr)) { + rz_analysis_xrefs_set(analysis, op.addr, last_push_addr, RZ_ANALYSIS_XREF_TYPE_CODE); + rz_analysis_task_item_new(analysis, tasks, fcn, NULL, last_push_addr, sp); + last_push_addr = UT64_MAX; + } break; case RZ_ANALYSIS_OP_TYPE_LEA: last_is_reg_mov_lea = false; @@ -1121,6 +1156,12 @@ static RzAnalysisBBEndCause run_basic_block_analysis(RzAnalysisTaskItem *item, R } gotoBeach(RZ_ANALYSIS_RET_BRANCH); } + if (analysis->opt.trycatch) { + RzAnalysisTrycatchCtx ctx = { analysis, tasks, fcn, sp, at, overlapped, bb }; + if (add_trycatch_handlers(&ctx)) { + gotoBeach(RZ_ANALYSIS_RET_BRANCH); + } + } gotoBeach(RZ_ANALYSIS_RET_END); } { @@ -1273,6 +1314,7 @@ static RzAnalysisBBEndCause run_basic_block_analysis(RzAnalysisTaskItem *item, R case RZ_ANALYSIS_OP_TYPE_RCALL: case RZ_ANALYSIS_OP_TYPE_ICALL: case RZ_ANALYSIS_OP_TYPE_IRCALL: + last_push_addr = UT64_MAX; /* call [dst] */ // XXX: this is TYPE_MCALL or indirect-call (void)rz_analysis_xrefs_set(analysis, op.addr, op.ptr, RZ_ANALYSIS_XREF_TYPE_CALL); @@ -1283,11 +1325,18 @@ static RzAnalysisBBEndCause run_basic_block_analysis(RzAnalysisTaskItem *item, R if (f) { f->is_noreturn = true; } + if (analysis->opt.trycatch) { + RzAnalysisTrycatchCtx ctx = { analysis, tasks, fcn, sp, at, overlapped, bb }; + if (add_trycatch_handlers(&ctx)) { + gotoBeach(RZ_ANALYSIS_RET_BRANCH); + } + } gotoBeach(RZ_ANALYSIS_RET_END); } break; case RZ_ANALYSIS_OP_TYPE_CCALL: case RZ_ANALYSIS_OP_TYPE_CALL: + last_push_addr = UT64_MAX; /* call dst */ (void)rz_analysis_xrefs_set(analysis, op.addr, op.jump, RZ_ANALYSIS_XREF_TYPE_CALL); @@ -1296,6 +1345,12 @@ static RzAnalysisBBEndCause run_basic_block_analysis(RzAnalysisTaskItem *item, R if (f) { f->is_noreturn = true; } + if (analysis->opt.trycatch) { + RzAnalysisTrycatchCtx ctx = { analysis, tasks, fcn, sp, at, overlapped, bb }; + if (add_trycatch_handlers(&ctx)) { + gotoBeach(RZ_ANALYSIS_RET_BRANCH); + } + } gotoBeach(RZ_ANALYSIS_RET_END); } diff --git a/librz/bin/bfile.c b/librz/bin/bfile.c index 4c4f3e9c12..03e2813e7e 100644 --- a/librz/bin/bfile.c +++ b/librz/bin/bfile.c @@ -583,12 +583,26 @@ RZ_API void rz_bin_class_free(RZ_NULLABLE RzBinClass *k) { free(k); } -RZ_API RZ_OWN RzPVector /**/ *rz_bin_file_get_trycatch(RZ_NONNULL RzBinFile *bf) { - rz_return_val_if_fail(bf && bf->o && bf->o->plugin, NULL); - if (bf->o->plugin->trycatch) { - return bf->o->plugin->trycatch(bf); +/** + * \brief Get the trycatch information from the given binary file. + * + * If the trycatch information has not been parsed yet, it will call the plugin's + * trycatch method to load it, if that fails then an empty vector will be returned. + * + * \param bf Binary file + * \return Borrowed vector of RzBinTrycatch pointers + */ +RZ_API RZ_BORROW RzPVector /**/ *rz_bin_file_get_trycatch(RZ_NONNULL RzBinFile *bf) { + rz_return_val_if_fail(bf && bf->o, NULL); + if (!bf->o->trycatch) { + if (bf->o->plugin && bf->o->plugin->trycatch) { + bf->o->trycatch = bf->o->plugin->trycatch(bf); + } + if (!bf->o->trycatch) { + bf->o->trycatch = rz_pvector_new((RzPVectorFree)rz_bin_trycatch_free); + } } - return NULL; + return bf->o->trycatch; } RZ_API RzPVector /**/ *rz_bin_file_get_symbols(RzBinFile *bf) { diff --git a/librz/bin/bin.c b/librz/bin/bin.c index c56093e9b9..3d65e4747c 100644 --- a/librz/bin/bin.c +++ b/librz/bin/bin.c @@ -978,6 +978,12 @@ static RzBinObject *bin_bind_get_bin_object(RzBin *bin) { return bf ? bf->o : NULL; } +static RzPVector /**/ *bin_bind_get_trycatch(RzBin *bin) { + rz_return_val_if_fail(bin, NULL); + RzBinFile *bf = rz_bin_cur(bin); + return bf ? rz_bin_file_get_trycatch(bf) : NULL; +} + RZ_API void rz_bin_bind(RzBin *bin, RzBinBind *b) { if (!b) { return; @@ -990,6 +996,7 @@ RZ_API void rz_bin_bind(RzBin *bin, RzBinBind *b) { b->get_vsect_at = bin_bind_get_vsection_at; b->demangle = rz_bin_demangle; b->get_bin_object = bin_bind_get_bin_object; + b->get_trycatch = bin_bind_get_trycatch; } RZ_API RzBuffer *rz_bin_create(RzBin *bin, const char *p, diff --git a/librz/bin/bobj.c b/librz/bin/bobj.c index 29361e33e9..16a10e68f2 100644 --- a/librz/bin/bobj.c +++ b/librz/bin/bobj.c @@ -207,6 +207,7 @@ RZ_IPI void rz_bin_object_free(RzBinObject *o) { rz_pvector_free(o->mem); rz_pvector_free(o->sections); rz_pvector_free(o->symbols); + rz_pvector_free(o->trycatch); rz_pvector_free(o->vfiles); rz_pvector_free(o->resources); for (ut32 i = 0; i < RZ_BIN_SPECIAL_SYMBOL_LAST; i++) { diff --git a/librz/core/cbin.c b/librz/core/cbin.c index 5b64f72f2b..31b03258d6 100644 --- a/librz/core/cbin.c +++ b/librz/core/cbin.c @@ -5654,8 +5654,6 @@ RZ_API bool rz_core_bin_trycatch_print(RZ_NONNULL RzCore *core, RZ_NONNULL RzBin } } - rz_pvector_free(trycatch); - rz_cmd_state_output_array_end(state); return true; } diff --git a/librz/core/cconfig.c b/librz/core/cconfig.c index 99c57ebadd..0eeda36b20 100644 --- a/librz/core/cconfig.c +++ b/librz/core/cconfig.c @@ -3057,7 +3057,7 @@ RZ_API int rz_core_config_init(RzCore *core) { SETCB("analysis.jmp.mid", "true", &cb_analysis_jmpmid, "Continue analysis after jump to middle of instruction (x86 only)"); SETCB("analysis.refstr", "false", &cb_analysis_searchstringrefs, "Search string references in data references"); - SETCB("analysis.trycatch", "false", &cb_analysis_trycatch, "Honor try.X.Y.{from,to,catch} flags"); + SETCB("analysis.trycatch", "true", &cb_analysis_trycatch, "Honor try.X.Y.{from,to,catch} flags"); SETCB("analysis.bb.maxsize", "63K", &cb_analysis_bb_max_size, "Maximum basic block size"); SETCB("analysis.fcn_max_size", "256K", &cb_analysis_fcn_max_size, "Maximum function size (unspecified units)"); SETCB("analysis.pushret", "false", &cb_analysis_pushret, "Analyze push+ret as jmp"); diff --git a/librz/include/rz_bin.h b/librz/include/rz_bin.h index ab8db43b72..67e6e1b285 100644 --- a/librz/include/rz_bin.h +++ b/librz/include/rz_bin.h @@ -338,6 +338,7 @@ typedef struct rz_bin_object_t { RzPVector /**/ *sections; RzPVector /**/ *imports; RzPVector /**/ *symbols; + RzPVector /**/ *trycatch; RzPVector /**/ *resources; /** * \brief Acceleration structure for fast access of the symbol for a given import. @@ -881,6 +882,7 @@ typedef const RzPVector *(*RzBinGetSections)(RzBinObject *obj); typedef RzBinSection *(*RzBinGetSectionAt)(RzBin *bin, ut64 addr); typedef char *(*RzBinDemangle)(RzBin *bin, const char *language, const char *mangled); typedef RzBinObject *(*RzBinGetObject)(RzBin *bin); +typedef RzPVector /**/ *(*RzBinGetTrycatch)(RzBin *bin); typedef struct rz_bin_bind_t { RzBin *bin; @@ -890,6 +892,7 @@ typedef struct rz_bin_bind_t { RzBinGetSectionAt get_vsect_at; RzBinDemangle demangle; RzBinGetObject get_bin_object; + RzBinGetTrycatch get_trycatch; ut32 visibility; } RzBinBind; @@ -1020,7 +1023,7 @@ RZ_API RZ_OWN RzPVector /**/ *rz_bin_file_strings(RZ_NONNULL RzBi // use RzBinFile instead RZ_DEPRECATE RZ_API int rz_bin_is_static(RZ_NONNULL RzBin *bin); -RZ_API RZ_OWN RzPVector /**/ *rz_bin_file_get_trycatch(RZ_NONNULL RzBinFile *bf); +RZ_API RZ_BORROW RzPVector /**/ *rz_bin_file_get_trycatch(RZ_NONNULL RzBinFile *bf); RZ_API RZ_BORROW const RzPVector /**/ *rz_bin_object_get_entries(RZ_NONNULL RzBinObject *obj); RZ_API const RzPVector /**/ *rz_bin_object_get_fields(RZ_NONNULL RzBinObject *obj); diff --git a/test/db/abi/compilers/pelles_c b/test/db/abi/compilers/pelles_c index 5a5346c12f..472e3aed20 100644 --- a/test/db/abi/compilers/pelles_c +++ b/test/db/abi/compilers/pelles_c @@ -44,7 +44,7 @@ EXPECT=<" ; int puts(const char *s) +| 0x140001790 call fcn.1400016a0 +| 0x140001795 lea rcx, qword str.Resumed_Exceptions_Test_PASSED. ; 0x140003340 ; "Resumed Exceptions Test PASSED." ; const char *s +| 0x14000179c call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll_puts] ; [0x1400031a0:8]=0x3e22 ; "\">" ; int puts(const char *s) +| 0x1400017a2 xor eax, eax +| 0x1400017a4 add rsp, 0x28 +\ 0x1400017a8 ret +EOF +RUN + +NAME=x64 structured exception handling +FILE=bins/pe/seh_x64.exe +CMDS=< 0x140001048 mov ebx, dword [var_18h] +| | ; CODE XREF from main @ 0x140001046 +| `--> 0x14000104c lea rcx, qword str.Try_dividing_by_zero. ; 0x140002350 ; "Try dividing by zero." ; const char *s +| 0x140001053 call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll_puts] ; [0x140002180:8]=0x2cbe ; int puts(const char *s) +| 0x140001059 mov eax, ebx +| 0x14000105b cdq +| 0x14000105c xor ecx, ecx +| 0x14000105e idiv ecx +| 0x140001060 mov dword [var_18h], eax +| ,=< 0x140001064 jmp 0x140001074 +| | ; CODE XREF from main @ 0x14000104c +| | 0x140001066 lea rcx, qword str.Inside_nested_except_block ; 0x140002368 ; "Inside nested except block!" ; const char *s +| | 0x14000106d call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll_puts] ; [0x140002180:8]=0x2cbe ; int puts(const char *s) +| | 0x140001073 nop +| | ; CODE XREF from main @ 0x140001064 +| ,`-> 0x140001074 jmp 0x1400010a2 +| | ; CODE XREF from main @ 0x140001022 +| | 0x140001076 lea rcx, qword str.Inside_exception_handler ; 0x140002388 ; "Inside exception handler!\n" ; const char *s +| | 0x14000107d call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll_puts] ; [0x140002180:8]=0x2cbe ; int puts(const char *s) +| | 0x140001083 nop +| | 0x140001084 mov edx, 0xbadbeef ; int64_t arg2 +| | 0x140001089 mov ecx, dword [var_18h] ; int64_t arg1 +| | 0x14000108d call fcn.140001170 +| |,=< 0x140001092 jmp 0x1400010a2 +| || ; CODE XREF from main @ 0x140001084 +| || 0x140001094 lea rcx, qword str.Divide_by_zero_exception ; 0x1400023a8 ; "Divide by zero exception!" ; const char *s +| || 0x14000109b call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll_puts] ; [0x140002180:8]=0x2cbe ; int puts(const char *s) +| || 0x1400010a1 nop +| || ; CODE XREFS from main @ 0x140001074, 0x140001092 +| ``-> 0x1400010a2 xor eax, eax +| 0x1400010a4 mov rbx, qword [var_8h] +| 0x1400010a9 add rsp, 0x30 +| 0x1400010ad pop rdi +\ 0x1400010ae ret +EOF +RUN