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