diff options
Diffstat (limited to 'plugins/pychrysalide/plugin.c')
-rw-r--r-- | plugins/pychrysalide/plugin.c | 790 |
1 files changed, 477 insertions, 313 deletions
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; } |