/* Chrysalide - Outil d'analyse de fichiers binaires
 * finder.c - recherche de gadgets pour ROP
 *
 * Copyright (C) 2015 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 "finder.h"
#include 
#include 
#include 
#include 
#include "helper_arm.h"
/* Données utiles à transmettre */
typedef struct _search_domain
{
    GExeFormat *format;                     /* Format du fichier binaire   */
    GBinContent *content;                   /* Contenu associé récupéré    */
    GArchProcessor *proc;                   /* Processeur idéal en place   */
    GProcContext *ctx;                      /* Contexte de désassemblage   */
    mrange_t *exe_ranges;                   /* Liste de zones exécutables  */
    size_t exe_count;                       /* Nombre de ces zones         */
    phys_t sum;                             /* Surface totale à parcourir  */
    size_t runs_count;                      /* Nombre de passages à faire  */
    size_t runs_done;                       /* Nombre de passages effectués*/
} search_domain;
/* Désassemble rapidement une instruction. */
static GArchInstruction *disassemble_instruction_in_domain(const search_domain *, vmpa2t *);
/* Désassemble en amont d'une position autant que possible. */
static GArchInstruction *look_backward_for_gadgets(const search_domain *, const mrange_t *, const vmpa2t *, unsigned int);
/* Etablit une liste de tous les gadgets présents. */
static GArchInstruction **list_all_gadgets_in_domain(const search_domain *, unsigned int, update_search_progress_cb, GObject *, size_t *);
/******************************************************************************
*                                                                             *
*  Paramètres  : domain = ensemble d'auxiliaires à disposition.               *
*                pos    = tête de lecture pour le désassemblage.              *
*                                                                             *
*  Description : Désassemble rapidement une instruction.                      *
*                                                                             *
*  Retour      : Instruction créée ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static GArchInstruction *disassemble_instruction_in_domain(const search_domain *domain, vmpa2t *pos)
{
    GArchInstruction *result;               /* Instruction à retourner     */
    GProcContext *ctx;                      /* Contexte de désassemblage   */
    ctx = domain->ctx;  /* TODO : copie */
    result = g_arch_processor_disassemble(domain->proc, ctx, domain->content, pos, domain->format);
    if (result != NULL)
    {
        g_arch_instruction_call_hook(result, IPH_LINK, domain->proc, ctx, domain->format);
        g_arch_instruction_call_hook(result, IPH_POST, domain->proc, ctx, domain->format);
    }
    //g_object_unref(G_OBJECT(ctx)); /* TODO */
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : domain    = ensemble d'auxiliaires à disposition.            *
*                exe_range = couverture globale dont il ne faut pas sortir.   *
*                ret       = point de retour final déjà trouvé.               *
*                max_depth = profondeur maximale des recherches.              *
*                                                                             *
*  Description : Désassemble en amont d'une position autant que possible.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static GArchInstruction *look_backward_for_gadgets(const search_domain *domain, const mrange_t *exe_range, const vmpa2t *ret, unsigned int max_depth)
{
    GArchInstruction *result;               /* Liste de gagdets trouvées   */
    const phys_t *ins_sizes;                /* Tailles potentielles        */
    size_t sizes_count;                     /* Quantité de tailles à tester*/
    vmpa2t last;                            /* Dernier point de départ     */
    unsigned int i;                         /* Boucle de parcours #1       */
    GArchInstruction *instr;                /* Elément de gadget trouvé    */
    size_t k;                               /* Boucle de parcours #2       */
    vmpa2t start;                           /* Point de départ courant     */
    vmpa2t end;                             /* Point d'arrivée obtenu      */
    phys_t diff;                            /* Volume de données traité    */
    mrange_t range;                         /* Couverture de l'instruction */
    result = NULL;
    ins_sizes = (phys_t []){ 2, 4 };    /* FIXME */
    sizes_count = 2;
    copy_vmpa(&last, ret);
    /* On parcours jusqu'à la profondeur maximale */
    for (i = 0; i < max_depth; i++)
    {
        instr = NULL;
        for (k = 0; k < sizes_count && instr == NULL; k++)
        {
            copy_vmpa(&start, &last);
            deminish_vmpa(&start, ins_sizes[k]);
            /* Est-on toujours dans les clous ? */
            if (!mrange_contains_addr(exe_range, &start)) break;
            copy_vmpa(&end, &start);
            instr = disassemble_instruction_in_domain(domain, &end);
            if (instr == NULL) continue;
            /* La jointure est-elle parfaite ? */
            if (cmp_vmpa(&end, &last) != 0)
            {
                g_object_unref(G_OBJECT(instr));
                instr = NULL;
                continue;
            }
            /* S'il s'agit d'un point de retour, on laisse la main à une autre liste */
            if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT)
            {
                g_object_unref(G_OBJECT(instr));
                instr = NULL;
                continue;
            }
        }
        /* Aucune instruction n'a été trouvée à cette profondeur, on s'arrête donc */
        if (instr == NULL) break;
        copy_vmpa(&last, &start);
        diff = compute_vmpa_diff(&end, &start);
        init_mrange(&range, &start, diff);
        g_arch_instruction_set_range(instr, &range);
        g_arch_instruction_merge_lists(&result, &instr);
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : domain    = ensemble d'auxiliaires à disposition.            *
*                max_depth = profondeur maximale des recherches.              *
*                update    = fonction de suivi mise à disposition.            *
*                data      = données à associer à une phase d'actualisation.  *
*                count     = nombre de gadgets trouvés. [OUT]                 *
*                                                                             *
*  Description : Etablit une liste de tous les gadgets présents.              *
*                                                                             *
*  Retour      : Liste de listes d'instructions, à libérer après usage.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static GArchInstruction **list_all_gadgets_in_domain(const search_domain *domain, unsigned int max_depth, update_search_progress_cb update, GObject *data, size_t *count)
{
    GArchInstruction **result;              /* Liste de listes à renvoyer  */
    phys_t done;                            /* Accumulation des quantités  */
    size_t i;                               /* Boucle de parcours #1       */
    phys_t max;                             /* Borne d'un parcours         */
    phys_t k;                               /* Boucle de parcours #2       */
    gdouble fraction;                       /* Progression générale        */
    vmpa2t ret;                             /* Emplacement du retour       */
    vmpa2t tmp;                             /* Copie de travail modifiable */
    GArchInstruction *gadget;               /* Nouveau gadget détecté      */
    phys_t diff;                            /* Volume de données traité    */
    mrange_t ins_range;                     /* Emplacement d'une instruct° */
    vmpa2t end;                             /* Point d'arrivée obtenu      */
    GArchInstruction *new;                  /* Nouvelle liste de gadgets   */
    result = NULL;
    *count = 0;
    done = 0;
    for (i = 0; i < domain->exe_count; i++)
    {
        max = get_mrange_length(&domain->exe_ranges[i]);
        for (k = 0; k < max; k++)
        {
            /* Affichage de la progression */
            fraction = 1.0 * (domain->runs_done * domain->sum + done + k);
            fraction /= (domain->runs_count * domain->sum);
            update(data, fraction);
            /* Réception de la première / dernière instruction */
            copy_vmpa(&ret, get_mrange_addr(&domain->exe_ranges[i]));
            advance_vmpa(&ret, k);
            copy_vmpa(&tmp, &ret);
            gadget = disassemble_instruction_in_domain(domain, &tmp);
            if (gadget == NULL) continue;
            /* A-t-on bien affaire à une instruction de retour ? */
            if (!(g_arch_instruction_get_flags(gadget) & AIF_RETURN_POINT))
            {
                g_object_unref(G_OBJECT(gadget));
                continue;
            }
            /* Ne déborde-t-on pas dans une zone voisine ? */
            diff = compute_vmpa_diff(&ret, &tmp);
            init_mrange(&ins_range, &ret, diff);
            compute_mrange_end_addr(&ins_range, &end);
            if (!mrange_contains_mrange(&domain->exe_ranges[i], &ins_range))
            {
                g_object_unref(G_OBJECT(gadget));
                continue;
            }
            /* Ajout d'un nouvel ensemble de gadgets */
            g_arch_instruction_set_range(gadget, &ins_range);
            //printf(" ROP @ 0x%08x / 0x%08x\n", gadget->range.addr.virtual, ret.virtual);
            new = look_backward_for_gadgets(domain, &domain->exe_ranges[i], &ret, max_depth);
            g_arch_instruction_merge_lists(&new, &gadget);
            result = (GArchInstruction **)realloc(result, ++(*count) * sizeof(GArchInstruction *));
            result[*count - 1] = new;
        }
        done += max;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary    = binaire dont le contenu est à traiter.           *
*                max_depth = profondeur maximale des recherches.              *
*                update    = fonction de suivi mise à disposition.            *
*                data      = données à associer à une phase d'actualisation.  *
*                count     = nombre de gadgets trouvés. [OUT]                 *
*                                                                             *
*  Description : Etablit une liste de tous les gadgets présents.              *
*                                                                             *
*  Retour      : Liste de listes d'instructions, à libérer après usage.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
found_rop_list *list_all_gadgets(GExeFormat *format, unsigned int max_depth, update_search_progress_cb update, GObject *data, size_t *count)
{
    found_rop_list *result;                 /* Liste de listes à renvoyer  */
    const char *target;                     /* Sous-traitance requise      */
    search_domain domain;                   /* Outils pour la recherche    */
    GBinPortion *portions;                  /* Couche première de portions */
    GProcContext **contexts;                /* Contextes pour recherches   */
    char **names;                           /* Désignations humaines liées */
    size_t i;                               /* Boucle de parcours          */
    /* Constitution du socle commun */
    g_object_ref(G_OBJECT(format));
    domain.format = format;
    domain.content = g_binary_format_get_content(G_BIN_FORMAT(format));
    target = g_exe_format_get_target_machine(format);
    domain.proc = get_arch_processor_for_type(target);
    bool collect_x_ranges(GBinPortion *portion, GBinPortion *parent, BinaryPortionVisit visit, void *unused)
    {
        const mrange_t *range;
        if (visit == BPV_SHOW)
        {
            if (g_binary_portion_get_rights(portion) & PAC_EXEC)
            {
                range = g_binary_portion_get_range(portion);
                domain.exe_ranges = (mrange_t *)realloc(domain.exe_ranges, ++domain.exe_count * sizeof(mrange_t));
                copy_mrange(&domain.exe_ranges[domain.exe_count - 1], range);
            }
        }
        return true;
    }
    domain.exe_ranges = NULL;
    domain.exe_count = 0;
    portions = g_exe_format_get_portions(format);
    g_binary_portion_visit(portions, (visit_portion_fc)collect_x_ranges, NULL);
    g_object_unref(G_OBJECT(portions));
    /* Récupération des différents contextes */
    if (strcmp(target, "armv7") == 0)
        contexts = get_rop_contexts_for_arm(domain.proc, &names, count);
    else
    {
        contexts = (GProcContext **)calloc(1, sizeof(GProcContext *));
        names = (char **)calloc(1, sizeof(char *));
        *count = 1;
        contexts[0] = g_arch_processor_get_context(domain.proc);
        names[0] = _("all");
    }
    /* Calcul de la surface totale à parcourir */
    domain.sum = 0;
    for (i = 0; i < domain.exe_count; i++)
        domain.sum += get_mrange_length(&domain.exe_ranges[i]);
    /* Détermination des différents parcours */
    domain.runs_count = *count;
    domain.runs_done = 0;
    /* Parcours des différentes surfaces */
    result = (found_rop_list *)calloc(*count, sizeof(found_rop_list));
    for (i = 0; i < *count; i++)
    {
        result[i].category = names[i];
        domain.ctx = contexts[i];
        result[i].gadgets = list_all_gadgets_in_domain(&domain, max_depth, update, data, &result[i].count);
        g_object_unref(G_OBJECT(domain.ctx));
        domain.runs_done++;
    }
    free(names);
    free(contexts);
    free(domain.exe_ranges);
    g_object_unref(G_OBJECT(domain.proc));
    g_object_unref(G_OBJECT(domain.content));
    g_object_unref(G_OBJECT(domain.format));
    return result;
}