/* Chrysalide - Outil d'analyse de fichiers binaires
 * stackvars.c - substitution des emplacements de pile par des variables
 *
 * Copyright (C) 2009-2017 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 <http://www.gnu.org/licenses/>.
 */


#include "stackvars.h"

#if 0
#include <analysis/routine.h>
#include <arch/x86/operand.h>
#include <format/executable.h>
#include <format/format.h>


#include "operand.h"



/* Effectue tous les remplacements possibles dans une routine. */
static bool replace_stack_vars_in_routine(GBinRoutine *, GRenderingLine *);

/* Effectue d'éventuels remplacements dans une instruction. */
static bool replace_stack_vars_in_instruction(GArchInstruction *, GBinRoutine *, bool);

/* Effectue d'éventuels remplacements dans un opérande. */
static bool replace_stack_var_in_operand(const GArchOperand *, GBinRoutine *, bool, GArchOperand **);



/******************************************************************************
*                                                                             *
*  Paramètres  : ref = espace de référencement global.                        *
*                                                                             *
*  Description : Initialise le greffon pour les bornes de routine.            *
*                                                                             *
*  Retour      : true.                                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

G_MODULE_EXPORT bool init_plugin(GObject *ref)
{
    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Fournit une indication sur le type d'opération(s) menée(s).  *
*                                                                             *
*  Retour      : Description d'une action.                                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

G_MODULE_EXPORT PluginAction get_plugin_action(void)
{
    return PGA_CODE_PROCESS;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : binary = représentation binaire à traiter.                   *
*                action = action attendue.                                    *
*                                                                             *
*  Description : Exécute une action définie sur un binaire chargé.            *
*                                                                             *
*  Retour      : true si une action a été menée, false sinon.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

G_MODULE_EXPORT bool execute_action_on_binary(GLoadedBinary *binary, PluginAction action)
{
    bool result;                            /* Bilan à retourner           */
    GRenderingLine *lines;                  /* Lignes de rendu             */
    GExeFormat *format;                     /* Format du binaire fourni    */
    GBinRoutine **routines;                 /* Liste des routines trouvées */
    size_t routines_count;                  /* Nombre de ces routines      */
    size_t i;                               /* Boucle de parcours          */

    result = false;

    lines = g_loaded_binary_get_lines(binary);

    format = g_loaded_binary_get_format(binary);
    routines = NULL;//g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count);
    routines_count = 0; //

    for (i = 0; i < routines_count; i++)
        result |= replace_stack_vars_in_routine(routines[i], lines);

    g_object_unref(G_OBJECT(format));

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : routine = routine dont le code est à analyser.               *
*                lines   = ensemble des lignes de rendu.                      *
*                                                                             *
*  Description : Effectue tous les remplacements possibles dans une routine.  *
*                                                                             *
*  Retour      : true si une action a été menée, false sinon.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool replace_stack_vars_in_routine(GBinRoutine *routine, GRenderingLine *lines)
{
    bool result;                            /* Bilan à retourner           */
    const mrange_t *range;                  /* Emplacement du symbole      */
    vmpa_t start;                           /* Adresse de début de routine */
    vmpa_t end;                             /* Adresse de fin de routine   */
    GRenderingLine *iter;                   /* Boucle de parcours          */
    GArchInstruction *instr;                /* Instruction à ausculter     */

    result = false;

    range = g_binary_symbol_get_range(G_BIN_SYMBOL(routine));

    start = get_mrange_addr(range)->virtual;
    end = start + get_mrange_length(range);

    for (iter = g_rendering_line_find_by_address(lines, NULL, start);
         iter != NULL && get_rendering_line_address(iter) < end;
         iter = g_rendering_line_get_next_iter(lines, iter, NULL))
    {
        if (!G_IS_CODE_LINE(iter)) continue;

        instr = g_code_line_get_instruction(G_CODE_LINE(iter));

        result |= replace_stack_vars_in_instruction(instr, routine, true);

    }

    if (!result) return false;
    else result = false;

    for (iter = g_rendering_line_find_by_address(lines, NULL, start);
         iter != NULL && get_rendering_line_address(iter) < end;
         iter = g_rendering_line_get_next_iter(lines, iter, NULL))
    {
        if (!G_IS_CODE_LINE(iter)) continue;

        instr = g_code_line_get_instruction(G_CODE_LINE(iter));

        result |= replace_stack_vars_in_instruction(instr, routine, false);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : instr   = instruction dont le contenu peut être modifié.     *
*                routine = routine contenant l'instruction analysée.          *
*                dryrun  = mode enregistrement ou remplacement ?              *
*                                                                             *
*  Description : Effectue d'éventuels remplacements dans une instruction.     *
*                                                                             *
*  Retour      : true si une action a été menée, false sinon.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool replace_stack_vars_in_instruction(GArchInstruction *instr, GBinRoutine *routine, bool dryrun)
{
    bool result;                            /* Bilan à renvoyer            */
    GArchOperand *new;                      /* Opérande d'encapsulation    */
    size_t opcount;                         /* Nombre d'opérandes          */
    size_t i;                               /* Boucle de parcours          */
    const GArchOperand *operand;            /* Opérande de l'instruction   */

    result = false;
    new = NULL;     /* Pour GCC */

    g_arch_instruction_lock_operands(instr);

    opcount = _g_arch_instruction_count_operands(instr);

    for (i = 0; i < opcount; i++)
    {
        operand = _g_arch_instruction_get_operand(instr, i);
        result = replace_stack_var_in_operand(operand, routine, dryrun, &new);

        if (!dryrun && result)
        {
            _g_arch_instruction_replace_operand(instr, operand, new);

            result = true;

        }

        g_object_unref(G_OBJECT(operand));

    }

    g_arch_instruction_unlock_operands(instr);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : operand = opérande dont le contenu est à analyser.           *
*                routine = routine contenant l'opérande analysé.              *
*                dryrun  = mode enregistrement ou remplacement ?              *
*                new     = éventuelle nouvelle encapsulation créée.           *
*                                                                             *
*  Description : Effectue d'éventuels remplacements dans un opérande.         *
*                                                                             *
*  Retour      : Nouvel opérande de remplacemet ou NULL si aucun besoin.      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool replace_stack_var_in_operand(const GArchOperand *operand, GBinRoutine *routine, bool dryrun, GArchOperand **new)
{
    bool result;                            /* Bilan à retourner           */
    GX86ModRMOperand *modrm;                /* Autre version de l'opérande */
    uint8_t scale;                          /* Puissance de deux           */
    const GX86Register *index;              /* Registre servant d'indice   */
    const GX86Register *base;               /* Registre de base            */
    const GImmOperand *displacement;        /* Décalage supplémentaire     */
    size_t value;                           /* Position dans la pile       */
    bool negative;                          /* Direction dans la pile      */

    result = false;

    if (G_IS_X86_MOD_RM_OPERAND(operand))
    {
        modrm = G_X86_MOD_RM_OPERAND(operand);

        g_x86_mod_rm_operand_get_scale_and_index(modrm, &scale, &index);
        base = g_x86_mod_rm_operand_get_base(modrm);
        displacement = g_x86_mod_rm_operand_get_displacement(modrm);

        if (scale == 0 && g_x86_register_is_base_pointer(index) && base == NULL
            && g_imm_operand_to_size_t(displacement, &value, &negative))
        {
            if (dryrun) g_binary_routine_register_if_needed(routine, value, negative);
            else *new = g_stack_var_operand_new(routine, modrm);

            result = true;

        }

    }

    return result;

}
#endif