Skip to content

Commit

Permalink
elf-module: Use MiniDebugInfo if no symbols found
Browse files Browse the repository at this point in the history
When enumerate_symbols() encounters an ELF with no symbols in memory,
instantiate an offline ElfModule instance and parse the .gnu_debugdata
section. Decompress the embedded ELF and reuse its symbols as a fallback.
This ensures symbol information is available even if the primary ELF
lacks them.
  • Loading branch information
oleavr committed Dec 14, 2024
1 parent 8f77e06 commit 8ed32c4
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 6 deletions.
119 changes: 117 additions & 2 deletions gum/gumelfmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include "gumelfmodule.h"

#include "gumelfmodule-priv.h"
#ifdef HAVE_LZMA
# include <lzma.h>
#endif
#ifdef HAVE_ANDROID
# include "gum/gumandroid.h"
# ifdef HAVE_MINIZIP
Expand Down Expand Up @@ -95,6 +98,9 @@ struct _GumElfModule
guint64 mapped_size;
GumElfDynamicAddressState dynamic_address_state;
const gchar * dynamic_strings;

GumElfModule * fallback_elf_module;
gboolean tried_fallback_load;
};

enum _GumElfDynamicAddressState
Expand Down Expand Up @@ -155,6 +161,7 @@ struct _GumElfEnumerateDepsContext
GumElfModule * module;
};

static void gum_elf_module_dispose (GObject * object);
static void gum_elf_module_finalize (GObject * object);
static void gum_elf_module_get_property (GObject * object,
guint property_id, GValue * value, GParamSpec * pspec);
Expand All @@ -175,6 +182,10 @@ static gboolean gum_elf_module_load_dynamic_entries (GumElfModule * self,
static gconstpointer gum_elf_module_get_live_data (GumElfModule * self,
gsize * size);
static void gum_elf_module_unload (GumElfModule * self);
static GumElfModule * gum_elf_module_try_get_fallback_elf_module (
GumElfModule * self);
static gboolean gum_try_load_debugdata_as_fallback_module (
const GumElfSectionDetails * details, gpointer user_data);
static gboolean gum_elf_module_emit_relocations (GumElfModule * self,
const GumElfRelocationGroup * g, GumFoundElfRelocationFunc func,
gpointer user_data);
Expand Down Expand Up @@ -238,6 +249,8 @@ static gint64 gum_elf_module_read_int64 (GumElfModule * self, const gint64 * v);
static guint64 gum_elf_module_read_uint64 (GumElfModule * self,
const guint64 * v);

static GBytes * gum_decompress_xz (gconstpointer data, gsize size);

static gboolean gum_maybe_extract_from_apk (const gchar * path,
GBytes ** file_bytes);

Expand All @@ -248,6 +261,7 @@ gum_elf_module_class_init (GumElfModuleClass * klass)
{
GObjectClass * object_class = G_OBJECT_CLASS (klass);

object_class->dispose = gum_elf_module_dispose;
object_class->finalize = gum_elf_module_finalize;
object_class->get_property = gum_elf_module_get_property;
object_class->set_property = gum_elf_module_set_property;
Expand Down Expand Up @@ -324,12 +338,22 @@ gum_elf_module_init (GumElfModule * self)
}

static void
gum_elf_module_finalize (GObject * object)
gum_elf_module_dispose (GObject * object)
{
GumElfModule * self = GUM_ELF_MODULE (object);

g_clear_object (&self->fallback_elf_module);

gum_elf_module_unload (self);

G_OBJECT_CLASS (gum_elf_module_parent_class)->dispose (object);
}

static void
gum_elf_module_finalize (GObject * object)
{
GumElfModule * self = GUM_ELF_MODULE (object);

g_array_unref (self->sections);

g_array_unref (self->dyns);
Expand Down Expand Up @@ -1039,6 +1063,51 @@ gum_elf_module_unload (GumElfModule * self)
self->file_size = 0;
}

static GumElfModule *
gum_elf_module_try_get_fallback_elf_module (GumElfModule * self)
{
if (self->tried_fallback_load)
return self->fallback_elf_module;

self->tried_fallback_load = TRUE;

if (self->source_mode == GUM_ELF_SOURCE_MODE_ONLINE)
{
self->fallback_elf_module =
gum_elf_module_new_from_file (self->source_path, NULL);
}
else
{
gum_elf_module_enumerate_sections (self,
gum_try_load_debugdata_as_fallback_module, self);
}

return self->fallback_elf_module;
}

static gboolean
gum_try_load_debugdata_as_fallback_module (const GumElfSectionDetails * details,
gpointer user_data)
{
GumElfModule * self = user_data;
GBytes * debugdata;

if (strcmp (details->name, ".gnu_debugdata") != 0)
return TRUE;

debugdata = gum_decompress_xz (
(const guint8 *) gum_elf_module_get_file_data (self, NULL) +
details->offset,
details->size);
if (debugdata != NULL)
{
self->fallback_elf_module = gum_elf_module_new_from_blob (debugdata, NULL);
g_bytes_unref (debugdata);
}

return FALSE;
}

GumElfType
gum_elf_module_get_etype (GumElfModule * self)
{
Expand Down Expand Up @@ -1900,7 +1969,7 @@ gum_elf_module_enumerate_symbols_in_section (GumElfModule * self,

shdr = gum_elf_module_find_section_header_by_type (self, section);
if (shdr == NULL)
return;
goto consider_fallback;

strings_shdr =
gum_elf_module_find_section_header_by_index (self, shdr->link);
Expand Down Expand Up @@ -1939,6 +2008,16 @@ gum_elf_module_enumerate_symbols_in_section (GumElfModule * self,

propagate_error:
return;

consider_fallback:
{
GumElfModule * fallback = gum_elf_module_try_get_fallback_elf_module (self);
if (fallback != NULL)
{
gum_elf_module_enumerate_symbols_in_section (fallback, section, func,
user_data);
}
}
}

void
Expand Down Expand Up @@ -2368,6 +2447,42 @@ gum_elf_module_read_uint64 (GumElfModule * self,
: GUINT64_FROM_BE (*v);
}

static GBytes *
gum_decompress_xz (gconstpointer data,
gsize size)
{
gpointer out_data;
gsize out_capacity;
size_t in_pos, out_pos;

out_capacity = 4 * size;
out_data = g_malloc (out_capacity);

in_pos = 0;
out_pos = 0;

while (TRUE)
{
uint64_t memlimit = UINT64_MAX;
uint32_t flags = 0;

switch (lzma_stream_buffer_decode (&memlimit, flags, NULL, data, &in_pos,
size, out_data, &out_pos, out_capacity))
{
case LZMA_OK:
out_data = g_realloc (out_data, out_pos);
return g_bytes_new_take (out_data, out_pos);
case LZMA_BUF_ERROR:
out_capacity *= 2;
out_data = g_realloc (out_data, out_capacity);
break;
default:
g_free (out_data);
return NULL;
}
}
}

static gboolean
gum_maybe_extract_from_apk (const gchar * path,
GBytes ** file_bytes)
Expand Down
7 changes: 3 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,9 @@ capstone_options = [
]
capstone_dep = dependency('capstone', version: '>=5.0.0', default_options: capstone_options)

if build_tests
lzma_dep = dependency('liblzma')
else
lzma_dep = disabler()
lzma_dep = dependency('liblzma', required: build_tests)
if lzma_dep.found()
cdata.set('HAVE_LZMA', 1)
endif

if glib_dep.type_name() == 'internal'
Expand Down

0 comments on commit 8ed32c4

Please sign in to comment.