From d110791309783e6e30df837a81cf8e14e1ac9641 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard 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 + + +#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 +#include + + +/* Définit les constantes relatives aux greffons Python. */ +bool define_plugin_module_constants(PyTypeObject *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CONSTANTS_H */ diff --git a/plugins/pychrysalide/core/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