/* Chrysalide - Outil d'analyse de fichiers binaires
 * pe.c - support du format Portable Executable
 *
 * Copyright (C) 2009-2012 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 <http://www.gnu.org/licenses/>.
 */


#include "pe.h"


#include <string.h>


#include "pe-int.h"
#include "section.h"
#include "symbols.h"



/* Initialise la classe des formats d'exécutables PE. */
static void g_pe_format_class_init(GPeFormatClass *);

/* Initialise une instance de format d'exécutable PE. */
static void g_pe_format_init(GPeFormat *);

/* Indique le type d'architecture visée par le format. */
static const char *g_pe_format_get_target_machine(const GPeFormat *);

/* Fournit les références aux zones binaires à analyser. */
//static GBinPart **g_pe_format_get_parts(const GPeFormat *, size_t *);



/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire à parcourir.                       *
*                                                                             *
*  Description : Indique si le format peut être pris en charge ici.           *
*                                                                             *
*  Retour      : true si la réponse est positive, false sinon.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool pe_is_matching(GBinContent *content)
{
    bool result;                            /* Bilan à faire connaître     */
    image_dos_header dos_header;            /* En-tête DOS                 */

    result = false;
#if 0
    if (length >= 2)
    {
        result = (strncmp((const char *)content, "\x4d\x5a" /* MZ */, 2) == 0);
        result &= length >= sizeof(image_dos_header);
    }

    if (result)
    {
        memcpy(&dos_header, content, sizeof(image_dos_header));

        result = length >= (dos_header.e_lfanew + 4);

        result &= (strncmp((const char *)&content[dos_header.e_lfanew],
                           "\x50\x45\x00\x00" /* PE00 */, 4) == 0);

    }
#endif
    return result;

}


/* Indique le type défini pour un format d'exécutable PE. */
G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXE_FORMAT);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des formats d'exécutables PE.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_pe_format_class_init(GPeFormatClass *klass)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une instance de format d'exécutable PE.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_pe_format_init(GPeFormat *format)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire à parcourir.                       *
*                length  = taille du contenu en question.                     *
*                                                                             *
*  Description : Prend en charge un nouveau format PE.                        *
*                                                                             *
*  Retour      : Adresse de la structure mise en place ou NULL en cas d'échec.*
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBinFormat *g_pe_format_new(const bin_t *content, off_t length)
{
    GPeFormat *result;                      /* Structure à retourner       */
    off_t offset;                           /* Tête de lecture             */


    int i;


    result = g_object_new(G_TYPE_PE_FORMAT, NULL);

    //g_binary_format_set_content(G_BIN_FORMAT(result), content, length);

    offset = 0;

    if (!read_dos_image_header(result, &offset, &result->dos_header))
    {
        /* TODO */
        return NULL;
    }

    offset = result->dos_header.e_lfanew;

    if (!read_pe_nt_header(result, &offset, &result->nt_headers))
    {
        /* TODO */
        return NULL;
    }

    result->section_offset = offset;

    printf("offset :: 0x%08x\n", offset);

    printf("Format :: 0x%08x\n", result->nt_headers.signature);

    printf("directories :: %d\n", result->nt_headers.optional_header.number_of_rva_and_sizes);

    for (i = 0; i < result->nt_headers.optional_header.number_of_rva_and_sizes; i++)
        printf(" [%d] addr=0x%08x size=%d\n", i,
               result->nt_headers.optional_header.data_directory[i].virtual_address,
               result->nt_headers.optional_header.data_directory[i].size);


    load_pe_symbols(result);


    return G_BIN_FORMAT(result);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations chargées à consulter.                  *
*                                                                             *
*  Description : Indique le type d'architecture visée par le format.          *
*                                                                             *
*  Retour      : Identifiant de l'architecture ciblée par le format.          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static const char *g_pe_format_get_target_machine(const GPeFormat *format)
{
    return "i386";

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations chargées à consulter.                  *
*                count  = quantité de zones listées. [OUT]                    *
*                                                                             *
*  Description : Fournit les références aux zones binaires à analyser.        *
*                                                                             *
*  Retour      : Zones binaires à analyser.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
#if 0
static GBinPart **g_pe_format_get_parts(const GPeFormat *format, size_t *count)
{
    GBinPart **result;                      /* Tableau à retourner         */
    uint16_t i;                             /* Boucle de parcours          */
    image_section_header section;           /* En-tête de section PE       */
    GBinPart *part;                         /* Partie à intégrer à la liste*/
    char name[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de section utilisable   */

    result = NULL;
    *count = 0;

    for (i = 0; i < format->nt_headers.file_header.number_of_sections; i++)
    {
        if (!find_pe_section_by_index(format, i, &section))
            continue;

        if (section.characteristics & IMAGE_SCN_MEM_EXECUTE)
        {
            part = g_binary_part_new();

            memset(name, 0, (IMAGE_SIZEOF_SHORT_NAME + 1) * sizeof(char));
            memcpy(name, section.name, (IMAGE_SIZEOF_SHORT_NAME + 1) * sizeof(char));

            g_binary_part_set_name(part, name);

            printf("section '%s'\n", name);

            g_binary_part_set_values(part,
                                     section.pointer_to_raw_data,
                                     section.size_of_raw_data,
                                     section.virtual_address);

            printf("section[%d] start=0x%08x  size=%d  addr=0x%08x\n", i,
                   section.pointer_to_raw_data,
                   section.size_of_raw_data,
                   section.virtual_address);

            result = (GBinPart **)realloc(result, ++(*count) * sizeof(GBinPart *));
            result[*count - 1] = part;

        }

    }

    return result;

}
#endif