/* 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 #include #include #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)); } } }