/* OpenIDA - Outil d'analyse de fichiers binaires * project.c - gestion d'un groupe de fichiers binaires * * Copyright (C) 2008-2012 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 #include "params.h" #include "common/xml.h" #include "analysis/binaries/file.h" #include "gtkext/easygtk.h" #include "gtkext/gtkblockview.h" #include "gtkext/gtkgraphview.h" #include "gtkext/gtksourceview.h" #include "gui/panels/panel.h" /* ------------------------- DEFINITION D'UN PROJET INTERNE ------------------------- */ /* Conservation d'un binaire chargé */ typedef struct _loaded_binary { GLoadedBinary *binary; /* Binaire en question */ GtkViewPanel *views[BVW_COUNT]; /* Composants pour l'affichage */ GtkWidget *scrollwindows[BVW_COUNT]; /* Supports pour l'affichage */ GEditorItem *item; /* Support d'affichage final */ } loaded_binary; /* Projet d'étude regroupant les binaires analysés (instance) */ struct _GStudyProject { GObject parent; /* A laisser en premier */ 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 */ }; /* Projet d'étude regroupant les binaires analysés (classe) */ struct _GStudyProjectClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe des projets d'étude. */ static void g_study_project_class_init(GStudyProjectClass *); /*Initialise une instance de projet d'étude. */ static void g_study_project_init(GStudyProject *); /* Supprime de l'écran un projet en place. */ static void g_study_project_hide(const GStudyProject *); /* ---------------------------------------------------------------------------------- */ /* DEFINITION D'UN PROJET INTERNE */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un projet d'étude. */ G_DEFINE_TYPE(GStudyProject, g_study_project, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des projets d'étude. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_study_project_class_init(GStudyProjectClass *klass) { } /****************************************************************************** * * * Paramètres : project = instance à initialiser. * * * * Description : Initialise une instance de projet d'étude. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_study_project_init(GStudyProject *project) { project->mutex = g_mutex_new(); if (project->mutex == NULL) /* FIXME */; } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée un nouveau projet vierge. * * * * Retour : Instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ GStudyProject *g_study_project_new(GObject *ref) { GStudyProject *result; /* Composant à retourner */ result = g_object_new(G_TYPE_STUDY_PROJECT, NULL); g_object_ref(ref); result->ref = ref; return result; } /****************************************************************************** * * * Paramètres : filename = chemin d'accès au fichier à charger. * * * * Description : Crée un projet à partir du contenu XML d'un fichier. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GStudyProject *g_study_project_open(GObject *ref, const char *filename) { GStudyProject *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.*/ GLoadedBinary *binary; /* Représentation à intégrer */ if (!open_xml_file(filename, &xdoc, &context)) return NULL; result = g_study_project_new(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_file_binary_new_from_xml(context, access); free(access); if (binary != NULL) { g_signal_connect(binary, "disassembly-done", G_CALLBACK(g_study_project_add_loaded_binary), result); g_loaded_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_study_project_save(GStudyProject *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()=%zu]", i + 1); result = g_loaded_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 à 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_study_project_get_filename(const GStudyProject *project) { return project->filename; } /****************************************************************************** * * * 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 g_study_project_add_loaded_binary(GLoadedBinary *binary, GStudyProject *project) { size_t index; /* Indice du nouveau binaire */ gdk_threads_enter(); index = g_study_project_attach_binary(project, binary); g_panel_item_dock(G_PANEL_ITEM(project->binaries[index]->item)); gdk_flush(); gdk_threads_leave(); } /****************************************************************************** * * * 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 g_study_project_attach_binary(GStudyProject *project, GLoadedBinary *binary) { size_t result; /* Indice à retourner */ loaded_binary *loaded; /* Structure à renvoyer */ BinaryView i; /* Boucle de parcours */ GtkWidget *view; /* Affichage du binaire */ BinaryDisplayType type; /* Options d'affichage */ GtkWidget *scroll; /* Surface d'exposition */ const char *title; /* Titre associé au binaire */ loaded = (loaded_binary *)calloc(1, sizeof(loaded_binary)); loaded->binary = binary; for (i = 0; i < BVW_COUNT; i++) { /* Préparation du support visuel */ //gdk_threads_enter(); switch (i) { case BVW_BLOCK: view = gtk_block_view_new(/*MRD_BLOCK*/); type = BDT_ASM; break; case BVW_GRAPH: view = gtk_graph_view_new(); type = BDT_GRAPH; break; case BVW_SOURCE: view = gtk_source_view_new(); type = BDT_ASM; /* FIXME */ break; default: /* GCC ! */ break; } gtk_widget_show(view); //gdk_flush(); //gdk_threads_leave(); loaded->views[i] = GTK_VIEW_PANEL(view); gtk_view_panel_attach_binary(loaded->views[i], binary, g_loaded_binary_display_addresses_in_text(binary, type), g_loaded_binary_display_code_in_text(binary, type)); /* Intégration finale dans un support défilant */ //gdk_threads_enter(); scroll = qck_create_scrolled_window(NULL, NULL); gtk_container_add(GTK_CONTAINER(scroll), view); loaded->scrollwindows[i] = scroll; } /* Support graphique final */ scroll = loaded->scrollwindows[BVW_BLOCK]; title = g_loaded_binary_get_filename(binary, false); loaded->item = g_panel_item_new(project->ref, title, title, scroll, "M"); /* Enregistrement dans le projet */ g_mutex_lock(project->mutex); project->binaries = (loaded_binary **)realloc(project->binaries, ++project->binaries_count * sizeof(loaded_binary *)); result = project->binaries_count - 1; project->binaries[result] = loaded; g_mutex_unlock(project->mutex); update_project_area(project); 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 g_study_project_detach_binary(GStudyProject *project, GLoadedBinary *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); FIXME !! //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 *)); update_project_area(project); } /****************************************************************************** * * * Paramètres : project = projet à consulter. * * binary = binaire chargé, encadré et concerné. * * kind = type d'affichage requis. * * view = afficheur effectif quelconque. [OUT] * * * * Description : Fournit un support d'affichage donné pour un binaire chargé. * * * * Retour : Composant GTK dédié à un affichage particulier. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *g_study_project_get_view_for_binary(const GStudyProject *project, const GLoadedBinary *binary, BinaryView kind, GtkViewPanel **view) { GtkWidget *result; /* Composant GTK à retourner */ size_t i; /* Boucle de parcours */ result = NULL; *view = NULL; for (i = 0; i < project->binaries_count; i++) if (project->binaries[i]->binary == binary) { result = project->binaries[i]->scrollwindows[kind]; *view = project->binaries[i]->views[kind]; break; } return result; } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à afficher. * * * * Description : Met en place un projet à l'écran. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_study_project_display(const GStudyProject *project) { size_t i; /* Boucle de parcours */ for (i = 0; i < project->binaries_count; i++) g_panel_item_dock(G_PANEL_ITEM(project->binaries[i]->item)); } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à cacher. * * * * Description : Supprime de l'écran un projet en place. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_study_project_hide(const GStudyProject *project) { size_t i; /* Boucle de parcours */ for (i = 0; i < project->binaries_count; i++) g_panel_item_undock(G_PANEL_ITEM(project->binaries[i]->item)); } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à afficher. * * count = nombre de binaires pris en compte. [OUT] * * * * Description : Fournit l'ensemble des binaires associés à un projet. * * * * Retour : Liste à libérer de la mémoire. * * * * Remarques : - * * * ******************************************************************************/ GLoadedBinary **g_study_project_get_binaries(const GStudyProject *project, size_t *count) { GLoadedBinary **result; /* Tableau à retourner */ size_t i; /* Boucle de parcours */ *count = project->binaries_count; result = (GLoadedBinary **)calloc(*count, sizeof(GLoadedBinary *)); for (i = 0; i < *count; i++) { result[i] = project->binaries[i]->binary; g_object_ref(G_OBJECT(result[i])); } return result; } /* ---------------------------------------------------------------------------------- */ /* GESTION GLOBALISEE DES PROJETS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * 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 : - * * * ******************************************************************************/ GStudyProject *_get_current_study_project(GStudyProject *project) { static GStudyProject *result = NULL; /* Adresse à retourner */ if (project != NULL) { if (result != NULL) { g_study_project_hide(result); g_object_unref(G_OBJECT(result)); } result = project; } return result; } /****************************************************************************** * * * Paramètres : - * * * * Description : Fournit le gestionnaire des projets connus. * * * * Retour : Instance de gestion unique. * * * * Remarques : - * * * ******************************************************************************/ GtkRecentManager *get_projects_manager(void) { static GtkRecentManager *result = NULL; /* Singleton à retourner */ if (result == NULL) { result = gtk_recent_manager_get_default(); //gtk_recent_manager_purge_items(result, NULL); } return result; } /****************************************************************************** * * * Paramètres : project = projet à traiter. * * * * Description : Place un projet au sommet de la pile des projets récents. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void push_project_into_recent_list(const GStudyProject *project) { GtkRecentManager *manager; /* Gestionnaire global */ char *qualified; /* Chemin avec 'file://' */ GtkRecentData recent; /* Données complètes */ if (project->filename == NULL) return; /* Constitution de la liste des projets récents */ /* Constitution de la liste des projets récents */ manager = get_projects_manager(); qualified = (char *)calloc(strlen("file://") + strlen(project->filename) + 1, sizeof(char)); strcpy(qualified, "file://"); strcat(qualified, project->filename); memset(&recent, 0, sizeof(GtkRecentData)); recent.mime_type = "application/chrysalide.project"; recent.app_name = "Chrysalide"; recent.app_exec = "chrysalide %f"; gtk_recent_manager_add_full(manager, qualified, &recent); free(qualified); /* Pour la prochaine ouverture du programme... */ set_string_config_value(get_main_configuration(), MPT_LAST_PROJECT, project->filename); /* Pour la prochaine ouverture du programme... */ set_string_config_value(get_main_configuration(), MPT_LAST_PROJECT, project->filename); }