/* Chrysalide - Outil d'analyse de fichiers binaires
* fetch.c - récupération d'instructions à partir de binaire brut
*
* Copyright (C) 2010-2014 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 "fetch.h"
#include
#include
#include "area.h"
#include "../../glibext/delayed-int.h"
/* ------------------------- RECUPERATIONS EN TOILE DE FOND ------------------------- */
/* ------------------------ DESASSEMBLAGE DE BINAIRE DIFFERE ------------------------ */
#define G_TYPE_DELAYED_FETCHING g_delayed_fetching_get_type()
#define G_DELAYED_FETCHING(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_fetching_get_type(), GDelayedFetching))
#define G_IS_DELAYED_FETCHING(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_fetching_get_type()))
#define G_DELAYED_FETCHING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_FETCHING, GDelayedFetchingClass))
#define G_IS_DELAYED_FETCHING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_FETCHING))
#define G_DELAYED_FETCHING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_FETCHING, GDelayedFetchingClass))
/* Ensembles binaires à désassembler (instance) */
typedef struct _GDelayedFetching
{
GDelayedWork parent; /* A laisser en premier */
wgroup_id_t gid; /* Groupe de travail parallèle */
GExeFormat *format; /* Format du fichier binaire */
GProcContext *ctx; /* Contexte de désassemblage */
mem_area_v2 *areas; /* Zone de productions */
size_t count; /* Nombre de ces zones */
GtkStatusStack *status; /* Barre de statut */
activity_id_t id; /* Groupe de progression */
DisassPriorityLevel level; /* Niveau d'importance du point*/
virt_t virt; /* Adresse de départ dépilée */
} GDelayedFetching;
/* Ensembles binaires à désassembler (classe) */
typedef struct _GDelayedFetchingClass
{
GDelayedWorkClass parent; /* A laisser en premier */
} GDelayedFetchingClass;
/* Indique le type défini pour les tâches de récupération différée. */
GType g_delayed_fetching_get_type(void);
/* Initialise la classe des tâches de désassemblage différé. */
static void g_delayed_fetching_class_init(GDelayedFetchingClass *);
/* Initialise une tâche de désassemblage différé. */
static void g_delayed_fetching_init(GDelayedFetching *);
/* Supprime toutes les références externes. */
static void g_delayed_fetching_dispose(GDelayedFetching *);
/* Procède à la libération totale de la mémoire. */
static void g_delayed_fetching_finalize(GDelayedFetching *);
/* Crée une tâche de récupération d'instructions différée. */
static GDelayedFetching *g_delayed_fetching_new(const GDelayedFetching *, unsigned int, virt_t);
/* Assure la récupération d'instructions en différé. */
static void g_delayed_fetching_process(GDelayedFetching *, GtkExtStatusBar *);
/* ------------------------ DESASSEMBLAGE DE BINAIRE DIFFERE ------------------------ */
/* ------------------------ DESASSEMBLAGE DE BINAIRE DIFFERE ------------------------ */
/* Suit un flot d'exécution pour désassembler du code. */
static void follow_execution_flow(const GLoadedBinary *, GProcContext *, mem_area **, size_t *, status_blob_info *);
/* ---------------------------------------------------------------------------------- */
/* RECUPERATIONS EN TOILE DE FOND */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les tâches de récupération différée. */
G_DEFINE_TYPE(GDelayedFetching, g_delayed_fetching, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches de récupération différée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_fetching_class_init(GDelayedFetchingClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GDelayedWorkClass *work; /* Version en classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_fetching_dispose;
object->finalize = (GObjectFinalizeFunc)g_delayed_fetching_finalize;
work = G_DELAYED_WORK_CLASS(klass);
work->run = (run_task_fc)g_delayed_fetching_process;
}
/******************************************************************************
* *
* Paramètres : fetching = instance à initialiser. *
* *
* Description : Initialise une tâche de récupération différée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_fetching_init(GDelayedFetching *fetching)
{
}
/******************************************************************************
* *
* Paramètres : fetching = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_fetching_dispose(GDelayedFetching *fetching)
{
g_object_unref(G_OBJECT(fetching->format));
g_object_unref(G_OBJECT(fetching->ctx));
g_object_unref(G_OBJECT(fetching->status));
G_OBJECT_CLASS(g_delayed_fetching_parent_class)->dispose(G_OBJECT(fetching));
}
/******************************************************************************
* *
* Paramètres : fetching = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_fetching_finalize(GDelayedFetching *fetching)
{
G_OBJECT_CLASS(g_delayed_fetching_parent_class)->finalize(G_OBJECT(fetching));
}
/******************************************************************************
* *
* Paramètres : template = modèle dont les informations sont à copier. *
* level = indication de priorité et d'origine de l'adresse. *
* virt = point départ dépilé et personnalisant l'instance. *
* *
* Description : Crée une tâche de récupération d'instructions différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
static GDelayedFetching *g_delayed_fetching_new(const GDelayedFetching *template, DisassPriorityLevel level, virt_t virt)
{
GDelayedFetching *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_DELAYED_FETCHING, NULL);
result->gid = template->gid;
result->format = template->format;
g_object_ref(G_OBJECT(result->format));
result->ctx = template->ctx;
g_object_ref(G_OBJECT(result->ctx));
result->areas = template->areas;
result->count = template->count;
result->status = template->status;
g_object_ref(G_OBJECT(result->status));
result->id = template->id;
result->level = level;
result->virt = virt;
return result;
}
/******************************************************************************
* *
* Paramètres : fetching = récupération à mener. *
* statusbar = barre de statut à tenir informée. *
* *
* Description : Assure la récupération d'instructions en différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_delayed_fetching_process(GDelayedFetching *fetching, GtkExtStatusBar *statusbar)
{
vmpa2t addr; /* Conversion en pleine adresse*/
mem_area_v2 *area; /* Zone trouvée à traiter */
if (!g_exe_format_translate_address_into_vmpa(fetching->format, fetching->virt, &addr))
return/*init_vmpa(&addr, VMPA_NO_PHYSICAL, fetching->virt)*/;
area = find_memory_area_by_addr_v2(fetching->areas, fetching->count, &addr);
if (area != NULL)
load_code_from_mem_area_v2(area, fetching->areas, fetching->count,
fetching->ctx, &addr, fetching->level < 2,
fetching->status, fetching->id);
}
/* Poursuit l'analyse à partir des points d'entrée découverts. */
static void follow_execution_flow_v2(GProcContext *, const GDelayedFetching *);
/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
static bool check_if_extra_wait_is_needed(GWorkQueue *, wgroup_id_t, GProcContext *);
/******************************************************************************
* *
* Paramètres : ctx = contexte de désass. avec une nouvelle entrée. *
* template = modèle dont les informations sont à copier. *
* *
* Description : Poursuit l'analyse à partir des points d'entrée découverts. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching *template)
{
GWorkQueue *queue; /* Gestionnaire de différés */
gint *remaining_counter; /* Compteur à considérer */
DisassPriorityLevel level; /* Niveau d'importance du point*/
virt_t virt; /* Adresse de départ dépilée */
GDelayedFetching *fetching; /* Récupération à mener */
queue = get_work_queue();
remaining_counter = (gint *)g_object_get_data(G_OBJECT(ctx), "remaining_counter");
while (g_proc_context_pop_drop_point(ctx, &level, &virt))
{
fetching = g_delayed_fetching_new(template, level, virt);
/**
* Pas très élégant : l'identifiant du groupe de travail ne sert qu'ici ;
* il n'est donc aucune utilité dans la tâche elle-même.
*
* Cependant, les paramètres d'appel étant limités, il faudrait créer
* une structure intermediare pour communiquer l'identifiant, ce qui
* est tout aussi moche.
*/
g_work_queue_schedule_work(queue, G_DELAYED_WORK(fetching), template->gid);
/**
* Le décompte n'est réalisé qu'après la programmation de la tâche.
* Ainsi, lors de l'attente de la fin des traitements, on a la garantie
* de ne pas avoir de trou entre le dépilement des points et la programmation
* des tâches de traitement associées.
*/
if (g_atomic_int_dec_and_test(remaining_counter))
g_work_queue_wake_up_waiters(queue, template->gid);
}
}
/******************************************************************************
* *
* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
* id = identifiant d'un groupe de travail. *
* ctx = contexte de désass. avec une nouvelle entrée. *
* *
* Description : Etudie le besoin d'attendre d'avantage de prochaines tâches. *
* *
* Retour : true pour attendre d'avantage, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool check_if_extra_wait_is_needed(GWorkQueue *queue, wgroup_id_t id, GProcContext *ctx)
{
bool result; /* Bilan à retourner */
gint *remaining_counter; /* Compteur à considérer */
remaining_counter = (gint *)g_object_get_data(G_OBJECT(ctx), "remaining_counter");
result = (g_atomic_int_get(remaining_counter) > 0);
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* areas = liste de zones contenant des données à traiter. *
* count = nombre de ces aires à disposition. *
* info = informations liées à l'affichage de la progression. *
* *
* Description : Suit un flot d'exécution pour désassembler du code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void follow_execution_flow(const GLoadedBinary *binary, GProcContext *ctx, mem_area **areas, size_t *count, status_blob_info *info)
{
#if 0
virt_t virt; /* Adresse de départ dépilée */
vmpa2t addr; /* Conversion en pleine adresse*/
GExeFormat *format; /* Format du fichier binaire */
size_t index; /* Zone trouvée à traiter */
while (g_proc_context_has_drop_points(ctx))
{
g_proc_context_pop_drop_point(ctx, &virt);
format = g_loaded_binary_get_format(binary);
if (!g_exe_format_translate_address_into_vmpa(format, virt, &addr))
init_vmpa(&addr, VMPA_NO_PHYSICAL, virt);
printf(" ++ point 0x%08x\n", (unsigned int)virt);
printf("looking area for 0x%08x\n", (unsigned int)virt);
index = find_memory_area_by_addr(*areas, *count, &addr);
if (index == *count) continue;
assert(index < *count);
load_code_from_mem_area(areas, count, &index, binary, ctx, &addr, info);
printf(" ++\n");
}
#endif
}
static GDelayedFetching template; /* Patron des tâches à venir */
/******************************************************************************
* *
* Paramètres : binary = représentation de binaire chargé. *
* ctx = contexte fourni pour suivre le désassemblage. *
* gid = identifiant du groupe de travail à utiliser. *
* status = barre de statut avec progression à mettre à jour. *
* *
* Description : Procède au désassemblage basique d'un contenu binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, GProcContext *ctx, wgroup_id_t gid, GtkStatusStack *status)
{
GArchInstruction *result; /* Instruction désassemblées */
//GDelayedFetching template; /* Patron des tâches à venir */
GBinFormat *format; /* Format du fichier binaire */
GBinContent *content; /* Contenu binaire à manipuler */
phys_t length; /* Taille des données à lire */
GWorkQueue *queue; /* Gestionnaire de différés */
gint remaining_counter; /* Quantité de points restants */
/* Constitution du modèle de référence */
template.gid = gid;
template.format = g_loaded_binary_get_format(binary);
format = G_BIN_FORMAT(template.format);
template.ctx = ctx;
content = g_binary_format_get_content(format);
length = g_binary_content_compute_size(content);
g_object_unref(G_OBJECT(content));
template.areas = compute_memory_areas_v2(binary, length, &template.count);
template.status = status;
/* Amorce des traitements */
queue = get_work_queue();
g_atomic_int_set(&remaining_counter, 0);
g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", &remaining_counter);
g_proc_context_attach_counter(template.ctx, &remaining_counter);
/**
* Première phase de désassemblage : suivi des chemins tracés.
*/
g_work_queue_set_extra_wait_callback(queue, gid,
(wait_for_incoming_works_cb)check_if_extra_wait_is_needed,
template.ctx);
g_signal_connect(template.ctx, "drop-point-pushed", G_CALLBACK(follow_execution_flow_v2), &template);
template.id = gtk_status_stack_add_activity(status,
_("Disassembling following the execution flow..."),
length);
g_binary_format_setup_disassembling_context(format, template.ctx);
g_work_queue_wait_for_completion(queue, gid);
/**
* Seconde phase : on comble les trous laissés.
*/
gtk_status_stack_update_activity(status, template.id, _("Disassembling the remaining instructions..."));
ensure_all_mem_areas_are_filled(template.areas, template.count, template.ctx, status, template.id);
g_work_queue_wait_for_completion(queue, gid);
g_work_queue_set_extra_wait_callback(queue, gid, NULL, NULL);
g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", NULL);
/**
* Troisième et dernière phase : récolte des fruits.
*/
gtk_status_stack_update_activity(status, template.id, _("Collecting disassembled instructions..."));
result = collect_instructions_from_mem_areas_v2(template.areas, template.count);
gtk_status_stack_remove_activity(status, template.id);
/* Libérations finales */
//g_object_unref(G_OBJECT(template.format));
/* TODO / del(areas); */
return result;
}