/* Chrysalide - Outil d'analyse de fichiers binaires * disassembler.c - encadrement des phases de désassemblage * * Copyright (C) 2010-2014 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 Foobar. If not, see . */ #include "disassembler.h" #include #include #include #include #include "fetch.h" #include "output.h" #include "instructions.h" #include "routines.h" #include "../../format/format.h" #include "../../glibext/delayed-int.h" #include "../../glibext/generators/prologue.h" #include "../../gui/panels/log.h" #include "../../plugins/pglist.h" #include "../human/asm/lang.h" // TODO : REMME -> format ! /* ------------------------ DESASSEMBLAGE DE BINAIRE DIFFERE ------------------------ */ /* Ensembles binaires à désassembler (instance) */ struct _GDelayedDisassembly { GDelayedWork parent; /* A laisser en premier */ GLoadedBinary *binary; /* Destinataire final */ GExeFormat *format; /* Format du binaire représenté*/ GCodingLanguage *lang; /* Traduction en ASM préférée */ GBufferCache *cache; /* Tampon pour le rendu */ }; /* Ensembles binaires à désassembler (classe) */ struct _GDelayedDisassemblyClass { GDelayedWorkClass parent; /* A laisser en premier */ }; /* Initialise la classe des tâches de désassemblage différé. */ static void g_delayed_disassembly_class_init(GDelayedDisassemblyClass *); /* Initialise une tâche de désassemblage différé. */ static void g_delayed_disassembly_init(GDelayedDisassembly *); /* Supprime toutes les références externes. */ static void g_delayed_disassembly_dispose(GDelayedDisassembly *); /* Procède à la libération totale de la mémoire. */ static void g_delayed_disassembly_finalize(GDelayedDisassembly *); /* Crée une tâche de désassemblage différé. */ static GDelayedDisassembly *g_delayed_disassembly_new(GLoadedBinary *, GBufferCache *); /* Opère sur toutes les instructions. */ static void process_all_instructions(wgroup_id_t, GtkStatusStack *, const char *, ins_fallback_cb, GArchProcessor *, GProcContext *, GExeFormat *); /* Opère sur toutes les routines. */ static void process_all_routines(wgroup_id_t, GtkStatusStack *, const char *, rtn_fallback_cb, GArchProcessor *, GExeFormat *); /* Assure le désassemblage en différé. */ static void g_delayed_disassembly_process(GDelayedDisassembly *, GtkStatusStack *); /* -------------------------- GESTION GLOBALE DE PROCEDURE -------------------------- */ /* Construit la description d'introduction du désassemblage. */ static void build_disass_prologue(GBufferCache *, const GCodingLanguage *, const char *, const char *); /* ---------------------------------------------------------------------------------- */ /* DESASSEMBLAGE DE BINAIRE DIFFERE */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour les tâches de désassemblage différé. */ G_DEFINE_TYPE(GDelayedDisassembly, g_delayed_disassembly, G_TYPE_DELAYED_WORK); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des tâches de désassemblage différé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_delayed_disassembly_class_init(GDelayedDisassemblyClass *klass) { GObjectClass *object; /* Autre version de la classe */ GDelayedWorkClass *work; /* Version en classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_disassembly_dispose; object->finalize = (GObjectFinalizeFunc)g_delayed_disassembly_finalize; work = G_DELAYED_WORK_CLASS(klass); work->run = (run_task_fc)g_delayed_disassembly_process; } /****************************************************************************** * * * Paramètres : disass = instance à initialiser. * * * * Description : Initialise une tâche de désassemblage différé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_delayed_disassembly_init(GDelayedDisassembly *disass) { } /****************************************************************************** * * * Paramètres : binary = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_delayed_disassembly_dispose(GDelayedDisassembly *disass) { g_object_unref(G_OBJECT(disass->format)); g_object_unref(G_OBJECT(disass->lang)); G_OBJECT_CLASS(g_delayed_disassembly_parent_class)->dispose(G_OBJECT(disass)); } /****************************************************************************** * * * Paramètres : disass = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_delayed_disassembly_finalize(GDelayedDisassembly *disass) { G_OBJECT_CLASS(g_delayed_disassembly_parent_class)->finalize(G_OBJECT(disass)); } /****************************************************************************** * * * Paramètres : binary = binaire chargé en attente des résultats. * * format = format du binaire représenté. * * buffer = tampon de sortie pour les instructions. * * * * Description : Crée une tâche de désassemblage différé. * * * * Retour : Tâche créée. * * * * Remarques : - * * * ******************************************************************************/ static GDelayedDisassembly *g_delayed_disassembly_new(GLoadedBinary *binary, GBufferCache *cache) { GDelayedDisassembly *result; /* Tâche à retourner */ result = g_object_new(G_TYPE_DELAYED_DISASSEMBLY, NULL); result->binary = binary; result->format = g_loaded_binary_get_format(binary); result->lang = g_asm_language_new(); result->cache = cache; return result; } /****************************************************************************** * * * Paramètres : gid = groupe de travail impliqué. * * status = barre de statut à tenir informée. * * msg = message à faire paraître pour la patience. * * fallback = routine de traitements particuliers. * * proc = ensemble d'instructions désassemblées. * * ctx = contexte fourni pour suivre le désassemblage. * * format = accès aux données du binaire d'origine. * * * * Description : Opère sur toutes les instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void process_all_instructions(wgroup_id_t gid, GtkStatusStack *status, const char *msg, ins_fallback_cb fallback, GArchProcessor *proc, GProcContext *ctx, GExeFormat *format) { guint runs_count; /* Qté d'exécutions parallèles */ size_t ins_count; /* Quantité d'instructions */ size_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t id; /* Identifiant de progression */ guint i; /* Boucle de parcours */ size_t begin; /* Début de bloc de traitement */ size_t end; /* Fin d'un bloc de traitement */ GInstructionsStudy *study; /* Tâche d'étude à programmer */ runs_count = g_get_num_processors(); g_arch_processor_lock(proc); ins_count = g_arch_processor_count_instructions(proc); g_arch_processor_unlock(proc); run_size = ins_count / runs_count; queue = get_work_queue(); id = gtk_status_stack_add_activity(status, msg, ins_count); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = ins_count; else end = begin + run_size; study = g_instructions_study_new(proc, ctx, format, begin, end, id, fallback); g_work_queue_schedule_work(queue, G_DELAYED_WORK(study), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, id); } /****************************************************************************** * * * Paramètres : gid = groupe de travail impliqué. * status = barre de statut à tenir informée. * * msg = message à faire paraître pour la patience. * * fallback = routine de traitements particuliers. * * proc = ensemble d'instructions désassemblées. * * format = accès aux données du binaire d'origine. * * * * Description : Opère sur toutes les routines. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void process_all_routines(wgroup_id_t gid, GtkStatusStack *status, const char *msg, rtn_fallback_cb fallback, GArchProcessor *proc, GExeFormat *format) { GBinPortion *portions; /* Couche première de portions */ GBinRoutine **routines; /* Liste des routines trouvées */ size_t routines_count; /* Nombre de ces routines */ guint runs_count; /* Qté d'exécutions parallèles */ size_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t id; /* Identifiant de progression */ guint i; /* Boucle de parcours */ size_t begin; /* Début de bloc de traitement */ size_t end; /* Fin d'un bloc de traitement */ GRoutinesStudy *study; /* Tâche d'étude à programmer */ portions = g_exe_format_get_portions(format); routines = g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count); runs_count = g_get_num_processors(); run_size = routines_count / runs_count; queue = get_work_queue(); id = gtk_status_stack_add_activity(status, msg, routines_count); for (i = 0; i < runs_count; i++) { begin = i * run_size; if ((i + 1) == runs_count) end = routines_count; else end = begin + run_size; study = g_routines_study_new(proc, portions, routines, routines_count, begin, end, id, fallback); g_work_queue_schedule_work(queue, G_DELAYED_WORK(study), gid); } g_work_queue_wait_for_completion(queue, gid); gtk_status_stack_remove_activity(status, id); g_object_unref(G_OBJECT(portions)); } /****************************************************************************** * * * Paramètres : disass = analyse à mener. * * status = barre de statut à tenir informée. * * * * Description : Assure le désassemblage en différé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_delayed_disassembly_process(GDelayedDisassembly *disass, GtkStatusStack *status) { wgroup_id_t gid; /* Identifiant pour les tâches */ //GBinFormat *format; /* Format du fichier binaire */ GArchProcessor *proc; /* Architecture du binaire */ GProcContext *ctx; /* Contexte de suivi dédié */ //size_t i; /* Boucle de parcours */ GArchInstruction **instrs; /* Instructions résultantes */ size_t count; /* Quantité de ces instructions*/ proc = g_loaded_binary_get_processor(disass->binary); ctx = g_arch_processor_get_context(proc); gid = g_work_queue_define_work_group(get_work_queue()); /* Première étape */ //id = gtk_extended_status_bar_push(statusbar, _("Disassembling..."), true); process_disassembly_event(PGA_DISASSEMBLY_STARTED, disass->binary); instrs = disassemble_binary_content(disass->binary, ctx, gid, status, &count); g_arch_processor_set_instructions(proc, instrs, count); // plugins ////////////////////////// process_disassembly_event(PGA_DISASSEMBLY_RAW, disass->binary); process_all_instructions(gid, status, _("Calling 'link' hook on all instructions..."), g_instructions_study_do_link_operation, proc, ctx, disass->format); // plugins ////////////////////////// process_disassembly_event(PGA_DISASSEMBLY_HOOKED_LINK, disass->binary); //gtk_extended_status_bar_remove(statusbar, id); //run_plugins_on_binary(disass->binary, PGA_BINARY_DISASSEMBLED, true); process_all_instructions(gid, status, _("Calling 'post' hook on all instructions..."), g_instructions_study_do_post_operation, proc, ctx, disass->format); // plugins ////////////////////////// process_disassembly_event(PGA_DISASSEMBLY_HOOKED_POST, disass->binary); /** * TODO : établir les couvertures de fonctions, * pour être en mesure de disposer de résolution de type XXX+yyy lors * de l'établissement des liens. */ /* Seconde étape */ process_all_routines(gid, status, _("Finding remaining limits..."), g_routines_study_compute_limits, proc, disass->format); // plugins ////////////////////////// process_disassembly_event(PGA_DISASSEMBLY_LIMITED, disass->binary); /* Troisième étape */ process_all_instructions(gid, status, _("Establishing links betweek all instructions..."), g_instructions_study_establish_links, proc, ctx, disass->format); // plugins ////////////////////////// process_disassembly_event(PGA_DISASSEMBLY_LINKED, disass->binary); /* Quatrième étape */ // -- old -- id = gtk_extended_status_bar_push(statusbar, _("Detecting loops..."), true); // -- old -- detect_loops_in_code(proc, routines, routines_count, statusbar, 0/*id*/); // -- old -- gtk_extended_status_bar_remove(statusbar, 0/*id*/); /// // plugins ////////////////////////// // -- old -- process_disassembly_event(PGA_DISASSEMBLY_LOOPS, disass->binary); ////////////////////////////////////// // Control-flow analysis... #if 1 process_all_routines(gid, status, _("Control-flow analysis for routines..."), g_routines_study_handle_blocks, proc, disass->format); #endif /* Cinquième étape */ // -- old -- id = gtk_extended_status_bar_push(statusbar, _("Grouping routines instructions..."), true); //qsort(routines, routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_rcompare); // -- old -- group_routines_instructions(proc, routines, routines_count, statusbar, 0/*id*/); // -- old -- gtk_extended_status_bar_remove(statusbar, 0/*id*/); //run_plugins_on_binary(disass->binary, PGA_BINARY_GROUPED, true); // -- old -- process_disassembly_event(PGA_DISASSEMBLY_GROUPED, disass->binary); /* Sixième étape */ // -- old -- id = gtk_extended_status_bar_push(statusbar, _("Ranking each instructions block..."), true); //qsort(routines, routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_rcompare); // -- old -- rank_routines_blocks(routines, routines_count, statusbar, 0/*id*/); // -- old -- gtk_extended_status_bar_remove(statusbar, 0/*id*/); //run_plugins_on_binary(disass->binary, PGA_BINARY_GROUPED, true); // -- old -- process_disassembly_event(PGA_DISASSEMBLY_RANKED, disass->binary); /* Septième étape */ //id = gtk_extended_status_bar_push(statusbar, _("Printing disassembled code..."), true); print_disassembled_instructions(disass->cache, disass->lang, disass->binary, status); /* Rajout de tous les éléments mis en place automatiquement */ void add_to_collection(GDbItem *item, GLoadedBinary *binary) { g_object_ref(G_OBJECT(item)); g_loaded_binary_add_to_collection(binary, item); } g_proc_context_foreach_db_item(ctx, (GFunc)add_to_collection, disass->binary); /* Nettoyage final et sortie ! */ g_object_unref(G_OBJECT(ctx)); g_object_unref(G_OBJECT(proc)); process_disassembly_event(PGA_DISASSEMBLY_ENDED, disass->binary); //gtk_extended_status_bar_remove(statusbar, 0/*id*/); //run_plugins_on_binary(disass->binary, PGA_BINARY_PRINTED, true); } /* ---------------------------------------------------------------------------------- */ /* GESTION GLOBALE DE PROCEDURE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : cache = tampon de destination pour le texte. * * lang = trauducteur pour l'impression finale. * * filename = nom du fichier ciblé à décompiler. * * checksum = empreinte identifiant le binaire chargé. * * * * Description : Construit la description d'introduction du désassemblage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void build_disass_prologue(GBufferCache *cache, const GCodingLanguage *lang, const char *filename, const char *checksum) { char **text; /* Contenu brute à imprimer */ GIntroGenerator *generator; /* Générateur constitué */ text = calloc(4, sizeof(char *)); /* Introduction */ text[0] = strdup(_("Disassembly generated by Chrysalide")); text[1] = strdup(_("Chrysalide is free software - © 2008-2016 Cyrille Bagard")); /* Fichier */ asprintf(&text[2], "%s%s", _("File: "), filename); /* Checksum SHA256 */ asprintf(&text[3], "%s%s", _("Sha256: "), checksum); /* Intégration finale */ generator = g_intro_generator_new(lang, text, 4); g_buffer_cache_append(cache, G_LINE_GENERATOR(generator), BLF_NONE); } /****************************************************************************** * * * Paramètres : binary = représentation de binaire chargé. * * parts = parties binaires à désassembler. * * count = nombre de parties à traiter. * * instrs = liste des instructions chargées. [OUT] * * cache = tampon de code mis en place. [OUT] * * ack = fonction à appeler une fois l'opération terminée. * * * * Description : Procède au désassemblage d'un contenu binaire donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void disassemble_binary(GLoadedBinary *binary, GBufferCache **cache, disassembly_ack_fc ack) { GBinFormat *format; /* Format associé au binaire */ GCodingLanguage *lang; /* Langage de sortie préféré */ GBinContent *content; /* Contenu bianire manipulé */ const gchar *checksum; /* Identifiant de binaire */ GDelayedDisassembly *disass; /* Désassemblage à mener */ GWorkQueue *queue; /* Gestionnaire de différés */ *cache = g_buffer_cache_new(); format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); lang = g_asm_language_new(); content = g_binary_format_get_content(format); checksum = g_binary_content_get_checksum(content); g_object_unref(G_OBJECT(content)); g_object_unref(G_OBJECT(format)); build_disass_prologue(*cache, lang, g_binary_content_describe(content, true), checksum); disass = g_delayed_disassembly_new(binary, *cache); g_signal_connect(disass, "work-completed", G_CALLBACK(ack), binary); queue = get_work_queue(); g_work_queue_schedule_work(queue, G_DELAYED_WORK(disass), DEFAULT_WORK_GROUP); g_object_unref(G_OBJECT(lang)); }