From d110791309783e6e30df837a81cf8e14e1ac9641 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 17 May 2020 20:05:48 +0200
Subject: Updated the plugin system and its Python documentation.

---
 plugins/devdbg/speed.c                    |   2 +-
 plugins/devdbg/speed.h                    |   2 +-
 plugins/libcsem/semantic.c                |   2 +-
 plugins/libcsem/semantic.h                |   2 +-
 plugins/lnxsyscalls/core.c                |   2 +-
 plugins/lnxsyscalls/core.h                |   2 +-
 plugins/pychrysalide/Makefile.am          |   1 +
 plugins/pychrysalide/analysis/constants.c |   2 +-
 plugins/pychrysalide/constants.c          |  95 ++++
 plugins/pychrysalide/constants.h          |  38 ++
 plugins/pychrysalide/core/constants.c     |  56 +++
 plugins/pychrysalide/core/constants.h     |   3 +
 plugins/pychrysalide/core/logs.c          |  32 +-
 plugins/pychrysalide/format/symbol.c      |  20 +-
 plugins/pychrysalide/plugin.c             | 790 ++++++++++++++++++------------
 plugins/pychrysalide/pychrysa.c           |   2 +-
 plugins/python/abackup/plugin.py          |  23 +-
 plugins/python/apkfiles/apkfiles.py       |  23 +-
 plugins/python/checksec/plugin.py         |  21 +-
 plugins/python/liveconv/plugin.py         |  19 +-
 src/plugins/plugin.c                      |  10 +-
 21 files changed, 734 insertions(+), 413 deletions(-)
 create mode 100644 plugins/pychrysalide/constants.c
 create mode 100644 plugins/pychrysalide/constants.h

diff --git a/plugins/devdbg/speed.c b/plugins/devdbg/speed.c
index 1378c66..c8416d1 100644
--- a/plugins/devdbg/speed.c
+++ b/plugins/devdbg/speed.c
@@ -218,7 +218,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_handle_binary_format_analysis(const GPlug
 *                                                                             *
 ******************************************************************************/
 
-G_MODULE_EXPORT void chrysalide_plugin_process_binary_disassembly(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
+G_MODULE_EXPORT void chrysalide_plugin_process_disassembly_event(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
 {
     speed_measure *measure;                 /* Suivi des progressions      */
     struct timeval point;                   /* Point de mesure courant     */
diff --git a/plugins/devdbg/speed.h b/plugins/devdbg/speed.h
index 7d591fb..2347706 100644
--- a/plugins/devdbg/speed.h
+++ b/plugins/devdbg/speed.h
@@ -34,7 +34,7 @@
 G_MODULE_EXPORT bool chrysalide_plugin_handle_binary_format_analysis(const GPluginModule *, PluginAction, GBinFormat *, wgroup_id_t, GtkStatusStack *);
 
 /* Exécute une action pendant un désassemblage de binaire. */
-G_MODULE_EXPORT void chrysalide_plugin_process_binary_disassembly(const GPluginModule *, PluginAction , GLoadedBinary *, GtkStatusStack *, GProcContext *);
+G_MODULE_EXPORT void chrysalide_plugin_process_disassembly_event(const GPluginModule *, PluginAction , GLoadedBinary *, GtkStatusStack *, GProcContext *);
 
 
 
diff --git a/plugins/libcsem/semantic.c b/plugins/libcsem/semantic.c
index 00e174f..6786894 100644
--- a/plugins/libcsem/semantic.c
+++ b/plugins/libcsem/semantic.c
@@ -54,7 +54,7 @@ DEFINE_CHRYSALIDE_PLUGIN("CSem", "Semantic information relative to the libc",
 *                                                                             *
 ******************************************************************************/
 
-G_MODULE_EXPORT void chrysalide_plugin_process_binary_disassembly(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
+G_MODULE_EXPORT void chrysalide_plugin_process_disassembly_event(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
 {
     if (action == PGA_DISASSEMBLY_HOOKED_POST)
     {
diff --git a/plugins/libcsem/semantic.h b/plugins/libcsem/semantic.h
index 58c14ef..016b85d 100644
--- a/plugins/libcsem/semantic.h
+++ b/plugins/libcsem/semantic.h
@@ -31,7 +31,7 @@
 
 
 /* Exécute une action pendant un désassemblage de binaire. */
-G_MODULE_EXPORT void chrysalide_plugin_process_binary_disassembly(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *);
+G_MODULE_EXPORT void chrysalide_plugin_process_disassembly_event(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *);
 
 
 
diff --git a/plugins/lnxsyscalls/core.c b/plugins/lnxsyscalls/core.c
index 314f89f..bee11db 100644
--- a/plugins/lnxsyscalls/core.c
+++ b/plugins/lnxsyscalls/core.c
@@ -96,7 +96,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
 *                                                                             *
 ******************************************************************************/
 
-G_MODULE_EXPORT void chrysalide_plugin_process_binary_disassembly(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
+G_MODULE_EXPORT void chrysalide_plugin_process_disassembly_event(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context)
 {
     GBinFormat *format;                     /* Format du binaire chargé    */
     const char *arch;                       /* Architecture d'exécution    */
diff --git a/plugins/lnxsyscalls/core.h b/plugins/lnxsyscalls/core.h
index dcf5c11..50b8ef0 100644
--- a/plugins/lnxsyscalls/core.h
+++ b/plugins/lnxsyscalls/core.h
@@ -34,7 +34,7 @@
 G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
 
 /* Exécute une action pendant un désassemblage de binaire. */
-G_MODULE_EXPORT void chrysalide_plugin_process_binary_disassembly(const GPluginModule *, PluginAction , GLoadedBinary *, GtkStatusStack *, GProcContext *);
+G_MODULE_EXPORT void chrysalide_plugin_process_disassembly_event(const GPluginModule *, PluginAction , GLoadedBinary *, GtkStatusStack *, GProcContext *);
 
 
 
diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am
index 6bcaef4..5d6c3e6 100644
--- a/plugins/pychrysalide/Makefile.am
+++ b/plugins/pychrysalide/Makefile.am
@@ -6,6 +6,7 @@ libdir = $(pluginslibdir)
 
 pychrysalide_la_SOURCES =				\
 	access.h access.c					\
+	constants.h constants.c				\
 	constval.h constval.c				\
 	helpers.h helpers.c					\
 	plugin.h plugin.c					\
diff --git a/plugins/pychrysalide/analysis/constants.c b/plugins/pychrysalide/analysis/constants.c
index 87f91a3..a162ac7 100644
--- a/plugins/pychrysalide/analysis/constants.c
+++ b/plugins/pychrysalide/analysis/constants.c
@@ -36,7 +36,7 @@
 *                                                                             *
 *  Paramètres  : type = type dont le dictionnaire est à compléter.            *
 *                                                                             *
-*  Description : Définit les constantes relatives au contenus binaires.       *
+*  Description : Définit les constantes relatives aux contenus binaires.      *
 *                                                                             *
 *  Retour      : true en cas de succès de l'opération, false sinon.           *
 *                                                                             *
diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c
new file mode 100644
index 0000000..e31a8fe
--- /dev/null
+++ b/plugins/pychrysalide/constants.c
@@ -0,0 +1,95 @@
+
+/* 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 <plugins/plugin-def.h>
+
+
+#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_EXIT", PGA_PLUGIN_EXIT);
+    if (result) result = add_const_to_group(values, "NATIVE_LOADED", PGA_NATIVE_LOADED);
+    if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME);
+    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
new file mode 100644
index 0000000..21f9d0e
--- /dev/null
+++ b/plugins/pychrysalide/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_CONSTANTS_H
+#define _PLUGINS_PYCHRYSALIDE_CONSTANTS_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+/* 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/constants.c b/plugins/pychrysalide/core/constants.c
index e61f451..f3ec530 100644
--- a/plugins/pychrysalide/core/constants.c
+++ b/plugins/pychrysalide/core/constants.c
@@ -73,3 +73,59 @@ bool define_core_logs_constants(PyObject *module)
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en constante LogMessageType.              *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_log_message_type(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+    unsigned long value;                    /* Valeur transcrite           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to LogMessageType");
+            break;
+
+        case 1:
+            value = PyLong_AsUnsignedLong(arg);
+
+            if (value > LMT_COUNT)
+            {
+                PyErr_SetString(PyExc_TypeError, "invalid value for LogMessageType");
+                result = 0;
+            }
+
+            else
+                *((LogMessageType *)dst) = value;
+
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/core/constants.h b/plugins/pychrysalide/core/constants.h
index 38a8ebc..6ed6fbb 100644
--- a/plugins/pychrysalide/core/constants.h
+++ b/plugins/pychrysalide/core/constants.h
@@ -34,6 +34,9 @@
 /* Définit les constantes pour les types de messages. */
 bool define_core_logs_constants(PyObject *);
 
+/* Tente de convertir en constante LogMessageType. */
+int convert_to_log_message_type(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_CORE_CONSTANTS_H */
diff --git a/plugins/pychrysalide/core/logs.c b/plugins/pychrysalide/core/logs.c
index 0965b4b..4e0bc7b 100644
--- a/plugins/pychrysalide/core/logs.c
+++ b/plugins/pychrysalide/core/logs.c
@@ -103,7 +103,7 @@ static PyObject *py_logs_get_verbosity(PyObject *self, PyObject *args)
 static PyObject *py_logs_set_verbosity(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Bilan à retourner           */
-    unsigned long verbosity;                /* Niveau de filtre de message */
+    LogMessageType verbosity;               /* Niveau de filtre de message */
 
 #define LOGS_SET_VERBOSITY_METHOD PYTHON_METHOD_DEF                         \
 (                                                                           \
@@ -116,7 +116,7 @@ static PyObject *py_logs_set_verbosity(PyObject *self, PyObject *args)
     " all kinds of logs get printed."                                       \
 )
 
-    if (!PyArg_ParseTuple(args, "k", &verbosity))
+    if (!PyArg_ParseTuple(args, "O&", convert_to_log_message_type, &verbosity))
         return NULL;
 
     set_log_verbosity(verbosity);
@@ -145,7 +145,7 @@ static PyObject *py_logs_set_verbosity(PyObject *self, PyObject *args)
 static PyObject *py_logs_log_message(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Bilan à retourner           */
-    unsigned long type;                     /* Espèce du message           */
+    LogMessageType type;                    /* Espèce du message           */
     const char *msg;                        /* Contenu du message          */
 
 #define LOGS_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF                           \
@@ -159,29 +159,13 @@ static PyObject *py_logs_log_message(PyObject *self, PyObject *args)
     " value."                                                               \
 )
 
-    if (!PyArg_ParseTuple(args, "ks", &type, &msg))
+    if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg))
         return NULL;
 
-    switch (type)
-    {
-        case LMT_INFO:
-        case LMT_PROCESS:
-        case LMT_WARNING:
-        case LMT_BAD_BINARY:
-        case LMT_ERROR:
-        case LMT_EXT_ERROR:
-            log_plugin_simple_message(type, msg);
-            result = Py_None;
-            Py_INCREF(result);
-            break;
-
-        default:
-            PyErr_SetString(PyExc_ValueError,
-                            _("Invalid type of message"));
-            result = NULL;
-            break;
-
-    }
+    log_plugin_simple_message(type, msg);
+
+    result = Py_None;
+    Py_INCREF(result);
 
     return result;
 
diff --git a/plugins/pychrysalide/format/symbol.c b/plugins/pychrysalide/format/symbol.c
index c86b453..7ecc576 100644
--- a/plugins/pychrysalide/format/symbol.c
+++ b/plugins/pychrysalide/format/symbol.c
@@ -59,7 +59,7 @@ static PyObject *py_binary_symbol_new(PyTypeObject *, PyObject *, PyObject *);
 static void py_binary_symbol_init_gclass(GBinSymbolClass *, gpointer);
 
 /* Fournit une étiquette pour viser un symbole. */
-static char *g_binary_symbol_get_label_wrapper(const GBinSymbol *);
+static char *py_binary_symbol_get_label_wrapper(const GBinSymbol *);
 
 /* Initialise une instance sur la base du dérivé de GObject. */
 static int py_binary_symbol_init(PyObject *, PyObject *, PyObject *);
@@ -194,7 +194,7 @@ static PyObject *py_binary_symbol_new(PyTypeObject *type, PyObject *args, PyObje
 
 static void py_binary_symbol_init_gclass(GBinSymbolClass *class, gpointer unused)
 {
-    class->get_label = g_binary_symbol_get_label_wrapper;
+    class->get_label = py_binary_symbol_get_label_wrapper;
 
 }
 
@@ -211,12 +211,21 @@ static void py_binary_symbol_init_gclass(GBinSymbolClass *class, gpointer unused
 *                                                                             *
 ******************************************************************************/
 
-static char *g_binary_symbol_get_label_wrapper(const GBinSymbol *symbol)
+static char *py_binary_symbol_get_label_wrapper(const GBinSymbol *symbol)
 {
     char *result;                           /* Etiquette à retourner       */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
+#define BINARY_SYMBOL_GET_LABEL_WRAPPER PYTHON_WRAPPER_DEF              \
+(                                                                       \
+    _get_label, "$self, /",                                             \
+    METH_VARARGS,                                                       \
+    "Abstract method used to provide the default label for a symbol.\n" \
+    "\n"                                                                \
+    "The returned value has to be a string."                            \
+)
+
     result = NULL;
 
     pyobj = pygobject_new(G_OBJECT(symbol));
@@ -275,8 +284,8 @@ static int py_binary_symbol_init(PyObject *self, PyObject *args, PyObject *kwds)
     "Where range is a memory space defined by pychrysalide.arch.mrange and"     \
     " stype a pychrysalide.format.BinSymbol.SymbolType value."                  \
     "\n"                                                                        \
-    "The following special method can be overridden:\n"                         \
-    "* _get_label(self): provides a default label for the symbol."
+    "The following methods have to be defined for new classes:\n"               \
+    "* pychrysalide.format.BinSymbol._get_label()."
 
     /* Récupération des paramètres */
 
@@ -892,6 +901,7 @@ static int py_binary_symbol_set_label(PyObject *self, PyObject *value, void *clo
 PyTypeObject *get_python_binary_symbol_type(void)
 {
     static PyMethodDef binary_symbol_methods[] = {
+        BINARY_SYMBOL_GET_LABEL_WRAPPER,
         BINARY_SYMBOL_SET_FLAG_METHOD,
         BINARY_SYMBOL_UNSET_FLAG_METHOD,
         BINARY_SYMBOL_HAS_FLAG_METHOD,
diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c
index 1736826..0d24f8c 100644
--- a/plugins/pychrysalide/plugin.c
+++ b/plugins/pychrysalide/plugin.c
@@ -37,8 +37,10 @@
 
 
 #include "access.h"
+#include "constants.h"
 #include "helpers.h"
 #include "pychrysa.h"
+#include "core/constants.h"
 
 
 
@@ -54,9 +56,6 @@ 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);
 
@@ -125,9 +124,6 @@ static void g_python_plugin_finalize(GPythonPlugin *);
 /* Affiche un message dans le journal des messages système. */
 static PyObject *py_plugin_module_log_message(PyObject *, PyObject *);
 
-/* Définit les constantes pour les greffons en Python. */
-static bool py_plugin_module_define_constants(PyTypeObject *);
-
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -250,24 +246,42 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu
 
 static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    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é  */
+    int ret;                                /* Bilan d'un appel            */
     GPluginModule *plugin;                  /* Greffon à manipuler         */
     plugin_interface *iface;                /* Interface à constituer      */
+    PyObject *value;                        /* Valeur à présence imposée   */
     size_t i;                               /* Boucle de parcours          */
     PyObject *action;                       /* Identifiant d'une action    */
 
-    static char *kwlist[] = { "name", "desc", "version", "actions", NULL };
-
-    /* Récupération des paramètres */
-
-    ret = PyArg_ParseTupleAndKeywords(args, kwds, "sssO!", kwlist,
-                                      &name, &desc, &version, &PyTuple_Type, &actions_obj);
-    if (!ret) return -1;
+#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._notify_native_loaded();\n"                \
+    "* pychrysalide.PluginModule._include_theme();\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 */
 
@@ -283,12 +297,34 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
 
     plugin = G_PLUGIN_MODULE(pygobject_get(self));
 
-    iface = malloc(sizeof(plugin_interface));
+    iface = calloc(1, sizeof(plugin_interface));
     plugin->interface = iface;
 
-    iface->name = strdup(name);
-    iface->desc = strdup(desc);
-    iface->version = strdup(version);
+#define LOAD_PYTHON_IFACE(attr)                                                                 \
+    do                                                                                          \
+    {                                                                                           \
+        if (PyObject_HasAttrString(self, "_" #attr))                                            \
+        {                                                                                       \
+            value = PyObject_GetAttrString(self, "_" #attr);                                    \
+            if (value != NULL)                                                                  \
+            {                                                                                   \
+                if (PyUnicode_Check(value))                                                     \
+                    iface->attr = strdup(PyUnicode_AsUTF8(value));                              \
+                Py_DECREF(value);                                                               \
+            }                                                                                   \
+        }                                                                                       \
+        if (iface->attr == NULL)                                                                \
+        {                                                                                       \
+            PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \
+            return -1;                                                                          \
+        }                                                                                       \
+    }                                                                                           \
+    while (0);
+
+    LOAD_PYTHON_IFACE(name);
+    LOAD_PYTHON_IFACE(desc);
+    LOAD_PYTHON_IFACE(version);
+    LOAD_PYTHON_IFACE(url);
 
     iface->container = false;
 
@@ -296,12 +332,21 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
     iface->required[0] = "PyChrysalide";
     iface->required_count = 1;
 
-    iface->actions_count = PyTuple_Size(actions_obj);
+    if (PyObject_HasAttrString(self, "_actions"))
+        value = PyObject_GetAttrString(self, "_actions");
+
+    else
+    {
+        PyErr_SetString(PyExc_TypeError, _("A '_actions' class attributes is missing."));
+        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(actions_obj, i);
+        action = PyTuple_GetItem(value, i);
 
         if (!PyLong_Check(action))
         {
@@ -313,12 +358,6 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
 
     }
 
-    if (!py_plugin_module_check_interface(self))
-    {
-        PyErr_SetString(PyExc_TypeError, _("missing features for the declared actions."));
-        return -1;
-    }
-
     return 0;
 
 }
@@ -326,184 +365,158 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : self = greffon Python en cours d'initialisation.             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                unused = variable non utilisé pour l'usage de __VA_ARGS__.   *
 *                                                                             *
-*  Description : Valide les fonctionnalités déclarées en actions.             *
+*  Description : Accompagne la fin du chargement des modules natifs.          *
 *                                                                             *
-*  Retour      : true si le greffon Python est à priori utilisable.           *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool py_plugin_module_check_interface(PyObject *self)
+static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *plugin, PluginAction action)
 {
-    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        */
+    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_NATIVE_LOADED_WRAPPER PYTHON_WRAPPER_DEF   \
+(                                                                       \
+    _notify_native_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 action such as"   \
+    " *NATIVE_LOADED*."                                                 \
+)
 
-    result = true;
+    gstate = PyGILState_Ensure();
 
-    plugin = G_PLUGIN_MODULE(pygobject_get(self));
+    pyobj = pygobject_new(G_OBJECT(plugin));
 
-    for (i = 0; i < plugin->interface->actions_count && result; i++)
+    if (has_python_method(pyobj, "_notify_native_loaded"))
     {
-        action = plugin->interface->actions[i];
-        category = MASK_PLUGIN_CATEGORY(action);
-        sub = MASK_PLUGIN_SUB_CATEGORY(action);
-
-        switch (category)
-        {
-            case DPC_BASIC:
+        args = PyTuple_New(1);
 
-                switch (sub)
-                {
-                    case DPS_NONE:
-                        break;
-
-                    case PGA_PLUGIN_INIT:
-                        result = has_python_method(self, "init");
-                        break;
-
-                    case PGA_PLUGIN_EXIT:
-                        result = has_python_method(self, "exit");
-                        break;
+        PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
 
-                    default:
-                        log_variadic_message(LMT_WARNING,
-                                             _("Unknown sub-category '0x%02x' in plugin '%s'..."),
-                                             sub, self->ob_type->tp_name);
-                        break;
+        pyret = run_python_method(pyobj, "_notify_native_loaded", args);
 
-                }
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-                break;
+    }
 
-            case DPC_BINARY_PROCESSING:
+    Py_DECREF(pyobj);
 
-                switch (sub)
-                {
-                    case DPS_CONTENT:
+    PyGILState_Release(gstate);
 
-                        switch (action)
-                        {
-                            case PGA_CONTENT_EXPLORER:
-                                result = has_python_method(self, "handle_binary_content");
-                                break;
+}
 
-                            case PGA_CONTENT_RESOLVER:
-                                result = has_python_method(self, "handle_loaded_content");
-                                break;
 
-                            default:
-                                log_variadic_message(LMT_WARNING,
-                                                     _("Unknown action '0x%02x' in plugin '%s'..."),
-                                                     action, self->ob_type->tp_name);
-                                break;
+/******************************************************************************
+*                                                                             *
+*  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*."                                                     \
+)
 
-                        break;
+    gstate = PyGILState_Ensure();
 
-                    case DPS_FORMAT:
+    pyobj = pygobject_new(G_OBJECT(plugin));
 
-                        switch (action)
-                        {
-                            case PGA_FORMAT_ANALYSIS_STARTED:
-                            case PGA_FORMAT_ANALYSIS_ENDED:
-                            case PGA_FORMAT_POST_ANALYSIS_STARTED:
-                            case PGA_FORMAT_POST_ANALYSIS_ENDED:
-                                result = has_python_method(self, "handle_format_analysis");
-                                break;
+    if (has_python_method(pyobj, "_include_theme"))
+    {
+        args = PyTuple_New(2);
 
-                            case PGA_FORMAT_PRELOAD:
-                                result = has_python_method(self, "preload_format");
-                                break;
+        darkness = (dark ? Py_True : Py_False);
+        Py_INCREF(darkness);
 
-                            case PGA_FORMAT_ATTACH_DEBUG:
-                                result = has_python_method(self, "attach_debug_format");
-                                break;
+        PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+        PyTuple_SetItem(args, 1, darkness);
 
-                            default:
-                                log_variadic_message(LMT_WARNING,
-                                                     _("Unknown action '0x%02x' in plugin '%s'..."),
-                                                     action, self->ob_type->tp_name);
-                                break;
+        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"));
 
-                        break;
+        else
+        {
+            length = PySequence_Length(pyret);
 
-                    case DPS_DISASSEMBLY:
-                        result = has_python_method(self, "process_disassembly");
-                        break;
+            for (i = 0; i < length; i++)
+            {
+                res = PySequence_GetItem(pyret, i);
 
-                    default:
-                        log_variadic_message(LMT_WARNING,
-                                             _("Unknown sub-category '0x%02x' in plugin '%s'..."),
-                                             sub, self->ob_type->tp_name);
-                        break;
+                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));
                 }
 
-                break;
+                Py_DECREF(res);
 
-            default:
-                log_variadic_message(LMT_WARNING,
-                                     _("Unknown category '0x%02x' in plugin '%s'..."),
-                                     category, self->ob_type->tp_name);
-                break;
+            }
 
         }
 
-    }
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : plugin = greffon à manipuler.                                *
-*                action = type d'action attendue.                             *
-*                unused = variable non utilisé pour l'usage de __VA_ARGS__.   *
-*                                                                             *
-*  Description : Accompagne la fin du chargement des modules natifs.          *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *plugin, PluginAction action)
-{
-
-}
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
+    }
 
-/******************************************************************************
-*                                                                             *
-*  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   : -                                                            *
-*                                                                             *
-******************************************************************************/
+    Py_DECREF(pyobj);
 
-static void py_plugin_module_include_theme_wrapper(const GPluginModule *plugin, PluginAction action, gboolean dark, char ***resources, size_t *count)
-{
+    PyGILState_Release(gstate);
 
 }
 
@@ -529,25 +542,47 @@ static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *
     PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *args;                         /* Arguments pour l'appel      */
-    PyObject *value;                        /* Valeurs obtenues            */
+    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));
 
-    assert(has_python_method(pyobj, "handle_binary_content"));
+    if (has_python_method(pyobj, "_handle_binary_content"))
+    {
+        args = PyTuple_New(4);
 
-    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)));
 
-    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);
 
-    value = run_python_method(pyobj, "handle_binary_content", args);
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-    Py_XDECREF(value);
-    Py_DECREF(args);
+    }
 
     Py_DECREF(pyobj);
 
@@ -561,7 +596,7 @@ static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *
 *  Paramètres  : plugin  = greffon à manipuler.                               *
 *                action  = type d'action attendue.                            *
 *                content = contenu chargé à traiter.                          *
-*                wid     = identifiant du groupe de traitement.               *
+*                gid     = identifiant du groupe de traitement.               *
 *                status  = barre de statut à tenir informée.                  *
 *                                                                             *
 *  Description : Procède à une opération liée à un contenu chargé.            *
@@ -572,30 +607,49 @@ static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *
 *                                                                             *
 ******************************************************************************/
 
-static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedContent *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 gid, GtkStatusStack *status)
 {
     PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *args;                         /* Arguments pour l'appel      */
-    PyObject *value;                        /* Valeurs obtenues            */
+    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));
 
-    assert(has_python_method(pyobj, "handle_loaded_content"));
+    if (has_python_method(pyobj, "_handle_loaded_content"))
+    {
+        args = PyTuple_New(4);
 
-    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)));
 
-    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_loaded_content", args);
 
-    value = run_python_method(pyobj, "handle_loaded_content", args);
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-    Py_XDECREF(value);
-    Py_DECREF(args);
+    }
 
     Py_DECREF(pyobj);
 
@@ -625,25 +679,47 @@ static bool py_plugin_module_handle_binary_format_analysis_wrapper(const GPlugin
     PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *args;                         /* Arguments pour l'appel      */
-    PyObject *value;                        /* Valeurs obtenues            */
+    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.BinFormat"      \
+    " 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*."              \
+)
 
     gstate = PyGILState_Ensure();
 
     pyobj = pygobject_new(G_OBJECT(plugin));
 
-    assert(has_python_method(pyobj, "handle_format_analysis"));
+    if (has_python_method(pyobj, "_handle_format_analysis"))
+    {
+        args = PyTuple_New(4);
 
-    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)));
 
-    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);
 
-    value = run_python_method(pyobj, "handle_format_analysis", args);
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-    Py_XDECREF(value);
-    Py_DECREF(args);
+    }
 
     Py_DECREF(pyobj);
 
@@ -675,25 +751,48 @@ static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *
     PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *args;                         /* Arguments pour l'appel      */
-    PyObject *value;                        /* Valeurs obtenues            */
+    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));
 
-    assert(has_python_method(pyobj, "preload_format"));
+    if (has_python_method(pyobj, "_preload_format"))
+    {
+        args = PyTuple_New(4);
 
-    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)));
 
-    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);
 
-    value = run_python_method(pyobj, "preload_format", args);
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-    Py_XDECREF(value);
-    Py_DECREF(args);
+    }
 
     Py_DECREF(pyobj);
 
@@ -723,23 +822,38 @@ static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *pl
     PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *args;                         /* Arguments pour l'appel      */
-    PyObject *value;                        /* Valeurs obtenues            */
+    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));
 
-    assert(has_python_method(pyobj, "attach_debug_format"));
+    if (has_python_method(pyobj, "_attach_debug_format"))
+    {
+        args = PyTuple_New(2);
 
-    args = PyTuple_New(2);
+        PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+        PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format)));
 
-    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);
 
-    value = run_python_method(pyobj, "attach_debug_format", args);
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-    Py_XDECREF(value);
-    Py_DECREF(args);
+    }
 
     Py_DECREF(pyobj);
 
@@ -769,25 +883,43 @@ static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModu
     PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *args;                         /* Arguments pour l'appel      */
-    PyObject *value;                        /* Valeurs obtenues            */
+    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));
 
-    assert(has_python_method(pyobj, "process_disassembly"));
+    if (has_python_method(pyobj, "_process_disassembly_event"))
+    {
+        args = PyTuple_New(4);
 
-    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)));
 
-    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);
 
-    value = run_python_method(pyobj, "process_disassembly", args);
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
 
-    Py_XDECREF(value);
-    Py_DECREF(args);
+    }
 
     Py_DECREF(pyobj);
 
@@ -815,6 +947,85 @@ static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModu
 
 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);
 
 }
 
@@ -941,12 +1152,15 @@ static void g_python_plugin_finalize(GPythonPlugin *plugin)
 
     if (final != NULL)
     {
-        free(final->name);
-        free(final->desc);
-        free(final->version);
+        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);
 
-        assert(final->required_count == 1);
-        free(final->required);
+        if (final->required != NULL)
+            free(final->required);
 
         if (final->actions != NULL)
             free(final->actions);
@@ -1059,86 +1273,31 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
 static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Bilan à retourner           */
-    unsigned long type;                     /* Espèce du message           */
+    LogMessageType type;                    /* Espèce du message           */
     const char *msg;                        /* Contenu du message          */
 
-    if (!PyArg_ParseTuple(args, "ks", &type, &msg))
+#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;
 
-    switch (type)
-    {
-        case LMT_INFO:
-        case LMT_PROCESS:
-        case LMT_WARNING:
-        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);
-            result = Py_None;
-            Py_INCREF(result);
-            break;
-
-        default:
-            PyErr_SetString(PyExc_ValueError,
-                            _("Invalid type of message"));
-            result = NULL;
-            break;
+    g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg);
 
-    }
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : obj_type = type dont le dictionnaire est à compléter.        *
-*                                                                             *
-*  Description : Définit les constantes pour les greffons en Python.          *
-*                                                                             *
-*  Retour      : true en cas de succès de l'opération, false sinon.           *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool py_plugin_module_define_constants(PyTypeObject *obj_type)
-{
-    bool result;                            /* Bilan à retourner           */
-
-    result = true;
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_BASIC_NONE);
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_PLUGIN_INIT);
-    result &= PyDict_AddULongMacro(obj_type, PGA_PLUGIN_EXIT);
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_NATIVE_LOADED);
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_CONTENT_EXPLORER);
-    result &= PyDict_AddULongMacro(obj_type, PGA_CONTENT_RESOLVER);
-    result &= PyDict_AddULongMacro(obj_type, PGA_CONTENT_ANALYZED);
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_FORMAT_ANALYSIS_STARTED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_FORMAT_PRELOAD);
-    result &= PyDict_AddULongMacro(obj_type, PGA_FORMAT_ATTACH_DEBUG);
-    result &= PyDict_AddULongMacro(obj_type, PGA_FORMAT_ANALYSIS_ENDED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_FORMAT_POST_ANALYSIS_STARTED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_FORMAT_POST_ANALYSIS_ENDED);
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_STARTED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_RAW);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_HOOKED_LINK);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_HOOKED_POST);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_LIMITED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_LOOPS);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_LINKED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_GROUPED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_RANKED);
-    result &= PyDict_AddULongMacro(obj_type, PGA_DISASSEMBLY_ENDED);
-
-    result &= PyDict_AddULongMacro(obj_type, PGA_DETECTION_OBFUSCATORS);
+    result = Py_None;
+    Py_INCREF(result);
 
     return result;
 
@@ -1160,11 +1319,16 @@ static bool py_plugin_module_define_constants(PyTypeObject *obj_type)
 PyTypeObject *get_python_plugin_module_type(void)
 {
     static PyMethodDef py_plugin_module_methods[] = {
-        {
-            "log_message", py_plugin_module_log_message,
-            METH_VARARGS,
-            "log_message(type, msg, /)\n--\n\nDisplay a message in the log window, if any."
-        },
+        PLUGIN_MODULE_NOTIFY_NATIVE_LOADED_WRAPPER,
+        PLUGIN_MODULE_INCLUDE_THEME_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_LOG_MESSAGE_METHOD,
         { NULL }
     };
 
@@ -1181,7 +1345,7 @@ PyTypeObject *get_python_plugin_module_type(void)
 
         .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 
-        .tp_doc         = "Chrysalide plugin for Python.",
+        .tp_doc         = PLUGIN_MODULE_DOC,
 
         .tp_methods     = py_plugin_module_methods,
         .tp_getset      = py_plugin_module_getseters,
@@ -1225,7 +1389,7 @@ bool ensure_python_plugin_module_is_registered(void)
         if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type, &PyGObject_Type))
             return false;
 
-        if (!py_plugin_module_define_constants(type))
+        if (!define_plugin_module_constants(type))
             return false;
 
     }
diff --git a/plugins/pychrysalide/pychrysa.c b/plugins/pychrysalide/pychrysa.c
index 333544a..df6cc9a 100644
--- a/plugins/pychrysalide/pychrysa.c
+++ b/plugins/pychrysalide/pychrysa.c
@@ -428,7 +428,7 @@ static bool add_plugin_module_to_python_module(PyObject *super)
     "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n"                \
     "\n"                                                                                                        \
     "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n"       \
-    "* either from the Chrysalide's GUI, by registering hooks or GLib signals,\n"                               \
+    "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n"                               \
     "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
     "\n"                                                                                                        \
     "In both cases, this is a good start point to have a look at already existing plugins to quickly learn "    \
diff --git a/plugins/python/abackup/plugin.py b/plugins/python/abackup/plugin.py
index d8f52b1..aea9a9c 100644
--- a/plugins/python/abackup/plugin.py
+++ b/plugins/python/abackup/plugin.py
@@ -34,27 +34,18 @@ from .password import PasswordReader
 class AndroidBackupPlugin(PluginModule):
     """Open and process Android backup files."""
 
+    _name = 'AndroidBackup'
+    _desc = 'Add suppport for the Android backup file format'
+    _version = '0.1'
+    _url = 'https://www.chrysalide.re/'
 
-    def __init__(self):
-        """Initialize the plugin for Chrysalide."""
+    _actions = ( PluginModule.PluginAction.CONTENT_EXPLORER, )
 
-        interface = {
 
-            'name' : 'AndroidBackup',
-            'desc' : 'Add suppport for the Android backup file format',
-            'version' : '0.1',
-
-            'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, )
-
-        }
-
-        super(AndroidBackupPlugin, self).__init__(**interface)
-
-
-    def handle_binary_content(self, action, content, wid, status):
+    def _handle_binary_content(self, action, content, wid, status):
         """Process an operation on a binary content."""
 
-        assert(action == PluginModule.PGA_CONTENT_EXPLORER)
+        assert(action == PluginModule.PluginAction.CONTENT_EXPLORER)
 
         try:
             backup = AndroidBackup(content)
diff --git a/plugins/python/apkfiles/apkfiles.py b/plugins/python/apkfiles/apkfiles.py
index 47dfac4..98d31c7 100644
--- a/plugins/python/apkfiles/apkfiles.py
+++ b/plugins/python/apkfiles/apkfiles.py
@@ -12,27 +12,18 @@ import zipfile
 class ApkFiles(PluginModule):
     """Open and process APK files."""
 
+    _name = 'ApkFiles'
+    _desc = 'Add suppport for the APK file format'
+    _version = '0.1'
+    _url = 'https://www.chrysalide.re/'
 
-    def __init__(self):
-        """Initialize the plugin for Chrysalide."""
+    _actions = ( PluginModule.PluginAction.CONTENT_EXPLORER, )
 
-        interface = {
 
-            'name' : 'ApkFiles',
-            'desc' : 'Add suppport for the APK file format',
-            'version' : '0.1',
-
-            'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, )
-
-        }
-
-        super(ApkFiles, self).__init__(**interface)
-
-
-    def handle_binary_content(self, action, content, wid, status):
+    def _handle_binary_content(self, action, content, wid, status):
         """Process an operation on a binary content."""
 
-        assert(action == PluginModule.PGA_CONTENT_EXPLORER)
+        assert(action == PluginModule.PluginAction.CONTENT_EXPLORER)
 
         pseudo_file = io.BytesIO(content.data)
 
diff --git a/plugins/python/checksec/plugin.py b/plugins/python/checksec/plugin.py
index f1229bb..6ab213f 100644
--- a/plugins/python/checksec/plugin.py
+++ b/plugins/python/checksec/plugin.py
@@ -10,24 +10,15 @@ from pychrysalide.format.elf import ElfFormat
 class CheckSec(PluginModule):
     """Check for Elf mititgations."""
 
+    _name = 'CheckSec'
+    _desc = 'Output the exploit mitigations compiled with a loaded binary'
+    _version = '0.1'
+    _url = 'https://www.chrysalide.re/'
 
-    def __init__(self):
-        """Initialize the plugin for Chrysalide."""
+    _actions = ( PluginModule.PluginAction.FORMAT_POST_ANALYSIS_ENDED, )
 
-        interface = {
 
-            'name' : 'CheckSec',
-            'desc' : 'Output the exploit mitigations compiled with a loaded binary',
-            'version' : '0.1',
-
-            'actions' : ( PluginModule.PGA_FORMAT_POST_ANALYSIS_ENDED, )
-
-        }
-
-        super(CheckSec, self).__init__(**interface)
-
-
-    def handle_format_analysis(self, action, format, gid, status):
+    def _handle_format_analysis(self, action, format, gid, status):
         """Get notified at the end of format analysis."""
 
         if type(format) == ElfFormat:
diff --git a/plugins/python/liveconv/plugin.py b/plugins/python/liveconv/plugin.py
index a1a182e..eadbea0 100644
--- a/plugins/python/liveconv/plugin.py
+++ b/plugins/python/liveconv/plugin.py
@@ -8,21 +8,18 @@ from .panel import ConvPanel
 class LiveConverter(PluginModule):
     """Convert raw values into interpreted values."""
 
+    _name = 'LiveConverter'
+    _desc = 'Convert raw values into interprered values'
+    _version = '0.1'
+    _url = 'https://www.chrysalide.re/'
 
-    def __init__(self):
-        """Initialize the plugin for Chrysalide."""
-
-        interface = {
+    _actions = ( )
 
-            'name' : 'LiveConverter',
-            'desc' : 'Convert raw values into interprered values',
-            'version' : '0.1',
 
-            'actions' : ( )
-
-        }
+    def __init__(self):
+        """Initialize the plugin for Chrysalide."""
 
-        super(LiveConverter, self).__init__(**interface)
+        super(LiveConverter, self).__init__()
 
         p = ConvPanel()
 
diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c
index 674b1a8..dc8fc59 100644
--- a/src/plugins/plugin.c
+++ b/src/plugins/plugin.c
@@ -424,7 +424,7 @@ GPluginModule *g_plugin_module_new(const gchar *filename)
                         break;
 
                     case DPS_DISASSEMBLY:
-                        valid = check_plugin_symbol(module, "chrysalide_plugin_process_binary_disassembly");
+                        valid = check_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event");
                         break;
 
                     case DPS_DETECTION:
@@ -684,7 +684,7 @@ static void g_plugin_module_init_gclass(GPluginModuleClass *class, GModule *modu
                         break;
 
                     case DPS_DISASSEMBLY:
-                        load_plugin_symbol(module, "chrysalide_plugin_process_binary_disassembly", &class->process_disass);
+                        load_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event", &class->process_disass);
                         break;
 
                     case DPS_DETECTION:
@@ -1130,7 +1130,7 @@ void g_plugin_module_handle_binary_content(const GPluginModule *plugin, PluginAc
 *  Paramètres  : plugin  = greffon à manipuler.                               *
 *                action  = type d'action attendue.                            *
 *                content = contenu chargé à traiter.                          *
-*                wid     = identifiant du groupe de traitement.               *
+*                gid     = identifiant du groupe de traitement.               *
 *                status  = barre de statut à tenir informée.                  *
 *                                                                             *
 *  Description : Procède à une opération liée à un contenu chargé.            *
@@ -1141,13 +1141,13 @@ void g_plugin_module_handle_binary_content(const GPluginModule *plugin, PluginAc
 *                                                                             *
 ******************************************************************************/
 
-void g_plugin_module_handle_loaded_content(const GPluginModule *plugin, PluginAction action, GLoadedContent *content, wgroup_id_t wid, GtkStatusStack *status)
+void g_plugin_module_handle_loaded_content(const GPluginModule *plugin, PluginAction action, GLoadedContent *content, wgroup_id_t gid, GtkStatusStack *status)
 {
     GPluginModuleClass *class;              /* Classe de l'instance active */
 
     class = G_PLUGIN_MODULE_GET_CLASS(plugin);
 
-    return class->handle_loaded(plugin, action, content, wid, status);
+    return class->handle_loaded(plugin, action, content, gid, status);
 
 }
 
-- 
cgit v0.11.2-87-g4458