/* Chrysalide - Outil d'analyse de fichiers binaires * area.c - définition et manipulation des aires à désassembler * * Copyright (C) 2014 Cyrille Bagard * * This file is part of Chrysalide. * * OpenIDA 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. * * OpenIDA 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 "area.h" #include /* Zone mémoire bien bornée */ typedef struct _mem_area { mrange_t range; /* Couverture de la zone */ unsigned long *processed; /* Octets traités dans la zone */ GArchInstruction **instructions; /* Instructions en place */ bool has_sym; /* Représentation via symbole ?*/ union { bool exec; /* Zone exécutable ? */ GBinSymbol *symbol; /* Symbole associé à la zone */ }; } mem_area; /* Initialise une aire de données à partir d'une adresse donnée. */ static void init_mem_area_from_addr(mem_area *, const vmpa2t *, phys_t); /* Initialise une aire de données à partir d'un espace donné. */ static void init_mem_area_from_range(mem_area *, const mrange_t *); /* Initialise une aire de données à partir d'un morceau donné. */ static void init_mem_area_from_bigger_area(mem_area *, const vmpa2t *, phys_t, const mem_area *); /* Copie certaines propriétés d'une aire à une autre. */ static void copy_mem_area_properties(mem_area *, const mem_area *); /* Libère d'une aire de données les ressources allouées. */ static void fini_mem_area(mem_area *); /* Indique si une zone donnée est intégralement vierge ou non. */ static bool is_range_blank_in_mem_area(mem_area *, phys_t, phys_t, GArchInstruction *); /* Marque une série d'octets comme ayant été traités. */ static bool mark_range_in_mem_area_as_processed(mem_area *, phys_t, phys_t, GArchInstruction *); /* S'assure de la présence d'un début de routine à un point. */ static void update_address_as_routine(GBinFormat *, const vmpa2t *); /* Procède au désassemblage d'un contenu binaire non exécutable. */ static void load_data_from_mem_area(mem_area *, mem_area *, size_t, const GLoadedBinary *, GProcContext *, const vmpa2t *, status_blob_info *); /* Rassemble les instructions conservées dans une zone donnée. */ static GArchInstruction *get_instructions_from_mem_area(const mem_area *); /////////////////////////////// /* Insère un symbole dans un découpage en aires. */ static bool insert_extra_symbol_into_mem_areas(mem_area **, size_t *, GBinSymbol *, size_t); /* Manipule la cartographie des octets traités d'une zone. */ typedef bool (* visit_bytes_map_fc) (mem_area *, phys_t, phys_t, GArchInstruction *); /* Manipule la cartographie des octets d'aires de données. */ static bool handle_bytes_map_in_mem_area(mem_area *, size_t, const mrange_t *, GArchInstruction *, visit_bytes_map_fc); /* Indique si une zone donnée est intégralement vierge ou non. */ static bool is_range_blank_in_mem_areas(mem_area *, size_t, const mrange_t *); /* Marque une série d'octets comme ayant été traités. */ static bool mark_range_in_mem_areas_as_processed(mem_area *, size_t, GArchInstruction *); /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à initialiser. * * addr = adresse de départ de l'espace à mettre en place. * * len = longueur de l'espace à créer. * * * * Description : Initialise une aire de données à partir d'une adresse donnée.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void init_mem_area_from_addr(mem_area *area, const vmpa2t *addr, phys_t len) { size_t requested; /* Nombre de mots à allouer */ init_mrange(&area->range, addr, len); requested = len / sizeof(unsigned long); if (len % sizeof(unsigned long) != 0) requested++; area->processed = (unsigned long *)calloc(requested, sizeof(unsigned long)); area->instructions = (GArchInstruction **)calloc(len, sizeof(GArchInstruction *)); } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à initialiser. * * range = espace limitant à associer à l'aire de données. * * * * Description : Initialise une aire de données à partir d'un espace donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void init_mem_area_from_range(mem_area *area, const mrange_t *range) { phys_t len; /* Taille de la zone courante */ size_t requested; /* Nombre de mots à allouer */ copy_mrange(&area->range, range); len = get_mrange_length(range); requested = len / sizeof(unsigned long); if (len % sizeof(unsigned long) != 0) requested++; area->processed = (unsigned long *)calloc(requested, sizeof(unsigned long)); area->instructions = (GArchInstruction **)calloc(len, sizeof(GArchInstruction *)); } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à initialiser. * * addr = adresse de départ de l'espace à mettre en place. * * len = longueur de l'espace à créer. * * ref = aire de référence avant découpage. * * * * Description : Initialise une aire de données à partir d'un morceau donné. * * * * Retour : - * * * * Remarques : On considère que la zone de destination est inclue dans * * celle de référence. * * * ******************************************************************************/ static void init_mem_area_from_bigger_area(mem_area *area, const vmpa2t *addr, phys_t len, const mem_area *ref) { phys_t start; /* Point de départ de la copie */ phys_t i; /* Boucle de parcours */ size_t index; /* Cellule de tableau visée #1 */ unsigned int remaining; /* Nombre de bits restants #1 */ size_t ref_index; /* Cellule de tableau visée #2 */ unsigned int ref_remaining; /* Nombre de bits restants #2 */ printf(" INIT_FROM (0x%08x / 0x%08x | 0x%x) : area (0x%08x / 0x%08x) len = 0x%x\n", (unsigned int)ref->range.addr.physical, (unsigned int)ref->range.addr.virtual, (unsigned int)ref->range.length, (unsigned int)addr->physical, (unsigned int)addr->virtual, (unsigned int)len); init_mem_area_from_addr(area, addr, len); assert(mrange_contains_mrange(&ref->range, &area->range)); start = compute_vmpa_diff(get_mrange_addr(&ref->range), get_mrange_addr(&area->range)); for (i = 0; i < len; i++) { index = i / (sizeof(unsigned long) * 8); remaining = i % (sizeof(unsigned long) * 8); ref_index = (start + i) / (sizeof(unsigned long) * 8); ref_remaining = (start + i) % (sizeof(unsigned long) * 8); if (ref->processed[ref_index] & (1ul << ref_remaining)) area->processed[index] |= (1ul << remaining); area->instructions[i] = ref->instructions[start + i]; } } /****************************************************************************** * * * Paramètres : dest = aire délimitée représentant des données. * * src = aire délimitée contenant les informations à copier. * * * * Description : Copie certaines propriétés d'une aire à une autre. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void copy_mem_area_properties(mem_area *dest, const mem_area *src) { dest->has_sym = src->has_sym; if (src->has_sym) dest->symbol = src->symbol; else dest->exec = src->exec; } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à nettoyer en mémoire. * * * * Description : Libère d'une aire de données les ressources allouées. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void fini_mem_area(mem_area *area) { free(area->processed); #if 0 if (area->has_sym) g_object_unref(area->symbol); /* FIXME ?! */ #endif } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * * start = début de la zone à manipuler. * * len = taille de cette même aire de données. * * instr = instruction à mémoriser pour la suite. * * * * Description : Indique si une zone donnée est intégralement vierge ou non. * * * * Retour : true si l'aire visée n'a jamais été traitée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool is_range_blank_in_mem_area(mem_area *area, phys_t start, phys_t len, GArchInstruction *instr) { bool result; /* Bilan à renvoyer */ phys_t max; /* Point d'arrêt de la boucle */ phys_t i; /* Boucle de parcours */ size_t index; /* Cellule de tableau visée */ unsigned int remaining; /* Nombre de bits restants */ if (area->has_sym) return false; max = start + len; assert(max <= get_mrange_length(&area->range)); result = true; for (i = start; i < max && result; i++) { index = i / (sizeof(unsigned long) * 8); remaining = i % (sizeof(unsigned long) * 8); result &= ((area->processed[index] & (1ul << remaining)) == 0); } return result; } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * * start = début de la zone à manipuler. * * len = taille de cette même aire de données. * * instr = instruction à mémoriser pour la suite. * * * * Description : Marque une série d'octets comme ayant été traités. * * * * Retour : true. * * * * Remarques : - * * * ******************************************************************************/ static bool mark_range_in_mem_area_as_processed(mem_area *area, phys_t start, phys_t len, GArchInstruction *instr) { phys_t max; /* Point d'arrêt de la boucle */ phys_t i; /* Boucle de parcours */ size_t index; /* Cellule de tableau visée */ unsigned int remaining; /* Nombre de bits restants */ max = start + len; assert(max <= get_mrange_length(&area->range)); for (i = start; i < max; i++) { index = i / (sizeof(unsigned long) * 8); remaining = i % (sizeof(unsigned long) * 8); //assert((area->processed[index] & (1ul << remaining)) == 0); area->processed[index] |= (1ul << remaining); } //assert(area->instructions[start] == NULL); area->instructions[start] = instr; return true; } /****************************************************************************** * * * Paramètres : format = format binaire en cours de traitement. * * addr = adresse d'une instruction présentée comme première. * * * * Description : S'assure de la présence d'un début de routine à un point. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_address_as_routine(GBinFormat *format, const vmpa2t *addr) { GBinSymbol *symbol; /* Symbole présent ou créé */ phys_t offset; /* Décallage trouvé */ bool found; /* Détection de symbole */ SymbolType sym_type; /* Type de symbole en place */ bool wrong_type; /* Analyse plus fine de ce type*/ mrange_t range; /* Etendue du symbole à créer */ VMPA_BUFFER(loc); /* Traduction de l'adresse */ char name[5 + VMPA_MAX_LEN]; /* Nom de symbole nouveau */ GBinRoutine *routine; /* Nouvelle routine trouvée */ found = g_binary_format_resolve_symbol(format, addr, &symbol, &offset); if (found) { sym_type = g_binary_symbol_get_target_type(symbol); wrong_type = (sym_type != STP_ROUTINE && sym_type != STP_ENTRY_POINT); } if (!found || (found && offset == 0 && wrong_type)) { init_mrange(&range, addr, 0); vmpa2_virt_to_string(addr, MDS_UNDEFINED, loc, NULL); snprintf(name, sizeof(name), "sub_%s", loc + 2); routine = g_binary_routine_new(); g_binary_routine_set_name(routine, strdup(name)); g_binary_routine_set_range(routine, &range); if (!found) { symbol = g_binary_symbol_new(STP_ROUTINE); g_binary_symbol_attach_routine(symbol, routine); g_binary_format_add_symbol(G_BIN_FORMAT(format), symbol); } else _g_binary_symbol_attach_routine(symbol, routine, STP_ROUTINE); } } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * * list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * index = indice de l'aire à considérer pendant l'opération. * * binary = représentation de binaire chargé. * * ctx = contexte offert en soutien à un désassemblage. * * start = démarrage de l'exécution au sein de la zone. * * info = indications quant à la progression à afficher. * * * * Description : Procède au désassemblage d'un contenu binaire exécutable. * * * * Retour : Recalcul de rafraîchissement de l'aire de travail requis ? * * * * Remarques : - * * * ******************************************************************************/ bool load_code_from_mem_area(mem_area **list, size_t *count, size_t *index, const GLoadedBinary *binary, GProcContext *ctx, const vmpa2t *start, status_blob_info *info) { bool result; /* Besoin à retourner */ mem_area *area; /* Zone de désassemblage */ GBinFormat *format; /* Format du fichier binaire */ GArchProcessor *proc; /* Architecture du binaire */ GBinContent *content; /* Données binaires à lire */ phys_t diff; /* Volume de données traité */ phys_t alen; /* Taille de l'aire utilisée */ phys_t i; /* Boucle de parcours */ vmpa2t pos; /* Boucle de parcours */ vmpa2t prev; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction décodée */ mrange_t range; /* Couverture de l'instruction */ vmpa2t sym_addr; /* Adresse de nouveau symbole */ bool has_new_sym; /* Statut d'un dépilement */ GBinSymbol *symbol; /* Symbole créé en parallèle */ bool refresh; /* Besoin de rafraîchissement */ result = false; /* On cherche à obtenir l'assurance que le traitement n'a jamais été fait */ init_mrange(&range, start, 1); if (!is_range_blank_in_mem_areas(*list, *count, &range)) return false; /* Récupération des informations de base */ format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); proc = g_loaded_binary_get_processor(binary); content = g_binary_format_get_content(format); /* TODO : unref */ area = (*list) + *index; diff = compute_vmpa_diff(get_mrange_addr(&area->range), start); alen = get_mrange_length(&area->range); /** * On copie la position courante à partir de l'aire plutôt que du * point de départ car cette première est potentiellement plus complète. */ copy_vmpa(&pos, get_mrange_addr(&area->range)); advance_vmpa(&pos, diff); //printf(" [%p] CODE start @ %u (len=%u)\n", area, (unsigned int)diff, (unsigned int)alen); printf(" START @ 0x%08x\n", (unsigned int)get_virt_addr(&pos)); for (i = diff; i < alen; i += diff) { /* S'il y a eu un point d'entrée en milieu de zone, on s'arrête ! */ if (!is_range_blank_in_mem_area(area, i, 1, NULL)) break; /* Décodage d'une nouvelle instruction */ copy_vmpa(&prev, &pos); instr = g_arch_processor_disassemble(proc, ctx, content, &pos); /* TODO : valider que la taille de l'instruction obtenue ne dépasse pas la zone */ //printf(" @ 0x%08x -> %p\n", (uint32_t)get_virt_addr(&prev), instr); if (instr == NULL) break; /* Enregistrement des positions et adresses */ diff = compute_vmpa_diff(&prev, &pos); init_mrange(&range, &prev, diff); g_arch_instruction_set_range(instr, &range); /* Enregistrement d'un éventuel début de routine */ if (g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START) update_address_as_routine(format, &prev); /* Eventuel renvoi vers d'autres adresses */ g_arch_instruction_call_hook(instr, IPH_FETCH, proc, ctx, format); /* Progression dans les traitements */ mark_range_in_mem_areas_as_processed(*list, *count, instr); inc_progessive_status(info, diff); assert(!is_range_blank_in_mem_areas(*list, *count, &range)); /* Insertion des symboles découverts en parallèle */ for (has_new_sym = g_proc_context_pop_new_symbol_at(ctx, &sym_addr); has_new_sym; has_new_sym = g_proc_context_pop_new_symbol_at(ctx, &sym_addr)) { has_new_sym = g_binary_format_find_symbol_at(format, &sym_addr, &symbol); assert(has_new_sym); refresh = insert_extra_symbol_into_mem_areas(list, count, symbol, *index); result |= refresh; /** * Si une insertion a été réalisée dans la même zone que celle courante ou avant, * en l'absence d'indication sur les localisations exactes, le plus simple * est de tout recharger. */ if (refresh) { *index = find_memory_area_by_addr(*list, *count, &pos); area = (*list) + *index; diff = compute_vmpa_diff(get_mrange_addr(&area->range), &pos); alen = get_mrange_length(&area->range); i = 0; } /** * Si la zone modifiée se trouve bien après la zone courante, l'emplacement de la liste * peut potentiellement avoir évolué. Donc on ne recharge ici que le minimum. */ else area = (*list) + *index; } assert(!is_range_blank_in_mem_areas(*list, *count, &range)); if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT) printf("BREAK @ 0x%08x\n", (unsigned int)get_virt_addr(&prev)); //continue; /* Rupture du flot d'exécution ? */ if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT) break; } printf("\n"); g_object_unref(G_OBJECT(content)); g_object_unref(G_OBJECT(proc)); return result; } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * * list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * binary = représentation de binaire chargé. * * ctx = contexte offert en soutien à un désassemblage. * * start = démarrage de l'exécution au sein de la zone. * * info = indications quant à la progression à afficher. * * * * Description : Procède au désassemblage d'un contenu binaire non exécutable.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void load_data_from_mem_area(mem_area *area, mem_area *list, size_t count, const GLoadedBinary *binary, GProcContext *ctx, const vmpa2t *start, status_blob_info *info) { GBinFormat *format; /* Format du fichier binaire */ GBinContent *content; /* Données binaires à lire */ GArchProcessor *proc; /* Architecture du binaire */ SourceEndian endianness; /* Boutisme de cette machine */ phys_t diff; /* Volume de données traité */ phys_t alen; /* Taille de l'aire utilisée */ phys_t i; /* Boucle de parcours */ vmpa2t pos; /* Boucle de parcours */ vmpa2t prev; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction décodée */ mrange_t range; /* Couverture de l'instruction */ /* On cherche à obtenir l'assurance que le traitement n'a jamais été fait */ init_mrange(&range, start, 1); if (!is_range_blank_in_mem_areas(list, count, &range)) printf("DATA OK ! @ 0x%08x\n", (unsigned int)get_phy_addr(get_mrange_addr(&area->range))); if (!is_range_blank_in_mem_areas(list, count, &range)) return; /* Récupération des informations de base */ format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); content = g_binary_format_get_content(format); /* TODO g_object_unref(G_OBJECT(format)); */ proc = g_loaded_binary_get_processor(binary); endianness = g_arch_processor_get_endianness(proc); g_object_unref(G_OBJECT(proc)); diff = compute_vmpa_diff(get_mrange_addr(&area->range), start); alen = get_mrange_length(&area->range); /** * On copie la position courante à partir de l'aire plutôt que du * point de départ car cette première est potentiellement plus complète. */ copy_vmpa(&pos, get_mrange_addr(&area->range)); advance_vmpa(&pos, diff); /* printf(" [%p] DATA start @ 0x%08x -> %u (len=%u) => 0x%08x <-> 0x%08x\n", area, (unsigned int)get_phy_addr(&pos), (unsigned int)diff, (unsigned int)alen, (unsigned int)get_virt_addr(&pos), (unsigned int)(get_virt_addr(&pos) + alen)); */ for (i = diff; i < alen; i += diff) { /* Décodage d'une nouvelle instruction, sur mesure */ copy_vmpa(&prev, &pos); instr = NULL; /* if (instr == NULL && (i + 4) <= alen) { init_mrange(&range, &pos, 4); if (is_range_blank_in_mem_areas(list, count, &range)) instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, &pos, endianness); } */ if (instr == NULL && (i + 2) <= alen) { copy_vmpa(&pos, &prev); init_mrange(&range, &pos, 2); if (is_range_blank_in_mem_areas(list, count, &range)) instr = g_raw_instruction_new_array(content, MDS_16_BITS, 1, &pos, endianness); } if (instr == NULL/* && (i + 1) <= alen*/) { copy_vmpa(&pos, &prev); init_mrange(&range, &pos, 1); if (is_range_blank_in_mem_areas(list, count, &range)) instr = g_raw_instruction_new_array(content, MDS_8_BITS, 1, &pos, endianness); else { /** * On rencontre ici un morceau déjà traité. * On recherche donc la fin de cette partie à sauter, si elle existe. */ ////////////// return; for (i++; i < alen; i++) { advance_vmpa(&pos, 1); init_mrange(&range, &pos, 1); if (is_range_blank_in_mem_areas(list, count, &range)) break; } diff = 0; continue; } } assert(instr != NULL); /* Enregistrement des positions et adresses */ diff = compute_vmpa_diff(&prev, &pos); init_mrange(&range, &prev, diff); g_arch_instruction_set_range(instr, &range); /* Progression dans les traitements */ mark_range_in_mem_areas_as_processed(list, count, instr); inc_progessive_status(info, diff); assert(!is_range_blank_in_mem_areas(list, count, &range)); if (area->exec) break; } g_object_unref(G_OBJECT(content)); } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * * list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * index = indice de l'aire à considérer pendant l'opération. * * binary = représentation de binaire chargé. * * ctx = contexte offert en soutien à un désassemblage. * * info = indications quant à la progression à afficher. * * * * Description : S'assure qu'une aire contient toutes ses instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void fill_mem_area(mem_area **list, size_t *count, size_t *index, const GLoadedBinary *binary, GProcContext *ctx, status_blob_info *info) { mem_area *area; /* Zone de désassemblage */ phys_t len; /* Taille de la zone à remplir */ phys_t i; /* Boucle de parcours */ vmpa2t start; /* Adresse de départ de combles*/ bool refresh; /* Besoin de rafraîchissement */ area = (*list) + *index; /* Les symboles se doivent d'être indépendants ! */ if (area->has_sym) return; len = get_mrange_length(&area->range); for (i = 0; i < len; i++) { if (is_range_blank_in_mem_area(area, i, 1, NULL)) { copy_vmpa(&start, get_mrange_addr(&area->range)); advance_vmpa(&start, i); if (area->exec && get_virt_addr(&start) % 2 == 0) { refresh = load_code_from_mem_area(list, count, index, binary, ctx, &start, info); /** * Mêmes commentaires que dans load_code_from_mem_area() : * - en cas de décalage avéré, on recharge tout. * - sinon, la liste a potentiellement été déplacée, donc on recharge un minimum. */ if (refresh) { area = (*list) + *index; len = get_mrange_length(&area->range); i = compute_vmpa_diff(get_mrange_addr(&area->range), &start); } else area = (*list) + *index; } if (is_range_blank_in_mem_area(area, i, 1, NULL)) load_data_from_mem_area(area, *list, *count, binary, ctx, &start, info); } assert(!is_range_blank_in_mem_area(area, i, 1, NULL)); } } /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * * * * Description : Rassemble les instructions conservées dans une zone donnée. * * * * Retour : Liste d'instructions prêtes à emploi. * * * * Remarques : - * * * ******************************************************************************/ static GArchInstruction *get_instructions_from_mem_area(const mem_area *area) { GArchInstruction *result; /* Liste d'instr. à renvoyer */ phys_t len; /* Nombre d'instructions au max*/ phys_t i; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction décodée */ result = NULL; if (area->has_sym) switch (g_binary_symbol_get_target_type(area->symbol)) { case STP_DATA: case STP_RO_STRING: result = g_binary_symbol_get_instruction(area->symbol); g_object_ref(G_OBJECT(result)); break; case STP_ROUTINE: assert(false); //instr = load_code_binary(binary, start, end, statusbar, id); // + fill break; default: assert(false); break; } else { len = get_mrange_length(&area->range); for (i = 0; i < len; i++) { instr = area->instructions[i]; if (instr != NULL) { g_object_ref(G_OBJECT(instr)); g_arch_instruction_add_to_list(&result, instr); } } } return result; } /****************************************************************************** * * * Paramètres : format = format d'un exécutable à consulter. * * bin_length = quantité d'octets à traiter au total. * * count = nombre de zones mises en place. [OUT] * * * * Description : Détermine une liste de zones contigües à traiter. * * * * Retour : Liste de zones mémoire à libérer après usage. * * * * Remarques : - * * * ******************************************************************************/ mem_area *compute_memory_areas(GExeFormat *format, phys_t bin_length, size_t *count) { mem_area *result; /* Liste à renvoyer */ mrange_t *exe_ranges; /* Liste de zones exécutables */ size_t exe_count; /* Nombre de ces zones */ GBinSymbol **symbols; /* Symboles à représenter */ size_t sym_count; /* Qté de symboles présents */ vmpa2t *last; /* Dernière bordure rencontrée */ size_t i; /* Boucle de parcours #1 */ vmpa2t *border; /* Nouvelle bordure rencontrée */ mem_area *area; /* Zone avec valeurs à éditer */ vmpa2t tmp; /* Stockage temporaire */ GBinPortion **portions; /* Morceaux d'encadrement */ size_t portions_count; /* Taille de cette liste */ const vmpa2t *portion_start; /* Point de départ de portion */ size_t j; /* Boucle de parcours #2 */ SymbolType type; /* Nature d'un symbole */ const mrange_t *range; /* Couverture d'un symbole */ const vmpa2t *start; /* Point de départ du symbole */ phys_t length; /* Taille de ce même symbole */ bool included; /* Inclusion dans une zone ? */ mem_area orig; /* Copie de la zone réduite */ phys_t old_length; /* Taille de zone originelle */ phys_t new_length; /* Nouvelle taille déterminée */ size_t next; /* Indice de zone suivante */ result = NULL; *count = 0; /** * Le parcours n'est valide que si les listes, symboles et zones exécutables, * sont triées ! */ exe_ranges = g_exe_format_get_x_ranges(format, &exe_count); symbols = g_binary_format_get_symbols(G_BIN_FORMAT(format), &sym_count); for (i = 0; i < exe_count; i++) { printf(" (init) AREA %zu :: 0x%08x + %u\n", i, (unsigned int)get_phy_addr(get_mrange_addr(&exe_ranges[i])), (unsigned int)get_mrange_length(&exe_ranges[i])); } printf("----------------\n"); /* Première étape : on comble les trous ! */ last = make_vmpa(0, VMPA_NO_VIRTUAL); for (i = 0; i < exe_count; i++) { border = get_mrange_addr(&exe_ranges[i]); /* Zone tampon à constituer */ if (cmp_vmpa_by_phy(last, border) < 0) { result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area)); area = &result[*count - 1]; init_mem_area_from_addr(area, last, compute_vmpa_diff(last, border)); area->has_sym = false; area->exec = false; } /* Insertion d'une zone exécutable déjà définie */ result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area)); area = &result[*count - 1]; init_mem_area_from_range(area, &exe_ranges[i]); area->has_sym = false; area->exec = true; /* Avancée du curseur */ copy_vmpa(last, border); advance_vmpa(last, get_mrange_length(&exe_ranges[i])); } delete_vmpa(last); /* Extension finale complémentaire ? */ area = &result[*count - 1]; copy_vmpa(&tmp, get_mrange_addr(&area->range)); advance_vmpa(&tmp, get_mrange_length(&area->range)); if (get_phy_addr(&tmp) < bin_length) { result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area)); area = &result[*count - 1]; init_mem_area_from_addr(area, &tmp, bin_length - get_phy_addr(&tmp)); area->has_sym = false; area->exec = false; } /* Seconde étape : on s'assure du découpage autour des portions pour respecter l'alignement */ for (i = 0; i < *count; i++) { printf(" (fini) AREA %zu :: 0x%08x + %u\n", i, (unsigned int)get_phy_addr(get_mrange_addr(&result[i].range)), (unsigned int)get_mrange_length(&result[i].range)); } printf("--------------------\n"); portions = g_exe_format_get_portions_at_level(format, -1, &portions_count); for (i = 1; i < portions_count; i++) { portion_start = get_mrange_addr(g_binary_portion_get_range(portions[i])); for (j = 0; j < *count; j++) { area = &result[j]; if (!mrange_contains_addr(&area->range, portion_start)) continue; /* Si le déccoupage actuel ne correspond pas au besoin des portions... */ if (cmp_vmpa(get_mrange_addr(&area->range), portion_start) != 0) { fini_mem_area(&result[j]); result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area)); memmove(&result[j + 2], &result[j + 1], (*count - j - 2) * sizeof(mem_area)); copy_mem_area_properties(&result[j + 1], &result[j]); /* Première moitié */ area = &result[j]; copy_vmpa(&tmp, get_mrange_addr(&area->range)); length = get_mrange_length(&area->range); init_mem_area_from_addr(area, &tmp, compute_vmpa_diff(&tmp, portion_start)); /* Seconde moitié */ length -= get_mrange_length(&area->range); area = &result[j + 1]; init_mem_area_from_addr(area, portion_start, length); } j = *count; } } for (i = 0; i < *count; i++) { printf(" (sect) AREA %zu :: 0x%08x + %u\n", i, (unsigned int)get_phy_addr(get_mrange_addr(&result[i].range)), (unsigned int)get_mrange_length(&result[i].range)); } /* Troisième étape : on insère les symboles existants */ j = 0; for (i = 0; i < *count; i++) { /* Sélection et écartement des symboles */ for (; j < sym_count; j++) { type = g_binary_symbol_get_target_type(symbols[j]); /** * On ne garde que les symboles renvoyant directement une ou * plusieurs instructions, c'est à dire les symboles valides * pour un appel à g_binary_symbol_get_instruction(). * * Les instructions des autres symboles sont obtenues et mises * en place durant la procédure de désassemblage. */ if (type == STP_ROUTINE || type == STP_ENTRY_POINT || type == STP_CODE_LABEL) continue; range = g_binary_symbol_get_range(symbols[j]); length = get_mrange_length(range); if (length > 0) break; } if (j == sym_count) break; start = get_mrange_addr(range); /* Si un découpage s'impose... */ if (mrange_contains_addr(&result[i].range, start)) { copy_vmpa(&tmp, start); advance_vmpa(&tmp, length); included = mrange_contains_addr(&result[i].range, &tmp); memcpy(&orig, &result[i], sizeof(mem_area)); fini_mem_area(&orig); /* Réduction de la zone de départ */ copy_vmpa(&tmp, get_mrange_addr(&result[i].range)); old_length = get_mrange_length(&result[i].range); new_length = compute_vmpa_diff(get_mrange_addr(&result[i].range), start); if (new_length == 0) { memmove(&result[i], &result[i + 1], (*count - i - 1) * sizeof(mem_area)); (*count)--; next = i; } else { init_mem_area_from_addr(&result[i], &tmp, new_length); next = i + 1; } /* Insertion de la zone du symbole */ result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area)); memmove(&result[next + 1], &result[next], (*count - next - 1) * sizeof(mem_area)); area = &result[next]; init_mem_area_from_range(area, range); area->has_sym = true; area->symbol = symbols[j]; /* Jointure finale... */ if (included) { /* Simple extension pour rattraper la fin originelle */ if ((old_length - new_length - length) > 0) { result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area)); memmove(&result[next + 2], &result[next + 1], (*count - next - 2) * sizeof(mem_area)); area = &result[next + 1]; copy_vmpa(&tmp, start); advance_vmpa(&tmp, length); init_mem_area_from_addr(area, &tmp, old_length - new_length - length); copy_mem_area_properties(area, &orig); } i = next; } else { /* Suppression des éventuelles zones totalement recouvertes */ /* Réduction de la zone d'arrivée */ } j++; } } if (exe_ranges != NULL) free(exe_ranges); return result; } /****************************************************************************** * * * Paramètres : list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * symbol = élément nouveau à venir insérer dans les zones. * * working = indice de l'aire de travail courante. * * * * Description : Insère un symbole dans un découpage en aires. * * * * Retour : Recalcul de rafraîchissement de l'aire de travail requis ? * * * * Remarques : - * * * ******************************************************************************/ static bool insert_extra_symbol_into_mem_areas(mem_area **list, size_t *count, GBinSymbol *symbol, size_t working) { bool result; /* Besoin à renvoyer */ const mrange_t *sym_range; /* Emplacement du symbole */ size_t index; /* Zone trouvée à scinder */ mem_area *area; /* Aire en cours de traitement */ mem_area saved; /* Aire aux données copiées */ mrange_t area_range; /* Emplacement d'une aire */ vmpa2t area_pos; /* Position relative à une aire*/ vmpa2t sym_pos; /* Position pour un symbole */ phys_t diff; /* Décalage entre localisation */ phys_t new_length; /* Nouvelle taille de zone */ sym_range = g_binary_symbol_get_range(symbol); index = find_memory_area_by_addr(*list, *count, get_mrange_addr(sym_range)); assert(index < *count); result = (working >= index); area = &(*list)[index]; do { size_t i; printf("--- INDEX = %zu OLD_INDEX = %zu\n", index, working); printf("--- comment '%s'...\n", g_db_comment_get_text(g_binary_symbol_get_comment(symbol))); printf("--- insert @ 0x%04x + %u\n", get_phy_addr(get_mrange_addr(sym_range)), get_mrange_length(sym_range)); for (i = 0; i < 3; i++) printf("area [ %zu ] <=> 0x%04x + %u (sym ? %d)\n", index - 1 + i, get_phy_addr(get_mrange_addr(&(&(*list)[index - 1 + i])->range)), get_mrange_length(&(&(*list)[index - 1 + i])->range), (&(*list)[index - 1 + i])->has_sym); } while (0); if (area->has_sym) return false; assert(!area->has_sym); saved = *area; copy_mrange(&area_range, &area->range); copy_vmpa(&area_pos, get_mrange_addr(&area_range)); copy_vmpa(&sym_pos, get_mrange_addr(sym_range)); /* Si le symbole est construit avec une localisation partielle, on complète ! */ if (get_phy_addr(&sym_pos) == VMPA_NO_PHYSICAL || get_virt_addr(&sym_pos) == VMPA_NO_VIRTUAL) { diff = compute_vmpa_diff(&area_pos, &sym_pos); copy_vmpa(&sym_pos, &area_pos); advance_vmpa(&sym_pos, diff); g_binary_symbol_fix_range(symbol, &sym_pos); } /* Si le symbole a une taille identique à la zone qui le contient, on remplace simplement... */ if (get_mrange_length(&area_range) == get_mrange_length(sym_range)) { assert((cmp_vmpa(&area_pos, &sym_pos) == 0)); init_mem_area_from_range(area, sym_range); area->has_sym = true; area->symbol = symbol; goto iesima_done; } /* Si le symbole se trouve en début de zone... */ if (cmp_vmpa(&area_pos, &sym_pos) == 0) { *list = (mem_area *)realloc(*list, ++(*count) * sizeof(mem_area)); memmove(&(*list)[index + 1], &(*list)[index], (*count - index - 1) * sizeof(mem_area)); /* Aire du symbole */ area = &(*list)[index]; init_mem_area_from_range(area, sym_range); area->has_sym = true; area->symbol = symbol; /* Aire raccourcie */ copy_vmpa(&area_pos, get_mrange_addr(&saved.range)); advance_vmpa(&area_pos, get_mrange_length(sym_range)); //compute_mrange_end_addr(sym_range, &area_pos); new_length = get_mrange_length(&area_range) - get_mrange_length(sym_range); area = &(*list)[index + 1]; init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved); goto iesima_done; } compute_mrange_end_addr(&area->range, &area_pos); compute_mrange_end_addr(sym_range, &sym_pos); /* Si le symbole se trouve en fin de zone... */ if (cmp_vmpa(&area_pos, &sym_pos) == 0) { *list = (mem_area *)realloc(*list, ++(*count) * sizeof(mem_area)); memmove(&(*list)[index + 1], &(*list)[index], (*count - index - 1) * sizeof(mem_area)); /* Aire raccourcie */ copy_vmpa(&area_pos, get_mrange_addr(&area_range)); new_length = get_mrange_length(&area_range) - get_mrange_length(sym_range); area = &(*list)[index]; init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved); /* Aire du symbole */ area = &(*list)[index + 1]; init_mem_area_from_range(area, sym_range); area->has_sym = true; area->symbol = symbol; } /* Sinon il se trouve au milieu et on découpe en trois... */ else { *count += 2; *list = (mem_area *)realloc(*list, *count * sizeof(mem_area)); memmove(&(*list)[index + 2], &(*list)[index], (*count - index - 2) * sizeof(mem_area)); /* Aire raccourcie #1 */ copy_vmpa(&area_pos, get_mrange_addr(&area_range)); new_length = compute_vmpa_diff(&area_pos, get_mrange_addr(sym_range)); assert(new_length != 0); /* Symbole non présent au début */ area = &(*list)[index]; init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved); /* Aire du symbole */ area = &(*list)[index + 1]; init_mem_area_from_range(area, sym_range); area->has_sym = true; area->symbol = symbol; /* Aire raccourcie #2 */ copy_vmpa(&area_pos, get_mrange_addr(&saved.range)); advance_vmpa(&area_pos, get_mrange_length(&(*list)[index].range)); advance_vmpa(&area_pos, get_mrange_length(sym_range)); //compute_mrange_end_addr(sym_range, &area_pos); new_length = get_mrange_length(&area_range) - get_mrange_length(sym_range) \ - get_mrange_length(&(*list)[index].range); assert(new_length != 0); /* Symbole non présent à la fin */ area = &(*list)[index + 2]; init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved); } fini_mem_area(&saved); iesima_done: return result; } /****************************************************************************** * * * Paramètres : list = listes de zones utable à consulter. * * count = nombre de zones mises en place. * * addr = adresse à retrouver dans les aires présentes. * * * * Description : Détermine une liste de zones contigües à traiter. * * * * Retour : Indice de la zone trouvée, ou nombre d'aires en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ size_t find_memory_area_by_addr(mem_area *list, size_t count, const vmpa2t *addr) { size_t result; /* Trouvaille à retourner */ mem_area *found; /* Elément trouvé éventuel */ int find_mem_area(const vmpa2t *addr, const mem_area *area) { int status; /* Bilan à retourner */ if (mrange_contains_addr(&area->range, addr)) status = 0; else status = cmp_vmpa(addr, get_mrange_addr(&area->range)); return status; } found = bsearch(addr, list, count, sizeof(mem_area), (__compar_fn_t)find_mem_area); result = (found != NULL ? found - list : count); return result; } /****************************************************************************** * * * Paramètres : list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * range = aire des octets à manipuler. * * visitor = fonction opérationnelle finale à appeler. * * * * Description : Manipule la cartographie des octets d'aires de données. * * * * Retour : valeur retournée par le visiteur, voire false si erreur. * * * * Remarques : - * * * ******************************************************************************/ static bool handle_bytes_map_in_mem_area(mem_area *list, size_t count, const mrange_t *range, GArchInstruction *instr, visit_bytes_map_fc visitor) { bool result; /* Bilan à retourner */ size_t found; /* Indice de la zone trouvée */ mem_area *area; /* Aire à traiter trouvée */ phys_t offset; /* Point de départ dans l'aire */ phys_t remaining; /* Quantité restant à traiter */ phys_t processed; /* Quantité traitée */ vmpa2t start; /* Point de départ suivant */ result = false; found = find_memory_area_by_addr(list, count, get_mrange_addr(range)); if (found == count) return false; area = list + found; offset = compute_vmpa_diff(get_mrange_addr(&area->range), get_mrange_addr(range)); remaining = get_mrange_length(range); /* Traitement intégral dans la zone trouvée */ if ((offset + remaining) <= get_mrange_length(&area->range)) result = visitor(area, offset, remaining, instr); else { printf("BUG_ON | off=%u remaining=%u length=%u\n", (unsigned int)offset, (unsigned int)remaining, (unsigned int)get_mrange_length(&area->range)); printf("BUG_ON @ 0x%08x + %d\n", (unsigned int)get_virt_addr(get_mrange_addr(range)), (int)get_mrange_length(range) ); assert(0); /* Traitement de la fin de la première aire */ processed = get_mrange_length(&area->range) - offset; result = visitor(area, offset, processed, instr); /* Traitement des zones adjacentes suivantes */ copy_vmpa(&start, get_mrange_addr(range)); for (remaining -= processed; remaining > 0 && result; remaining -= processed) { advance_vmpa(&start, processed); found = find_memory_area_by_addr(list, count, &start); if (found == count) { result = false; break; } area = list + found; processed = get_mrange_length(&area->range); if (remaining < processed) processed = remaining; result = visitor(area, 0, processed, instr); } } return result; } /****************************************************************************** * * * Paramètres : list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * range = aire des octets à manipuler. * * * * Description : Indique si une zone donnée est intégralement vierge ou non. * * * * Retour : true si l'aire visée n'a jamais été traitée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool is_range_blank_in_mem_areas(mem_area *list, size_t count, const mrange_t *range) { return handle_bytes_map_in_mem_area(list, count, range, NULL, is_range_blank_in_mem_area); } /****************************************************************************** * * * Paramètres : list = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * instr = instruction désassemblée à conserver en mémoire. * * * * Description : Marque une série d'octets comme ayant été traités. * * * * Retour : true. * * * * Remarques : - * * * ******************************************************************************/ static bool mark_range_in_mem_areas_as_processed(mem_area *list, size_t count, GArchInstruction *instr) { return handle_bytes_map_in_mem_area(list, count, g_arch_instruction_get_range(instr), instr, mark_range_in_mem_area_as_processed); } /****************************************************************************** * * * Paramètres : areas = série d'aires représentant à contenu à parcourir. * * count = nombre de ces zones présentes. * * * * Description : Rassemble les instructions conservées dans des zones données.* * * * Retour : Liste d'instructions prêtes à emploi. * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *collect_instructions_from_mem_areas(mem_area *list, size_t count) { GArchInstruction *result; /* Liste d'instr. à renvoyer */ size_t i; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction(s) à insérer */ result = NULL; for (i = 0; i < count; i++) { instr = get_instructions_from_mem_area(&list[i]); g_arch_instruction_merge_lists(&result, &instr); } return result; }