/* Chrysalide - Outil d'analyse de fichiers binaires
* area.c - définition et manipulation des aires à désassembler
*
* Copyright (C) 2014 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 "area.h"
#include
#include
#include "../../analysis/contents/restricted.h"
#include "../../common/bits.h"
#include "../../gui/panels/log.h"
/* Zone mémoire bien bornée */
typedef struct _mem_area_v2
{
GBinFormat *format; /* Format du fichier binaire */
GBinContent *content; /* Données binaires à lire */
GArchProcessor *proc; /* Architecture du binaire */
SourceEndian endianness; /* Boutisme de cette machine */
mrange_t range; /* Couverture de la zone */
phys_t packing_size; /* Granularité des découpages */
bitfield_t *processed; /* Octets traités dans la zone */
GArchInstruction **instructions; /* Instructions en place */
bool is_exec; /* Zone exécutable ? */
} mem_area_v2;
/* Initialise une aire de données à partir d'une adresse donnée. */
static void init_mem_area_from_addr_v2(mem_area_v2 *, const vmpa2t *, phys_t, const GLoadedBinary *);
/* Libère d'une aire de données les ressources allouées. */
static void fini_mem_area_v2(mem_area_v2 *);
/* Indique si une zone donnée est intégralement vierge ou non. */
static bool is_range_blank_in_mem_area_v2(mem_area_v2 *, phys_t, phys_t);
/* Marque une série d'octets comme ayant été traités. */
static bool mark_range_in_mem_area_as_processed_v2(mem_area_v2 *, GArchInstruction *, bool);
/* Crée une instruction issue d'un désassemblage brut. */
static GArchInstruction *load_raw_instruction_from_mem_area_v2(mem_area_v2 *, phys_t, vmpa2t *, phys_t *);
/* Procède au désassemblage d'un contenu binaire non exécutable. */
static void load_data_from_mem_area_v2(mem_area_v2 *, GProcContext *, const vmpa2t *, GtkStatusStack *, activity_id_t);
/* S'assure qu'une aire contient toutes ses instructions. */
static void fill_mem_area_v2(mem_area_v2 *, mem_area_v2 *, size_t, GProcContext *, GtkStatusStack *, activity_id_t);
/* Rassemble les instructions conservées dans une zone donnée. */
static GArchInstruction *get_instructions_from_mem_area_v2(const mem_area_v2 *);
/* S'assure de la présence d'un début de routine à un point. */
static void update_address_as_routine(GBinFormat *, const vmpa2t *);
/**
* Content :: à passer en restricted.
* Les sections croisées : pas de sens de les traiter ici, car au final une instruction sera à cheval
* sur deux sections différentes -> incohérence au niveau de l'éditeur.
* => régler le périmètres de segments règle donc tous les soucis.
*/
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à initialiser. *
* addr = adresse de départ de l'espace à mettre en place. *
* len = longueur de l'espace à créer. *
* binary = binaire analysé content quantités d'informations. *
* *
* Description : Initialise une aire de données à partir d'une adresse donnée.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void init_mem_area_from_addr_v2(mem_area_v2 *area, const vmpa2t *addr, phys_t len, const GLoadedBinary *binary)
{
GBinContent *content; /* Données binaires à lire */
assert(len > 0);
area->format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
content = g_binary_format_get_content(area->format);
area->proc = g_loaded_binary_get_processor(binary);
area->endianness = g_arch_processor_get_endianness(area->proc);
init_mrange(&area->range, addr, len);
area->content = g_restricted_content_new(content, &area->range);
area->packing_size = 2; /* FIXME */
area->processed = create_bit_field(len, false);
area->instructions = (GArchInstruction **)calloc(len, sizeof(GArchInstruction *));
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à nettoyer en mémoire. *
* *
* Description : Libère d'une aire de données les ressources allouées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void fini_mem_area_v2(mem_area_v2 *area)
{
phys_t len; /* Etendue du parcours total */
phys_t i; /* Boucle de parcours */
//g_object_unref(area->format); /* FIXME */
g_object_unref(area->content);
//g_object_unref(area->proc); /* FIXME */
delete_bit_field(area->processed);
len = get_mrange_length(&area->range);
for (i = 0; i < len; i++)
if (area->instructions[i] != NULL)
g_object_unref(G_OBJECT(area->instructions[i]));
free(area->instructions);
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* start = début de la zone à manipuler. *
* len = taille de cette même aire de données. *
* *
* Description : Indique si une zone donnée est intégralement vierge ou non. *
* *
* Retour : true si l'aire visée n'a jamais été traitée, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool is_range_blank_in_mem_area_v2(mem_area_v2 *area, phys_t start, phys_t len)
{
bool result; /* Résultat à renvoyer */
assert((start + len) <= get_mrange_length(&area->range));
result = !test_in_bit_field(area->processed, start, len);
return result;
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* instr = instruction à mémoriser pour la suite. *
* force = impose l'enregistrement de l'instruction. *
* *
* Description : Marque une série d'octets comme ayant été traités. *
* *
* Retour : true si l'enregistrement a bien été réalisé, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool mark_range_in_mem_area_as_processed_v2(mem_area_v2 *area, GArchInstruction *instr, bool force)
{
bool result; /* Bilan d'action à renvoyer */
const vmpa2t *start; /* Adresse de départ de la zone*/
const mrange_t *range; /* Emplacement d'instruction */
const vmpa2t *addr; /* Début de la zone à traiter */
phys_t len; /* Taille de l'aire visée */
phys_t offset; /* Décallage de départ */
start = get_mrange_addr(&area->range);
range = g_arch_instruction_get_range(instr);
addr = get_mrange_addr(range);
len = get_mrange_length(range);
offset = compute_vmpa_diff(start, addr);
result = set_atomic_in_bit_field(area->processed, offset, len);
/* Si l'instruction était bien la première à s'inscrire... */
result |= force;
if (result)
area->instructions[offset] = instr;
return result;
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* offset = point de départ au sein de l'aire en question. *
* pos = tête de lecture dans l'espace global. *
* size = taille de l'instruction mise en place. [OUT] *
* *
* Description : Crée une instruction issue d'un désassemblage brut. *
* *
* Retour : Instruction mise en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static GArchInstruction *load_raw_instruction_from_mem_area_v2(mem_area_v2 *area, phys_t offset, vmpa2t *pos, phys_t *size)
{
GArchInstruction *result; /* Instruction à retourner */
GBinContent *content; /* Données binaires à lire */
SourceEndian endianness; /* Boutisme de cette machine */
phys_t sz; /* Volume de données traité */
vmpa2t prev; /* Boucle de parcours */
result = NULL;
content = area->content;
endianness = area->endianness;
sz = area->packing_size;
if (get_virt_addr(pos) % sz == 0 && is_range_blank_in_mem_area_v2(area, offset, sz))
{
*size = sz;
copy_vmpa(&prev, pos);
result = g_raw_instruction_new_array(content, MDS_FROM_BYTES(sz), 1, pos, endianness);
if (result == NULL)
copy_vmpa(pos, &prev);
}
if (result == NULL)
{
*size = 1;
result = g_raw_instruction_new_array(content, MDS_8_BITS, 1, pos, endianness);
}
return result;
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* index = indice de l'aire à considérer pendant l'opération. *
* binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* start = démarrage de l'exécution au sein de la zone. *
* force = force la création d'au moins une instruction. *
* status = barre de statut à actualiser. *
* id = identifiant du groupe de progression à l'affichage. *
* *
* Description : Procède au désassemblage d'un contenu binaire exécutable. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void load_code_from_mem_area_v2(mem_area_v2 *area, mem_area_v2 *list, size_t count, GProcContext *ctx, const vmpa2t *start, bool force, GtkStatusStack *status, activity_id_t id)
{
GBinFormat *format; /* Format du fichier binaire */
GArchProcessor *proc; /* Architecture du binaire */
GBinContent *content; /* Données binaires à lire */
phys_t init_diff; /* Position initiale de lecture*/
phys_t alen; /* Taille de l'aire utilisée */
bool forced_once; /* Préfigure une sortie rapide */
phys_t i; /* Boucle de parcours */
vmpa2t pos; /* Boucle de parcours */
vmpa2t prev; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
phys_t diff; /* Volume de données traité */
mrange_t range; /* Couverture de l'instruction */
bool done; /* Enregistrement effectué ? */
vmpa2t sym_addr; /* Adresse de nouveau symbole */
bool has_new_sym; /* Statut d'un dépilement */
GBinSymbol *symbol; /* Symbole créé en parallèle */
/* Récupération des informations de base */
format = area->format;
proc = area->proc;
content = area->content;
init_diff = compute_vmpa_diff(get_mrange_addr(&area->range), start);
alen = get_mrange_length(&area->range);
copy_vmpa(&pos, start);
/* Traitement de la zone */
forced_once = false;
for (i = init_diff; i < alen; i += diff)
{
/**
* On réalise un premier test informel (car non atomique) peu coûteux
* avant de se lancer dans un désassemblage d'instruction potentiellement
* inutile.
*/
if (!is_range_blank_in_mem_area_v2(area, i, 1))
break;
/* Décodage d'une nouvelle instruction */
copy_vmpa(&prev, &pos);
instr = g_arch_processor_disassemble(proc, ctx, content, &pos, G_EXE_FORMAT(format));
if (instr != NULL)
diff = compute_vmpa_diff(&prev, &pos);
else
{
if (i == init_diff && force)
{
instr = load_raw_instruction_from_mem_area_v2(area, i, &pos, &diff);
forced_once = true;
}
if (instr == NULL)
break;
}
/* Enregistrement des positions et adresses */
init_mrange(&range, &prev, diff);
g_arch_instruction_set_range(instr, &range);
/* Progression dans les traitements */
done = mark_range_in_mem_area_as_processed_v2(area, instr, false);
if (!done)
{
g_object_unref(G_OBJECT(instr));
break;
}
gtk_status_stack_update_activity_value(status, id, diff);
assert(!is_range_blank_in_mem_area_v2(area, i, diff));
/* Enregistrement d'un éventuel début de routine */
if (g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START)
update_address_as_routine(format, &prev);
/* Eventuel renvoi vers d'autres adresses */
g_arch_instruction_call_hook(instr, IPH_FETCH, proc, ctx, format);
/* Insertion des symboles découverts en parallèle */
for (has_new_sym = g_proc_context_pop_new_symbol_at(ctx, &sym_addr);
has_new_sym;
has_new_sym = g_proc_context_pop_new_symbol_at(ctx, &sym_addr))
{
has_new_sym = g_binary_format_find_symbol_at(format, &sym_addr, &symbol);
if (has_new_sym)
insert_extra_symbol_into_mem_areas_v2(list, count, symbol);
}
/* Rupture du flot d'exécution ? */
if (forced_once || g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT)
break;
}
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* ctx = contexte offert en soutien à un désassemblage. *
* start = démarrage de l'exécution au sein de la zone. *
* status = barre de statut à actualiser. *
* id = identifiant du groupe de progression à l'affichage. *
* *
* Description : Procède au désassemblage d'un contenu binaire non exécutable.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void load_data_from_mem_area_v2(mem_area_v2 *area, GProcContext *ctx, const vmpa2t *start, GtkStatusStack *status, activity_id_t id)
{
phys_t diff; /* Volume de données traité */
phys_t alen; /* Taille de l'aire utilisée */
vmpa2t pos; /* Boucle de parcours */
phys_t i; /* Boucle de parcours */
vmpa2t prev; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
mrange_t range; /* Couverture de l'instruction */
bool done; /* Enregistrement effectué ? */
/* Récupération des informations de base */
diff = compute_vmpa_diff(get_mrange_addr(&area->range), start);
alen = get_mrange_length(&area->range);
copy_vmpa(&pos, start);
/* Traitement de la zone */
for (i = diff; i < alen; i += diff)
{
/* On cherche à obtenir l'assurance que le traitement n'a jamais été fait */
if (!is_range_blank_in_mem_area_v2(area, i, 1))
break;
instr = NULL;
copy_vmpa(&prev, &pos);
/* Décodage d'une nouvelle instruction, sur mesure puis minimale */
instr = load_raw_instruction_from_mem_area_v2(area, i, &pos, &diff);
/* On rencontre ici un morceau déjà traité. */
if (instr == NULL) break;
/* Enregistrement des positions et adresses */
assert(diff == compute_vmpa_diff(&prev, &pos));
init_mrange(&range, &prev, diff);
g_arch_instruction_set_range(instr, &range);
/* Progression dans les traitements */
done = mark_range_in_mem_area_as_processed_v2(area, instr, false);
if (!done)
{
g_object_unref(G_OBJECT(instr));
break;
}
gtk_status_stack_update_activity_value(status, id, diff);
assert(!is_range_blank_in_mem_area_v2(area, i, diff));
/* On laisse une chance au code pour se reprendre... */
if (area->is_exec) break;
}
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* status = barre de statut à actualiser. *
* id = identifiant du groupe de progression à l'affichage. *
* *
* Description : S'assure qu'une aire contient toutes ses instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void fill_mem_area_v2(mem_area_v2 *area, mem_area_v2 *list, size_t count, GProcContext *ctx, GtkStatusStack *status, activity_id_t id)
{
const vmpa2t *addr; /* Début de la zone à traiter */
phys_t len; /* Taille de la zone à remplir */
phys_t i; /* Boucle de parcours */
vmpa2t start; /* Adresse de départ de combles*/
addr = get_mrange_addr(&area->range);
len = get_mrange_length(&area->range);
for (i = 0; i < len; i++)
{
if (is_range_blank_in_mem_area_v2(area, i, 1))
{
copy_vmpa(&start, addr);
advance_vmpa(&start, i);
if (area->is_exec && get_virt_addr(&start) % area->packing_size == 0)
load_code_from_mem_area_v2(area, list, count, ctx, &start, false, status, id);
if (is_range_blank_in_mem_area_v2(area, i, 1))
load_data_from_mem_area_v2(area, ctx, &start, status, id);
}
assert(!is_range_blank_in_mem_area_v2(area, i, 1));
}
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* *
* Description : Rassemble les instructions conservées dans une zone donnée. *
* *
* Retour : Liste d'instructions prêtes à emploi. *
* *
* Remarques : - *
* *
******************************************************************************/
static GArchInstruction *get_instructions_from_mem_area_v2(const mem_area_v2 *area)
{
GArchInstruction *result; /* Liste d'instr. à renvoyer */
phys_t len; /* Nombre d'instructions au max*/
phys_t i; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
result = NULL;
len = get_mrange_length(&area->range);
for (i = 0; i < len; i++)
{
instr = area->instructions[i];
if (instr != NULL)
{
g_object_ref(G_OBJECT(instr));
g_arch_instruction_add_to_list(&result, instr);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : list = listes de zones utable à consulter. *
* count = nombre de zones mises en place. *
* addr = adresse à retrouver dans les aires présentes. *
* *
* Description : Détermine une liste de zones contigües à traiter. *
* *
* Retour : Indice de la zone trouvée, ou nombre d'aires en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
mem_area_v2 *find_memory_area_by_addr_v2(mem_area_v2 *list, size_t count, const vmpa2t *addr)
{
mem_area_v2 *result; /* Elément trouvé à renvoyer */
int find_mem_area(const vmpa2t *_addr, const mem_area_v2 *_area)
{
int status; /* Bilan à retourner */
if (mrange_contains_addr(&_area->range, _addr))
status = 0;
else
status = cmp_vmpa(_addr, get_mrange_addr(&_area->range));
return status;
}
result = bsearch(addr, list, count, sizeof(mem_area_v2), (__compar_fn_t)find_mem_area);
return result;
}
/******************************************************************************
* *
* Paramètres : binary = binaire analysé contenant quantités d'infos. *
* bin_length = quantité d'octets à traiter au total. *
* count = nombre de zones mises en place. [OUT] *
* *
* Description : Détermine une liste de zones contigües à traiter. *
* *
* Retour : Liste de zones mémoire à libérer après usage. *
* *
* Remarques : - *
* *
******************************************************************************/
mem_area_v2 *compute_memory_areas_v2(const GLoadedBinary *binary, phys_t bin_length, size_t *count)
{
mem_area_v2 *result; /* Liste à renvoyer */
GExeFormat *format; /* Format de fichier associé */
mrange_t *exe_ranges; /* Liste de zones exécutables */
size_t exe_count; /* Nombre de ces zones */
GBinSymbol **symbols; /* Symboles à représenter */
size_t sym_count; /* Qté de symboles présents */
vmpa2t last; /* Dernière bordure rencontrée */
bool status; /* Bilan d'une conversion */
size_t i; /* Boucle de parcours #1 */
const vmpa2t *border; /* Nouvelle bordure rencontrée */
mem_area_v2 *area; /* Zone avec valeurs à éditer */
vmpa2t tmp; /* Stockage temporaire */
GPortionLayer *layer; /* Couche première de portions */
GBinPortion **portions; /* Morceaux d'encadrement */
size_t portions_count; /* Taille de cette liste */
const vmpa2t *portion_start; /* Point de départ de portion */
const vmpa2t *portion_next; /* Départ de portion suivante */
size_t j; /* Boucle de parcours #2 */
SymbolType type; /* Nature d'un symbole */
const mrange_t *range; /* Couverture d'un symbole */
phys_t length; /* Taille de ce même symbole */
phys_t new_length; /* Nouvelle taille déterminée */
result = NULL;
*count = 0;
/**
* Le parcours n'est valide que si les zones exécutables sont triées !
*/
format = g_loaded_binary_get_format(binary);
exe_ranges = g_exe_format_get_x_ranges(format, &exe_count);
symbols = g_binary_format_get_symbols(G_BIN_FORMAT(format), &sym_count);
/* Première étape : on comble les trous ! */
status = g_exe_format_translate_offset_into_vmpa(format, 0, &last);
assert(status);
for (i = 0; i < exe_count; i++)
{
border = get_mrange_addr(&exe_ranges[i]);
/* Zone tampon à constituer */
if (cmp_vmpa(&last, border) < 0)
{
result = (mem_area_v2 *)realloc(result, ++(*count) * sizeof(mem_area_v2));
area = &result[*count - 1];
init_mem_area_from_addr_v2(area, &last, compute_vmpa_diff(&last, border), binary);
area->is_exec = false;
}
/* Insertion d'une zone exécutable déjà définie */
result = (mem_area_v2 *)realloc(result, ++(*count) * sizeof(mem_area_v2));
area = &result[*count - 1];
init_mem_area_from_addr_v2(area, get_mrange_addr(&exe_ranges[i]),
get_mrange_length(&exe_ranges[i]), binary);
area->is_exec = true;
/* Avancée du curseur */
compute_mrange_end_addr(&exe_ranges[i], &last);
}
/* Extension finale complémentaire ? */
area = &result[*count - 1];
copy_vmpa(&tmp, get_mrange_addr(&area->range));
advance_vmpa(&tmp, get_mrange_length(&area->range));
if (get_phy_addr(&tmp) < bin_length)
{
result = (mem_area_v2 *)realloc(result, ++(*count) * sizeof(mem_area_v2));
area = &result[*count - 1];
init_mem_area_from_addr_v2(area, &tmp, bin_length - get_phy_addr(&tmp), binary);
area->is_exec = false;
}
/* Seconde étape : on s'assure du découpage autour des portions pour respecter l'alignement */
layer = g_exe_format_get_main_layer(format);
portions = g_portion_layer_collect_all_portions(layer, &portions_count);
for (i = 1; i < portions_count; i++)
{
portion_start = get_mrange_addr(g_binary_portion_get_range(portions[i]));
/**
* Si plusieurs portions débutent au même endroit, il ne sert
* à rien de découper plusieurs fois.
*/
if ((i + 1) < portions_count)
{
portion_next = get_mrange_addr(g_binary_portion_get_range(portions[i + 1]));
if (cmp_vmpa(portion_start, portion_next) == 0)
continue;
}
for (j = 0; j < *count; j++)
{
area = &result[j];
if (!mrange_contains_addr(&area->range, portion_start))
continue;
/* Si le déccoupage actuel ne correspond pas au besoin des portions... */
if (cmp_vmpa(get_mrange_addr(&area->range), portion_start) != 0)
{
fini_mem_area_v2(&result[j]);
result = (mem_area_v2 *)realloc(result, ++(*count) * sizeof(mem_area_v2));
memmove(&result[j + 2], &result[j + 1], (*count - j - 2) * sizeof(mem_area_v2));
status = result[j].is_exec;
/* Première moitié */
area = &result[j];
copy_vmpa(&tmp, get_mrange_addr(&area->range));
length = get_mrange_length(&area->range);
new_length = compute_vmpa_diff(&tmp, portion_start);
init_mem_area_from_addr_v2(area, &tmp, new_length, binary);
area->is_exec = status;
/* Seconde moitié */
length -= get_mrange_length(&area->range);
area = &result[j + 1];
init_mem_area_from_addr_v2(area, portion_start, length, binary);
area->is_exec = status;
}
j = *count;
}
}
if (portions != NULL)
free(portions);
g_object_unref(G_OBJECT(layer));
/* Troisième étape : on insère les symboles existants */
for (i = 0; i < sym_count; i++)
{
type = g_binary_symbol_get_target_type(symbols[i]);
/**
* On ne garde que les symboles renvoyant directement une ou
* plusieurs instructions, c'est à dire les symboles valides
* pour un appel à g_binary_symbol_get_instruction().
*
* Les instructions des autres symboles sont obtenues et mises
* en place durant la procédure de désassemblage.
*/
if (type == STP_ROUTINE || type == STP_ENTRY_POINT || type == STP_CODE_LABEL)
continue;
range = g_binary_symbol_get_range(symbols[i]);
length = get_mrange_length(range);
if (length == 0)
continue;
insert_extra_symbol_into_mem_areas_v2(result, *count, symbols[i]);
}
/* Nettoyage final */
if (exe_ranges != NULL)
free(exe_ranges);
/// FIXME g_object_unref(G_OBJECT(format));
return result;
}
/******************************************************************************
* *
* Paramètres : areas = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* symbol = élément nouveau à venir insérer dans les zones. *
* *
* Description : Insère un symbole dans un découpage en aires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void insert_extra_symbol_into_mem_areas_v2(mem_area_v2 *areas, size_t count, const GBinSymbol *symbol)
{
SymbolType type; /* Type de symbole */
GArchInstruction *list; /* Ensemble à insérer */
GArchInstruction *iter; /* Boucle de parcours */
const mrange_t *range; /* Emplacement d'instruction */
const vmpa2t *addr; /* Départ de cet emplacement */
mem_area_v2 *area; /* Zone d'accueil désignée */
VMPA_BUFFER(loc); /* Description d'un emplacement*/
phys_t start; /* Point de départ */
type = g_binary_symbol_get_target_type(symbol);
if (!HAS_DATA_INSTR(type))
return;
list = g_binary_symbol_get_instruction(symbol);
for (iter = list; iter != NULL; iter = g_arch_instruction_get_next_iter(list, iter, ~0))
{
range = g_arch_instruction_get_range(iter);
addr = get_mrange_addr(range);
/* Une aire d'accueil existe-t-elle ? */
area = find_memory_area_by_addr_v2(areas, count, addr);
if (area == NULL)
{
vmpa2_virt_to_string(addr, MDS_UNDEFINED, loc, NULL);
log_variadic_message(LMT_WARNING, _("No place found for symbol located at %s."), loc);
continue;
}
/* L'instruction est-elle accueillie dans son intégralité ? */
start = compute_vmpa_diff(get_mrange_addr(&area->range), addr);
if (start + get_mrange_length(range) > get_mrange_length(&area->range))
{
vmpa2_virt_to_string(addr, MDS_UNDEFINED, loc, NULL);
log_variadic_message(LMT_WARNING, _("The symbol located at %s is too big for one place only."), loc);
continue;
}
/* Inscription d'une instruction de symbole (sans retour arrière possible :/ ) */
mark_range_in_mem_area_as_processed_v2(area, iter, true);
g_object_ref(G_OBJECT(iter));
}
}
/******************************************************************************
* *
* Paramètres : list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* status = barre de statut à actualiser. *
* id = identifiant du groupe de progression à l'affichage. *
* *
* Description : S'assure que l'ensemble des aires est entièrement décodé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void ensure_all_mem_areas_are_filled(mem_area_v2 *list, size_t count, GProcContext *ctx, GtkStatusStack *status, activity_id_t id)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < count; i++)
fill_mem_area_v2(&list[i], list, count, ctx, status, id);
}
/******************************************************************************
* *
* Paramètres : areas = série d'aires représentant à contenu à parcourir. *
* count = nombre de ces zones présentes. *
* *
* Description : Rassemble les instructions conservées dans des zones données.*
* *
* Retour : Liste d'instructions prêtes à emploi. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *collect_instructions_from_mem_areas_v2(const mem_area_v2 *list, size_t count)
{
GArchInstruction *result; /* Liste d'instr. à renvoyer */
size_t i; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction(s) à insérer */
result = NULL;
for (i = 0; i < count; i++)
{
instr = get_instructions_from_mem_area_v2(&list[i]);
g_arch_instruction_merge_lists(&result, &instr);
}
return result;
}
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
//////////////////////////////////::://////////////////////////////////////////////////////////////////////
/* Zone mémoire bien bornée */
typedef struct _mem_area
{
mrange_t range; /* Couverture de la zone */
unsigned long *processed; /* Octets traités dans la zone */
GArchInstruction **instructions; /* Instructions en place */
bool has_sym; /* Représentation via symbole ?*/
union
{
bool exec; /* Zone exécutable ? */
GBinSymbol *symbol; /* Symbole associé à la zone */
};
} mem_area;
/* Initialise une aire de données à partir d'une adresse donnée. */
static void init_mem_area_from_addr(mem_area *, const vmpa2t *, phys_t);
/* Initialise une aire de données à partir d'un espace donné. */
static void init_mem_area_from_range(mem_area *, const mrange_t *);
/* Initialise une aire de données à partir d'un morceau donné. */
static void init_mem_area_from_bigger_area(mem_area *, const vmpa2t *, phys_t, const mem_area *);
/* Copie certaines propriétés d'une aire à une autre. */
static void copy_mem_area_properties(mem_area *, const mem_area *);
/* Libère d'une aire de données les ressources allouées. */
static void fini_mem_area(mem_area *);
/* Indique si une zone donnée est intégralement vierge ou non. */
static bool is_range_blank_in_mem_area(mem_area *, phys_t, phys_t, GArchInstruction *);
/* Marque une série d'octets comme ayant été traités. */
static bool mark_range_in_mem_area_as_processed(mem_area *, phys_t, phys_t, GArchInstruction *);
/* S'assure de la présence d'un début de routine à un point. */
//static void update_address_as_routine(GBinFormat *, const vmpa2t *);
/* Procède au désassemblage d'un contenu binaire non exécutable. */
static void load_data_from_mem_area(mem_area *, mem_area *, size_t, const GLoadedBinary *, GProcContext *, const vmpa2t *, status_blob_info *);
/* Rassemble les instructions conservées dans une zone donnée. */
static GArchInstruction *get_instructions_from_mem_area(const mem_area *);
///////////////////////////////
/* Insère un symbole dans un découpage en aires. */
static bool insert_extra_symbol_into_mem_areas(mem_area **, size_t *, GBinSymbol *, size_t);
/* Manipule la cartographie des octets traités d'une zone. */
typedef bool (* visit_bytes_map_fc) (mem_area *, phys_t, phys_t, GArchInstruction *);
/* Manipule la cartographie des octets d'aires de données. */
static bool handle_bytes_map_in_mem_area(mem_area *, size_t, const mrange_t *, GArchInstruction *, visit_bytes_map_fc);
/* Indique si une zone donnée est intégralement vierge ou non. */
static bool is_range_blank_in_mem_areas(mem_area *, size_t, const mrange_t *);
/* Marque une série d'octets comme ayant été traités. */
static bool mark_range_in_mem_areas_as_processed(mem_area *, size_t, GArchInstruction *);
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à initialiser. *
* addr = adresse de départ de l'espace à mettre en place. *
* len = longueur de l'espace à créer. *
* *
* Description : Initialise une aire de données à partir d'une adresse donnée.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void init_mem_area_from_addr(mem_area *area, const vmpa2t *addr, phys_t len)
{
size_t requested; /* Nombre de mots à allouer */
init_mrange(&area->range, addr, len);
requested = len / sizeof(unsigned long);
if (len % sizeof(unsigned long) != 0) requested++;
area->processed = (unsigned long *)calloc(requested, sizeof(unsigned long));
area->instructions = (GArchInstruction **)calloc(len, sizeof(GArchInstruction *));
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à initialiser. *
* range = espace limitant à associer à l'aire de données. *
* *
* Description : Initialise une aire de données à partir d'un espace donné. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void init_mem_area_from_range(mem_area *area, const mrange_t *range)
{
phys_t len; /* Taille de la zone courante */
size_t requested; /* Nombre de mots à allouer */
copy_mrange(&area->range, range);
len = get_mrange_length(range);
requested = len / sizeof(unsigned long);
if (len % sizeof(unsigned long) != 0) requested++;
area->processed = (unsigned long *)calloc(requested, sizeof(unsigned long));
area->instructions = (GArchInstruction **)calloc(len, sizeof(GArchInstruction *));
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à initialiser. *
* addr = adresse de départ de l'espace à mettre en place. *
* len = longueur de l'espace à créer. *
* ref = aire de référence avant découpage. *
* *
* Description : Initialise une aire de données à partir d'un morceau donné. *
* *
* Retour : - *
* *
* Remarques : On considère que la zone de destination est inclue dans *
* celle de référence. *
* *
******************************************************************************/
static void init_mem_area_from_bigger_area(mem_area *area, const vmpa2t *addr, phys_t len, const mem_area *ref)
{
phys_t start; /* Point de départ de la copie */
phys_t i; /* Boucle de parcours */
size_t index; /* Cellule de tableau visée #1 */
unsigned int remaining; /* Nombre de bits restants #1 */
size_t ref_index; /* Cellule de tableau visée #2 */
unsigned int ref_remaining; /* Nombre de bits restants #2 */
init_mem_area_from_addr(area, addr, len);
assert(mrange_contains_mrange(&ref->range, &area->range));
start = compute_vmpa_diff(get_mrange_addr(&ref->range), get_mrange_addr(&area->range));
for (i = 0; i < len; i++)
{
index = i / (sizeof(unsigned long) * 8);
remaining = i % (sizeof(unsigned long) * 8);
ref_index = (start + i) / (sizeof(unsigned long) * 8);
ref_remaining = (start + i) % (sizeof(unsigned long) * 8);
if (ref->processed[ref_index] & (1ul << ref_remaining))
area->processed[index] |= (1ul << remaining);
area->instructions[i] = ref->instructions[start + i];
}
}
/******************************************************************************
* *
* Paramètres : dest = aire délimitée représentant des données. *
* src = aire délimitée contenant les informations à copier. *
* *
* Description : Copie certaines propriétés d'une aire à une autre. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void copy_mem_area_properties(mem_area *dest, const mem_area *src)
{
dest->has_sym = src->has_sym;
if (src->has_sym)
dest->symbol = src->symbol;
else
dest->exec = src->exec;
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à nettoyer en mémoire. *
* *
* Description : Libère d'une aire de données les ressources allouées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void fini_mem_area(mem_area *area)
{
free(area->processed);
#if 0
if (area->has_sym)
g_object_unref(area->symbol); /* FIXME ?! */
#endif
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* start = début de la zone à manipuler. *
* len = taille de cette même aire de données. *
* instr = instruction à mémoriser pour la suite. *
* *
* Description : Indique si une zone donnée est intégralement vierge ou non. *
* *
* Retour : true si l'aire visée n'a jamais été traitée, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool is_range_blank_in_mem_area(mem_area *area, phys_t start, phys_t len, GArchInstruction *instr)
{
bool result; /* Bilan à renvoyer */
phys_t max; /* Point d'arrêt de la boucle */
phys_t i; /* Boucle de parcours */
size_t index; /* Cellule de tableau visée */
unsigned int remaining; /* Nombre de bits restants */
if (area->has_sym)
return false;
max = start + len;
assert(max <= get_mrange_length(&area->range));
result = true;
for (i = start; i < max && result; i++)
{
index = i / (sizeof(unsigned long) * 8);
remaining = i % (sizeof(unsigned long) * 8);
result &= ((area->processed[index] & (1ul << remaining)) == 0);
}
return result;
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* start = début de la zone à manipuler. *
* len = taille de cette même aire de données. *
* instr = instruction à mémoriser pour la suite. *
* *
* Description : Marque une série d'octets comme ayant été traités. *
* *
* Retour : true. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool mark_range_in_mem_area_as_processed(mem_area *area, phys_t start, phys_t len, GArchInstruction *instr)
{
phys_t max; /* Point d'arrêt de la boucle */
phys_t i; /* Boucle de parcours */
size_t index; /* Cellule de tableau visée */
unsigned int remaining; /* Nombre de bits restants */
max = start + len;
assert(max <= get_mrange_length(&area->range));
for (i = start; i < max; i++)
{
index = i / (sizeof(unsigned long) * 8);
remaining = i % (sizeof(unsigned long) * 8);
//assert((area->processed[index] & (1ul << remaining)) == 0);
area->processed[index] |= (1ul << remaining);
}
//assert(area->instructions[start] == NULL);
area->instructions[start] = instr;
return true;
}
/******************************************************************************
* *
* Paramètres : format = format binaire en cours de traitement. *
* addr = adresse d'une instruction présentée comme première. *
* *
* Description : S'assure de la présence d'un début de routine à un point. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void update_address_as_routine(GBinFormat *format, const vmpa2t *addr)
{
GBinSymbol *symbol; /* Symbole présent ou créé */
bool found; /* Détection de symbole */
SymbolType sym_type; /* Type de symbole en place */
bool wrong_type; /* Analyse plus fine de ce type*/
mrange_t range; /* Etendue du symbole à créer */
VMPA_BUFFER(loc); /* Traduction de l'adresse */
char name[5 + VMPA_MAX_LEN]; /* Nom de symbole nouveau */
GBinRoutine *routine; /* Nouvelle routine trouvée */
found = g_binary_format_find_symbol_at(format, addr, &symbol);
if (found)
{
sym_type = g_binary_symbol_get_target_type(symbol);
wrong_type = (sym_type != STP_ROUTINE && sym_type != STP_ENTRY_POINT);
}
if (!found || (found && wrong_type))
{
init_mrange(&range, addr, 0);
vmpa2_virt_to_string(addr, MDS_UNDEFINED, loc, NULL);
snprintf(name, sizeof(name), "sub_%s", loc + 2);
routine = g_binary_routine_new();
g_binary_routine_set_name(routine, strdup(name));
g_binary_routine_set_range(routine, &range);
if (!found)
{
symbol = g_binary_symbol_new(STP_ROUTINE);
g_binary_symbol_attach_routine(symbol, routine);
g_binary_format_add_symbol(G_BIN_FORMAT(format), symbol);
}
else _g_binary_symbol_attach_routine(symbol, routine, STP_ROUTINE);
}
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* index = indice de l'aire à considérer pendant l'opération. *
* binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* start = démarrage de l'exécution au sein de la zone. *
* info = indications quant à la progression à afficher. *
* *
* Description : Procède au désassemblage d'un contenu binaire exécutable. *
* *
* Retour : Recalcul de rafraîchissement de l'aire de travail requis ? *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_code_from_mem_area(mem_area **list, size_t *count, size_t *index, const GLoadedBinary *binary, GProcContext *ctx, const vmpa2t *start, status_blob_info *info)
{
bool result; /* Besoin à retourner */
mem_area *area; /* Zone de désassemblage */
GBinFormat *format; /* Format du fichier binaire */
GArchProcessor *proc; /* Architecture du binaire */
GBinContent *content; /* Données binaires à lire */
phys_t diff; /* Volume de données traité */
phys_t alen; /* Taille de l'aire utilisée */
phys_t i; /* Boucle de parcours */
vmpa2t pos; /* Boucle de parcours */
vmpa2t prev; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
mrange_t range; /* Couverture de l'instruction */
vmpa2t sym_addr; /* Adresse de nouveau symbole */
bool has_new_sym; /* Statut d'un dépilement */
GBinSymbol *symbol; /* Symbole créé en parallèle */
bool refresh; /* Besoin de rafraîchissement */
result = false;
/* On cherche à obtenir l'assurance que le traitement n'a jamais été fait */
init_mrange(&range, start, 1);
if (!is_range_blank_in_mem_areas(*list, *count, &range)) return false;
/* Récupération des informations de base */
format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
proc = g_loaded_binary_get_processor(binary);
content = g_binary_format_get_content(format);
/* TODO : unref */
area = (*list) + *index;
diff = compute_vmpa_diff(get_mrange_addr(&area->range), start);
alen = get_mrange_length(&area->range);
/**
* On copie la position courante à partir de l'aire plutôt que du
* point de départ car cette première est potentiellement plus complète.
*/
copy_vmpa(&pos, get_mrange_addr(&area->range));
advance_vmpa(&pos, diff);
for (i = diff; i < alen; i += diff)
{
/* S'il y a eu un point d'entrée en milieu de zone, on s'arrête ! */
if (!is_range_blank_in_mem_area(area, i, 1, NULL)) break;
/* Décodage d'une nouvelle instruction */
copy_vmpa(&prev, &pos);
instr = g_arch_processor_disassemble(proc, ctx, content, &pos, G_EXE_FORMAT(format));
/* TODO : valider que la taille de l'instruction obtenue ne dépasse pas la zone */
if (instr == NULL) break;
/* Enregistrement des positions et adresses */
diff = compute_vmpa_diff(&prev, &pos);
assert(diff == 4 || diff == 2); /* FIXME */
init_mrange(&range, &prev, diff);
g_arch_instruction_set_range(instr, &range);
/* Enregistrement d'un éventuel début de routine */
if (g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START)
update_address_as_routine(format, &prev);
/* Eventuel renvoi vers d'autres adresses */
g_arch_instruction_call_hook(instr, IPH_FETCH, proc, ctx, format);
/* Progression dans les traitements */
mark_range_in_mem_areas_as_processed(*list, *count, instr);
inc_progessive_status(info, diff);
assert(!is_range_blank_in_mem_areas(*list, *count, &range));
/* Insertion des symboles découverts en parallèle */
for (has_new_sym = g_proc_context_pop_new_symbol_at(ctx, &sym_addr);
has_new_sym;
has_new_sym = g_proc_context_pop_new_symbol_at(ctx, &sym_addr))
{
has_new_sym = g_binary_format_find_symbol_at(format, &sym_addr, &symbol);
assert(has_new_sym);
refresh = insert_extra_symbol_into_mem_areas(list, count, symbol, *index);
result |= refresh;
/**
* Si une insertion a été réalisée dans la même zone que celle courante ou avant,
* en l'absence d'indication sur les localisations exactes, le plus simple
* est de tout recharger.
*/
if (refresh)
{
*index = find_memory_area_by_addr(*list, *count, &pos);
area = (*list) + *index;
diff = compute_vmpa_diff(get_mrange_addr(&area->range), &pos);
alen = get_mrange_length(&area->range);
i = 0;
}
/**
* Si la zone modifiée se trouve bien après la zone courante, l'emplacement de la liste
* peut potentiellement avoir évolué. Donc on ne recharge ici que le minimum.
*/
else area = (*list) + *index;
}
assert(!is_range_blank_in_mem_areas(*list, *count, &range));
/* Rupture du flot d'exécution ? */
if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT)
break;
}
g_object_unref(G_OBJECT(content));
g_object_unref(G_OBJECT(proc));
return result;
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* start = démarrage de l'exécution au sein de la zone. *
* info = indications quant à la progression à afficher. *
* *
* Description : Procède au désassemblage d'un contenu binaire non exécutable.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void load_data_from_mem_area(mem_area *area, mem_area *list, size_t count, const GLoadedBinary *binary, GProcContext *ctx, const vmpa2t *start, status_blob_info *info)
{
GBinFormat *format; /* Format du fichier binaire */
GBinContent *content; /* Données binaires à lire */
GArchProcessor *proc; /* Architecture du binaire */
SourceEndian endianness; /* Boutisme de cette machine */
phys_t diff; /* Volume de données traité */
phys_t alen; /* Taille de l'aire utilisée */
phys_t i; /* Boucle de parcours */
vmpa2t pos; /* Boucle de parcours */
vmpa2t prev; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
mrange_t range; /* Couverture de l'instruction */
/* On cherche à obtenir l'assurance que le traitement n'a jamais été fait */
init_mrange(&range, start, 1);
if (!is_range_blank_in_mem_areas(list, count, &range)) return;
/* Récupération des informations de base */
format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
content = g_binary_format_get_content(format);
/* TODO g_object_unref(G_OBJECT(format)); */
proc = g_loaded_binary_get_processor(binary);
endianness = g_arch_processor_get_endianness(proc);
g_object_unref(G_OBJECT(proc));
diff = compute_vmpa_diff(get_mrange_addr(&area->range), start);
alen = get_mrange_length(&area->range);
/**
* On copie la position courante à partir de l'aire plutôt que du
* point de départ car cette première est potentiellement plus complète.
*/
copy_vmpa(&pos, get_mrange_addr(&area->range));
advance_vmpa(&pos, diff);
for (i = diff; i < alen; i += diff)
{
/* Décodage d'une nouvelle instruction, sur mesure */
copy_vmpa(&prev, &pos);
instr = NULL;
/*
if (instr == NULL && (i + 4) <= alen)
{
init_mrange(&range, &pos, 4);
if (is_range_blank_in_mem_areas(list, count, &range))
instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, &pos, endianness);
}
*/
if (instr == NULL && (i + 2) <= alen)
{
copy_vmpa(&pos, &prev);
init_mrange(&range, &pos, 2);
if (is_range_blank_in_mem_areas(list, count, &range))
instr = g_raw_instruction_new_array(content, MDS_16_BITS, 1, &pos, endianness);
}
if (instr == NULL/* && (i + 1) <= alen*/)
{
copy_vmpa(&pos, &prev);
init_mrange(&range, &pos, 1);
if (is_range_blank_in_mem_areas(list, count, &range))
instr = g_raw_instruction_new_array(content, MDS_8_BITS, 1, &pos, endianness);
else
{
/**
* On rencontre ici un morceau déjà traité.
* On recherche donc la fin de cette partie à sauter, si elle existe.
*/
//////////////
return;
for (i++; i < alen; i++)
{
advance_vmpa(&pos, 1);
init_mrange(&range, &pos, 1);
if (is_range_blank_in_mem_areas(list, count, &range))
break;
}
diff = 0;
continue;
}
}
assert(instr != NULL);
/* Enregistrement des positions et adresses */
diff = compute_vmpa_diff(&prev, &pos);
init_mrange(&range, &prev, diff);
g_arch_instruction_set_range(instr, &range);
/* Progression dans les traitements */
mark_range_in_mem_areas_as_processed(list, count, instr);
inc_progessive_status(info, diff);
assert(!is_range_blank_in_mem_areas(list, count, &range));
if (area->exec) break;
}
g_object_unref(G_OBJECT(content));
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* index = indice de l'aire à considérer pendant l'opération. *
* binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* info = indications quant à la progression à afficher. *
* *
* Description : S'assure qu'une aire contient toutes ses instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void fill_mem_area(mem_area **list, size_t *count, size_t *index, const GLoadedBinary *binary, GProcContext *ctx, status_blob_info *info)
{
mem_area *area; /* Zone de désassemblage */
phys_t len; /* Taille de la zone à remplir */
phys_t i; /* Boucle de parcours */
vmpa2t start; /* Adresse de départ de combles*/
bool refresh; /* Besoin de rafraîchissement */
area = (*list) + *index;
/* Les symboles se doivent d'être indépendants ! */
if (area->has_sym) return;
len = get_mrange_length(&area->range);
for (i = 0; i < len; i++)
{
if (is_range_blank_in_mem_area(area, i, 1, NULL))
{
copy_vmpa(&start, get_mrange_addr(&area->range));
advance_vmpa(&start, i);
if (area->exec && get_virt_addr(&start) % 4/*2 - FIXME */ == 0)
{
refresh = load_code_from_mem_area(list, count, index, binary, ctx, &start, info);
/**
* Mêmes commentaires que dans load_code_from_mem_area() :
* - en cas de décalage avéré, on recharge tout.
* - sinon, la liste a potentiellement été déplacée, donc on recharge un minimum.
*/
if (refresh)
{
area = (*list) + *index;
len = get_mrange_length(&area->range);
i = compute_vmpa_diff(get_mrange_addr(&area->range), &start);
}
else area = (*list) + *index;
}
if (is_range_blank_in_mem_area(area, i, 1, NULL))
load_data_from_mem_area(area, *list, *count, binary, ctx, &start, info);
}
assert(!is_range_blank_in_mem_area(area, i, 1, NULL));
}
}
/******************************************************************************
* *
* Paramètres : area = aire représentant à contenu à parcourir. *
* *
* Description : Rassemble les instructions conservées dans une zone donnée. *
* *
* Retour : Liste d'instructions prêtes à emploi. *
* *
* Remarques : - *
* *
******************************************************************************/
static GArchInstruction *get_instructions_from_mem_area(const mem_area *area)
{
GArchInstruction *result; /* Liste d'instr. à renvoyer */
phys_t len; /* Nombre d'instructions au max*/
phys_t i; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction décodée */
result = NULL;
if (area->has_sym)
switch (g_binary_symbol_get_target_type(area->symbol))
{
case STP_DATA:
case STP_RO_STRING:
result = g_binary_symbol_get_instruction(area->symbol);
g_object_ref(G_OBJECT(result));
break;
case STP_ROUTINE:
assert(false);
//instr = load_code_binary(binary, start, end, statusbar, id);
// + fill
break;
default:
assert(false);
break;
}
else
{
len = get_mrange_length(&area->range);
for (i = 0; i < len; i++)
{
instr = area->instructions[i];
if (instr != NULL)
{
g_object_ref(G_OBJECT(instr));
g_arch_instruction_add_to_list(&result, instr);
}
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = format d'un exécutable à consulter. *
* bin_length = quantité d'octets à traiter au total. *
* count = nombre de zones mises en place. [OUT] *
* *
* Description : Détermine une liste de zones contigües à traiter. *
* *
* Retour : Liste de zones mémoire à libérer après usage. *
* *
* Remarques : - *
* *
******************************************************************************/
mem_area *compute_memory_areas(GExeFormat *format, phys_t bin_length, size_t *count)
{
mem_area *result; /* Liste à renvoyer */
mrange_t *exe_ranges; /* Liste de zones exécutables */
size_t exe_count; /* Nombre de ces zones */
GBinSymbol **symbols; /* Symboles à représenter */
size_t sym_count; /* Qté de symboles présents */
vmpa2t *last; /* Dernière bordure rencontrée */
size_t i; /* Boucle de parcours #1 */
vmpa2t *border; /* Nouvelle bordure rencontrée */
mem_area *area; /* Zone avec valeurs à éditer */
vmpa2t tmp; /* Stockage temporaire */
GBinPortion **portions; /* Morceaux d'encadrement */
size_t portions_count; /* Taille de cette liste */
const vmpa2t *portion_start; /* Point de départ de portion */
size_t j; /* Boucle de parcours #2 */
SymbolType type; /* Nature d'un symbole */
const mrange_t *range; /* Couverture d'un symbole */
const vmpa2t *start; /* Point de départ du symbole */
phys_t length; /* Taille de ce même symbole */
bool included; /* Inclusion dans une zone ? */
mem_area orig; /* Copie de la zone réduite */
phys_t old_length; /* Taille de zone originelle */
phys_t new_length; /* Nouvelle taille déterminée */
size_t next; /* Indice de zone suivante */
result = NULL;
*count = 0;
/**
* Le parcours n'est valide que si les listes, symboles et zones exécutables,
* sont triées !
*/
exe_ranges = g_exe_format_get_x_ranges(format, &exe_count);
symbols = g_binary_format_get_symbols(G_BIN_FORMAT(format), &sym_count);
/* Première étape : on comble les trous ! */
last = make_vmpa(0, VMPA_NO_VIRTUAL);
for (i = 0; i < exe_count; i++)
{
border = get_mrange_addr(&exe_ranges[i]);
/* Zone tampon à constituer */
if (cmp_vmpa_by_phy(last, border) < 0)
{
result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area));
area = &result[*count - 1];
init_mem_area_from_addr(area, last, compute_vmpa_diff(last, border));
area->has_sym = false;
area->exec = false;
}
/* Insertion d'une zone exécutable déjà définie */
result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area));
area = &result[*count - 1];
init_mem_area_from_range(area, &exe_ranges[i]);
area->has_sym = false;
area->exec = true;
/* Avancée du curseur */
copy_vmpa(last, border);
advance_vmpa(last, get_mrange_length(&exe_ranges[i]));
}
delete_vmpa(last);
/* Extension finale complémentaire ? */
area = &result[*count - 1];
copy_vmpa(&tmp, get_mrange_addr(&area->range));
advance_vmpa(&tmp, get_mrange_length(&area->range));
if (get_phy_addr(&tmp) < bin_length)
{
result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area));
area = &result[*count - 1];
init_mem_area_from_addr(area, &tmp, bin_length - get_phy_addr(&tmp));
area->has_sym = false;
area->exec = false;
}
/* Seconde étape : on s'assure du découpage autour des portions pour respecter l'alignement */
portions = NULL;//g_exe_format_get_portions_at_level(format, -1, &portions_count);
portions_count = 0;
for (i = 1; i < portions_count; i++)
{
portion_start = get_mrange_addr(g_binary_portion_get_range(portions[i]));
for (j = 0; j < *count; j++)
{
area = &result[j];
if (!mrange_contains_addr(&area->range, portion_start))
continue;
/* Si le déccoupage actuel ne correspond pas au besoin des portions... */
if (cmp_vmpa(get_mrange_addr(&area->range), portion_start) != 0)
{
fini_mem_area(&result[j]);
result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area));
memmove(&result[j + 2], &result[j + 1], (*count - j - 2) * sizeof(mem_area));
copy_mem_area_properties(&result[j + 1], &result[j]);
/* Première moitié */
area = &result[j];
copy_vmpa(&tmp, get_mrange_addr(&area->range));
length = get_mrange_length(&area->range);
init_mem_area_from_addr(area, &tmp, compute_vmpa_diff(&tmp, portion_start));
/* Seconde moitié */
length -= get_mrange_length(&area->range);
area = &result[j + 1];
init_mem_area_from_addr(area, portion_start, length);
}
j = *count;
}
}
/* Troisième étape : on insère les symboles existants */
j = 0;
for (i = 0; i < *count; i++)
{
/* Sélection et écartement des symboles */
range = NULL; /* Pour GCC */
length = 0; /* Pour GCC */
for (; j < sym_count; j++)
{
type = g_binary_symbol_get_target_type(symbols[j]);
/**
* On ne garde que les symboles renvoyant directement une ou
* plusieurs instructions, c'est à dire les symboles valides
* pour un appel à g_binary_symbol_get_instruction().
*
* Les instructions des autres symboles sont obtenues et mises
* en place durant la procédure de désassemblage.
*/
if (type == STP_ROUTINE || type == STP_ENTRY_POINT || type == STP_CODE_LABEL)
continue;
range = g_binary_symbol_get_range(symbols[j]);
length = get_mrange_length(range);
if (length > 0)
break;
}
if (j == sym_count)
break;
start = get_mrange_addr(range);
/* Si un découpage s'impose... */
if (mrange_contains_addr(&result[i].range, start))
{
copy_vmpa(&tmp, start);
advance_vmpa(&tmp, length);
included = mrange_contains_addr(&result[i].range, &tmp);
memcpy(&orig, &result[i], sizeof(mem_area));
fini_mem_area(&orig);
/* Réduction de la zone de départ */
copy_vmpa(&tmp, get_mrange_addr(&result[i].range));
old_length = get_mrange_length(&result[i].range);
new_length = compute_vmpa_diff(get_mrange_addr(&result[i].range), start);
if (new_length == 0)
{
memmove(&result[i], &result[i + 1], (*count - i - 1) * sizeof(mem_area));
(*count)--;
next = i;
}
else
{
init_mem_area_from_addr(&result[i], &tmp, new_length);
next = i + 1;
}
/* Insertion de la zone du symbole */
result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area));
memmove(&result[next + 1], &result[next], (*count - next - 1) * sizeof(mem_area));
area = &result[next];
init_mem_area_from_range(area, range);
area->has_sym = true;
area->symbol = symbols[j];
/* Jointure finale... */
if (included)
{
/* Simple extension pour rattraper la fin originelle */
if ((old_length - new_length - length) > 0)
{
result = (mem_area *)realloc(result, ++(*count) * sizeof(mem_area));
memmove(&result[next + 2], &result[next + 1], (*count - next - 2) * sizeof(mem_area));
area = &result[next + 1];
copy_vmpa(&tmp, start);
advance_vmpa(&tmp, length);
init_mem_area_from_addr(area, &tmp, old_length - new_length - length);
copy_mem_area_properties(area, &orig);
}
i = next;
}
else
{
/* Suppression des éventuelles zones totalement recouvertes */
/* Réduction de la zone d'arrivée */
}
j++;
}
}
if (exe_ranges != NULL)
free(exe_ranges);
return result;
}
/******************************************************************************
* *
* Paramètres : list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* symbol = élément nouveau à venir insérer dans les zones. *
* working = indice de l'aire de travail courante. *
* *
* Description : Insère un symbole dans un découpage en aires. *
* *
* Retour : Recalcul de rafraîchissement de l'aire de travail requis ? *
* *
* Remarques : - *
* *
******************************************************************************/
static bool insert_extra_symbol_into_mem_areas(mem_area **list, size_t *count, GBinSymbol *symbol, size_t working)
{
bool result; /* Besoin à renvoyer */
const mrange_t *sym_range; /* Emplacement du symbole */
size_t index; /* Zone trouvée à scinder */
mem_area *area; /* Aire en cours de traitement */
mem_area saved; /* Aire aux données copiées */
mrange_t area_range; /* Emplacement d'une aire */
vmpa2t area_pos; /* Position relative à une aire*/
vmpa2t sym_pos; /* Position pour un symbole */
phys_t diff; /* Décalage entre localisation */
phys_t new_length; /* Nouvelle taille de zone */
sym_range = g_binary_symbol_get_range(symbol);
index = find_memory_area_by_addr(*list, *count, get_mrange_addr(sym_range));
assert(index < *count);
result = (working >= index);
area = &(*list)[index];
if (area->has_sym) return false;
assert(!area->has_sym);
saved = *area;
copy_mrange(&area_range, &area->range);
copy_vmpa(&area_pos, get_mrange_addr(&area_range));
copy_vmpa(&sym_pos, get_mrange_addr(sym_range));
/* Si le symbole est construit avec une localisation partielle, on complète ! */
if (get_phy_addr(&sym_pos) == VMPA_NO_PHYSICAL || get_virt_addr(&sym_pos) == VMPA_NO_VIRTUAL)
{
assert(false);
diff = compute_vmpa_diff(&area_pos, &sym_pos);
copy_vmpa(&sym_pos, &area_pos);
advance_vmpa(&sym_pos, diff);
g_binary_symbol_fix_range(symbol, &sym_pos);
}
/* Si le symbole a une taille identique à la zone qui le contient, on remplace simplement... */
if (get_mrange_length(&area_range) == get_mrange_length(sym_range))
{
assert((cmp_vmpa(&area_pos, &sym_pos) == 0));
init_mem_area_from_range(area, sym_range);
area->has_sym = true;
area->symbol = symbol;
goto iesima_done;
}
/* Si le symbole se trouve en début de zone... */
if (cmp_vmpa(&area_pos, &sym_pos) == 0)
{
*list = (mem_area *)realloc(*list, ++(*count) * sizeof(mem_area));
memmove(&(*list)[index + 1], &(*list)[index], (*count - index - 1) * sizeof(mem_area));
/* Aire du symbole */
area = &(*list)[index];
init_mem_area_from_range(area, sym_range);
area->has_sym = true;
area->symbol = symbol;
/* Aire raccourcie */
copy_vmpa(&area_pos, get_mrange_addr(&saved.range));
advance_vmpa(&area_pos, get_mrange_length(sym_range));
//compute_mrange_end_addr(sym_range, &area_pos);
new_length = get_mrange_length(&area_range) - get_mrange_length(sym_range);
area = &(*list)[index + 1];
init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved);
goto iesima_done;
}
compute_mrange_end_addr(&area->range, &area_pos);
compute_mrange_end_addr(sym_range, &sym_pos);
/* Si le symbole se trouve en fin de zone... */
if (cmp_vmpa(&area_pos, &sym_pos) == 0)
{
*list = (mem_area *)realloc(*list, ++(*count) * sizeof(mem_area));
memmove(&(*list)[index + 1], &(*list)[index], (*count - index - 1) * sizeof(mem_area));
copy_vmpa(&area_pos, get_mrange_addr(&area_range));
new_length = get_mrange_length(&area_range) - get_mrange_length(sym_range);
area = &(*list)[index];
init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved);
/* Aire du symbole */
area = &(*list)[index + 1];
init_mem_area_from_range(area, sym_range);
area->has_sym = true;
area->symbol = symbol;
}
/* Sinon il se trouve au milieu et on découpe en trois... */
else
{
*count += 2;
*list = (mem_area *)realloc(*list, *count * sizeof(mem_area));
memmove(&(*list)[index + 2], &(*list)[index], (*count - index - 2) * sizeof(mem_area));
/* Aire raccourcie #1 */
copy_vmpa(&area_pos, get_mrange_addr(&area_range));
new_length = compute_vmpa_diff(&area_pos, get_mrange_addr(sym_range));
assert(new_length != 0); /* Symbole non présent au début */
area = &(*list)[index];
init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved);
/* Aire du symbole */
area = &(*list)[index + 1];
init_mem_area_from_range(area, sym_range);
area->has_sym = true;
area->symbol = symbol;
/* Aire raccourcie #2 */
copy_vmpa(&area_pos, get_mrange_addr(&saved.range));
advance_vmpa(&area_pos, get_mrange_length(&(*list)[index].range));
advance_vmpa(&area_pos, get_mrange_length(sym_range));
//compute_mrange_end_addr(sym_range, &area_pos);
new_length = get_mrange_length(&area_range) - get_mrange_length(sym_range) \
- get_mrange_length(&(*list)[index].range);
assert(new_length != 0); /* Symbole non présent à la fin */
area = &(*list)[index + 2];
init_mem_area_from_bigger_area(area, &area_pos, new_length, &saved);
}
fini_mem_area(&saved);
iesima_done:
return result;
}
/******************************************************************************
* *
* Paramètres : list = listes de zones utable à consulter. *
* count = nombre de zones mises en place. *
* addr = adresse à retrouver dans les aires présentes. *
* *
* Description : Détermine une liste de zones contigües à traiter. *
* *
* Retour : Indice de la zone trouvée, ou nombre d'aires en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t find_memory_area_by_addr(mem_area *list, size_t count, const vmpa2t *addr)
{
size_t result; /* Trouvaille à retourner */
mem_area *found; /* Elément trouvé éventuel */
int find_mem_area(const vmpa2t *addr, const mem_area *area)
{
int status; /* Bilan à retourner */
if (mrange_contains_addr(&area->range, addr))
status = 0;
else
status = cmp_vmpa(addr, get_mrange_addr(&area->range));
return status;
}
found = bsearch(addr, list, count, sizeof(mem_area), (__compar_fn_t)find_mem_area);
result = (found != NULL ? found - list : count);
return result;
}
/******************************************************************************
* *
* Paramètres : list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* range = aire des octets à manipuler. *
* visitor = fonction opérationnelle finale à appeler. *
* *
* Description : Manipule la cartographie des octets d'aires de données. *
* *
* Retour : valeur retournée par le visiteur, voire false si erreur. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool handle_bytes_map_in_mem_area(mem_area *list, size_t count, const mrange_t *range, GArchInstruction *instr, visit_bytes_map_fc visitor)
{
bool result; /* Bilan à retourner */
size_t found; /* Indice de la zone trouvée */
mem_area *area; /* Aire à traiter trouvée */
phys_t offset; /* Point de départ dans l'aire */
phys_t remaining; /* Quantité restant à traiter */
phys_t processed; /* Quantité traitée */
vmpa2t start; /* Point de départ suivant */
result = false;
found = find_memory_area_by_addr(list, count, get_mrange_addr(range));
if (found == count) return false;
area = list + found;
offset = compute_vmpa_diff(get_mrange_addr(&area->range), get_mrange_addr(range));
remaining = get_mrange_length(range);
/* Traitement intégral dans la zone trouvée */
if ((offset + remaining) <= get_mrange_length(&area->range))
result = visitor(area, offset, remaining, instr);
else
{
printf("BUG_ON | off=%u remaining=%u length=%u\n",
(unsigned int)offset,
(unsigned int)remaining,
(unsigned int)get_mrange_length(&area->range));
printf("BUG_ON @ 0x%08x + %d\n",
(unsigned int)get_virt_addr(get_mrange_addr(range)),
(int)get_mrange_length(range)
);
assert(0);
/* Traitement de la fin de la première aire */
processed = get_mrange_length(&area->range) - offset;
result = visitor(area, offset, processed, instr);
/* Traitement des zones adjacentes suivantes */
copy_vmpa(&start, get_mrange_addr(range));
for (remaining -= processed; remaining > 0 && result; remaining -= processed)
{
advance_vmpa(&start, processed);
found = find_memory_area_by_addr(list, count, &start);
if (found == count)
{
result = false;
break;
}
area = list + found;
processed = get_mrange_length(&area->range);
if (remaining < processed) processed = remaining;
result = visitor(area, 0, processed, instr);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* range = aire des octets à manipuler. *
* *
* Description : Indique si une zone donnée est intégralement vierge ou non. *
* *
* Retour : true si l'aire visée n'a jamais été traitée, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool is_range_blank_in_mem_areas(mem_area *list, size_t count, const mrange_t *range)
{
return handle_bytes_map_in_mem_area(list, count,
range, NULL,
is_range_blank_in_mem_area);
}
/******************************************************************************
* *
* Paramètres : list = liste de zones délimitant des contenus à traiter. *
* count = nombre de zones à disposition. *
* instr = instruction désassemblée à conserver en mémoire. *
* *
* Description : Marque une série d'octets comme ayant été traités. *
* *
* Retour : true. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool mark_range_in_mem_areas_as_processed(mem_area *list, size_t count, GArchInstruction *instr)
{
return handle_bytes_map_in_mem_area(list, count,
g_arch_instruction_get_range(instr), instr,
mark_range_in_mem_area_as_processed);
}
/******************************************************************************
* *
* Paramètres : areas = série d'aires représentant à contenu à parcourir. *
* count = nombre de ces zones présentes. *
* *
* Description : Rassemble les instructions conservées dans des zones données.*
* *
* Retour : Liste d'instructions prêtes à emploi. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *collect_instructions_from_mem_areas(mem_area *list, size_t count)
{
GArchInstruction *result; /* Liste d'instr. à renvoyer */
size_t i; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction(s) à insérer */
result = NULL;
for (i = 0; i < count; i++)
{
instr = get_instructions_from_mem_area(&list[i]);
g_arch_instruction_merge_lists(&result, &instr);
}
return result;
}