guint8 * gum_kernel_read (GumAddress address, gsize len, gsize * n_bytes_read) { mach_port_t task; task = gum_kernel_get_task (); if (task == MACH_PORT_NULL) return NULL; return gum_darwin_read (task, address, len, n_bytes_read); }
static gboolean gum_emit_modules_in_range (const GumMemoryRange * range, GumEnumerateModulesSlowContext * ctx) { GumAddress address = range->base_address; gsize remaining = range->size; gboolean carry_on = TRUE; do { struct mach_header * header; gboolean is_dylib; guint8 * chunk; gsize chunk_size; guint8 * first_command, * p; guint cmd_index; GumMemoryRange dylib_range; header = (struct mach_header *) gum_darwin_read (ctx->task, address, sizeof (struct mach_header), NULL); if (header == NULL) return TRUE; is_dylib = (header->magic == MH_MAGIC || header->magic == MH_MAGIC_64) && header->filetype == MH_DYLIB; g_free (header); if (!is_dylib) { address += ctx->alignment; remaining -= ctx->alignment; continue; } chunk = gum_darwin_read (ctx->task, address, MIN (MAX_MACH_HEADER_SIZE, remaining), &chunk_size); if (chunk == NULL) return TRUE; header = (struct mach_header *) chunk; if (header->magic == MH_MAGIC) first_command = chunk + sizeof (struct mach_header); else first_command = chunk + sizeof (struct mach_header_64); dylib_range.base_address = address; dylib_range.size = ctx->alignment; p = first_command; for (cmd_index = 0; cmd_index != header->ncmds; cmd_index++) { const struct load_command * lc = (struct load_command *) p; if (lc->cmd == GUM_LC_SEGMENT) { gum_segment_command_t * sc = (gum_segment_command_t *) lc; if (strcmp (sc->segname, "__TEXT") == 0) { dylib_range.size = sc->vmsize; break; } } p += lc->cmdsize; } p = first_command; for (cmd_index = 0; cmd_index != header->ncmds; cmd_index++) { const struct load_command * lc = (struct load_command *) p; if (lc->cmd == LC_ID_DYLIB) { const struct dylib * dl = &((struct dylib_command *) lc)->dylib; const gchar * raw_path; guint raw_path_len; gchar * path, * name; GumModuleDetails details; raw_path = (gchar *) p + dl->name.offset; raw_path_len = lc->cmdsize - sizeof (struct dylib_command); path = g_malloc (raw_path_len + 1); memcpy (path, raw_path, raw_path_len); path[raw_path_len] = '\0'; name = g_path_get_basename (path); details.name = name; details.range = &dylib_range; details.path = path; carry_on = ctx->func (&details, ctx->user_data); g_free (name); g_free (path); break; } p += lc->cmdsize; } g_free (chunk); address += dylib_range.size; remaining -= dylib_range.size; if (!carry_on) break; } while (remaining != 0); return carry_on; }
void gum_darwin_enumerate_modules (mach_port_t task, GumFoundModuleFunc func, gpointer user_data) { struct task_dyld_info info; mach_msg_type_number_t count; kern_return_t kr; gsize info_array_count, info_array_size, i; GumAddress info_array_address; gpointer info_array = NULL; gpointer header_data = NULL; gchar * file_path = NULL; gboolean carry_on = TRUE; #if defined (HAVE_ARM) || defined (HAVE_ARM64) DyldInfo info_raw; count = DYLD_INFO_COUNT; kr = task_info (task, TASK_DYLD_INFO, (task_info_t) &info_raw, &count); if (kr != KERN_SUCCESS) goto beach; switch (count) { case DYLD_INFO_LEGACY_COUNT: info.all_image_info_addr = info_raw.info_legacy.all_image_info_addr; info.all_image_info_size = 0; info.all_image_info_format = TASK_DYLD_ALL_IMAGE_INFO_32; break; case DYLD_INFO_32_COUNT: info.all_image_info_addr = info_raw.info_32.all_image_info_addr; info.all_image_info_size = info_raw.info_32.all_image_info_size; info.all_image_info_format = info_raw.info_32.all_image_info_format; break; case DYLD_INFO_64_COUNT: info.all_image_info_addr = info_raw.info_64.all_image_info_addr; info.all_image_info_size = info_raw.info_64.all_image_info_size; info.all_image_info_format = info_raw.info_64.all_image_info_format; break; default: g_assert_not_reached (); } #else count = TASK_DYLD_INFO_COUNT; kr = task_info (task, TASK_DYLD_INFO, (task_info_t) &info, &count); if (kr != KERN_SUCCESS) goto beach; #endif if (info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_64) { DyldAllImageInfos64 * all_info; all_info = (DyldAllImageInfos64 *) gum_darwin_read (task, info.all_image_info_addr, sizeof (DyldAllImageInfos64), NULL); if (all_info == NULL) goto beach; info_array_count = all_info->info_array_count; info_array_size = info_array_count * DYLD_IMAGE_INFO_64_SIZE; info_array_address = all_info->info_array; g_free (all_info); } else { DyldAllImageInfos32 * all_info; all_info = (DyldAllImageInfos32 *) gum_darwin_read (task, info.all_image_info_addr, sizeof (DyldAllImageInfos32), NULL); if (all_info == NULL) goto beach; info_array_count = all_info->info_array_count; info_array_size = info_array_count * DYLD_IMAGE_INFO_32_SIZE; info_array_address = all_info->info_array; g_free (all_info); } if (info_array_address == 0) goto fallback; info_array = gum_darwin_read (task, info_array_address, info_array_size, NULL); for (i = 0; i != info_array_count && carry_on; i++) { GumAddress load_address, file_path_address; struct mach_header * header; guint8 * first_command, * p; guint cmd_index; GumMemoryRange dylib_range; gchar * name; GumModuleDetails details; if (info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_64) { DyldImageInfo64 * info = info_array + (i * DYLD_IMAGE_INFO_64_SIZE); load_address = info->image_load_address; file_path_address = info->image_file_path; } else { DyldImageInfo32 * info = info_array + (i * DYLD_IMAGE_INFO_32_SIZE); load_address = info->image_load_address; file_path_address = info->image_file_path; } header_data = gum_darwin_read (task, load_address, MAX_MACH_HEADER_SIZE, NULL); file_path = (gchar *) gum_darwin_read (task, file_path_address, 2 * MAXPATHLEN, NULL); if (header_data == NULL || file_path == NULL) goto beach; header = (struct mach_header *) header_data; if (info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_64) first_command = header_data + sizeof (struct mach_header_64); else first_command = header_data + sizeof (struct mach_header); dylib_range.base_address = load_address; dylib_range.size = 4096; p = first_command; for (cmd_index = 0; cmd_index != header->ncmds; cmd_index++) { const struct load_command * lc = (struct load_command *) p; if (lc->cmd == GUM_LC_SEGMENT) { gum_segment_command_t * sc = (gum_segment_command_t *) lc; if (strcmp (sc->segname, "__TEXT") == 0) { dylib_range.size = sc->vmsize; break; } } p += lc->cmdsize; } name = g_path_get_basename (file_path); details.name = name; details.range = &dylib_range; details.path = file_path; carry_on = func (&details, user_data); g_free (name); g_free (file_path); file_path = NULL; g_free (header_data); header_data = NULL; } goto beach; fallback: gum_darwin_enumerate_modules_slow (task, func, user_data); beach: g_free (file_path); g_free (header_data); g_free (info_array); return; }
static gboolean gum_probe_range_for_entrypoint (const GumRangeDetails * details, gpointer user_data) { const GumMemoryRange * range = details->range; GumFindEntrypointContext * ctx = user_data; gboolean carry_on = TRUE; guint8 * chunk, * page, * p; gsize chunk_size; chunk = gum_darwin_read (ctx->task, range->base_address, range->size, &chunk_size); if (chunk == NULL) return TRUE; g_assert (chunk_size % ctx->alignment == 0); for (page = chunk; page != chunk + chunk_size; page += ctx->alignment) { struct mach_header * header; gint64 slide; guint cmd_index; GumAddress text_base = 0, text_offset = 0; header = (struct mach_header *) page; if (header->magic != MH_MAGIC && header->magic != MH_MAGIC_64) continue; if (header->filetype != MH_EXECUTE) continue; if (!gum_darwin_find_slide (range->base_address + (page - chunk), page, chunk_size - (page - chunk), &slide)) { continue; } carry_on = FALSE; if (header->magic == MH_MAGIC) p = page + sizeof (struct mach_header); else p = page + sizeof (struct mach_header_64); for (cmd_index = 0; cmd_index != header->ncmds; cmd_index++) { const struct load_command * lc = (struct load_command *) p; switch (lc->cmd) { case LC_SEGMENT: { struct segment_command * sc = (struct segment_command *) lc; if (strcmp (sc->segname, "__TEXT") == 0) text_base = sc->vmaddr + slide; break; } case LC_SEGMENT_64: { struct segment_command_64 * sc = (struct segment_command_64 *) lc; if (strcmp (sc->segname, "__TEXT") == 0) text_base = sc->vmaddr + slide; break; } #ifdef HAVE_I386 case LC_UNIXTHREAD: { guint8 * thread = p + sizeof (struct thread_command); while (thread != p + lc->cmdsize) { thread_state_flavor_t * flavor = (thread_state_flavor_t *) thread; mach_msg_type_number_t * count = (mach_msg_type_number_t *) (flavor + 1); if (header->magic == MH_MAGIC && *flavor == x86_THREAD_STATE32) { x86_thread_state32_t * ts = (x86_thread_state32_t *) (count + 1); ctx->result = ts->__eip + slide; } else if (header->magic == MH_MAGIC_64 && *flavor == x86_THREAD_STATE64) { x86_thread_state64_t * ts = (x86_thread_state64_t *) (count + 1); ctx->result = ts->__rip + slide; } thread = ((guint8 *) (count + 1)) + (*count * sizeof (int)); } break; } #endif case LC_MAIN: { struct entry_point_command * ec = (struct entry_point_command *) p; text_offset = ec->entryoff; break; } } p += lc->cmdsize; } if (ctx->result == 0) ctx->result = text_base + text_offset; if (!carry_on) break; } g_free (chunk); return carry_on; }
static gboolean gum_do_enumerate_exports (GumEnumerateExportsContext * ctx, const gchar * module_name) { gboolean carry_on = TRUE; GVariant * address_value; GumAddress address; guint8 * chunk = NULL; gsize chunk_size; struct mach_header * header; gint64 slide; GumAddress linkedit; GSList * text_section_ids = NULL; struct symtab_command * sc; gsize symbol_size; guint8 * symbols = NULL; GumAddress strings_address; gchar * strings; guint8 * cur_sym; guint symbol_index; address_value = g_hash_table_lookup (ctx->modules, module_name); if (address_value == NULL) goto beach; address = g_variant_get_uint64 (address_value); if (address == 0) goto beach; chunk = gum_darwin_read (ctx->task, address, MAX_MACH_HEADER_SIZE, &chunk_size); if (chunk == NULL) goto beach; header = (struct mach_header *) chunk; if (!gum_darwin_find_slide (address, chunk, chunk_size, &slide)) goto beach; if (!gum_darwin_find_linkedit (chunk, chunk_size, &linkedit)) goto beach; linkedit += slide; text_section_ids = gum_darwin_find_text_section_ids (chunk, chunk_size); if (!gum_darwin_find_symtab_command (chunk, chunk_size, &sc)) goto beach; if (header->magic == MH_MAGIC) symbol_size = sizeof (struct nlist); else symbol_size = sizeof (struct nlist_64); symbols = gum_darwin_read (ctx->task, linkedit + sc->symoff, sc->nsyms * symbol_size, NULL); if (symbols == NULL) goto beach; strings_address = linkedit + sc->stroff; strings = g_hash_table_lookup (ctx->strings, GSIZE_TO_POINTER (strings_address)); if (strings == NULL) { strings = (gchar *) gum_darwin_read (ctx->task, strings_address, sc->strsize, NULL); if (strings == NULL) goto beach; g_hash_table_insert (ctx->strings, GSIZE_TO_POINTER (strings_address), strings); } cur_sym = symbols; for (symbol_index = 0; symbol_index != sc->nsyms; symbol_index++) { GumExportDetails details; details.name = NULL; if (header->magic == MH_MAGIC) { struct nlist * sym = (struct nlist *) cur_sym; if (!SYMBOL_IS_UNDEFINED_DEBUG_OR_LOCAL (sym)) { details.type = g_slist_find (text_section_ids, GSIZE_TO_POINTER (sym->n_sect)) != NULL ? GUM_EXPORT_FUNCTION : GUM_EXPORT_VARIABLE; details.name = gum_symbol_name_from_darwin (strings + sym->n_un.n_strx); details.address = sym->n_value + slide; if ((sym->n_desc & N_ARM_THUMB_DEF) != 0) details.address++; } } else { struct nlist_64 * sym = (struct nlist_64 *) cur_sym; if (!SYMBOL_IS_UNDEFINED_DEBUG_OR_LOCAL (sym)) { details.type = g_slist_find (text_section_ids, GSIZE_TO_POINTER (sym->n_sect)) != NULL ? GUM_EXPORT_FUNCTION : GUM_EXPORT_VARIABLE; details.name = gum_symbol_name_from_darwin (strings + sym->n_un.n_strx); details.address = sym->n_value + slide; if ((sym->n_desc & N_ARM_THUMB_DEF) != 0) details.address++; } } if (details.name != NULL) { if (!ctx->func (&details, ctx->user_data)) { carry_on = FALSE; break; } } cur_sym += symbol_size; } if (carry_on) { guint8 * cur_cmd; guint cmd_index; if (header->magic == MH_MAGIC) cur_cmd = chunk + sizeof (struct mach_header); else cur_cmd = chunk + sizeof (struct mach_header_64); for (cmd_index = 0; cmd_index != header->ncmds; cmd_index++) { struct load_command * lc = (struct load_command *) cur_cmd; if (lc->cmd == LC_REEXPORT_DYLIB) { struct dylib_command * dc = (struct dylib_command *) lc; const char * name = (const char *) (((guint8 *) dc) + dc->dylib.name.offset); if (!gum_do_enumerate_exports (ctx, name)) { carry_on = FALSE; break; } } cur_cmd += lc->cmdsize; } } beach: g_free (symbols); g_slist_free (text_section_ids); g_free (chunk); return carry_on; }