/* Chrysalide - Outil d'analyse de fichiers binaires
* loading.c - chargements parallèles des symboles de format ELF
*
* Copyright (C) 2017-2019 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chrysalide. If not, see .
*/
#include "loading.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "elf-int.h"
/* Fraction de routines à limiter (instance) */
struct _GElfLoading
{
GDelayedWork parent; /* A laisser en premier */
GElfFormat *format; /* Format à faire évoluer */
phys_t str_start; /* Chaînes à disposition */
/**
* Gestion des informations de contexte.
*/
union
{
struct
{
bool use_virt; /* Représentatio par défaut */
};
struct
{
elf_rel *relocs_to_fill; /* Tableau à remplir */
};
struct
{
elf_rel *relocs; /* Relocalisations présentes */
size_t rel_count; /* Qté de ces relocalisations */
phys_t sym_start; /* Début de zone des symboles */
uint32_t sym_count; /* Nombre de symboles présents */
};
struct
{
phys_t global_start; /* Départ global dans la zone */
phys_t global_end; /* Fin globale dans la zone */
virt_t global_addr; /* Adresse virtuelle initiale */
GBinContent *content; /* Contenu binaire à lire */
const bin_t *data; /* Contenu complet et original */
};
};
/**
* Gestion du mode de parcours.
*/
union
{
struct
{
phys_t begin; /* Point de départ du parcours */
phys_t end; /* Point d'arrivée exclu */
elf_loading_cb callback_0; /* Routine de traitement #0 */
};
struct
{
sym_iter_t *iter; /* Symboles à parcourir */
elf_applying_cb callback_1; /* Routine de traitement #1 */
};
};
unsigned int kind; /* Type de traitements */
bool status; /* Bilan des traitements */
activity_id_t id; /* Identifiant pour messages */
};
/* Fraction de routines à limiter (classe) */
struct _GElfLoadingClass
{
GDelayedWorkClass parent; /* A laisser en premier */
};
/* Initialise la classe des tâches des chargements pour ELF. */
static void g_elf_loading_class_init(GElfLoadingClass *);
/* Initialise une tâche de chargements pour ELF. */
static void g_elf_loading_init(GElfLoading *);
/* Supprime toutes les références externes. */
static void g_elf_loading_dispose(GElfLoading *);
/* Procède à la libération totale de la mémoire. */
static void g_elf_loading_finalize(GElfLoading *);
/* Assure le chargement pour un format ELF en différé. */
static void g_elf_loading_process(GElfLoading *, GtkStatusStack *);
/* Indique le type défini pour les tâches de chargements pour format ELF. */
G_DEFINE_TYPE(GElfLoading, g_elf_loading, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches des chargements pour ELF. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_elf_loading_class_init(GElfLoadingClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GDelayedWorkClass *work; /* Version en classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_elf_loading_dispose;
object->finalize = (GObjectFinalizeFunc)g_elf_loading_finalize;
work = G_DELAYED_WORK_CLASS(klass);
work->run = (run_task_fc)g_elf_loading_process;
}
/******************************************************************************
* *
* Paramètres : loading = instance à initialiser. *
* *
* Description : Initialise une tâche de chargements pour ELF. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_elf_loading_init(GElfLoading *loading)
{
loading->status = false;
}
/******************************************************************************
* *
* Paramètres : loading = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_elf_loading_dispose(GElfLoading *loading)
{
if (loading->kind == 2)
g_clear_object(&loading->content);
G_OBJECT_CLASS(g_elf_loading_parent_class)->dispose(G_OBJECT(loading));
}
/******************************************************************************
* *
* Paramètres : loading = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_elf_loading_finalize(GElfLoading *loading)
{
G_OBJECT_CLASS(g_elf_loading_parent_class)->finalize(G_OBJECT(loading));
}
/******************************************************************************
* *
* Paramètres : format = ensemble d'instructions désassemblées. *
* str_start = début des chaînes de caractères à consulter. *
* first = position du premier élément. *
* begin = point de départ du parcours de liste. *
* end = point d'arrivée exclu du parcours. *
* id = identifiant du message affiché à l'utilisateur. *
* callback = routine de traitements particuliers. *
* *
* Description : Crée une tâche de chargement pour ELF différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GElfLoading *g_elf_loading_new_for_symbols(GElfFormat *format, phys_t str_start, phys_t first, phys_t begin, phys_t end, activity_id_t id, elf_loading_cb callback)
{
GElfLoading *result; /* Tâche à retourner */
#ifndef NDEBUG
bool status; /* Etat d'une consultation */
#endif
result = g_object_new(G_TYPE_ELF_LOADING, NULL);
result->format = format;
result->str_start = str_start;
#ifndef NDEBUG
status = g_generic_config_get_value(get_main_configuration(), MPK_FORMAT_NO_NAME, &result->use_virt);
assert(status);
#else
g_generic_config_get_value(get_main_configuration(), MPK_FORMAT_NO_NAME, &result->use_virt);
#endif
result->begin = begin;
result->end = end;
result->callback_0 = callback;
result->kind = 0;
result->id = id;
return result;
}
/******************************************************************************
* *
* Paramètres : format = ensemble d'instructions désassemblées. *
* begin = point de départ du parcours de liste. *
* end = point d'arrivée exclu du parcours. *
* relocs = table des relocalisations à remplir. *
* id = identifiant du message affiché à l'utilisateur. *
* callback = routine de traitements particuliers. *
* *
* Description : Crée une tâche de chargement pour ELF différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GElfLoading *g_elf_loading_new_for_relocations(GElfFormat *format, phys_t begin, phys_t end, elf_rel *relocs, activity_id_t id, elf_loading_cb callback)
{
GElfLoading *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_ELF_LOADING, NULL);
result->format = format;
result->str_start = VMPA_NO_PHYSICAL;
result->relocs_to_fill = relocs;
result->begin = begin;
result->end = end;
result->callback_0 = callback;
result->kind = 0;
result->id = id;
return result;
}
/******************************************************************************
* *
* Paramètres : format = ensemble d'instructions désassemblées. *
* iter = itérateur sur les symboles à parcourir. *
* str_start = début de la zone contenant les descriptions. *
* relocs = table des relocalisations chargées. *
* rel_count = nombre de ces éléments à interpréter. *
* sym_start = localisation du début de la zone de symboles. *
* sym_count = nombre de descriptions de symbole attendues. *
* id = identifiant du message affiché à l'utilisateur. *
* callback = routine de traitements particuliers. *
* *
* Description : Crée une tâche de chargement pour ELF différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GElfLoading *g_elf_loading_new_for_applying(GElfFormat *format, sym_iter_t *iter, phys_t str_start, elf_rel *relocs, size_t rel_count, phys_t sym_start, uint32_t sym_count, activity_id_t id, elf_applying_cb callback)
{
GElfLoading *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_ELF_LOADING, NULL);
result->format = format;
result->str_start = str_start;
result->relocs = relocs;
result->rel_count = rel_count;
result->sym_start = sym_start;
result->sym_count = sym_count;
result->iter = iter;
result->callback_1 = callback;
result->kind = 1;
result->id = id;
return result;
}
/******************************************************************************
* *
* Paramètres : format = ensemble d'instructions désassemblées. *
* begin = point de départ de la zone à traiter. *
* end = point d'arrivée exclu du parcours. *
* gb_start = position de départ pour l'ensemble des données. *
* gb_end = position finale dans l'ensemble des données. *
* addr = adresse virtuelle de la position initiale. *
* id = identifiant du message affiché à l'utilisateur. *
* callback = routine de traitements particuliers. *
* *
* Description : Crée une tâche de chargement de chaînes pour ELF différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GElfLoading *g_elf_loading_new_for_strings(GElfFormat *format, phys_t begin, phys_t end, phys_t gb_start, phys_t gb_end, virt_t addr, activity_id_t id, elf_loading_cb callback)
{
GElfLoading *result; /* Tâche à retourner */
vmpa2t pos; /* Tête de lecture */
result = g_object_new(G_TYPE_ELF_LOADING, NULL);
result->format = format;
result->global_start = gb_start;
result->global_end = gb_end;
result->global_addr = addr;
result->content = g_binary_format_get_content(G_BIN_FORMAT(format));
init_vmpa(&pos, gb_start, addr);
result->data = g_binary_content_get_raw_access(result->content, &pos, gb_end - gb_start);
if (result->data == NULL) goto no_data;
result->begin = begin;
result->end = end;
result->callback_0 = callback;
result->kind = 2;
result->id = id;
return result;
no_data:
g_object_unref(G_OBJECT(result));
return NULL;
}
/******************************************************************************
* *
* Paramètres : loading = traitements différés à mener. *
* status = barre de statut à tenir informée. *
* *
* Description : Assure le chargement pour un format ELF en différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_elf_loading_process(GElfLoading *loading, GtkStatusStack *status)
{
GElfFormat *format; /* Format plus accessible */
phys_t iter; /* Boucle de parcours */
phys_t old; /* Sauvegarde du point d'avant */
bool ret; /* Bilan d'un appel */
size_t processed; /* Nombre de symboles traités */
GBinSymbol *symbol; /* Symbole analysé */
format = loading->format;
switch (loading->kind)
{
case 0:
for (iter = loading->begin; iter < loading->end; )
{
old = iter;
ret = loading->callback_0(loading, format, &iter);
if (!ret)
{
log_variadic_message(LMT_ERROR, _("Error while loading ELF data @ 0x%08x!"), old);
break;
}
gtk_status_stack_update_activity_value(status, loading->id, 1);
}
loading->status = (iter == loading->end);
break;
case 1:
ret = true;
processed = 0;
for (symbol = get_symbol_iterator_next(loading->iter);
symbol != NULL && ret;
symbol = get_symbol_iterator_next(loading->iter))
{
ret = loading->callback_1(loading, format, symbol);
g_object_unref(G_OBJECT(symbol));
if (!ret)
{
log_variadic_message(LMT_ERROR, _("Error while applying ELF relocation %zu!"), processed);
break;
}
processed++;
gtk_status_stack_update_activity_value(status, loading->id, 1);
if (processed == loading->rel_count)
break;
}
loading->status = (processed == loading->rel_count);
break;
case 2:
for (iter = loading->begin; iter < loading->end; )
{
old = iter;
loading->status |= loading->callback_0(loading, format, &iter);
gtk_status_stack_update_activity_value(status, loading->id, iter - old);
}
break;
default:
assert(false);
break;
}
}
/******************************************************************************
* *
* Paramètres : loading = procédure de chargements écoulés à consulter. *
* *
* Description : Fournit le bilan des traitements différés. *
* *
* Retour : true si tout s'est bien déroulé. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_elf_loading_get_status(const GElfLoading *loading)
{
return loading->status;
}
/******************************************************************************
* *
* Paramètres : loading = chargement pour ELF à mener. *
* name = indice de la désignation du symbole concerné. *
* virt = adresse virtuelle du symbole en mémoire. *
* prefix = préfixe d'une désignation par défaut. *
* alt = zone de constitution d'un nom alternatif. [OUT] *
* addr = localisation compléte à associer au symbole. [OUT] *
* *
* Description : Construit la désignation adaptée à un symbole. *
* *
* Retour : Pointeur vers une étiquette constituée. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_elf_loading_build_name(const GElfLoading *loading, uint32_t name, virt_t virt, const char *prefix, char *alt, vmpa2t *addr)
{
const char *result; /* Désignation à retourner */
GElfFormat *format; /* Format plus accessible */
vmpa2t pos; /* Position de lecture */
const GBinContent *content; /* Contenu binaire à lire */
size_t plen; /* Taille du préfixe */
result = NULL;
format = loading->format;
if (g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), virt, addr))
{
init_vmpa(&pos, loading->str_start + name, VMPA_NO_VIRTUAL);
content = G_BIN_FORMAT(format)->content;
result = (const char *)g_binary_content_get_raw_access(content, &pos, 1);
if (result != NULL && result[0] == '\0')
result = NULL;
if (result == NULL)
{
/**
* Charge à l'appelant de s'assurer que la zone tampon est assez grande !
*/
strcpy(alt, prefix);
plen = strlen(prefix);
if (loading->use_virt)
vmpa2_virt_to_string(addr, MDS_UNDEFINED, alt + plen, NULL);
else
vmpa2_phys_to_string(addr, MDS_UNDEFINED, alt + plen, NULL);
result = alt;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : loading = chargement pour ELF à compléter. *
* iter = tête de lecture courante. *
* reloc = relocalisation à conserver en mémoire. *
* *
* Description : Intègre dans la liste adaptée une relocalisation chargée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_elf_loading_store_relocation(const GElfLoading *loading, const phys_t *iter, const elf_rel *reloc)
{
GElfFormat *format; /* Format plus accessible */
size_t index; /* Indice de l'élément */
format = loading->format;
index = (*iter - loading->begin);
assert(index % sizeof(ELF_SIZEOF_REL(format)) == 0);
index /= sizeof(ELF_SIZEOF_REL(format));
/* La tête de lecture a consommé un élément ! */
index--;
memcpy(&loading->relocs_to_fill[index], reloc, sizeof(elf_rel));
}
/******************************************************************************
* *
* Paramètres : loading = chargement pour ELF à consulter. *
* offset = décalage à retrouver idéalement. *
* reloc = informations quant à la relocalisation. [OUT] *
* *
* Description : Recherche une relocalisation par son décalage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_elf_loading_search_for_relocation(const GElfLoading *loading, const uint64_t *offset, elf_rel **reloc)
{
bool result; /* Validité à faire remonter */
void *found; /* Eventuel élément trouvé */
int compare_relocations(const uint64_t *off, const elf_rel *rel)
{
return sort_uint64_t(*off, ELF_REL(loading->format, *rel, r_offset));
}
found = bsearch(offset, loading->relocs, loading->rel_count,
sizeof(elf_rel), (__compar_fn_t)compare_relocations);
result = (found != NULL);
if (result)
*reloc = (elf_rel *)found;
return result;
}
/******************************************************************************
* *
* Paramètres : loading = chargement pour ELF à poursuivre. *
* index = indice du symbole concerné. *
* *
* Description : Construit la désignation adaptée à un symbole importé. *
* *
* Retour : Nouvelle étiquette constituée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
char *g_elf_loading_build_plt_name(const GElfLoading *loading, uint64_t index)
{
char *result; /* Désignation à retourner */
GElfFormat *format; /* Format plus accessible */
phys_t offset; /* Tête de lecture brute */
elf_sym sym; /* Symbole aux infos visées */
bool status; /* Bilan de récupération */
uint32_t name; /* Indice du nom du symbole */
vmpa2t pos; /* Position de lecture */
const GBinContent *content; /* Contenu binaire à lire */
const char *prefix; /* Première partie de nom */
format = loading->format;
offset = loading->sym_start + index * ELF_SIZEOF_SYM(format);
status = read_elf_symbol(format, &offset, &sym);
if (!status)
result = NULL;
else
{
name = ELF_SYM(format, sym, st_name);
offset = loading->str_start + name;
init_vmpa(&pos, offset, VMPA_NO_VIRTUAL);
content = G_BIN_FORMAT(format)->content;
prefix = (const char *)g_binary_content_get_raw_access(content, &pos, 1);
if (prefix != NULL && prefix[0] == '\0')
result = NULL;
else
{
result = strdup(prefix);
result = stradd(result, "@plt");
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : loading = chargement pour ELF à mener. *
* content = gestionnaire de contenu utilisé. [OUT] *
* first = première position traitée par la tâche. [OUT] *
* offset = décalage pour les données. [OUT] *
* final = première position dans les données à exclure. [OUT]*
* *
* Description : Donne les informations utiles à la recherche de chaînes. *
* *
* Retour : Données brutes à analyser. *
* *
* Remarques : - *
* *
******************************************************************************/
const bin_t *g_elf_loading_get_info_for_strings(const GElfLoading *loading, GBinContent **content, phys_t *first, phys_t *offset, phys_t *final)
{
const bin_t *result; /* Données à communiquer */
result = loading->data;
*content = loading->content;
*first = loading->begin;
*offset = loading->global_start;
*final = loading->global_end;
return result;
}
/******************************************************************************
* *
* Paramètres : loading = chargement pour ELF à poursuivre. *
* iter = point de départ dans la zone de données traitée. *
* pos = emplacement construit à la demande. [OUT] *
* *
* Description : Détermine l'adresse de départ d'une chaîne avec une position.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_elf_loading_compute_string_address(const GElfLoading *loading, const phys_t *iter, vmpa2t *pos)
{
init_vmpa(pos, *iter, loading->global_addr + (*iter - loading->global_start));
}