/* Chrysalide - Outil d'analyse de fichiers binaires * format.c - support des différents formats binaires * * Copyright (C) 2009-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 Foobar. If not, see . */ #include "format.h" #include #include #include #include #include "format-int.h" #include "preload.h" #include "dex/dex.h" #include "dwarf/dwarf.h" #include "elf/elf.h" #include "java/java.h" #include "pe/pe.h" #include "../arch/processor.h" #include "../common/sort.h" #include "../gui/panels/log.h" #include "../plugins/pglist.h" /* Initialise la classe des formats binaires génériques. */ static void g_binary_format_class_init(GBinFormatClass *); /* Initialise une instance de format binaire générique. */ static void g_binary_format_init(GBinFormat *); /* Retire un symbole de la collection du format binaire. */ static void _g_binary_format_remove_symbol(GBinFormat *, size_t); /* Supprime les éventuels doublons au sein des symboles. */ static void g_binary_format_delete_duplicated_symbols(GBinFormat *); /* Recherche le symbole associé à une adresse. */ static bool _g_binary_format_find_symbol(const GBinFormat *, const vmpa2t *, __compar_fn_t, size_t *, GBinSymbol **); /* Indique le type défini pour un format binaire générique. */ G_DEFINE_TYPE(GBinFormat, g_binary_format, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des formats binaires génériques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_format_class_init(GBinFormatClass *klass) { } /****************************************************************************** * * * Paramètres : format = instance à initialiser. * * * * Description : Initialise une instance de format binaire générique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_format_init(GBinFormat *format) { g_rw_lock_init(&format->pt_lock); g_rw_lock_init(&format->syms_lock); } /****************************************************************************** * * * Paramètres : format = instance à traiter. * * status = barre de statut à tenir informée. * * * * Description : Effectue les ultimes opérations de chargement d'un binaire. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_complete_loading(GBinFormat *format, GtkStatusStack *status) { handle_binary_format(PGA_FORMAT_LOADER_LAST, format, status); g_binary_format_delete_duplicated_symbols(format); return true; } /* FIXME : g_rw_lock_clear(&format->syms_lock);*/ /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * content = contenu binaire à parcourir. * * * * Description : Définit le contenu binaire à analyser. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_format_set_content(GBinFormat *format, GBinContent *content) { assert(format->content == NULL); g_object_ref(G_OBJECT(content)); format->content = content; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * * * Description : Fournit une référence vers le contenu binaire analysé. * * * * Retour : Gestionnaire de contenu binaire en place. * * * * Remarques : - * * * ******************************************************************************/ GBinContent *g_binary_format_get_content(const GBinFormat *format) { g_object_ref(G_OBJECT(format->content)); return format->content; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * * * Description : Indique le boutisme employé par le format binaire analysé. * * * * Retour : Boutisme associé au format. * * * * Remarques : - * * * ******************************************************************************/ SourceEndian g_binary_format_get_endianness(const GBinFormat *format) { return G_BIN_FORMAT_GET_CLASS(format)->get_endian(format); } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * pt = point de l'espace mémoire à considérer. * * entry = nature du point fourni. * * * * Description : Enregistre une adresse comme début d'une zone de code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_format_register_code_point(GBinFormat *format, virt_t pt, bool entry) { g_rw_lock_writer_lock(&format->pt_lock); if (entry) { format->entry_points = (virt_t *)realloc(format->entry_points, ++format->ep_count * sizeof(virt_t)); format->entry_points[format->ep_count - 1] = pt; } else { if (format->xp_count == format->xp_allocated) { format->xp_allocated += EXTRA_POINT_BLOCK; format->extra_points = (virt_t *)realloc(format->extra_points, format->xp_allocated * sizeof(virt_t)); } format->extra_points[format->xp_count++] = pt; } g_rw_lock_writer_unlock(&format->pt_lock); } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * ctx = contexte de désassemblage à préparer. * * status = barre de statut à tenir informée. * * * * Description : Intègre dans un contexte les informations tirées d'un format.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_format_preload_disassembling_context(GBinFormat *format, GProcContext *ctx, GtkStatusStack *status) { preload_binary_format(PGA_FORMAT_PRELOAD, format, G_PRELOAD_INFO(ctx), status); } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * ctx = contexte de désassemblage à préparer. * * status = barre de statut à tenir informée. * * * * Description : Définit les points de départ d'un contexte de désassemblage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_format_activate_disassembling_context(GBinFormat *format, GProcContext *ctx, GtkStatusStack *status) { size_t i; /* Boucle de parcours */ g_rw_lock_reader_lock(&format->pt_lock); for (i = 0; i < format->ep_count; i++) g_proc_context_push_drop_point(ctx, DPL_ENTRY_POINT, format->entry_points[i]); for (i = 0; i < format->xp_count; i++) g_proc_context_push_drop_point(ctx, DPL_SYMBOL, format->extra_points[i]); g_rw_lock_reader_unlock(&format->pt_lock); } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * symbol = symbole à ajouter à la liste. * * * * Description : Ajoute un symbole à la collection du format binaire. * * * * Retour : true si le symbole était bien localisé et a été inséré. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol) { bool result; /* Statut d'ajout à retourner */ #ifndef NDEBUG const mrange_t *range; /* Couverture du symbole */ const vmpa2t *addr; /* Emplacement du symbole */ #endif size_t index; /* Indice du point d'insertion */ GBinRoutine *routine; /* Nouvelle routine à insérer */ /** * Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol() * fassent bien leur office, il faut que les symboles soient triés. * * Cependant, les localisations à satisfaire lors d'une recherche recontrent * un problème si les positions physiques ne sont pas renseignées. En effet * les adresses virtuelles en sont potentiellement décorrélées (c'est le cas * avec le format ELF par exemple, où les zones en mémoire ne suivent pas le * même ordre que les segments du binaire). * * Comme les comparaisons entre localisations se réalisent sur les éléments * renseignés communs, à commencer par la position physique si c'est possible, * une localisation s'appuyant uniquement sur une adresse virtuelle va être * analysée suivant une liste non triée d'adresses virtuelles. * * On corrige donc le tir si besoin est en forçant la comparaison via les * positions physiques. */ #ifndef NDEBUG range = g_binary_symbol_get_range(symbol); addr = get_mrange_addr(range); assert(has_phys_addr(addr)); #endif g_rw_lock_writer_lock(&format->syms_lock); /** * Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution * amènent à la création d'un même symbole. * * Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps) * pour faire une vérification avant construction puis ajout, on préfère limiter * l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la * construction du symbole dans les cas peu fréquents où le symbole était déjà en place. */ result = bsearch_index(&symbol, format->symbols, format->symbols_count, sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index); if (!result) { format->symbols = _qinsert(format->symbols, &format->symbols_count, sizeof(GBinSymbol *), &symbol, index); result = true; switch (g_binary_symbol_get_target_type(symbol)) { case STP_ROUTINE: case STP_ENTRY_POINT: routine = g_binary_symbol_get_routine(symbol); format->routines = qinsert(format->routines, &format->routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_compare, &routine); break; default: break; } } else g_object_unref(G_OBJECT(symbol)); g_rw_lock_writer_unlock(&format->syms_lock); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * index = indice du symbole à retirer de la liste. * * * * Description : Retire un symbole de la collection du format binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void _g_binary_format_remove_symbol(GBinFormat *format, size_t index) { GBinSymbol *symbol; /* Symbole visé par l'opération*/ GBinRoutine *routine; /* Eventuelle routine associée */ size_t i; /* Boucle de parcours */ /** * TODO : envoyer un signal pour avertir les opérandes concernées. * TODO : vérifier les conditions d'accès (verrou). */ symbol = format->symbols[index]; switch (g_binary_symbol_get_target_type(symbol)) { case STP_ROUTINE: case STP_ENTRY_POINT: routine = g_binary_symbol_get_routine(symbol); for (i = 0; i < format->routines_count; i++) if (format->routines[i] == routine) break; assert(i < format->routines_count); if ((i + 1) < format->routines_count) memmove(&format->routines[i], &format->routines[i + 1], (format->routines_count - i - 1) * sizeof(GBinRoutine *)); format->routines = (GBinRoutine **)realloc(format->routines, --format->routines_count * sizeof(GBinRoutine *)); break; default: break; } assert(index < format->symbols_count); if ((index + 1) < format->symbols_count) memmove(&format->symbols[index], &format->symbols[index + 1], (format->symbols_count - index - 1) * sizeof(GBinSymbol *)); format->symbols = (GBinSymbol **)realloc(format->symbols, --format->symbols_count * sizeof(GBinSymbol *)); } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * symbol = symbole à retirer de la liste. * * * * Description : Retire un symbole de la collection du format binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_format_remove_symbol(GBinFormat *format, GBinSymbol *symbol) { size_t i; /* Boucle de parcours */ for (i = 0; i < format->symbols_count; i++) if (format->symbols[i] == symbol) break; _g_binary_format_remove_symbol(format, i); } /****************************************************************************** * * * Paramètres : format = informations chargées à corriger si besoin est. * * * * Description : Supprime les éventuels doublons au sein des symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_format_delete_duplicated_symbols(GBinFormat *format) { size_t i; /* Boucle de parcours */ const mrange_t *range; /* Emplacement à consulter */ mrange_t last; /* Dernière localisation vue */ size_t index; /* Indice de suppression */ g_rw_lock_writer_lock(&format->syms_lock); if (format->symbols_count > 1) { range = g_binary_symbol_get_range(format->symbols[0]); copy_mrange(&last, range); } for (i = 1; i < format->symbols_count; i++) { range = g_binary_symbol_get_range(format->symbols[i]); if (cmp_vmpa(get_mrange_addr(&last), get_mrange_addr(range)) == 0) { /* TODO : établir une meilleur comparaison pour trouver le bon candidat */ /* TODO : (éviter de) traiter selon le type aussi */ index = (get_mrange_length(&last) == 0 ? i - 1 : i); /* printf("DELETE @ %zu / 0x%08x '%s'\n", index, (unsigned int)last.addr.virtual, g_binary_symbol_get_label(format->symbols[index])); */ _g_binary_format_remove_symbol(format, index); i--; } copy_mrange(&last, range); } g_rw_lock_writer_unlock(&format->syms_lock); } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * count = taille du tableau créé. [OUT] * * * * Description : Fournit la liste de tous les symboles détectés. * * * * Retour : Tableau créé ou NULL si aucun symbole trouvé. * * * * Remarques : - * * * ******************************************************************************/ GBinSymbol **g_binary_format_get_symbols(const GBinFormat *format, size_t *count) { *count = format->symbols_count; return format->symbols; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse liée à la chaîne à traiter. * * length = taille de la chaîne à représenter. * * * * Description : Construit une désignation pour chaîne de caractères. * * * * Retour : Chaîne de caractères à libérer de la mémoire. * * * * Remarques : - * * * ******************************************************************************/ char *create_string_label(GBinFormat *format, const vmpa2t *addr, size_t length) { char *result; /* Nouvelle chaîne à retourner */ vmpa2t pos; /* Tête de lecture modifiable */ const bin_t *base; /* Contenu complet et original */ unsigned int wc; /* Nombre de mots rencontrés */ size_t iter; /* Tête d'écriture de recopie */ size_t i; /* Boucle de parcours */ bool empty; /* Base de l'étiquette vide ? */ GBinSymbol *found; /* Symbole similaire trouvé */ VMPA_BUFFER(last_sfx); /* Dernier suffixe à intégrer */ copy_vmpa(&pos, addr); base = g_binary_content_get_raw_access(format->content, &pos, length); if (base == NULL) return NULL; result = (char *)calloc(length + 5 + VMPA_MAX_LEN + 1, sizeof(char)); wc = 0; iter = 0; for (i = 0; i < length; i++) { if (isalnum(base[i])) result[iter++] = tolower(base[i]); else if (iter > 0) { if (result[iter - 1] != '_') wc++; if (wc == 3) break; if (result[iter - 1] != '_') result[iter++] = '_'; } } /* Détermination du suffixe suffisant */ empty = (iter == 0); if (iter > 0 && result[iter - 1] != '_') result[iter++] = '_'; strcpy(&result[iter], "str"); iter += 3; found = NULL; if (empty || g_binary_format_find_symbol_by_label(format, result, &found)) { if (found != NULL) g_object_unref(G_OBJECT(found)); if (iter > 0 && result[iter - 1] != '_') result[iter++] = '_'; assert(has_phys_addr(addr) || has_virt_addr(addr)); /* TODO : use_phy_instead_of_virt */ if (has_virt_addr(addr)) vmpa2_virt_to_string(addr, MDS_UNDEFINED, last_sfx, NULL); else vmpa2_phys_to_string(addr, MDS_UNDEFINED, last_sfx, NULL); strcpy(&result[iter], last_sfx); } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * label = étiquette à retrouver lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole correspondant à une étiquette. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ const char *cur_lbl; /* Etiquette courante */ result = false; g_rw_lock_reader_lock(&format->syms_lock); for (i = 0; i < format->symbols_count && !result; i++) { cur_lbl = g_binary_symbol_get_label(format->symbols[i]); if (cur_lbl == NULL) continue; if (strcmp(label, cur_lbl) == 0) { *symbol = format->symbols[i]; g_object_ref(G_OBJECT(*symbol)); result = true; } } g_rw_lock_reader_unlock(&format->syms_lock); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * fn = méthode de comparaison des symboles. * * index = indice de l'éventuel symbole trouvé ou NULL. [OUT] * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole associé à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool _g_binary_format_find_symbol(const GBinFormat *format, const vmpa2t *addr, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ void *found; /* Résultat de recherches */ /** * Pour ce qui est des justifications quant à la vérification suivante, * se référer aux commentaires placés dans g_binary_format_add_symbol(). */ assert(has_phys_addr(addr)); found = bsearch(addr, format->symbols, format->symbols_count, sizeof(GBinSymbol *), fn); if (found != NULL) { if (index != NULL) *index = (GBinSymbol **)found - format->symbols; if (symbol != NULL) { *symbol = *(GBinSymbol **)found; g_object_ref(G_OBJECT(*symbol)); } result = true; } else { *symbol = NULL; result = false; } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole correspondant à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_find_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_vmpa(addr, get_mrange_addr(range)); } g_rw_lock_reader_lock(&format->syms_lock); result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); g_rw_lock_reader_unlock(&format->syms_lock); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole contenant une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_find_symbol_for(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_mrange_with_vmpa(range, addr); } g_rw_lock_reader_lock(&format->syms_lock); result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); g_rw_lock_reader_unlock(&format->syms_lock); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole suivant celui lié à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ size_t index; /* Indice à considérer */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_mrange_with_vmpa(range, addr); } g_rw_lock_reader_lock(&format->syms_lock); result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, &index, NULL); if (result && (index + 1) < format->symbols_count) { *symbol = format->symbols[index + 1]; g_object_ref(G_OBJECT(*symbol)); } else { *symbol = NULL; result = false; } g_rw_lock_reader_unlock(&format->syms_lock); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * strict = indication de tolérance acceptée. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * diff = décallage entre l'adresse et le symbole. [OUT] * * * * Description : Recherche le symbole correspondant à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_format_resolve_symbol(GBinFormat *format, const vmpa2t *addr, bool strict, GBinSymbol **symbol, phys_t *diff) { bool result; /* Bilan à retourner */ const mrange_t *range; /* Espace mémoire parcouru */ if (strict) result = g_binary_format_find_symbol_at(format, addr, symbol); else result = g_binary_format_find_symbol_for(format, addr, symbol); if (result) { range = g_binary_symbol_get_range(*symbol); *diff = compute_vmpa_diff(get_mrange_addr(range), addr); assert(!strict || *diff == 0); } else *diff = 0; return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * count = taille du tableau créé. [OUT] * * * * Description : Fournit le prototype de toutes les routines détectées. * * * * Retour : Tableau créé ou NULL si aucun symbole de routine trouvé. * * * * Remarques : - * * * ******************************************************************************/ GBinRoutine **g_binary_format_get_routines(const GBinFormat *format, size_t *count) { *count = format->routines_count; return format->routines; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * count = taille de la liste retournée. [OUT] * * defsrc = fichier de code principal. [OUT] * * * * Description : Fournit la liste des fichiers source détectés. * * * * Retour : Liste de noms de fichier ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ const char * const *g_binary_format_get_source_files(const GBinFormat *format, size_t *count, size_t *defsrc) { *count = format->src_count; *defsrc = format->def_source; return format->src_files; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * buffer = tampon mis à disposition pour la sortie. * * filename = nom du fichier source à cibler. * * * * Description : Procède à la décompilation complète du format. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ #if 0 void g_binary_format_decompile(const GBinFormat *format, GCodeBuffer *buffer, const char *filename) { GBinRoutine **routines; size_t count; size_t i; GDecInstruction *instr; if (format->decompile != NULL) format->decompile(format, buffer, filename); routines = g_binary_format_get_routines(format, &count); for (i = 0; i < count; i++) { //printf(" -- %s --\n", g_binary_routine_get_name(routines[i])); //if (strcmp("cryptself", g_binary_routine_get_name(routines[i])) == 0) { instr = g_binary_routine_get_decomp_instructions(routines[i]); if (instr == NULL) continue; //g_dec_instruction_print(instr, buffer, NULL, g_java_output_new()); } } } #endif