/* OpenIDA - Outil d'analyse de fichiers binaires
* delayed.c - gestion des actions d'analyse différées
*
* Copyright (C) 2009 Cyrille Bagard
*
* This file is part of OpenIDA.
*
* 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 "delayed.h"
#include
#include "line_code.h"
#include "line_comment.h"
#include "../common/dllist.h"
#include "../format/format.h"
#include "../gtkext/gtkextstatusbar.h"
#include "../gtkext/iodamarshal.h"
#ifndef _
# define _(str) (str)
#endif
/* Ensembles binaires à désassembler */
typedef struct _disassembly_task
{
GOpenidaBinary *owner; /* Destinataire final */
DL_LIST_ITEM(link); /* Lien vers les maillons */
GBinPart **parts; /* Parties binaires à traiter */
size_t count; /* Nombre de ces parties */
} disassembly_task;
#define disassembly_task_list_add_tail(new, head) dl_list_add_tail(new, head, disassembly_task, link)
#define disassembly_task_list_del(item, head) dl_list_del(item, head, disassembly_task, link)
/* Crée un tâche de désassemblage différé. */
static disassembly_task *create_disassembly_task(GOpenidaBinary *, GBinPart **, size_t);
/* Efface une tâche de désassemblage de la mémoire. */
static void delete_disassembly_task(disassembly_task *);
/* Gestionnaire des analyses différées (instance) */
struct _GDelayedManager
{
GObject parent; /* A laisser en premier */
GtkExtStatusBar *statusbar; /* Barre de statut principale */
disassembly_task *disassemblies; /* Binaires à désassembler */
GMutex *disass_mutex; /* Verrou pour l'accès */
GCond *disass_cond; /* Réveil pour un traitement */
GThread *disassemble; /* Procédures de désassemblage */
};
/* Gestionnaire des analyses différées (classe) */
struct _GDelayedManagerClass
{
GObjectClass parent; /* A laisser en premier */
/* Signaux */
void (* disassembly_completed) (GDelayedManager *, GOpenidaBinary *, GRenderingLine *);
};
/* Initialise la classe des gestionnaires d'analyses différées. */
static void g_delayed_manager_class_init(GDelayedManagerClass *);
/* Initialise un gestionnaire d'analyses différées. */
static void g_delayed_manager_init(GDelayedManager *);
/* Crée un gestionnaire d'analyses différées. */
static GDelayedManager *g_delayed_manager_new(GObject *);
/* Assure le désassemblage en différé. */
static void *process_disassemblies(GDelayedManager *);
/* Procède au désassemblage basique d'un contenu binaire. */
static GRenderingLine *disassemble_binary_parts(disassembly_task *, GBinRoutine **, size_t, GtkExtStatusBar *, guint);
/* Etablit les liens entres les différentes lignes de code. */
static void establish_links_between_lines(GRenderingLine *, GBinRoutine **, size_t, GtkExtStatusBar *, guint);
/* S'assure que toutes les routines ont une taille définie. */
static void limit_all_routines(GRenderingLine *, GBinRoutine **, size_t, GtkExtStatusBar *, guint);
/* Cherche l'adresse de fin d'une routine. */
static vmpa_t find_best_ending_address_for_routine(GRenderingLine *, size_t, const vmpa_t *, const off_t *, size_t);
/******************************************************************************
* *
* Paramètres : owner = binaire chargé en attente des résultats. *
* parts = parties binaires à désassembler. *
* count = nombre de parties à traiter. *
* *
* Description : Crée un tâche de désassemblage différé. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
static disassembly_task *create_disassembly_task(GOpenidaBinary *owner, GBinPart **parts, size_t count)
{
disassembly_task *result; /* Tâche à retourner */
result = (disassembly_task *)calloc(1, sizeof(disassembly_task));
result->owner = owner;
DL_LIST_ITEM_INIT(&result->link);
result->parts = parts;
result->count = count;
return result;
}
/******************************************************************************
* *
* Paramètres : task = tâche à libérer de la mémoire. *
* *
* Description : Efface une tâche de désassemblage de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void delete_disassembly_task(disassembly_task *task)
{
/* TODO
result->parts = parts;
result->count = count;
*/
free(task);
}
/* Indique le type défini pour le gestionnaire des analyses différées. */
G_DEFINE_TYPE(GDelayedManager, g_delayed_manager, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des gestionnaires d'analyses différées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_manager_class_init(GDelayedManagerClass *klass)
{
g_signal_new("disassembly-completed",
G_TYPE_DELAYED_MANAGER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GDelayedManagerClass, disassembly_completed),
NULL, NULL,
g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
G_TYPE_NONE, 2, G_TYPE_OPENIDA_BINARY, G_TYPE_RENDERING_LINE);
}
/******************************************************************************
* *
* Paramètres : manager = instance à initialiser. *
* *
* Description : Initialise un gestionnaire d'analyses différées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_manager_init(GDelayedManager *manager)
{
}
/******************************************************************************
* *
* Paramètres : ref = espace de référencements global. *
* *
* Description : Crée un gestionnaire d'analyses différées. *
* *
* Retour : Gestionnaire mis en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static GDelayedManager *g_delayed_manager_new(GObject *ref)
{
GDelayedManager *result; /* Adresse à retourner */
GError *error; /* Bilan de création de thread */
result = g_object_new(G_TYPE_DELAYED_MANAGER, NULL);
result->statusbar = g_object_get_data(ref, "statusbar");
result->disass_mutex = g_mutex_new();
if (result->disass_mutex == NULL)
goto dmn_error;
result->disass_cond = g_cond_new();
if (result->disass_cond == NULL)
goto dmn_error;
result->disassemble = g_thread_create((GThreadFunc)process_disassemblies, result, FALSE, &error);
if (!result->disassemble)
goto dmn_error;
return result;
dmn_error:
/* TODO */
return NULL;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire des actions à mener. *
* *
* Description : Assure le désassemblage en différé. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static void *process_disassemblies(GDelayedManager *manager)
{
disassembly_task *task; /* Désassemblage à effectuer */
GBinRoutine **routines; /* Liste des routines trouvées */
size_t routines_count; /* Nombre de ces routines */
guint id; /* Identifiant de statut */
GRenderingLine *lines; /* Nouvelles lignes de rendu */
while (1)
{
g_mutex_lock(manager->disass_mutex);
if (dl_list_empty(manager->disassemblies))
g_cond_wait(manager->disass_cond, manager->disass_mutex);
task = manager->disassemblies;
disassembly_task_list_del(task, &manager->disassemblies);
g_mutex_unlock(manager->disass_mutex);
routines = g_binary_format_get_routines(G_BIN_FORMAT(g_openida_binary_get_format(task->owner)), &routines_count);
qsort(routines, routines_count, sizeof(GBinRoutine *), g_binary_routine_rcompare);
/* Première étape */
id = gtk_extended_status_bar_push(manager->statusbar, _("Disassembling..."), true);
lines = disassemble_binary_parts(task, routines, routines_count,
manager->statusbar, id);
gtk_extended_status_bar_remove(manager->statusbar, id);
/* Seconde étape */
id = gtk_extended_status_bar_push(manager->statusbar, _("Establishing links..."), true);
establish_links_between_lines(lines, routines, routines_count,
manager->statusbar, id);
gtk_extended_status_bar_remove(manager->statusbar, id);
/* Troisième étape */
id = gtk_extended_status_bar_push(manager->statusbar, _("Finding remaining limits..."), true);
limit_all_routines(lines, routines, routines_count,
manager->statusbar, id);
gtk_extended_status_bar_remove(manager->statusbar, id);
/* Fin */
g_signal_emit_by_name(manager, "disassembly-completed", task->owner, lines);
delete_disassembly_task(task);
}
return NULL;
}
/******************************************************************************
* *
* Paramètres : task = tâche à l'origine du traitement. *
* 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 : Procède au désassemblage basique d'un contenu binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static GRenderingLine *disassemble_binary_parts(disassembly_task *task, GBinRoutine **routines, size_t count, GtkExtStatusBar *statusbar, guint id)
{
GRenderingLine *result; /* Ligne de rendu à retourner */
GArchProcessor *proc; /* Architecture du binaire */
GRenderingOptions *options; /* Options de désassemblage */
bin_t *bin_data; /* Données binaires à lire */
size_t i; /* Boucle de parcours #1 */
off_t sum; /* Somme de toutes les tailles */
off_t done; /* Quantité déjà traitée */
off_t pos; /* Début d'une zone binaire */
off_t len; /* Taille de cette même zone */
vmpa_t base; /* Adresse de la zone binaire */
off_t start; /* Conservation du pt de départ*/
vmpa_t addr; /* Adresse d'une instruction */
GArchInstruction *instr; /* Instruction décodée */
GRenderingLine *line; /* Nouvelle ligne de rendu */
size_t k; /* Boucle de parcours #2 */
uint64_t routine_offset; /* Point de départ de routine */
char *routine_desc; /* Prototype d'une routine */
result = NULL;
proc = get_arch_processor_from_format(g_openida_binary_get_format(task->owner));
options = g_openida_binary_get_options(task->owner);
bin_data = g_openida_binary_get_data(task->owner, NULL);
/* Préparation du suivi de la progression */
sum = 0;
for (i = 0; i < task->count; i++)
{
g_binary_part_get_values(task->parts[i], NULL, &len, NULL);
sum += len;
}
done = 0;
for (i = 0; i < task->count; i++)
{
g_binary_part_get_values(task->parts[i], &pos, &len, &base);
/* Décodage des instructions */
start = pos;
pos = 0;
while (pos < len)
{
addr = base + pos;
instr = g_arch_processor_decode_instruction(proc, &bin_data[start],
&pos, len, start, addr);
line = g_code_line_new(addr, instr, options);
g_rendering_line_add_to_lines(&result, line);
if (pos < len)
gtk_extended_status_bar_update_activity(statusbar, id, (done + pos) * 1.0 / sum);
}
done += len;
gtk_extended_status_bar_update_activity(statusbar, id, done * 1.0 / sum);
/* Ajout des prototypes de fonctions */
for (k = 0; k < count; k++)
{
routine_offset = g_binary_routine_get_address(routines[k]);
if (!(base <= routine_offset && routine_offset < (base + len))) continue;
routine_desc = g_binary_routine_to_string(routines[k]);
line = g_comment_line_new(routine_offset, routine_desc, options);
g_rendering_line_insert_into_lines(&result, line, true);
free(routine_desc);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : lines = lignes de rendu à 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 : Etablit les liens entres les différentes lignes de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void establish_links_between_lines(GRenderingLine *lines, GBinRoutine **routines, size_t count, GtkExtStatusBar *statusbar, guint id)
{
size_t i; /* Boucle de parcours */
vmpa_t start; /* Adresse de départ */
vmpa_t end; /* Adresse de fin */
GRenderingLine *iter; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction à ausculter */
vmpa_t addr; /* Adresse référencée */
InstructionLinkType type; /* Type de référence */
GRenderingLine *target; /* Ligne visée par la référence*/
for (i = 0; i < count; i++)
{
start = g_binary_routine_get_address(routines[i]);
end = start + g_binary_routine_get_size(routines[i]);
for (iter = g_rendering_line_find_by_address(lines, NULL, start);
iter != NULL;
iter = g_rendering_line_get_next_iter(lines, iter, NULL))
{
/* Si on sort de la zone... */
if (get_rendering_line_address(iter) >= end) break;
/* On ne traite que du code ici ! */
if (!G_IS_CODE_LINE(iter)) continue;
instr = g_code_line_get_instruction(G_CODE_LINE(iter));
type = g_arch_instruction_get_link(instr, &addr);
switch (type)
{
case ILT_NONE:
break;
case ILT_JUMP:
target = g_rendering_line_find_by_address(lines, NULL, addr);
if (target != NULL)
g_rendering_line_link_with(iter, target, type);
break;
case ILT_JUMP_IF_FALSE:
break;
case ILT_JUMP_IF_TRUE:
target = g_rendering_line_find_by_address(lines, NULL, addr);
if (target != NULL)
{
g_rendering_line_link_with(iter, target, type);
target = g_rendering_line_get_next_iter(lines, iter, NULL);
if (target != NULL)
g_rendering_line_link_with(iter, target, ILT_JUMP_IF_FALSE);
}
break;
case ILT_CALL:
target = g_rendering_line_find_by_address(lines, NULL, addr);
if (target != NULL)
g_rendering_line_link_with(iter, target, type);
break;
}
}
gtk_extended_status_bar_update_activity(statusbar, id, (i + 1) * 1.0 / count);
}
}
/******************************************************************************
* *
* Paramètres : lines = lignes de rendu à parcourir. *
* 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 : S'assure que toutes les routines ont une taille définie. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void limit_all_routines(GRenderingLine *lines, GBinRoutine **routines, size_t count, GtkExtStatusBar *statusbar, guint id)
{
size_t i; /* Boucle de parcours */
vmpa_t *starts; /* Adresses de départ */
off_t *lengths; /* Tailles des routines */
GRenderingLine *line; /* Ligne de départ / d'arrivée */
vmpa_t start; /* Adresse de début de routine */
vmpa_t last; /* Meilleur dernière adresse */
GArchInstruction *instr; /* Instruction à ausculter */
off_t length; /* Taille du code */
if (count == 0) return;
starts = (vmpa_t *)calloc(count, sizeof(vmpa_t));
lengths = (off_t *)calloc(count, sizeof(off_t));
for (i = 0; i < count; i++)
{
starts[i] = g_binary_routine_get_address(routines[i]);
lengths[i] = g_binary_routine_get_size(routines[i]);
gtk_extended_status_bar_update_activity(statusbar, id, (i + 1) * 1.0 / (count * 2));
}
for (i = 0; i < count; i++)
{
if (lengths[i] > 0) continue;
start = g_binary_routine_get_address(routines[i]);
line = g_rendering_line_find_by_address(lines, NULL, start);
last = find_best_ending_address_for_routine(line, i, starts, lengths, count);
line = g_rendering_line_find_by_address(lines, NULL, last);
line = g_rendering_line_loop_for_code(line, NULL);
instr = g_code_line_get_instruction(G_CODE_LINE(line));
g_arch_instruction_get_location(instr, NULL, &length, NULL);
lengths[i] = last - start + length;
g_binary_routine_set_size(routines[i], lengths[i]);
gtk_extended_status_bar_update_activity(statusbar, id, (i + 1 + count) * 1.0 / (count * 2));
}
free(starts);
free(lengths);
}
/******************************************************************************
* *
* Paramètres : line = ligne de départ du parcours. *
* index = indice de la routine traitée dans la liste. *
* starts = adresse de départ des autres routines. *
* lengths = taille des différentes routines, valides ou nulles.*
* count = quantité de routines présentes. *
* *
* Description : Cherche l'adresse de fin d'une routine. *
* *
* Retour : Plus grande adresse de dernière instruction de routine. *
* *
* Remarques : - *
* *
******************************************************************************/
static vmpa_t find_best_ending_address_for_routine(GRenderingLine *line, size_t index, const vmpa_t *starts, const off_t *lengths, size_t count)
{
vmpa_t result; /* Haute adresse à remonter */
GRenderingLine *iter; /* Boucle de parcours #1 */
vmpa_t candidate; /* Candidat potentiel */
size_t i; /* Boucle de parcours #2 */
GArchInstruction *instr; /* Instruction à ausculter */
result = starts[index];
for (iter = line; iter != NULL; iter = g_rendering_line_get_next_iter(line, iter, NULL))
{
if (!G_IS_CODE_LINE(iter)) continue;
candidate = get_rendering_line_address(iter);
/* Regarde si on n'empiète pas sur une autre routine */
for (i = 0; i < count; i++)
{
if (i == index) continue;
if (starts[i] <= candidate && candidate < (starts[i] + lengths[i]))
break;
}
if (i != count) break;
else result = candidate;
/* Retour de fonction ? */
instr = g_code_line_get_instruction(G_CODE_LINE(iter));
if (g_arch_instruction_is_return(instr)) break;
}
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire des actions à mener. *
* binary = élément binaire concerné par la procédure. *
* parts = blocs binaires à désassembler. *
* count = quantité de ces blocs binaires. *
* *
* Description : Place un nouveau désassemblage en attente. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_delayed_manager_schedule_disassembly(GDelayedManager *manager, GOpenidaBinary *binary, GBinPart **parts, size_t count)
{
disassembly_task *task; /* Nouveau désassemblage */
task = create_disassembly_task(binary, parts, count);
g_mutex_lock(manager->disass_mutex);
disassembly_task_list_add_tail(task, &manager->disassemblies);
g_cond_signal(manager->disass_cond);
g_mutex_unlock(manager->disass_mutex);
}
/******************************************************************************
* *
* Paramètres : manager = nouveau gestionnaire à mémoriser ou NULL. *
* *
* Description : Fournit le gestionnaire d'analyse en différé courant. *
* *
* Retour : Gestionnaire d'analyse en différé courant. *
* *
* Remarques : - *
* *
******************************************************************************/
GDelayedManager *_get_delayed_manager(GDelayedManager *manager)
{
static GDelayedManager *result = NULL; /* Singleton à retourner */
if (manager != NULL)
result = manager;
return result;
}
/******************************************************************************
* *
* Paramètres : ref = espace de référencements global. *
* *
* Description : Procède au chargement du gestionnaire d'analyse différées. *
* *
* Retour : true pour indiquer un chargement réussi, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool init_delayed_manager(GObject *ref)
{
GDelayedManager *manager; /* Singleton à mettre en place */
manager = g_delayed_manager_new(ref);
if (manager != NULL)
_get_delayed_manager(manager);
return (manager != NULL);
}