/* 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.
*
* 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 "finder.h"
#include
#include
#include
#include "helper_arm.h"
/* Données utiles à transmettre */
typedef struct _search_domain
{
GBinFormat *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);
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(GBinFormat *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 */
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(format);
target = g_exe_format_get_target_machine(G_EXE_FORMAT(format));
domain.proc = get_arch_processor_for_type(target);
domain.exe_ranges = g_exe_format_get_x_ranges(G_EXE_FORMAT(format), &domain.exe_count);
/* 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;
}