/* OpenIDA - Outil d'analyse de fichiers binaires * plugin.c - interactions avec un greffon Python * * Copyright (C) 2010-2012 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA 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. * * OpenIDA 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 #include "../../src/analysis/binary.h" #include "../../src/plugins/plugin-int.h" #include "helpers.h" #include "analysis/binary.h" #include "debug/debugger.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 *); /* Procède à l'initialisation du greffon. */ static bool g_python_plugin_do_init(GPythonPlugin *, GObject *); /* Indique l'utilité pratique du greffon. */ static PluginAction g_python_plugin_get_action(const GPythonPlugin *); /* Indentifie un format à associer à un contenu binaire. */ static MatchingFormatAction g_python_plugin_is_matching(const GPythonPlugin *, char **, bin_t **, off_t *); /* Exécute une action définie sur un binaire chargé. */ static bool g_python_plugin_execute_on_binary(GPythonPlugin *, GLoadedBinary *, PluginAction); /* Exécute une action relative à un débogueur. */ static bool g_python_plugin_handle_debugger(const GPythonPlugin *, GBinaryDebugger *, PluginAction); /* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ /* Classe plugin pour Python */ typedef struct _pychrysa_plugin { PyObject_HEAD } pychrysa_plugin; /* Crée un nouveau greffon Python abstrait. */ static PyObject *pychrysa_plugin_new(PyTypeObject *, PyObject *, PyObject *); /* Exécute une action valide pour le greffon Python. */ static PyObject *pychrysa_plugin_run(PyObject *, PyObject *); /* Définit les constantes pour les greffons en Python. */ static bool pychrysa_plugin_define_constants(PyObject *); /* Procède à l'initialisation du greffon. */ static PyObject *pychrysa_plugin_init(PyObject *, PyObject *); /* Définit le comportement par défaut d'un greffon Python. */ static PyObject *pychrysa_plugin_get_action(PyObject *, PyObject *); /* Définit l'issue de la recherche d'un format par défaut. */ static PyObject *pychrysa_plugin_is_matching(PyObject *, PyObject *); /* Exécute une action relative à un débogueur. */ static PyObject *pychrysa_plugin_handle_debugger(PyObject *, PyObject *); int main2(const char *filename, const char *method) { PyObject *pName, *pModule, *pDict, *pFunc; PyObject *pArgs, *pValue; int i; return 0; pName = PyString_FromString/*PyUnicode_FromString*/(filename); /* Error checking of pName left out */ pModule = PyImport_Import(pName); Py_DECREF(pName); if (pModule != NULL) { pFunc = PyObject_GetAttrString(pModule, method); /* pFunc is a new reference */ if (pFunc && PyCallable_Check(pFunc)) { pArgs = PyTuple_New(0/*argc - 3*/); #if 0 for (i = 0; i < argc - 3; ++i) { pValue = PyLong_FromLong(atoi(argv[i + 3])); if (!pValue) { Py_DECREF(pArgs); Py_DECREF(pModule); fprintf(stderr, "Cannot convert argument\n"); return 1; } /* pValue reference stolen here: */ PyTuple_SetItem(pArgs, i, pValue); } #endif pValue = PyObject_CallObject(pFunc, pArgs); Py_DECREF(pArgs); if (pValue != NULL) { printf("Result of call: %ld\n", PyLong_AsLong(pValue)); Py_DECREF(pValue); } else { Py_DECREF(pFunc); Py_DECREF(pModule); PyErr_Print(); fprintf(stderr,"Call failed\n"); return 1; } } else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function \"%s\"\n", method); } Py_XDECREF(pFunc); Py_DECREF(pModule); } else { PyErr_Print(); fprintf(stderr, "Failed to load \"%s\"\n", filename); return 1; } return 0; } /* ---------------------------------------------------------------------------------- */ /* 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) { GPluginModule *plugin_parent; /* Instance parente */ plugin_parent = G_PLUGIN_MODULE(plugin); plugin_parent->exec_on_bin = (execute_action_on_binary_fc)g_python_plugin_execute_on_binary; plugin_parent->handle_debugger = (execute_on_debugger_fc)g_python_plugin_handle_debugger; } /****************************************************************************** * * * 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 *dict; /* Dictionnaire associé */ PyObject *class; /* Classe à instancier */ PyObject *instance; /* Instance Python du greffon */ #if PY_VERSION_HEX >= 0x03000000 name = PyUnicode_FromString(filename); #else name = PyString_FromString(filename); #endif if (name == NULL) goto gppn_bad_exit; module = PyImport_Import(name); Py_DECREF(name); if (module == NULL) { PyErr_Print(); goto gppn_bad_exit; } dict = PyModule_GetDict(module); class = PyDict_GetItemString(dict, modname); Py_DECREF(dict); 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; Py_DECREF(class); result = g_object_new(G_TYPE_PYTHON_PLUGIN, NULL); G_PLUGIN_MODULE(result)->name = strdup(modname); G_PLUGIN_MODULE(result)->name = stradd(G_PLUGIN_MODULE(result)->name, ".py"); G_PLUGIN_MODULE(result)->filename = strdup(G_PLUGIN_MODULE(result)->name); G_PLUGIN_MODULE(result)->init = g_python_plugin_do_init; G_PLUGIN_MODULE(result)->get_action = g_python_plugin_get_action; G_PLUGIN_MODULE(result)->is_matching = g_python_plugin_is_matching; result->module = module; result->instance = instance; return G_PLUGIN_MODULE(result); gppn_no_instance: Py_DECREF(class); gppn_no_class: Py_DECREF(module); gppn_bad_exit: return NULL; } /****************************************************************************** * * * 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, GObject *ref) { bool result; /* Bilan à retourner */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeur obtenue */ int cmp; /* Bilan de la comparaison */ args = PyTuple_New(1); PyTuple_SetItem(args, 0, pygobject_new(ref)); value = run_python_method(plugin->instance, "init", args); if (PyObject_Cmp(value, Py_True, &cmp) == -1) result = false; else result = (cmp == 0); Py_XDECREF(value); Py_DECREF(args); return result; } /****************************************************************************** * * * Paramètres : plugin = greffon de prise en charge à utiliser. * * * * Description : Indique l'utilité pratique du greffon. * * * * Retour : Action(s) codifiée(s), PGA_NONE par défaut. * * * * Remarques : - * * * ******************************************************************************/ static PluginAction g_python_plugin_get_action(const GPythonPlugin *plugin) { PluginAction result; /* Valeur à retourner */ PyObject *value; /* Valeur obtenue */ value = run_python_method(plugin->instance, "get_action", NULL); result = PyLong_AsLong(value); Py_DECREF(value); return result; } /****************************************************************************** * * * Paramètres : plugin = greffon de prise en charge à utiliser. * * filename = éventuel nom de fichier associé ou NULL. [OUT] * * data = données chargées. [OUT] * * length = quantité de ces données. [OUT] * * * * Description : Indentifie un format à associer à un contenu binaire. * * * * Retour : Bilan de la recherche de correspondances. * * * * Remarques : - * * * ******************************************************************************/ static MatchingFormatAction g_python_plugin_is_matching(const GPythonPlugin *plugin, char **filename, bin_t **data, off_t *length) { MatchingFormatAction result; /* Valeur à retourner */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ PyObject *action; /* Action à faire suivre */ PyObject *new_filename; /* Nouveau fichier */ PyObject *new_data; /* Nouvelles données */ char *tmp; /* Stockage avant recopie */ if (*filename == NULL) return MFA_NONE; args = PyTuple_New(2); PyTuple_SetItem(args, 0, PyString_FromString(*filename)); PyTuple_SetItem(args, 1, PyByteArray_FromStringAndSize(*data, *length)); value = run_python_method(plugin->instance, "is_matching", args); if (value != NULL && PyTuple_Check(value) && PyTuple_Size(value) == 3) { action = PyTuple_GetItem(value, 0); new_filename = PyTuple_GetItem(value, 1); new_data = PyTuple_GetItem(value, 2); if (action == NULL || new_filename == NULL || new_data == NULL) goto is_matching_bad; if (!PyInt_Check(action) || (new_filename != Py_None && !PyString_Check(new_filename)) || (new_data != Py_None && !PyByteArray_Check(new_data))) goto is_matching_bad; result = PyInt_AsLong(action); if (result >= MFA_COUNT) goto is_matching_bad; if (result != MFA_NONE && new_data == Py_None) goto is_matching_bad; if (new_filename != Py_None) { free(*filename); *filename = strdup(PyString_AsString(new_filename)); } /** * La suppression de la part du greffon n'est permise que * si une prise en charge est assurée. */ else if (result != MFA_NONE) { free(*filename); *filename = NULL; } /** * Pareil que précédemment. */ if (new_data != Py_None) { tmp = PyByteArray_AsString(new_data); *length = PyByteArray_Size(new_data); free(*data); *data = (bin_t *)calloc(*length, sizeof(bin_t)); memcpy(*data, tmp, *length * sizeof(bin_t)); } goto is_matching_ok; } is_matching_bad: printf("Bad result from is_matching() plugin function.\n"); result = MFA_NONE; is_matching_ok: Py_XDECREF(value); Py_DECREF(args); return result; } /****************************************************************************** * * * Paramètres : plugin = greffon de prise en charge à utiliser. * * binary = représentation binaire à traiter. * * action = action attendue. * * * * Description : Exécute une action définie sur un binaire chargé. * * * * Retour : true si une action a été menée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool g_python_plugin_execute_on_binary(GPythonPlugin *plugin, GLoadedBinary *binary, PluginAction action) { bool result; /* Bilan à remonter */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ args = PyTuple_New(2); PyTuple_SetItem(args, 0, py_loaded_binary_from_c(binary)); PyTuple_SetItem(args, 1, PyInt_FromLong(action)); value = run_python_method(plugin->instance, "execute_on_binary", args); result = (value == Py_True); Py_XDECREF(value); Py_DECREF(args); return result; } /****************************************************************************** * * * Paramètres : plugin = greffon à consulter. * * debugger = débogueur à l'origine de l'opération. * * action = action attendue. * * * * Description : Exécute une action relative à un débogueur. * * * * Retour : true si une action a été menée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool g_python_plugin_handle_debugger(const GPythonPlugin *plugin, GBinaryDebugger *debugger, PluginAction action) { bool result; /* Bilan à remonter */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ args = PyTuple_New(2); PyTuple_SetItem(args, 0, py_binary_debugger_from_c(debugger)); PyTuple_SetItem(args, 1, PyInt_FromLong(action)); value = run_python_method(plugin->instance, "handle_debugger", args); result = (value == Py_True); Py_XDECREF(value); Py_DECREF(args); return result; } /* ---------------------------------------------------------------------------------- */ /* MODULE PYTHON POUR LES SCRIPTS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : type = type de l'objet à instancier. * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * * * * Description : Crée un nouveau greffon Python abstrait. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static PyObject *pychrysa_plugin_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { pychrysa_plugin *result; /* Instance à retourner */ result = (pychrysa_plugin *)type->tp_alloc(type, 0); return (PyObject *)result; } /****************************************************************************** * * * Paramètres : dict = dictionnaire à compléter. * * * * Description : Définit les constantes pour les greffons en Python. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static bool pychrysa_plugin_define_constants(PyObject *dict) { int ret; /* Bilan d'un ajout */ ret = PyDict_SetItemString(dict, "PGA_NONE", PyInt_FromLong(PGA_NONE)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "PGA_FORMAT_MATCHER", PyInt_FromLong(PGA_FORMAT_MATCHER)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "PGA_DISASSEMBLE", PyInt_FromLong(PGA_DISASSEMBLE)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "PGA_DISASS_PROCESS", PyInt_FromLong(PGA_DISASS_PROCESS)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "PGA_CODE_PROCESS", PyInt_FromLong(PGA_CODE_PROCESS)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "PGA_DEBUGGER_ATTACH", PyInt_FromLong(PGA_DEBUGGER_ATTACH)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "PGA_DEBUGGER_DETACH", PyInt_FromLong(PGA_DEBUGGER_DETACH)); if (ret == -1) return false; /* PGA_FORMAT_MATCHER */ ret = PyDict_SetItemString(dict, "MFA_NONE", PyInt_FromLong(MFA_NONE)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "MFA_MATCHED", PyInt_FromLong(MFA_MATCHED)); if (ret == -1) return false; ret = PyDict_SetItemString(dict, "MFA_RELOAD", PyInt_FromLong(MFA_RELOAD)); if (ret == -1) return false; return true; } /****************************************************************************** * * * Paramètres : self = classe assurant le lien avec l'éditeur de messages. * * args = arguments fournis à l'appel. * * * * Description : Procède à l'initialisation du greffon. * * * * Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ static PyObject *pychrysa_plugin_init(PyObject *self, PyObject *args) { Py_INCREF(Py_True); return Py_True; } /****************************************************************************** * * * Paramètres : self = classe assurant le lien avec l'éditeur de messages. * * args = arguments fournis à l'appel. * * * * Description : Définit le comportement par défaut d'un greffon Python. * * * * Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ static PyObject *pychrysa_plugin_get_action(PyObject *self, PyObject *args) { return PyInt_FromLong(PGA_NONE); } /****************************************************************************** * * * Paramètres : self = classe assurant le lien avec l'éditeur de messages. * * args = arguments fournis à l'appel. * * * * Description : Définit l'issue de la recherche d'un format par défaut. * * * * Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ static PyObject *pychrysa_plugin_is_matching(PyObject *self, PyObject *args) { PyObject *result; /* Liste à retourner */ result = PyTuple_New(3); PyTuple_SetItem(result, 0, PyInt_FromLong(MFA_NONE)); PyTuple_SetItem(result, 1, Py_None); PyTuple_SetItem(result, 2, Py_None); return result; } /****************************************************************************** * * * Paramètres : self = classe assurant le lien avec l'éditeur de messages. * * args = arguments fournis à l'appel. * * * * Description : Exécute une action relative à un débogueur. * * * * Retour : True en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ static PyObject *pychrysa_plugin_handle_debugger(PyObject *self, PyObject *args) { Py_RETURN_TRUE; } /****************************************************************************** * * * Paramètres : self = classe assurant le lien avec l'éditeur de messages. * * args = arguments fournis à l'appel. * * * * Description : Exécute une action valide pour le greffon Python. * * * * Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ static PyObject *pychrysa_plugin_run(PyObject *self, PyObject *args) { return Py_None; } #include static int convert_to_w(PyGObject *obj, void **out) { //if (!pygobject_check(obj, *out = pygobject_get(obj); return (1); } static PyObject *add_wgt(PyObject *self, PyObject *args) { GtkWidget *result; GtkWidget *button; result = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_size_request(result, 400, 300); gtk_window_set_position(GTK_WINDOW(result), GTK_WIN_POS_CENTER); gtk_container_set_border_width(GTK_CONTAINER(result), 4); gtk_window_set_title(GTK_WINDOW(result), _("ChrysalideWWW")); gtk_widget_show (result); if (!PyArg_ParseTuple(args, "O&", convert_to_w, &button)) return Py_None; gtk_container_add(GTK_CONTAINER(result), button); return Py_None; } /****************************************************************************** * * * Paramètres : module = module dont la définition est à compléter. * * * * Description : Ajoute l'objet 'plugin' au module Python. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ bool add_plugin_to_python_module(PyObject *module) { int ret; /* Bilan d'un appel */ static PyMethodDef pychrysa_plugin_methods[] = { { "init", (PyCFunction)pychrysa_plugin_init, METH_VARARGS, "Initialize the plugin." }, { "get_action", (PyCFunction)pychrysa_plugin_get_action, METH_NOARGS, "Register the plugin for given actions." }, { "is_matching", (PyCFunction)pychrysa_plugin_is_matching, METH_VARARGS, "Define if the given file can be handled." }, { "handle_debugger", (PyCFunction)pychrysa_plugin_handle_debugger, METH_VARARGS, "Be notify about debugger attaching or detaching." }, { "run", (PyCFunction)pychrysa_plugin_run, METH_VARARGS, "Run the plugin for a specific action." }, { "add_wgt", (PyCFunction)add_wgt, METH_VARARGS, "Run the plugin for a specific action." }, NULL }; static PyTypeObject pychrysa_plugin_type = { PyObject_HEAD_INIT(NULL) #if PY_VERSION_HEX < 0x03000000 0, /*ob_size*/ #endif "plugin.Plugin", /* tp_name */ sizeof(pychrysa_plugin), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved / tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "PyCHRYSA Plugin objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ pychrysa_plugin_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ (newfunc)pychrysa_plugin_new, /* tp_new */ }; if (PyType_Ready(&pychrysa_plugin_type) < 0) return false; if (!pychrysa_plugin_define_constants(pychrysa_plugin_type.tp_dict)) return false; Py_INCREF(&pychrysa_plugin_type); PyModule_AddObject(module, "Plugin", (PyObject *)&pychrysa_plugin_type); return true; /* FIXME */ }