From 684277d227e7c73d12a4b82298ad04dc56030608 Mon Sep 17 00:00:00 2001 From: Tushar Jain <150601689+tushar3q34@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:17:06 +0530 Subject: [PATCH] Added class methods identification and inheritance in swift using `rz-libswift` demangler (#5296) --- librz/arch/meson.build | 2 + librz/arch/rtti.c | 39 +++++--- librz/arch/swift_rtti.c | 117 ++++++++++++++++++++++++ librz/bin/bobj_process.c | 70 ++++++++++++--- librz/bin/bobj_process_symbol.c | 5 +- librz/bin/i/private.h | 2 +- librz/include/rz_analysis.h | 1 + librz/include/rz_demangler.h | 1 + test/db/cmd/cmd_acll | 153 ++++++++++++++++++++++++++++++++ test/db/formats/mach0/mach0 | 14 +-- test/db/formats/mach0/objc | 3 +- test/db/formats/mach0/swift | 63 ++++++++++++- 12 files changed, 433 insertions(+), 37 deletions(-) create mode 100644 librz/arch/swift_rtti.c diff --git a/librz/arch/meson.build b/librz/arch/meson.build index 48ba4331fb..792067f5c7 100644 --- a/librz/arch/meson.build +++ b/librz/arch/meson.build @@ -438,6 +438,7 @@ arch_common_sources = [ 'rtti_msvc.c', 'serialize_analysis.c', 'similarity.c', + 'swift_rtti.c', 'switch.c', 'types.c', 'value.c', @@ -480,6 +481,7 @@ rz_arch = library('rz_arch', rz_arch_sources, rz_bin_dep, rz_type_dep, rz_il_dep, + rz_io_dep, capstone_dep, zydis_dep, mth, diff --git a/librz/arch/rtti.c b/librz/arch/rtti.c index 6844bef7d8..f5f04b2b1e 100644 --- a/librz/arch/rtti.c +++ b/librz/arch/rtti.c @@ -85,19 +85,30 @@ RZ_API void rz_analysis_rtti_print_all(RzAnalysis *analysis, RzOutputMode mode) } RZ_API void rz_analysis_rtti_recover_all(RzAnalysis *analysis) { - RVTableContext context; - rz_analysis_vtable_begin(analysis, &context); - - rz_cons_break_push(NULL, NULL); - RzList *vtables = rz_analysis_vtable_search(&context); - if (vtables) { - if (context.abi == RZ_ANALYSIS_CPP_ABI_MSVC) { - rz_analysis_rtti_msvc_recover_all(&context, vtables); - } else { - rz_analysis_rtti_itanium_recover_all(&context, vtables); - } - rz_analysis_no_rtti_analysis(&context, vtables); + RzBinObject *bin_obj = rz_bin_cur_object(analysis->binb.bin); + if (!bin_obj) { + return; + } + switch (bin_obj->lang) { + case RZ_BIN_LANGUAGE_SWIFT: + rz_analysis_rtti_swift(analysis); + break; + default: { + RVTableContext context; + rz_analysis_vtable_begin(analysis, &context); + + rz_cons_break_push(NULL, NULL); + RzList *vtables = rz_analysis_vtable_search(&context); + if (vtables) { + if (context.abi == RZ_ANALYSIS_CPP_ABI_MSVC) { + rz_analysis_rtti_msvc_recover_all(&context, vtables); + } else { + rz_analysis_rtti_itanium_recover_all(&context, vtables); + } + rz_analysis_no_rtti_analysis(&context, vtables); + } + rz_list_free(vtables); + rz_cons_break_pop(); + } } - rz_list_free(vtables); - rz_cons_break_pop(); } diff --git a/librz/arch/swift_rtti.c b/librz/arch/swift_rtti.c new file mode 100644 index 0000000000..5cdbe22fa6 --- /dev/null +++ b/librz/arch/swift_rtti.c @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: 2025 tushar3q34 +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rz_analysis.h" +#include "rz_core.h" +#include "rz_io.h" + +static void add_metaclass_info(HtUP *swift_metaclass_info, RzList /**/ *fields, char *class_name) { + RzListIter *iter; + RzBinClassField *field; + rz_list_foreach (fields, iter, field) { + if (!field) { + continue; + } + if (strstr(field->name, "type metadata for ")) { + ht_up_insert(swift_metaclass_info, field->vaddr + 0x100000000, class_name); + } + } +} + +static void add_class_base(RzAnalysis *analysis, char *class_name, char *super_class_name) { + RzAnalysisBaseClass base = { .class_name = super_class_name, .offset = 0 }; + rz_analysis_class_base_set(analysis, class_name, &base); +} + +static void rz_add_swift_base_classes(RzAnalysis *analysis, RzBinClass *bin_class, HtUP *swift_metaclass_info) { + RzCore *core = analysis->core; + RzList *fields = bin_class->fields; + RzListIter *iter; + RzBinClassField *field; + ut64 vaddr = UT64_MAX; + rz_list_foreach (fields, iter, field) { + if (!field) { + continue; + } + if (strstr(field->name, "type metadata for ")) { + vaddr = field->vaddr + 0x100000000; + break; + } + } + if (vaddr == UT64_MAX) { + return; + } + + vaddr += 0x8; + // check hexa value ar vaddr + 0x8 + // it should be address to type metadata of superclass + ut64 super_vaddr = 0; + ut8 buffer[sizeof(ut64)] = { 0 }; + if (rz_io_nread_at(core->io, vaddr, (ut8 *)&buffer, sizeof(super_vaddr))) { + super_vaddr = rz_read_ble64(buffer, analysis->big_endian); + } + bool found = false; + char *super_class_name = ht_up_find(swift_metaclass_info, super_vaddr, &found); + if (found) { + // If found, we can use the superclass name + add_class_base(analysis, bin_class->name, super_class_name); + } +} + +static void identify_constructor_destructor(RzAnalysis *analysis) { + RzPVector *vect = rz_analysis_class_get_all(analysis, false); + void **iter; + rz_pvector_foreach (vect, iter) { + SdbKv *kv = *iter; + const char *class_name = sdbkv_key(kv); + RzVector *methods = rz_analysis_class_method_get_all(analysis, class_name); + RzAnalysisMethod *method; + rz_vector_foreach (methods, method) { + if (!method || !method->name) { + continue; + } + if (strstr(method->name, "_init") != NULL && strstr(method->name, "_allocating_init") == NULL) { + method->method_type = RZ_ANALYSIS_CLASS_METHOD_CONSTRUCTOR; + rz_analysis_class_method_set(analysis, class_name, method); + } else if (strstr(method->name, "_deinit") != NULL && strstr(method->name, "_deallocating_deinit") == NULL) { + method->method_type = RZ_ANALYSIS_CLASS_METHOD_DESTRUCTOR; + rz_analysis_class_method_set(analysis, class_name, method); + } + } + } + rz_pvector_free(vect); +} + +/** + * \brief Process Swift RTTI information. + * This function processes Swift RTTI information from the given RzBinObject. + * It extracts class, metaclass and method information, and adds it to the analysis. + * \param analysis The RzAnalysis context to use for processing. + */ +RZ_API void rz_analysis_rtti_swift(RzAnalysis *analysis) { + HtUP *swift_metaclass_info = ht_up_new(NULL, free); + RzBinObject *bin_obj = rz_bin_cur_object(analysis->binb.bin); + const RzPVector *classes = rz_bin_object_get_classes(bin_obj); + if (!classes) { + return; + } + void **iter_class; + RzBinClass *class; + rz_pvector_foreach (classes, iter_class) { + class = *iter_class; + if (!class) { + continue; + } + RzList *fields = class->fields; + if (fields) { + add_metaclass_info(swift_metaclass_info, fields, class->name); + } + } + + rz_pvector_foreach (classes, iter_class) { + class = *iter_class; + rz_add_swift_base_classes(analysis, class, swift_metaclass_info); + } + + identify_constructor_destructor(analysis); +} \ No newline at end of file diff --git a/librz/bin/bobj_process.c b/librz/bin/bobj_process.c index 2d9f55f62e..91ce2c6381 100644 --- a/librz/bin/bobj_process.c +++ b/librz/bin/bobj_process.c @@ -93,18 +93,66 @@ RZ_IPI void rz_bin_process_cxx(RzBinObject *o, char *demangled, ut64 paddr, ut64 *name = ':'; } -// this process function does not work with the Apple demangler. -RZ_IPI void rz_bin_process_swift(RzBinObject *o, char *classname, char *demangled, ut64 paddr, ut64 vaddr) { - // if (!classname) { - // return; - // } +static char *find_swift_methodname(RZ_NONNULL char *demangled) { + // methods can be main.Tost.deinit or main.Tost.init() -> main.Tost + // so we will return after second dot + char *dot = strchr(demangled, '.'); + dot = dot ? strchr(dot + 1, '.') : NULL; + if (!dot) { + return NULL; + } + char *methodname = dot + 1; + if (RZ_STR_ISEMPTY(methodname)) { + return NULL; + } + return methodname; +} - // char *name = get_swift_field(demangled, classname); - // if (name) { - // rz_bin_object_add_field(o, classname, name, paddr, vaddr); - // free(name); - // return; - // } +static void bin_process_metaclass(RzBinObject *o, RzBinSymbol *symbol) { + if (RZ_STR_ISEMPTY(symbol->dname)) { + return; + } + char *no_classname = strstr(symbol->dname, "full type metadata for "); + char *classname = strstr(symbol->dname, "type metadata for "); + if (!classname || no_classname) { // only for "type metadata for class" + return; + } + rz_bin_object_add_field(o, classname + strlen("type metadata for "), symbol->dname, symbol->paddr, symbol->vaddr); +} + +// This function is used to process Swift methods. +static void bin_process_swift_class_method(RzBinObject *o, RzBinSymbol *symbol) { + // Before the second dot, we have the class name + char *dot = strchr(symbol->dname, '.'); + dot = dot ? strchr(dot + 1, '.') : NULL; + if (!dot) { + return; + } + char *classname = rz_str_ndup(symbol->dname, dot - symbol->dname); + // classname should not have any spaces or ( or ) + if (strchr(classname, ' ') || strchr(classname, '(') || strchr(classname, ')')) { + free(classname); + return; + } + symbol->classname = classname; + char *methodname = find_swift_methodname(symbol->dname); + + if (!methodname) { + free(classname); + return; + } + + rz_bin_object_add_class(o, classname, NULL, UT64_MAX); + rz_bin_object_add_method(o, classname, methodname, symbol->paddr, symbol->vaddr); +} + +// this process function does not work with the Apple demangler. +RZ_IPI void rz_bin_process_swift(RzBinObject *o, RzBinSymbol *symbol) { + if (RZ_STR_ISEMPTY(symbol->dname)) { + return; + } + bin_process_metaclass(o, symbol); + bin_process_swift_class_method(o, symbol); } /** diff --git a/librz/bin/bobj_process_symbol.c b/librz/bin/bobj_process_symbol.c index f44b9fd038..e7d03306c0 100644 --- a/librz/bin/bobj_process_symbol.c +++ b/librz/bin/bobj_process_symbol.c @@ -38,7 +38,10 @@ static void process_cxx_symbol(RzBinObject *o, RzBinSymbol *symbol) { } static void process_swift_symbol(RzBinObject *o, RzBinSymbol *symbol) { - rz_bin_process_swift(o, symbol->classname, symbol->dname, symbol->paddr, symbol->vaddr); + if (!symbol->dname) { + return; + } + rz_bin_process_swift(o, symbol); } RZ_IPI RzBinProcessLanguage rz_bin_process_language_symbol(RzBinObject *o) { diff --git a/librz/bin/i/private.h b/librz/bin/i/private.h index 761c8ca16a..32c0b803e9 100644 --- a/librz/bin/i/private.h +++ b/librz/bin/i/private.h @@ -44,7 +44,7 @@ RZ_IPI int rz_bin_compare_class_field(RzBinClassField *a, RzBinClassField *b); typedef void (*RzBinProcessLanguage)(RzBinObject *o, const void *user); RZ_IPI void rz_bin_process_rust(RzBinObject *o, char *demangled, ut64 paddr, ut64 vaddr, bool is_method); RZ_IPI void rz_bin_process_cxx(RzBinObject *o, char *demangled, ut64 paddr, ut64 vaddr); -RZ_IPI void rz_bin_process_swift(RzBinObject *o, char *classname, char *demangled, ut64 paddr, ut64 vaddr); +RZ_IPI void rz_bin_process_swift(RzBinObject *o, RzBinSymbol *symbol); RZ_IPI bool rz_bin_object_process_plugin_data(RZ_NONNULL RzBinFile *bf, RZ_NONNULL RzBinObject *o); RZ_IPI const RzDemanglerPlugin *rz_bin_process_get_demangler_plugin_from_lang(RzBin *bin, RzBinLanguage language); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index a939946219..56fa9d9784 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -2280,6 +2280,7 @@ RZ_API void rz_analysis_rtti_msvc_print_class_hierarchy_descriptor(RVTableContex RZ_API void rz_analysis_rtti_msvc_print_base_class_descriptor(RVTableContext *context, ut64 addr, int mode); RZ_API bool rz_analysis_rtti_msvc_print_at_vtable(RVTableContext *context, ut64 addr, RzOutputMode mode, bool strict); RZ_API void rz_analysis_rtti_msvc_recover_all(RVTableContext *vt_context, RzList /**/ *vtables); +RZ_API void rz_analysis_rtti_swift(RzAnalysis *analysis); RZ_API char *rz_analysis_rtti_itanium_demangle_class_name(RVTableContext *context, const char *name); RZ_API bool rz_analysis_rtti_itanium_print_at_vtable(RVTableContext *context, ut64 addr, RzOutputMode mode); diff --git a/librz/include/rz_demangler.h b/librz/include/rz_demangler.h index f69d9c1a5a..c1e70342be 100644 --- a/librz/include/rz_demangler.h +++ b/librz/include/rz_demangler.h @@ -39,6 +39,7 @@ typedef bool (*RzDemanglerIter)(const RzDemanglerPlugin *plugin, RzDemanglerFlag RZ_API RZ_OWN char *rz_demangler_java(RZ_NULLABLE const char *symbol, RzDemanglerFlag flags); RZ_API RZ_OWN char *rz_demangler_cxx(RZ_NONNULL const char *symbol, RzDemanglerFlag flags); +RZ_API RZ_OWN char *rz_demangler_swift(RZ_NONNULL const char *symbol, RzDemanglerFlag flags); RZ_API RZ_OWN char *rz_demangler_objc(RZ_NONNULL const char *symbol, RzDemanglerFlag flags); RZ_API RZ_OWN char *rz_demangler_pascal(RZ_NONNULL const char *symbol, RzDemanglerFlag flags); RZ_API RZ_OWN char *rz_demangler_rust(RZ_NONNULL const char *symbol, RzDemanglerFlag flags); diff --git a/test/db/cmd/cmd_acll b/test/db/cmd/cmd_acll index 4deca7c5ff..38ffdbb843 100644 --- a/test/db/cmd/cmd_acll +++ b/test/db/cmd/cmd_acll @@ -426,4 +426,157 @@ nth addr vt_offset type name 1 0x1000037d8 ---------- DEFAULT __debug_db_insert_c[abi:v160006], std::__1::allocator > > EOF +RUN + +NAME=Analyze classes in swift +FILE=bins/mach0/swift-main +CMDS=< Swift.String +0x100004410 1 Swift.String init(stringInterpolationSegment: Swift.String) -> Swift.String +0x100004416 2 Swift.String init(stringInterpolationSegment: Swift.Int) -> Swift.String +0x10000441c 3 Swift.String init(stringInterpolation: Swift.Array...) -> Swift.String +0x100001380 4 main.Balance width.getter : Swift.Double +0x100001390 5 main.Balance width.setter : Swift.Double +0x1000013a0 6 main.Balance width.materializeForSet : Swift.Double +0x1000013c0 7 main.Balance height.getter : Swift.Double +0x1000013e0 8 main.Balance height.setter : Swift.Double +0x1000013f0 9 main.Balance height.materializeForSet : Swift.Double +0x100001410 10 main.Balance pos.getter : Swift.Double +0x100001430 11 main.Balance pos.setter : Swift.Double +0x100001440 12 main.Balance pos.materializeForSet : Swift.Double +0x100001460 13 main.Balance init(width: Swift.Double, height: Swift.Double, pos: Swift.Double) -> main.Balance +0x100001470 14 main.Balance init() -> main.Balance +0x100001a30 15 main.BarClass sayHello() -> () +0x100001ae0 16 main.BarClass __deallocating_deinit +0x100001b10 17 main.BarClass deinit +0x100001b20 18 main.BarClass init() -> main.BarClass +0x100001b30 19 main.BarClass __allocating_init() -> main.BarClass +0x1000014c0 20 main.FooClass init() -> main.FooClass +0x1000015a0 21 main.FooClass __allocating_init() -> main.FooClass +0x1000015e0 22 main.FooClass sayHello() -> () +0x100001720 23 main.FooClass __deallocating_deinit +0x100001750 24 main.FooClass deinit +0x100001780 25 main.FooClass foo.getter : Swift.Int +0x100001790 26 main.FooClass foo.setter : Swift.Int +0x1000017a0 27 main.FooClass foo.materializeForSet : Swift.Int +0x1000017c0 28 main.FooClass bar.getter : Swift.String +0x100001800 29 main.FooClass bar.setter : Swift.String +0x100001860 30 main.FooClass bar.materializeForSet : Swift.String +0x100002040 31 main.Tost init() -> main.Tost +0x100002080 32 main.Tost __allocating_init() -> main.Tost +0x100002110 33 main.Tost __deallocating_deinit +0x100002140 34 main.Tost deinit +0x100002170 35 main.Tost msg.getter : Swift.String +0x1000021b0 36 main.Tost msg.setter : Swift.String +0x100002210 37 main.Tost msg.materializeForSet : Swift.String +EOF +RUN \ No newline at end of file