/* 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/known.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_known_format_get_content(G_KNOWN_FORMAT(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; }