diff options
Diffstat (limited to 'src/gui/panels/bintree.c')
-rw-r--r-- | src/gui/panels/bintree.c | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/src/gui/panels/bintree.c b/src/gui/panels/bintree.c new file mode 100644 index 0000000..9963f94 --- /dev/null +++ b/src/gui/panels/bintree.c @@ -0,0 +1,724 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bintree.c - panneau d'accueil par défaut + * + * Copyright (C) 2012-2017 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "bintree.h" + + +#include <malloc.h> +#include <regex.h> + + +#include <i18n.h> + + +#include "panel-int.h" +#include "../../gtkext/tmgt.h" + + + +/* Origine de la dernière ouverture/fermeture reproductible */ +typedef enum _UserActionType +{ + UAT_COLLAPSE, /* Fermeture totale */ + UAT_EXPAND, /* Ouverture totale */ + UAT_DEPTH, /* Descente contrôlée */ + +} UserActionType; + + +/* Panneau de présentation des portions (instance) */ +struct _GBintreePanel +{ + GPanelItem parent; /* A laisser en premier */ + + GtkBuilder *builder; /* Constructeur utilisé */ + + GLoadedBinary *binary; /* Binaire représenté */ + regex_t *filter; /* Filtre appliqué ou NULL */ + + UserActionType last; /* Dernière action */ + + GtkTreeIter *top; /* Transfert de racine */ + +}; + +/* Panneau de présentation des portions (classe) */ +struct _GBintreePanelClass +{ + GPanelItemClass parent; /* A laisser en premier */ + +}; + + +/* Colonnes de la liste des messages */ +typedef enum _BinaryTreeColumn +{ + BTC_ICON, /* Image de représentation */ + BTC_CAPTION, /* Désignation de l'élément */ + BTC_START, /* Position de départ */ + BTC_END, /* Position d'arrivée */ + BTC_RIGHTS, /* Droits d'accès */ + + BTC_MATCHED, /* Correspondance établie ? */ + BTC_PORTION /* Elément interne représenté */ + +} BinaryTreeColumn; + + +/* Initialise la classe des panneaux d'affichage des portions. */ +static void g_bintree_panel_class_init(GBintreePanelClass *); + +/* Initialise une instance de panneau d'affichage des portions. */ +static void g_bintree_panel_init(GBintreePanel *); + +/* Supprime toutes les références externes. */ +static void g_bintree_panel_dispose(GBintreePanel *); + +/* Procède à la libération totale de la mémoire. */ +static void g_bintree_panel_finalize(GBintreePanel *); + +/* Parcourt un ensemble de portions. */ +static bool populate_tree_with_portion(GBinPortion *, GBinPortion *, BinaryPortionVisit, GBintreePanel *); + +/* Réagit à un changement d'affichage principal de contenu. */ +static void update_panel_with_binary_portions(GBintreePanel *, GLoadedBinary *); + +/* Modifie la profondeur affichée des portions présentes. */ +static void on_depth_spin_value_changed(GtkSpinButton *, GtkTreeView *); + +/* Prend note du changement de filtre sur les portions. */ +static void on_search_entry_changed(GtkSearchEntry *, GBintreePanel *); + +/* Parcourt un arbre en place et retire les branches filtrées. */ +static void apply_filter_on_portions(GtkTreeStore *); + +/* Réagit au changement de sélection des portions. */ +static void on_bintree_selection_changed(GtkTreeSelection *, GBintreePanel *); + + + +/* Indique le type défini pour un panneau d'affichage des portions. */ +G_DEFINE_TYPE(GBintreePanel, g_bintree_panel, G_TYPE_PANEL_ITEM); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des panneaux d'affichage des portions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_bintree_panel_class_init(GBintreePanelClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GEditorItemClass *editem; /* Encore une autre vision... */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_bintree_panel_dispose; + object->finalize = (GObjectFinalizeFunc)g_bintree_panel_finalize; + + editem = G_EDITOR_ITEM_CLASS(klass); + + editem->update_binary = (update_item_binary_fc)update_panel_with_binary_portions; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance à initialiser. * +* * +* Description : Initialise une instance de panneau d'affichage des portions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_bintree_panel_init(GBintreePanel *panel) +{ + GEditorItem *base; /* Version basique d'instance */ + GPanelItem *pitem; /* Version parente du panneau */ + GtkTreeView *treeview; /* Affichage de la liste */ + GtkCellRenderer *renderer; /* Moteur de rendu de colonne */ + GtkTreeViewColumn *column; /* Colonne de la liste */ + + /* Eléments de base */ + + base = G_EDITOR_ITEM(panel); + + base->name = PANEL_BINTREE_ID; + + pitem = G_PANEL_ITEM(panel); + + pitem->personality = PIP_SINGLETON; + pitem->lname = _("Binary tree"); + pitem->dock_at_startup = true; + pitem->path = strdup("eN"); + + /* Compléments propres */ + + panel->binary = NULL; + panel->filter = NULL; + + panel->last = UAT_EXPAND; + + /* Représentation graphique */ + + panel->builder = gtk_builder_new_from_resource("/org/chrysalide/gui/panels/bintree.ui"); + + base->widget = GTK_WIDGET(gtk_builder_get_object(panel->builder, "box")); + g_object_ref(G_OBJECT(base->widget)); + gtk_widget_unparent(base->widget); + + /* Liste des projets récents */ + + treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview")); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(treeview, column); + gtk_tree_view_set_expander_column(treeview, column); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_add_attribute(column, renderer, "surface", BTC_ICON); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_CAPTION); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(treeview, column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_START); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(treeview, column); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_END); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(treeview, column); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_RIGHTS); + + /* Connexion des signaux */ + + gtk_builder_add_callback_symbols(panel->builder, + "gtk_tree_view_collapse_all", G_CALLBACK(gtk_tree_view_collapse_all), + "gtk_tree_view_expand_all", G_CALLBACK(gtk_tree_view_expand_all), + "on_depth_spin_value_changed", G_CALLBACK(on_depth_spin_value_changed), + "on_search_entry_changed", G_CALLBACK(on_search_entry_changed), + "on_bintree_selection_changed", G_CALLBACK(on_bintree_selection_changed), + NULL); + + gtk_builder_connect_signals(panel->builder, panel); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_bintree_panel_dispose(GBintreePanel *panel) +{ + g_object_unref(G_OBJECT(panel->builder)); + + if (panel->binary != NULL) + g_object_unref(G_OBJECT(panel->binary)); + + G_OBJECT_CLASS(g_bintree_panel_parent_class)->dispose(G_OBJECT(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_bintree_panel_finalize(GBintreePanel *panel) +{ + if (panel->filter != NULL) + { + regfree(panel->filter); + free(panel->filter); + } + + G_OBJECT_CLASS(g_bintree_panel_parent_class)->finalize(G_OBJECT(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée un panneau présentant l'arborescence des portions. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GPanelItem *g_bintree_panel_new(void) +{ + GBintreePanel *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_BINTREE_PANEL, NULL); + + return G_PANEL_ITEM(result); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = portion de binaire à traiter. * +* parent = portion parent de la portion visitée. * +* visit = indication sur le sens de la visite. * +* panel = lien vers toutes les autres informations utiles. * +* * +* Description : Parcourt un ensemble de portions. * +* * +* Retour : true pour continuer la visite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool populate_tree_with_portion(GBinPortion *portion, GBinPortion *parent, BinaryPortionVisit visit, GBintreePanel *panel) +{ + const char *desc; /* Description d'origine */ + bool fmatched; /* Correspondance rencontrée ? */ + regmatch_t match; /* Position d'un filtre */ + char *node_caption; /* Etiquette de nouveau noeud */ + const mrange_t *range; /* Espace de portion à traiter */ + VMPA_BUFFER(offset); /* Décalage physique */ + char *node_start; /* Position pour nouveau noeud */ + vmpa2t end; /* Zone de construction temp. */ + char *node_end; /* Bordure pour nouveau noeud */ + PortionAccessRights rights; /* Droits d'accès à analyser */ + char hrights[4]; /* Version humainement lisible */ + char *node_rights; /* Droits pour nouveau noeud */ + cairo_surface_t *icon; /* Miniature de décoration */ + GtkTreeStore *store; /* Modèle de gestion */ + GtkTreeIter iter; /* Point d'insertion */ + GtkTreeIter *save; /* Sauvegarde d'une position */ + + if (parent == NULL) + return true; + + /* Insertion de la portion courante */ + + if (visit == BPV_ENTER || visit == BPV_SHOW) + { + /* Etiquette */ + + desc = g_binary_portion_get_desc(portion); + + fmatched = is_content_matching(panel->filter, desc, &match); + node_caption = build_highlighted_name(desc, &match, 0); + + /* Point de départ */ + + range = g_binary_portion_get_range(portion); + + vmpa2_phys_to_string(get_mrange_addr(range), MDS_UNDEFINED, offset, NULL); + + fmatched |= is_content_matching(panel->filter, offset, &match); + node_start = build_highlighted_name(offset, &match, 0); + + /* Point d'arrivée */ + + compute_mrange_end_addr(range, &end); + + vmpa2_phys_to_string(&end, MDS_UNDEFINED, offset, NULL); + + fmatched |= is_content_matching(panel->filter, offset, &match); + node_end = build_highlighted_name(offset, &match, 0); + + /* Droits nominaux */ + + rights = g_binary_portion_get_rights(portion); + + hrights[0] = (rights & PAC_READ ? 'r' : '-'); + hrights[1] = (rights & PAC_WRITE ? 'w' : '-'); + hrights[2] = (rights & PAC_EXEC ? 'x' : '-'); + hrights[3] = '\0'; + + fmatched |= is_content_matching(panel->filter, hrights, &match); + node_rights = build_highlighted_name(hrights, &match, 0); + + /* Intégration */ + + icon = NULL; + + store = GTK_TREE_STORE(gtk_builder_get_object(panel->builder, "store")); + + gtk_tree_store_append(store, &iter, panel->top); + + gtk_tree_store_set(store, &iter, + BTC_ICON, icon, + BTC_CAPTION, node_caption, + BTC_START, node_start, + BTC_END, node_end, + BTC_RIGHTS, node_rights, + BTC_MATCHED, fmatched, + BTC_PORTION, portion, + -1); + + free(node_caption); + free(node_start); + free(node_end); + free(node_rights); + + if (icon != NULL) + cairo_surface_destroy(icon); + + } + + /* Définition de la hiérarchie */ + + if (visit == BPV_ENTER) + { + save = gtk_tree_iter_copy(panel->top); + + g_object_set_data_full(G_OBJECT(portion), "_save", save, (GDestroyNotify)gtk_tree_iter_free); + + *panel->top = iter; + + } + + else if (visit == BPV_EXIT) + { + save = g_object_get_data(G_OBJECT(portion), "_save"); + + *panel->top = *save; + + g_object_set_data(G_OBJECT(portion), "_save", NULL); + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau à mettre à jour. * +* binary = nouvelle instance de binaire analysé. * +* * +* Description : Réagit à un changement d'affichage principal de contenu. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void update_panel_with_binary_portions(GBintreePanel *panel, GLoadedBinary *binary) +{ + GtkTreeStore *store; /* Modèle de gestion */ + GExeFormat *format; /* Format du binaire */ + GBinPortion *portions; /* Couche première de portions */ + GtkTreeIter top; /* Racine de l'arborescence */ + gint max_depth; /* Profondeur maximale */ + GtkSpinButton *depth_spin; /* Bouton de variation */ + GtkTreeView *treeview; /* Arborescence constituée */ + + /* Réinitialisation */ + + if (panel->binary != NULL) + g_object_unref(G_OBJECT(panel->binary)); + + panel->binary = binary; + + store = GTK_TREE_STORE(gtk_builder_get_object(panel->builder, "store")); + + gtk_tree_store_clear(store); + + /* Chargement */ + + if (binary != NULL) + { + g_object_ref(G_OBJECT(binary)); + + format = g_loaded_binary_get_format(binary); + + portions = g_exe_format_get_portions(format); + + gtk_tree_store_append(store, &top, NULL); + + gtk_tree_store_set(store, &top, + BTC_ICON, NULL, + BTC_CAPTION, g_loaded_binary_get_name(binary, false), + -1); + + panel->top = ⊤ + g_binary_portion_visit(portions, (visit_portion_fc)populate_tree_with_portion, panel); + + g_object_unref(G_OBJECT(portions)); + + g_object_unref(G_OBJECT(format)); + + } + + /* Détermination de la profondeur maximale */ + + gboolean compute_max_depth(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *max) + { + gint depth; /* Profondeur du point courant */ + + depth = gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter); + + if (depth > *max) + *max = depth; + + return FALSE; + + } + + max_depth = 0; + + gtk_tree_model_foreach(GTK_TREE_MODEL(store), (GtkTreeModelForeachFunc)compute_max_depth, &max_depth); + + depth_spin = GTK_SPIN_BUTTON(gtk_builder_get_object(panel->builder, "depth_spin")); + + gtk_spin_button_set_range(depth_spin, 0, max_depth); + + /* Restauration au mieux de l'affichage */ + + treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview")); + + switch (panel->last) + { + case UAT_COLLAPSE: + gtk_tree_view_collapse_all(treeview); + break; + + case UAT_EXPAND: + gtk_tree_view_expand_all(treeview); + break; + + case UAT_DEPTH: + on_depth_spin_value_changed(depth_spin, treeview); + break; + + } + + if (panel->filter != NULL) + apply_filter_on_portions(store); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton de réglage de l'affichage. * +* treeview = arborescence dont l'affichage est à moduler. * +* * +* Description : Modifie la profondeur affichée des portions présentes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_depth_spin_value_changed(GtkSpinButton *button, GtkTreeView *treeview) +{ + gint max_depth; /* Profondeur maximale */ + GtkTreeModel *model; /* Modèle de gestion */ + + max_depth = gtk_spin_button_get_value_as_int(button); + + gboolean apply_max_depth(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer unused) + { + gint depth; /* Profondeur du point courant */ + + depth = gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter); + + if (depth < max_depth) + gtk_tree_view_expand_to_path(treeview, path); + + return FALSE; + + } + + gtk_tree_view_collapse_all(treeview); + + model = gtk_tree_view_get_model(treeview); + + gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)apply_max_depth, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : entry = zone de texte avec un nouveau filtre d'affichage. * +* panel = panneau contenant les informations globales. * +* * +* Description : Prend note du changement de filtre sur les portions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_search_entry_changed(GtkSearchEntry *entry, GBintreePanel *panel) +{ + update_regex_on_search_entry_changed(entry, &panel->filter); + + if (panel->binary != NULL) + { + g_object_ref(G_OBJECT(panel->binary)); + update_panel_with_binary_portions(panel, panel->binary); + g_object_unref(G_OBJECT(panel->binary)); + } + +} + + +/****************************************************************************** +* * +* Paramètres : store = gestionnaire de contenu d'une arborescence donnée. * +* * +* Description : Parcourt un arbre en place et retire les branches filtrées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void apply_filter_on_portions(GtkTreeStore *store) +{ + GtkTreeIter root; /* Racine de l'arboresence */ + + void check_portion_iter(GtkTreeIter *iter) + { + GtkTreeModel *model; /* Version alternative */ + gint children_count; /* Nombre d'enfants présents */ + gint i; /* Boucle de parcours */ + GtkTreeIter child; /* Pointeur vers la descendance*/ + gboolean fmatched; /* Correspondance immédiate ? */ + + model = GTK_TREE_MODEL(store); + + children_count = gtk_tree_model_iter_n_children(model, iter); + + for (i = children_count; i > 0; i--) + if (gtk_tree_model_iter_nth_child(model, &child, iter, i - 1)) + check_portion_iter(&child); + + children_count = gtk_tree_model_iter_n_children(model, iter); + + gtk_tree_model_get(model, iter, BTC_MATCHED, &fmatched, -1); + + if (!fmatched && children_count == 0) + gtk_tree_store_remove(store, iter); + + } + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &root)) + check_portion_iter(&root); + +} + + +/****************************************************************************** +* * +* Paramètres : selection = sélection modifiée. * +* panel = structure contenant les informations maîtresses. * +* * +* Description : Réagit au changement de sélection des portions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_bintree_selection_changed(GtkTreeSelection *selection, GBintreePanel *panel) +{ + GtkTreeIter iter; /* Point de sélection */ + GtkTreeModel *model; /* Modèle de gestion */ + GBinPortion *portion; /* Portion à traiter */ + const mrange_t *range; /* Couverture dudit symbole */ + GtkDisplayPanel *display; /* Afficheur effectif de code */ + + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gtk_tree_model_get(model, &iter, BTC_PORTION, &portion, -1); + + if (portion != NULL) + { + range = g_binary_portion_get_range(portion); + + display = g_editor_item_get_current_view(G_EDITOR_ITEM(panel)); + gtk_display_panel_request_move(display, get_mrange_addr(range)); + + g_object_unref(G_OBJECT(portion)); + + } + + } + +} |