/* Chrysalide - Outil d'analyse de fichiers binaires
* fetch.c - récupération d'instructions à partir de binaire brut
*
* Copyright (C) 2010-2018 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 "fetch.h"
#include
#include
#include "area.h"
#include "../../core/global.h"
#include "../../format/format.h"
#include "../../glibext/delayed-int.h"
/* ------------------------- RECUPERATIONS EN TOILE DE FOND ------------------------- */
#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 *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 *, DisassPriorityLevel, virt_t);
/* Assure la récupération d'instructions en différé. */
static void g_delayed_fetching_process(GDelayedFetching *, GtkStatusStack *);
/* ------------------------ DESASSEMBLAGE DE BINAIRE DIFFERE ------------------------ */
/* Poursuit l'analyse à partir des points d'entrée découverts. */
static void follow_execution_flow(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 *);
/* ---------------------------------------------------------------------------------- */
/* 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_clear_object(&fetching->format);
g_clear_object(&fetching->ctx);
g_clear_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;
if (result->status != NULL)
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. *
* status = 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, GtkStatusStack *status)
{
vmpa2t addr; /* Conversion en pleine adresse*/
mem_area *area; /* Zone trouvée à traiter */
if (!g_exe_format_translate_address_into_vmpa(fetching->format, fetching->virt, &addr))
return;
area = find_memory_area_by_addr(fetching->areas, fetching->count, &addr);
if (area != NULL)
load_code_from_mem_area(area, fetching->areas, fetching->count,
fetching->ctx, &addr, fetching->level < 2,
fetching->status, fetching->id);
}
/* ---------------------------------------------------------------------------------- */
/* DESASSEMBLAGE DE BINAIRE DIFFERE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* 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(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 fourni pour suivre le désassemblage. *
* gid = identifiant du groupe de travail à utiliser. *
* status = barre de statut avec progression à mettre à jour. *
* count = nombre d'instructions récupérées. *
* *
* Description : Procède au désassemblage basique d'un contenu binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction **disassemble_binary_content(GLoadedBinary *binary, GProcContext *ctx, wgroup_id_t gid, GtkStatusStack *status, size_t *count)
{
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 = collect_memory_areas(gid, status, 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 : intégration des infos du format,
* récupérées dans le contexte via un appel à g_binary_format_preload_disassembling_context().
*/
populate_fresh_memory_areas(gid, status, template.areas, template.count, G_PRELOAD_INFO(ctx));
g_work_queue_wait_for_completion(queue, gid);
/**
* Seconde phase : 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), &template);
template.id = gtk_status_stack_add_activity(status,
_("Disassembling following the execution flow..."),
length);
g_binary_format_activate_disassembling_context(format, template.ctx, status);
g_work_queue_wait_for_completion(queue, gid);
/**
* Troisième phase : on comble les trous laissés.
*/
ensure_all_mem_areas_are_filled(gid, status, template.id, template.areas, template.count, template.ctx);
g_work_queue_set_extra_wait_callback(queue, gid, NULL, NULL);
g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", NULL);
ensure_all_mem_areas_are_filled(gid, status, template.id, template.areas, template.count, NULL);
gtk_status_stack_remove_activity(status, template.id);
/**
* Quatrième et dernière phase : récolte des fruits.
*/
result = collect_disassembled_instructions(gid, status, template.areas, template.count, count);
/* Libération finale */
g_object_unref(G_OBJECT(template.format));
return result;
}