/* Chrysalide - Outil d'analyse de fichiers binaires
* loop.c - détection des boucles dans du code machine
*
* Copyright (C) 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 "loop.h"
#include
#include
#include "../../common/bits.h"
/* Description de noeud, en référence à "Compilers: Principles, Techniques, and Tools" */
typedef struct _dragon_node
{
GArchInstruction *first; /* Arrivée d'un lien (début) */
GArchInstruction *last; /* Départ d'un lien (fin) */
bitfield_t *bits; /* Représentation par masque */
} dragon_node;
/* Définition des blocs d'allocation */
#define NODE_ALLOC_SIZE 100
/* Dénombre le nombre de noeuds présents dans une routine. */
static dragon_node *create_dragon_nodes(const GArchProcessor *, const instr_coverage *, const mrange_t *, const vmpa2t *, size_t *);
/* Termine l'initialisation de noeuds trouvés dans une routine. */
static void define_mask_for_nodes(dragon_node *, size_t);
/* Supprime de la mémoire tous les noeuds détectés. */
static void delete_dragon_nodes(dragon_node *, size_t);
/* Recherche un noeud selon son intruction de départ. */
static dragon_node *find_node_for_instruction(dragon_node *, size_t, bool, const GArchInstruction *);
/* Détermine toute la chaîne hiérarchique de domination. */
static void compute_all_dominators(dragon_node *, size_t);
/* Matérialise les liens de retour arrière en tant que boucles. */
static void detect_back_edges(dragon_node *, size_t);
/* Suit un flot d'exécution à la recherche de boucles. */
static void track_loops_in_code(const GArchProcessor *, const instr_coverage *, const mrange_t *, const vmpa2t *, memfield_t *);
/******************************************************************************
* *
* Paramètres : proc = ensemble d'instructions à parcourir. *
* coverage = zone de couverture où rechercher des instructions.*
* range = zone de couverture de la routine analysée. *
* start = adresse du début de l'analyse. *
* count = taille de la liste de noeuds retournés. [OUT] *
* *
* Description : Dénombre le nombre de noeuds présents dans une routine. *
* *
* Retour : Liste de noeuds initialisés de façon incomplète. *
* *
* Remarques : - *
* *
******************************************************************************/
static dragon_node *create_dragon_nodes(const GArchProcessor *proc, const instr_coverage *coverage, const mrange_t *range, const vmpa2t *start, size_t *count)
{
dragon_node *result; /* Liste à créer et renvoyer */
size_t allocated; /* Dimensionnement en mémoire */
GArchInstruction *last; /* Mémorisation du passé */
GArchInstruction *iter; /* Boucle de parcours */
const mrange_t *irange; /* Emplacement d'instruction */
InstructionLinkType *types; /* Type de lien entre instr. */
size_t scount; /* Nombre de liens de dest. */
size_t i; /* Boucle de parcours */
dragon_node *new; /* Nouvel élément à créer */
result = NULL;
*count = 0;
allocated = 0;
for (last = NULL, iter = g_arch_processor_find_covered_instr_by_address(proc, coverage, start);
iter != NULL;
last = iter, iter = g_arch_instruction_get_next_iter(iter /* FIXME : list*/, iter, ~0))
{
/* L'instruction sort-elle des clous ? */
irange = g_arch_instruction_get_range(iter);
if (!mrange_contains_mrange(range, irange))
break;
/* Analyse des sources */
if (result == NULL)
{
(*count)++;
if (*count >= allocated)
{
allocated += NODE_ALLOC_SIZE;
result = (dragon_node *)realloc(result, allocated * sizeof(dragon_node));
}
new = &result[*count - 1];
new->first = iter;
}
else
{
scount = g_arch_instruction_get_sources(iter, NULL, &types);
if (scount == 0) continue;
for (i = 0; i < scount; i++)
switch (types[i])
{
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
if (*count > 0)
result[*count - 1].last = last;
(*count)++;
i = scount;
if (*count >= allocated)
{
allocated += NODE_ALLOC_SIZE;
result = (dragon_node *)realloc(result, allocated * sizeof(dragon_node));
}
new = &result[*count - 1];
new->first = iter;
break;
default:
break;
}
}
}
if (*count > 0)
result[*count - 1].last = last;
return result;
}
/******************************************************************************
* *
* Paramètres : nodes = liste de noeuds détectés dans une routine. *
* count = taille de cette liste de noeuds à traiter. *
* *
* Description : Termine l'initialisation de noeuds trouvés dans une routine. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void define_mask_for_nodes(dragon_node *nodes, size_t count)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < count; i++)
nodes[i].bits = create_bit_field(count, i > 0);
set_in_bit_field(nodes[0].bits, 0, 1);
}
/******************************************************************************
* *
* Paramètres : nodes = liste de noeuds détectés dans une routine. *
* count = taille de cette liste de noeuds à traiter. *
* *
* Description : Supprime de la mémoire tous les noeuds détectés. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void delete_dragon_nodes(dragon_node *nodes, size_t count)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < count; i++)
delete_bit_field(nodes[i].bits);
free(nodes);
}
/******************************************************************************
* *
* Paramètres : nodes = liste de noeuds détectés dans une routine. *
* count = taille de cette liste de noeuds à parcourir. *
* final = précise si l'instruction visée est la première. *
* instr = instruction à retrouver en tant que début de noeud. *
* *
* Description : Recherche un noeud selon son intruction de départ. *
* *
* Retour : Noeud trouvé ou NULL si aucune trouvaille. *
* *
* Remarques : - *
* *
******************************************************************************/
static dragon_node *find_node_for_instruction(dragon_node *nodes, size_t count, bool final, const GArchInstruction *instr)
{
dragon_node *result; /* Résultat des recherches */
const mrange_t *irange; /* Emplacement d'instruction */
int find_node_from_range(const mrange_t *range, const dragon_node *node)
{
int status; /* Bilan à retourner */
const mrange_t *nrange; /* Emplacement de noeud */
nrange = g_arch_instruction_get_range(final ? node->last : node->first);
status = cmp_mrange(range, nrange);
return status;
}
irange = g_arch_instruction_get_range(instr);
result = bsearch(irange, nodes, count, sizeof(dragon_node), (__compar_fn_t)find_node_from_range);
return result;
}
/******************************************************************************
* *
* Paramètres : nodes = liste de noeuds détectés dans une routine. *
* count = taille de cette liste de noeuds à traiter. *
* *
* Description : Détermine toute la chaîne hiérarchique de domination. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void compute_all_dominators(dragon_node *nodes, size_t count)
{
bitfield_t *inter; /* Intersection de dominations */
bool changed; /* Note un changement qq part */
size_t k; /* Boucle de parcours #1 */
dragon_node *node; /* Noeud à traiter */
dragon_node *predecessor; /* Noeud prédécesseur direct */
GArchInstruction **srcs; /* Instructions d'origine */
InstructionLinkType *types; /* Type de lien entre instr. */
size_t scount; /* Nombre de liens de source */
size_t i; /* Boucle de parcours #2 */
inter = create_bit_field(count, false);
do
{
changed = false;
for (k = 1; k < count; k++)
{
node = &nodes[k];
set_all_in_bit_field(inter);
scount = g_arch_instruction_get_sources(node->first, &srcs, &types);
assert(scount > 0);
for (i = 0; i < scount; i++)
switch (types[i])
{
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
predecessor = find_node_for_instruction(nodes, count, true, srcs[i]);
printf(" -- finding pred @ 0x%08x -> 0x%08x :: %p\n",
(unsigned int)g_arch_instruction_get_range(node->first)->addr.virtual,
(unsigned int)g_arch_instruction_get_range(srcs[i])->addr.virtual,
predecessor);
if (predecessor == NULL) break;
and_bit_field(inter, predecessor->bits);
break;
default:
break;
}
set_in_bit_field(inter, k, 1);
if (!is_bit_field_equal_to(node->bits, inter))
{
copy_bit_field(node->bits, inter);
changed = true;
}
}
}
while (changed);
delete_bit_field(inter);
}
/******************************************************************************
* *
* Paramètres : nodes = liste de noeuds détectés dans une routine. *
* count = taille de cette liste de noeuds à traiter. *
* *
* Description : Matérialise les liens de retour arrière en tant que boucles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void detect_back_edges(dragon_node *nodes, size_t count)
{
size_t k; /* Boucle de parcours #1 */
dragon_node *node; /* Noeud à traiter */
GArchInstruction **dests; /* Instr. visée par une autre */
InstructionLinkType *types; /* Type de lien entre lignes */
size_t dcount; /* Nombre de liens de dest. */
size_t i; /* Boucle de parcours #2 */
dragon_node *target; /* Noeud référencé à tester */
size_t id; /* Indice du bit associé */
printf("-----------------------------------------------------------------\n");
for (k = 0; k < count; k++)
{
node = &nodes[k];
printf("#[ node %zu ]# @ 0x%08x / 0x%08x - mask = 0x%08lx\n", k,
(unsigned int)g_arch_instruction_get_range(node->first)->addr.virtual,
(unsigned int)g_arch_instruction_get_range(node->last)->addr.virtual,
gfw(node->bits));
}
printf("\n");
for (k = 1; k < count; k++)
{
node = &nodes[k];
dcount = g_arch_instruction_get_destinations(node->last, &dests, &types, NULL);
if (dcount == 0) continue;
for (i = 0; i < dcount; i++)
switch (types[i])
{
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
target = find_node_for_instruction(nodes, count, false, dests[i]);
if (target == NULL) break;
id = (target - nodes);
if (test_in_bit_field(node->bits, id, 1))
{
printf("BACKEDGE :: 0x%08lx -> 0x%08lx\n",
(unsigned int)g_arch_instruction_get_range(node->last)->addr.virtual,
(unsigned int)g_arch_instruction_get_range(dests[i])->addr.virtual);
/* status = */g_arch_instruction_change_link(node->last, dests[i], types[i], ILT_LOOP);
}
break;
default:
break;
}
}
}
/******************************************************************************
* *
* Paramètres : proc = ensemble d'instructions à parcourir. *
* coverage = zone de couverture où rechercher des instructions.*
* range = zone de couverture de la routine analysée. *
* start = adresse du début de l'analyse. *
* flow = ensemble des jalons de l'exécution du code. *
* *
* Description : Suit un flot d'exécution à la recherche de boucles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void track_loops_in_code(const GArchProcessor *proc, const instr_coverage *coverage, const mrange_t *range, const vmpa2t *start, memfield_t *flow)
{
dragon_node *nodes; /* Liste des noeuds détectés */
size_t count; /* Taille de cette liste */
nodes = create_dragon_nodes(proc, coverage, range, start, &count);
if (nodes == NULL) return;
assert(nodes != NULL);
printf("nodes count :: %d\n", (int)count);
define_mask_for_nodes(nodes, count);
compute_all_dominators(nodes, count);
detect_back_edges(nodes, count);
delete_dragon_nodes(nodes, count);
}
/******************************************************************************
* *
* Paramètres : proc = ensemble d'instructions à relier. *
* routines = prototypes existants à insérer. *
* count = quantité de ces prototypes. *
* statusbar = barre de statut avec progression à mettre à jour.*
* id = identifiant du message affiché à l'utilisateur. *
* *
* Description : Détecte les boucles dans du code machine. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void detect_loops_in_code(const GArchProcessor *proc, GBinRoutine **routines, size_t count, GtkExtStatusBar *statusbar, bstatus_id_t id)
{
size_t i; /* Boucle de parcours */
const mrange_t *range; /* Couverture d'une routine */
const vmpa2t *start; /* Adresse de départ */
const instr_coverage *coverage; /* Instructions couvertes */
memfield_t *flow; /* Flot d'exécution à suivre */
//for (i = 286; i == 286; i++)
for (i = 0; i < count; i++)
{
range = g_binary_routine_get_range(routines[i]);
start = get_mrange_addr(range);
printf("====== '%s' @ 0x%08x\n",
g_binary_routine_get_name(routines[i]),
start->virtual);
coverage = g_arch_processor_find_coverage_by_address(proc, start);
flow = NULL;//create_mem_field(range);
track_loops_in_code(proc, coverage, range, start, flow);
//delete_mem_field(flow);
gtk_extended_status_bar_update_activity(statusbar, id, (i + 1) * 1.0 / count);
}
printf("done\n\n");
//exit(0);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if 0
#include
#include
#include
#include "../../common/bits.h"
/* Suit un flot d'exécution à la recherche de boucles. */
static void track_loops_in_code(const GArchProcessor *, const instr_coverage *, const mrange_t *, const vmpa2t *, memfield_t *);
/******************************************************************************
* *
* Paramètres : proc = ensemble d'instructions à parcourir. *
* coverage = zone de couverture où rechercher des instructions.*
* range = zone de couverture de la routine analysée. *
* start = adresse du début de l'analyse. *
* flow = ensemble des jalons de l'exécution du code. *
* *
* Description : Suit un flot d'exécution à la recherche de boucles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void track_loops_in_code(const GArchProcessor *proc, const instr_coverage *coverage, const mrange_t *range, const vmpa2t *start, memfield_t *flow)
{
bool exit_track; /* Détermine la fin du parcours*/
GArchInstruction *iter; /* Boucle de parcours */
const mrange_t *irange; /* Emplacement d'instruction */
GArchInstruction **dests; /* Instr. visée par une autre */
InstructionLinkType *types; /* Type de lien entre lignes */
size_t dcount; /* Nombre de liens de dest. */
size_t i; /* Boucle de parcours */
const vmpa2t *addr; /* Prochaine adresse de saut */
memfield_t *next_flow; /* Suite de l'exécution */
set_in_mem_field(flow, start);
exit_track = false;
for (iter = g_arch_processor_find_covered_instr_by_address(proc, coverage, start);
iter != NULL && !exit_track;
iter = g_arch_instruction_get_next_iter(iter /* FIXME : list*/, iter, ~0))
{
/* L'instruction sort-elle des clous ? */
irange = g_arch_instruction_get_range(iter);
if (!mrange_contains_mrange(range, irange))
break;
/* Fin de parcours ? */
if (g_arch_instruction_get_flags(iter) & AIF_RETURN_POINT)
break;
/**
* Afin de détecter les boucles le plus en aval possible,
* on marque toutes les arrivées potentielles de boucles comme jalons.
* Ainsi la détection se réalise sur l'ultime saut qui boucle effectivement.
*/
if (g_arch_instruction_has_sources(iter))
{
addr = get_mrange_addr(irange);
if (!test_in_mem_field(flow, addr))
set_in_mem_field(flow, start);
}
/* Analyse des destinations */
dcount = g_arch_instruction_get_destinations(iter, &dests, &types, NULL);
if (dcount == 0) continue;
for (i = 0; i < dcount; i++)
switch (types[i])
{
case ILT_LOOP:
/**
* On est déjà passé par là, donc on peut arrêter le parcours courant.
*/
exit_track = true;
break;
case ILT_CATCH_EXCEPTION:
irange = g_arch_instruction_get_range(dests[i]);
addr = get_mrange_addr(irange);
next_flow = create_mem_field_from(flow);
track_loops_in_code(proc, coverage, range, addr, next_flow);
delete_mem_field(next_flow);
break;
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
/**
* On se lance dans d'autres suivis qui vont parcourir le reste des
* instructions, donc on peut arrêter le parcours courant ici.
*/
exit_track = true;
irange = g_arch_instruction_get_range(dests[i]);
if (!mrange_contains_mrange(range, irange))
break;
addr = get_mrange_addr(irange);
if (test_in_mem_field(flow, addr))
/* status = */g_arch_instruction_change_link(iter, dests[i], types[i], ILT_LOOP);
else
{
next_flow = dup_mem_field(flow);
track_loops_in_code(proc, coverage, range, addr, next_flow);
delete_mem_field(next_flow);
}
break;
default:
break;
}
}
}
/******************************************************************************
* *
* Paramètres : proc = ensemble d'instructions à relier. *
* routines = prototypes existants à insérer. *
* count = quantité de ces prototypes. *
* statusbar = barre de statut avec progression à mettre à jour.*
* id = identifiant du message affiché à l'utilisateur. *
* *
* Description : Détecte les boucles dans du code machine. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void detect_loops_in_code(const GArchProcessor *proc, GBinRoutine **routines, size_t count, GtkExtStatusBar *statusbar, bstatus_id_t id)
{
size_t i; /* Boucle de parcours */
const mrange_t *range; /* Couverture d'une routine */
const vmpa2t *start; /* Adresse de départ */
const instr_coverage *coverage; /* Instructions couvertes */
memfield_t *flow; /* Flot d'exécution à suivre */
for (i = 0; i < count; i++)
{
range = g_binary_routine_get_range(routines[i]);
start = get_mrange_addr(range);
coverage = g_arch_processor_find_coverage_by_address(proc, start);
flow = create_mem_field(range);
track_loops_in_code(proc, coverage, range, start, flow);
delete_mem_field(flow);
gtk_extended_status_bar_update_activity(statusbar, id, (i + 1) * 1.0 / count);
}
}
#endif