/* Chrysalide - Outil d'analyse de fichiers binaires * fetch.c - récupération d'instructions à partir de binaire brut * * Copyright (C) 2010-2013 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 "fetch.h" #include #include "../../arch/raw.h" #include "../../arch/instruction.h" /* Zone mémoire bien bornée */ typedef struct _mem_area { mrange_t range; /* Couverture de la zone */ bool has_sym; /* Représentation via symbole ?*/ union { bool exec; /* Zone exécutable ? */ GBinSymbol *symbol; /* Symbole associé à la zone */ }; } mem_area; /* Détermine une liste de zones contigües à traiter. */ static mem_area *compute_memory_areas(const GExeFormat *, phys_t, size_t *); /* Procède au désassemblage basique d'un contenu binaire. */ static GArchInstruction *load_raw_binary(const GLoadedBinary *, const vmpa2t *, off_t, GtkExtStatusBar *, bstatus_id_t); /* Procède au désassemblage d'un contenu binaire exécutable. */ static GArchInstruction *load_code_binary(const GLoadedBinary *, const vmpa2t *, off_t, GtkExtStatusBar *, bstatus_id_t); /****************************************************************************** * * * 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 : - * * * ******************************************************************************/ static mem_area *compute_memory_areas(const 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 */ size_t j; /* Boucle de parcours #2 */ 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); /* 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_mrange(&area->range, 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]; copy_mrange(&area->range, &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_mrange(&area->range, &tmp, bin_length - get_phy_addr(&tmp)); area->has_sym = false; area->exec = false; } /* Seconde étape : on insère les symboles existants */ j = 0; #define SKIP_EMPTY_SYMBOLS \ for (; j < sym_count; j++) \ { \ range = g_binary_symbol_get_range(symbols[j]); \ \ length = get_mrange_length(range); \ if (length > 0) break; \ \ } \ SKIP_EMPTY_SYMBOLS for (i = 0; i < *count && j < sym_count; i++) { range = g_binary_symbol_get_range(symbols[j]); start = get_mrange_addr(range); length = get_mrange_length(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)); /* 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_mrange(&result[i].range, &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]; copy_mrange(&area->range, 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); memcpy(area, &orig, sizeof(mem_area)); init_mrange(&area->range, &tmp, old_length - new_length - length); } i = next; } else { /* Suppression des éventuelles zones totalement recouvertes */ /* Réduction de la zone d'arrivée */ } j++; SKIP_EMPTY_SYMBOLS } } //free //exit(0); return result; } /****************************************************************************** * * * Paramètres : binary = représentation de binaire chargé. * * parts = parties binaires à désassembler. * * count = nombre de parties à traiter. * * statusbar = barre de statut avec progression à mettre à jour.* * id = identifiant du message affiché à l'utilisateur. * * * * Description : Procède au désassemblage basique d'un contenu binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static GArchInstruction *load_raw_binary(const GLoadedBinary *binary, const vmpa2t *base, off_t end, GtkExtStatusBar *statusbar, bstatus_id_t id) { GArchInstruction *result; /* Liste d'instr. à renvoyer */ GBinFormat *format; /* Format du fichier binaire */ GArchProcessor *proc; /* Architecture du binaire */ off_t bin_length; /* Taille des données à lire */ bin_t *bin_data; /* Données binaires à lire */ vmpa2t pos; /* Boucle de parcours */ vmpa2t prev; /* Boucle de parcours */ off_t old_phy; /* Ancienne position physique */ GArchInstruction *instr; /* Instruction décodée */ off_t new_phy; /* Nouvelle position physique */ mrange_t range; /* Couverture de l'instruction */ result = NULL; format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); proc = get_arch_processor_from_format(G_EXE_FORMAT(format)); bin_data = g_loaded_binary_get_data(binary, &bin_length); copy_vmpa(&pos, base); copy_vmpa(&prev, base); old_phy = get_phy_addr(&prev); while (old_phy < end) { instr = g_raw_instruction_new_array(bin_data, MDS_32_BITS, 1, &pos, end, g_arch_processor_get_endianness(proc)); if (instr == NULL) printf(" Break !!!\n"); if (instr == NULL) break; new_phy = get_phy_addr(&pos); init_mrange(&range, &prev, new_phy - old_phy); g_arch_instruction_set_range(instr, &range); g_arch_instruction_add_to_list(&result, instr); copy_vmpa(&prev, &pos); old_phy = get_phy_addr(&prev); //done += (new_phy - old_phy); //gtk_extended_status_bar_update_activity(statusbar, id, done * 1.0 / sum); } return result; } /****************************************************************************** * * * Paramètres : binary = représentation de binaire chargé. * * parts = parties binaires à désassembler. * * count = nombre de parties à traiter. * * statusbar = barre de statut avec progression à mettre à jour.* * id = identifiant du message affiché à l'utilisateur. * * * * Description : Procède au désassemblage d'un contenu binaire exécutable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static GArchInstruction *load_code_binary(const GLoadedBinary *binary, const vmpa2t *base, off_t end, GtkExtStatusBar *statusbar, bstatus_id_t id) { GArchInstruction *result; /* Liste d'instr. à renvoyer */ GBinFormat *format; /* Format du fichier binaire */ GArchProcessor *proc; /* Architecture du binaire */ off_t bin_length; /* Taille des données à lire */ bin_t *bin_data; /* Données binaires à lire */ vmpa2t pos; /* Boucle de parcours */ vmpa2t prev; /* Boucle de parcours */ off_t old_phy; /* Ancienne position physique */ GArchInstruction *instr; /* Instruction décodée */ off_t new_phy; /* Nouvelle position physique */ mrange_t range; /* Couverture de l'instruction */ result = NULL; format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); proc = get_arch_processor_from_format(G_EXE_FORMAT(format)); bin_data = g_loaded_binary_get_data(binary, &bin_length); copy_vmpa(&pos, base); copy_vmpa(&prev, base); old_phy = get_phy_addr(&prev); while (old_phy < end) { instr = g_arch_processor_disassemble(proc, NULL, bin_data, &pos, end); //if (!G_IS_RAW_INSTRUCTION(instr)) printf("GOT %p\n", instr); if (instr == NULL) instr = g_raw_instruction_new_array(bin_data, MDS_32_BITS, 1, &pos, end, g_arch_processor_get_endianness(proc)); if (instr == NULL) printf(" Break !!!\n"); if (instr == NULL) break; new_phy = get_phy_addr(&pos); init_mrange(&range, &prev, new_phy - old_phy); g_arch_instruction_set_range(instr, &range); g_arch_instruction_add_to_list(&result, instr); copy_vmpa(&prev, &pos); old_phy = get_phy_addr(&prev); //done += (new_phy - old_phy); //gtk_extended_status_bar_update_activity(statusbar, id, done * 1.0 / sum); g_arch_instruction_call_hook(instr, IPH_LINK, NULL, format); g_arch_instruction_call_hook(instr, IPH_POST, NULL, format); } return result; } /****************************************************************************** * * * Paramètres : binary = représentation de binaire chargé. * * statusbar = barre de statut avec progression à mettre à jour.* * id = identifiant du message affiché à l'utilisateur. * * * * Description : Procède au désassemblage basique d'un contenu binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static GArchInstruction *follow_execution_flow(const GLoadedBinary *binary, GProcContext *ctx, mem_area *areas, size_t count, GtkExtStatusBar *statusbar, bstatus_id_t id) { GBinFormat *format; /* Format du fichier binaire */ virt_t addr; mem_area *area; /* Zone de désassemblage */ format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); /* Insertion du point de départ */ addr = g_binary_format_get_entry_point(format); g_proc_context_push_drop_point(ctx, addr); /* Suivi de l'exécution autant que possible */ while (g_proc_context_has_drop_points(ctx)) { //virt = g_proc_context_pop_drop_point(ctx); area = NULL; } // ctx.add(entry_point) // while (ctx.has_pending_addresses) // virt = ctx.pop // find mem_area for virt // if area.limit < virt then continue } /****************************************************************************** * * * Paramètres : binary = représentation de binaire chargé. * * statusbar = barre de statut avec progression à mettre à jour.* * id = identifiant du message affiché à l'utilisateur. * * * * Description : Procède au désassemblage basique d'un contenu binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *disassemble_binary_content_new(const GLoadedBinary *binary, GtkExtStatusBar *statusbar, bstatus_id_t id) { GBinFormat *format; /* Format du fichier binaire */ GArchProcessor *proc; /* Architecture du binaire */ GProcContext *ctx; /* Contexte de désassemblage */ format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); proc = get_arch_processor_from_format(G_EXE_FORMAT(format)); ctx = g_arch_processor_get_context(proc); } /****************************************************************************** * * * Paramètres : binary = représentation de binaire chargé. * * statusbar = barre de statut avec progression à mettre à jour.* * id = identifiant du message affiché à l'utilisateur. * * * * Description : Procède au désassemblage basique d'un contenu binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, GtkExtStatusBar *statusbar, bstatus_id_t id) { GArchInstruction *result; /* Instruction désassemblées */ GExeFormat *format; /* Format du fichier binaire */ off_t length; /* Taille des données à lire */ mem_area *areas; /* Zone de productions */ size_t count; /* Nombre de ces zones */ size_t i; /* Boucle de parcours */ mem_area *iter; /* Zone parcourue */ GArchInstruction *instr; /* Instruction(s) à insérer */ const vmpa2t *start; /* Début d'une zone traitée */ phys_t end; /* Fin d'une zone traitée */ result = NULL; format = g_loaded_binary_get_format(binary); g_loaded_binary_get_data(binary, &length); areas = compute_memory_areas(format, length, &count); for (i = 0; i < count; i++) { iter = &areas[i]; start = get_mrange_addr(&iter->range); end = get_phy_addr(start) + get_mrange_length(&iter->range); assert(get_mrange_length(&iter->range) > 0); if (iter->has_sym) switch (g_binary_symbol_get_target_type(iter->symbol)) { case STP_DATA: instr = g_binary_symbol_get_instruction(iter->symbol); g_object_ref(G_OBJECT(instr)); break; case STP_ROUTINE: instr = load_code_binary(binary, start, end, statusbar, id); break; default: assert(false); break; } else { if (iter->exec) instr = load_code_binary(binary, start, end, statusbar, id); else instr = load_raw_binary(binary, start, end, statusbar, id); } g_arch_instruction_merge_lists(&result, &instr); } free(areas); return result; }