/* Chrysalide - Outil d'analyse de fichiers binaires * rank.c - classement des blocs d'instructions * * Copyright (C) 2013-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 "rank.h" #if 0 #include #include #include "../blocks/flow.h" #include "../blocks/virtual.h" /* Classe le contenu d'un bloc d'instructions exécutées. */ static bool rank_flow_block(GFlowBlock *, BlockVisitOrder, const GInstrBlock *); /****************************************************************************** * * * Paramètres : block = bloc d'instructions concerné par la visite. * * order = indication quant au sens de visite. * * list = ensemble des blocs basiques à parcourir. * * * * Description : Classe le contenu d'un bloc d'instructions exécutées. * * * * Retour : true pour continuer la visite. * * * * Remarques : - * * * ******************************************************************************/ static bool rank_flow_block(GFlowBlock *block, BlockVisitOrder order, const GInstrBlock *list) { unsigned int next; /* Rang suivant obtenu */ GInstrBlock *links; /* Blocs liés au bloc courant */ GArchInstruction *last; /* Dernière instruction du bloc*/ size_t dcount; /* Nombre de liens de dest. */ size_t i; /* Boucle de parcours */ instr_link_t *dest; /* Instr. visée par une autre */ const mrange_t *range; /* Emplacement d'une cible */ GFlowBlock *target; /* Bloc ciblé par un lien */ unsigned int rank; /* Rang à constituer */ /* Si le bloc visité n'est pas basique... */ if (order != BVO_PENDING) return true; next = g_flow_block_get_rank(block) + 1; links = g_instr_block_get_links_block(G_INSTR_BLOCK(block)); g_flow_block_get_boundary(block, NULL, &last); g_arch_instruction_lock_dest(last); dcount = g_arch_instruction_count_destinations(last); for (i = 0; i < dcount; i++) { dest = g_arch_instruction_get_destination(last, i); range = g_arch_instruction_get_range(dest->linked); switch (dest->type) { case ILT_EXEC_FLOW: case ILT_CATCH_EXCEPTION: target = G_FLOW_BLOCK(g_instr_block_find_by_addr(list, get_mrange_addr(range), true)); //assert(target != NULL); break; case ILT_JUMP: target = G_FLOW_BLOCK(g_instr_block_find_by_addr(list, get_mrange_addr(range), true)); /** * Les sauts ne se font pas toujours à l'intérieur d'une même fonction. * Par exemple sous ARM : * * 00008358 : * .... * 8362: f7ff bfcf b.w 8304 <_init+0x38> * .... * */ /* assert(target != NULL);*/ break; case ILT_CASE_JUMP: target = G_FLOW_BLOCK(g_instr_block_find_by_addr(links, get_mrange_addr(range), true)); //assert(target != NULL); break; case ILT_JUMP_IF_TRUE: case ILT_JUMP_IF_FALSE: /** * Même dans les branches if/then/else, il n'y a pas forcément de groupe de blocs * associé. C'est par exemple le cas dans des constructions telles que celle de * la fonction __libc_csu_init() : * * if (...) * do * { * ... * } * while (...) * * ... * * Le code final est étiquetté comme étant le 'else' de la première condition, * donc au niveau de la condition de bouclage, il appartient déjà à un autre * et n'est donc pas réattribué une seconde fois. * */ if (links != NULL) target = G_FLOW_BLOCK(g_instr_block_find_by_addr(links, get_mrange_addr(range), true)); else target = NULL; /** * Le bloc visé ne se trouve pas toujours dans les blocs attachés : * * { bloc IF } * { bloc THEN } * { block NEXT } * * Le bloc NEXT est ici à rechercher dans la liste globale. */ if (target == NULL) target = G_FLOW_BLOCK(g_instr_block_find_by_addr(list, get_mrange_addr(range), true)); //assert(target != NULL); break; default: target = NULL; break; } if (target != NULL) { rank = MAX(next, g_flow_block_get_rank(target)); g_flow_block_set_rank(target, rank); } } g_arch_instruction_unlock_dest(last); return true; } /****************************************************************************** * * * Paramètres : routine = routine regroupant les blocs à traiter. * * * * Description : Classe les blocs des routines. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void rank_routine_blocks(GBinRoutine *routine) { GInstrBlock *main_block; /* Ensemble des blocs d'instr. */ main_block = g_binary_routine_get_basic_blocks(routine); if (main_block == NULL) return; g_instr_block_visit(main_block, (instr_block_visitor_cb)rank_flow_block, main_block); #if 0 printf("===== BLOCK(S) xXXx ======\n"); bool visit_ranked_block(GInstrBlock *blk, BlockVisitOrder order, int *indent) { int i; switch (order) { case BVO_IN: case BVO_PENDING: for (i = 0; i < *indent; i++) printf(" "); printf("%p '%s'", blk, G_OBJECT_TYPE_NAME(blk)); if (G_IS_FLOW_BLOCK(blk)) { vmpa2t start; vmpa2t end; g_flow_block_get_boundary_addresses(G_FLOW_BLOCK(blk), &start, &end); printf(" 0x%08x -> 0x%08x (rank = %u)", (unsigned int)start.virtual, (unsigned int)end.virtual, g_flow_block_get_rank(G_FLOW_BLOCK(blk))); } printf("\n"); if (order == BVO_IN) (*indent)++; break; case BVO_OUT: (*indent)--; break; } return true; } g_instr_block_visit(main_block, (instr_block_visitor_cb)visit_ranked_block, (int []){ 0 }); printf("\n"); #endif } #endif #include /* Classe les blocs basiques d'une routine. */ void rank_routine_block(const GBlockList *, GBasicBlock *); /****************************************************************************** * * * Paramètres : list = ensemble de blocs basiques à traiter. * * block = bloc d'analyse courant. * * * * Description : Classe les blocs basiques d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void rank_routine_block(const GBlockList *list, GBasicBlock *block) { unsigned int next; /* Rang suivant obtenu */ GArchInstruction *last; /* Dernière instruction du bloc*/ size_t dcount; /* Nombre de liens de dest. */ size_t i; /* Boucle de parcours */ instr_link_t *dest; /* Instr. visée par une autre */ InstructionLinkType type; /* Raccourci pour confort */ GBasicBlock *target; /* Bloc ciblé par un lien */ unsigned int rank; /* Rang à constituer */ next = g_basic_block_get_rank(block) + 1; g_basic_block_get_boundary(block, NULL, &last); g_arch_instruction_lock_dest(last); dcount = g_arch_instruction_count_destinations(last); for (i = 0; i < dcount; i++) { dest = g_arch_instruction_get_destination(last, i); type = dest->type; /* La boucle de remontée n'abaisse pas les rangs */ if (type == ILT_LOOP) continue; /** * On se doit de suivre le même cheminement que celui emprunté lors * du parcours de create_dragon_nodes(). * Sinon, les chemins divergent et une récursion infinie peut survenir. */ if (type != ILT_EXEC_FLOW && type != ILT_JUMP && type != ILT_CASE_JUMP && type != ILT_JUMP_IF_TRUE && type != ILT_JUMP_IF_FALSE) continue; target = g_block_list_find_by_starting_instr(list, dest->linked); /** * Les sauts ne se font pas toujours à l'intérieur d'une même fonction. * Par exemple sous ARM : * * 00008358 : * .... * 8362: f7ff bfcf b.w 8304 <_init+0x38> * .... * */ if (target != NULL) { rank = g_basic_block_get_rank(target); if (next > rank || rank == -1) { g_basic_block_set_rank(target, next); rank_routine_block(list, target); } } } g_arch_instruction_unlock_dest(last); } /****************************************************************************** * * * Paramètres : routine = routine regroupant les blocs à traiter. * * * * Description : Classe les blocs des routines. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void rank_routine_blocks(GBinRoutine *routine) { GBlockList *blocks; /* Ensemble des blocs d'instr. */ GBasicBlock *start; /* Bloc basique de départ */ blocks = g_binary_routine_get_basic_blocks(routine); start = g_block_list_get_block(blocks, 0 /* FIXME */); assert(start != NULL); g_basic_block_set_rank(start, 0); rank_routine_block(blocks, start); }