/* 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 Chrysalide. 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 */
const 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;
}
unref_instr_link(dest);
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 */
const 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) goto next_dest;
/**
* 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)
goto next_dest;
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);
}
}
next_dest:
unref_instr_link(dest);
}
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);
}