/* 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.
 *
 *  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 "disassembler.h"
#include 
#include 
#include 
#include 
#include "fetch.h"
#include "output.h"
#include "instructions.h"
#include "routines.h"
#include "../../decomp/lang/asm.h"
#include "../../format/format.h"
#include "../../glibext/delayed-int.h"
#include "../../gui/panels/log.h"
#include "../../plugins/pglist.h"
/* ------------------------ 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é*/
    GArchInstruction **instrs;              /* Instructions résultantes    */
    GCodeBuffer *buffer;                    /* 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 *);
/* Crée une tâche de désassemblage différé. */
static GDelayedDisassembly *g_delayed_disassembly_new(GLoadedBinary *, GArchInstruction **, GCodeBuffer *);
/* Opère sur toutes les instructions. */
static void process_all_instructions(wgroup_id_t, GtkStatusStack *, const char *, ins_fallback_cb, GArchProcessor *, 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(GCodeBuffer *, 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)
{
    GDelayedWorkClass *work;                /* Version en classe parente   */
    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 = binaire chargé en attente des résultats.            *
*                format = format du binaire représenté.                       *
*                instrs = emplacement pour la liste d'instructions.           *
*                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, GArchInstruction **instrs, GCodeBuffer *buffer)
{
    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->instrs = instrs;
    result->buffer = buffer;
    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.            *
*                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, 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();
    ins_count = g_arch_processor_count_disassembled_instructions(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, G_BIN_FORMAT(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)
{
    mrange_t *exe_ranges;                   /* Liste de zones exécutables  */
    size_t exe_count;                       /* Nombre de ces zones         */
    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  */
    exe_ranges = g_exe_format_get_x_ranges(format, &exe_count);
    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, exe_ranges, exe_count, 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);
    if (exe_ranges != NULL)
        free(exe_ranges);
}
/******************************************************************************
*                                                                             *
*  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     */
    //size_t i;                               /* Boucle de parcours          */
    GBinRoutine **routines;                 /* Liste des routines trouvées */
    size_t routines_count;                  /* Nombre de ces routines      */
    gid = g_work_queue_define_work_group(get_work_queue());
    //format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
    proc = g_loaded_binary_get_processor(disass->binary);
    routines = g_binary_format_get_routines(G_BIN_FORMAT(disass->format), &routines_count);
    /* Première étape */
    //id = gtk_extended_status_bar_push(statusbar, _("Disassembling..."), true);
    process_disassembly_event(PGA_DISASSEMBLY_STARTED, disass->binary);
    *disass->instrs = disassemble_binary_content(disass->binary, gid, status);
    g_arch_processor_set_disassembled_instructions(proc, *disass->instrs);
    // 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, 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, 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 */
    routines = g_binary_format_get_routines(G_BIN_FORMAT(disass->format), &routines_count);
    //qsort(routines, routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_rcompare);
    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, 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);
    qsort(routines, routines_count, sizeof(GBinRoutine *), (__compar_fn_t)g_binary_routine_compare);
    proc = g_loaded_binary_get_processor(disass->binary);
    print_disassembled_instructions(disass->buffer, disass->format, proc, status);
    g_object_unref(G_OBJECT(proc));
    process_disassembly_event(PGA_DISASSEMBLY_ENDED, disass->binary);
    printf("---fin\n");
    //gtk_extended_status_bar_remove(statusbar, 0/*id*/);
    //run_plugins_on_binary(disass->binary, PGA_BINARY_PRINTED, true);
}
/* ---------------------------------------------------------------------------------- */
/*                            GESTION GLOBALE DE PROCEDURE                            */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : buffer   = tampon de destination pour le texte.              *
*                filename = nom du fichier ciblé à décompiler.                *
*                data     = données en mémoire pour l'empreinte.              *
*                length   = quantité de données à prendre en compte.          *
*                                                                             *
*  Description : Construit la description d'introduction du désassemblage.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void build_disass_prologue(GCodeBuffer *buffer, const char *filename, const char *checksum)
{
    GLangOutput *output;                    /* Modèle de sortie adéquat    */
    GBufferLine *line;                      /* Ligne de destination        */
    bool managed;                           /* Groupe déjà défini ?        */
    size_t len;                             /* Taille du texte             */
    char *content;                          /* Contenu textuel d'une ligne */
    output = g_asm_output_new();
    line = g_lang_output_start_comments(output, buffer);
    if (line != NULL)
    {
        g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
        g_buffer_line_add_flag(line, BLF_WIDTH_MANAGER);
        g_code_buffer_append_new_line(buffer, line);
    }
    managed = (line != NULL);
    /* Introduction */
    line = g_lang_output_continue_comments(output, buffer,
                                           SL(_("Disassembly generated by Chrysalide")));
    g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
    if (!managed)
        g_buffer_line_add_flag(line, BLF_WIDTH_MANAGER);
    g_code_buffer_append_new_line(buffer, line);
    line = g_lang_output_continue_comments(output, buffer,
                                           SL(_("Chrysalide is free software - © 2008-2015 Cyrille Bagard")));
    g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
    g_code_buffer_append_new_line(buffer, line);
    line = g_lang_output_continue_comments(output, buffer, NULL, 0);
    g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
    g_code_buffer_append_new_line(buffer, line);
    /* Fichier */
    len = strlen(_("File: ")) + strlen(filename) + 1;
    content = (char *)calloc(len, sizeof(char));
    snprintf(content, len, "%s%s", _("File: "), filename);
    line = g_lang_output_continue_comments(output, buffer, content, len - 1);
    g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
    g_code_buffer_append_new_line(buffer, line);
    free(content);
    /* Checksum SHA256 */
    len = strlen(_("Sha256: ")) + strlen(checksum);
    content = (char *)calloc(len + 1, sizeof(char));
    snprintf(content, len + 1, "%s%s", _("Sha256: "), checksum);
    line = g_lang_output_continue_comments(output, buffer, content, len - 1);
    g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
    g_code_buffer_append_new_line(buffer, line);
    free(content);
    /* Ligne de séparation */
    line = g_lang_output_continue_comments(output, buffer, NULL, 0);
    g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
    g_code_buffer_append_new_line(buffer, line);
    /* Conclusion */
    line = g_lang_output_end_comments(output, buffer);
    if (line != NULL)
    {
        g_buffer_line_start_merge_at(line, BLC_PHYSICAL);
        g_code_buffer_append_new_line(buffer, line);
    }
    g_object_unref(G_OBJECT(output));
}
/******************************************************************************
*                                                                             *
*  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]              *
*                buffer = 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, GArchInstruction **instrs, GCodeBuffer **buffer, disassembly_ack_fc ack)
{
    GBinFormat *format;                     /* Format associé au binaire   */
    GBinContent *content;                   /* Contenu bianire manipulé    */
    const gchar *checksum;                  /* Identifiant de binaire      */
    GDelayedDisassembly *disass;            /* Désassemblage à mener       */
    GWorkQueue *queue;                      /* Gestionnaire de différés    */
    *buffer = g_code_buffer_new(BLC_ASSEMBLY);
    format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
    content = g_binary_format_get_content(format);
    checksum = g_binary_content_get_cheksum(content);
    g_object_unref(G_OBJECT(content));
    build_disass_prologue(*buffer, g_binary_content_describe(content, true), checksum);
    disass = g_delayed_disassembly_new(binary, instrs, *buffer);
    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);
}