From ea702e32c43ed724d1dc24c161a1a0db860d4d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Tue, 19 Sep 2023 21:51:21 +0200 Subject: [PATCH] [WIP] Refactor and handle method overrides --- gum/gumswiftapiresolver.c | 350 +++++++++++++++++++++++--------------- 1 file changed, 209 insertions(+), 141 deletions(-) diff --git a/gum/gumswiftapiresolver.c b/gum/gumswiftapiresolver.c index 928f83427..0949bfb63 100644 --- a/gum/gumswiftapiresolver.c +++ b/gum/gumswiftapiresolver.c @@ -55,6 +55,9 @@ typedef struct _GumModuleMetadata GumModuleMetadata; typedef struct _GumFunctionMetadata GumFunctionMetadata; typedef size_t (* GumSwiftDemangle) (const char * name, char * output, size_t length); + +typedef struct _GumClass GumClass; + typedef guint GumContextDescriptorKind; typedef struct _GumContextDescriptor GumContextDescriptor; typedef struct _GumModuleContextDescriptor GumModuleContextDescriptor; @@ -108,6 +111,17 @@ struct _GumFunctionMetadata GumAddress address; }; +struct _GumClass +{ + gchar * name; + + const GumMethodDescriptor * methods; + guint num_methods; + + const GumMethodOverrideDescriptor * overrides; + guint num_overrides; +}; + enum _GumContextDescriptorKind { GUM_CONTEXT_DESCRIPTOR_MODULE, @@ -254,8 +268,8 @@ struct _GumOverrideTableHeader struct _GumMethodOverrideDescriptor { - GumRelativeDirectPtr class; - GumRelativeDirectPtr method; + GumRelativeIndirectablePtr class; + GumRelativeIndirectablePtr method; GumRelativeDirectPtr impl; }; @@ -272,9 +286,6 @@ static gboolean gum_module_metadata_collect_section ( const GumSectionDetails * details, gpointer user_data); static void gum_module_metadata_collect_class (GumModuleMetadata * self, const GumTypeContextDescriptor * type); -static void gum_module_metadata_collect_method (GumModuleMetadata * self, - const GumMethodDescriptor * method, gint vtable_index, - const GumTypeContextDescriptor * holder); static gboolean gum_module_metadata_collect_export ( const GumExportDetails * details, gpointer user_data); static void gum_module_metadata_maybe_ingest_thunk (GumModuleMetadata * self, @@ -287,6 +298,15 @@ static const gchar * gum_find_character_backwards (const gchar * starting_point, static void gum_function_metadata_free (GumFunctionMetadata * function); +static void gum_class_parse (GumClass * klass, const GumClassDescriptor * cd); +static void gum_class_clear (GumClass * klass); + +static gconstpointer gum_resolve_method_implementation ( + const GumRelativeDirectPtr * impl, const GumMethodDescriptor * method); + +static gchar * gum_compute_context_descriptor_name ( + const GumContextDescriptor * cd); + static void gum_skip_generic_type_trailers (gconstpointer * trailer_ptr, const GumTypeContextDescriptor * t); static void gum_skip_generic_parts (gconstpointer * trailer_ptr, @@ -589,164 +609,79 @@ static void gum_module_metadata_collect_class (GumModuleMetadata * self, const GumTypeContextDescriptor * type) { - const GumClassDescriptor * cd; - gconstpointer trailer; - guint16 type_flags; + GumClass klass; - cd = (const GumClassDescriptor *) type; - trailer = cd + 1; - - gum_skip_generic_type_trailers (&trailer, type); - - gum_skip_metadata_initialization_trailers (&trailer, type); - - type_flags = GUM_DESCRIPTOR_FLAGS_KIND_FLAGS (type->context.flags); + gum_class_parse (&klass, (const GumClassDescriptor *) type); - if (GUM_TYPE_FLAGS_CLASS_HAS_VTABLE (type_flags)) + if (klass.num_methods != 0) { - const GumVTableDescriptorHeader * vth; - const GumMethodDescriptor * methods; - guint32 i; + GPtrArray * vtable; + guint i; - vth = GUM_ALIGN (trailer, GumVTableDescriptorHeader); - methods = GUM_ALIGN ((const GumMethodDescriptor *) (vth + 1), - GumMethodDescriptor); + vtable = g_hash_table_lookup (self->vtables, klass.name); - for (i = 0; i != vth->vtable_size; i++) + for (i = 0; i != klass.num_methods; i++) { - const GumMethodDescriptor * method = &methods[i]; - - gum_module_metadata_collect_method (self, method, i, type); - } - - trailer = methods + vth->vtable_size; - } + const GumMethodDescriptor * method = &klass.methods[i]; + gconstpointer impl; + GumFunctionMetadata func; -#if 1 - if (GUM_TYPE_FLAGS_CLASS_HAS_OVERRIDE_TABLE (type_flags)) - { - g_printerr ("TODO: handle override: %s\n", gum_resolve_relative_direct_ptr (&type->name)); -#if 0 - const GumOverrideTableHeader * oth; - const GumMethodOverrideDescriptor * methods; - guint32 i; + impl = gum_resolve_method_implementation (&method->impl, method); + if (impl == NULL) + continue; - oth = GUM_ALIGN (trailer, GumOverrideTableHeader); - methods = GUM_ALIGN ((const GumMethodOverrideDescriptor *) (oth + 1), - GumMethodOverrideDescriptor); + func.name = NULL; + if (vtable != NULL && i < vtable->len) + func.name = g_strdup (g_ptr_array_index (vtable, i)); + if (func.name == NULL) + func.name = g_strdup_printf ("%s.vtable[%u]", klass.name, i); - for (i = 0; i != oth->num_entries; i++) - { - const GumMethodOverrideDescriptor * method = &methods[i]; + func.address = GUM_ADDRESS (impl); - gum_module_metadata_collect_function (self, - gum_resolve_relative_direct_ptr (&method->impl), -1, 0, type); + g_array_append_val (self->functions, func); } - trailer = methods + oth->num_entries; -#endif - } -#endif -} - -static void -gum_module_metadata_collect_method (GumModuleMetadata * self, - const GumMethodDescriptor * method, - gint vtable_index, - const GumTypeContextDescriptor * holder) -{ - gconstpointer impl; - GString * scope; - GumFunctionMetadata func; - const GumContextDescriptor * cur; - GPtrArray * vtable; - - impl = gum_resolve_relative_direct_ptr (&method->impl); - if (impl == NULL) - return; - - if (GUM_METHOD_DESCRIPTOR_IS_ASYNC (method)) - { - impl = gum_resolve_relative_direct_ptr ( - (const GumRelativeDirectPtr *) impl); - } - - scope = g_string_sized_new (16); - - for (cur = gum_resolve_relative_indirectable_ptr (&holder->context.parent); - cur != NULL; - cur = gum_resolve_relative_indirectable_ptr (&cur->parent)) - { - GumContextDescriptorKind kind = GUM_DESCRIPTOR_FLAGS_KIND (cur->flags); - - switch (kind) + for (i = 0; i != klass.num_overrides; i++) { - case GUM_CONTEXT_DESCRIPTOR_MODULE: - { - const GumModuleContextDescriptor * m = - (const GumModuleContextDescriptor *) cur; - g_string_prepend_c (scope, '.'); - g_string_prepend (scope, gum_resolve_relative_direct_ptr (&m->name)); - break; - } -#if 0 - case GUM_CONTEXT_DESCRIPTOR_EXTENSION: + const GumMethodOverrideDescriptor * od = &klass.overrides[i]; + GumClass parent_class; + const GumMethodDescriptor * parent_method; + guint vtable_index; + GPtrArray * parent_vtable; + GumFunctionMetadata func; + + gum_class_parse (&parent_class, + gum_resolve_relative_indirectable_ptr (&od->class)); + parent_method = gum_resolve_relative_indirectable_ptr (&od->method); + vtable_index = parent_method - parent_class.methods; + + parent_vtable = g_hash_table_lookup (self->vtables, parent_class.name); + + func.name = NULL; + if (parent_vtable != NULL && vtable_index < parent_vtable->len) { - const GumExtensionContextDescriptor * e = - (const GumExtensionContextDescriptor *) cur; - g_string_prepend_c (scope, '.'); - g_string_prepend (scope, - gum_resolve_relative_direct_ptr (&e->extended_context)); - break; - } -#endif - case GUM_CONTEXT_DESCRIPTOR_ANONYMOUS: - break; - default: - if (kind >= GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST && - kind <= GUM_CONTEXT_DESCRIPTOR_TYPE_LAST) + const gchar * name = g_ptr_array_index (parent_vtable, vtable_index); + if (name != NULL) { - const GumTypeContextDescriptor * t = - (const GumTypeContextDescriptor *) cur; - g_string_prepend_c (scope, '.'); - g_string_prepend (scope, gum_resolve_relative_direct_ptr (&t->name)); - break; + func.name = g_strconcat ( + klass.name, + name + strlen (parent_class.name), + NULL); } + } + if (func.name == NULL) + func.name = g_strdup_printf ("%s.overrides[%u]", klass.name, i); - break; - } - } - - g_string_append (scope, gum_resolve_relative_direct_ptr (&holder->name)); + func.address = GUM_ADDRESS ( + gum_resolve_method_implementation (&od->impl, parent_method)); - func.name = NULL; + g_array_append_val (self->functions, func); - if (vtable_index != -1) - { - vtable = g_hash_table_lookup (self->vtables, scope->str); - if (vtable != NULL) - { - if (vtable_index < vtable->len) - func.name = g_strdup (g_ptr_array_index (vtable, vtable_index)); + gum_class_clear (&parent_class); } } - if (func.name == NULL) - { - func.name = g_strdup_printf ( - "%s.%s[%d](%s+0x%" G_GINT64_MODIFIER "x)", - scope->str, - (vtable_index != -1) ? "vtable" : "overrides", - vtable_index, - self->name, - GUM_ADDRESS (impl) - self->base_address); - } - - func.address = GUM_ADDRESS (impl); - - g_array_append_val (self->functions, func); - - g_string_free (scope, TRUE); + gum_class_clear (&klass); } static gboolean @@ -1015,6 +950,139 @@ gum_function_metadata_free (GumFunctionMetadata * function) g_free (function->name); } +static void +gum_class_parse (GumClass * klass, + const GumClassDescriptor * cd) +{ + const GumTypeContextDescriptor * type; + gconstpointer trailer; + guint16 type_flags; + + memset (klass, 0, sizeof (GumClass)); + + type = &cd->type_context; + + klass->name = gum_compute_context_descriptor_name (&type->context); + + trailer = cd + 1; + + gum_skip_generic_type_trailers (&trailer, type); + + gum_skip_metadata_initialization_trailers (&trailer, type); + + type_flags = GUM_DESCRIPTOR_FLAGS_KIND_FLAGS (type->context.flags); + + if (GUM_TYPE_FLAGS_CLASS_HAS_VTABLE (type_flags)) + { + const GumVTableDescriptorHeader * vth; + const GumMethodDescriptor * methods; + + vth = GUM_ALIGN (trailer, GumVTableDescriptorHeader); + methods = GUM_ALIGN ((const GumMethodDescriptor *) (vth + 1), + GumMethodDescriptor); + + klass->methods = methods; + klass->num_methods = vth->vtable_size; + + trailer = methods + vth->vtable_size; + } + + if (GUM_TYPE_FLAGS_CLASS_HAS_OVERRIDE_TABLE (type_flags)) + { + const GumOverrideTableHeader * oth; + const GumMethodOverrideDescriptor * overrides; + + oth = GUM_ALIGN (trailer, GumOverrideTableHeader); + overrides = GUM_ALIGN ((const GumMethodOverrideDescriptor *) (oth + 1), + GumMethodOverrideDescriptor); + + klass->overrides = overrides; + klass->num_overrides = oth->num_entries; + + trailer = overrides + oth->num_entries; + } +} + +static void +gum_class_clear (GumClass * klass) +{ + g_free (klass->name); +} + +static gconstpointer +gum_resolve_method_implementation (const GumRelativeDirectPtr * impl, + const GumMethodDescriptor * method) +{ + gconstpointer address; + + address = gum_resolve_relative_direct_ptr (impl); + if (address == NULL) + return NULL; + + if (GUM_METHOD_DESCRIPTOR_IS_ASYNC (method)) + address = gum_resolve_relative_direct_ptr (address); + + return address; +} + +static gchar * +gum_compute_context_descriptor_name (const GumContextDescriptor * cd) +{ + GString * name; + const GumContextDescriptor * cur; + + name = g_string_sized_new (16); + + for (cur = cd; + cur != NULL; + cur = gum_resolve_relative_indirectable_ptr (&cur->parent)) + { + GumContextDescriptorKind kind = GUM_DESCRIPTOR_FLAGS_KIND (cur->flags); + + switch (kind) + { + case GUM_CONTEXT_DESCRIPTOR_MODULE: + { + const GumModuleContextDescriptor * m = + (const GumModuleContextDescriptor *) cur; + if (name->len != 0) + g_string_prepend_c (name, '.'); + g_string_prepend (name, gum_resolve_relative_direct_ptr (&m->name)); + break; + } +#if 0 + case GUM_CONTEXT_DESCRIPTOR_EXTENSION: + { + const GumExtensionContextDescriptor * e = + (const GumExtensionContextDescriptor *) cur; + if (name->len != 0) + g_string_prepend_c (name, '.'); + g_string_prepend (name, + gum_resolve_relative_direct_ptr (&e->extended_context)); + break; + } +#endif + case GUM_CONTEXT_DESCRIPTOR_ANONYMOUS: + break; + default: + if (kind >= GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST && + kind <= GUM_CONTEXT_DESCRIPTOR_TYPE_LAST) + { + const GumTypeContextDescriptor * t = + (const GumTypeContextDescriptor *) cur; + if (name->len != 0) + g_string_prepend_c (name, '.'); + g_string_prepend (name, gum_resolve_relative_direct_ptr (&t->name)); + break; + } + + break; + } + } + + return g_string_free (name, FALSE); +} + static void gum_skip_generic_type_trailers (gconstpointer * trailer_ptr, const GumTypeContextDescriptor * t)