summaryrefslogtreecommitdiff
path: root/plugins/pychrysalide/pychrysa.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/pychrysalide/pychrysa.c')
-rw-r--r--plugins/pychrysalide/pychrysa.c616
1 files changed, 616 insertions, 0 deletions
diff --git a/plugins/pychrysalide/pychrysa.c b/plugins/pychrysalide/pychrysa.c
new file mode 100644
index 0000000..35d14f4
--- /dev/null
+++ b/plugins/pychrysalide/pychrysa.c
@@ -0,0 +1,616 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * pychrysa.c - plugin permettant des extensions en 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "pychrysa.h"
+
+
+#include <assert.h>
+#include <errno.h>
+#include <pygobject.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#include <config.h>
+#include <common/cpp.h>
+#include <common/environment.h>
+#include <common/extstr.h>
+#include <core/core.h>
+#include <plugins/pglist.h>
+#include <plugins/plugin-def.h>
+#include <plugins/plugin-int.h>
+
+
+#include "helpers.h"
+#include "plugin.h"
+#include "struct.h"
+#include "analysis/module.h"
+#include "arch/module.h"
+#include "common/module.h"
+#include "core/module.h"
+#include "debug/module.h"
+#include "format/module.h"
+#include "glibext/module.h"
+#include "gtkext/module.h"
+#include "gui/module.h"
+
+
+
+DEFINE_CHRYSALIDE_ACTIVE_PLUGIN("PyChrysalide", "Provides bindings to Python", "0.1.0", PGA_PLUGIN_INIT);
+
+
+/* Note la nature du chargement */
+static bool _standalone = true;
+
+/* Réceptacle pour le chargement forcé */
+static PyObject *_chrysalide_module = NULL;
+
+
+/* Fournit la révision du programme global. */
+static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
+
+/* Fournit la version du programme global. */
+static PyObject *py_chrysalide_version(PyObject *, PyObject *);
+
+/* Fournit la version du greffon pour Python. */
+static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
+
+/* Détermine si l'interpréteur lancé est celui pris en compte. */
+static bool is_current_abi_suitable(void);
+
+/* Définit la version attendue de GTK à charger dans Python. */
+static bool set_version_for_gtk_namespace(const char *);
+
+/* Charge autant de greffons composés en Python que possible. */
+static bool load_python_plugins(GPluginModule *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la révision du programme global. *
+* *
+* Retour : Numéro de révision. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args)
+{
+ return PyUnicode_FromString("r" XSTR(REVISION));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la version du programme global. *
+* *
+* Retour : Numéro de version. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_version(PyObject *self, PyObject *args)
+{
+ char version[16];
+ int major;
+ int minor;
+ int revision;
+
+ major = REVISION / 1000;
+ minor = (REVISION - (major * 1000)) / 100;
+ revision = REVISION % 100;
+
+ snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
+
+ return PyUnicode_FromString(version);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la version du greffon pour Python. *
+* *
+* Retour : Numéro de version. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
+{
+ char version[16];
+
+ snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version);
+
+ return PyUnicode_FromString(version);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Détermine si l'interpréteur lancé est celui pris en compte. *
+* *
+* Retour : true si l'exécution peut se poursuivre, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool is_current_abi_suitable(void)
+{
+ bool result;
+ int fds[2];
+ int ret;
+ char cmds[128];
+ char content[64];
+ ssize_t got;
+
+#define GRAB_ABI_FLAGS_IN_PYTHON \
+ "import sys" "\n" \
+ "import os" "\n" \
+ "os.write(%d, bytes(sys.abiflags, 'UTF-8'))" "\n"
+
+ result = false;
+
+ ret = pipe(fds);
+ if (ret == -1)
+ {
+ perror("pipe()");
+ return false;
+ }
+
+ snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
+
+ ret = PyRun_SimpleString(cmds);
+ if (ret != 0) goto icas_exit;
+
+ got = read(fds[0], content, sizeof(content));
+ if (got < 0)
+ {
+ perror("read()");
+ goto icas_exit;
+ }
+
+ content[got] = '\0';
+
+ result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
+
+ icas_exit:
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
+ "the ones of the Python library used during the module compilation.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : version = idenfiant de la version de GTK à stipuler. *
+* *
+* Description : Définit la version attendue de GTK à charger dans Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool set_version_for_gtk_namespace(const char *version)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *gi_mod; /* Module Python-GObject */
+ PyObject *args; /* Arguments à fournir */
+
+ result = false;
+
+ /**
+ * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement :
+ *
+ *
+ * PyGIWarning: Gtk was imported without specifying a version first. \
+ * Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded.
+ *
+ */
+
+ gi_mod = PyImport_ImportModule("gi");
+
+ if (gi_mod != NULL)
+ {
+ args = Py_BuildValue("ss", "Gtk", "3.0");
+
+ run_python_method(gi_mod, "require_version", args);
+
+ result = (PyErr_Occurred() == NULL);
+
+ Py_DECREF(args);
+ Py_DECREF(gi_mod);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Point d'entrée pour l'initialisation de Python. *
+* *
+* Retour : ? *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+#include <gtkext/support.h> // REMME
+#include <glibext/delayed.h>
+
+#define PYCHRYSALIDE_DOC \
+ "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" \
+ "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
+ "\n" \
+ "In both cases, it is a good start point to have a look at already existing plugins to quickly learn " \
+ "how the API works.\n" \
+ "\n" \
+ "These plugins are located in the 'plugins/python' directory."
+
+PyMODINIT_FUNC PyInit_pychrysalide(void)
+{
+ PyObject *result; /* Module Python à retourner */
+ bool status; /* Bilan des inclusions */
+ GPluginModule *self; /* Représentation interne */
+ PluginStatusFlags self_flags; /* Fanions à mettre à jour */
+
+ static PyMethodDef py_chrysalide_methods[] = {
+
+ { "revision", py_chrysalide_revision,
+ METH_NOARGS,
+ "revision(/)\n--\n\nProvide the revision number of Chrysalide."
+ },
+ { "version", py_chrysalide_version,
+ METH_NOARGS,
+ "version(/)\n--\n\nProvide the version number of Chrysalide."
+ },
+ { "mod_version", py_chrysalide_mod_version,
+ METH_NOARGS,
+ "mod_version(/)\n--\n\nProvide the version number of Chrysalide module for Python."
+ },
+ { NULL }
+
+ };
+
+ static PyModuleDef py_chrysalide_module = {
+
+ .m_base = PyModuleDef_HEAD_INIT,
+
+ .m_name = "pychrysalide",
+ .m_doc = PYCHRYSALIDE_DOC,
+
+ .m_size = -1,
+
+ .m_methods = py_chrysalide_methods
+
+ };
+
+#if 0
+ do
+ {
+ int argc = 0;
+ //char *argv[] = { NULL };
+
+ /* Initialisation de GTK */
+ g_set_prgname("Chrysalide");
+ setlocale (LC_ALL, "");
+ gtk_init(&argc, NULL/*&argv-*/);
+
+ } while (0);
+
+ add_pixmap_directory(PACKAGE_DATA_DIR);
+ add_pixmap_directory(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "pixmaps");
+
+ /**
+ * On évite d'initialiser deux fois...
+ */
+ if (get_work_queue() != NULL)
+ init_work_queue(NULL/* !! */);
+#endif
+
+
+
+
+
+ if (!is_current_abi_suitable())
+ return NULL;
+
+ if (pygobject_init(-1, -1, -1) == NULL)
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
+ return NULL;
+ }
+
+ if (!set_version_for_gtk_namespace("3.0"))
+ return NULL;
+
+ if (!load_all_basic_components())
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to load all basic components.");
+ return NULL;
+ }
+
+ /* Mise en place des fonctionnalités offertes */
+
+ result = PyModule_Create(&py_chrysalide_module);
+
+ status = register_python_py_struct(result);
+
+ /* Interface 'LineGenerator' en premier... */
+ if (status) status = add_glibext_module_to_python_module(result);
+
+ /* BinRoutine hérite de BinSymbol... */
+ if (status) status = add_format_module_to_python_module(result);
+
+ if (status) status = register_python_plugin_module(result);
+ if (status) status = add_analysis_module_to_python_module(result);
+ if (status) status = add_arch_module_to_python_module(result);
+ if (status) status = add_common_module_to_python_module(result);
+ if (status) status = add_core_module_to_python_module(result);
+ if (status) status = add_debug_module_to_python_module(result);
+ if (status) status = add_gtkext_module_to_python_module(result);
+ if (status) status = add_gui_module_to_python_module(result);
+
+ if (!status)
+ {
+ PyErr_SetString(PyExc_SystemError, "fail to load all PyChrysalide components.");
+ return NULL;
+ }
+
+ if (_standalone)
+ {
+ init_all_plugins(false);
+
+ lock_plugin_list_for_reading();
+
+ self = get_plugin_by_name("PyChrysalide", NULL);
+ assert(self != NULL);
+
+ self_flags = g_plugin_module_get_flags(self);
+ self_flags &= ~(PSF_FAILURE | PSF_LOADED);
+ self_flags |= (status ? PSF_LOADED : PSF_FAILURE);
+
+ g_plugin_module_override_flags(self, self_flags);
+
+ unlock_plugin_list_for_reading();
+
+ load_remaning_plugins();
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance représentant le greffon Python d'origine. *
+* *
+* Description : Charge autant de greffons composés en Python que possible. *
+* *
+* Retour : true. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool load_python_plugins(GPluginModule *plugin)
+{
+ char *paths; /* Emplacements de greffons */
+ char *save; /* Sauvegarde pour ré-entrance */
+ char *path; /* Chemin à fouiller */
+ DIR *dir; /* Répertoire à parcourir */
+ struct dirent *entry; /* Elément trouvé */
+ char *modname; /* Nom du module pour Python */
+ char *filename; /* Chemin d'accès reconstruit */
+ GPluginModule *pyplugin; /* Lien vers un grffon Python */
+
+ paths = get_env_var("PYTHONPATH");
+
+ save = NULL; /* gcc... */
+
+ for (path = strtok_r(paths, ":", &save);
+ path != NULL;
+ path = strtok_r(NULL, ":", &save))
+ {
+ dir = opendir(path);
+ if (dir == NULL)
+ {
+ perror("opendir");
+ continue;
+ }
+
+ g_plugin_module_log_variadic_message(plugin, LMT_INFO,
+ _("Looking for Python plugins in '%s'..."),
+ path);
+
+ while (1)
+ {
+ errno = 0;
+
+ entry = readdir(dir);
+
+ if (entry == NULL)
+ {
+ if (errno != 0)
+ perror("readdir");
+
+ break;
+
+ }
+
+ if (entry->d_type != DT_DIR) continue;
+ if (entry->d_name[0] == '.') continue;
+
+ modname = strdup(entry->d_name);
+ modname = stradd(modname, ".");
+ modname = stradd(modname, "__init__");
+
+ filename = strdup(path);
+ filename = stradd(filename, G_DIR_SEPARATOR_S);
+ filename = stradd(filename, entry->d_name);
+
+ pyplugin = g_python_plugin_new(modname, filename);
+
+ if (pyplugin == NULL)
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("No suitable Python plugin found in '%s'"),
+ filename);
+ else
+ {
+ g_plugin_module_log_variadic_message(plugin, LMT_PROCESS,
+ _("Loaded the Python plugin found in the '<b>%s</b>' directory"),
+ filename);
+ _register_plugin(pyplugin);
+ }
+
+ free(filename);
+ free(modname);
+
+ }
+
+ closedir(dir);
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte du chargement du greffon. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
+{
+ bool result; /* Bilan à retourner */
+ DIR *dir; /* Répertoire à parcourir */
+ int ret; /* Bilan de préparatifs */
+
+ /* Définition des zones d'influence */
+
+ dir = opendir(PLUGINS_DIR G_DIR_SEPARATOR_S "python");
+
+ if (dir != NULL)
+ {
+ closedir(dir);
+ add_to_env_var("PYTHONPATH", PLUGINS_DIR G_DIR_SEPARATOR_S "python", ":");
+ }
+ else
+ add_to_env_var("PYTHONPATH", PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" \
+ G_DIR_SEPARATOR_S "python", ":");
+
+ g_plugin_module_log_variadic_message(plugin, LMT_INFO,
+ _("PYTHONPATH environment variable set to '%s'"),
+ getenv("PYTHONPATH"));
+
+ /* Chargement du module pour Python */
+
+ _standalone = false;
+
+ ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
+
+ if (ret == -1)
+ {
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("Can not extend the existing table of Python built-in modules."));
+ result = false;
+ goto cpi_done;
+ }
+
+ Py_Initialize();
+
+ PySys_SetArgv(0, (wchar_t *[]) { NULL });
+
+ _chrysalide_module = PyImport_ImportModule("pychrysalide");
+
+ result = load_python_plugins(plugin);
+
+ cpi_done:
+
+ return result;
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte du déchargement du greffon. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
+{
+ Py_XDECREF(_chrysalide_module);
+
+}