/* Chrysalide - Outil d'analyse de fichiers binaires * symbols.c - gestion des symboles d'un ELF * * Copyright (C) 2008-2013 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "symbols.h" #include #include #include #include #include "dynamic.h" #include "elf-int.h" #include "helper_arm.h" #include "helper_x86.h" #include "loading.h" #include "program.h" #include "section.h" #include "../mangling/demangler.h" #include "../../arch/raw.h" #include "../../common/extstr.h" #include "../../core/params.h" #include "../../gui/panels/log.h" /* Enregistre un point d'entrée au sein d'un binaire ELF. */ static void register_elf_entry_point(GElfFormat *, virt_t, phys_t, GBinRoutine *); /* Enumère tous les points d'entrée principaux d'un binaire ELF. */ static bool load_all_elf_basic_entry_points(GElfFormat *); /* -------------------------- DETAIL DES SYMBOLES INTERNES -------------------------- */ /* Assure le chargement des symboles internes ELF en différé. */ static bool do_elf_internal_symbol_loading(GElfLoading *, GElfFormat *, phys_t *); /* Charge tous les symboles internes possibles. */ static bool load_elf_internal_symbols(GElfFormat *, wgroup_id_t, GtkStatusStack *); /* -------------------------- DETAIL DES SYMBOLES EXTERNES -------------------------- */ /* Retrouve un élément donné dans la section dynamique. */ static bool find_elf_dynamic_item(const GElfFormat *, const elf_shdr *, int32_t, elf_dyn *); /* Charge tous les éléments dynamiques externes possibles. */ static bool load_elf_external_symbols(GElfFormat *, const elf_shdr *); /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * status = barre de statut à tenir informée. * * * * Description : Charge en mémoire la liste humaine des symboles. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool load_elf_symbols(GElfFormat *format, GtkStatusStack *status) { bool result; /* Bilan à retourner */ wgroup_id_t gid; /* Identifiant pour les tâches */ elf_shdr *sections; /* Groupe de sections trouvées */ size_t count; /* Quantité de données */ result = true; gid = g_work_queue_define_work_group(get_work_queue()); /* Symboles internes */ result &= load_elf_internal_symbols(format, gid, status); /* Symboles externes */ #if 1 if (find_elf_sections_by_type(format, SHT_DYNAMIC, §ions, &count)) { log_variadic_message(LMT_INFO, _("Binary is dynamically linked")); result &= load_elf_external_symbols(format, §ions[0]); free(sections); } else log_variadic_message(LMT_INFO, _("Binary is statically linked")); #endif /* Symboles internes */ //result &= load_elf_internal_symbols(format); /* Symboles d'entrée, si encore besoin */ /** * Le tri en préalable */ result &= load_all_elf_basic_entry_points(format); return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * vaddr = adresse virtuelle du symbole à insérer. * * len = taille de la routine à ajouter. * * routine = représentation de la fonction repérée. * * * * Description : Enregistre un point d'entrée au sein d'un binaire ELF. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void register_elf_entry_point(GElfFormat *format, virt_t vaddr, phys_t len, GBinRoutine *routine) { GBinFormat *base; /* Version basique du format */ virt_t final_vaddr; /* Adresse virtuelle retenue */ bool status; /* Bilan d'une opération */ vmpa2t addr; /* Localisation d'une routine */ mrange_t range; /* Couverture mémoire associée */ GBinSymbol *symbol; /* Nouveau symbole construit */ /* Localisation complète du symbole */ if (ELF_HDR(format, format->header, e_machine) == EM_ARM) final_vaddr = vaddr & ~0x1; else final_vaddr = vaddr; status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), final_vaddr, &addr); if (!status) return; /* Comptabilisation en tant que symbole */ if (g_binary_format_find_symbol_at(G_BIN_FORMAT(format), &addr, &symbol)) { g_object_unref(G_OBJECT(routine)); routine = g_binary_symbol_get_routine(symbol); g_object_ref(G_OBJECT(routine)); _g_binary_symbol_attach_routine(symbol, routine, STP_ENTRY_POINT); } else { base = G_BIN_FORMAT(format); init_mrange(&range, &addr, len); g_binary_routine_set_range(routine, &range); symbol = g_binary_symbol_new(STP_ENTRY_POINT); g_binary_symbol_attach_routine(symbol, routine); g_binary_format_add_symbol(base, symbol); /* Comptabilisation pour le désassemblage brut */ g_binary_format_register_code_point(base, vaddr, true); } } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * * * Description : Enumère tous les points d'entrée principaux d'un binaire ELF.* * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_all_elf_basic_entry_points(GElfFormat *format) { virt_t ep; /* Point d'entrée détecté */ GBinRoutine *routine; /* Routine à associer à un pt. */ elf_phdr dynamic; /* En-tête de programme DYNAMIC*/ elf_dyn item_a; /* Premier élément DYNAMIC */ elf_dyn item_b; /* Second élément DYNAMIC */ const GBinContent *content; /* Contenu binaire à lire */ phys_t length; /* Taille totale du contenu */ bool status; /* Bilan d'une opération */ vmpa2t pos; /* Tête de lecture courante */ uint32_t virt_32; /* Adresse virtuelle sur 32b */ uint64_t virt_64; /* Adresse virtuelle sur 64b */ /* Point d'entrée principal éventuel */ ep = ELF_HDR(format, format->header, e_entry); if (ep != 0x0) { routine = try_to_demangle_routine("entry_point"); register_elf_entry_point(format, ep, 0, routine); } /* Chargemet de l'en-tête de programme DYNAMIC */ if (!find_elf_dynamic_program_header(format, &dynamic)) goto laebep_exit; /* Détection des constructeurs & destructeurs */ if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_INIT, &item_a)) { ep = ELF_DYN(format, item_a, d_un.d_ptr); if (ep != 0x0) { routine = try_to_demangle_routine("init_function"); register_elf_entry_point(format, ep, 0, routine); } } if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_FINI, &item_a)) { ep = ELF_DYN(format, item_a, d_un.d_ptr); if (ep != 0x0) { routine = try_to_demangle_routine("termination_function"); register_elf_entry_point(format, ep, 0, routine); } } void load_entry_points_from_array(GElfFormat *fmt, const elf_dyn *ar, const elf_dyn *sz, const char *prefix) { unsigned int i; /* Boucle de parcours */ char fullname[64]; /* Désignation humaine */ assert(sizeof(fullname) >= (strlen(prefix) + sizeof(XSTR(UINT64_MAX) + 1))); content = G_BIN_FORMAT(fmt)->content; status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), ELF_DYN(fmt, *ar, d_un.d_val), &pos); if (!status) return; length = get_phy_addr(&pos) + ELF_DYN(fmt, *sz, d_un.d_val); for (i = 0; get_phy_addr(&pos) < length; i++) { /** * Selon la libc d'Android (https://www.codeaurora.org/.../android/bionic/linker/README.TXT) : * * DT_INIT_ARRAY * Points to an array of function addresses that must be * called, in-order, to perform initialization. Some of * the entries in the array can be 0 or -1, and should * be ignored. * * On étend le principe aux sections DT_FINI_ARRAY et DT_PREINIT_ARRAY. */ if (fmt->is_32b) { status = g_binary_content_read_u32(content, &pos, fmt->endian, &virt_32); status &= (virt_32 != 0x0 && virt_32 != 0xffffffff); ep = virt_32; } else { status = g_binary_content_read_u64(content, &pos, fmt->endian, &virt_64); status &= (virt_64 != 0x0 && virt_64 != 0xffffffffffffffff); ep = virt_64; } if (!status) break; snprintf(fullname, sizeof(fullname), "%s%u", prefix, i); routine = try_to_demangle_routine(fullname); register_elf_entry_point(fmt, ep, 0, routine); } } if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_INIT_ARRAY, &item_a)) { if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_INIT_ARRAYSZ, &item_b)) { load_entry_points_from_array(format, &item_a, &item_b, "init_array_function_"); } } if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_FINI_ARRAY, &item_a)) { if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_FINI_ARRAYSZ, &item_b)) { load_entry_points_from_array(format, &item_a, &item_b, "fini_array_function_"); } } if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_PREINIT_ARRAY, &item_a)) { if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_PREINIT_ARRAYSZ, &item_b)) { load_entry_points_from_array(format, &item_a, &item_b, "preinit_array_function_"); } } /* Identification de l'entrée de la PLT */ if (find_elf_dynamic_item_from_pheader(format, &dynamic, DT_PLTGOT, &item_a)) { status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), ELF_DYN(format, item_a, d_un.d_val), &pos); if (status) { content = G_BIN_FORMAT(format)->content; /* On saute le premier élément... */ if (format->is_32b) status = g_binary_content_read_u32(content, &pos, format->endian, &virt_32); else status = g_binary_content_read_u64(content, &pos, format->endian, &virt_64); while (1) { if (format->is_32b) { status = g_binary_content_read_u32(content, &pos, format->endian, &virt_32); ep = virt_32; } else { status = g_binary_content_read_u64(content, &pos, format->endian, &virt_64); ep = virt_64; } if (!status) break; if (ep != 0x0) { routine = try_to_demangle_routine("plt_entry"); register_elf_entry_point(format, ep, 0, routine); break; } } } } laebep_exit: return true; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * sym = section comprenant les symboles à venir lire. * * index = indice de l'entrée à venir lire. * * symbol = ensemble d'informations lues. [OUT] * * * * Description : Récupère la définition complète d'un symbole donné. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool get_elf_symbol_by_index(GElfFormat *format, const elf_shdr *sym, off_t index, elf_sym *symbol) { phys_t sym_start; /* Début de section */ phys_t sym_size; /* Taille de section */ phys_t offset; /* Emplacement à venir lire */ get_elf_section_content(format, sym, &sym_start, &sym_size, NULL); offset = sym_start + index * ELF_SIZEOF_SYM(format); if ((offset + ELF_SIZEOF_SYM(format)) > (sym_start + sym_size)) return NULL; return read_elf_symbol(format, &offset, symbol); } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * sym = section comprenant les symboles à venir lire. * * str = section de chaînes de caractères pour les noms. * * index = indice de l'entrée à venir lire. * * * * Description : Récupère la désignation d'un symbole donné. * * * * Retour : Nom du symbole trouvé, ou NULL si erreur ou non adapté. * * * * Remarques : - * * * ******************************************************************************/ const char *get_elf_symbol_name(GElfFormat *format, const elf_shdr *sym, const elf_shdr *str, off_t index) { const char *result; /* Résultat à retourner */ elf_sym symbol; /* Symbole aux infos visées */ result = NULL; if (get_elf_symbol_by_index(format, sym, index, &symbol)) result = extract_name_from_elf_string_section(format, str, ELF_SYM(format, symbol, st_name)); return result; } /* ---------------------------------------------------------------------------------- */ /* DETAIL DES SYMBOLES INTERNES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : loading = chargement de symboles internes en cours. * * format = format ELF à compléter. * * iter = tête de lecture évoluant avec le temps. [OUT] * * * * Description : Assure le chargement des symboles internes ELF en différé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static bool do_elf_internal_symbol_loading(GElfLoading *loading, GElfFormat *format, phys_t *iter) { bool result; /* Bilan à retourner */ elf_sym sym; /* Symbole aux infos visées */ virt_t virt; /* Adresse virtuelle */ const elf_shdr *section; /* Groupe de sections trouvées */ bool use_virt; /* Choix de construction de nom*/ const elf_shdr *strtab; /* Section .strtab trouvée */ bool has_strtab; /* Présence de cette section */ phys_t first; /* Position du premier élément */ const char *name; /* Nom du symbole trouvé */ GBinFormat *base; /* Version basique du format */ vmpa2t addr; /* Localisation d'une routine */ GBinSymbol *symbol; /* Nouveau symbole construit */ char alt_name[6 + VMPA_MAX_LEN]; /* Nom abstrait de substitution*/ virt_t final_virt; /* Adresse virtuelle retenue */ mrange_t range; /* Couverture mémoire associée */ GBinRoutine *routine; /* Nouvelle routine trouvée */ result = read_elf_symbol(format, iter, &sym); if (!result) goto geslp_done; /* On rejette les symboles qui ne sont pas définis au sein du binaire */ if (ELF_SYM(format, sym, st_shndx) == 0) goto geslp_done; /* Résolution précise d'adresse */ virt = ELF_SYM(format, sym, st_value); if (virt == 0) goto geslp_done; /* TODO */ //init_vmpa(&addr, VMPA_NO_PHYSICAL, ELF_SYM(format, sym, st_value)); //init_mrange(&range, &addr, 0); /* Première ébauche de nom */ g_elf_loading_get_internal_info(loading, §ion, &use_virt, &strtab, &has_strtab, &first); if (!has_strtab) name = NULL; else name = get_elf_symbol_name(format, section, strtab, ((*iter - first) / ELF_SIZEOF_SYM(format)) - 1); /* Traitements particuliers */ base = G_BIN_FORMAT(format); switch (ELF_ST_TYPE(format, sym)) { case STT_OBJECT: /* Ajustement de la position */ if (!g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), virt, &addr)) { symbol = NULL; break; } /* Création d'un nom unique ? */ if (name == NULL) { strcpy(alt_name, "obj_"); if (use_virt) vmpa2_virt_to_string(&addr, MDS_UNDEFINED, alt_name + 4, NULL); else vmpa2_phys_to_string(&addr, MDS_UNDEFINED, alt_name + 4, NULL); name = alt_name; } /* TODO */ symbol = NULL; break; case STT_FUNC: /* Ajustement de la position */ if (ELF_HDR(format, format->header, e_machine) == EM_ARM) final_virt = virt & ~0x1; else final_virt = virt; if (!g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), final_virt, &addr)) { symbol = NULL; break; } init_mrange(&range, &addr, ELF_SYM(format, sym, st_size)); /* Création d'un nom unique ? */ if (name == NULL) { strcpy(alt_name, "func_"); if (use_virt) vmpa2_virt_to_string(&addr, MDS_UNDEFINED, alt_name + 5, NULL); else vmpa2_phys_to_string(&addr, MDS_UNDEFINED, alt_name + 5, NULL); name = alt_name; } /* Routine */ routine = try_to_demangle_routine(name); g_binary_routine_set_range(routine, &range); /* Symbole uniquement */ symbol = g_binary_symbol_new(STP_ROUTINE); g_binary_symbol_attach_routine(symbol, routine); /* Comptabilisation pour le désassemblage brut */ g_binary_format_register_code_point(G_BIN_FORMAT(format), virt, false); break; default: symbol = NULL; break; } if (symbol != NULL) g_binary_format_add_symbol(base, symbol); geslp_done: return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * gid = groupe de travail impliqué. * status = barre de statut à tenir informée. * * * * Description : Charge tous les symboles internes possibles. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_elf_internal_symbols(GElfFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ bool no_name; /* Choix de construction de nom*/ activity_id_t msg; /* Message de progression */ GWorkQueue *queue; /* Gestionnaire de différés */ elf_shdr *dynsym_sections; /* Groupe de sections trouvées */ size_t count; /* Quantité de données */ elf_shdr *symtab_sections; /* Groupe de sections trouvées */ size_t i; /* Boucle de parcours */ result = true; /* Charge tous les symboles définis dans une section */ void add_all_symbols_from_section(const elf_shdr *section, bool use_virt, GWorkQueue *wq, activity_id_t id) { phys_t start; /* Début de la zone à traiter */ phys_t size; /* Taille de cette même zone */ phys_t sym_size; /* Taille de chaque symbole lu */ guint runs_count; /* Qté d'exécutions parallèles */ phys_t run_size; /* Volume réparti par exécution*/ guint i; /* Boucle de parcours */ phys_t begin; /* Début de zone de traitement */ phys_t end; /* Fin d'un zone de traitement */ GElfLoading *loading; /* Tâche de chargement à lancer*/ get_elf_section_content(format, section, &start, &size, NULL); sym_size = ELF_SIZEOF_SYM(format); runs_count = g_get_num_processors(); run_size = size / (sym_size * runs_count); gtk_status_stack_extend_activity(status, id, size / sym_size); for (i = 0; i < runs_count; i++) { begin = start + i * run_size * sym_size; if ((i + 1) == runs_count) end = start + size; else end = begin + run_size * sym_size; loading = g_elf_loading_new(format, section, use_virt, start, begin, end, id, do_elf_internal_symbol_loading); g_work_queue_schedule_work(wq, G_DELAYED_WORK(loading), gid); } } if (!g_generic_config_get_value(get_main_configuration(), MPK_FORMAT_NO_NAME, &no_name)) return false; msg = gtk_status_stack_add_activity(status, _("Loading internal symbols..."), 0); queue = get_work_queue(); if (find_elf_sections_by_type(format, SHT_DYNSYM, &dynsym_sections, &count)) for (i = 0; i < count; i++) add_all_symbols_from_section(&dynsym_sections[i], no_name, queue, msg); if (find_elf_sections_by_type(format, SHT_SYMTAB, &symtab_sections, &count)) for (i = 0; i < count; i++) add_all_symbols_from_section(&symtab_sections[i], no_name, queue, msg); g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); if (dynsym_sections != NULL) free(dynsym_sections); if (symtab_sections != NULL) free(symtab_sections); return result; } /* ---------------------------------------------------------------------------------- */ /* DETAIL DES SYMBOLES EXTERNES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * dynamic = section de type SHT_DYNAMIC. * * type = sorte d'élément recherché. * * item = élément retrouvé dans la section. [OUT] * * * * Description : Retrouve un élément donné dans la section dynamique. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool find_elf_dynamic_item(const GElfFormat *format, const elf_shdr *section, int32_t type, elf_dyn *item) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ phys_t pos; /* Position de lecture */ vmpa2t tmp; /* Position écrasable */ int32_t tag32; /* Type de l'entrée (32 bits) */ int64_t tag64; /* Type de l'entrée (64 bits) */ result = true; content = G_BIN_FORMAT(format)->content; for (pos = ELF_SHDR(format, *section, sh_offset); result; pos += ELF_SIZEOF_DYN(format)) { init_vmpa(&tmp, pos, VMPA_NO_VIRTUAL); if (format->is_32b) { result = g_binary_content_read_s32(content, &tmp, format->endian, &tag32); if (tag32 == type) break; } else { result = g_binary_content_read_s64(content, &tmp, format->endian, &tag64); if (tag64 == type) break; } } if (result) { init_vmpa(&tmp, pos, VMPA_NO_VIRTUAL); if (format->is_32b) { result = g_binary_content_read_s32(content, &tmp, format->endian, &item->dyn32.d_tag); result &= g_binary_content_read_s32(content, &tmp, format->endian, &item->dyn32.d_un.d_val); } else { result = g_binary_content_read_s64(content, &tmp, format->endian, &item->dyn64.d_tag); result &= g_binary_content_read_s64(content, &tmp, format->endian, &item->dyn64.d_un.d_val); } } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * dynamic = section de type SHT_DYNAMIC. * * * * Description : Charge tous les éléments dynamiques externes possibles. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_elf_external_symbols(GElfFormat *format, const elf_shdr *section) { bool result; /* Bilan à retourner */ elf_dyn item; /* Elément dynamique */ elf_shdr relxxx; /* Section .rel.xxx trouvée */ elf_shdr dynsym; /* Section .dynsym trouvée */ elf_shdr dynstr; /* Section .dynstr trouvée */ elf_shdr plt; /* Section .plt trouvée */ result = true; /* Section .rel.plt */ if (find_elf_dynamic_item(format, section, DT_JMPREL, &item)) { result &= find_elf_section_by_virtual_address(format, ELF_DYN(format, item, d_un.d_ptr), &relxxx); if (result) result = find_elf_section_by_index(format, ELF_SHDR(format, relxxx, sh_link), &dynsym); if (result) result = find_elf_section_by_index(format, ELF_SHDR(format, dynsym, sh_link), &dynstr); if (result) switch (ELF_HDR(format, format->header, e_machine)) { case EM_ARM: result = load_elf_arm_relocated_symbols(format, &relxxx, &dynsym, &dynstr); break; case EM_386: result = load_elf_x86_relocated_symbols(format, &relxxx, &dynsym, &dynstr); break; default: break; } } #if 0 /* Entrées équivalentes dans le binaire */ if (find_elf_dynamic_item(format, section, DT_SYMTAB, &item)) { result &= find_elf_section_by_virtual_address(format, ELF_DYN(format, item, d_un.d_ptr), &dynsym); if (result) result = find_elf_section_by_index(format, ELF_SHDR(format, dynsym, sh_link), &dynstr); if (result) switch (g_exe_format_get_target_machine(G_EXE_FORMAT(format))) { case FTM_MIPS: //result = find_elf_mips_dynamic_symbols(format, &dynsym, &dynstr); break; case FTM_386: if (find_elf_dynamic_item(format, section, DT_JMPREL, &item)) { result &= find_elf_section_by_virtual_address(format, ELF_DYN(format, item, d_un.d_ptr), &relxxx); printf("VMA :: 0x%08llx\n", ELF_DYN(format, item, d_un.d_ptr)); if (result) result = find_elf_section_by_index(format, ELF_SHDR(format, relxxx, sh_info), &plt); if (result) result = find_elf_x86_dynamic_symbols(format, &plt, &relxxx, &dynsym, &dynstr); } break; default: break; } } #endif return result; }