/* OpenIDA - Outil d'analyse de fichiers binaires
 * binary.c - traitement des flots de code binaire
 *
 * Copyright (C) 2008 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  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 "binary.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "delayed.h"
#include "line_code.h"          /* TODO : supprimer ? */
#include "line_comment.h"       /* TODO : supprimer ? */
#include "line_prologue.h"
#include "prototype.h"
#include "../common/extstr.h"
#include "../glibext/delayed.h"
#include "../format/format.h"
#include "../panel/log.h"
#include "../plugins/pglist.h"
#include "../format/dbg_format.h"
#ifndef _
#   define _(str) str
#endif
/* Description de fichier binaire (instance) */
struct _GOpenidaBinary
{
    GObject parent;                         /* A laisser en premier        */
    char *filename;                         /* Fichier chargé en mémoire   */
    off_t bin_length;                       /* Taille des données brutes   */
    bin_t *bin_data;                        /* Données binaires brutes     */
    GExeFormat *format;                     /* Format du binaire           */
    GArchProcessor *proc;                   /* Architecture du binaire     */
    GRenderingLine *lines;                  /* Lignes de rendu en place    */
    GRenderingOptions *options;             /* Options de désassemblage    */
};
/* Description de fichier binaire (classe) */
struct _GOpenidaBinaryClass
{
    GObjectClass parent;                    /* A laisser en premier        */
    /* Signaux */
    void (* disassembly_done) (GOpenidaBinary *);
};
/* Initialise la classe des descriptions de fichier binaire. */
static void g_openida_binary_class_init(GOpenidaBinaryClass *);
/* Initialise une description de fichier binaire. */
static void g_openida_binary_init(GOpenidaBinary *);
/* Charge en mémoire le contenu d'un fichier. */
bin_t *map_binary_file(const char *, off_t *);
/* Construit la description d'introduction du désassemblage. */
GRenderingLine *build_binary_prologue(const char *, const uint8_t *, off_t);
/* Acquitte la fin d'un désasemblage différé et complet. */
void ack_completed_disassembly(GDisassManager *, GOpenidaBinary *, GRenderingLine *, GOpenidaBinary *);
/* Indique le type défini pour une description de fichier binaire. */
G_DEFINE_TYPE(GOpenidaBinary, g_openida_binary, G_TYPE_OBJECT);
/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des descriptions de fichier binaire.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_openida_binary_class_init(GOpenidaBinaryClass *klass)
{
    g_signal_new("disassembly-done",
                 G_TYPE_OPENIDA_BINARY,
                 G_SIGNAL_RUN_LAST,
                 G_STRUCT_OFFSET(GOpenidaBinaryClass, disassembly_done),
                 NULL, NULL,
                 g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : line = instance à initialiser.                               *
*                                                                             *
*  Description : Initialise une description de fichier binaire.               *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_openida_binary_init(GOpenidaBinary *line)
{
}
/******************************************************************************
*                                                                             *
*  Paramètres  : filename = nom du fichier à charger.                         *
*                                                                             *
*  Description : Charge en mémoire le contenu d'un fichier.                   *
*                                                                             *
*  Retour      : Adresse de la représentation ou NULL en cas d'échec.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GOpenidaBinary *g_openida_binary_new_from_file(const char *filename)
{
    GOpenidaBinary *result;                 /* Adresse à retourner         */
    result = g_object_new(G_TYPE_OPENIDA_BINARY, NULL);
    result->filename = strdup(filename);
    result->bin_data = map_binary_file(filename, &result->bin_length);
    if (result->bin_data == NULL) goto lbf_error;
    result->format = G_EXE_FORMAT(load_new_format(FMT_EXEC, result->bin_data, result->bin_length));
    if (result->format == NULL) goto lbf_error;
    switch (g_exe_format_get_target_machine(result->format))
    {
        case FTM_JVM:
            log_simple_message(LMT_INFO, _("Detected architecture: Java Virtual Machine"));
            break;
        case FTM_MIPS:
            log_simple_message(LMT_INFO, _("Detected architecture: Microprocessor without Interlocked Pipeline Stages"));
            break;
        case FTM_386:
            log_simple_message(LMT_INFO, _("Detected architecture: i386"));
            break;
        default:
            log_simple_message(LMT_INFO, _("Unknown architecture"));
            goto lbf_error;
            break;
    }
    result->proc = get_arch_processor_from_format(result->format);
    result->options = g_rendering_options_new(result->format, result->proc);
    g_rendering_options_show_address(result->options, MRD_BLOCK, true);
    g_rendering_options_show_code(result->options, MRD_BLOCK, true);
    g_rendering_options_show_address(result->options, MRD_GRAPH, true);
    g_rendering_options_show_code(result->options, MRD_GRAPH, false);
    return result;
 lbf_error:
    //unload_binary_file(result);
    return NULL;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte pour les recherches XPath.                *
*                path    = chemin d'accès au noeud XML à lire.                *
*                                                                             *
*  Description : Charge en mémoire le contenu d'un fichier à partir d'XML.    *
*                                                                             *
*  Retour      : Adresse de la représentation ou NULL en cas d'échec.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GOpenidaBinary *g_openida_binary_new_from_xml(xmlXPathContextPtr context, const char *path)
{
    GOpenidaBinary *result;                 /* Adresse à retourner         */
    size_t access_len;                      /* Taille d'un chemin interne  */
    char *access;                           /* Chemin pour une sous-config.*/
    char *filename;                         /* Chemin du binaire à charger */
    result = NULL;
    /* Chemin du fichier à retrouver */
    access_len = strlen(path) + strlen("/Filename") + 1;
    access = calloc(access_len, sizeof(char));
    snprintf(access, access_len, "%s/Filename", path);
    filename = get_node_text_value(context, access);
    free(access);
    /* Chargement */
    if (filename != NULL)
    {
        result = g_openida_binary_new_from_file(filename);
        free(filename);
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary  = élément binaire à traiter.                         *
*                xdoc    = structure XML en cours d'édition.                  *
*                context = contexte à utiliser pour les recherches.           *
*                path    = chemin d'accès réservé au binaire.                 *
*                                                                             *
*  Description : Ecrit une sauvegarde du binaire dans un fichier XML.         *
*                                                                             *
*  Retour      : true si l'opération a bien tourné, false sinon.              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool g_openida_binary_save(const GOpenidaBinary *binary, xmlDocPtr xdoc, xmlXPathContextPtr context, const char *path)
{
    bool result;                            /* Bilan à faire remonter      */
    char *access;                           /* Chemin d'accès à un élément */
    result = true;
    /* Nom du fichier associé */
    access = strdup(path);
    access = stradd(access, "/Filename");
    result &= add_content_to_node(xdoc, context, access, binary->filename);
    free(access);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à traiter.                          *
*                                                                             *
*  Description : Lance l'analyse d'un élément binaire chargé.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_openida_binary_analyse(GOpenidaBinary *binary)
{
    GDisassManager *manager;                /* Gestionnaire de différés    */
    GBinPart **parts;                       /* Parties d'élément binaire   */
    size_t parts_count;                     /* Nombre de ces parties       */
    GDelayedDisassembly *disass;            /* Désassemblage à mener       */
    manager = get_disassembly_manager();
    parts = g_exe_format_get_parts(binary->format, &parts_count);
    qsort(parts, parts_count, sizeof(GBinPart *), g_binary_part_compare);
    disass = g_delayed_disassembly_new(binary, parts, parts_count);
    g_signal_connect(manager, "disassembly-completed",
                     G_CALLBACK(ack_completed_disassembly), binary);
    g_delayed_queue_schedule_work(G_WORK_QUEUE(manager), G_DELAYED_WORK(disass));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à consulter.                        *
*                                                                             *
*  Description : Fournit une description humaine d'un élément binaire.        *
*                                                                             *
*  Retour      : Chaîne de caractères humainenement lisible de représentation.*
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
const char *g_openida_binary_to_string(const GOpenidaBinary *binary)
{
    return binary->filename;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à consulter.                        *
*                                                                             *
*  Description : Fournit le fichier correspondant à l'élément binaire.        *
*                                                                             *
*  Retour      : Nom de fichier avec chemin absolu.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
const char *g_openida_binary_get_filename(const GOpenidaBinary *binary)
{
    return binary->filename;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à consulter.                        *
*                length = taille en octets des données chargées. [OUT]        *
*                                                                             *
*  Description : Fournit les détails du contenu binaire chargé en mémoire.    *
*                                                                             *
*  Retour      : Pointeur vers le début des données.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bin_t *g_openida_binary_get_data(const GOpenidaBinary *binary, off_t *length)
{
    if (length != NULL)
        *length = binary->bin_length;
    return binary->bin_data;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à consulter.                        *
*                                                                             *
*  Description : Fournit le format de fichier reconnu dans le contenu binaire.*
*                                                                             *
*  Retour      : Adresse du format reconnu.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GExeFormat *g_openida_binary_get_format(const GOpenidaBinary *binary)
{
    return binary->format;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à consulter.                        *
*                                                                             *
*  Description : Fournit les options d'affichage définies pour le binaire.    *
*                                                                             *
*  Retour      : Adresse des options d'affichage.                             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GRenderingOptions *g_openida_binary_get_options(const GOpenidaBinary *binary)
{
    return binary->options;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = élément binaire à consulter.                        *
*                                                                             *
*  Description : Fournit les lignes de rendu issues du désassemblage.         *
*                                                                             *
*  Retour      : Lignes issues du désassemblage.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GRenderingLine *g_openida_binary_get_lines(const GOpenidaBinary *binary)
{
    return binary->lines;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : filename = nom du fichier à charger.                         *
*                length   = taille des données mises en mémoire. [OUT]        *
*                                                                             *
*  Description : Charge en mémoire le contenu d'un fichier.                   *
*                                                                             *
*  Retour      : Adresse du contenu binaire ou NULL en cas d'échec.           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bin_t *map_binary_file(const char *filename, off_t *length)
{
    uint8_t *result;                        /* Données à retourner         */
    int fd;                                 /* Fichier ouvert en lecture   */
    struct stat info;                       /* Informations sur le fichier */
    int ret;                                /* Bilan d'un appel            */
    fd = open(filename, 0, O_RDONLY);
    if (fd == -1)
    {
        perror("open()");
        return NULL;
    }
    ret = fstat(fd, &info);
    if (ret == -1)
    {
        perror("fstat()");
        close(fd);
        return NULL;
    }
    *length = info.st_size;
    result = (uint8_t *)mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
    if (result == MAP_FAILED)
    {
        perror("mmap()");
        result = NULL;
    }
    ret = close(fd);
    if (ret == -1)
        perror("close()");
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : filename = nom du fichier chargé.                            *
*                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   : -                                                            *
*                                                                             *
******************************************************************************/
GRenderingLine *build_binary_prologue(const char *filename, const uint8_t *data, off_t length)
{
    GRenderingLine *result;                 /* Contenu à renvoyer          */
    size_t len;                             /* Taille du texte             */
    char *content;                          /* Contenu textuel d'une ligne */
    GRenderingLine *line;                   /* Représentation à ajouter    */
    GChecksum *checksum;                    /* Calcul de l'empreinte       */
    const gchar *hex;                       /* Valeur hexadécimale du SHA  */
    result = NULL;/* FIXME DL_LIST_HEAD_INIT(  **/
    line = g_prologue_line_new("Disassembly generated by OpenIDA");
    g_rendering_line_add_to_lines(&result, line);
    line = g_prologue_line_new("OpenIDA is free software - © 2008-2009 Cyrille Bagard");
    g_rendering_line_add_to_lines(&result, line);
    line = g_prologue_line_new("");
    g_rendering_line_add_to_lines(&result, line);
    /* Fichier */
    len = strlen(_("File: ")) + strlen(filename);
    content = (char *)calloc(len + 1, sizeof(char));
    snprintf(content, len + 1, "%s%s", _("File: "), filename);
    line = g_prologue_line_new(content);
    g_rendering_line_add_to_lines(&result, line);
    free(content);
    /* Checksum SHA256 */
    checksum = g_checksum_new(G_CHECKSUM_SHA256);
    g_checksum_update(checksum, data, length);
    hex = g_checksum_get_string(checksum);
    len = strlen(_("Sha256: ")) + strlen(hex);
    content = (char *)calloc(len + 1, sizeof(char));
    snprintf(content, len + 1, "%s%s", _("Sha256: "), hex);
    g_checksum_free(checksum);
    line = g_prologue_line_new(content);
    g_rendering_line_add_to_lines(&result, line);
    free(content);
    line = g_prologue_line_new("");
    g_rendering_line_add_to_lines(&result, line);
    line = g_prologue_line_new("");
    g_rendering_line_add_to_lines(&result, line);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : manager = gestionnaire des traitements en parallèle.         *
*                binary  = binaire dont le contenu est à analyser.            *
*                lines   = lignes de rendu produites par le désasemblage.     *
*                user    = élément binaire à l'origine du traitement.         *
*                                                                             *
*  Description : Acquitte la fin d'un désasemblage différé et complet.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void ack_completed_disassembly(GDisassManager *manager, GOpenidaBinary *binary, GRenderingLine *lines, GOpenidaBinary *user)
{
    GRenderingLine *line;                   /* "Première" ligne de rendu   */
    GPluginModule **pglist;                 /* Liste de greffons           */
    size_t pgcount;                         /* Taille de cette liste       */
    size_t i;                               /* Boucle de parcours          */
    /* Si ce n'est pas pour nous... */
    if (binary != user) return;
    binary->lines = lines;
    line = g_rendering_line_find_by_address(lines, NULL,
                                            g_exe_format_get_entry_point(binary->format));
    if (line != NULL) g_rendering_line_add_flag(line, RLF_ENTRY_POINT);
    /* Action post-désassemblage */
    pglist = get_all_plugins_for_action(PGA_CODE_PROCESS, &pgcount);
    if (pgcount > 0)
    {
        for (i = 0; i < pgcount; i++)
            g_plugin_module_execute_action_on_binary(pglist[i], binary, PGA_CODE_PROCESS);
        free(pglist);
    }
    /* On réintègre le flot premier */
    gdk_threads_enter();
    g_signal_emit_by_name(binary, "disassembly-done");
    gdk_flush ();
    gdk_threads_leave();
}