/* Chrysalide - Outil d'analyse de fichiers binaires * symbols.c - gestion des symboles d'un ELF * * Copyright (C) 2017-2019 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 Chrysalide. If not, see . */ #include "symbols.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dynamic.h" #include "elf-int.h" #include "loading.h" #include "program.h" #include "section.h" /* ------------------------- CHARGEMENT GLOBAL DES SYMBOLES ------------------------- */ /* Assure le chargement des symboles internes ELF en différé. */ static bool do_elf_symbol_loading(GElfLoading *, GElfFormat *, bool, phys_t *, GBinSymbol **); /* Charge tous les symboles possibles. */ static void add_all_elf_symbols(GElfFormat *, phys_t, size_t, phys_t, GWorkQueue *, wgroup_id_t, elf_loading_cb, GtkStatusStack *, activity_id_t); /* --------------------------- DETAIL DES SYMBOLES LOCAUX --------------------------- */ /* Assure le chargement des symboles locaux ELF en différé. */ static bool do_elf_local_symbol_loading(GElfLoading *, GElfFormat *, phys_t *); /* Charge tous les symboles internes possibles. */ static bool load_elf_local_symbols(GElfFormat *, wgroup_id_t, GtkStatusStack *); /* --------------------------- DETAIL DE SYMBOLES GLOBAUX --------------------------- */ /* Assure le chargement des symboles globaux ELF en différé. */ static bool do_elf_global_symbol_loading(GElfLoading *, GElfFormat *, phys_t *); /* Dénombre le nombre de symboles en lien avec l'extérieur. */ static bool count_elf_global_symbols(GElfFormat *, GExeFormat *, uint32_t *); /* Charge tous les éléments dynamiques externes possibles. */ static bool load_elf_global_symbols(GElfFormat *, wgroup_id_t, GtkStatusStack *); /* ------------------------ INSCRIPTION DE SYMBOLES IMPORTES ------------------------ */ /* Assure le chargement des relocalisations ELF en différé. */ static bool do_elf_relocation_loading(GElfLoading *, GElfFormat *, phys_t *); /* Charge en mémoire toutes les relocalisations présentes. */ static bool load_elf_relocations(GElfFormat *, const elf_phdr *, elf_rel **, size_t *, wgroup_id_t, GtkStatusStack *); /* Assure la construction d'un symbole issu des relocalisations. */ static GBinSymbol *do_elf_relocation_convert(GElfLoading *, GElfFormat *, vmpa2t *); /* Construit une liste de symboles issus des relocalisations. */ static GBinSymbol **convert_elf_relocations_to_symbols(GElfFormat *, elf_rel *, size_t, vmpa2t *, wgroup_id_t, GtkStatusStack *, size_t *); /* Ajoute l'ensemble des symboles importés. */ static bool load_imported_elf_symbols(GElfFormat *, wgroup_id_t, GtkStatusStack *); /* ------------------------- INSCRIPTION DE POINTS D'ENTREE ------------------------- */ /* Enregistre un point d'entrée au sein d'un binaire ELF. */ static bool register_elf_entry_point(GElfFormat *, virt_t, GBinRoutine *, DisassPriorityLevel); /* Désigne tous les points d'entrée par une étiquette dédiée. */ static bool load_elf_entry_points_from_array(GElfFormat *, const elf_dyn *, const elf_dyn *, const char *, DisassPriorityLevel); /* Enumère tous les points d'entrée principaux d'un binaire ELF. */ static bool load_all_elf_basic_entry_points(GElfFormat *, GtkStatusStack *); /* ---------------------------------------------------------------------------------- */ /* CHARGEMENT GLOBAL DES SYMBOLES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * gid = groupe de travail impliqué. * 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, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ result = true; /* Symboles internes */ result &= load_elf_local_symbols(format, gid, status); /* Symboles importés et/ou exportés */ if (find_elf_dynamic_program_header(format, (elf_phdr []) { { { 0 } } })) { log_variadic_message(LMT_INFO, _("Binary is dynamically linked")); result &= load_elf_global_symbols(format, gid, status); result &= load_imported_elf_symbols(format, gid, status); } else log_variadic_message(LMT_INFO, _("Binary is statically linked")); /* Symboles d'entrée, si encore besoin */ if (result) result = load_all_elf_basic_entry_points(format, status); return result; } /****************************************************************************** * * * Paramètres : loading = chargement de symboles en cours. * * format = format ELF à compléter. * * local = s'apprête-t-on à constuire un symbole interne ? * * iter = tête de lecture évoluant avec le temps. [OUT] * * new = éventuel renseignement du nouveau symbole. [OUT] * * * * Description : Assure le chargement des symboles internes ELF en différé. * * * * Retour : Bilan de l'exécution, utile pour la poursuite du traitement. * * * * Remarques : - * * * ******************************************************************************/ static bool do_elf_symbol_loading(GElfLoading *loading, GElfFormat *format, bool local, phys_t *iter, GBinSymbol **new) { bool result; /* Bilan à retourner */ elf_sym sym; /* Symbole aux infos visées */ virt_t virt; /* Adresse virtuelle */ SymbolStatus status; /* Visibilité du symbole */ GBinFormat *base; /* Version basique du format */ uint32_t index; /* Indice du nom du symbole */ const char *name; /* Nom du symbole trouvé */ GBinSymbol *symbol; /* Nouveau symbole construit */ char alt_name[6 + VMPA_MAX_LEN]; /* Nom abstrait de substitution*/ virt_t original_virt; /* Adresse virtuelle retenue */ vmpa2t addr; /* Localisation d'un symbole */ mrange_t range; /* Couverture mémoire associée */ GBinRoutine *routine; /* Nouvelle routine trouvée */ if (new != NULL) *new = NULL; result = read_elf_symbol(format, iter, &sym); if (!result) goto desl_done; /** * Si l'adresse virtuelle est nulle, on ne peut ratacher le symbole à aucune position... * * On ne réalise donc aucune opération ici, quitte à laisser une seconde passe * s'occuper des symboles importés par exemple. */ virt = ELF_SYM(format, sym, st_value); if (virt == 0) goto desl_done; /** * En ce qui concerne la nature de la visibilité, on distingue les deux situations suivantes : * - zone DYNSYM : uniquement les importations / exportations. * - zone SYMTAB : tous les symboles. * * La première zone doit donc être traitée en amont, et la seconde complète les traitements * avec à priori uniquement des symboles locaux. */ if (local) status = SSS_INTERNAL; else { status = ELF_SYM(format, sym, st_shndx) == 0 ? SSS_IMPORTED : SSS_EXPORTED; /** * Si le symbol doit être traité ailleurs... */ if (status == SSS_IMPORTED) goto desl_done; } /* Traitements particuliers */ base = G_BIN_FORMAT(format); index = ELF_SYM(format, sym, st_name); switch (ELF_ST_TYPE(format, sym)) { case STT_OBJECT: name = g_elf_loading_build_name(loading, index, virt, "obj_", alt_name, &addr); if (name == NULL) { symbol = NULL; break; } init_mrange(&range, &addr, ELF_SYM(format, sym, st_size)); symbol = g_binary_symbol_new(&range, STP_OBJECT); g_binary_symbol_set_alt_label(symbol, name); break; case STT_FUNC: original_virt = virt; /* Ajustement de la position */ virt = format->ops.fix_virt(virt); /* Constitution d'une routine */ name = g_elf_loading_build_name(loading, index, virt, "func_", alt_name, &addr); if (name == NULL) { symbol = NULL; break; } routine = g_binary_format_decode_routine(base, name); symbol = G_BIN_SYMBOL(routine); init_mrange(&range, &addr, ELF_SYM(format, sym, st_size)); g_binary_symbol_set_range(symbol, &range); /* Comptabilisation pour le désassemblage brut */ g_binary_format_register_code_point(base, original_virt, DPL_SYMBOL); break; default: symbol = NULL; break; } if (symbol != NULL) { g_binary_symbol_set_status(symbol, status); /* if (new != NULL) { g_object_ref(G_OBJECT(symbol)); *new = symbol; } */ g_binary_format_add_symbol(base, symbol); } desl_done: return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * sym_start = localisation du début de la zone de symboles. * * count = nombre de descriptions de symbole attendues. * * str_start = début de la zone contenant les descriptions. * * wq = espace de travail dédié. * * gid = groupe de travail impliqué. * * callback = routine de traitements particuliers. * * status = barre de statut à tenir informée. * * msg = identifiant du message de progression. * * * * Description : Charge tous les symboles possibles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void add_all_elf_symbols(GElfFormat *format, phys_t sym_start, size_t count, phys_t str_start, GWorkQueue *wq, wgroup_id_t gid, elf_loading_cb callback, GtkStatusStack *status, activity_id_t msg) { 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*/ sym_size = ELF_SIZEOF_SYM(format); run_size = compute_run_size(count, &runs_count); gtk_status_stack_extend_activity(status, msg, count); for (i = 0; i < runs_count; i++) { begin = sym_start + i * run_size * sym_size; if ((i + 1) == runs_count) end = sym_start + count * sym_size; else end = begin + run_size * sym_size; loading = g_elf_loading_new_for_symbols(format, str_start, sym_start, begin, end, msg, callback); g_work_queue_schedule_work(wq, G_DELAYED_WORK(loading), gid); } } /* ---------------------------------------------------------------------------------- */ /* DETAIL DES SYMBOLES LOCAUX */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : loading = chargement de symboles externes en cours. * * format = format ELF à compléter. * * iter = tête de lecture évoluant avec le temps. [OUT] * * * * Description : Assure le chargement des symboles locaux ELF en différé. * * * * Retour : Bilan de l'exécution, utile pour la poursuite du traitement. * * * * Remarques : - * * * ******************************************************************************/ static bool do_elf_local_symbol_loading(GElfLoading *loading, GElfFormat *format, phys_t *iter) { bool result; /* Bilan à retourner */ result = do_elf_symbol_loading(loading, format, true, iter, NULL); 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_local_symbols(GElfFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ activity_id_t msg; /* Message de progression */ GWorkQueue *queue; /* Gestionnaire de différés */ size_t size; /* Taille de chaque symbole lu */ elf_shdr *symtabs; /* Groupe de sections trouvées */ size_t count; /* Quantité de données */ size_t i; /* Boucle de parcours */ phys_t sym_start; /* Début de la zone à traiter */ phys_t sym_size; /* Taille de cette même zone */ size_t sym_count; /* Nombre de symboles déduits */ elf_shdr strtab; /* Section dédiées aux chaînes */ phys_t str_start; /* Début de cette section */ result = true; msg = gtk_status_stack_add_activity(status, _("Loading local symbols..."), 0); queue = get_work_queue(); size = ELF_SIZEOF_SYM(format); if (find_elf_sections_by_type(format, SHT_SYMTAB, &symtabs, &count)) for (i = 0; i < count; i++) { get_elf_section_content(format, &symtabs[i], &sym_start, &sym_size, NULL); if (sym_size % size != 0) continue; sym_count = sym_size / size; if (!find_elf_section_by_index(format, ELF_SHDR(format, symtabs[i], sh_link), &strtab)) continue; get_elf_section_content(format, &strtab, &str_start, NULL, NULL); add_all_elf_symbols(format, sym_start, sym_count, str_start, queue, gid, do_elf_local_symbol_loading, status, msg); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); if (symtabs != NULL) free(symtabs); return result; } /* ---------------------------------------------------------------------------------- */ /* DETAIL DE SYMBOLES GLOBAUX */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : loading = chargement de symboles externes en cours. * * format = format ELF à compléter. * * iter = tête de lecture évoluant avec le temps. [OUT] * * * * Description : Assure le chargement des symboles globaux ELF en différé. * * * * Retour : Bilan de l'exécution, utile pour la poursuite du traitement. * * * * Remarques : - * * * ******************************************************************************/ static bool do_elf_global_symbol_loading(GElfLoading *loading, GElfFormat *format, phys_t *iter) { bool result; /* Bilan à retourner */ GBinSymbol *symbol; /* Nouveau symbole en place */ result = do_elf_symbol_loading(loading, format, false, iter, &symbol); //g_clear_object(&symbol); return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * exec = autre vision de ce format. * * count = nombre de symboles présents. [OUT] * * * * Description : Dénombre le nombre de symboles en lien avec l'extérieur. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool count_elf_global_symbols(GElfFormat *format, GExeFormat *exec, uint32_t *count) { bool result; /* Bilan à retourner */ elf_dyn hash; /* Table de type DT_HASH */ bool found; /* Détection validée */ vmpa2t addr; /* Position de départ brute */ uint32_t n_buckets; /* Quantité de bacs en place */ uint32_t sym_offset; /* Indice du premier symbole */ uint32_t bloom_size; /* Taille du filtre Bloom */ uint32_t last_symbol; /* Indice de dernier symbole */ uint32_t i; /* Boucle de parcours */ uint32_t start; /* Indice de départ d'un bac */ uint32_t value; /* Valeur d'un maillon */ result = false; /** * Cf. l'astuce indiquée par : * * - http://www.gabriel.urdhr.fr/2015/09/28/elf-file-format/#symbol-tables * - http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#hash * * Le fonctionnement global des chaînes est décrit ici : * * - https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/ * * Celui des chaînes GNU fait l'objet de l'article suivant : * * - https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ * */ /* Méthode DT_HASH */ found = find_elf_dynamic_item_by_type(format, DT_HASH, &hash); if (found) { result = g_exe_format_translate_address_into_vmpa(exec, ELF_DYN(format, hash, d_un.d_ptr), &addr); if (!result) goto exit; advance_vmpa(&addr, sizeof(uint32_t)); result = g_binary_content_read_u32(G_KNOWN_FORMAT(format)->content, &addr, format->endian, count); if (!result) goto exit; goto exit; } /* Méthode DT_GNU_HASH */ found = find_elf_dynamic_item_by_type(format, DT_GNU_HASH, &hash); if (found) { result = g_exe_format_translate_address_into_vmpa(exec, ELF_DYN(format, hash, d_un.d_ptr), &addr); if (!result) goto exit; result = g_binary_content_read_u32(G_KNOWN_FORMAT(format)->content, &addr, format->endian, &n_buckets); if (!result) goto exit; result = g_binary_content_read_u32(G_KNOWN_FORMAT(format)->content, &addr, format->endian, &sym_offset); if (!result) goto exit; result = g_binary_content_read_u32(G_KNOWN_FORMAT(format)->content, &addr, format->endian, &bloom_size); if (!result) goto exit; /* Saut de bloom_shift */ advance_vmpa(&addr, sizeof(uint32_t)); /* Saut de bloom[bloom_size] */ if (format->is_32b) advance_vmpa(&addr, bloom_size * sizeof(uint32_t)); else advance_vmpa(&addr, bloom_size * sizeof(uint64_t)); /* Localisation de la chaîne comportant le plus grand index */ last_symbol = 0; for (i = 0; i < n_buckets; i++) { result = g_binary_content_read_u32(G_KNOWN_FORMAT(format)->content, &addr, format->endian, &start); if (!result) goto exit; if (last_symbol < start) last_symbol = start; } if (last_symbol < sym_offset) { *count = sym_offset; result = true; } else { /* Parcours de la chaîne au plus haut potentiel */ advance_vmpa(&addr, (last_symbol - sym_offset) * sizeof(uint32_t)); while (true) { result = g_binary_content_read_u32(G_KNOWN_FORMAT(format)->content, &addr, format->endian, &value); if (!result) goto exit; last_symbol++; if (value & 0x1) break; } *count = last_symbol; result = true; } } exit: 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 éléments dynamiques externes possibles. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_elf_global_symbols(GElfFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ GExeFormat *exec; /* Autre vision du format */ elf_dyn strtab; /* Table de type DT_STRTAB */ phys_t str_start; /* Début de zone des chaînes */ elf_dyn symtab; /* Table de type DT_SYMTAB */ phys_t sym_start; /* Début de zone des symboles */ uint32_t count; /* Nombre de symboles présents */ activity_id_t msg; /* Message de progression */ GWorkQueue *queue; /* Gestionnaire de différés */ result = true; /** * Les spécifications ne sont pas très claires sur le nombre de tables * possible... On y parle de LA table des symboles, ce qui laisse penser * qu'il ne peut y en avoir qu'une. */ exec = G_EXE_FORMAT(format); /* Récupération du début des chaînes de description */ result = find_elf_dynamic_item_by_type(format, DT_STRTAB, &strtab); if (!result) goto exit; result = g_exe_format_translate_address_into_offset(exec, ELF_DYN(format, strtab, d_un.d_ptr), &str_start); if (!result) goto exit; /* Récupération du début des définitions de symboles */ result = find_elf_dynamic_item_by_type(format, DT_SYMTAB, &symtab); if (!result) goto exit; result = g_exe_format_translate_address_into_offset(exec, ELF_DYN(format, symtab, d_un.d_ptr), &sym_start); if (!result) goto exit; /* Détermination du nombre d'éléments */ result = count_elf_global_symbols(format, exec, &count); if (!result) goto exit; /* Chargement des symboles */ msg = gtk_status_stack_add_activity(status, _("Loading global symbols..."), 0); queue = get_work_queue(); add_all_elf_symbols(format, sym_start, count, str_start, queue, gid, do_elf_global_symbol_loading, status, msg); g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); exit: return result; } /* ---------------------------------------------------------------------------------- */ /* INSCRIPTION DE SYMBOLES IMPORTES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : loading = chargement de relocalisations en cours. * * format = format ELF à compléter. * * iter = tête de lecture évoluant avec le temps. [OUT] * * * * Description : Assure le chargement des relocalisations ELF en différé. * * * * Retour : Bilan de l'exécution, utile pour la poursuite du traitement. * * * * Remarques : - * * * ******************************************************************************/ static bool do_elf_relocation_loading(GElfLoading *loading, GElfFormat *format, phys_t *iter) { bool result; /* Bilan à retourner */ elf_rel reloc; /* Relocalisation constituée */ result = read_elf_relocation(format, iter, &reloc); if (result) g_elf_loading_store_relocation(loading, iter, &reloc); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * dynamic = en-tête de programme de type DYNAMIC. * * relocs = liste des relocalisations triées à charger. [OUT] * * count = taille de cette liste. [OUT] * * gid = groupe de travail dédié. * * status = barre de statut à tenir informée. * * * * Description : Charge en mémoire toutes les relocalisations présentes. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_elf_relocations(GElfFormat *format, const elf_phdr *dynamic, elf_rel **relocs, size_t *count, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ GExeFormat *exec; /* Autre vision du format */ elf_dyn jmprel; /* Table des relocalisations */ vmpa2t start; /* Position de départ brute */ elf_dyn pltrelsz; /* Taille de table en octets */ uint64_t length; /* Nombre total des éléments */ mrange_t shr_range; /* Emplacement des relocs. #1 */ mrange_t phr_range; /* Emplacement des relocs. #2 */ phys_t rel_size; /* Taille de chaque élément lu */ bool ret; /* Bilan d'un appel */ activity_id_t msg; /* Message de progression */ GWorkQueue *queue; /* Gestionnaire de différés */ guint runs_count; /* Qté d'exécutions parallèles */ phys_t run_size; /* Volume réparti par exécution*/ GElfLoading **loadings; /* Tâches de chargement */ guint i; /* Boucle de parcours */ phys_t begin; /* Début de zone de traitement */ phys_t end; /* Fin d'un zone de traitement */ result = true; *relocs = NULL; *count = 0; exec = G_EXE_FORMAT(format); /* Collecte des informations */ if (!_find_elf_dynamic_item_by_type(format, dynamic, DT_JMPREL, &jmprel)) goto exit; result = g_exe_format_translate_address_into_vmpa(exec, ELF_DYN(format, jmprel, d_un.d_ptr), &start); if (!result) goto exit; if (!_find_elf_dynamic_item_by_type(format, dynamic, DT_PLTRELSZ, &pltrelsz)) goto exit; length = ELF_DYN(format, pltrelsz, d_un.d_val); /* Corrélation des informations */ ret = find_elf_section_range_by_name(format, ".rel.plt", &shr_range); if (ret) { init_mrange(&phr_range, &start, length); if (cmp_mrange(&phr_range, &shr_range) != 0) log_simple_message(LMT_BAD_BINARY, _("The linker PLT and the PLT section differ by their area definition.")); } /* Détermination du nombre d'éléments */ rel_size = ELF_SIZEOF_REL(format); if (length % rel_size != 0) { result = false; goto exit; } length /= rel_size; /* Chargement en mémoire des relocalisations */ if (length == 0) goto exit; *relocs = malloc(length * sizeof(elf_rel)); *count = length; msg = gtk_status_stack_add_activity(status, _("Loading relocations..."), length); queue = get_work_queue(); run_size = compute_run_size(length, &runs_count); loadings = malloc(runs_count * sizeof(GElfLoading *)); for (i = 0; i < runs_count; i++) { begin = get_phy_addr(&start) + i * run_size * rel_size; if ((i + 1) == runs_count) end = get_phy_addr(&start) + length * rel_size; else end = begin + run_size * rel_size; loadings[i] = g_elf_loading_new_for_relocations(format, begin, end, (*relocs) + i * run_size, msg, do_elf_relocation_loading); g_object_ref(G_OBJECT(loadings[i])); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loadings[i]), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); /* Vérifications du bon déroulement */ for (i = 0; i < runs_count; i++) { result &= g_elf_loading_get_status(loadings[i]); g_object_unref(G_OBJECT(loadings[i])); } free(loadings); if (!result) { free(*relocs); goto exit; } /* Tri de la liste obtenue */ int compare_relocations(const elf_rel *a, const elf_rel *b) { return sort_uint64_t(ELF_REL(format, *a, r_offset), ELF_REL(format, *b, r_offset)); } qsort(*relocs, *count, sizeof(elf_rel), (__compar_fn_t)compare_relocations); exit: return result; } /****************************************************************************** * * * Paramètres : loading = chargement de relocalisations en cours. * * format = format ELF à compléter. * * addr = emplacement de code à traiter. [OUT] * * * * Description : Assure la construction d'un symbole issu des relocalisations.* * * * Retour : Nouveau symbole constitué ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ static GBinSymbol *do_elf_relocation_convert(GElfLoading *loading, GElfFormat *format, vmpa2t *addr) { GBinSymbol *result; /* Symbole à retourner */ vmpa2t start; /* Sauvegarde du départ */ uint64_t offset; /* Décalage à retrouver */ bool status; /* Bilan d'une opération */ elf_rel *reloc; /* Infos de relocalisation */ uint64_t index; /* Indice du symbole concerné */ phys_t length; /* Taille du nouveau symbole */ mrange_t range; /* Couverture mémoire associée */ result = NULL; /* Détermination de la relocalisation associée */ copy_vmpa(&start, addr); status = format->ops.get_linkage_offset(format, addr, &offset); if (!status) goto exit; status = g_elf_loading_search_for_relocation(loading, &offset, &reloc); if (!status) goto exit; /* Récupération des données du symbole visé */ index = ELF_REL_SYM(format, *reloc); result = g_elf_loading_build_plt_symbol(loading, index); if (result == NULL) goto exit; /* Inscription des propriétés associées */ length = compute_vmpa_diff(&start, addr); init_mrange(&range, &start, length); g_binary_symbol_set_range(result, &range); g_binary_symbol_set_status(result, SSS_IMPORTED); /* Comptabilisation pour le désassemblage brut */ g_binary_format_register_code_point(G_BIN_FORMAT(format), start.virtual, DPL_FORMAT_POINT); exit: return result; } /****************************************************************************** * * * Paramètres : format = format ELF à compléter. * * relocs = table des relocalisations chargées. * * rel_count = nombre de ces éléments à interpréter. * * start = emplacement du premier symbole. * * gid = groupe de travail dédié. * * status = barre de statut à tenir informée. * * count = nombre de symboles mis en place. [OUT] * * * * Description : Construit une liste de symboles issus des relocalisations. * * * * Retour : Liste de symboles triée ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ static GBinSymbol **convert_elf_relocations_to_symbols(GElfFormat *format, elf_rel *relocs, size_t rel_count, vmpa2t *start, wgroup_id_t gid, GtkStatusStack *status, size_t *count) { GBinSymbol **result; /* Liste à retourner */ GExeFormat *exec; /* Autre vision du format */ elf_dyn strtab; /* Table de type DT_STRTAB */ bool ret; /* Bilan d'une opération */ phys_t str_start; /* Début de zone des chaînes */ elf_dyn symtab; /* Table de type DT_SYMTAB */ phys_t sym_start; /* Début de zone des symboles */ uint32_t sym_count; /* Nombre de symboles présents */ activity_id_t msg; /* Message de progression */ GWorkQueue *queue; /* Gestionnaire de différés */ GElfLoading *loading; /* Tâche de chargement */ result = NULL; exec = G_EXE_FORMAT(format); /* Récupération du début des chaînes de description */ ret = find_elf_dynamic_item_by_type(format, DT_STRTAB, &strtab); if (!ret) goto aer_exit; ret = g_exe_format_translate_address_into_offset(exec, ELF_DYN(format, strtab, d_un.d_ptr), &str_start); if (!ret) goto aer_exit; /* Récupération du début des définitions de symboles */ ret = find_elf_dynamic_item_by_type(format, DT_SYMTAB, &symtab); if (!ret) goto aer_exit; ret = g_exe_format_translate_address_into_offset(exec, ELF_DYN(format, symtab, d_un.d_ptr), &sym_start); if (!ret) goto aer_exit; /* Détermination du nombre d'éléments */ ret = count_elf_global_symbols(format, exec, &sym_count); if (!ret) goto aer_exit; /* Mise en application des références externes */ msg = gtk_status_stack_add_activity(status, _("Applying relocations..."), rel_count); queue = get_work_queue(); loading = g_elf_loading_new_for_imported(format, start, str_start, relocs, rel_count, sym_start, sym_count, msg, do_elf_relocation_convert); g_object_ref(G_OBJECT(loading)); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loading), gid); g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, msg); /* Vérification du bon déroulement */ ret = g_elf_loading_get_status(loading); if (ret) result = g_elf_loading_get_imported_symbols(loading, count); g_object_unref(G_OBJECT(loading)); aer_exit: return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * gid = groupe de travail dédié. * * status = barre de statut à tenir informée. * * * * Description : Ajoute l'ensemble des symboles importés. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_imported_elf_symbols(GElfFormat *format, wgroup_id_t gid, GtkStatusStack *status) { bool result; /* Bilan à retourner */ elf_phdr dynamic; /* En-tête de programme DYNAMIC*/ elf_rel *relocs; /* Relocalisations présentes */ size_t rel_count; /* Qté de ces relocalisations */ virt_t plt_virt; /* Adresse de la PLT */ GExeFormat *exec; /* Autre vision du format */ vmpa2t plt_addr; /* Localisation complète */ size_t count; /* Quantité de symboles chargés*/ GBinSymbol **symbols; /* Liste de ces symboles */ size_t i; /* Boucle de parcours */ result = false; if (!find_elf_dynamic_program_header(format, &dynamic)) goto quick_exit; /* Chargement des relocalisations */ if (!load_elf_relocations(format, &dynamic, &relocs, &rel_count, gid, status)) goto quick_exit; /* Localisation du code de la PLT */ if (!resolve_plt_using_got(format, &plt_virt)) goto exit; exec = G_EXE_FORMAT(format); if (!g_exe_format_translate_address_into_vmpa(exec, plt_virt, &plt_addr)) goto exit; /* Inscription des symboles */ result = format->ops.find_first_plt(format, &plt_addr); if (!result) goto exit; symbols = convert_elf_relocations_to_symbols(format, relocs, rel_count, &plt_addr, gid, status, &count); if (symbols == NULL) goto exit; result = g_binary_format_add_symbols(G_BIN_FORMAT(format), symbols, count); for (i = 0; i < count; i++) g_object_unref(symbols[i]); if (symbols != NULL) free(symbols); exit: if (relocs != NULL) free(relocs); quick_exit: return result; } /* ---------------------------------------------------------------------------------- */ /* INSCRIPTION DE POINTS D'ENTREE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * vaddr = adresse virtuelle du symbole à insérer. * * routine = représentation de la fonction repérée. * * level = indication de priorité et d'origine de l'adresse. * * * * Description : Enregistre un point d'entrée au sein d'un binaire ELF. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool register_elf_entry_point(GElfFormat *format, virt_t vaddr, GBinRoutine *routine, DisassPriorityLevel level) { bool result; /* Bilan à renvoyer */ virt_t final_vaddr; /* Adresse virtuelle retenue */ vmpa2t addr; /* Localisation d'une routine */ GBinFormat *base; /* Version basique du format */ GBinSymbol *symbol; /* Nouveau symbole construit */ mrange_t range; /* Couverture mémoire associée */ /* Localisation complète du symbole */ final_vaddr = format->ops.fix_virt(vaddr); result = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), final_vaddr, &addr); if (!result) goto exit; /* Comptabilisation en tant que symbole */ base = G_BIN_FORMAT(format); if (g_binary_format_find_symbol_at(base, &addr, &symbol)) { /** * On ne relâche pas tout de suite le symbole trouvé, afin de le traiter * en fin de fonction. * * Par contre, la routine proposée est détruite ici. */ g_object_unref(G_OBJECT(routine)); } else { symbol = G_BIN_SYMBOL(routine); g_object_ref(G_OBJECT(symbol)); init_mrange(&range, &addr, 0); g_binary_symbol_set_range(symbol, &range); g_binary_format_add_symbol(base, symbol); } /* Marquage */ g_binary_symbol_set_stype(symbol, STP_ENTRY_POINT); g_object_unref(G_OBJECT(symbol)); /* Comptabilisation pour le désassemblage brut */ g_binary_format_register_code_point(base, vaddr, level); exit: return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * array = indications quant au tableau à charger. * * size = indications quant à la taille de ce tableau. * * prefix = désignation de base des éléments du tableau. * * level = indication de priorité et d'origine de l'adresse. * * * * Description : Désigne tous les points d'entrée par une étiquette dédiée. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool load_elf_entry_points_from_array(GElfFormat *format, const elf_dyn *array, const elf_dyn *size, const char *prefix, DisassPriorityLevel level) { bool result; /* Bilan à renvoyer */ GBinFormat *base; /* Autre version du format */ GBinContent *content; /* Contenu binaire à lire */ vmpa2t pos; /* Tête de lecture courante */ phys_t length; /* Taille totale du contenu */ uint32_t virt_32; /* Adresse virtuelle sur 32b */ uint64_t virt_64; /* Adresse virtuelle sur 64b */ unsigned int i; /* Boucle de parcours */ bool status; /* Bilan d'une opération */ virt_t ep; /* Point d'entrée détecté */ char fullname[64]; /* Désignation humaine */ GBinRoutine *routine; /* Routine à associer à un pt. */ assert(sizeof(fullname) >= (strlen(prefix) + sizeof(XSTR(UINT64_MAX) + 1))); base = G_BIN_FORMAT(format); content = G_KNOWN_FORMAT(base)->content; result = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), ELF_DYN(format, *array, d_un.d_val), &pos); if (!result) goto exit; length = get_phy_addr(&pos) + ELF_DYN(format, *size, d_un.d_val); for (i = 0; get_phy_addr(&pos) < length && result; 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 (format->is_32b) { result = g_binary_content_read_u32(content, &pos, format->endian, &virt_32); status = (virt_32 != 0x0 && virt_32 != 0xffffffff); ep = virt_32; } else { result = g_binary_content_read_u64(content, &pos, format->endian, &virt_64); status = (virt_64 != 0x0 && virt_64 != 0xffffffffffffffff); ep = virt_64; } if (result && status) { snprintf(fullname, sizeof(fullname), "%s%u", prefix, i); routine = g_binary_format_decode_routine(base, fullname); result = register_elf_entry_point(format, ep, routine, level); } } exit: return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * status = barre de statut à tenir informée. * * * * 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, GtkStatusStack *status) { bool result; /* Bilan à renvoyer */ activity_id_t msg; /* Message de progression */ GBinFormat *base; /* Autre version du 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 */ result = true; msg = gtk_status_stack_add_activity(status, _("Registering entry points..."), 0); base = G_BIN_FORMAT(format); /* Point d'entrée principal éventuel */ ep = ELF_HDR(format, format->header, e_entry); if (ep != 0x0) { routine = g_binary_format_decode_routine(base, "entry_point"); result = register_elf_entry_point(format, ep, routine, DPL_ENTRY_POINT); if (!result) goto exit; } /* Chargemet de l'en-tête de programme DYNAMIC */ if (!find_elf_dynamic_program_header(format, &dynamic)) goto exit; /* Détection des constructeurs & destructeurs */ if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_INIT, &item_a)) { ep = ELF_DYN(format, item_a, d_un.d_ptr); if (ep != 0x0) { routine = g_binary_format_decode_routine(base, "init_function"); result = register_elf_entry_point(format, ep, routine, DPL_ENTRY_POINT); if (!result) goto exit; } } if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_FINI, &item_a)) { ep = ELF_DYN(format, item_a, d_un.d_ptr); if (ep != 0x0) { routine = g_binary_format_decode_routine(base, "termination_function"); result = register_elf_entry_point(format, ep, routine, DPL_FORMAT_POINT); if (!result) goto exit; } } if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_INIT_ARRAY, &item_a)) { if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_INIT_ARRAYSZ, &item_b)) { result = load_elf_entry_points_from_array(format, &item_a, &item_b, "init_array_function_", DPL_ENTRY_POINT); if (!result) goto exit; } } if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_FINI_ARRAY, &item_a)) { if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_FINI_ARRAYSZ, &item_b)) { result = load_elf_entry_points_from_array(format, &item_a, &item_b, "fini_array_function_", DPL_FORMAT_POINT); if (!result) goto exit; } } if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_PREINIT_ARRAY, &item_a)) { if (_find_elf_dynamic_item_by_type(format, &dynamic, DT_PREINIT_ARRAYSZ, &item_b)) { result = load_elf_entry_points_from_array(format, &item_a, &item_b, "preinit_array_function_", DPL_ENTRY_POINT); if (!result) goto exit; } } /* Identification de l'entrée de la PLT */ if (resolve_plt_using_got(format, &ep)) { if (ep != 0x0) { routine = g_binary_format_decode_routine(base, "plt_entry"); result = register_elf_entry_point(format, ep, routine, DPL_FORMAT_POINT); } } exit: gtk_status_stack_remove_activity(status, msg); return result; }