/* Chrysalide - Outil d'analyse de fichiers binaires * flow.c - encadrement des instructions par blocs d'exécution * * Copyright (C) 2012-2013 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 "flow.h" #include #include #include "../block-int.h" /* Description d'un bloc d'exécution d'instructions (instance) */ struct _GFlowBlock { GInstrBlock parent; /* A laisser en premier */ unsigned int rank; /* Rang dans l'exécution */ unsigned int next_rank; /* Rang suivant de l'exécution */ GArchProcessor *proc; /* Liste complète d'instruct° */ GArchInstruction *first; /* Première instruction */ GArchInstruction *last; /* Dernière instruction */ GRAccessList *regs; /* Accès aux registres */ GRAccessList *awaited; /* Registres définis pour après*/ }; /* Description d'un bloc d'exécution d'instructions (classe) */ struct _GFlowBlockClass { GInstrBlockClass parent; /* A laisser en premier */ }; /* Initialise la classe des blocs d'instructions. */ static void g_flow_block_class_init(GFlowBlockClass *); /* Initialise un bloc d'instructions. */ static void g_flow_block_init(GFlowBlock *); /* Supprime toutes les références externes. */ static void g_flow_block_dispose(GFlowBlock *); /* Procède à la libération totale de la mémoire. */ static void g_flow_block_finalize(GFlowBlock *); /* Recherche le bloc contenant une adresse donnée. */ static GInstrBlock *g_flow_block_find_by_addr(const GFlowBlock *, const vmpa2t *, bool); /* Parcourt le bloc d'instructions dans un ordre donné. */ static bool g_flow_block_visit(GFlowBlock *, instr_block_visitor_cb, void *); /* Etablit la liste de tous les blocs présents. */ static void g_flow_block_list_all_blocks(const GFlowBlock *, GInstrBlock ***, size_t *); /* Etablit la liste de tous les blocs terminaux. */ static void g_flow_block_list_leafs_blocks(const GFlowBlock *, GInstrBlock ***, size_t *); /* Prend note de l'usage d'un registre, au besoin. */ static void g_flow_block_memorize_access(GFlowBlock *, GArchRegister *, RegAccessType, vmpa_t); /* Note les différents accès aux registres. */ static void g_flow_block_compute_regs_access(GFlowBlock *); /* Fournit les différents accès aux registres. */ //static const reg_access *g_flow_block_list_regs_accesses(const GFlowBlock *, size_t *); /* Indique le type défini pour un bloc d'exécution d'instructions. */ G_DEFINE_TYPE(GFlowBlock, g_flow_block, G_TYPE_INSTR_BLOCK); /****************************************************************************** * * * Paramètres : class = classe à initialiser. * * * * Description : Initialise la classe des blocs d'instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_class_init(GFlowBlockClass *class) { GObjectClass *object; /* Autre version de la classe */ GInstrBlockClass *block_class; /* Version parente du la classe*/ object = G_OBJECT_CLASS(class); block_class = G_INSTR_BLOCK_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_flow_block_dispose; object->finalize = (GObjectFinalizeFunc)g_flow_block_finalize; block_class->find_by_addr = (find_by_addr_fc)g_flow_block_find_by_addr; block_class->visit_blocks = (visit_all_blocks_fc)g_flow_block_visit; block_class->list_blocks = (list_all_blocks_fc)g_flow_block_list_all_blocks; block_class->list_leafs = (list_leafs_blocks_fc)g_flow_block_list_leafs_blocks; //block_class->list_regs = (list_regs_accesses_fc)g_flow_block_list_regs_accesses; } /****************************************************************************** * * * Paramètres : block = instance à initialiser. * * * * Description : Initialise un bloc d'instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_init(GFlowBlock *block) { g_flow_block_set_rank(block, 0); block->regs = g_raccess_list_new(); block->awaited = g_raccess_list_new(); } /****************************************************************************** * * * Paramètres : block = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_dispose(GFlowBlock *block) { //g_object_unref(G_OBJECT(block->proc)); g_object_unref(G_OBJECT(block->first)); g_object_unref(G_OBJECT(block->last)); g_object_unref(G_OBJECT(block->regs)); g_object_unref(G_OBJECT(block->awaited)); G_OBJECT_CLASS(g_flow_block_parent_class)->dispose(G_OBJECT(block)); } /****************************************************************************** * * * Paramètres : block = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_finalize(GFlowBlock *block) { G_OBJECT_CLASS(g_flow_block_parent_class)->finalize(G_OBJECT(block)); } /****************************************************************************** * * * Paramètres : proc = liste de toutes les instructions. * * first = première instruction du bloc. * * last = dernière instruction du bloc. * * * * Description : Crée un bloc d'exécution d'instructions. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GInstrBlock *g_flow_block_new(GArchProcessor *proc, GArchInstruction *first, GArchInstruction *last) { GFlowBlock *result; /* Structure à retourner */ result = g_object_new(G_TYPE_FLOW_BLOCK, NULL); /* FIXME : proc non utilisé ! */ result->proc = proc; result->first = first; result->last = last; //g_object_ref(G_OBJECT(result->proc)); g_object_ref(G_OBJECT(result->first)); g_object_ref(G_OBJECT(result->last)); //g_flow_block_compute_regs_access(result); return G_INSTR_BLOCK(result); } /****************************************************************************** * * * Paramètres : block = bloc d'instruction à consulter. * * * * Description : Fournit le rang du bloc dans le flot d'exécution. * * * * Retour : Indice supérieur ou égal à zéro. * * * * Remarques : - * * * ******************************************************************************/ unsigned int g_flow_block_get_rank(const GFlowBlock *block) { return block->rank; } /****************************************************************************** * * * Paramètres : block = bloc d'instruction à consulter. * * rank = Indice supérieur à zéro à prendre en compte. * * * * Description : Définit le rang du bloc dans le flot d'exécution. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_flow_block_set_rank(GFlowBlock *block, unsigned int rank) { block->rank = rank; } /****************************************************************************** * * * Paramètres : block = bloc de départ des recherches. * * addr = ensemble de blocs à parcourir. * * final = indique si la cible ou le conteneur est renvoyée. * * * * Description : Recherche le bloc contenant une adresse donnée. * * * * Retour : bloc basique trouvé ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ static GInstrBlock *g_flow_block_find_by_addr(const GFlowBlock *block, const vmpa2t *addr, bool final) { GInstrBlock *result; /* Resultat à retourner */ vmpa2t start; /* Adresse de début du bloc */ vmpa2t end; /* Adresse de fin du bloc */ mrange_t range; /* Couverture globale */ g_flow_block_get_boundary_addresses(block, &start, &end); init_mrange(&range, &start, compute_vmpa_diff(&start, &end)); if (mrange_contains_addr(&range, addr)) result = G_INSTR_BLOCK(block); else result = NULL; return result; } /****************************************************************************** * * * Paramètres : block = bloc d'instructions concerné par la visite. * * callback = ensemble de blocs à parcourir. * * data = donnée utilisateur à associer au parcours. * * * * Description : Parcourt le bloc d'instructions dans un ordre donné. * * * * Retour : true si le parcours a été jusqu'à son terme, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool g_flow_block_visit(GFlowBlock *block, instr_block_visitor_cb callback, void *data) { return callback(G_INSTR_BLOCK(block), BVO_PENDING, data); } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * list = ensemble de blocs à compléter. [OUT] * * count = nombre de blocs au total. [OUT] * * * * Description : Etablit la liste de tous les blocs présents. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_list_all_blocks(const GFlowBlock *block, GInstrBlock ***list, size_t *count) { (*list) = (GInstrBlock **)realloc(*list, ++(*count) * sizeof(GInstrBlock *)); (*list)[*count - 1] = G_INSTR_BLOCK(block); } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * list = ensemble de blocs à compléter. [OUT] * * count = nombre de blocs au total. [OUT] * * * * Description : Etablit la liste de tous les blocs terminaux. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_list_leafs_blocks(const GFlowBlock *block, GInstrBlock ***list, size_t *count) { (*list) = (GInstrBlock **)realloc(*list, ++(*count) * sizeof(GInstrBlock *)); (*list)[*count - 1] = G_INSTR_BLOCK(block); } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à mettre à jour. * * reg = registre visé par l'opération. * * type = type d'accès à l'opérande. * * addr = adresse de l'instruction associée. * * * * Description : Prend note de l'usage d'un registre, au besoin. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_memorize_access(GFlowBlock *block, GArchRegister *reg, RegAccessType type, vmpa_t addr) { reg_access *found; /* Accès à mettre à jour */ reg_access access; /* Accès à mettre en place */ found = g_raccess_list_find(block->regs, reg); /* Simpple mise à jour ? */ if (found != NULL) { if (type == RAT_WRITE) found->last_write = addr; } /* Ou ajout pur et simple ? */ else { access.reg = reg; access.first_access = type; access.last_write = (type == RAT_WRITE ? addr : VMPA_MAX); g_raccess_list_add(block->regs, &access); } } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à parcourir. * * * * Description : Note les différents accès aux registres. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_flow_block_compute_regs_access(GFlowBlock *block) { GArchInstruction *iter; /* Boucle de parcours #1 */ vmpa_t max; /* Adresse de fin */ vmpa_t addr; /* Adresse d'instruction */ GArchRegister **rregs; /* Liste des registres lus */ size_t rcount; /* Nombre de registres lus */ GArchRegister **wregs; /* Liste des registres écrits */ size_t wcount; /* Nombre de registres écrits */ size_t i; /* Boucle de parcours #2 */ g_arch_instruction_get_location(block->last, NULL, NULL, &max); max++; for (iter = block->first; iter != NULL; iter = NULL/* FIXME g_arch_instruction_get_next_iter(block->instrs, iter, max)*/) { g_arch_instruction_get_location(iter, NULL, NULL, &addr); g_arch_instruction_get_rw_registers(iter, &rregs, &rcount, &wregs, &wcount); for (i = 0; i < rcount; i++) { g_flow_block_memorize_access(block, rregs[i], RAT_READ, addr); g_object_unref(G_OBJECT(rregs[i])); } for (i = 0; i < wcount; i++) { g_flow_block_memorize_access(block, wregs[i], RAT_WRITE, addr); g_object_unref(G_OBJECT(wregs[i])); } if (rregs != NULL) free(rregs); if (wregs != NULL) free(wregs); } } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * * * Description : Donne le processeur d'appartenance des instructions du bloc. * * * * Retour : Liste de l'ensemble des instructions. * * * * Remarques : - * * * ******************************************************************************/ GArchProcessor *g_flow_block_get_processor(const GFlowBlock *block) { return block->proc; } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * first = instruction de départ du bloc. [OUT] * * last = dernière instruction du bloc. [OUT] * * * * Description : Fournit les instructions limites d'un bloc d'exécution. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_flow_block_get_boundary(const GFlowBlock *block, GArchInstruction **first, GArchInstruction **last) { if (first != NULL) *first = block->first; if (last != NULL) *last = block->last; } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * start = adresse de départ du bloc. [OUT] * * end = dernière adresse du bloc. [OUT] * * * * Description : Fournit les adresses limites d'un bloc d'exécution. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_flow_block_get_boundary_addresses(const GFlowBlock *block, vmpa2t *start, vmpa2t *end) { const mrange_t *range; /* Couverture d'instruction */ if (start != NULL) { range = g_arch_instruction_get_range(block->first); copy_vmpa(start, get_mrange_addr(range)); } if (end != NULL) { range = g_arch_instruction_get_range(block->last); copy_vmpa(end, get_mrange_addr(range)); } } /****************************************************************************** * * * Paramètres : block = bloc d'instructions démarrant la visite. * * list = ensemble des blocs basiques à parcourir. * * target = bloc de fin de parcours. * * * * Description : Détermine si un bloc peut conduire à un autre. * * * * Retour : true si un lien existe entre les deux blocs fournis. * * * * Remarques : - * * * ******************************************************************************/ bool g_flow_block_is_looping_to(GFlowBlock *block, const GInstrBlock *list, GFlowBlock *target) { bool result; /* Bilan à retourner */ instr_link_t *dests; /* Instr. visées par une autre */ size_t dcount; /* Nombre de liens de dest. */ size_t i; /* Boucle de parcours */ vmpa_t addr; /* Adresse de la destination */ GInstrBlock *next; /* Bloc suivant à visiter */ result = (block == target); g_arch_instruction_rlock_dest(block->last); dcount = g_arch_instruction_get_destinations(block->last, &dests); for (i = 0; i < dcount && !result; i++) switch (dests[i].type) { case ILT_EXEC_FLOW: case ILT_JUMP: case ILT_CASE_JUMP: case ILT_JUMP_IF_TRUE: case ILT_JUMP_IF_FALSE: g_arch_instruction_get_location(dests[i].linked, NULL, NULL, &addr); next = g_instr_block_find_by_addr(list, addr, true); result = g_flow_block_is_looping_to(G_FLOW_BLOCK(next), list, target); break; case ILT_LOOP: g_arch_instruction_get_location(dests[i].linked, NULL, NULL, &addr); next = g_instr_block_find_by_addr(list, addr, true); result = (G_FLOW_BLOCK(next) == target); default: break; } g_arch_instruction_runlock_dest(block->last); return result; } /****************************************************************************** * * * Paramètres : block = bloc d'instructions démarrant la visite. * * list = ensemble des blocs basiques à parcourir. * * mask = points de passage à marquer. * * callback = ensemble de blocs à parcourir. * * data = donnée utilisateur à associer au parcours. * * * * Description : Suit le flot d'excution bloc par bloc. * * * * Retour : true si le parcours a été jusqu'à son terme, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_flow_block_follow(GFlowBlock *block, const GInstrBlock *list, BlockFollowPosition mask, flow_block_follow_cb callback, void *data) { bool result; /* Bilan à retourner */ instr_link_t *dests; /* Instr. visées par une autre */ size_t dcount; /* Nombre de liens de dest. */ size_t i; /* Boucle de parcours */ vmpa_t addr; /* Adresse de la destination */ GInstrBlock *next; /* Bloc suivant à visiter */ result = true; if (mask & BFP_ENTER) result = callback(block, BFP_ENTER, data); g_arch_instruction_rlock_dest(block->last); dcount = g_arch_instruction_get_destinations(block->last, &dests); for (i = 0; i < dcount && result; i++) switch (dests[i].type) { case ILT_EXEC_FLOW: case ILT_JUMP: case ILT_CASE_JUMP: case ILT_JUMP_IF_TRUE: case ILT_JUMP_IF_FALSE: g_arch_instruction_get_location(dests[i].linked, NULL, NULL, &addr); next = g_instr_block_find_by_addr(list, addr, true); if (next) { result = callback(block, BFP_FOLLOW, data); result &= g_flow_block_follow(G_FLOW_BLOCK(next), list, mask, callback, data); result &= callback(block, BFP_BACK, data); } break; default: break; } g_arch_instruction_runlock_dest(block->last); if (mask & BFP_EXIT) result &= callback(block, BFP_EXIT, data); return result; } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * * * Description : Fournit les différents accès aux registres. * * * * Retour : Liste des accès aux registres. * * * * Remarques : - * * * ******************************************************************************/ const GRAccessList *g_flow_block_list_regs_accesses(const GFlowBlock *block) { return block->regs; } /****************************************************************************** * * * Paramètres : block = bloc d'instructions à consulter. * * * * Description : Fournit les registres écrits par le bloc et utilisées après. * * * * Retour : Liste des accès aux registres. * * * * Remarques : - * * * ******************************************************************************/ GRAccessList *g_flow_block_list_awaited_regs(const GFlowBlock *block) { return block->awaited; }