diff options
Diffstat (limited to 'plugins/pychrysalide/plugin.c')
-rw-r--r-- | plugins/pychrysalide/plugin.c | 929 |
1 files changed, 543 insertions, 386 deletions
diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c index a693d7b..22bfabb 100644 --- a/plugins/pychrysalide/plugin.c +++ b/plugins/pychrysalide/plugin.c @@ -32,6 +32,7 @@ #include <common/extstr.h> +#include <plugins/dt.h> #include <plugins/plugin-int.h> @@ -41,6 +42,50 @@ +/* ------------------------ 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); + +/* Valide les fonctionnalités déclarées en actions. */ +static bool py_plugin_module_check_interface(PyObject *); + +/* Accompagne la fin du chargement des modules natifs. */ +static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *, PluginAction); + +/* Complète une liste de resources pour thème. */ +static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, char ***, size_t *); + +/* 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_binary_format_analysis_wrapper(const GPluginModule *, PluginAction, GBinFormat *, 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 --------------------- */ @@ -75,30 +120,6 @@ 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 *); -/* Reconstruit la déclaration d'interface à partir de lectures. */ -static bool g_python_plugin_read_interface(GPythonPlugin *); - -/* Procède à l'initialisation du greffon. */ -static bool g_python_plugin_do_init(GPythonPlugin *); - -/* Procède à l'extinction du greffon. */ -static bool g_python_plugin_do_exit(GPythonPlugin *); - -/* Procède à une opération liée à un contenu binaire. */ -static void g_python_plugin_handle_binary_content(const GPythonPlugin *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); - -/* Procède à une opération liée à l'analyse d'un format. */ -static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *, PluginAction, GBinFormat *, wgroup_id_t, GtkStatusStack *); - -/* Procède à un préchargement de format de fichier. */ -static bool g_python_plugin_preload_binary_format(const GPythonPlugin *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); - -/* Procède au rattachement d'éventuelles infos de débogage. */ -static void g_python_plugin_attach_debug_format(const GPythonPlugin *, PluginAction, GExeFormat *); - -/* Exécute une action pendant un désassemblage de binaire. */ -static void g_python_plugin_process_disass(const GPythonPlugin *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); - /* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ @@ -113,61 +134,78 @@ static bool py_plugin_module_define_constants(PyTypeObject *); /* ---------------------------------------------------------------------------------- */ -/* INTERFACE INTERNE POUR GREFFONS PYTHON */ +/* GLUE POUR CREATION DEPUIS 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. * +* Paramètres : type = type du nouvel objet à mettre en place. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition. * * * -* Description : Initialise la classe des greffons Python. * +* Description : Accompagne la création d'une instance dérivée en Python. * * * -* Retour : - * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -static void g_python_plugin_class_init(GPythonPluginClass *klass) +static PyObject *py_plugin_module_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - GObjectClass *object; /* Autre version de la classe */ + 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 */ - object = G_OBJECT_CLASS(klass); + /* Validations diverses */ - object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; - object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + 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é */ -/****************************************************************************** -* * -* Paramètres : plugin = instance à initialiser. * -* * -* Description : Initialise l'instance d'un greffon Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + first_time = (g_type_from_name(type->tp_name) == 0); -static void g_python_plugin_init(GPythonPlugin *plugin) -{ + gtype = built_dynamic_type(G_TYPE_PYTHON_PLUGIN, type->tp_name, + (GClassInitFunc)py_plugin_module_init_gclass, NULL); + + if (first_time) + status = register_class_for_dynamic_pygobject(gtype, type, base); + else + status = true; + + 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 : plugin = instance d'objet GLib à traiter. * +* Paramètres : class = classe à initialiser. * +* unused = données non utilisées ici. * * * -* Description : Supprime toutes les références externes. * +* Description : Initialise la classe des greffons d'extension. * * * * Retour : - * * * @@ -175,191 +213,147 @@ static void g_python_plugin_init(GPythonPlugin *plugin) * * ******************************************************************************/ -static void g_python_plugin_dispose(GPythonPlugin *plugin) +static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) { - PyThreadState *tstate; /* Contexte d'environnement */ + class->init = NULL; + class->exit = NULL; - /** - * 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. - */ + class->native_loaded = py_plugin_module_notify_native_loaded_wrapper; - tstate = get_pychrysalide_main_tstate(); + class->include_theme = py_plugin_module_include_theme_wrapper; - if (tstate != NULL) - PyEval_RestoreThread(tstate); + class->handle_content = py_plugin_module_handle_binary_content_wrapper; + class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; - Py_XDECREF(plugin->instance); - plugin->instance = NULL; + class->handle_fmt_analysis = py_plugin_module_handle_binary_format_analysis_wrapper; + class->preload_format = py_plugin_module_preload_binary_format_wrapper; + class->attach_debug = py_plugin_module_attach_debug_format_wrapper; - if (tstate != NULL) - PyEval_SaveThread(); + class->process_disass = py_plugin_module_process_disassembly_event_wrapper; - G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin)); + class->detect = py_plugin_module_detect_external_tools_wrapper; } /****************************************************************************** * * -* Paramètres : plugin = instance d'objet GLib à traiter. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Procède à la libération totale de la mémoire. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : - * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static void g_python_plugin_finalize(GPythonPlugin *plugin) +static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) { - plugin_interface *final; /* Interface finale conservée */ + const char *name; /* Désignation humaine courte */ + const char *desc; /* Description plus loquace */ + const char *version; /* Version du greffon */ + PyObject *actions_obj; /* Liste des actions offertes */ + int ret; /* Bilan de lecture des args. */ + PyObject *new_kwds; /* Nouveau dictionnaire épuré */ + GPluginModule *plugin; /* Greffon à manipuler */ + plugin_interface *iface; /* Interface à constituer */ + size_t i; /* Boucle de parcours */ + PyObject *action; /* Identifiant d'une action */ - Py_DECREF(plugin->module); + static char *kwlist[] = { "name", "desc", "version", "actions", NULL }; - final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + /* Récupération des paramètres */ - if (final != NULL) - { - assert(final->required_count == 1); + ret = PyArg_ParseTupleAndKeywords(args, kwds, "sssO!", kwlist, + &name, &desc, &version, &PyTuple_Type, &actions_obj); + if (!ret) return -1; - free(final->required); - free(final); + /* Initialisation d'un objet GLib */ - } + new_kwds = PyDict_New(); - G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + ret = PyGObject_Type.tp_init(self, args, new_kwds); -} + Py_DECREF(new_kwds); + if (ret == -1) return -1; -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ + /* Eléments de base */ -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 *err_type; /* Type d'erreur Python */ - PyObject *err_value; /* Instance Python d'erreur */ - PyObject *err_traceback; /* Trace Python associée */ - PyObject *err_string; /* Description Python d'erreur */ - const char *err_msg; /* Représentation humaine */ - PyObject *dict; /* Dictionnaire associé */ - PyObject *class; /* Classe à instancier */ - PyObject *instance; /* Instance Python du greffon */ - size_t i; /* Boucle de parcours */ - uint32_t action; /* Identifiant d'une action */ - uint32_t category; /* Catégorie principale */ - uint32_t sub; /* Sous-catégorie visée */ + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - name = PyUnicode_FromString(modname); - if (name == NULL) goto gppn_bad_exit; + iface = malloc(sizeof(plugin_interface)); + plugin->interface = iface; - module = PyImport_Import(name); - Py_DECREF(name); + iface->name = strdup(name); + iface->desc = strdup(desc); + iface->version = strdup(version); - if (PyErr_Occurred()) - { - PyErr_Fetch(&err_type, &err_value, &err_traceback); + iface->container = false; - if (err_value == NULL) - log_variadic_message(LMT_ERROR, - _("An unknown error occured when importing '%s'..."), modname); - else - { - err_string = PyObject_Str(err_value); - err_msg = PyUnicode_AsUTF8(err_string); + iface->required = malloc(sizeof(char *)); + iface->required[0] = "PyChrysalide"; + iface->required_count = 1; - log_variadic_message(LMT_ERROR, - _("An error occured when importing '%s': \"%s\""), modname, err_msg); + iface->actions_count = PyTuple_Size(actions_obj); + iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); - Py_DECREF(err_string); - Py_DECREF(err_value); + for (i = 0; i < iface->actions_count; i++) + { + action = PyTuple_GetItem(actions_obj, i); + if (!PyLong_Check(action)) + { + PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); + return -1; } - Py_XDECREF(err_traceback); - Py_XDECREF(err_type); - - Py_XDECREF(module); + iface->actions[i] = PyLong_AsUnsignedLong(action); - module = NULL; + } + if (!py_plugin_module_check_interface(self)) + { + PyErr_SetString(PyExc_TypeError, _("missing features for the declared actions.")); + return -1; } - if (module == NULL) goto gppn_bad_exit; + return 0; - dict = PyModule_GetDict(module); - class = PyDict_GetItemString(dict, "AutoLoad"); +} - if (class == NULL) goto gppn_no_class; - if (!PyType_Check(class->ob_type)) goto gppn_no_class; - instance = PyObject_CallFunction(class, NULL); - if (instance == NULL) goto gppn_no_instance; +/****************************************************************************** +* * +* Paramètres : self = greffon Python en cours d'initialisation. * +* * +* Description : Valide les fonctionnalités déclarées en actions. * +* * +* Retour : true si le greffon Python est à priori utilisable. * +* * +* Remarques : - * +* * +******************************************************************************/ - result = g_object_new(G_TYPE_PYTHON_PLUGIN, NULL); +static bool py_plugin_module_check_interface(PyObject *self) +{ + bool result; /* Bilan à retourner */ + GPluginModule *plugin; /* Greffon à manipuler */ + size_t i; /* Boucle de parcours */ + uint32_t action; /* Identifiant d'une action */ + uint32_t category; /* Catégorie principale */ + uint32_t sub; /* Sous-catégorie visée */ - G_PLUGIN_MODULE(result)->filename = strdup(filename); + result = true; - result->module = module; - result->instance = instance; + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - if (!g_python_plugin_read_interface(result)) - goto gppn_interface_error; - - /* Localisation des différents points d'entrée déclarés */ - -#define register_python_binding(inst, pysym, sym, binding) \ - ({ \ - bool __result; \ - if (!has_python_method(inst, #pysym)) \ - { \ - log_variadic_message(LMT_ERROR, \ - _("No '%s' entry in plugin candidate '%s'"), \ - #pysym, G_PLUGIN_MODULE(result)->filename); \ - __result = false; \ - } \ - else \ - { \ - G_PLUGIN_MODULE(result)->sym = binding; \ - __result = true; \ - } \ - __result; \ - }) - - for (i = 0; i < G_PLUGIN_MODULE(result)->interface->actions_count; i++) + for (i = 0; i < plugin->interface->actions_count && result; i++) { - action = G_PLUGIN_MODULE(result)->interface->actions[i]; + action = plugin->interface->actions[i]; category = MASK_PLUGIN_CATEGORY(action); sub = MASK_PLUGIN_SUB_CATEGORY(action); @@ -373,20 +367,17 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) break; case PGA_PLUGIN_INIT: - if (!register_python_binding(instance, init, init, - (pg_management_fc)g_python_plugin_do_init)) - goto gppn_bad_plugin; + result = has_python_method(self, "init"); break; case PGA_PLUGIN_EXIT: - if (!register_python_binding(instance, exit, exit, - (pg_management_fc)g_python_plugin_do_exit)) - goto gppn_bad_plugin; + result = has_python_method(self, "exit"); break; default: log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + _("Unknown sub-category '0x%02x' in plugin '%s'..."), + sub, self->ob_type->tp_name); break; } @@ -402,16 +393,17 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) switch (action) { case PGA_CONTENT_EXPLORER: + result = has_python_method(self, "handle_binary_content"); + break; + case PGA_CONTENT_RESOLVER: - if (!register_python_binding(instance, handle_content, handle_content, - (pg_handle_content_fc)g_python_plugin_handle_binary_content)) - goto gppn_bad_plugin; + result = has_python_method(self, "handle_loaded_content"); break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), - action, filename); + action, self->ob_type->tp_name); break; } @@ -426,27 +418,21 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) case PGA_FORMAT_ANALYSIS_ENDED: case PGA_FORMAT_POST_ANALYSIS_STARTED: case PGA_FORMAT_POST_ANALYSIS_ENDED: - if (!register_python_binding(instance, handle_format_analysis, handle_fmt_analysis, - (pg_handle_format_analysis_fc)g_python_plugin_handle_binary_format_analysis)) - goto gppn_bad_plugin; + result = has_python_method(self, "handle_format_analysis"); break; case PGA_FORMAT_PRELOAD: - if (!register_python_binding(instance, preload_format, preload_format, - (pg_preload_format_fc)g_python_plugin_preload_binary_format)) - goto gppn_bad_plugin; + result = has_python_method(self, "preload_format"); break; case PGA_FORMAT_ATTACH_DEBUG: - if (!register_python_binding(instance, attach_debug_format, attach_debug, - (pg_attach_debug)g_python_plugin_attach_debug_format)) - goto gppn_bad_plugin; + result = has_python_method(self, "attach_debug_format"); break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), - action, filename); + action, self->ob_type->tp_name); break; } @@ -454,14 +440,13 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) break; case DPS_DISASSEMBLY: - if (!register_python_binding(instance, process_disassembly, process_disass, - (pg_process_disassembly_fc)g_python_plugin_process_disass)) - goto gppn_bad_plugin; + result = has_python_method(self, "process_disassembly"); break; default: log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + _("Unknown sub-category '0x%02x' in plugin '%s'..."), + sub, self->ob_type->tp_name); break; } @@ -470,217 +455,102 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) default: log_variadic_message(LMT_WARNING, - _("Unknown category '0x%02x' in plugin '%s'..."), category, filename); + _("Unknown category '0x%02x' in plugin '%s'..."), + category, self->ob_type->tp_name); break; } } - /* Conclusion */ - - return G_PLUGIN_MODULE(result); - - gppn_bad_plugin: - - gppn_interface_error: - - g_object_unref(G_OBJECT(result)); - - return NULL; - - gppn_no_instance: - - gppn_no_class: - - Py_DECREF(module); - - gppn_bad_exit: - - return NULL; + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à initialiser. * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* unused = variable non utilisé pour l'usage de __VA_ARGS__. * * * -* Description : Reconstruit la déclaration d'interface à partir de lectures. * +* Description : Accompagne la fin du chargement des modules natifs. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_python_plugin_read_interface(GPythonPlugin *plugin) +static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *plugin, PluginAction action) { - bool result; /* Bilan à renvoyer */ - plugin_interface interface; /* Recueil des éléments */ - PyObject *desc; /* Tableau de description */ - PyObject *str; /* Chaîne de caractères */ - PyObject *tuple; /* Liste d'éléments à traiter */ - Py_ssize_t count; /* Nombre d'éléments présents */ - Py_ssize_t i; /* Boucle de parcours */ - PyObject *action; /* Identifiant d'une action */ - plugin_interface *final; /* Interface finale conservée */ - - result = true; - - desc = run_python_method(plugin->instance, "get_interface", NULL); - if (!PyDict_Check(desc)) - { - result = false; - goto pgpri_end; - } - - memset(&interface, 0, sizeof(interface)); - - /* Chargements des premières chaînes */ - -#define READ_STR_FIELD(name) \ - str = PyDict_GetItemString(desc, #name); \ - if ((result = PyUnicode_Check(str))) \ - interface.name = strdup(PyUnicode_DATA(str)); \ - - READ_STR_FIELD(name); - READ_STR_FIELD(desc); - READ_STR_FIELD(version); - - /* Chargement des actions supportées */ - - tuple = PyDict_GetItemString(desc, "actions"); - - if (!PyList_Check(tuple)) - { - result = false; - goto pgpri_failed; - } - - count = PyList_GET_SIZE(tuple); - - interface.actions = (plugin_action_t *)calloc(count, sizeof(plugin_action_t)); - interface.actions_count = count; - - for (i = 0; i < count; i++) - { - action = PyList_GET_ITEM(tuple, i); - - interface.actions[i] = PyLong_AsUnsignedLong(action); - - } - - pgpri_failed: - - if (result) - { - final = (plugin_interface *)calloc(1, sizeof(plugin_interface)); - - memcpy(final, &interface, sizeof(interface)); - - final->required = (const char **)malloc(sizeof(char *)); - final->required[0] = "PyChrysalide"; - final->required_count = 1; - - G_PLUGIN_MODULE(plugin)->interface = final; - - } - else - { - if (interface.name != NULL) free((char *)interface.name); - if (interface.desc != NULL) free((char *)interface.desc); - if (interface.version != NULL) free((char *)interface.version); - - if (interface.actions != NULL) free(interface.actions); - - } - - pgpri_end: - - Py_XDECREF(desc); - - return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à initialiser. * -* ref = espace de référencement global. * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* resources = liste de ressources à constituer. [OUT] * +* count = taille de cette liste. [OUT] * * * -* Description : Procède à l'initialisation du greffon. * +* Description : Complète une liste de resources pour thème. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_python_plugin_do_init(GPythonPlugin *plugin) +static void py_plugin_module_include_theme_wrapper(const GPluginModule *plugin, PluginAction action, char ***resources, size_t *count) { - bool result; /* Bilan à retourner */ - PyThreadState *tstate; /* Contexte d'environnement */ - PyObject *value; /* Valeur obtenue */ - - tstate = PyThreadState_Get(); - - if (tstate != NULL) - PyEval_RestoreThread(tstate); - - if (!has_python_method(plugin->instance, "init")) - result = true; - - else - { - value = run_python_method(plugin->instance, "init", NULL); - - result = (value != NULL && PyObject_IsTrue(value)); - - Py_XDECREF(value); - - } - - if (tstate != NULL) - PyEval_SaveThread(); - - return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à initialiser. * +* 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 à l'extinction du greffon. * +* Description : Procède à une opération liée à un contenu binaire. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_python_plugin_do_exit(GPythonPlugin *plugin) +static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) { - bool result; /* Bilan à retourner */ - PyObject *value; /* Valeur obtenue */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *value; /* Valeurs obtenues */ - if (!has_python_method(plugin->instance, "exit")) - result = true; + gstate = PyGILState_Ensure(); - else - { - value = run_python_method(plugin->instance, "exit", NULL); + pyobj = pygobject_new(G_OBJECT(plugin)); - result = (value != NULL && PyObject_IsTrue(value)); + assert(has_python_method(pyobj, "handle_binary_content")); - Py_XDECREF(value); + 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))); - return result; + value = run_python_method(pyobj, "handle_binary_content", args); + + Py_XDECREF(value); + Py_DECREF(args); + + PyGILState_Release(gstate); } @@ -689,11 +559,11 @@ static bool g_python_plugin_do_exit(GPythonPlugin *plugin) * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * -* content = contenu binaire à traiter. * +* content = contenu chargé à traiter. * * wid = identifiant du groupe de traitement. * * status = barre de statut à tenir informée. * * * -* Description : Procède à une opération liée à un contenu binaire. * +* Description : Procède à une opération liée à un contenu chargé. * * * * Retour : - * * * @@ -701,14 +571,19 @@ static bool g_python_plugin_do_exit(GPythonPlugin *plugin) * * ******************************************************************************/ -static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) +static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedContent *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 *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "handle_loaded_content")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -716,7 +591,7 @@ static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, P PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(wid)); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - value = run_python_method(plugin->instance, "handle_content", args); + value = run_python_method(pyobj, "handle_loaded_content", args); Py_XDECREF(value); Py_DECREF(args); @@ -742,14 +617,19 @@ static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, P * * ******************************************************************************/ -static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *plugin, PluginAction action, GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status) +static bool py_plugin_module_handle_binary_format_analysis_wrapper(const GPluginModule *plugin, PluginAction action, GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "handle_format_analysis")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -757,7 +637,7 @@ static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *p PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid)); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - value = run_python_method(plugin->instance, "handle_format_analysis", args); + value = run_python_method(pyobj, "handle_format_analysis", args); Py_XDECREF(value); Py_DECREF(args); @@ -785,14 +665,19 @@ static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *p * * ******************************************************************************/ -static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status) +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 *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "preload_format")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -800,7 +685,7 @@ static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, P PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(info))); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - value = run_python_method(plugin->instance, "preload_format", args); + value = run_python_method(pyobj, "preload_format", args); Py_XDECREF(value); Py_DECREF(args); @@ -826,20 +711,25 @@ static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, P * * ******************************************************************************/ -static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, PluginAction action, GExeFormat *format) +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 *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(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))); - value = run_python_method(plugin->instance, "attach_debug_format", args); + value = run_python_method(pyobj, "attach_debug_format", args); Py_XDECREF(value); Py_DECREF(args); @@ -851,9 +741,9 @@ static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, Plu /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* binary = binaire dont le contenu est en cours de traitement.* +* 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. * * * @@ -865,14 +755,19 @@ static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, Plu * * ******************************************************************************/ -static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) +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 *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "process_disassembly")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -880,7 +775,7 @@ static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAc PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(status))); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(context))); - value = run_python_method(plugin->instance, "process_disassembly", args); + value = run_python_method(pyobj, "process_disassembly", args); Py_XDECREF(value); Py_DECREF(args); @@ -890,6 +785,265 @@ static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAc } +/****************************************************************************** +* * +* 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) +{ + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* 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 */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + +} + + +/****************************************************************************** +* * +* 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) +{ + 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); + + Py_XDECREF(plugin->instance); + plugin->instance = NULL; + + if (tstate != NULL) + PyEval_SaveThread(); + + 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 */ + + Py_XDECREF(plugin->module); + + final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + + if (final != NULL) + { + assert(final->required_count == 1); + free(final->required); + + free(final); + + } + + G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* 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 *err_type; /* Type d'erreur Python */ + PyObject *err_value; /* Instance Python d'erreur */ + PyObject *err_traceback; /* Trace Python associée */ + PyObject *err_string; /* Description Python d'erreur */ + const char *err_msg; /* Représentation humaine */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ + + name = PyUnicode_FromString(modname); + if (name == NULL) goto gppn_bad_exit; + + module = PyImport_Import(name); + Py_DECREF(name); + + if (PyErr_Occurred()) + { + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + if (err_value == NULL) + log_variadic_message(LMT_ERROR, + _("An unknown error occured when importing '%s'..."), modname); + else + { + err_string = PyObject_Str(err_value); + err_msg = PyUnicode_AsUTF8(err_string); + + log_variadic_message(LMT_ERROR, + _("An error occured when importing '%s': \"%s\""), modname, err_msg); + + Py_DECREF(err_string); + Py_DECREF(err_value); + + } + + Py_XDECREF(err_traceback); + Py_XDECREF(err_type); + + Py_XDECREF(module); + + module = NULL; + + } + + if (module == NULL) goto gppn_bad_exit; + + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); + + if (class == NULL) goto gppn_no_class; + if (!PyType_Check(class->ob_type)) goto gppn_no_class; + + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto gppn_no_instance; + + result = G_PYTHON_PLUGIN(pygobject_get(instance)); + + G_PLUGIN_MODULE(result)->filename = strdup(filename); + + result->module = module; + result->instance = instance; + + Py_INCREF(instance); + + return G_PLUGIN_MODULE(result); + + gppn_no_instance: + + gppn_no_class: + + Py_DECREF(module); + + gppn_bad_exit: + + return NULL; + +} + + /* ---------------------------------------------------------------------------------- */ /* MODULE PYTHON POUR LES SCRIPTS */ @@ -926,7 +1080,7 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) case LMT_BAD_BINARY: case LMT_ERROR: case LMT_EXT_ERROR: - /*g_plugin_module_*/log_simple_message(/*G_PLUGIN_MODULE(pygobject_get(self)), */type, msg); + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); result = Py_None; Py_INCREF(result); break; @@ -1037,7 +1191,10 @@ PyTypeObject *get_python_plugin_module_type(void) .tp_doc = "Chrysalide plugin for Python.", .tp_methods = py_plugin_module_methods, - .tp_getset = py_plugin_module_getseters + .tp_getset = py_plugin_module_getseters, + + .tp_init = py_plugin_module_init, + .tp_new = py_plugin_module_new, }; @@ -1072,7 +1229,7 @@ bool ensure_python_plugin_module_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type, &PyGObject_Type)) + if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type, &PyGObject_Type)) return false; if (!py_plugin_module_define_constants(type)) |