diff options
Diffstat (limited to 'plugins/pychrysalide/plugin.c')
-rw-r--r-- | plugins/pychrysalide/plugin.c | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c new file mode 100644 index 0000000..fad0084 --- /dev/null +++ b/plugins/pychrysalide/plugin.c @@ -0,0 +1,809 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * plugin.c - interactions avec un greffon Python + * + * Copyright (C) 2012-2017 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "plugin.h" + + +#include <pygobject.h> +#include <string.h> + + +#include <common/extstr.h> +#include "../../src/core/formats.h" +//#include <core/formats.h> +#include <plugins/plugin-int.h> + + +#include "helpers.h" + + + +/* --------------------- INTERFACE INTERNE POUR GREFFONS PYTHON --------------------- */ + + +/* Ligne de représentation de code binaire (instance) */ +struct _GPythonPlugin +{ + GPluginModule parent; /* Instance parente */ + + PyObject *module; /* Script Python chargé */ + PyObject *instance; /* Instance Python du greffon */ + +}; + + +/* Ligne de représentation de code binaire (classe) */ +struct _GPythonPluginClass +{ + GPluginModuleClass parent; /* Classe parente */ + +}; + + +/* Initialise la classe des greffons Python. */ +static void g_python_plugin_class_init(GPythonPluginClass *); + +/* Initialise l'instance d'un greffon Python. */ +static void g_python_plugin_init(GPythonPlugin *); + +/* Reconstruit la déclaration d'interface à partir de lectures. */ +static bool g_python_plugin_read_interface(GPythonPlugin *); + +/* Procède à l'initialisation du greffon. */ +static bool g_python_plugin_do_init(GPythonPlugin *); + +/* Procède à l'extinction du greffon. */ +static bool g_python_plugin_do_exit(GPythonPlugin *, GObject *); + +/* Indique si le format peut être pris en charge ici. */ +FormatMatchStatus python_plugin_is_matching(GBinContent *, GExeFormat *, GPythonPlugin *, char **); + +/* Exécute une action pendant un désassemblage de binaire. */ +static void g_python_plugin_process_disass(const GPythonPlugin *, PluginAction, GLoadedBinary *); + + + +/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ + + +/* Définit les constantes pour les greffons en Python. */ +static bool py_plugin_module_define_constants(PyTypeObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERFACE INTERNE POUR GREFFONS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type définit par la GLib pour le greffon Python. */ +G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des greffons Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_class_init(GPythonPluginClass *klass) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise l'instance d'un greffon Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_init(GPythonPlugin *plugin) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : modname = nom du module à charger. * +* filename = chemin d'accès au code Python à charger. * +* * +* Description : Crée un greffon à partir de code Python. * +* * +* Retour : Adresse de la structure mise en place ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GPluginModule *g_python_plugin_new(const char *modname, const char *filename) +{ + GPythonPlugin *result; /* Structure à retourner */ + PyObject *name; /* Chemin d'accès pour Python */ + PyObject *module; /* Script Python chargé */ + PyObject *err_type; /* Type d'erreur Python */ + PyObject *err_value; /* Instance Python d'erreur */ + PyObject *err_traceback; /* Trace Python associée */ + PyObject *err_string; /* Description Python d'erreur */ + const char *err_msg; /* Représentation humaine */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ + 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 */ + + name = PyUnicode_FromString(modname); + if (name == NULL) goto gppn_bad_exit; + + module = PyImport_Import(name); + Py_DECREF(name); + + if (PyErr_Occurred()) + { + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + if (err_value == NULL) + log_variadic_message(LMT_ERROR, + _("An unknown error occured when importing '%s'..."), modname); + else + { + err_string = PyObject_Str(err_value); + err_msg = PyUnicode_AsUTF8(err_string); + + log_variadic_message(LMT_ERROR, + _("An error occured when importing '%s': \"%s\""), modname, err_msg); + + Py_DECREF(err_string); + Py_DECREF(err_value); + + } + + Py_XDECREF(err_traceback); + Py_XDECREF(err_type); + + Py_XDECREF(module); + + module = NULL; + + } + + if (module == NULL) goto gppn_bad_exit; + + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); + + if (class == NULL) goto gppn_no_class; + if (!PyType_Check(class->ob_type)) goto gppn_no_class; + + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto gppn_no_instance; + + result = g_object_new(G_TYPE_PYTHON_PLUGIN, NULL); + + G_PLUGIN_MODULE(result)->filename = strdup(filename); + + result->module = module; + result->instance = instance; + + if (!g_python_plugin_read_interface(result)) + { + printf("bad interface------------\n"); + goto gppn_interface_error; + } + + /* Localisation des différents points d'entrée déclarés */ + +#define register_python_binding(inst, sym, binding) \ + ({ \ + bool __result; \ + if (!has_python_method(inst, #sym)) \ + { \ + log_variadic_message(LMT_ERROR, \ + _("No '%s' entry in plugin candidate '%s'"), \ + #sym, G_PLUGIN_MODULE(result)->filename); \ + __result = false; \ + } \ + else \ + { \ + G_PLUGIN_MODULE(result)->sym = binding; \ + __result = true; \ + } \ + __result; \ + }) + + for (i = 0; i < G_PLUGIN_MODULE(result)->interface->actions_count; i++) + { + action = G_PLUGIN_MODULE(result)->interface->actions[i]; + category = MASK_PLUGIN_CATEGORY(action); + sub = MASK_PLUGIN_SUB_CATEGORY(action); + + switch (category) + { + case DPC_BASIC: + + switch (sub) + { + case DPS_NONE: + break; + + case PGA_PLUGIN_INIT: + if (!register_python_binding(instance, init, (pg_management_fc)g_python_plugin_do_init)) + goto gppn_bad_plugin; + break; + + case PGA_PLUGIN_EXIT: + if (!register_python_binding(instance, exit, (pg_management_fc)g_python_plugin_do_exit)) + goto gppn_bad_plugin; + break; + + default: + log_variadic_message(LMT_WARNING, + _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + break; + + } + + break; + + case DPC_BINARY_PROCESSING: + + switch (sub) + { + case DPS_FORMAT: + + switch (action) + { + case PGA_FORMAT_MATCHER: + + if (!has_python_method(instance, "is_format_matching")) + { + log_variadic_message(LMT_ERROR, + _("No '%s' entry in plugin candidate '%s'"), + "is_format_matching", + G_PLUGIN_MODULE(result)->filename); + goto gppn_bad_plugin; + } + + if (!register_format_matcher((format_match_fc)python_plugin_is_matching, result)) + goto gppn_bad_plugin; + + break; + + case PGA_FORMAT_LOADER_LAST: + /* TODO */ + break; + + default: + log_variadic_message(LMT_WARNING, + _("Unknown action '0x%02x' in plugin '%s'..."), + action, filename); + break; + + } + + break; + + case DPS_DISASSEMBLY: + if (!register_python_binding(instance, process_disass, + (pg_process_disassembly_fc)g_python_plugin_process_disass)) + goto gppn_bad_plugin; + break; + + default: + log_variadic_message(LMT_WARNING, + _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + break; + + } + + break; + + default: + log_variadic_message(LMT_WARNING, + _("Unknown category '0x%02x' in plugin '%s'..."), category, filename); + break; + + } + + } + + /* Conclusion */ + + /* + if (!g_plugin_module_load(G_PLUGIN_MODULE(result))) + goto gppn_bad_plugin; + */ + + return G_PLUGIN_MODULE(result); + + gppn_bad_plugin: + + gppn_interface_error: + + g_object_unref(G_OBJECT(result)); + + //Py_DECREF(instance); + + gppn_no_instance: + + gppn_no_class: + + //Py_DECREF(module); + + gppn_bad_exit: + + printf("bad exit :(\n"); + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à initialiser. * +* * +* Description : Reconstruit la déclaration d'interface à partir de lectures. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_plugin_read_interface(GPythonPlugin *plugin) +{ + bool result; /* Bilan à renvoyer */ + plugin_interface interface; /* Recueil des éléments */ + PyObject *desc; /* Tableau de description */ + PyObject *str; /* Chaîne de caractères */ + PyObject *tuple; /* Liste d'éléments à traiter */ + Py_ssize_t count; /* Nombre d'éléments présents */ + Py_ssize_t i; /* Boucle de parcours */ + PyObject *action; /* Identifiant d'une action */ + plugin_interface *final; /* Interface finale conservée */ + + result = true; + + desc = run_python_method(plugin->instance, "get_interface", NULL); + if (!PyDict_Check(desc)) + { + result = false; + goto pgpri_end; + } + + memset(&interface, 0, sizeof(interface)); + + /* Chargements des premières chaînes */ + +#define READ_STR_FIELD(name) \ + str = PyDict_GetItemString(desc, #name); \ + if ((result = PyUnicode_Check(str))) \ + interface.name = strdup(PyUnicode_DATA(str)); \ + + READ_STR_FIELD(name); + READ_STR_FIELD(desc); + READ_STR_FIELD(version); + + /* Chargement des actions supportées */ + + tuple = PyDict_GetItemString(desc, "actions"); + + if (!PyList_Check(tuple)) + { + result = false; + goto pgpri_failed; + } + + count = PyList_GET_SIZE(tuple); + + interface.actions = (plugin_action_t *)calloc(count, sizeof(plugin_action_t)); + interface.actions_count = count; + + for (i = 0; i < count; i++) + { + action = PyList_GET_ITEM(tuple, i); + + interface.actions[i] = PyLong_AsLong(action); + + } + + pgpri_failed: + + if (result) + { + final = (plugin_interface *)calloc(1, sizeof(plugin_interface)); + + memcpy(final, &interface, sizeof(interface)); + + G_PLUGIN_MODULE(plugin)->interface = final; + + } + else + { + if (interface.name != NULL) free((char *)interface.name); + if (interface.desc != NULL) free((char *)interface.desc); + if (interface.version != NULL) free((char *)interface.version); + + if (interface.actions != NULL) free(interface.actions); + + } + + pgpri_end: + + Py_XDECREF(desc); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à initialiser. * +* ref = espace de référencement global. * +* * +* Description : Procède à l'initialisation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_plugin_do_init(GPythonPlugin *plugin) +{ + bool result; /* Bilan à retourner */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *value; /* Valeur obtenue */ + + args = Py_None; + Py_INCREF(args); + + value = run_python_method(plugin->instance, "init", args); + + result = (value != NULL && PyObject_IsTrue(value)); + + Py_XDECREF(value); + Py_DECREF(args); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à initialiser. * +* ref = espace de référencement global. * +* * +* Description : Procède à l'extinction du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_plugin_do_exit(GPythonPlugin *plugin, GObject *ref) +{ + bool result; /* Bilan à retourner */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *value; /* Valeur obtenue */ + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(ref)); + + value = run_python_method(plugin->instance, "exit", args); + + result = PyObject_IsTrue(value); + + Py_XDECREF(value); + Py_DECREF(args); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : content = contenu binaire à parcourir. * +* parent = éventuel format exécutable déjà chargé. * +* plugin = grefon C interne représentant le grefon Python. * +* key = identifiant de format trouvé ou NULL. [OUT] * +* * +* Description : Indique si le format peut être pris en charge ici. * +* * +* Retour : Conclusion de haut niveau sur la reconnaissance effectuée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +FormatMatchStatus python_plugin_is_matching(GBinContent *content, GExeFormat *parent, GPythonPlugin *plugin, char **key) +{ + FormatMatchStatus result; /* Bilan à renvoyer */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *value; /* Valeur obtenue */ + PyObject *arg; /* Argument en élément de tuple*/ + + result = FMS_UNKNOWN; + + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(content))); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(parent))); + + value = run_python_method(plugin->instance, "is_format_matching", args); + + if (PyTuple_Check(value)) + { + if (PyTuple_Size(value) > 0) + { + arg = PyTuple_GetItem(value, 0); + + if (PyLong_Check(arg)) + { + result = PyLong_AsLong(arg); + + switch (result) + { + case FMS_MATCHED: + + if (PyTuple_Size(value) != 2) + { + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_ERROR, + _("Expecting only a tuple [ status, key ] " \ + "as result for format matching.")); + result = FMS_UNKNOWN; + break; + } + + arg = PyTuple_GetItem(value, 1); + + if (PyUnicode_Check(arg)) + *key = strdup(PyUnicode_AsUTF8(arg)); + + else + { + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_ERROR, + _("Expecting a key string for format matching.")); + result = FMS_UNKNOWN; + } + + break; + + case FMS_FORWARDED: + case FMS_UNKNOWN: + if (PyTuple_Size(value) != 1) + { + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_WARNING, + _("Unused second item for format matching.")); + } + break; + + default: + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_ERROR, + _("Unexpected result for format matching.")); + result = FMS_UNKNOWN; + break; + + } + + } + + else + { + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_ERROR, + _("Unexpected result status for format matching.")); + result = FMS_UNKNOWN; + } + + } + else + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_WARNING, + _("Interpreting a empty tuple as FMS_UNKNOWN " \ + "for format matching.")); + + } + else + g_plugin_module_log_variadic_message(G_PLUGIN_MODULE(plugin), LMT_ERROR, + _("Expected a tuple containing [ status, key ] as result " \ + "for format matching.")); + + Py_XDECREF(value); + Py_DECREF(args); + + return result; + + + + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* binary = binaire dont le contenu est en cours de traitement. * +* * +* Description : Exécute une action pendant un désassemblage de binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAction action, GLoadedBinary *binary) +{ + PyObject *args; /* Arguments pour l'appel */ + PyObject *value; /* Valeurs obtenues */ + + args = PyTuple_New(2); + + PyTuple_SetItem(args, 0, PyLong_FromLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(binary))); + + value = run_python_method(plugin->instance, "process_binary_disassembly", args); + + Py_XDECREF(value); + Py_DECREF(args); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MODULE PYTHON POUR LES SCRIPTS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* 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_AddIntMacro(obj_type, PGA_BASIC_NONE); + result &= PyDict_AddIntMacro(obj_type, PGA_PLUGIN_INIT); + result &= PyDict_AddIntMacro(obj_type, PGA_PLUGIN_EXIT); + result &= PyDict_AddIntMacro(obj_type, PGA_FORMAT_MATCHER); + result &= PyDict_AddIntMacro(obj_type, PGA_FORMAT_LOADER_LAST); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_STARTED); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_RAW); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_HOOKED_LINK); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_HOOKED_POST); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_LIMITED); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_LOOPS); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_LINKED); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_GROUPED); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_RANKED); + result &= PyDict_AddIntMacro(obj_type, PGA_DISASSEMBLY_ENDED); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_plugin_module_type(void) +{ + static PyMethodDef py_plugin_module_methods[] = { + { NULL } + }; + + static PyGetSetDef py_plugin_module_getseters[] = { + { NULL } + }; + + static PyTypeObject py_plugin_module_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.PluginModule", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = "Chrysalide plugin for Python.", + + .tp_methods = py_plugin_module_methods, + .tp_getset = py_plugin_module_getseters + + }; + + return &py_plugin_module_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.PluginModule'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool register_python_plugin_module(PyObject *module) +{ + PyTypeObject *py_plugin_module_type; /* Type Python 'PluginModule' */ + PyObject *dict; /* Dictionnaire du module */ + + py_plugin_module_type = get_python_plugin_module_type(); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, py_plugin_module_type, &PyGObject_Type)) + return false; + + if (!py_plugin_module_define_constants(py_plugin_module_type)) + return false; + + return true; + +} |