/* OpenIDA - Outil d'analyse de fichiers binaires * binportion.c - représentation graphique de portions de binaire * * Copyright (C) 2013 Cyrille Bagard * * This file is part of OpenIDA. * * 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 "gbinportion.h" #include #include #include #include #include "../common/extstr.h" /* --------------------------- COULEURS DE REPRESENTATION --------------------------- */ /* Information sur une colorisation de portion */ typedef struct _portion_color { bp_color_t code; /* Identifiant pour les usages */ char *name; /* Désignation humaine */ double red; /* Taux de rouge */ double green; /* Taux de vert */ double blue; /* Taux de bleu */ double alpha; /* Transparence */ } portion_color; /* Mémoire centrale... */ static portion_color *_portion_colors = NULL; static size_t _portion_colors_count = 0; /* Compare deux enregistrements de couleur pour portions. */ static int compare_portion_colors(const portion_color *, const portion_color *); /* Tente de retrouver une couleur de portion donnée. */ static portion_color *find_binary_portion_color(bp_color_t); /* ------------------------------- PORTION DE BINAIRE ------------------------------- */ /* Portion de données binaires quelconques (instance) */ struct _GBinPortion { GObject parent; /* A laisser en premier */ GBinPortion *container; /* Portion parente ou racine */ bp_color_t code; /* Code de la couleur de fond */ char *desc; /* Désignation humaine */ off_t offset; /* Position physique */ off_t size; /* Taille de la partie */ vmpa_t addr; /* Adresse associée */ PortionAccessRights rights; /* Droits d'accès */ GBinPortion **sub_portions; /* Portions incluses */ size_t sub_count; /* Quantité d'inclusions */ #ifdef DEBUG unsigned int valid; /* Instructions reconnues */ unsigned int db; /* Instructions non traduites */ #endif }; /* Portion de données binaires quelconques (classe) */ struct _GBinPortionClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe des blocs de données binaires. */ static void g_binary_portion_class_init(GBinPortionClass *); /* Initialise une instance de bloc de données binaires. */ static void g_binary_portion_init(GBinPortion *); /* Supprime toutes les références externes. */ static void g_binary_portion_dispose(GBinPortion *); /* Procède à la libération totale de la mémoire. */ static void g_binary_portion_finalize(GBinPortion *); /* Détermine l'aire d'une sous-portion. */ static bool g_binary_portion_compute_sub_area(GBinPortion *, GBinPortion *, const GdkRectangle *, GdkRectangle *); /* ---------------------------------------------------------------------------------- */ /* COULEURS DE REPRESENTATION */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : a = échantillon n°1 à comparer. * * b = échantillon n°2 à comparer. * * * * Description : Compare deux enregistrements de couleur pour portions. * * * * Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ static int compare_portion_colors(const portion_color *a, const portion_color *b) { int result; /* Bilan à renvoyer */ if (a->code < b->code) result = -1; else if (a->code > b->code) result = 1; else result = 0; return result; } /****************************************************************************** * * * Paramètres : name = désignation humaine de la couleur. * * red = taux de rouge dans la couleur. * * gren = taux de vert dans la couleur. * * blue = taux de bleu dans la couleur. * * alpha = transparence de la couleur. * * * * Description : Enregistre une couleur pour le dessin de portions. * * * * Retour : true si l'enregistrement a pu être effectué, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool register_binary_portion_color(const char *name, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { bp_color_t code; /* Accès rapide à la couleur */ portion_color *new; /* Nouvel élément à constituer */ /* Vérification de l'unicité des noms */ code = fnv_64a_hash(name); new = find_binary_portion_color(code); if (new != NULL) return false; /* Création du nouvel élément */ _portion_colors = (portion_color *)realloc(_portion_colors, ++_portion_colors_count * sizeof(portion_color)); new = &_portion_colors[_portion_colors_count - 1]; /* Définition du nouvel élément */ new->name = strdup(name); new->code = code; new->red = red / 255.0; new->green = green / 255.0; new->blue = blue / 255.0; new->alpha = alpha / 255.0; /* Actualisation finale par tri */ qsort(_portion_colors, _portion_colors_count, sizeof(portion_color), (__compar_fn_t)compare_portion_colors); return true; } /****************************************************************************** * * * Paramètres : name = désignation humaine de la couleur. * * * * Description : Supprime une couleur pour le dessin de portions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void unregister_binary_portion_color(const char *name) { bp_color_t code; /* Accès rapide à la couleur */ portion_color *item; /* Elément à retirer */ size_t left; /* Quantité à déplacer */ code = fnv_64a_hash(name); item = find_binary_portion_color(code); if (item == NULL) return; free(item->name); left = (size_t)(_portion_colors + _portion_colors_count) - (size_t)(item + 1); if (left > 0) memmove(item, item + 1, left); _portion_colors = (portion_color *)realloc(_portion_colors, --_portion_colors_count * sizeof(portion_color)); } /****************************************************************************** * * * Paramètres : code = identifiant de la couleur à retrouver. * * * * Description : Tente de retrouver une couleur de portion donnée. * * * * Retour : Caractéristiques de la couleur visée, ou NULL. * * * * Remarques : - * * * ******************************************************************************/ static portion_color *find_binary_portion_color(bp_color_t code) { portion_color *result; /* Elément trouvé à retourner */ result = (portion_color *)bsearch(&code, _portion_colors, _portion_colors_count, sizeof(portion_color), (__compar_fn_t)compare_portion_colors); return result; } /****************************************************************************** * * * Paramètres : - * * * * Description : Enregistre les couleurs de base pour le dessin des portions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void init_binary_portion_colors(void) { register_binary_portion_color(BPC_RAW, 0, 0, 0, 255); register_binary_portion_color(BPC_CODE, 0, 0, 255, 255); register_binary_portion_color(BPC_DATA, 255, 255, 0, 255); register_binary_portion_color(BPC_DATA_RO, 0, 255, 0, 255); } /****************************************************************************** * * * Paramètres : - * * * * Description : Supprime les couleurs de base pour le dessin des portions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void exit_binary_portion_colors(void) { while (_portion_colors_count > 0) unregister_binary_portion_color(_portion_colors[0].name); if (_portion_colors != NULL) { free(_portion_colors); _portion_colors = NULL; } } /* ---------------------------------------------------------------------------------- */ /* PORTION DE BINAIRE */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini par la GLib pour les blocs de données. */ G_DEFINE_TYPE(GBinPortion, g_binary_portion, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des blocs de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_portion_class_init(GBinPortionClass *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 bloc de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_portion_init(GBinPortion *portion) { } /****************************************************************************** * * * Paramètres : portion = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_portion_dispose(GBinPortion *portion) { size_t i; /* Boucle de parcours */ for (i = 0; i < portion->sub_count; i++) g_object_unref(G_OBJECT(portion->sub_portions[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(GBinPortion *portion) { if (portion->desc != NULL) free(portion->desc); if (portion->sub_portions != NULL) free(portion->sub_portions); G_OBJECT_CLASS(g_binary_portion_parent_class)->finalize(G_OBJECT(portion)); } /****************************************************************************** * * * Paramètres : code = désignation humaine de la couleur de fond. * * * * Description : Crée une description de partie de code vierge. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GBinPortion *g_binary_portion_new(const char *code) { GBinPortion *result; /* Structure à retourner */ result = g_object_new(G_TYPE_BIN_PORTION, NULL); result->code = fnv_64a_hash(code); 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(GBinPortion *portion, const char *desc) { if (portion->desc != NULL) free(portion->desc); portion->desc = strdup(desc); } /****************************************************************************** * * * 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 GBinPortion *portion) { return portion->desc; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * offset = position de la section à conserver. * * size = taille de la section à conserver. * * addr = adresse de la section à conserver. * * * * Description : Définit les valeurs utiles d'une partie de code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_portion_set_values(GBinPortion *portion, off_t offset, off_t size, vmpa_t addr) { portion->offset = offset; portion->size = size; portion->addr = addr; } /****************************************************************************** * * * 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(GBinPortion *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 GBinPortion *portion) { return portion->rights; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * sub = portion à inclure dans la définition courante. * * * * Description : Procède à l'inclusion d'une portion dans une autre. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_portion_include(GBinPortion *portion, GBinPortion *sub) { portion->sub_portions = (GBinPortion **)realloc(portion->sub_portions, ++portion->sub_count * sizeof(GBinPortion *)); portion->sub_portions[portion->sub_count - 1] = sub; } /****************************************************************************** * * * Paramètres : portion = portion mère à consulter. * * sub = portion fille à traiter. * * area = étendue de représentation de la portion mère. * * sub_area = étendue de représentation de la portion fille. * * * * Description : Détermine l'aire d'une sous-portion. * * * * Retour : true si la sous-surface a été calculée correctement. * * * * Remarques : - * * * ******************************************************************************/ static bool g_binary_portion_compute_sub_area(GBinPortion *portion, GBinPortion *sub, const GdkRectangle *area, GdkRectangle *sub_area) { /* On saute les portions comme le segment GNU_STACK... */ if (sub->size == 0) return false; sub_area->y = area->y; sub_area->height = area->height; sub_area->x = area->x + (sub->offset * area->width) / portion->size; sub_area->width = (sub->size * area->width) / portion->size; return true; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * x = abscisse du point de recherche. * * y = ordonnée du point de recherche. * * area = étendue de portion mère, puis celle trouvée. [OUT] * * * * Description : Recherche la portion présente à un point donné. * * * * Retour : Portion trouvée à l'endroit indiqué. * * * * Remarques : - * * * ******************************************************************************/ GBinPortion *g_binary_portion_find_at_pos(GBinPortion *portion, gint x, GdkRectangle *area) { GBinPortion *result; /* Portion à retourner */ size_t i; /* Boucle de parcours */ GBinPortion *sub; /* Portion incluse à traiter */ GdkRectangle sub_area; /* Etendue d'une sous-portion */ result = NULL; for (i = 0; i < portion->sub_count && !result; i++) { sub = portion->sub_portions[i]; if (!g_binary_portion_compute_sub_area(portion, sub, area, &sub_area)) continue; if (sub_area.x <= x && x < (sub_area.x + sub_area.width)) { result = sub; *area = sub_area; } } if (result == NULL) result = portion; return result; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * addr = adresse du point de recherche. * * area = étendue de portion mère, puis celle trouvée. [OUT] * * * * Description : Recherche la portion présente à une adresse donnée. * * * * Retour : Portion trouvée à l'endroit indiqué. * * * * Remarques : - * * * ******************************************************************************/ GBinPortion *g_binary_portion_find_at_addr(GBinPortion *portion, vmpa_t addr, GdkRectangle *area) { GBinPortion *result; /* Portion à retourner */ size_t i; /* Boucle de parcours #1 */ GBinPortion *sub; /* Portion incluse à traiter */ GdkRectangle sub_area; /* Etendue d'une sous-portion */ size_t j; /* Boucle de parcours #2 */ result = NULL; for (i = 0; i < portion->sub_count && !result; i++) { sub = portion->sub_portions[i]; /* Portion non allouée en mémoire -> adresse nulle ; on écarte */ if (sub->addr == 0) continue; if (addr < sub->addr || addr >= (sub->addr + sub->size)) continue; if (!g_binary_portion_compute_sub_area(portion, sub, area, &sub_area)) continue; for (j = 0; j < sub->sub_count && !result; j++) result = g_binary_portion_find_at_addr(sub->sub_portions[j], addr, &sub_area); if (result == NULL) { result = sub; *area = sub_area; } } if (result == NULL) result = portion; return result; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * x = abscisse du point de recherche. * * area = étendue de représentation de la portion mère. * * addr = adresse correspondante. [OUT] * * * * Description : Fournit la position correspondant à une adresse donnée. * * * * Retour : Succès de la traduction. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_portion_get_addr_from_pos(GBinPortion *portion, gint x, const GdkRectangle *area, vmpa_t *addr) { GdkRectangle owner_area; /* Aire de contenance */ GBinPortion *owner; /* Conteneur propriétaire */ owner_area = *area; owner = g_binary_portion_find_at_pos(portion, x, &owner_area); if (owner == NULL) return false; *addr = owner->addr + (owner->size * (x - owner_area.x)) / owner_area.width; return true; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * addr = adresse du point de recherche. * * area = étendue de représentation de la portion mère. * * x = position correspondante. [OUT] * * * * Description : Fournit l'adresse correspondant à une position donnée. * * * * Retour : Succès de la traduction. * * * * Remarques : - * * * ******************************************************************************/ bool g_binary_portion_get_pos_from_addr(GBinPortion *portion, vmpa_t addr, const GdkRectangle *area, gint *x) { GdkRectangle owner_area; /* Aire de contenance */ GBinPortion *owner; /* Conteneur propriétaire */ vmpa_t diff; /* Décallage à appliquer */ owner_area = *area; owner = g_binary_portion_find_at_addr(portion, addr, &owner_area); if (owner == NULL) return false; diff = addr - owner->addr; *x = owner_area.x + (diff * owner_area.width) / owner->size; return true; } /****************************************************************************** * * * Paramètres : portion = description de partie à mettre à jour. * * x = abscisse du point de recherche. * * y = ordonnée du point de recherche. * * area = étendue de représentation de la portion mère. * * tooltip = astuce à compléter. [OUT] * * * * Description : Prépare une astuce concernant une portion pour son affichage.* * * * Retour : TRUE pour valider l'affichage. * * * * Remarques : - * * * ******************************************************************************/ gboolean g_binary_portion_query_tooltip(GBinPortion *portion, gint x, gint y, const GdkRectangle *area, GtkTooltip *tooltip) { GBinPortion *selected; /* Portion à décrire ici */ char *markup; /* Description à construire */ GBinPortion *iter; /* Remontée hiérarchique */ char value[2 * VMPA_MAX_SIZE]; /* Traduction en texte */ selected = g_binary_portion_find_at_pos(portion, x, (GdkRectangle []) { *area }); /* Nom */ if (selected->desc != NULL) { markup = strdup(""); markup = stradd(markup, selected->desc); markup = stradd(markup, "\n"); for (iter = selected->container; iter != NULL; iter = iter->container) if (iter->desc != NULL) { markup = stradd(markup, selected->desc); markup = stradd(markup, "\n"); } markup = stradd(markup, "\n"); } else markup = strdup(""); markup = stradd(markup, "taille : "); snprintf(value, 2 * VMPA_MAX_SIZE, OFF_FMT, OFF_CAST(selected->size)); markup = stradd(markup, value); markup = stradd(markup, "\n"); /* Localisation */ markup = stradd(markup, ""); markup = stradd(markup, _("Localisation")); markup = stradd(markup, "\n"); markup = stradd(markup, _("physical: from ")); snprintf(value, 2 * VMPA_MAX_SIZE, OFF_FMT, OFF_CAST(selected->offset)); markup = stradd(markup, value); markup = stradd(markup, _(" to ")); snprintf(value, 2 * VMPA_MAX_SIZE, OFF_FMT, OFF_CAST(selected->offset + selected->size)); markup = stradd(markup, value); markup = stradd(markup, "\n"); markup = stradd(markup, _("memory: from ")); snprintf(value, 2 * VMPA_MAX_SIZE, VMPA_FMT_LONG, VMPA_CAST(selected->addr)); markup = stradd(markup, value); markup = stradd(markup, _(" to ")); snprintf(value, 2 * VMPA_MAX_SIZE, VMPA_FMT_LONG, VMPA_CAST(selected->addr + selected->size)); markup = stradd(markup, value); markup = stradd(markup, "\n\n"); /* Droits d'accès */ markup = stradd(markup, ""); markup = stradd(markup, _("Rights")); markup = stradd(markup, "\n"); snprintf(value, 2 * VMPA_MAX_SIZE, "%s%s%s", selected->rights & PAC_READ ? "r" : "-", selected->rights & PAC_WRITE ? "w" : "-", selected->rights & PAC_EXEC ? "x" : "-"); markup = stradd(markup, value); /* Impression finale */ gtk_tooltip_set_markup(tooltip, markup); free(markup); return TRUE; } /****************************************************************************** * * * Paramètres : portion = description de partie à consulter. * * cr = contexte graphique pour le dessin. * * area = étendue mise à disposition. * * * * Description : Représente la portion sur une bande dédiée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_portion_draw(GBinPortion *portion, cairo_t *cr, const GdkRectangle *area) { portion_color *color; /* Couleur du fond */ size_t i; /* Boucle de parcours */ GBinPortion *sub; /* Portion incluse à montrer */ GdkRectangle sub_area; /* Etendue d'une sous-portion */ /* Dessin de la portion courante */ cairo_set_line_width(cr, 1.0); cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); color = find_binary_portion_color(portion->code); if (color != NULL) cairo_set_source_rgba(cr, color->red, color->green, color->blue, color->alpha); cairo_rectangle(cr, area->x, area->y, area->width, area->height); cairo_fill(cr); if (color != NULL) cairo_set_source_rgba(cr, color->red * 0.7, color->green * 0.7, color->blue * 0.7, color->alpha); cairo_rectangle(cr, area->x, area->y, area->width, area->height); cairo_stroke(cr); /* Dessin des portions incluses */ sub_area.y = area->y; sub_area.height = area->height; for (i = 0; i < portion->sub_count; i++) { sub = portion->sub_portions[i]; if (!g_binary_portion_compute_sub_area(portion, sub, area, &sub_area)) continue; g_binary_portion_draw(sub, cr, &sub_area); } }