/* OpenIDA - Outil d'analyse de fichiers binaires * project.c - gestion d'un groupe de fichiers binaires * * Copyright (C) 2008 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 "project.h" #include #include #include "params.h" #include "common/xml.h" #include "gtkext/easygtk.h" #include "gtkext/gtkblockview.h" #include "gtkext/gtkdockpanel.h" #include "gtkext/gtkgraphview.h" #include "panels/panel.h" /* Conservation d'un binaire chargé */ typedef struct _loaded_binary { GOpenidaBinary *binary; /* Binaire en question */ bool lines_set; /* Construction complète */ GMutex *mutex; /* Accès à la variable */ GCond *cond; /* Attente de changement */ GtkWidget *views[BVW_COUNT]; /* Composants pour l'affichage */ } loaded_binary; /* Met en place un nouveau binaire pour un projet. */ loaded_binary *load_openida_binary(GOpenidaBinary *); /* Prend note de la fin d'une construction d'une visualisation. */ static void notify_loaded_binary(GtkBinView *, loaded_binary *); /* Fournit un support d'affichage donné pour un binaire chargé. */ GtkWidget *get_loaded_binary_view(const loaded_binary *, BinaryView); /* Propriétés d'un ensemble de fichiers ouverts */ struct openida_project { GObject *ref; /* Espace de référencement */ char *filename; /* Lieu d'enregistrement */ loaded_binary **binaries; /* Fichiers binaires associés */ size_t binaries_count; /* Nombre de ces fichiers */ GMutex *mutex; /* Modification de la liste */ }; /* Assure l'intégration d'un élément binaire dans un projet. */ void display_new_binary_of_openida_project(GOpenidaBinary *, openida_project *); /****************************************************************************** * * * Paramètres : binary = binaire chargé à encadrer. * * * * Description : Met en place un nouveau binaire pour un projet. * * * * Retour : Adresse de la structure intermédiaire ou NULL si aucune (!). * * * * Remarques : - * * * ******************************************************************************/ loaded_binary *load_openida_binary(GOpenidaBinary *binary) { loaded_binary *result; /* Structure à renvoyer */ BinaryView i; /* Boucle de parcours */ GtkWidget *scrolledwindow; /* Surface d'exposition */ GtkWidget *view; /* Affichage du binaire */ result = (loaded_binary *)calloc(1, sizeof(loaded_binary)); result->binary = binary; result->mutex = g_mutex_new(); result->cond = g_cond_new(); for (i = 0; i < BVW_COUNT; i++) { /* Préparation du support */ gdk_threads_enter(); scrolledwindow = qck_create_scrolled_window(NULL, NULL); switch (i) { default: /* GCC ! */ case BVW_BLOCK: view = gtk_block_view_new(MRD_BLOCK); break; case BVW_GRAPH: view = gtk_graph_view_new(); break; } gdk_flush (); gdk_threads_leave(); result->lines_set = false; g_signal_connect(view, "lines-set", G_CALLBACK(notify_loaded_binary), result); gtk_bin_view_set_rendering_lines(GTK_BIN_VIEW(view), binary, g_openida_binary_get_lines(binary), NULL); /* Attente de la fin de construction */ g_mutex_lock(result->mutex); while (!result->lines_set) g_cond_wait(result->cond, result->mutex); g_mutex_unlock(result->mutex); g_signal_handlers_disconnect_by_func(view, G_CALLBACK(notify_loaded_binary), result); /* Intégration finale */ gdk_threads_enter(); gtk_widget_show(view); gtk_container_add(GTK_CONTAINER(scrolledwindow), view); result->views[i] = scrolledwindow; gdk_flush (); gdk_threads_leave(); } return result; } /****************************************************************************** * * * Paramètres : view = composant d'affichage prêt à utilisation. * * binary = binaire chargé et encadré. * * * * Description : Prend note de la fin d'une construction d'une visualisation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void notify_loaded_binary(GtkBinView *view, loaded_binary *binary) { g_mutex_lock(binary->mutex); binary->lines_set = true; g_cond_signal(binary->cond); g_mutex_unlock(binary->mutex); } /****************************************************************************** * * * Paramètres : binary = binaire chargé et encadré. * * view = type d'affichage requis. * * * * Description : Fournit un support d'affichage donné pour un binaire chargé. * * * * Retour : Composant GTK dédié à un affichage particulier. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *get_loaded_binary_view(const loaded_binary *binary, BinaryView view) { return binary->views[view]; } /****************************************************************************** * * * Paramètres : project = éventuel adresse à renvoyer désormais. * * * * Description : Fournit l'adresse du projet courant. * * * * Retour : Adresse du projet ouvert ou NULL si aucun (!). * * * * Remarques : - * * * ******************************************************************************/ openida_project *_get_current_openida_project(openida_project *project) { static openida_project *result = NULL; /* Adresse à retourner */ if (project != NULL) { if (result != NULL) close_openida_project(result); result = project; } return result; } /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * * * Description : Crée un projet vide. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ openida_project *create_empty_openida_project(GObject *ref) { openida_project *result; /* Adresse à retourner */ result = (openida_project *)calloc(1, sizeof(openida_project)); result->ref = ref; result->mutex = g_mutex_new(); if (result->mutex == NULL) goto crop_error; return result; crop_error: return NULL; } /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * filename = chemin d'accès au fichier à charger. * * * * Description : Crée un projet à partir du contenu XML d'un fichier. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ openida_project *g_openida_project_new_from_xml(GObject *ref, const char *filename) { openida_project *result; /* Adresse à retourner */ xmlDocPtr xdoc; /* Structure XML chargée */ xmlXPathContextPtr context; /* Contexte pour les XPath */ xmlXPathObjectPtr xobject; /* Cible d'une recherche */ unsigned int i; /* Boucle de parcours */ size_t access_len; /* Taille d'un chemin interne */ char *access; /* Chemin pour une sous-config.*/ GOpenidaBinary *binary; /* Représentation à intégrer */ if (!open_xml_file(filename, &xdoc, &context)) return NULL; result = create_empty_openida_project(ref); result->filename = strdup(filename); /* Chargement des éléments binaires attachés */ xobject = get_node_xpath_object(context, "/OpenIDAProject/Binaries/Binary"); for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) { access_len = strlen("/OpenIDAProject/Binaries/Binary[position()=") + strlen("4294967295" /* UINT_MAX */) + strlen("]") + 1; access = calloc(access_len, sizeof(char)); snprintf(access, access_len, "/OpenIDAProject/Binaries/Binary[position()=%u]", i + 1); binary = g_openida_binary_new_from_xml(context, access); free(access); if (binary != NULL) { g_signal_connect(binary, "disassembly-done", G_CALLBACK(display_new_binary_of_openida_project), result); g_openida_binary_analyse(binary); } } if(xobject != NULL) xmlXPathFreeObject(xobject); /* Fin du chargement */ close_xml_file(xdoc, context); return result; } /****************************************************************************** * * * Paramètres : project = project à sauvegarder. * * filename = nom de fichier à utiliser ou NULL pour l'existant.* * * * Description : Procède à l'enregistrement d'un projet donné. * * * * Retour : true si l'enregistrement s'est déroule sans encombre. * * * * Remarques : - * * * ******************************************************************************/ bool g_openida_project_save(openida_project *project, const char *filename) { bool result; /* Bilan à retourner */ xmlDocPtr xdoc; /* Document XML à créer */ xmlXPathContextPtr context; /* Contexte pour les recherches*/ size_t i; /* Boucle de parcours */ size_t access_len; /* Taille d'un chemin interne */ char *access; /* Chemin pour une sous-config.*/ result = create_new_xml_file(&xdoc, &context); result &= (ensure_node_exist(xdoc, context, "/OpenIDAProject") != NULL); /* Enregistrement des éléments binaires attachés */ for (i = 0; i < project->binaries_count && result; i++) { access_len = strlen("/OpenIDAProject/Binaries/Binary[position()=") + strlen("4294967295" /* UINT_MAX */) + strlen("]") + 1; access = calloc(access_len, sizeof(char)); snprintf(access, access_len, "/OpenIDAProject/Binaries/Binary[position()=%u]", i + 1); result = g_openida_binary_save(project->binaries[i]->binary, xdoc, context, access); free(access); } /* Sauvegarde finale */ result &= save_xml_file(xdoc, filename != NULL ? filename : project->filename); if (result && filename != NULL) { if (project->filename != NULL) free(project->filename); project->filename = strdup(filename); } close_xml_file(xdoc, context); return result; } /****************************************************************************** * * * Paramètres : project = project à effacer de la mémoire. * * * * Description : Ferme un projet et libère la mémoire associée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void close_openida_project(openida_project *project) { size_t max; /* Nombre de binaires chargés */ size_t i; /* Boucle de parcours */ /* TODO : sauvegarde automatique */ /* Fermeture propre */ max = project->binaries_count; for (i = 0; i < max; i++) detach_binary_to_openida_project(project, project->binaries[0]->binary); /* ... */ free(project); } /****************************************************************************** * * * Paramètres : project = project à consulter. * * * * Description : Indique le chemin du fichier destiné à la sauvegarde. * * * * Retour : Chemin de fichier pour l'enregistrement ou NULL si indéfini. * * * * Remarques : - * * * ******************************************************************************/ const char *g_openida_project_get_filename(const openida_project *project) { return project->filename; } /****************************************************************************** * * * Paramètres : project = project à effacer de la mémoire. * * binary = fichier binaire à associer au projet actuel. * * * * Description : Attache un fichier donné à un projet donné. * * * * Retour : Emplacement de l'élément créé. * * * * Remarques : - * * * ******************************************************************************/ size_t attach_binary_to_openida_project(openida_project *project, GOpenidaBinary *binary) { size_t result; /* Indice à retourner */ g_mutex_lock(project->mutex); project->binaries = (loaded_binary **)realloc(project->binaries, ++project->binaries_count * sizeof(loaded_binary *)); result = project->binaries_count - 1; g_mutex_unlock(project->mutex); project->binaries[result] = load_openida_binary(binary); return result; } /****************************************************************************** * * * Paramètres : project = project à effacer de la mémoire. * * binary = fichier binaire à dissocier au projet actuel. * * * * Description : Détache un fichier donné à un projet donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void detach_binary_to_openida_project(openida_project *project, GOpenidaBinary *binary) { GtkDockPanel *dpanel; /* Support de panneaux */ GDockItem *ditem; /* Support d'affichage utilisé */ size_t i; /* Boucle de parcours */ dpanel = GTK_DOCK_PANEL(g_object_get_data(project->ref, "binpanel")); ditem = gtk_dock_panel_get_item_from_binary(project, binary); gtk_dock_panel_remove_item(dpanel, ditem); for (i = 0; i < project->binaries_count; i++) if (project->binaries[i]->binary == binary) break; if ((i + 1) < project->binaries_count) memmove(&project->binaries[i], &project->binaries[i + 1], (project->binaries_count - i - 1) * sizeof(loaded_binary *)); project->binaries = (loaded_binary **)realloc(project->binaries, --project->binaries_count * sizeof(loaded_binary *)); } /****************************************************************************** * * * Paramètres : project = projet à consulter. * * binary = binaire chargé et encadré. * * * * Description : Fournit le support d'affichage principal d'un binaire chargé.* * * * Retour : Composant GLib dédié à l'affichage principal. * * * * Remarques : - * * * ******************************************************************************/ GDockItem *gtk_dock_panel_get_item_from_binary(const openida_project *project, GOpenidaBinary *binary) { GDockItem *result; /* Panneau avec ses infos. */ GtkDockPanel *dpanel; /* Support de panneaux */ const char *title; /* Titre associé au binaire */ dpanel = GTK_DOCK_PANEL(g_object_get_data(project->ref, "binpanel")); title = g_openida_binary_to_string(binary); result = gtk_dock_panel_item_from_name(dpanel, strrchr(title, '/') + 1); return result; } /****************************************************************************** * * * Paramètres : project = projet à consulter. * * binary = binaire chargé, encadré et concerné. * * view = type d'affichage requis. * * binview = afficheur effectif de code binaire. [OUT] * * * * Description : Fournit un support d'affichage donné pour un binaire chargé. * * * * Retour : Composant GTK dédié à un affichage particulier. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *get_view_for_openida_project_binary(const openida_project *project, const GOpenidaBinary *binary, BinaryView view, GtkBinView **binview) { GtkWidget *result; /* Composant GTK à retourner */ size_t i; /* Boucle de parcours */ result = NULL; for (i = 0; i < project->binaries_count; i++) if (project->binaries[i]->binary == binary) { result = get_loaded_binary_view(project->binaries[i], view); *binview = GTK_BIN_VIEW(gtk_bin_get_child(result)); break; } return result; } /****************************************************************************** * * * Paramètres : project = project à effacer de la mémoire. * * count = nombre de binaires pris en compte. [OUT] * * * * Description : Fournit l'ensemble des binaires associés à un projet. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ const GOpenidaBinary **get_openida_project_binaries(const openida_project *project, size_t *count) { *count = project->binaries_count; return project->binaries; } /* ---------------------------------------------------------------------------------- */ /* PARTIE GRAPHIQUE DES [DE]CHARGEMENTS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : project = projet à traiter. * * * * Description : Place un projet au sommet de la pile des projets récents. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void push_openida_project_into_recent_list(const openida_project *project) { configuration *config; /* Configuration principale */ unsigned int i; /* Boucle de parcours */ const char *filename; /* Chemin d'un projet donné */ pop_openida_project_from_recent_list(project); config = get_main_configuration(); for (i = MPT_RECENT_PROJECT_7; i > MPT_RECENT_PROJECT_1; i--) { filename = get_string_config_value(config, i - 1); set_string_config_value(config, i, filename); } set_string_config_value(config, MPT_RECENT_PROJECT_1, project->filename); } /****************************************************************************** * * * Paramètres : project = projet à traiter. * * * * Description : Retire un projet de la pile des projets récents. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void pop_openida_project_from_recent_list(const openida_project *project) { configuration *config; /* Configuration principale */ unsigned int i; /* Boucle de parcours #1 */ const char *filename; /* Chemin d'un projet donné */ unsigned int k; /* Boucle de parcours #2 */ if (project->filename == NULL) return; config = get_main_configuration(); for (i = MPT_RECENT_PROJECT_1; i <= MPT_RECENT_PROJECT_7; i++) { filename = get_string_config_value(config, i); if (filename == NULL || strcmp(filename, project->filename) == 0) { for (k = i; k <= MPT_RECENT_PROJECT_6; k++) { filename = get_string_config_value(config, k + 1); set_string_config_value(config, k, filename); } set_string_config_value(config, k, NULL); } } } /****************************************************************************** * * * Paramètres : ref = espace global de référencement. * * func = fonction à appeler lors d'un clic sur les menus. * * * * Description : Met en place les menus rechargeant les projets récents. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void load_recent_openida_projects_list(GObject *ref, GCallback func) { gboolean one_entry; /* Au moins en entrée chargée */ GtkWidget *menuitem; /* Menu principal à compléter */ GtkWidget *menubar; /* Support pour éléments */ configuration *config; /* Configuration principale */ unsigned int i; /* Boucle de parcours */ const char *filename; /* Nom de fichier à ouvrir */ GtkWidget *submenuitem; /* Sous-menu à ajouter */ one_entry = false; menuitem = GTK_WIDGET(g_object_get_data(ref, "menu_recent_prjs")); menubar = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menuitem)); gtk_container_foreach(GTK_CONTAINER(menubar), G_CALLBACK(gtk_widget_destroy), NULL); config = get_main_configuration(); for (i = MPT_RECENT_PROJECT_1; i <= MPT_RECENT_PROJECT_7; i++) { filename = get_string_config_value(config, i); if (filename != NULL) { submenuitem = qck_create_menu_item(NULL, NULL, filename, func, ref); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); one_entry = true; } } gtk_widget_set_sensitive(menuitem, one_entry); } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à afficher. * * func = fonction à appeler lors d'un clic sur les menus. * * * * Description : Met en place un projet à l'écran. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void display_openida_project(const openida_project *project, GObject *ref) { GtkDockPanel *dpanel; /* Support de panneaux */ size_t i; /* Boucle de parcours */ GOpenidaBinary *binary; GtkWidget *view; /* Affichage du code binaire */ GDockItem *ditem; /* Panneau avec ses infos. */ GtkBinView *binview; /* Affichage à faire défiler */ dpanel = GTK_DOCK_PANEL(g_object_get_data(ref, "binpanel")); for (i = 0; i < project->binaries_count; i++) { binary = project->binaries[i]->binary; view = get_loaded_binary_view(project->binaries[i], BVW_BLOCK); ditem = g_dock_item_new(g_openida_binary_to_string(binary), view); gtk_dock_panel_add_item(dpanel, ditem); } } /****************************************************************************** * * * Paramètres : binary = élément binaire tout juste désassemblé. * * project = projet dont le contenu est à compléter. * * * * Description : Assure l'intégration d'un élément binaire dans un projet. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void display_new_binary_of_openida_project(GOpenidaBinary *binary, openida_project *project) { size_t index; /* Indice du nouveau binaire */ GtkDockPanel *dpanel; /* Support de panneaux */ GtkWidget *view; /* Affichage du code binaire */ char *title; /* Titre associé au binaire */ GDockItem *ditem; /* Panneau avec ses infos. */ GtkBinView *binview; /* Affichage à faire défiler */ index = attach_binary_to_openida_project(project, binary); dpanel = GTK_DOCK_PANEL(g_object_get_data(project->ref, "binpanel")); view = get_loaded_binary_view(project->binaries[index], BVW_BLOCK); title = g_openida_binary_to_string(binary); gdk_threads_enter(); ditem = g_dock_item_new(strrchr(title, '/') + 1, view); g_dock_item_set_desc(ditem, title); gtk_dock_panel_add_item(dpanel, ditem); gdk_flush (); gdk_threads_leave(); }