/* Chrysalide - Outil d'analyse de fichiers binaires * plugin.c - interactions avec un greffon donné * * Copyright (C) 2009-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 "plugin.h" #include #include #include #include #include #include #include #include #include "pglist.h" #include "plugin-int.h" /* Initialise la classe des greffons. */ static void g_plugin_module_class_init(GPluginModuleClass *); /* Initialise une instance de greffon. */ static void g_plugin_module_init(GPluginModule *); /* Supprime toutes les références externes. */ static void g_plugin_module_dispose(GPluginModule *); /* Procède à la libération totale de la mémoire. */ static void g_plugin_module_finalize(GPluginModule *); /* Indique le type défini pour un greffon. */ G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe à initialiser. * * * * Description : Initialise la classe des greffons. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_plugin_module_class_init(GPluginModuleClass *class) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_plugin_module_dispose; object->finalize = (GObjectFinalizeFunc)g_plugin_module_finalize; } /****************************************************************************** * * * Paramètres : plugin = instance à initialiser. * * * * Description : Initialise une instance de greffon. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_plugin_module_init(GPluginModule *plugin) { } /****************************************************************************** * * * Paramètres : plugin = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_plugin_module_dispose(GPluginModule *plugin) { const plugin_interface *pg_iface; /* Définition du greffon */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ pg_iface = g_plugin_module_get_interface(plugin); if (pg_iface != NULL) { lock_plugin_list_for_reading(); for (i = 0; i < pg_iface->required_count; i++) { dependency = get_plugin_by_name(pg_iface->required[i], NULL); /* Si le chargement a bien été complet avant la sortie... */ if (dependency != NULL) { /* Un coup pour l'appel à get_plugin_by_name(). */ g_object_unref(G_OBJECT(dependency)); /* Un coup pour la dépendance */ g_object_unref(G_OBJECT(dependency)); } } unlock_plugin_list_for_reading(); } if (plugin->exit != NULL) plugin->exit(plugin); if (plugin->module != NULL) g_module_close(plugin->module); G_OBJECT_CLASS(g_plugin_module_parent_class)->dispose(G_OBJECT(plugin)); } /****************************************************************************** * * * Paramètres : plugin = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_plugin_module_finalize(GPluginModule *plugin) { free(plugin->filename); if (plugin->dependencies != NULL) delete_bit_field(plugin->dependencies); G_OBJECT_CLASS(g_plugin_module_parent_class)->finalize(G_OBJECT(plugin)); } /****************************************************************************** * * * Paramètres : filename = nom du fichier à charger. * * * * Description : Crée un module pour un greffon donné. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GPluginModule *g_plugin_module_new(const gchar *filename) { GPluginModule *result; /* Structure à retourner */ plugin_abi_version_t current; /* Version de l'ABI actuelle */ size_t i; /* Boucle de parcours */ uint32_t action; /* Identifiant d'une action */ uint32_t category; /* Catégorie principale */ uint32_t sub; /* Sous-catégorie visée */ result = g_object_new(G_TYPE_PLUGIN_MODULE, NULL); result->filename = strdup(filename); result->module = g_module_open(filename, G_MODULE_BIND_LAZY); if (result->module == NULL) { log_variadic_message(LMT_ERROR, _("Error while loading the plugin candidate '%s' : %s"), filename, g_module_error()); goto bad_plugin; } #define load_plugin_symbol(mod, sym, dest) \ ({ \ bool __result; \ if (!g_module_symbol(mod, sym, (gpointer *)dest)) \ { \ log_variadic_message(LMT_ERROR, \ _("No '%s' entry in plugin candidate '%s'"), \ sym, result->filename); \ __result = false; \ } \ else __result = true; \ __result; \ }) /* Récupération de la version d'ABI */ if (!load_plugin_symbol(result->module, "_chrysalide_plugin", &result->interface)) goto bad_plugin; current = CURRENT_ABI_VERSION; if (current != result->interface->abi_version) { log_variadic_message(LMT_ERROR, _("ABI mismatch detected! Plugin '%s' rejected"), filename); goto bad_plugin; } /* Localisation des différents points d'entrée déclarés */ for (i = 0; i < result->interface->actions_count; i++) { action = result->interface->actions[i]; category = MASK_PLUGIN_CATEGORY(action); sub = MASK_PLUGIN_SUB_CATEGORY(action); switch (category) { case DPC_BASIC: switch (sub) { case DPS_NONE: break; case DPS_PG_MANAGEMENT: switch (action) { case PGA_PLUGIN_INIT: if (!load_plugin_symbol(result->module, "chrysalide_plugin_init", &result->init)) goto bad_plugin; break; case PGA_PLUGIN_EXIT: if (!load_plugin_symbol(result->module, "chrysalide_plugin_exit", &result->exit)) goto bad_plugin; break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), result->interface->actions[i], filename); break; } break; default: log_variadic_message(LMT_WARNING, _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); break; } break; case DPC_GUI: switch (sub) { case DPS_SETUP: switch (action) { case PGA_GUI_THEME: if (!load_plugin_symbol(result->module, "chrysalide_plugin_include_theme", &result->include_theme)) goto bad_plugin; break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), result->interface->actions[i], filename); break; } break; default: log_variadic_message(LMT_WARNING, _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); break; } break; case DPC_BINARY_PROCESSING: switch (sub) { case DPS_CONTENT: switch (action) { case PGA_CONTENT_EXPLORER: case PGA_CONTENT_RESOLVER: if (!load_plugin_symbol(result->module, "chrysalide_plugin_handle_binary_content", &result->handle_content)) goto bad_plugin; break; case PGA_CONTENT_ANALYZED: if (!load_plugin_symbol(result->module, "chrysalide_plugin_handle_loaded_content", &result->handle_loaded)) goto bad_plugin; break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), result->interface->actions[i], filename); break; } break; case DPS_FORMAT: switch (action) { case PGA_FORMAT_ANALYSIS_STARTED: case PGA_FORMAT_ANALYSIS_ENDED: case PGA_FORMAT_POST_ANALYSIS_STARTED: case PGA_FORMAT_POST_ANALYSIS_ENDED: if (!load_plugin_symbol(result->module, "handle_binary_format_analysis", &result->handle_fmt_analysis)) goto bad_plugin; break; case PGA_FORMAT_LOADER_LAST: if (!load_plugin_symbol(result->module, "handle_binary_format", &result->handle_format)) goto bad_plugin; break; case PGA_FORMAT_PRELOAD: if (!load_plugin_symbol(result->module, "preload_binary_format", &result->preload_format)) goto bad_plugin; break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), result->interface->actions[i], filename); break; } break; case DPS_DISASSEMBLY: if (!load_plugin_symbol(result->module, "process_binary_disassembly", &result->process_disass)) goto bad_plugin; break; case DPS_DETECTION: if (!load_plugin_symbol(result->module, "chrysalide_plugin_detect_external_tools", &result->detect)) goto bad_plugin; break; default: log_variadic_message(LMT_WARNING, _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); break; } break; default: log_variadic_message(LMT_WARNING, _("Unknown category '0x%02x' in plugin '%s'..."), category, filename); break; } } return result; bad_plugin: g_object_unref(G_OBJECT(result)); return NULL; } /****************************************************************************** * * * Paramètres : plugin = greffon à consulter. * * * * Description : Indique le fichier contenant le greffon manipulé. * * * * Retour : Chemin d'accès au greffon. * * * * Remarques : - * * * ******************************************************************************/ const char *g_plugin_module_get_filename(const GPluginModule *plugin) { return plugin->filename; } /****************************************************************************** * * * Paramètres : plugin = greffon à consulter. * * * * Description : Fournit la description du greffon dans son intégralité. * * * * Retour : Interfaçage renseigné. * * * * Remarques : - * * * ******************************************************************************/ const plugin_interface *g_plugin_module_get_interface(const GPluginModule *plugin) { return plugin->interface; } /****************************************************************************** * * * Paramètres : plugin = greffon à consulter. * * * * Description : Fournit des indications sur l'état du greffon. * * * * Retour : Fanions portant des indications. * * * * Remarques : - * * * ******************************************************************************/ PluginStatusFlags g_plugin_module_get_flags(const GPluginModule *plugin) { return plugin->flags; } /****************************************************************************** * * * Paramètres : plugin = greffon à modifier. * * flags = fanions à ajouter brutalement au greffon. * * * * Description : Ajoute des indications sur l'état du greffon. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_override_flags(GPluginModule *plugin, PluginStatusFlags flags) { plugin->flags |= flags; } /****************************************************************************** * * * Paramètres : plugin = greffon à mettre à jour. * * list = ensemble des greffons disponibles. * * count = taille de cet ensemble. * * * * Description : Met à jour l'ensemble des dépendances du greffon. * * * * Retour : true si la liste des dépendances a évolué. * * * * Remarques : - * * * ******************************************************************************/ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule **list, size_t count) { bool result; /* Bilan à faire remonter */ const plugin_interface *pg_iface; /* Définition du greffon */ bitfield_t *new; /* Nouvelle définition */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ size_t index; /* Indice de greffon visé */ result = false; if (plugin->dependencies == NULL) plugin->dependencies = create_bit_field(count, false); #ifndef NDEBUG else assert(count == get_bit_field_size(plugin->dependencies)); #endif if ((plugin->flags & (PSF_UNKNOW_DEP | PSF_DEP_LOOP)) == 0) { pg_iface = g_plugin_module_get_interface(plugin); /* Collecte des dépendances */ new = dup_bit_field(plugin->dependencies); for (i = 0; i < pg_iface->required_count; i++) { dependency = get_plugin_by_name(pg_iface->required[i], &index); if (dependency == NULL) plugin->flags |= PSF_UNKNOW_DEP; else { if (dependency->dependencies == NULL) dependency->dependencies = create_bit_field(count, false); set_in_bit_field(new, index, 1); or_bit_field(new, dependency->dependencies); /** * Si la référence pour dépendance a déjà été prise. */ if (test_in_bit_field(plugin->dependencies, index)) g_object_unref(G_OBJECT(dependency)); } } /* Mise à jour du suivi */ if (compare_bit_fields(plugin->dependencies, new) != 0) { copy_bit_field(plugin->dependencies, new); result = true; } delete_bit_field(new); /* Vérification sanitaire */ dependency = get_plugin_by_name(pg_iface->name, &index); assert(dependency != NULL); if (test_in_bit_field(plugin->dependencies, index)) plugin->flags |= PSF_DEP_LOOP; g_object_unref(G_OBJECT(dependency)); } return result; } /****************************************************************************** * * * Paramètres : plugin = greffon à valider. * * list = ensemble des greffons disponibles. * * count = taille de cet ensemble. * * * * Description : Termine le chargement du greffon préparé. * * * * Retour : Bilan du chargement effectif. * * * * Remarques : - * * * ******************************************************************************/ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t count) { bool result; /* Bilan à retourner */ PluginStatusFlags flags; /* Fanions de greffon */ const plugin_interface *pg_iface; /* Définition du greffon */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ char *dir; /* Répertoire modifiable */ /* Si un essai précédent a déjà échoué ou réussi... */ flags = g_plugin_module_get_flags(plugin); if (flags & BROKEN_PLUGIN_STATUS) return false; if (flags & PSF_LOADED) return true; /* Chargement des dépendances */ pg_iface = g_plugin_module_get_interface(plugin); result = true; for (i = 0; i < pg_iface->required_count && result; i++) { dependency = get_plugin_by_name(pg_iface->required[i], NULL); assert(dependency != NULL); result = g_plugin_module_load(dependency, list, count); g_object_unref(G_OBJECT(dependency)); } if (!result) log_variadic_message(LMT_ERROR, _("Some dependencies failed to load for plugin '%s'"), plugin->filename); /* Chargement du greffon courant */ if (result) { if (plugin->init != NULL) { result = plugin->init(plugin); if (!result) { log_variadic_message(LMT_ERROR, _("Plugin '%s' failed to load itself..."), plugin->filename); plugin->flags |= PSF_FAILURE; } } if (result) { dir = strdup(plugin->filename); dir = dirname(dir); log_variadic_message(LMT_PROCESS, _("Loaded the '%s' file as plugin from the '%s' directory"), strrchr(plugin->filename, G_DIR_SEPARATOR) + 1, dir); free(dir); plugin->flags |= PSF_LOADED; } } return result; } /****************************************************************************** * * * Paramètres : plugin = greffon à consulter. * * msg = message à faire apparaître à l'écran. * * * * Description : Présente dans le journal un message simple. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_log_simple_message(const GPluginModule *plugin, LogMessageType type, const char *msg) { size_t len; /* Taille tampon disponible */ char *buffer; /* Tampon du msg reconstitué */ len = 4 + strlen(plugin->interface->name) + 6 + strlen(msg) + 1; buffer = calloc(len, sizeof(char)); strcpy(buffer, "["); strcat(buffer, plugin->interface->name); strcat(buffer, "] "); strcat(buffer, msg); log_simple_message(type, buffer); free(buffer); } /****************************************************************************** * * * Paramètres : plugin = greffon à consulter. * * type = espèce du message à ajouter. * * fmt = format du message à faire apparaître à l'écran. * * ... = éventuels arguments venant compléter le message. * * * * Description : Présente dans le journal un message complexe. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_log_variadic_message(const GPluginModule *plugin, LogMessageType type, const char *fmt, ...) { va_list ap; /* Liste d'arguments variable */ char *buffer; /* Tampon du msg reconstitué */ va_start(ap, fmt); buffer = build_variadic_message(fmt, ap); va_end(ap); if (buffer != NULL) { g_plugin_module_log_simple_message(plugin, type, buffer); free(buffer); } } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * resources = liste de ressources à constituer. [OUT] * * count = taille de cette liste. [OUT] * * * * Description : Complète une liste de resources pour thème. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_include_theme(const GPluginModule *plugin, PluginAction action, char ***resources, size_t *count) { plugin->include_theme(plugin, action, resources, count); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * content = contenu binaire à traiter. * * wid = identifiant du groupe de traitement. * * status = barre de statut à tenir informée. * * * * Description : Procède à une opération liée à un contenu binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_handle_binary_content(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) { return plugin->handle_content(plugin, action, content, wid, status); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * content = contenu chargé à traiter. * * wid = identifiant du groupe de traitement. * * status = barre de statut à tenir informée. * * * * Description : Procède à une opération liée à un contenu chargé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_handle_loaded_content(const GPluginModule *plugin, PluginAction action, GLoadedContent *content, wgroup_id_t wid, GtkStatusStack *status) { return plugin->handle_loaded(plugin, action, content, wid, status); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * format = format de binaire à manipuler pendant l'opération. * * gid = groupe de travail dédié. * * status = barre de statut à tenir informée. * * * * Description : Procède à une opération liée à l'analyse d'un format. * * * * Retour : Bilan de l'exécution du traitement. * * * * Remarques : - * * * ******************************************************************************/ bool g_plugin_module_handle_binary_format_analysis(const GPluginModule *plugin, PluginAction action, GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status) { return plugin->handle_fmt_analysis(plugin, action, format, gid, status); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * format = format de binaire à manipuler pendant l'opération. * * status = barre de statut à tenir informée. * * * * Description : Procède à une opération liée au format de fichier uniquement.* * * * Retour : Bilan de l'exécution du traitement. * * * * Remarques : - * * * ******************************************************************************/ bool g_plugin_module_handle_binary_format(const GPluginModule *plugin, PluginAction action, GBinFormat *format, GtkStatusStack *status) { return plugin->handle_format(plugin, action, format, status); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * format = format de binaire à manipuler pendant l'opération. * * info = informations à constituer en avance de phase. * * status = barre de statut à tenir informée. * * * * Description : Procède à un préchargement de format de fichier. * * * * Retour : Bilan de l'exécution du traitement. * * * * Remarques : - * * * ******************************************************************************/ bool g_plugin_module_preload_binary_format(const GPluginModule *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status) { return plugin->preload_format(plugin, action, format, info, status); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * binary = binaire dont le contenu est en cours de traitement. * * status = barre de statut à tenir informée. * * context = contexte de désassemblage. * * * * Description : Exécute une action pendant un désassemblage de binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_process_disassembly_event(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) { plugin->process_disass(plugin, action, binary, status, context); } /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * content = élément chargé à consulter. * * version = précise si les versions doivent être recherchées. * * names = désignations humaines correspondantes, à libérer. * * count = nombre de types d'obscurcissement trouvés. [OUT] * * * * Description : Effectue la détection d'effets d'outils externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_plugin_module_detect_external_tools(const GPluginModule *plugin, PluginAction action, const GLoadedContent *content, bool version, char ***names, size_t *count) { plugin->detect(plugin, action, content, version, names, count); }