From 42540e681161aab0a1c27c66541ed5dc833ca411 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Sun, 7 Feb 2021 21:30:07 +0100 Subject: Created the right place for plugins in the Python API. --- configure.ac | 1 + plugins/pychrysalide/Makefile.am | 9 +- plugins/pychrysalide/constants.c | 100 -- plugins/pychrysalide/constants.h | 38 - plugins/pychrysalide/core.c | 55 +- plugins/pychrysalide/plugin.c | 1883 ----------------------------- plugins/pychrysalide/plugin.h | 75 -- plugins/pychrysalide/plugins/Makefile.am | 24 + plugins/pychrysalide/plugins/constants.c | 100 ++ plugins/pychrysalide/plugins/constants.h | 38 + plugins/pychrysalide/plugins/module.c | 104 ++ plugins/pychrysalide/plugins/module.h | 42 + plugins/pychrysalide/plugins/plugin.c | 1884 ++++++++++++++++++++++++++++++ plugins/pychrysalide/plugins/plugin.h | 75 ++ plugins/python/abackup/plugin.py | 2 +- plugins/python/apkfiles/apkfiles.py | 2 +- plugins/python/cglimpse/core.py | 2 +- plugins/python/checksec/plugin.py | 2 +- plugins/python/liveconv/plugin.py | 2 +- plugins/python/scripting/core.py | 2 +- 20 files changed, 2283 insertions(+), 2157 deletions(-) delete mode 100644 plugins/pychrysalide/constants.c delete mode 100644 plugins/pychrysalide/constants.h delete mode 100644 plugins/pychrysalide/plugin.c delete mode 100644 plugins/pychrysalide/plugin.h create mode 100644 plugins/pychrysalide/plugins/Makefile.am create mode 100644 plugins/pychrysalide/plugins/constants.c create mode 100644 plugins/pychrysalide/plugins/constants.h create mode 100644 plugins/pychrysalide/plugins/module.c create mode 100644 plugins/pychrysalide/plugins/module.h create mode 100644 plugins/pychrysalide/plugins/plugin.c create mode 100644 plugins/pychrysalide/plugins/plugin.h diff --git a/configure.ac b/configure.ac index 1ee20a4..5f10574 100644 --- a/configure.ac +++ b/configure.ac @@ -502,6 +502,7 @@ AC_CONFIG_FILES([Makefile plugins/pychrysalide/gui/core/Makefile plugins/pychrysalide/gui/panels/Makefile plugins/pychrysalide/mangling/Makefile + plugins/pychrysalide/plugins/Makefile plugins/python/Makefile plugins/python/abackup/Makefile plugins/python/apkfiles/Makefile diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am index d3b5483..b4b20ad 100644 --- a/plugins/pychrysalide/Makefile.am +++ b/plugins/pychrysalide/Makefile.am @@ -1,4 +1,6 @@ +DEFAULT_INCLUDES = -I$(top_builddir) -idirafter. + lib_LTLIBRARIES = pychrysalide.la libdir = $(pluginslibdir) @@ -6,10 +8,8 @@ libdir = $(pluginslibdir) pychrysalide_la_SOURCES = \ access.h access.c \ - constants.h constants.c \ core.h core.c \ helpers.h helpers.c \ - plugin.h plugin.c \ star.h star.c \ strenum.h strenum.c \ struct.h struct.c \ @@ -25,7 +25,8 @@ pychrysalide_la_LIBADD = \ glibext/libpychrysaglibext.la \ gtkext/libpychrysagtkext.la \ gui/libpychrysagui.la \ - mangling/libpychrysamangling.la + mangling/libpychrysamangling.la \ + plugins/libpychrysaplugins.la pychrysalide_la_LDFLAGS = \ -module -avoid-version \ @@ -43,4 +44,4 @@ AM_CPPFLAGS = $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) $(LIBGTK_CFLAGS) $(LIBX AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) -SUBDIRS = analysis arch common core debug format glibext gtkext gui mangling +SUBDIRS = analysis arch common core debug format glibext gtkext gui mangling plugins diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c deleted file mode 100644 index 97cf43b..0000000 --- a/plugins/pychrysalide/constants.c +++ /dev/null @@ -1,100 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * constants.c - ajout des constantes principales - * - * Copyright (C) 2020 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 "constants.h" - - -#include - - -#include "helpers.h" - - - -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes relatives aux greffons Python. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_plugin_module_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - result = true; - - values = PyDict_New(); - - if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE); - if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT); - if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED); - if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT); - if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING); - if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME); - if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION); - if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING); - if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER); - if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER); - if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD); - if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED); - if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, false, "PluginAction", values, - "Features with which plugins can extend the core of Chrysalide."); - - exit: - - return result; - -} diff --git a/plugins/pychrysalide/constants.h b/plugins/pychrysalide/constants.h deleted file mode 100644 index 21f9d0e..0000000 --- a/plugins/pychrysalide/constants.h +++ /dev/null @@ -1,38 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * constants.h - prototypes pour l'ajout des constantes principales - * - * Copyright (C) 2020 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 - */ - - -#ifndef _PLUGINS_PYCHRYSALIDE_CONSTANTS_H -#define _PLUGINS_PYCHRYSALIDE_CONSTANTS_H - - -#include -#include - - -/* Définit les constantes relatives aux greffons Python. */ -bool define_plugin_module_constants(PyTypeObject *); - - - -#endif /* _PLUGINS_PYCHRYSALIDE_CONSTANTS_H */ diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index 7bc46e4..30d4259 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -49,7 +49,6 @@ #include "access.h" #include "helpers.h" -#include "plugin.h" #include "star.h" #include "strenum.h" #include "struct.h" @@ -63,6 +62,8 @@ #include "gtkext/module.h" #include "gui/module.h" #include "mangling/module.h" +#include "plugins/module.h" +#include "plugins/plugin.h" @@ -100,9 +101,6 @@ static bool set_version_for_gtk_namespace(const char *); /* Point de sortie pour l'initialisation de Python. */ static void PyExit_pychrysalide(void); -/* Ajoute le module 'plugins' au module Python. */ -static bool add_plugin_module_to_python_module(PyObject *); - /* Complète les chemins de recherches de Python. */ static void extend_python_path(const char *); @@ -371,50 +369,6 @@ static void PyExit_pychrysalide(void) /****************************************************************************** * * -* Paramètres : super = module dont la définition est à compléter. * -* * -* Description : Ajoute le module 'plugins' au module Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool add_plugin_module_to_python_module(PyObject *super) -{ - bool result; /* Bilan à retourner */ - PyObject *module; /* Sous-module mis en place */ - -#define PYCHRYSALIDE_PLUGINS_DOC \ - "Home for all plugins without another home." - - static PyModuleDef py_chrysalide_deguard_module = { - - .m_base = PyModuleDef_HEAD_INIT, - - .m_name = "pychrysalide.plugins", - .m_doc = PYCHRYSALIDE_PLUGINS_DOC, - - .m_size = -1, - - }; - - result = false; - - module = build_python_module(super, &py_chrysalide_deguard_module); - - result = (module != NULL); - - assert(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : - * * * * Description : Point d'entrée pour l'initialisation de Python. * @@ -514,8 +468,6 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) if (status) status = add_features_module(result); - if (status) add_plugin_module_to_python_module(result); - if (status) status = add_analysis_module(result); if (status) status = add_arch_module(result); if (status) status = add_common_module(result); @@ -526,8 +478,8 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) if (status) status = add_gtkext_module(result); if (status) status = add_gui_module(result); if (status) status = add_mangling_module(result); + if (status) status = add_plugins_module(result); - if (status) status = ensure_python_plugin_module_is_registered(); if (status) status = ensure_python_string_enum_is_registered(); if (status) status = ensure_python_py_struct_is_registered(); @@ -541,6 +493,7 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) if (status) status = populate_gtkext_module(); if (status) status = populate_gui_module(); if (status) status = populate_mangling_module(); + if (status) status = populate_plugins_module(); if (!status) { diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c deleted file mode 100644 index 88b20de..0000000 --- a/plugins/pychrysalide/plugin.c +++ /dev/null @@ -1,1883 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * plugin.c - interactions avec un greffon Python - * - * Copyright (C) 2018-2019 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 - - -#include "access.h" -#include "constants.h" -#include "core.h" -#include "helpers.h" -#include "core/constants.h" - - - -/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ - - -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_plugin_module_new(PyTypeObject *, PyObject *, PyObject *); - -/* Initialise la classe des greffons d'extension. */ -static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer); - -/* Initialise une instance sur la base du dérivé de GObject. */ -static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); - -/* Encadre une étape de la vie d'un greffon. */ -static bool py_plugin_module_manage_wrapper(GPluginModule *); - -/* Accompagne la fin du chargement des modules natifs. */ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction); - -/* Complète une liste de resources pour thème. */ -static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *); - -/* Rend compte de la création d'un panneau. */ -static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *); - -/* Rend compte d'un affichage ou d'un retrait de panneau. */ -static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool); - -/* Procède à une opération liée à un contenu binaire. */ -static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); - -/* Procède à une opération liée à un contenu chargé. */ -static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *); - -/* Procède à une opération liée à l'analyse d'un format. */ -static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *); - -/* Procède à un préchargement de format de fichier. */ -static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); - -/* Procède au rattachement d'éventuelles infos de débogage. */ -static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *); - -/* Exécute une action pendant un désassemblage de binaire. */ -static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); - -/* Effectue la détection d'effets d'outils externes. */ -static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *); - - - -/* --------------------- INTERFACE INTERNE POUR GREFFONS PYTHON --------------------- */ - - -/* Ligne de représentation de code binaire (instance) */ -struct _GPythonPlugin -{ - GPluginModule parent; /* Instance parente */ - -}; - - -/* Ligne de représentation de code binaire (classe) */ -struct _GPythonPluginClass -{ - GPluginModuleClass parent; /* Classe parente */ - -}; - - -/* Initialise la classe des greffons Python. */ -static void g_python_plugin_class_init(GPythonPluginClass *); - -/* Initialise l'instance d'un greffon Python. */ -static void g_python_plugin_init(GPythonPlugin *); - -/* Supprime toutes les références externes. */ -static void g_python_plugin_dispose(GPythonPlugin *); - -/* Description : Procède à la libération totale de la mémoire. */ -static void g_python_plugin_finalize(GPythonPlugin *); - -/* Fournit le nom brut associé au greffon. */ -static char *g_python_plugin_get_modname(const GPythonPlugin *); - - - -/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ - - -/* Construit le nom d'un fichier de configuration du greffon. */ -static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *); - -/* Affiche un message dans le journal des messages système. */ -static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); - -/* Fournit le nom brut associé au greffon. */ -static PyObject *py_plugin_module_get_modname(PyObject *, void *); - -/* Indique le fichier contenant le greffon manipulé. */ -static PyObject *py_plugin_module_get_filename(PyObject *, void *); - - - -/* ---------------------------------------------------------------------------------- */ -/* GLUE POUR CREATION DEPUIS PYTHON */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_plugin_module_type(); - - if (type == base) - { - result = NULL; - PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); - goto exit; - } - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_PYTHON_PLUGIN, type->tp_name, - (GClassInitFunc)py_plugin_module_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type, base); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * -* * -* Description : Initialise la classe des greffons d'extension. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) -{ - class->init = NULL; - class->manage = py_plugin_module_manage_wrapper; - class->exit = NULL; - - class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper; - - class->include_theme = py_plugin_module_include_theme_wrapper; - class->notify_panel = py_plugin_module_notify_panel_creation_wrapper; - class->notify_docking = py_plugin_module_notify_panel_docking_wrapper; - - class->handle_content = py_plugin_module_handle_binary_content_wrapper; - class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; - - class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper; - class->preload_format = py_plugin_module_preload_binary_format_wrapper; - class->attach_debug = py_plugin_module_attach_debug_format_wrapper; - - class->process_disass = py_plugin_module_process_disassembly_event_wrapper; - - class->detect = py_plugin_module_detect_external_tools_wrapper; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet à initialiser (théoriquement). * -* args = arguments fournis à l'appel. * -* kwds = arguments de type key=val fournis. * -* * -* Description : Initialise une instance sur la base du dérivé de GObject. * -* * -* Retour : 0. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - int ret; /* Bilan d'un appel */ - GPluginModule *plugin; /* Greffon à manipuler */ - plugin_interface *iface; /* Interface à constituer */ - GPluginModule *dependency; /* Module nécessaire */ - PyObject *value; /* Valeur à présence imposée */ - size_t i; /* Boucle de parcours */ - PyObject *action; /* Identifiant d'une action */ - -#define PLUGIN_MODULE_DOC \ - "PythonModule is the class allowing the creation of Chrysalide plugins" \ - " for Python." \ - "\n" \ - "Calls to the *__init__* constructor of this abstract object expect" \ - " no particular argument.\n" \ - "\n" \ - "Several items have to be defined as class attributes in the final" \ - " class:\n" \ - "* *_name*: a string providing a small name for the plugin;\n" \ - "* *_desc*: a string for a human readable description of the plugin;\n" \ - "* *_version*: a string providing the version of the plugin;\n" \ - "* *_url*: a string for the homepage describing the plugin;\n" \ - "* *_actions*: a tuple of pychrysalide.PluginModule.PluginAction" \ - " defining the features the plugin is bringing; this list can be" \ - " empty.\n" \ - "\n" \ - "Depending on the implemented actions, some of the following methods" \ - " have to be defined for new classes:\n" \ - "* pychrysalide.PluginModule._init_config();\n" \ - "* pychrysalide.PluginModule._notify_plugins_loaded();\n" \ - "* pychrysalide.PluginModule._include_theme();\n" \ - "* pychrysalide.PluginModule._on_panel_creation;\n" \ - "* pychrysalide.PluginModule._on_panel_docking();\n" \ - "* pychrysalide.PluginModule._handle_binary_content();\n" \ - "* pychrysalide.PluginModule._handle_loaded_content();\n" \ - "* pychrysalide.PluginModule._handle_format_analysis();\n" \ - "* pychrysalide.PluginModule._preload_format();\n" \ - "* pychrysalide.PluginModule._attach_debug_format();\n" \ - "* pychrysalide.PluginModule._process_disassembly_event();\n" \ - "* pychrysalide.PluginModule._detect_external_tools()." - - /* Initialisation d'un objet GLib */ - - ret = forward_pygobjet_init(self); - if (ret == -1) return -1; - - /* Eléments de base */ - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - - iface = calloc(1, sizeof(plugin_interface)); - plugin->interface = iface; - -#define LOAD_PYTHON_IFACE(attr) \ - do \ - { \ - value = PyObject_GetAttrString(self, "_" #attr); \ - if (value == NULL) \ - { \ - PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \ - return -1; \ - } \ - if (PyUnicode_Check(value)) \ - { \ - iface->attr = strdup(PyUnicode_AsUTF8(value)); \ - Py_DECREF(value); \ - } \ - else \ - { \ - Py_DECREF(value); \ - PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \ - return -1; \ - } \ - assert(iface->attr != NULL); \ - } \ - while (0); - - LOAD_PYTHON_IFACE(name); - LOAD_PYTHON_IFACE(desc); - LOAD_PYTHON_IFACE(version); - LOAD_PYTHON_IFACE(url); - - iface->container = false; - - /** - * Comme le greffon n'est pas passé par la résolution des dépendances, - * orchestrée par la fonction g_plugin_module_resolve_dependencies(), - * on simule l'effet attendu en obtenant une référence par un appel à - * get_plugin_by_name(). - * - * L'incrémentation des références doit coller au plus près de - * l'inscription nominative du greffon : en cas de sortie impromptue - * (lorsqu'une erreur intervient pendant un chargement par exemple), - * l'état de l'ensemble est ainsi cohérent au moment du retrait du - * greffon fautif via la fonction g_plugin_module_dispose(). - */ - - lock_plugin_list_for_reading(); - dependency = get_plugin_by_name("PyChrysalide", NULL); - unlock_plugin_list_for_reading(); - - assert(dependency != NULL); - - if (dependency == NULL) - { - PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!")); - return -1; - } - - iface->required = malloc(sizeof(char *)); - iface->required[0] = "PyChrysalide"; - iface->required_count = 1; - - /* Validation du reste de l'interface */ - - value = PyObject_GetAttrString(self, "_actions"); - - if (value == NULL) - { - PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing.")); - return -1; - } - - if (!PyTuple_Check(value)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple.")); - return -1; - } - - iface->actions_count = PyTuple_Size(value); - iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); - - for (i = 0; i < iface->actions_count; i++) - { - action = PyTuple_GetItem(value, i); - - if (!PyLong_Check(action)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); - return -1; - } - - iface->actions[i] = PyLong_AsUnsignedLong(action); - - } - - Py_DECREF(value); - - return 0; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Encadre une étape de la vie d'un greffon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) -{ - bool result; /* Bilan à faire remonter */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _manage, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called to react to several steps of the plugin" \ - " life.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *PLUGIN_LOADED*." \ -) - - result = true; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_manage")) - { - args = PyTuple_New(1); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED)); - - pyret = run_python_method(pyobj, "_manage", args); - - result = (pyret == Py_True); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* * -* Description : Accompagne la fin du chargement des modules natifs. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _notify_plugins_loaded, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called once all the (native?) plugins are" \ - " loaded.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_notify_plugins_loaded")) - { - args = PyTuple_New(1); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - - pyret = run_python_method(pyobj, "_notify_plugins_loaded", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* dark = indique une préférence pour la variante foncée. * -* resources = liste de ressources à constituer. [OUT] * -* count = taille de cette liste. [OUT] * -* * -* Description : Complète une liste de resources pour thème. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_plugin_module_include_theme_wrapper(const GPluginModule *plugin, PluginAction action, gboolean dark, char ***resources, size_t *count) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *darkness; /* Valeur booléenne à joindre */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - Py_ssize_t length; /* Nombre d'éléments collectés */ - Py_ssize_t i; /* Boucle de parcours */ - PyObject *res; /* Ressource à ajouter */ - -#define PLUGIN_MODULE_INCLUDE_THEME_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _include_theme, "$self, action, dark, /", \ - METH_VARARGS, \ - "Abstract method called once all the native plugins are loaded.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the *dark* parameter indicates if a dark theme is" \ - " being to get loaded.\n" \ - "\n" \ - "The expected result is a list of CSS definition resource URIs," \ - " provided as strings such as 'resource:///org/xxx/extra.css'" \ - " for instance.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *GUI_THEME*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_include_theme")) - { - args = PyTuple_New(2); - - darkness = (dark ? Py_True : Py_False); - Py_INCREF(darkness); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, darkness); - - pyret = run_python_method(pyobj, "_include_theme", args); - - if (!PySequence_Check(pyret)) - g_plugin_module_log_simple_message(plugin, LMT_ERROR, _("The returned value must be a string list")); - - else - { - length = PySequence_Length(pyret); - - for (i = 0; i < length; i++) - { - res = PySequence_GetItem(pyret, i); - - if (!PyUnicode_Check(res)) - g_plugin_module_log_variadic_message(plugin, LMT_ERROR, - _("The returned #%zd value must be a string")); - - else - { - *resources = realloc(*resources, ++(*count) * sizeof(char **)); - *resources[*count - 1] = strdup(PyUnicode_DATA(res)); - } - - Py_DECREF(res); - - } - - } - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* item = nouveau panneau créé. * -* * -* Description : Rend compte de la création d'un panneau. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *plugin, PluginAction action, GPanelItem *item) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _on_panel_creation, "$self, action, item, /", \ - METH_VARARGS, \ - "Abstract method called when a new instance of panel is created.\n" \ - "\n" \ - "The expected *action* is a pychrysalide.PluginModule.PluginAction" \ - " value and the *item* is a pychrysalide.gui.PanelItem instance.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *PANEL_CREATION*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_on_panel_creation")) - { - args = PyTuple_New(2); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(item))); - - pyret = run_python_method(pyobj, "_on_panel_creation", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* item = panneau marqué par un changement d'affichage. * -* dock = indique une accroche et non un décrochage. * -* * -* Description : Rend compte d'un affichage ou d'un retrait de panneau. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *plugin, PluginAction action, GPanelItem *item, bool dock) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pydock; /* Valeur booléenne à joindre */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _on_panel_docking, "$self, action, item, dock, /", \ - METH_VARARGS, \ - "Abstract method called when a panel is docked or undocked into" \ - " the Chrysalide main graphical interface.\n" \ - "\n" \ - "The expected *action* is a pychrysalide.PluginModule.PluginAction" \ - " value, the *item* is a pychrysalide.gui.PanelItem instance and" \ - " the *dock* parameter indicates if the panel request a docking" \ - " operation or an undocking one.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *PANEL_DOCKING*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_on_panel_docking")) - { - args = PyTuple_New(3); - - pydock = (dock ? Py_True : Py_False); - Py_INCREF(pydock); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(item))); - PyTuple_SetItem(args, 2, pydock); - - pyret = run_python_method(pyobj, "_on_panel_docking", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ - -static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _handle_binary_content, "$self, action, content, wid, status, /", \ - METH_VARARGS, \ - "Abstract method used to explore a binary content (and possibly to add new" \ - " contents to explore) or to load a recognized binary content into a" \ - " pychrysalide.analysis.LoadedContent instance.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the initial binary content is a pychrysalide.analysis.BinContent" \ - " instance. A tracking identifier is provided and is aimed to be" \ - " used with methods from pychrysalide.analysis.ContentExplorer and" \ - " pychrysalide.analysis.ContentResolver. A reference to the main status bar" \ - " may also be provided, as a pychrysalide.gtkext.StatusStack instance if" \ - " running in graphical mode or None otherwise.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *CONTENT_EXPLORER* or *CONTENT_RESOLVER*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_handle_binary_content")) - { - args = PyTuple_New(4); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); - PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(wid)); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - - pyret = run_python_method(pyobj, "_handle_binary_content", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* content = contenu chargé à traiter. * -* gid = 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 : - * -* * -******************************************************************************/ - -static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedContent *content, wgroup_id_t gid, GtkStatusStack *status) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _handle_loaded_content, "$self, action, content, gid, status, /", \ - METH_VARARGS, \ - "Abstract method run once a loaded binary has been analyzed with success.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the analyzed content is a pychrysalide.analysis.LoadedContent" \ - " instance. The identifier refers to the working queue used to process the" \ - " analysis. A reference to the main status bar may also be provided, as a" \ - " pychrysalide.gtkext.StatusStack instance if running in graphical mode or" \ - " None otherwise.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *CONTENT_ANALYZED*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_handle_loaded_content")) - { - args = PyTuple_New(4); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); - PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid)); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - - pyret = run_python_method(pyobj, "_handle_loaded_content", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ - -static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *plugin, PluginAction action, GKnownFormat *format, wgroup_id_t gid, GtkStatusStack *status) -{ - bool result; /* Bilan à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _handle_binary_format_analysis, "$self, action, format, gid, status, /", \ - METH_VARARGS, \ - "Abstract method run at several different steps of a binary format analysis:\n" \ - "* at the beginning and at the end of the main analysis pass;\n" \ - "* at the beginning and at the end of the extra final pass.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the provided format is a pychrysalide.format.KnownFormat" \ - " instance. The identifier refers to the working queue used to process the" \ - " analysis. A reference to the main status bar may also be provided, as a" \ - " pychrysalide.gtkext.StatusStack instance if running in graphical mode or" \ - " None otherwise.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *FORMAT_ANALYSIS_STARTED*, *FORMAT_ANALYSIS_ENDED*," \ - " *FORMAT_POST_ANALYSIS_STARTED* or *FORMAT_POST_ANALYSIS_ENDED*." \ -) - - result = false; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_handle_format_analysis")) - { - args = PyTuple_New(4); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); - PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid)); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - - pyret = run_python_method(pyobj, "_handle_format_analysis", args); - - result = (pyret == Py_True); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; - -} - - -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ - -static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _preload_binary_format, "$self, action, format, info, status, /", \ - METH_VARARGS, \ - "Abstract method which is an opportunity to setup instructions or comments" \ - " ahead of the disassembling process.\n" \ - "\n" \ - "Format fields do not need to get disassembled and may be annotated for" \ - " instance.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the provided format is a pychrysalide.format.BinFormat" \ - " instance. The information holder to fill is a pychrysalide.format.PreloadInfo"\ - " instance. A reference to the main status bar may also be provided, as a" \ - " pychrysalide.gtkext.StatusStack instance if running in graphical mode or" \ - " None otherwise.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *FORMAT_PRELOAD*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_preload_format")) - { - args = PyTuple_New(4); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); - PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(info))); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - - pyret = run_python_method(pyobj, "_preload_format", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return true; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* format = format de binaire à manipuler pendant l'opération. * -* * -* Description : Procède au rattachement d'éventuelles infos de débogage. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *plugin, PluginAction action, GExeFormat *format) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _attach_debug_format, "$self, action, format, /", \ - METH_VARARGS, \ - "Abstract method called when a debugger is attached to a binary format.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the provided format is a pychrysalide.format.ExeFormat instance.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *FORMAT_ATTACH_DEBUG*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_attach_debug_format")) - { - args = PyTuple_New(2); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); - - pyret = run_python_method(pyobj, "_attach_debug_format", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ - -static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - -#define PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _process_disassembly_event, "$self, action, format, /", \ - METH_VARARGS, \ - "Abstract method run at several different steps of a binary analysis.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the provided format is a pychrysalide.format.ExeFormat instance.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *DISASSEMBLY_STARTED*, *DISASSEMBLY_RAW*, *DISASSEMBLY_HOOKED_LINK*," \ - " *DISASSEMBLY_HOOKED_POST*, *DISASSEMBLY_LIMITED*, *DISASSEMBLY_LOOPS*," \ - " *DISASSEMBLY_LINKED*, *DISASSEMBLY_GROUPED*, *DISASSEMBLY_RANKED*," \ - " *DISASSEMBLY_ENDED*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_process_disassembly_event")) - { - args = PyTuple_New(4); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(binary))); - PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(status))); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(context))); - - pyret = run_python_method(pyobj, "_process_disassembly_event", args); - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ - -static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *plugin, PluginAction action, const GLoadedContent *content, bool version, char ***names, size_t *count) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *details; /* Valeur booléenne à joindre */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan d'exécution */ - Py_ssize_t length; /* Nombre d'éléments collectés */ - Py_ssize_t i; /* Boucle de parcours */ - PyObject *res; /* Ressource à ajouter */ - -#define PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _detect_external_tools, "$self, action, content, version, /", \ - METH_VARARGS, \ - "Abstract method called when a detection of tools used the build" \ - " the analyzed content is required.\n" \ - "\n" \ - "The expected action is a pychrysalide.PluginModule.PluginAction" \ - " value and the content is a pychrysalide.analysis.LoadedContent" \ - " instance. The *version* parameter is a boolean value indicating" \ - " if some extra details about the tools version are wished.\n" \ - "\n" \ - "The expected result is a list of strings.\n" \ - "\n" \ - "This method has to be defined in order to handle action such as" \ - " *DETECTION_OBFUSCATORS*." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(plugin)); - - if (has_python_method(pyobj, "_detect_external_tools")) - { - args = PyTuple_New(3); - - details = (version ? Py_True : Py_False); - Py_INCREF(details); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); - PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); - PyTuple_SetItem(args, 2, details); - - pyret = run_python_method(pyobj, "_detect_external_tools", args); - - if (!PySequence_Check(pyret)) - g_plugin_module_log_simple_message(plugin, LMT_ERROR, _("The returned value must be a string list")); - - else - { - length = PySequence_Length(pyret); - - for (i = 0; i < length; i++) - { - res = PySequence_GetItem(pyret, i); - - if (!PyUnicode_Check(res)) - g_plugin_module_log_variadic_message(plugin, LMT_ERROR, - _("The returned #%zd value must be a string")); - - else - { - *names = realloc(*names, ++(*count) * sizeof(char **)); - *names[*count - 1] = strdup(PyUnicode_DATA(res)); - } - - Py_DECREF(res); - - } - - } - - Py_XDECREF(pyret); - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* INTERFACE INTERNE POUR GREFFONS PYTHON */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini par la GLib pour le greffon Python. */ -G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des greffons Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_python_plugin_class_init(GPythonPluginClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - GPluginModuleClass *plugin; /* Version parente de la classe*/ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; - object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; - - plugin = G_PLUGIN_MODULE_CLASS(klass); - - plugin->get_modname = (pg_get_modname_fc)g_python_plugin_get_modname; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = instance à initialiser. * -* * -* Description : Initialise l'instance d'un greffon Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_python_plugin_init(GPythonPlugin *plugin) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_python_plugin_dispose(GPythonPlugin *plugin) -{ -#if 0 - PyThreadState *tstate; /* Contexte d'environnement */ - - /** - * Cf. https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock - * - * Cependant, comme on se trouve à priori dans le thread principal de l'interpréteur, - * PyGILState_Ensure() ne pose aucun verrou. Ce qui aboutit à la situation suivante : - * - * Fatal Python error: drop_gil: GIL is not locked - * - * On peut forcer les choses avec PyEval_AcquireLock(), mais cette fonction est marquée - * comme dépréciée depuis Python 3.2. - * - * Donc on choisit les alternatives officielles. - * - * Cependant, PyThreadState_Get() renvoit l'erreur suivante : - * - * Fatal Python error: PyThreadState_Get: no current thread - * - * Donc on se rabat sur une sauvegarde, qui n'est initialisée que lorsque l'interpréteur - * est intégré dans l'éditeur. - */ - - tstate = get_pychrysalide_main_tstate(); - - if (tstate != NULL) - PyEval_RestoreThread(tstate); - - if (tstate != NULL) - PyEval_SaveThread(); -#endif - - G_OBJECT_CLASS(g_python_plugin_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_python_plugin_finalize(GPythonPlugin *plugin) -{ - plugin_interface *final; /* Interface finale conservée */ - - final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; - - if (final != NULL) - { - if (final->name != NULL) free(final->name); - if (final->desc != NULL) free(final->desc); - if (final->version != NULL) free(final->version); - if (final->url != NULL) free(final->url); - - assert(final->required_count == 1); - - if (final->required != NULL) - free(final->required); - - if (final->actions != NULL) - free(final->actions); - - free(final); - - } - - G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à valider. * -* * -* Description : Fournit le nom brut associé au greffon. * -* * -* Retour : Désignation brute du greffon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *g_python_plugin_get_modname(const GPythonPlugin *plugin) -{ - char *result; /* Désignation brute à renvoyer*/ - char *path; /* Chemin à traiter */ - - path = strdup(g_plugin_module_get_filename(G_PLUGIN_MODULE(plugin))); - - result = strdup(basename(path)); - - free(path); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : modname = nom du module à charger. * -* filename = chemin d'accès au code Python à charger. * -* * -* Description : Crée un greffon à partir de code Python. * -* * -* Retour : Adresse de la structure mise en place ou NULL si erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPluginModule *g_python_plugin_new(const char *modname, const char *filename) -{ - GPythonPlugin *result; /* Structure à retourner */ - PyObject *name; /* Chemin d'accès pour Python */ - PyObject *module; /* Script Python chargé */ - PyObject *dict; /* Dictionnaire associé */ - PyObject *class; /* Classe à instancier */ - PyObject *instance; /* Instance Python du greffon */ - - name = PyUnicode_FromString(modname); - if (name == NULL) goto bad_exit; - - module = PyImport_Import(name); - Py_DECREF(name); - - if (module == NULL) goto no_import; - - dict = PyModule_GetDict(module); - class = PyDict_GetItemString(dict, "AutoLoad"); - - if (class == NULL) goto no_class; - if (!PyType_Check(class->ob_type)) goto no_class; - - instance = PyObject_CallFunction(class, NULL); - if (instance == NULL) goto no_instance; - - result = G_PYTHON_PLUGIN(pygobject_get(instance)); - - G_PLUGIN_MODULE(result)->filename = strdup(filename); - - /** - * L'instance Python et l'objet GLib résultante sont un même PyGObject. - * - * Donc pas besoin de toucher au comptage des références ici, la libération - * se réalisera à la fin, quand l'objet GLib sera libéré. - */ - - Py_DECREF(module); - - return G_PLUGIN_MODULE(result); - - no_instance: - - log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); - - no_class: - - if (class == NULL) - log_plugin_simple_message(LMT_ERROR, - _("An error occured when looking for the 'AutoLoad': item not found!")); - - no_import: - - Py_XDECREF(module); - - log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); - - bad_exit: - - return NULL; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* MODULE PYTHON POUR LES SCRIPTS */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Construit le nom d'un fichier de configuration du greffon. * -* * -* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - const char *final; /* Suffixe de fichier imposé */ - int create; /* Volonté de création */ - char *filename; /* Nom de fichier déterminé */ - -#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \ -( \ - build_config_filename, "final, /, create=False", \ - METH_VARARGS, py_plugin_module, \ - "Build a filename suitable for the plugin configuration, ending with" \ - " the *final* suffix.\n" \ - "\n" \ - "If the *create* parameter is set, the path to this filename is" \ - " created.\n" \ - "\n" \ - "The result is a string or None on failure." \ -) - - create = 0; - - if (!PyArg_ParseTuple(args, "s|p", &final, &create)) - return NULL; - - filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create); - - if (filename != NULL) - { - result = PyUnicode_FromString(filename); - free(filename); - } - else - { - result = Py_None; - Py_INCREF(result); - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Affiche un message dans le journal des messages système. * -* * -* Retour : Rien en équivalent Python. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - LogMessageType type; /* Espèce du message */ - const char *msg; /* Contenu du message */ - -#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ -( \ - log_message, "type, msg, /", \ - METH_VARARGS, py_plugin_module, \ - "Display a message in the log window, in graphical mode, or in the" \ - " console output if none.\n" \ - "\n" \ - "The type of the message has to be a pychrysalide.core.LogMessageType" \ - " value." \ - "\n" \ - "The only difference with the main pychrysalide.core.log_message()" \ - " function is that messages are automatically prefixed with the plugin" \ - " name here." \ -) - - if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) - return NULL; - - g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); - - result = Py_None; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit le nom brut associé au greffon. * -* * -* Retour : Désignation brute du greffon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GPluginModule *plugin; /* Version native du greffon */ - char *modname; /* Désignation brute */ - -#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - modname, py_plugin_module, \ - "Raw module name of the plugin." \ -) - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - modname = g_plugin_module_get_modname(plugin); - - result = PyUnicode_FromString(modname); - - free(modname); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Indique le fichier contenant le greffon manipulé. * -* * -* Retour : Chemin d'accès au greffon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GPluginModule *plugin; /* Version native du greffon */ - const char *filename; /* Chemin d'accès associé */ - -#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - filename, py_plugin_module, \ - "Filename of the plugin." \ -) - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - filename = g_plugin_module_get_filename(plugin); - - result = PyUnicode_FromString(filename); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit la configuration mise en place pour le greffon. * -* * -* Retour : Configuration dédiée à l'extension. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GPluginModule *plugin; /* Version native du greffon */ - GGenConfig *config; /* Configuration associée */ - -#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - config, py_plugin_module, \ - "Dedicated configuration for the plugin." \ - "\n" \ - "The value is a pychrysalide.glibext.GenConfig instance" \ - " or None if the configuration is not yet created.\n" \ - "\n" \ - "As configuration storage path depends on the plugin name," \ - " all plugin properties have to get fully loaded by the" \ - " core before the configuration can be setup." \ - "automatically" \ -) - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - config = g_plugin_module_get_config(plugin); - - if (config == NULL) - { - result = Py_None; - Py_INCREF(result); - } - - else - { - result = pygobject_new(G_OBJECT(config)); - - g_object_unref(G_OBJECT(config)); - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Fournit un accès à une définition de type à diffuser. * -* * -* Retour : Définition d'objet pour Python. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PyTypeObject *get_python_plugin_module_type(void) -{ - static PyMethodDef py_plugin_module_methods[] = { - PLUGIN_MODULE_MANAGE_WRAPPER, - PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER, - PLUGIN_MODULE_INCLUDE_THEME_WRAPPER, - PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER, - PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER, - PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER, - PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER, - PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER, - PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER, - PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER, - PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD, - PLUGIN_MODULE_LOG_MESSAGE_METHOD, - { NULL } - }; - - static PyGetSetDef py_plugin_module_getseters[] = { - PLUGIN_MODULE_MODNAME_ATTRIB, - PLUGIN_MODULE_FILENAME_ATTRIB, - PLUGIN_MODULE_CONFIG_ATTRIB, - { NULL } - }; - - static PyTypeObject py_plugin_module_type = { - - PyVarObject_HEAD_INIT(NULL, 0) - - .tp_name = "pychrysalide.PluginModule", - .tp_basicsize = sizeof(PyGObject), - - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - - .tp_doc = PLUGIN_MODULE_DOC, - - .tp_methods = py_plugin_module_methods, - .tp_getset = py_plugin_module_getseters, - - .tp_init = py_plugin_module_init, - .tp_new = py_plugin_module_new, - - }; - - return &py_plugin_module_type; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Prend en charge l'objet 'pychrysalide.PluginModule'. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool ensure_python_plugin_module_is_registered(void) -{ - PyTypeObject *type; /* Type Python 'PluginModule' */ - PyObject *module; /* Module à recompléter */ - PyObject *dict; /* Dictionnaire du module */ - - type = get_python_plugin_module_type(); - - if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) - { - module = get_access_to_python_module("pychrysalide"); - - dict = PyModule_GetDict(module); - - if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type, &PyGObject_Type)) - return false; - - if (!define_plugin_module_constants(type)) - return false; - - } - - return true; - -} diff --git a/plugins/pychrysalide/plugin.h b/plugins/pychrysalide/plugin.h deleted file mode 100644 index 183ae4a..0000000 --- a/plugins/pychrysalide/plugin.h +++ /dev/null @@ -1,75 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * plugin.h - prototypes pour les interactions avec un greffon Python - * - * Copyright (C) 2018 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 - */ - - -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGIN_H -#define _PLUGINS_PYCHRYSALIDE_PLUGIN_H - - -#include -#include -#include - - -#include - - - -/* --------------------- INTERFACE INTERNE POUR GREFFONS PYTHON --------------------- */ - - -#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type()) -#define G_PYTHON_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PYTHON_PLUGIN, GPythonPlugin)) -#define G_IS_PYTHON_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PYTHON_PLUGIN)) -#define G_PYTHON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PYTHON_PLUGIN, GPythonPluginClass)) -#define G_IS_PYTHON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PYTHON_PLUGIN)) -#define G_PYTHON_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PYTHON_PLUGIN, GPythonPluginClass)) - - -/* Ligne de représentation de code binaire (instance) */ -typedef struct _GPythonPlugin GPythonPlugin; - -/* Ligne de représentation de code binaire (classe) */ -typedef struct _GPythonPluginClass GPythonPluginClass; - - -/* Indique le type défini par la GLib pour le greffon Python. */ -GType g_python_plugin_get_type(void); - -/* Crée un greffon à partir de code Python. */ -GPluginModule *g_python_plugin_new(const char *, const char *); - - - -/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ - - -/* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_plugin_module_type(void); - -/* Prend en charge l'objet 'pychrysalide.PluginModule'. */ -bool ensure_python_plugin_module_is_registered(void); - - - -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGIN_H */ diff --git a/plugins/pychrysalide/plugins/Makefile.am b/plugins/pychrysalide/plugins/Makefile.am new file mode 100644 index 0000000..0ab8c15 --- /dev/null +++ b/plugins/pychrysalide/plugins/Makefile.am @@ -0,0 +1,24 @@ + +noinst_LTLIBRARIES = libpychrysaplugins.la + +libpychrysaplugins_la_SOURCES = \ + constants.h constants.c \ + plugin.h plugin.c \ + module.h module.c + +libpychrysaplugins_la_LIBADD = + +libpychrysaplugins_la_LDFLAGS = + + +devdir = $(includedir)/chrysalide/$(subdir) + +dev_HEADERS = $(libpychrysaplugins_la_SOURCES:%c=) + + +AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) + +SUBDIRS = diff --git a/plugins/pychrysalide/plugins/constants.c b/plugins/pychrysalide/plugins/constants.c new file mode 100644 index 0000000..9de044a --- /dev/null +++ b/plugins/pychrysalide/plugins/constants.c @@ -0,0 +1,100 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants.c - ajout des constantes principales + * + * Copyright (C) 2020 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 "constants.h" + + +#include + + +#include "../helpers.h" + + + +/****************************************************************************** +* * +* Paramètres : type = type dont le dictionnaire est à compléter. * +* * +* Description : Définit les constantes relatives aux greffons Python. * +* * +* Retour : true en cas de succès de l'opération, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool define_plugin_module_constants(PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + PyObject *values; /* Groupe de valeurs à établir */ + + result = true; + + values = PyDict_New(); + + if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE); + if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT); + if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED); + if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT); + if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED); + if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED); + if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING); + if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME); + if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION); + if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING); + if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER); + if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER); + if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED); + if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED); + if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD); + if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG); + if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED); + if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED); + if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED); + if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED); + if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW); + if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK); + if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST); + if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED); + if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS); + if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED); + if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED); + if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED); + if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED); + if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, false, "PluginAction", values, + "Features with which plugins can extend the core of Chrysalide."); + + exit: + + return result; + +} diff --git a/plugins/pychrysalide/plugins/constants.h b/plugins/pychrysalide/plugins/constants.h new file mode 100644 index 0000000..71abcff --- /dev/null +++ b/plugins/pychrysalide/plugins/constants.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants.h - prototypes pour l'ajout des constantes principales + * + * Copyright (C) 2020 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 + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H + + +#include +#include + + +/* Définit les constantes relatives aux greffons Python. */ +bool define_plugin_module_constants(PyTypeObject *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H */ diff --git a/plugins/pychrysalide/plugins/module.c b/plugins/pychrysalide/plugins/module.c new file mode 100644 index 0000000..c38caa3 --- /dev/null +++ b/plugins/pychrysalide/plugins/module.c @@ -0,0 +1,104 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire plugins en tant que module + * + * Copyright (C) 2021 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 "module.h" + + +#include + + +#include "plugin.h" +#include "../helpers.h" + + + +/****************************************************************************** +* * +* Paramètres : super = module dont la définition est à compléter. * +* * +* Description : Ajoute le module 'plugins' à un module Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_plugins_module(PyObject *super) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Sous-module mis en place */ + +#define PYCHRYSALIDE_PLUGINS_DOC \ + "This module provides features to deal with plugins: the definitions" \ + " required to build new Python plugins as well as functions to" \ + " interact with existing plugins.\n" \ + "\n" \ + "The module is also the place for all plugins without another home." + + static PyModuleDef py_chrysalide_plugins_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.plugins", + .m_doc = PYCHRYSALIDE_PLUGINS_DOC, + + .m_size = -1, + + }; + + module = build_python_module(super, &py_chrysalide_plugins_module); + + result = (module != NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'plugins'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_plugins_module(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_plugin_module_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/plugins/module.h b/plugins/pychrysalide/plugins/module.h new file mode 100644 index 0000000..e2b10b9 --- /dev/null +++ b/plugins/pychrysalide/plugins/module.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire plugins en tant que module + * + * Copyright (C) 2021 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 + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_MODULE_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_MODULE_H + + +#include +#include + + + +/* Ajoute le module 'plugins' à un module Python. */ +bool add_plugins_module(PyObject *); + +/* Intègre les objets du module 'plugins'. */ +bool populate_plugins_module(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_MODULE_H */ diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c new file mode 100644 index 0000000..e5ee1ad --- /dev/null +++ b/plugins/pychrysalide/plugins/plugin.c @@ -0,0 +1,1884 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * plugin.c - interactions avec un greffon Python + * + * Copyright (C) 2018-2019 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 +#include + + +#include "constants.h" +#include "../access.h" +#include "../core.h" +#include "../helpers.h" +#include "../core/constants.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Accompagne la création d'une instance dérivée en Python. */ +static PyObject *py_plugin_module_new(PyTypeObject *, PyObject *, PyObject *); + +/* Initialise la classe des greffons d'extension. */ +static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); + +/* Encadre une étape de la vie d'un greffon. */ +static bool py_plugin_module_manage_wrapper(GPluginModule *); + +/* Accompagne la fin du chargement des modules natifs. */ +static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction); + +/* Complète une liste de resources pour thème. */ +static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *); + +/* Rend compte de la création d'un panneau. */ +static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *); + +/* Rend compte d'un affichage ou d'un retrait de panneau. */ +static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool); + +/* Procède à une opération liée à un contenu binaire. */ +static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); + +/* Procède à une opération liée à un contenu chargé. */ +static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *); + +/* Procède à une opération liée à l'analyse d'un format. */ +static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *); + +/* Procède à un préchargement de format de fichier. */ +static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); + +/* Procède au rattachement d'éventuelles infos de débogage. */ +static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *); + +/* Exécute une action pendant un désassemblage de binaire. */ +static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); + +/* Effectue la détection d'effets d'outils externes. */ +static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *); + + + +/* --------------------- INTERFACE INTERNE POUR GREFFONS PYTHON --------------------- */ + + +/* Ligne de représentation de code binaire (instance) */ +struct _GPythonPlugin +{ + GPluginModule parent; /* Instance parente */ + +}; + + +/* Ligne de représentation de code binaire (classe) */ +struct _GPythonPluginClass +{ + GPluginModuleClass parent; /* Classe parente */ + +}; + + +/* Initialise la classe des greffons Python. */ +static void g_python_plugin_class_init(GPythonPluginClass *); + +/* Initialise l'instance d'un greffon Python. */ +static void g_python_plugin_init(GPythonPlugin *); + +/* Supprime toutes les références externes. */ +static void g_python_plugin_dispose(GPythonPlugin *); + +/* Description : Procède à la libération totale de la mémoire. */ +static void g_python_plugin_finalize(GPythonPlugin *); + +/* Fournit le nom brut associé au greffon. */ +static char *g_python_plugin_get_modname(const GPythonPlugin *); + + + +/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ + + +/* Construit le nom d'un fichier de configuration du greffon. */ +static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *); + +/* Affiche un message dans le journal des messages système. */ +static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); + +/* Fournit le nom brut associé au greffon. */ +static PyObject *py_plugin_module_get_modname(PyObject *, void *); + +/* Indique le fichier contenant le greffon manipulé. */ +static PyObject *py_plugin_module_get_filename(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : type = type du nouvel objet à mettre en place. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* * +* Description : Accompagne la création d'une instance dérivée en Python. * +* * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Objet à retourner */ + PyTypeObject *base; /* Type de base à dériver */ + bool first_time; /* Evite les multiples passages*/ + GType gtype; /* Nouveau type de processeur */ + bool status; /* Bilan d'un enregistrement */ + + /* Validations diverses */ + + base = get_python_plugin_module_type(); + + if (type == base) + { + result = NULL; + PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); + goto exit; + } + + /* Mise en place d'un type dédié */ + + first_time = (g_type_from_name(type->tp_name) == 0); + + gtype = build_dynamic_type(G_TYPE_PYTHON_PLUGIN, type->tp_name, + (GClassInitFunc)py_plugin_module_init_gclass, NULL, NULL); + + if (first_time) + { + status = register_class_for_dynamic_pygobject(gtype, type, base); + + if (!status) + { + result = NULL; + goto exit; + } + + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* unused = données non utilisées ici. * +* * +* Description : Initialise la classe des greffons d'extension. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) +{ + class->init = NULL; + class->manage = py_plugin_module_manage_wrapper; + class->exit = NULL; + + class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper; + + class->include_theme = py_plugin_module_include_theme_wrapper; + class->notify_panel = py_plugin_module_notify_panel_creation_wrapper; + class->notify_docking = py_plugin_module_notify_panel_docking_wrapper; + + class->handle_content = py_plugin_module_handle_binary_content_wrapper; + class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; + + class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper; + class->preload_format = py_plugin_module_preload_binary_format_wrapper; + class->attach_debug = py_plugin_module_attach_debug_format_wrapper; + + class->process_disass = py_plugin_module_process_disassembly_event_wrapper; + + class->detect = py_plugin_module_detect_external_tools_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan d'un appel */ + GPluginModule *plugin; /* Greffon à manipuler */ + plugin_interface *iface; /* Interface à constituer */ + GPluginModule *dependency; /* Module nécessaire */ + PyObject *value; /* Valeur à présence imposée */ + size_t i; /* Boucle de parcours */ + PyObject *action; /* Identifiant d'une action */ + +#define PLUGIN_MODULE_DOC \ + "PythonModule is the class allowing the creation of Chrysalide plugins" \ + " for Python." \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect" \ + " no particular argument.\n" \ + "\n" \ + "Several items have to be defined as class attributes in the final" \ + " class:\n" \ + "* *_name*: a string providing a small name for the plugin;\n" \ + "* *_desc*: a string for a human readable description of the plugin;\n" \ + "* *_version*: a string providing the version of the plugin;\n" \ + "* *_url*: a string for the homepage describing the plugin;\n" \ + "* *_actions*: a tuple of pychrysalide.PluginModule.PluginAction" \ + " defining the features the plugin is bringing; this list can be" \ + " empty.\n" \ + "\n" \ + "Depending on the implemented actions, some of the following methods" \ + " have to be defined for new classes:\n" \ + "* pychrysalide.PluginModule._init_config();\n" \ + "* pychrysalide.PluginModule._notify_plugins_loaded();\n" \ + "* pychrysalide.PluginModule._include_theme();\n" \ + "* pychrysalide.PluginModule._on_panel_creation;\n" \ + "* pychrysalide.PluginModule._on_panel_docking();\n" \ + "* pychrysalide.PluginModule._handle_binary_content();\n" \ + "* pychrysalide.PluginModule._handle_loaded_content();\n" \ + "* pychrysalide.PluginModule._handle_format_analysis();\n" \ + "* pychrysalide.PluginModule._preload_format();\n" \ + "* pychrysalide.PluginModule._attach_debug_format();\n" \ + "* pychrysalide.PluginModule._process_disassembly_event();\n" \ + "* pychrysalide.PluginModule._detect_external_tools()." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + iface = calloc(1, sizeof(plugin_interface)); + plugin->interface = iface; + +#define LOAD_PYTHON_IFACE(attr) \ + do \ + { \ + value = PyObject_GetAttrString(self, "_" #attr); \ + if (value == NULL) \ + { \ + PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \ + return -1; \ + } \ + if (PyUnicode_Check(value)) \ + { \ + iface->attr = strdup(PyUnicode_AsUTF8(value)); \ + Py_DECREF(value); \ + } \ + else \ + { \ + Py_DECREF(value); \ + PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \ + return -1; \ + } \ + assert(iface->attr != NULL); \ + } \ + while (0); + + LOAD_PYTHON_IFACE(name); + LOAD_PYTHON_IFACE(desc); + LOAD_PYTHON_IFACE(version); + LOAD_PYTHON_IFACE(url); + + iface->container = false; + + /** + * Comme le greffon n'est pas passé par la résolution des dépendances, + * orchestrée par la fonction g_plugin_module_resolve_dependencies(), + * on simule l'effet attendu en obtenant une référence par un appel à + * get_plugin_by_name(). + * + * L'incrémentation des références doit coller au plus près de + * l'inscription nominative du greffon : en cas de sortie impromptue + * (lorsqu'une erreur intervient pendant un chargement par exemple), + * l'état de l'ensemble est ainsi cohérent au moment du retrait du + * greffon fautif via la fonction g_plugin_module_dispose(). + */ + + lock_plugin_list_for_reading(); + dependency = get_plugin_by_name("PyChrysalide", NULL); + unlock_plugin_list_for_reading(); + + assert(dependency != NULL); + + if (dependency == NULL) + { + PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!")); + return -1; + } + + iface->required = malloc(sizeof(char *)); + iface->required[0] = "PyChrysalide"; + iface->required_count = 1; + + /* Validation du reste de l'interface */ + + value = PyObject_GetAttrString(self, "_actions"); + + if (value == NULL) + { + PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing.")); + return -1; + } + + if (!PyTuple_Check(value)) + { + Py_DECREF(value); + PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple.")); + return -1; + } + + iface->actions_count = PyTuple_Size(value); + iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); + + for (i = 0; i < iface->actions_count; i++) + { + action = PyTuple_GetItem(value, i); + + if (!PyLong_Check(action)) + { + Py_DECREF(value); + PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); + return -1; + } + + iface->actions[i] = PyLong_AsUnsignedLong(action); + + } + + Py_DECREF(value); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Encadre une étape de la vie d'un greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) +{ + bool result; /* Bilan à faire remonter */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _manage, "$self, action, /", \ + METH_VARARGS, \ + "Abstract method called to react to several steps of the plugin" \ + " life.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value.\n" \ + "\n" \ + "This method has to be defined in order to handle actions such as" \ + " *PLUGIN_LOADED*." \ +) + + result = true; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_manage")) + { + args = PyTuple_New(1); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED)); + + pyret = run_python_method(pyobj, "_manage", args); + + result = (pyret == Py_True); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _notify_plugins_loaded, "$self, action, /", \ + METH_VARARGS, \ + "Abstract method called once all the (native?) plugins are" \ + " loaded.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value.\n" \ + "\n" \ + "This method has to be defined in order to handle actions such as" \ + " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_notify_plugins_loaded")) + { + args = PyTuple_New(1); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + + pyret = run_python_method(pyobj, "_notify_plugins_loaded", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* dark = indique une préférence pour la variante foncée. * +* resources = liste de ressources à constituer. [OUT] * +* count = taille de cette liste. [OUT] * +* * +* Description : Complète une liste de resources pour thème. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_include_theme_wrapper(const GPluginModule *plugin, PluginAction action, gboolean dark, char ***resources, size_t *count) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *darkness; /* Valeur booléenne à joindre */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + Py_ssize_t length; /* Nombre d'éléments collectés */ + Py_ssize_t i; /* Boucle de parcours */ + PyObject *res; /* Ressource à ajouter */ + +#define PLUGIN_MODULE_INCLUDE_THEME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _include_theme, "$self, action, dark, /", \ + METH_VARARGS, \ + "Abstract method called once all the native plugins are loaded.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the *dark* parameter indicates if a dark theme is" \ + " being to get loaded.\n" \ + "\n" \ + "The expected result is a list of CSS definition resource URIs," \ + " provided as strings such as 'resource:///org/xxx/extra.css'" \ + " for instance.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *GUI_THEME*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_include_theme")) + { + args = PyTuple_New(2); + + darkness = (dark ? Py_True : Py_False); + Py_INCREF(darkness); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, darkness); + + pyret = run_python_method(pyobj, "_include_theme", args); + + if (!PySequence_Check(pyret)) + g_plugin_module_log_simple_message(plugin, LMT_ERROR, _("The returned value must be a string list")); + + else + { + length = PySequence_Length(pyret); + + for (i = 0; i < length; i++) + { + res = PySequence_GetItem(pyret, i); + + if (!PyUnicode_Check(res)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned #%zd value must be a string")); + + else + { + *resources = realloc(*resources, ++(*count) * sizeof(char **)); + *resources[*count - 1] = strdup(PyUnicode_DATA(res)); + } + + Py_DECREF(res); + + } + + } + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* item = nouveau panneau créé. * +* * +* Description : Rend compte de la création d'un panneau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *plugin, PluginAction action, GPanelItem *item) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _on_panel_creation, "$self, action, item, /", \ + METH_VARARGS, \ + "Abstract method called when a new instance of panel is created.\n" \ + "\n" \ + "The expected *action* is a pychrysalide.PluginModule.PluginAction" \ + " value and the *item* is a pychrysalide.gui.PanelItem instance.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *PANEL_CREATION*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_on_panel_creation")) + { + args = PyTuple_New(2); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(item))); + + pyret = run_python_method(pyobj, "_on_panel_creation", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* item = panneau marqué par un changement d'affichage. * +* dock = indique une accroche et non un décrochage. * +* * +* Description : Rend compte d'un affichage ou d'un retrait de panneau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *plugin, PluginAction action, GPanelItem *item, bool dock) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pydock; /* Valeur booléenne à joindre */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _on_panel_docking, "$self, action, item, dock, /", \ + METH_VARARGS, \ + "Abstract method called when a panel is docked or undocked into" \ + " the Chrysalide main graphical interface.\n" \ + "\n" \ + "The expected *action* is a pychrysalide.PluginModule.PluginAction" \ + " value, the *item* is a pychrysalide.gui.PanelItem instance and" \ + " the *dock* parameter indicates if the panel request a docking" \ + " operation or an undocking one.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *PANEL_DOCKING*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_on_panel_docking")) + { + args = PyTuple_New(3); + + pydock = (dock ? Py_True : Py_False); + Py_INCREF(pydock); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(item))); + PyTuple_SetItem(args, 2, pydock); + + pyret = run_python_method(pyobj, "_on_panel_docking", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ + +static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _handle_binary_content, "$self, action, content, wid, status, /", \ + METH_VARARGS, \ + "Abstract method used to explore a binary content (and possibly to add new" \ + " contents to explore) or to load a recognized binary content into a" \ + " pychrysalide.analysis.LoadedContent instance.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the initial binary content is a pychrysalide.analysis.BinContent" \ + " instance. A tracking identifier is provided and is aimed to be" \ + " used with methods from pychrysalide.analysis.ContentExplorer and" \ + " pychrysalide.analysis.ContentResolver. A reference to the main status bar" \ + " may also be provided, as a pychrysalide.gtkext.StatusStack instance if" \ + " running in graphical mode or None otherwise.\n" \ + "\n" \ + "This method has to be defined in order to handle actions such as" \ + " *CONTENT_EXPLORER* or *CONTENT_RESOLVER*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_handle_binary_content")) + { + args = PyTuple_New(4); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); + PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(wid)); + PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); + + pyret = run_python_method(pyobj, "_handle_binary_content", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* content = contenu chargé à traiter. * +* gid = 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 : - * +* * +******************************************************************************/ + +static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedContent *content, wgroup_id_t gid, GtkStatusStack *status) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _handle_loaded_content, "$self, action, content, gid, status, /", \ + METH_VARARGS, \ + "Abstract method run once a loaded binary has been analyzed with success.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the analyzed content is a pychrysalide.analysis.LoadedContent" \ + " instance. The identifier refers to the working queue used to process the" \ + " analysis. A reference to the main status bar may also be provided, as a" \ + " pychrysalide.gtkext.StatusStack instance if running in graphical mode or" \ + " None otherwise.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *CONTENT_ANALYZED*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_handle_loaded_content")) + { + args = PyTuple_New(4); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); + PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid)); + PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); + + pyret = run_python_method(pyobj, "_handle_loaded_content", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ + +static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *plugin, PluginAction action, GKnownFormat *format, wgroup_id_t gid, GtkStatusStack *status) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _handle_binary_format_analysis, "$self, action, format, gid, status, /", \ + METH_VARARGS, \ + "Abstract method run at several different steps of a binary format analysis:\n" \ + "* at the beginning and at the end of the main analysis pass;\n" \ + "* at the beginning and at the end of the extra final pass.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the provided format is a pychrysalide.format.KnownFormat" \ + " instance. The identifier refers to the working queue used to process the" \ + " analysis. A reference to the main status bar may also be provided, as a" \ + " pychrysalide.gtkext.StatusStack instance if running in graphical mode or" \ + " None otherwise.\n" \ + "\n" \ + "This method has to be defined in order to handle actions such as" \ + " *FORMAT_ANALYSIS_STARTED*, *FORMAT_ANALYSIS_ENDED*," \ + " *FORMAT_POST_ANALYSIS_STARTED* or *FORMAT_POST_ANALYSIS_ENDED*." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_handle_format_analysis")) + { + args = PyTuple_New(4); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); + PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid)); + PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); + + pyret = run_python_method(pyobj, "_handle_format_analysis", args); + + result = (pyret == Py_True); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ + +static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _preload_binary_format, "$self, action, format, info, status, /", \ + METH_VARARGS, \ + "Abstract method which is an opportunity to setup instructions or comments" \ + " ahead of the disassembling process.\n" \ + "\n" \ + "Format fields do not need to get disassembled and may be annotated for" \ + " instance.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the provided format is a pychrysalide.format.BinFormat" \ + " instance. The information holder to fill is a pychrysalide.format.PreloadInfo"\ + " instance. A reference to the main status bar may also be provided, as a" \ + " pychrysalide.gtkext.StatusStack instance if running in graphical mode or" \ + " None otherwise.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *FORMAT_PRELOAD*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_preload_format")) + { + args = PyTuple_New(4); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(info))); + PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); + + pyret = run_python_method(pyobj, "_preload_format", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* format = format de binaire à manipuler pendant l'opération. * +* * +* Description : Procède au rattachement d'éventuelles infos de débogage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *plugin, PluginAction action, GExeFormat *format) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _attach_debug_format, "$self, action, format, /", \ + METH_VARARGS, \ + "Abstract method called when a debugger is attached to a binary format.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the provided format is a pychrysalide.format.ExeFormat instance.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *FORMAT_ATTACH_DEBUG*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_attach_debug_format")) + { + args = PyTuple_New(2); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); + + pyret = run_python_method(pyobj, "_attach_debug_format", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ + +static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _process_disassembly_event, "$self, action, format, /", \ + METH_VARARGS, \ + "Abstract method run at several different steps of a binary analysis.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the provided format is a pychrysalide.format.ExeFormat instance.\n" \ + "\n" \ + "This method has to be defined in order to handle actions such as" \ + " *DISASSEMBLY_STARTED*, *DISASSEMBLY_RAW*, *DISASSEMBLY_HOOKED_LINK*," \ + " *DISASSEMBLY_HOOKED_POST*, *DISASSEMBLY_LIMITED*, *DISASSEMBLY_LOOPS*," \ + " *DISASSEMBLY_LINKED*, *DISASSEMBLY_GROUPED*, *DISASSEMBLY_RANKED*," \ + " *DISASSEMBLY_ENDED*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_process_disassembly_event")) + { + args = PyTuple_New(4); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(binary))); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(status))); + PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(context))); + + pyret = run_python_method(pyobj, "_process_disassembly_event", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ + +static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *plugin, PluginAction action, const GLoadedContent *content, bool version, char ***names, size_t *count) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *details; /* Valeur booléenne à joindre */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan d'exécution */ + Py_ssize_t length; /* Nombre d'éléments collectés */ + Py_ssize_t i; /* Boucle de parcours */ + PyObject *res; /* Ressource à ajouter */ + +#define PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _detect_external_tools, "$self, action, content, version, /", \ + METH_VARARGS, \ + "Abstract method called when a detection of tools used the build" \ + " the analyzed content is required.\n" \ + "\n" \ + "The expected action is a pychrysalide.PluginModule.PluginAction" \ + " value and the content is a pychrysalide.analysis.LoadedContent" \ + " instance. The *version* parameter is a boolean value indicating" \ + " if some extra details about the tools version are wished.\n" \ + "\n" \ + "The expected result is a list of strings.\n" \ + "\n" \ + "This method has to be defined in order to handle action such as" \ + " *DETECTION_OBFUSCATORS*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_detect_external_tools")) + { + args = PyTuple_New(3); + + details = (version ? Py_True : Py_False); + Py_INCREF(details); + + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); + PyTuple_SetItem(args, 2, details); + + pyret = run_python_method(pyobj, "_detect_external_tools", args); + + if (!PySequence_Check(pyret)) + g_plugin_module_log_simple_message(plugin, LMT_ERROR, _("The returned value must be a string list")); + + else + { + length = PySequence_Length(pyret); + + for (i = 0; i < length; i++) + { + res = PySequence_GetItem(pyret, i); + + if (!PyUnicode_Check(res)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned #%zd value must be a string")); + + else + { + *names = realloc(*names, ++(*count) * sizeof(char **)); + *names[*count - 1] = strdup(PyUnicode_DATA(res)); + } + + Py_DECREF(res); + + } + + } + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERFACE INTERNE POUR GREFFONS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini par la GLib pour le greffon Python. */ +G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des greffons Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_class_init(GPythonPluginClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(klass); + + plugin->get_modname = (pg_get_modname_fc)g_python_plugin_get_modname; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise l'instance d'un greffon Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_init(GPythonPlugin *plugin) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_dispose(GPythonPlugin *plugin) +{ +#if 0 + PyThreadState *tstate; /* Contexte d'environnement */ + + /** + * Cf. https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock + * + * Cependant, comme on se trouve à priori dans le thread principal de l'interpréteur, + * PyGILState_Ensure() ne pose aucun verrou. Ce qui aboutit à la situation suivante : + * + * Fatal Python error: drop_gil: GIL is not locked + * + * On peut forcer les choses avec PyEval_AcquireLock(), mais cette fonction est marquée + * comme dépréciée depuis Python 3.2. + * + * Donc on choisit les alternatives officielles. + * + * Cependant, PyThreadState_Get() renvoit l'erreur suivante : + * + * Fatal Python error: PyThreadState_Get: no current thread + * + * Donc on se rabat sur une sauvegarde, qui n'est initialisée que lorsque l'interpréteur + * est intégré dans l'éditeur. + */ + + tstate = get_pychrysalide_main_tstate(); + + if (tstate != NULL) + PyEval_RestoreThread(tstate); + + if (tstate != NULL) + PyEval_SaveThread(); +#endif + + G_OBJECT_CLASS(g_python_plugin_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_python_plugin_finalize(GPythonPlugin *plugin) +{ + plugin_interface *final; /* Interface finale conservée */ + + final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + + if (final != NULL) + { + if (final->name != NULL) free(final->name); + if (final->desc != NULL) free(final->desc); + if (final->version != NULL) free(final->version); + if (final->url != NULL) free(final->url); + + assert(final->required_count == 1); + + if (final->required != NULL) + free(final->required); + + if (final->actions != NULL) + free(final->actions); + + free(final); + + } + + G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à valider. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_modname(const GPythonPlugin *plugin) +{ + char *result; /* Désignation brute à renvoyer*/ + char *path; /* Chemin à traiter */ + + path = strdup(g_plugin_module_get_filename(G_PLUGIN_MODULE(plugin))); + + result = strdup(basename(path)); + + free(path); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : modname = nom du module à charger. * +* filename = chemin d'accès au code Python à charger. * +* * +* Description : Crée un greffon à partir de code Python. * +* * +* Retour : Adresse de la structure mise en place ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GPluginModule *g_python_plugin_new(const char *modname, const char *filename) +{ + GPythonPlugin *result; /* Structure à retourner */ + PyObject *name; /* Chemin d'accès pour Python */ + PyObject *module; /* Script Python chargé */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ + + name = PyUnicode_FromString(modname); + if (name == NULL) goto bad_exit; + + module = PyImport_Import(name); + Py_DECREF(name); + + if (module == NULL) goto no_import; + + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); + + if (class == NULL) goto no_class; + if (!PyType_Check(class->ob_type)) goto no_class; + + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto no_instance; + + result = G_PYTHON_PLUGIN(pygobject_get(instance)); + + G_PLUGIN_MODULE(result)->filename = strdup(filename); + + /** + * L'instance Python et l'objet GLib résultante sont un même PyGObject. + * + * Donc pas besoin de toucher au comptage des références ici, la libération + * se réalisera à la fin, quand l'objet GLib sera libéré. + */ + + Py_DECREF(module); + + return G_PLUGIN_MODULE(result); + + no_instance: + + log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); + + no_class: + + if (class == NULL) + log_plugin_simple_message(LMT_ERROR, + _("An error occured when looking for the 'AutoLoad': item not found!")); + + no_import: + + Py_XDECREF(module); + + log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); + + bad_exit: + + return NULL; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MODULE PYTHON POUR LES SCRIPTS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Construit le nom d'un fichier de configuration du greffon. * +* * +* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + const char *final; /* Suffixe de fichier imposé */ + int create; /* Volonté de création */ + char *filename; /* Nom de fichier déterminé */ + +#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \ +( \ + build_config_filename, "final, /, create=False", \ + METH_VARARGS, py_plugin_module, \ + "Build a filename suitable for the plugin configuration, ending with" \ + " the *final* suffix.\n" \ + "\n" \ + "If the *create* parameter is set, the path to this filename is" \ + " created.\n" \ + "\n" \ + "The result is a string or None on failure." \ +) + + create = 0; + + if (!PyArg_ParseTuple(args, "s|p", &final, &create)) + return NULL; + + filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create); + + if (filename != NULL) + { + result = PyUnicode_FromString(filename); + free(filename); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Affiche un message dans le journal des messages système. * +* * +* Retour : Rien en équivalent Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + LogMessageType type; /* Espèce du message */ + const char *msg; /* Contenu du message */ + +#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ +( \ + log_message, "type, msg, /", \ + METH_VARARGS, py_plugin_module, \ + "Display a message in the log window, in graphical mode, or in the" \ + " console output if none.\n" \ + "\n" \ + "The type of the message has to be a pychrysalide.core.LogMessageType" \ + " value." \ + "\n" \ + "The only difference with the main pychrysalide.core.log_message()" \ + " function is that messages are automatically prefixed with the plugin" \ + " name here." \ +) + + if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) + return NULL; + + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *modname; /* Désignation brute */ + +#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + modname, py_plugin_module, \ + "Raw module name of the plugin." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + modname = g_plugin_module_get_modname(plugin); + + result = PyUnicode_FromString(modname); + + free(modname); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + const char *filename; /* Chemin d'accès associé */ + +#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + filename, py_plugin_module, \ + "Filename of the plugin." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + filename = g_plugin_module_get_filename(plugin); + + result = PyUnicode_FromString(filename); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la configuration mise en place pour le greffon. * +* * +* Retour : Configuration dédiée à l'extension. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + GGenConfig *config; /* Configuration associée */ + +#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + config, py_plugin_module, \ + "Dedicated configuration for the plugin." \ + "\n" \ + "The value is a pychrysalide.glibext.GenConfig instance" \ + " or None if the configuration is not yet created.\n" \ + "\n" \ + "As configuration storage path depends on the plugin name," \ + " all plugin properties have to get fully loaded by the" \ + " core before the configuration can be setup." \ + "automatically" \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + config = g_plugin_module_get_config(plugin); + + if (config == NULL) + { + result = Py_None; + Py_INCREF(result); + } + + else + { + result = pygobject_new(G_OBJECT(config)); + + g_object_unref(G_OBJECT(config)); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_plugin_module_type(void) +{ + static PyMethodDef py_plugin_module_methods[] = { + PLUGIN_MODULE_MANAGE_WRAPPER, + PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER, + PLUGIN_MODULE_INCLUDE_THEME_WRAPPER, + PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER, + PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER, + PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER, + PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER, + PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER, + PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER, + PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER, + PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER, + PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER, + PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD, + PLUGIN_MODULE_LOG_MESSAGE_METHOD, + { NULL } + }; + + static PyGetSetDef py_plugin_module_getseters[] = { + PLUGIN_MODULE_MODNAME_ATTRIB, + PLUGIN_MODULE_FILENAME_ATTRIB, + PLUGIN_MODULE_CONFIG_ATTRIB, + { NULL } + }; + + static PyTypeObject py_plugin_module_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.PluginModule", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = PLUGIN_MODULE_DOC, + + .tp_methods = py_plugin_module_methods, + .tp_getset = py_plugin_module_getseters, + + .tp_init = py_plugin_module_init, + .tp_new = py_plugin_module_new, + + }; + + return &py_plugin_module_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.PluginModule'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_plugin_module_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'PluginModule' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_plugin_module_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type, &PyGObject_Type)) + return false; + + if (!define_plugin_module_constants(type)) + return false; + + } + + return true; + +} diff --git a/plugins/pychrysalide/plugins/plugin.h b/plugins/pychrysalide/plugins/plugin.h new file mode 100644 index 0000000..2a31cf2 --- /dev/null +++ b/plugins/pychrysalide/plugins/plugin.h @@ -0,0 +1,75 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * plugin.h - prototypes pour les interactions avec un greffon Python + * + * Copyright (C) 2018 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 + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H + + +#include +#include +#include + + +#include + + + +/* --------------------- INTERFACE INTERNE POUR GREFFONS PYTHON --------------------- */ + + +#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type()) +#define G_PYTHON_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PYTHON_PLUGIN, GPythonPlugin)) +#define G_IS_PYTHON_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PYTHON_PLUGIN)) +#define G_PYTHON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PYTHON_PLUGIN, GPythonPluginClass)) +#define G_IS_PYTHON_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PYTHON_PLUGIN)) +#define G_PYTHON_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PYTHON_PLUGIN, GPythonPluginClass)) + + +/* Ligne de représentation de code binaire (instance) */ +typedef struct _GPythonPlugin GPythonPlugin; + +/* Ligne de représentation de code binaire (classe) */ +typedef struct _GPythonPluginClass GPythonPluginClass; + + +/* Indique le type défini par la GLib pour le greffon Python. */ +GType g_python_plugin_get_type(void); + +/* Crée un greffon à partir de code Python. */ +GPluginModule *g_python_plugin_new(const char *, const char *); + + + +/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_plugin_module_type(void); + +/* Prend en charge l'objet 'pychrysalide.PluginModule'. */ +bool ensure_python_plugin_module_is_registered(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H */ diff --git a/plugins/python/abackup/plugin.py b/plugins/python/abackup/plugin.py index aea9a9c..a3222c9 100644 --- a/plugins/python/abackup/plugin.py +++ b/plugins/python/abackup/plugin.py @@ -23,10 +23,10 @@ import io import tarfile -from pychrysalide import PluginModule from pychrysalide import core from pychrysalide.analysis.contents import EncapsulatedContent from pychrysalide.analysis.contents import MemoryContent +from pychrysalide.plugins import PluginModule from .backup import AndroidBackup from .password import PasswordReader diff --git a/plugins/python/apkfiles/apkfiles.py b/plugins/python/apkfiles/apkfiles.py index 98d31c7..d0fe5b4 100644 --- a/plugins/python/apkfiles/apkfiles.py +++ b/plugins/python/apkfiles/apkfiles.py @@ -1,10 +1,10 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -from pychrysalide import PluginModule from pychrysalide import core from pychrysalide.analysis.contents import EncapsulatedContent from pychrysalide.analysis.contents import MemoryContent +from pychrysalide.plugins import PluginModule import io import zipfile diff --git a/plugins/python/cglimpse/core.py b/plugins/python/cglimpse/core.py index 29fb535..145d41d 100644 --- a/plugins/python/cglimpse/core.py +++ b/plugins/python/cglimpse/core.py @@ -1,7 +1,7 @@ -from pychrysalide import PluginModule from pychrysalide.glibext import ConfigParam from pychrysalide.gui import core +from pychrysalide.plugins import PluginModule from .panel import CGlimpsePanel diff --git a/plugins/python/checksec/plugin.py b/plugins/python/checksec/plugin.py index 6ab213f..7471681 100644 --- a/plugins/python/checksec/plugin.py +++ b/plugins/python/checksec/plugin.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- from .mitigations import ElfMitigations -from pychrysalide import PluginModule from pychrysalide.core import log_message, LogMessageType from pychrysalide.format.elf import ElfFormat +from pychrysalide.plugins import PluginModule class CheckSec(PluginModule): diff --git a/plugins/python/liveconv/plugin.py b/plugins/python/liveconv/plugin.py index 8caeb17..cacdf09 100644 --- a/plugins/python/liveconv/plugin.py +++ b/plugins/python/liveconv/plugin.py @@ -1,6 +1,6 @@ -from pychrysalide import PluginModule from pychrysalide.gui import core +from pychrysalide.plugins import PluginModule from .panel import ConvPanel diff --git a/plugins/python/scripting/core.py b/plugins/python/scripting/core.py index 1b91688..ff912ed 100644 --- a/plugins/python/scripting/core.py +++ b/plugins/python/scripting/core.py @@ -2,11 +2,11 @@ from gi.repository import GLib, Gtk import os -from pychrysalide import PluginModule from pychrysalide import core from pychrysalide.gui import core as gcore from pychrysalide.gui import MenuBar from pychrysalide.gtkext import EasyGtk +from pychrysalide.plugins import PluginModule from .panel import ScriptPanel -- cgit v0.11.2-87-g4458