/* Chrysalide - Outil d'analyse de fichiers binaires * loading.c - chargements parallèles des symboles de format ELF * * Copyright (C) 2016-2017 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 "loading.h" #include #include #include #include #include #include #include #include #include "elf-int.h" /* Fraction de routines à limiter (instance) */ struct _GElfLoading { GDelayedWork parent; /* A laisser en premier */ GElfFormat *format; /* Format à faire évoluer */ phys_t str_start; /* Chaînes à disposition */ /** * Gestion des informations de contexte. */ union { struct { bool use_virt; /* Représentatio par défaut */ }; struct { elf_rel *relocs_to_fill; /* Tableau à remplir */ }; struct { elf_rel *relocs; /* Relocalisations présentes */ size_t rel_count; /* Qté de ces relocalisations */ phys_t sym_start; /* Début de zone des symboles */ uint32_t sym_count; /* Nombre de symboles présents */ }; struct { phys_t global_start; /* Départ global dans la zone */ phys_t global_end; /* Fin globale dans la zone */ virt_t global_addr; /* Adresse virtuelle initiale */ GBinContent *content; /* Contenu binaire à lire */ const bin_t *data; /* Contenu complet et original */ }; }; /** * Gestion du mode de parcours. */ union { struct { phys_t begin; /* Point de départ du parcours */ phys_t end; /* Point d'arrivée exclu */ elf_loading_cb callback_0; /* Routine de traitement #0 */ }; struct { sym_iter_t *iter; /* Symboles à parcourir */ elf_applying_cb callback_1; /* Routine de traitement #1 */ }; }; unsigned int kind; /* Type de traitements */ bool status; /* Bilan des traitements */ activity_id_t id; /* Identifiant pour messages */ }; /* Fraction de routines à limiter (classe) */ struct _GElfLoadingClass { GDelayedWorkClass parent; /* A laisser en premier */ }; /* Initialise la classe des tâches des chargements pour ELF. */ static void g_elf_loading_class_init(GElfLoadingClass *); /* Initialise une tâche de chargements pour ELF. */ static void g_elf_loading_init(GElfLoading *); /* Supprime toutes les références externes. */ static void g_elf_loading_dispose(GElfLoading *); /* Procède à la libération totale de la mémoire. */ static void g_elf_loading_finalize(GElfLoading *); /* Assure le chargement pour un format ELF en différé. */ static void g_elf_loading_process(GElfLoading *, GtkStatusStack *); /* Indique le type défini pour les tâches de chargements pour format ELF. */ G_DEFINE_TYPE(GElfLoading, g_elf_loading, G_TYPE_DELAYED_WORK); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des tâches des chargements pour ELF. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_loading_class_init(GElfLoadingClass *klass) { GObjectClass *object; /* Autre version de la classe */ GDelayedWorkClass *work; /* Version en classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_elf_loading_dispose; object->finalize = (GObjectFinalizeFunc)g_elf_loading_finalize; work = G_DELAYED_WORK_CLASS(klass); work->run = (run_task_fc)g_elf_loading_process; } /****************************************************************************** * * * Paramètres : loading = instance à initialiser. * * * * Description : Initialise une tâche de chargements pour ELF. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_loading_init(GElfLoading *loading) { loading->status = false; } /****************************************************************************** * * * Paramètres : loading = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_loading_dispose(GElfLoading *loading) { if (loading->kind == 2) g_object_unref(G_OBJECT(loading->content)); G_OBJECT_CLASS(g_elf_loading_parent_class)->dispose(G_OBJECT(loading)); } /****************************************************************************** * * * Paramètres : loading = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_loading_finalize(GElfLoading *loading) { G_OBJECT_CLASS(g_elf_loading_parent_class)->finalize(G_OBJECT(loading)); } /****************************************************************************** * * * Paramètres : format = ensemble d'instructions désassemblées. * * str_start = début des chaînes de caractères à consulter. * * first = position du premier élément. * * begin = point de départ du parcours de liste. * * end = point d'arrivée exclu du parcours. * * id = identifiant du message affiché à l'utilisateur. * * callback = routine de traitements particuliers. * * * * Description : Crée une tâche de chargement pour ELF différée. * * * * Retour : Tâche créée. * * * * Remarques : - * * * ******************************************************************************/ GElfLoading *g_elf_loading_new_for_symbols(GElfFormat *format, phys_t str_start, phys_t first, phys_t begin, phys_t end, activity_id_t id, elf_loading_cb callback) { GElfLoading *result; /* Tâche à retourner */ #ifndef NDEBUG bool status; /* Etat d'une consultation */ #endif result = g_object_new(G_TYPE_ELF_LOADING, NULL); result->format = format; result->str_start = str_start; #ifndef NDEBUG status = g_generic_config_get_value(get_main_configuration(), MPK_FORMAT_NO_NAME, &result->use_virt); assert(status); #else g_generic_config_get_value(get_main_configuration(), MPK_FORMAT_NO_NAME, &result->use_virt); #endif result->begin = begin; result->end = end; result->callback_0 = callback; result->kind = 0; result->id = id; return result; } /****************************************************************************** * * * Paramètres : format = ensemble d'instructions désassemblées. * * begin = point de départ du parcours de liste. * * end = point d'arrivée exclu du parcours. * * relocs = table des relocalisations à remplir. * * id = identifiant du message affiché à l'utilisateur. * * callback = routine de traitements particuliers. * * * * Description : Crée une tâche de chargement pour ELF différée. * * * * Retour : Tâche créée. * * * * Remarques : - * * * ******************************************************************************/ GElfLoading *g_elf_loading_new_for_relocations(GElfFormat *format, phys_t begin, phys_t end, elf_rel *relocs, activity_id_t id, elf_loading_cb callback) { GElfLoading *result; /* Tâche à retourner */ result = g_object_new(G_TYPE_ELF_LOADING, NULL); result->format = format; result->str_start = VMPA_NO_PHYSICAL; result->relocs_to_fill = relocs; result->begin = begin; result->end = end; result->callback_0 = callback; result->kind = 0; result->id = id; return result; } /****************************************************************************** * * * Paramètres : format = ensemble d'instructions désassemblées. * * iter = itérateur sur les symboles à parcourir. * * str_start = début de la zone contenant les descriptions. * * relocs = table des relocalisations chargées. * * rel_count = nombre de ces éléments à interpréter. * * sym_start = localisation du début de la zone de symboles. * * sym_count = nombre de descriptions de symbole attendues. * * id = identifiant du message affiché à l'utilisateur. * * callback = routine de traitements particuliers. * * * * Description : Crée une tâche de chargement pour ELF différée. * * * * Retour : Tâche créée. * * * * Remarques : - * * * ******************************************************************************/ GElfLoading *g_elf_loading_new_for_applying(GElfFormat *format, sym_iter_t *iter, phys_t str_start, elf_rel *relocs, size_t rel_count, phys_t sym_start, uint32_t sym_count, activity_id_t id, elf_applying_cb callback) { GElfLoading *result; /* Tâche à retourner */ result = g_object_new(G_TYPE_ELF_LOADING, NULL); result->format = format; result->str_start = str_start; result->relocs = relocs; result->rel_count = rel_count; result->sym_start = sym_start; result->sym_count = sym_count; result->iter = iter; result->callback_1 = callback; result->kind = 1; result->id = id; return result; } /****************************************************************************** * * * Paramètres : format = ensemble d'instructions désassemblées. * * begin = point de départ de la zone à traiter. * * end = point d'arrivée exclu du parcours. * * gb_start = position de départ pour l'ensemble des données. * * gb_end = position finale dans l'ensemble des données. * * addr = adresse virtuelle de la position initiale. * * id = identifiant du message affiché à l'utilisateur. * * callback = routine de traitements particuliers. * * * * Description : Crée une tâche de chargement de chaînes pour ELF différée. * * * * Retour : Tâche créée. * * * * Remarques : - * * * ******************************************************************************/ GElfLoading *g_elf_loading_new_for_strings(GElfFormat *format, phys_t begin, phys_t end, phys_t gb_start, phys_t gb_end, virt_t addr, activity_id_t id, elf_loading_cb callback) { GElfLoading *result; /* Tâche à retourner */ vmpa2t pos; /* Tête de lecture */ result = g_object_new(G_TYPE_ELF_LOADING, NULL); result->format = format; result->global_start = gb_start; result->global_end = gb_end; result->global_addr = addr; result->content = g_binary_format_get_content(G_BIN_FORMAT(format)); init_vmpa(&pos, gb_start, addr); result->data = g_binary_content_get_raw_access(result->content, &pos, gb_end - gb_start); if (result->data == NULL) goto no_data; result->begin = begin; result->end = end; result->callback_0 = callback; result->kind = 2; result->id = id; return result; no_data: g_object_unref(G_OBJECT(result)); return NULL; } /****************************************************************************** * * * Paramètres : loading = traitements différés à mener. * * status = barre de statut à tenir informée. * * * * Description : Assure le chargement pour un format ELF en différé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_elf_loading_process(GElfLoading *loading, GtkStatusStack *status) { GElfFormat *format; /* Format plus accessible */ phys_t iter; /* Boucle de parcours */ phys_t old; /* Sauvegarde du point d'avant */ bool ret; /* Bilan d'un appel */ size_t processed; /* Nombre de symboles traités */ GBinSymbol *symbol; /* Symbole analysé */ format = loading->format; switch (loading->kind) { case 0: for (iter = loading->begin; iter < loading->end; ) { old = iter; ret = loading->callback_0(loading, format, &iter); if (!ret) { log_variadic_message(LMT_ERROR, _("Error while loading ELF data @ 0x%08x!"), old); break; } gtk_status_stack_update_activity_value(status, loading->id, 1); } loading->status = (iter == loading->end); break; case 1: ret = true; processed = 0; for (symbol = get_symbol_iterator_next(loading->iter); symbol != NULL && ret; symbol = get_symbol_iterator_next(loading->iter)) { ret = loading->callback_1(loading, format, symbol); if (!ret) { log_variadic_message(LMT_ERROR, _("Error while applying ELF relocation %zu!"), processed); break; } processed++; gtk_status_stack_update_activity_value(status, loading->id, 1); if (processed == loading->rel_count) break; } loading->status = (processed == loading->rel_count); break; case 2: for (iter = loading->begin; iter < loading->end; ) { old = iter; loading->status |= loading->callback_0(loading, format, &iter); gtk_status_stack_update_activity_value(status, loading->id, iter - old); } break; default: assert(false); break; } } /****************************************************************************** * * * Paramètres : loading = procédure de chargements écoulés à consulter. * * * * Description : Fournit le bilan des traitements différés. * * * * Retour : true si tout s'est bien déroulé. * * * * Remarques : - * * * ******************************************************************************/ bool g_elf_loading_get_status(const GElfLoading *loading) { return loading->status; } /****************************************************************************** * * * Paramètres : loading = chargement pour ELF à mener. * * name = indice de la désignation du symbole concerné. * * virt = adresse virtuelle du symbole en mémoire. * * prefix = préfixe d'une désignation par défaut. * * alt = zone de constitution d'un nom alternatif. [OUT] * * addr = localisation compléte à associer au symbole. [OUT] * * * * Description : Construit la désignation adaptée à un symbole. * * * * Retour : Pointeur vers une étiquette constituée. * * * * Remarques : - * * * ******************************************************************************/ const char *g_elf_loading_build_name(const GElfLoading *loading, uint32_t name, virt_t virt, const char *prefix, char *alt, vmpa2t *addr) { const char *result; /* Désignation à retourner */ GElfFormat *format; /* Format plus accessible */ vmpa2t pos; /* Position de lecture */ const GBinContent *content; /* Contenu binaire à lire */ size_t plen; /* Taille du préfixe */ result = NULL; format = loading->format; if (g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), virt, addr)) { init_vmpa(&pos, loading->str_start + name, VMPA_NO_VIRTUAL); content = G_BIN_FORMAT(format)->content; result = (const char *)g_binary_content_get_raw_access(content, &pos, 1); if (result != NULL && result[0] == '\0') result = NULL; if (result == NULL) { /** * Charge à l'appelant de s'assurer que la zone tampon est assez grande ! */ strcpy(alt, prefix); plen = strlen(prefix); if (loading->use_virt) vmpa2_virt_to_string(addr, MDS_UNDEFINED, alt + plen, NULL); else vmpa2_phys_to_string(addr, MDS_UNDEFINED, alt + plen, NULL); result = alt; } } return result; } /****************************************************************************** * * * Paramètres : loading = chargement pour ELF à compléter. * * iter = tête de lecture courante. * * reloc = relocalisation à conserver en mémoire. * * * * Description : Intègre dans la liste adaptée une relocalisation chargée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_elf_loading_store_relocation(const GElfLoading *loading, const phys_t *iter, const elf_rel *reloc) { GElfFormat *format; /* Format plus accessible */ size_t index; /* Indice de l'élément */ format = loading->format; index = (*iter - loading->begin); assert(index % sizeof(ELF_SIZEOF_REL(format)) == 0); index /= sizeof(ELF_SIZEOF_REL(format)); /* La tête de lecture a consommé un élément ! */ index--; memcpy(&loading->relocs_to_fill[index], reloc, sizeof(elf_rel)); } /****************************************************************************** * * * Paramètres : loading = chargement pour ELF à consulter. * * offset = décalage à retrouver idéalement. * * reloc = informations quant à la relocalisation. [OUT] * * * * Description : Recherche une relocalisation par son décalage. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_elf_loading_search_for_relocation(const GElfLoading *loading, const uint64_t *offset, elf_rel **reloc) { bool result; /* Validité à faire remonter */ void *found; /* Eventuel élément trouvé */ int compare_relocations(const uint64_t *off, const elf_rel *rel) { return sort_uint64_t(*off, ELF_REL(loading->format, *rel, r_offset)); } found = bsearch(offset, loading->relocs, loading->rel_count, sizeof(elf_rel), (__compar_fn_t)compare_relocations); result = (found != NULL); if (result) *reloc = (elf_rel *)found; return result; } /****************************************************************************** * * * Paramètres : loading = chargement pour ELF à poursuivre. * * index = indice du symbole concerné. * * * * Description : Construit la désignation adaptée à un symbole importé. * * * * Retour : Nouvelle étiquette constituée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ char *g_elf_loading_build_plt_name(const GElfLoading *loading, uint64_t index) { char *result; /* Désignation à retourner */ GElfFormat *format; /* Format plus accessible */ phys_t offset; /* Tête de lecture brute */ elf_sym sym; /* Symbole aux infos visées */ bool status; /* Bilan de récupération */ uint32_t name; /* Indice du nom du symbole */ vmpa2t pos; /* Position de lecture */ const GBinContent *content; /* Contenu binaire à lire */ const char *prefix; /* Première partie de nom */ format = loading->format; offset = loading->sym_start + index * ELF_SIZEOF_SYM(format); status = read_elf_symbol(format, &offset, &sym); if (!status) result = NULL; else { name = ELF_SYM(format, sym, st_name); offset = loading->str_start + name; init_vmpa(&pos, offset, VMPA_NO_VIRTUAL); content = G_BIN_FORMAT(format)->content; prefix = (const char *)g_binary_content_get_raw_access(content, &pos, 1); if (prefix != NULL && prefix[0] == '\0') result = NULL; else { result = strdup(prefix); result = stradd(result, "@plt"); } } return result; } /****************************************************************************** * * * Paramètres : loading = chargement pour ELF à mener. * * content = gestionnaire de contenu utilisé. [OUT] * * first = première position traitée par la tâche. [OUT] * * offset = décalage pour les données. [OUT] * * final = première position dans les données à exclure. [OUT]* * * * Description : Donne les informations utiles à la recherche de chaînes. * * * * Retour : Données brutes à analyser. * * * * Remarques : - * * * ******************************************************************************/ const bin_t *g_elf_loading_get_info_for_strings(const GElfLoading *loading, GBinContent **content, phys_t *first, phys_t *offset, phys_t *final) { const bin_t *result; /* Données à communiquer */ result = loading->data; *content = loading->content; *first = loading->begin; *offset = loading->global_start; *final = loading->global_end; return result; } /****************************************************************************** * * * Paramètres : loading = chargement pour ELF à poursuivre. * * iter = point de départ dans la zone de données traitée. * * pos = emplacement construit à la demande. [OUT] * * * * Description : Détermine l'adresse de départ d'une chaîne avec une position.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_elf_loading_compute_string_address(const GElfLoading *loading, const phys_t *iter, vmpa2t *pos) { init_vmpa(pos, *iter, loading->global_addr + (*iter - loading->global_start)); }