/* Chrysalide - Outil d'analyse de fichiers binaires
* portion.c - représentation graphique de portions de binaire
*
* Copyright (C) 2013-2024 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 "portion.h"
#include
#include
//#include
//#include
#include
#include
#include "portion-int.h"
//#include "../analysis/human/asm/lang.h"
#include "../common/extstr.h"
#include "../common/sort.h"
//#include "../core/columns.h"
//#include "../glibext/gbinarycursor.h"
//#include "../glibext/linegen-int.h"
/* ------------------------------- PORTION DE BINAIRE ------------------------------- */
/* Initialise la classe des portions de données binaires. */
static void g_binary_portion_class_init(GBinaryPortionClass *);
/* Initialise une instance de portion de données binaires. */
static void g_binary_portion_init(GBinaryPortion *);
/* Supprime toutes les références externes. */
static void g_binary_portion_dispose(GBinaryPortion *);
/* Procède à la libération totale de la mémoire. */
static void g_binary_portion_finalize(GBinaryPortion *);
/* ------------------------ PARCOURS D'ENSEMBLES DE PORTIONS ------------------------ */
/* Détermine si une portion contient une adresse donnée. */
static bool g_binary_portion_contains_vmpa(const GBinaryPortion *, const vmpa2t *);
/* ---------------------------------------------------------------------------------- */
/* PORTION DE BINAIRE */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini par la GLib pour les portions de données binaires. */
G_DEFINE_TYPE(GBinaryPortion, g_binary_portion, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des portions de données binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_class_init(GBinaryPortionClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_portion_dispose;
object->finalize = (GObjectFinalizeFunc)g_binary_portion_finalize;
}
/******************************************************************************
* *
* Paramètres : portion = instance à initialiser. *
* *
* Description : Initialise une instance de portion de données binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_init(GBinaryPortion *portion)
{
vmpa2t dummy; /* Coquille presque vide */
portion->desc = NULL;
init_vmpa(&dummy, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
init_mrange(&portion->range, &dummy, VMPA_NO_VIRTUAL);
portion->continued = false;
portion->rights = PAC_NONE;
portion->subs = NULL;
portion->count = 0;
}
/******************************************************************************
* *
* Paramètres : portion = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_dispose(GBinaryPortion *portion)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < portion->count; i++)
g_clear_object(&portion->subs[i]);
G_OBJECT_CLASS(g_binary_portion_parent_class)->dispose(G_OBJECT(portion));
}
/******************************************************************************
* *
* Paramètres : portion = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_finalize(GBinaryPortion *portion)
{
if (portion->desc != NULL)
free(portion->desc);
if (portion->subs != NULL)
free(portion->subs);
G_OBJECT_CLASS(g_binary_portion_parent_class)->finalize(G_OBJECT(portion));
}
/******************************************************************************
* *
* Paramètres : addr = emplacement de la section à conserver. *
* size = taille de la section à conserver. *
* *
* Description : Crée une description de partie de code vierge. *
* *
* Retour : Instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinaryPortion *g_binary_portion_new(const vmpa2t *addr, phys_t size)
{
GBinaryPortion *result; /* Structure à retourner */
result = g_object_new(G_TYPE_BINARY_PORTION, NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : portion = instance à initialiser pleinement. *
* addr = emplacement de la section à conserver. *
* size = taille de la section à conserver. *
* *
* Description : Met en place une description de partie de code vierge. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_create(GBinaryPortion *portion, const vmpa2t *addr, phys_t size)
{
bool result; /* Bilan à retourner */
result = true;
init_mrange(&portion->range, addr, size);
return result;
}
/******************************************************************************
* *
* Paramètres : a = premières informations à consulter. *
* b = secondes informations à consulter. *
* *
* Description : Etablit la comparaison ascendante entre deux portions. *
* *
* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). *
* *
* Remarques : - *
* *
******************************************************************************/
int g_binary_portion_compare(const GBinaryPortion **a, const GBinaryPortion **b)
{
int result; /* Bilan à retourner */
const vmpa2t *addr_a; /* Adresse de la portion 'a' */
const vmpa2t *addr_b; /* Adresse de la portion 'b' */
addr_a = get_mrange_addr(&(*a)->range);
addr_b = get_mrange_addr(&(*b)->range);
result = cmp_vmpa(addr_a, addr_b);
return result;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* desc = nom à donner à la partie. *
* *
* Description : Attribue une description humaine à une partie de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_set_desc(GBinaryPortion *portion, const char *desc)
{
if (portion->desc != NULL)
free(portion->desc);
if (desc == NULL)
portion->desc = NULL;
else
{
portion->desc = strdup(desc);
if (portion->continued)
portion->desc = stradd(portion->desc, _(" (continued)"));
}
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à consulter. *
* *
* Description : Fournit la description attribuée à une partie de code. *
* *
* Retour : Nom donné à la partie. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_binary_portion_get_desc(const GBinaryPortion *portion)
{
return portion->desc;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* *
* Description : Fournit l'emplacement d'une partie de code binaire. *
* *
* Retour : Espace de couverture associé à la portion. *
* *
* Remarques : - *
* *
******************************************************************************/
const mrange_t *g_binary_portion_get_range(const GBinaryPortion *portion)
{
return &portion->range;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* max = taille maximale accordée à la portion. *
* *
* Description : Assure qu'une portion ne dépasse pas une position donnée. *
* *
* Retour : true si la portion a été modifiée, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_limit_range(GBinaryPortion *portion, phys_t max)
{
bool result; /* Bilan à retourner */
phys_t current; /* Taille courante */
current = get_mrange_length(&portion->range);
result = (current > max);
if (result)
set_mrange_length(&portion->range, max);
return result;
}
/******************************************************************************
* *
* Paramètres : portion = portion dont la définition est à metre à jour. *
* *
* Description : Définit la nature de la portion en terme d'originalité. *
* *
* Retour : - *
* *
* Remarques : L'action ne modifie aucunement la description courante. *
* C'est le changement de description qui s'appuie sur la *
* notée ici. *
* *
******************************************************************************/
void g_binary_portion_mark_as_continued(GBinaryPortion *portion, bool continued)
{
portion->continued = continued;
}
/******************************************************************************
* *
* Paramètres : portion = portion dont la définition est à consulter. *
* *
* Description : Indique la nature de la portion en terme d'originalité. *
* *
* Retour : true si la portion est la suite d'une portion découpée. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_is_continuation(const GBinaryPortion *portion)
{
return portion->continued;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* rights = droits d'accès de la partie. *
* *
* Description : Définit les droits associés à une partie de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_set_rights(GBinaryPortion *portion, PortionAccessRights rights)
{
portion->rights = rights;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à consulter. *
* *
* Description : Fournit les droits associés à une partie de code. *
* *
* Retour : Droits d'accès de la partie. *
* *
* Remarques : - *
* *
******************************************************************************/
PortionAccessRights g_binary_portion_get_rights(const GBinaryPortion *portion)
{
return portion->rights;
}
/******************************************************************************
* *
* Paramètres : portion = portion principale à compléter. *
* sub = portion à inclure dans la définition courante. *
* *
* Description : Procède à l'inclusion d'une portion dans une autre. *
* *
* Retour : Bilan de l'opération : true si inclusion, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_include(GBinaryPortion *portion, GBinaryPortion *sub)
{
bool result; /* Bilan à retourner */
bool found; /* Zone d'accueil trouvée ? */
size_t best; /* Meilleur point d'insertion */
size_t missed; /* Indice de zone à déplacer */
const mrange_t *brange; /* Raccourci de confort d'usage*/
vmpa2t end; /* Fin de la zone commune */
phys_t overlapping; /* Taille de la zone commune */
bool continued; /* Suite d'une découpe ? */
GBinaryPortion *left_part; /* Partie intégrable */
vmpa2t start; /* Départ de la seconde partie */
GBinaryPortion *right_part; /* Partie restante */
int g_binary_portion_is_included(const GBinaryPortion **a, const GBinaryPortion **b)
{
int result; /* Bilan à retourner */
result = mrange_includes_mrange(&(*b)->range, &(*a)->range);
return result;
}
found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *),
(__compar_fn_t)g_binary_portion_is_included, &best);
if (!found)
{
/**
* On se prépare à réaliser une insertion au niveau courant. Mais des
* portions précédentes sont peut-être à déplacer dans la nouvelle zone :
*
* EXIDX 0x001178 0x00009178 0x00009178 0x00008 0x00008 R 0x4
* PHDR 0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4
* INTERP 0x000154 0x00008154 0x00008154 0x00019 0x00019 R 0x1
* LOAD 0x000000 0x00008000 0x00008000 0x01184 0x01184 R E 0x8000
*
* On refait donc une passe sur toutes les sous-portions du niveau.
*
* Cette approche a le mérite de traiter également et naturellement les
* sections définies dans le désordre :
*
* [21] .bss NOBITS 00088240 07823c 0018c8 00 WA 0 0 8
* [22] __libc_freeres_ptrs NOBITS 00089b08 07823c 000018 00 WA 0 0 4
* [23] .comment PROGBITS 00000000 07823c 000022 01 MS 0 0 1
*
* Quant aux cas de figure où les portions sont identiques, l'ordre d'appel
* induit l'ordre d'inclusion.
*
* Cela concerne par exemple les zones de données :
*
* En-têtes de section:
* [Nr] Nom Type Adr Décala.Taille ES Fan LN Inf Al
* ...
* [ 2] .data PROGBITS 00010098 000098 00000c 00 WA 0 0 1
*
* En-têtes de programme:
* Type Décalage Adr. vir. Adr.phys. T.Fich. T.Mém. Fan Alignement
* ...
* LOAD 0x000098 0x00010098 0x00010098 0x0000c 0x0000c RW 0x8000
*
*/
int g_binary_portion_track_missed_inclusion(const GBinaryPortion **a, const GBinaryPortion **b)
{
int result; /* Bilan à retourner */
result = mrange_includes_mrange(&(*a)->range, &(*b)->range);
return result;
}
do
{
found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *),
(__compar_fn_t)g_binary_portion_track_missed_inclusion, &missed);
if (found)
{
result = g_binary_portion_include(sub, portion->subs[missed]);
assert(result);
portion->subs = _qdelete(portion->subs, &portion->count, sizeof(GBinaryPortion *), missed);
}
}
while (found);
/**
* Il peut arriver que certaines portions débordent de leur zone d'inclusion :
*
* [24] .bss NOBITS 00012088 002084 000044 00 WA 0 0 8
* [25] .ARM.attributes ARM_ATTRIBUTES 00000000 002084 000037 00 0 0 1
* [26] .shstrtab STRTAB 00000000 0020bb 0000ed 00 0 0 1
*
* Afin de respecter une certaine cohérence dans l'arbre des portions, on choisit
* de découper la portion qui déborde.
*/
int g_binary_portion_track_partial_inclusion(const GBinaryPortion **a, const GBinaryPortion **b)
{
int result; /* Bilan à retourner */
result = cmp_mrange_with_vmpa(&(*b)->range, get_mrange_addr(&(*a)->range));
return result;
}
found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *),
(__compar_fn_t)g_binary_portion_track_partial_inclusion, &best);
if (found)
{
brange = &portion->subs[best]->range;
compute_mrange_end_addr(brange, &end);
overlapping = compute_vmpa_diff(get_mrange_addr(&sub->range), &end);
continued = g_binary_portion_is_continuation(sub);
/* Partie contenue */
left_part = g_binary_portion_new(get_mrange_addr(&sub->range), overlapping);
g_binary_portion_set_desc(left_part, sub->desc);
g_binary_portion_mark_as_continued(left_part, continued);
g_binary_portion_set_rights(left_part, sub->rights);
/* Partie qui déborde... */
/**
* Comme la portion incluante peut avoir une définition d'adresse
* virtuelle différente de celle de la portion incluse, on recalcule
* la position de départ de la seconde partie de la portion découpée
* à partir des données d'origine.
*/
copy_vmpa(&start, get_mrange_addr(&sub->range));
advance_vmpa(&start, overlapping);
right_part = g_binary_portion_new(&start, get_mrange_length(&sub->range) - overlapping);
if (!continued)
g_binary_portion_mark_as_continued(right_part, true);
g_binary_portion_set_desc(right_part, sub->desc);
if (continued)
g_binary_portion_mark_as_continued(right_part, true);
g_binary_portion_set_rights(right_part, sub->rights);
/* Inclusions des parties */
result = g_binary_portion_include(portion, left_part);
if (result)
result = g_binary_portion_include(portion, right_part);
unref_object(left_part);
unref_object(right_part);
}
else
{
ref_object(sub);
portion->subs = qinsert(portion->subs, &portion->count, sizeof(GBinaryPortion *),
(__compar_fn_t)g_binary_portion_compare, &sub);
result = true;
}
}
/* Poursuite de l'inclusion dans la sous-portion adaptée... */
else
result = g_binary_portion_include(portion->subs[best], sub);
return result;
}
/******************************************************************************
* *
* Paramètres : portion = première couche amorçant la visite. *
* visitor = fonction à appeler à chaque étape de la descente. *
* data = adresse pointant vers des données de l'utilisateur.*
* *
* Description : Parcourt un ensemble de portions binaires. *
* *
* Retour : true si la visite a été jusqu'à son terme, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_visit(GBinaryPortion *portion, visit_portion_fc visitor, void *data)
{
bool result; /* Etat à retourner */
bool visit_portion(GBinaryPortion *p, GBinaryPortion *pp)
{
bool ret; /* Etat à retourner */
size_t i; /* Boucle de parcours */
if (p->count == 0)
ret = visitor(p, pp, BPV_SHOW, data);
else
{
ret = visitor(p, pp, BPV_ENTER, data);
for (i = 0; i < p->count && ret; i++)
ret = visit_portion(p->subs[i], p);
if (ret)
ret = visitor(p, pp, BPV_EXIT, data);
}
return ret;
}
result = visit_portion(portion, NULL);
return result;
}
/* ---------------------------------------------------------------------------------- */
/* PARCOURS D'ENSEMBLES DE PORTIONS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : portion = portion mère à consulter. *
* addr = adresse du point de recherche. *
* *
* Description : Détermine si une portion contient une adresse donnée. *
* *
* Retour : true ou false selon le résultat. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_binary_portion_contains_vmpa(const GBinaryPortion *portion, const vmpa2t *addr)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Emplacement de portion */
result = false;
range = g_binary_portion_get_range(portion);
/* Portion non allouée en mémoire : on écarte */
if (!has_virt_addr(get_mrange_addr(range)))
goto not_found;
result = mrange_contains_addr(range, addr);
not_found:
return result;
}
/******************************************************************************
* *
* Paramètres : portion = couche de portions à parcourir pour les recherches.*
* addr = adresse du point de recherche. *
* *
* Description : Recherche la portion présente à une adresse donnée. *
* *
* Retour : Portion trouvée à l'endroit indiqué. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinaryPortion *g_binary_portion_find_at_addr(GBinaryPortion *portion, const vmpa2t *addr)
{
GBinaryPortion *result; /* Portion à retourner */
size_t i; /* Boucle de parcours */
GBinaryPortion *sub; /* Portion incluse à traiter */
result = NULL;
if (!g_binary_portion_contains_vmpa(portion, addr))
goto done;
for (i = 0; i < portion->count && result == NULL; i++)
{
sub = portion->subs[i];
if (!g_binary_portion_contains_vmpa(sub, addr))
continue;
result = g_binary_portion_find_at_addr(sub, addr);
}
if (result == NULL)
{
result = portion;
ref_object(result);
}
done:
return result;
}
/******************************************************************************
* *
* Paramètres : portion = portion mère à consulter. *
* off = position physique du point de recherche. *
* *
* Description : Détermine si une portion contient une position donnée. *
* *
* Retour : true ou false selon le résultat. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_binary_portion_contains_physical(const GBinaryPortion *portion, phys_t off)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Emplacement de portion */
const vmpa2t *addr; /* Départ de la portion */
range = g_binary_portion_get_range(portion);
addr = get_mrange_addr(range);
if (!has_phys_addr(addr))
result = false;
else
result = (addr->physical <= off && off < (addr->physical + range->length));
return result;
}
/******************************************************************************
* *
* Paramètres : portion = couche de portions à parcourir pour les recherches.*
* off = position physique à retrouver. *
* pos = position correspondante. [OUT] *
* *
* Description : Fournit l'emplacement correspondant à une position physique. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_translate_offset_into_vmpa(const GBinaryPortion *portion, phys_t off, vmpa2t *pos)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours #1 */
GBinaryPortion *sub; /* Portion incluse à traiter */
const mrange_t *range; /* Emplacement de portion */
const vmpa2t *addr; /* Départ de la portion */
result = false;
for (i = 0; i < portion->count; i++)
{
sub = portion->subs[i];
if (!g_binary_portion_contains_physical(sub, off))
continue;
result = g_binary_portion_translate_offset_into_vmpa(sub, off, pos);
break;
}
if (i == portion->count)
{
result = g_binary_portion_contains_physical(portion, off);
if (result)
{
range = g_binary_portion_get_range(portion);
addr = get_mrange_addr(range);
if (has_virt_addr(get_mrange_addr(range)))
init_vmpa(pos, off, addr->virtual + off - addr->physical);
else
init_vmpa(pos, off, VMPA_NO_VIRTUAL);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : portion = portion mère à consulter. *
* virt = adresse virtuelle du point de recherche. *
* *
* Description : Détermine si une portion contient une adresse donnée. *
* *
* Retour : true ou false selon le résultat. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_binary_portion_contains_virtual(const GBinaryPortion *portion, virt_t virt)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Emplacement de portion */
const vmpa2t *addr; /* Départ de la portion */
range = g_binary_portion_get_range(portion);
addr = get_mrange_addr(range);
if (!has_virt_addr(addr))
result = false;
else
result = (addr->virtual <= virt && virt < (addr->virtual + range->length));
return result;
}
/******************************************************************************
* *
* Paramètres : portion = couche de portions à parcourir pour les recherches.*
* virt = adresse virtuelle à retrouver. *
* pos = position correspondante. [OUT] *
* *
* Description : Fournit l'emplacement correspondant à une adresse virtuelle. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_portion_translate_address_into_vmpa(const GBinaryPortion *portion, virt_t virt, vmpa2t *pos)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours #1 */
GBinaryPortion *sub; /* Portion incluse à traiter */
const mrange_t *range; /* Emplacement de portion */
const vmpa2t *addr; /* Départ de la portion */
result = false;
for (i = 0; i < portion->count; i++)
{
sub = portion->subs[i];
if (!g_binary_portion_contains_virtual(sub, virt))
continue;
result = g_binary_portion_translate_address_into_vmpa(sub, virt, pos);
break;
}
if (i == portion->count)
{
result = g_binary_portion_contains_virtual(portion, virt);
if (result)
{
range = g_binary_portion_get_range(portion);
addr = get_mrange_addr(range);
if (has_phys_addr(addr) && has_virt_addr(addr))
init_vmpa(pos, addr->physical + virt - addr->virtual, virt);
else
init_vmpa(pos, VMPA_NO_PHYSICAL, virt);
}
}
return result;
}