/* Chrysalide - Outil d'analyse de fichiers binaires
* fetch.c - récupération d'instructions à partir de binaire brut
*
* Copyright (C) 2010-2013 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 "fetch.h"
#include
#include "../../arch/raw.h"
#include "../../arch/instruction.h"
/* Zone mémoire bien bornée */
typedef struct _mem_area
{
mrange_t range; /* Couverture de la zone */
bool has_sym; /* Représentation via symbole ?*/
union
{
bool exec; /* Zone exécutable ? */
GBinSymbol *symbol; /* Symbole associé à la zone */
};
} mem_area;
/* Détermine une liste de zones contigües à traiter. */
static mem_area *compute_memory_areas(const GExeFormat *, phys_t, size_t *);
/* Procède au désassemblage basique d'un contenu binaire. */
static GArchInstruction *load_raw_binary(const GLoadedBinary *, const vmpa2t *, off_t, GtkExtStatusBar *, bstatus_id_t);
/* Procède au désassemblage d'un contenu binaire exécutable. */
static GArchInstruction *load_code_binary(const GLoadedBinary *, const vmpa2t *, off_t, GtkExtStatusBar *, bstatus_id_t);
/******************************************************************************
* *
* 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 : - *
* *
******************************************************************************/
static mem_area *compute_memory_areas(const 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 */
size_t j; /* Boucle de parcours #2 */
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_mrange(&area->range, 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];
copy_mrange(&area->range, &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_mrange(&area->range, &tmp, bin_length - get_phy_addr(&tmp));
area->has_sym = false;
area->exec = false;
}
/* Seconde étape : on insère les symboles existants */
j = 0;
#define SKIP_EMPTY_SYMBOLS \
for (; j < sym_count; j++) \
{ \
range = g_binary_symbol_get_range(symbols[j]); \
\
length = get_mrange_length(range); \
if (length > 0) break; \
\
} \
SKIP_EMPTY_SYMBOLS
for (i = 0; i < *count && j < sym_count; i++)
{
range = g_binary_symbol_get_range(symbols[j]);
start = get_mrange_addr(range);
length = get_mrange_length(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));
/* 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_mrange(&result[i].range, &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];
copy_mrange(&area->range, 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);
memcpy(area, &orig, sizeof(mem_area));
init_mrange(&area->range, &tmp, old_length - new_length - length);
}
i = next;
}
else
{
/* Suppression des éventuelles zones totalement recouvertes */
/* Réduction de la zone d'arrivée */
}
j++;
SKIP_EMPTY_SYMBOLS
}
}
//free
//exit(0);
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation de binaire chargé. *
* parts = parties binaires à désassembler. *
* count = nombre de parties à traiter. *
* statusbar = barre de statut avec progression à mettre à jour.*
* id = identifiant du message affiché à l'utilisateur. *
* *
* Description : Procède au désassemblage basique d'un contenu binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static GArchInstruction *load_raw_binary(const GLoadedBinary *binary, const vmpa2t *base, off_t end, GtkExtStatusBar *statusbar, bstatus_id_t id)
{
GArchInstruction *result; /* Liste d'instr. à renvoyer */
GBinFormat *format; /* Format du fichier binaire */
GArchProcessor *proc; /* Architecture du binaire */
off_t bin_length; /* Taille des données à lire */
bin_t *bin_data; /* Données binaires à lire */
vmpa2t pos; /* Boucle de parcours */
vmpa2t prev; /* Boucle de parcours */
off_t old_phy; /* Ancienne position physique */
GArchInstruction *instr; /* Instruction décodée */
off_t new_phy; /* Nouvelle position physique */
mrange_t range; /* Couverture de l'instruction */
result = NULL;
format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
proc = get_arch_processor_from_format(G_EXE_FORMAT(format));
bin_data = g_loaded_binary_get_data(binary, &bin_length);
copy_vmpa(&pos, base);
copy_vmpa(&prev, base);
old_phy = get_phy_addr(&prev);
while (old_phy < end)
{
instr = g_raw_instruction_new_array(bin_data, MDS_32_BITS, 1, &pos, end,
g_arch_processor_get_endianness(proc));
if (instr == NULL) printf(" Break !!!\n");
if (instr == NULL) break;
new_phy = get_phy_addr(&pos);
init_mrange(&range, &prev, new_phy - old_phy);
g_arch_instruction_set_range(instr, &range);
g_arch_instruction_add_to_list(&result, instr);
copy_vmpa(&prev, &pos);
old_phy = get_phy_addr(&prev);
//done += (new_phy - old_phy);
//gtk_extended_status_bar_update_activity(statusbar, id, done * 1.0 / sum);
}
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation de binaire chargé. *
* parts = parties binaires à désassembler. *
* count = nombre de parties à traiter. *
* statusbar = barre de statut avec progression à mettre à jour.*
* id = identifiant du message affiché à l'utilisateur. *
* *
* Description : Procède au désassemblage d'un contenu binaire exécutable. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static GArchInstruction *load_code_binary(const GLoadedBinary *binary, const vmpa2t *base, off_t end, GtkExtStatusBar *statusbar, bstatus_id_t id)
{
GArchInstruction *result; /* Liste d'instr. à renvoyer */
GBinFormat *format; /* Format du fichier binaire */
GArchProcessor *proc; /* Architecture du binaire */
off_t bin_length; /* Taille des données à lire */
bin_t *bin_data; /* Données binaires à lire */
vmpa2t pos; /* Boucle de parcours */
vmpa2t prev; /* Boucle de parcours */
off_t old_phy; /* Ancienne position physique */
GArchInstruction *instr; /* Instruction décodée */
off_t new_phy; /* Nouvelle position physique */
mrange_t range; /* Couverture de l'instruction */
result = NULL;
format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
proc = get_arch_processor_from_format(G_EXE_FORMAT(format));
bin_data = g_loaded_binary_get_data(binary, &bin_length);
copy_vmpa(&pos, base);
copy_vmpa(&prev, base);
old_phy = get_phy_addr(&prev);
while (old_phy < end)
{
instr = g_arch_processor_disassemble(proc, NULL, bin_data, &pos, end);
//if (!G_IS_RAW_INSTRUCTION(instr)) printf("GOT %p\n", instr);
if (instr == NULL)
instr = g_raw_instruction_new_array(bin_data, MDS_32_BITS, 1, &pos, end,
g_arch_processor_get_endianness(proc));
if (instr == NULL) printf(" Break !!!\n");
if (instr == NULL) break;
new_phy = get_phy_addr(&pos);
init_mrange(&range, &prev, new_phy - old_phy);
g_arch_instruction_set_range(instr, &range);
g_arch_instruction_add_to_list(&result, instr);
copy_vmpa(&prev, &pos);
old_phy = get_phy_addr(&prev);
//done += (new_phy - old_phy);
//gtk_extended_status_bar_update_activity(statusbar, id, done * 1.0 / sum);
}
return result;
}
/******************************************************************************
* *
* Paramètres : binary = représentation de binaire chargé. *
* statusbar = barre de statut avec progression à mettre à jour.*
* id = identifiant du message affiché à l'utilisateur. *
* *
* Description : Procède au désassemblage basique d'un contenu binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, GtkExtStatusBar *statusbar, bstatus_id_t id)
{
GArchInstruction *result; /* Instruction désassemblées */
GExeFormat *format; /* Format du fichier binaire */
off_t length; /* Taille des données à lire */
mem_area *areas; /* Zone de productions */
size_t count; /* Nombre de ces zones */
size_t i; /* Boucle de parcours */
mem_area *iter; /* Zone parcourue */
GArchInstruction *instr; /* Instruction(s) à insérer */
const vmpa2t *start; /* Début d'une zone traitée */
phys_t end; /* Fin d'une zone traitée */
result = NULL;
format = g_loaded_binary_get_format(binary);
g_loaded_binary_get_data(binary, &length);
areas = compute_memory_areas(format, length, &count);
for (i = 0; i < count; i++)
{
iter = &areas[i];
start = get_mrange_addr(&iter->range);
end = get_phy_addr(start) + get_mrange_length(&iter->range);
assert(get_mrange_length(&iter->range) > 0);
if (iter->has_sym)
switch (g_binary_symbol_get_target_type(iter->symbol))
{
case STP_DATA:
instr = g_binary_symbol_get_instruction(iter->symbol);
g_object_ref(G_OBJECT(instr));
break;
case STP_ROUTINE:
instr = load_code_binary(binary, start, end, statusbar, id);
break;
default:
assert(false);
break;
}
else
{
if (iter->exec)
instr = load_code_binary(binary, start, end, statusbar, id);
else
instr = load_raw_binary(binary, start, end, statusbar, id);
}
g_arch_instruction_merge_lists(&result, &instr);
}
free(areas);
return result;
}