summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/pychrysa/analysis/Makefile.am3
-rw-r--r--plugins/pychrysa/analysis/binary.c211
-rw-r--r--plugins/pychrysa/analysis/binary.h8
-rw-r--r--plugins/pychrysa/analysis/module.c18
-rw-r--r--plugins/pychrysa/debug/debugger.c11
-rw-r--r--plugins/pychrysa/plugin.c43
-rw-r--r--plugins/pychrysa/pychrysa.c47
-rw-r--r--plugins/pychrysa/pychrysa.h5
-rw-r--r--plugins/python/Makefile.am2
-rw-r--r--plugins/python/androperms/Makefile.am12
-rw-r--r--plugins/python/androperms/__init__.py2
-rw-r--r--plugins/python/androperms/androperms.py41
-rw-r--r--plugins/python/androperms/defs.py53
-rw-r--r--plugins/python/androperms/manifest.py75
-rw-r--r--plugins/python/androperms/parser.py328
-rw-r--r--plugins/python/androperms/reader.py41
-rw-r--r--plugins/python/androperms/stack.py108
-rw-r--r--plugins/python/androperms/string.py82
-rw-r--r--plugins/python/apkfiles/apkfiles.py7
19 files changed, 888 insertions, 209 deletions
diff --git a/plugins/pychrysa/analysis/Makefile.am b/plugins/pychrysa/analysis/Makefile.am
index b18d16d..123279b 100644
--- a/plugins/pychrysa/analysis/Makefile.am
+++ b/plugins/pychrysa/analysis/Makefile.am
@@ -13,7 +13,8 @@ libpychrysaanalysis_la_SOURCES = \
libpychrysaanalysis_la_LDFLAGS =
-INCLUDES = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) -I../../../src
+INCLUDES = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+ -I../../../src
AM_CPPFLAGS =
diff --git a/plugins/pychrysa/analysis/binary.c b/plugins/pychrysa/analysis/binary.c
index d378496..4cfb615 100644
--- a/plugins/pychrysa/analysis/binary.c
+++ b/plugins/pychrysa/analysis/binary.c
@@ -25,41 +25,18 @@
#include "binary.h"
-#include "line.h"
-#include "../format/executable.h"
+#include <pygobject.h>
-
-
-/* Classe 'analysis.binary' pour Python */
-typedef struct _py_binary
-{
- PyObject_HEAD
-
- GOpenidaBinary *binary; /* Référence GLib */
-
-} py_binary;
-
-
-
-
-/* Crée un nouvel objet Python de type 'py_binary'. */
-static PyObject *py_binary_new(PyTypeObject *, PyObject *, PyObject *);
-
+#include "../quirks.h"
-/* Fournit le format de fichier reconnu dans le contenu binaire. */
-static PyObject *py_binary_get_format(py_binary *);
-
-/* Fournit les lignes de rendu associé pour Python. */
-static PyObject *py_binary_get_lines(py_binary *);
-
-
-
-/* Fournit le type d'objet 'analysis.binary' pour Python. */
-static PyTypeObject *get_analysis_binary_type(void);
+/* Crée un nouvel objet Python de type 'LoadedBinary'. */
+static PyObject *py_loaded_binary_new(PyTypeObject *, PyObject *, PyObject *);
+/* Fournit le fichier correspondant à l'élément binaire. */
+static PyObject *py_loaded_binary_get_filename(PyObject *self, PyObject *args);
@@ -69,19 +46,28 @@ static PyTypeObject *get_analysis_binary_type(void);
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
* *
-* Description : Crée un nouvel objet Python de type 'py_binary'. *
+* Description : Crée un nouvel objet Python de type 'LoadedBinary'. *
* *
-* Retour : - *
+* Retour : Instance Python mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static PyObject *py_loaded_binary_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
- py_binary *result; /* Instance à retourner */
+ PyObject *result; /* Instance à retourner */
+ char *filename; /* Nom du fichier à charger */
+ int ret; /* Bilan de lecture des args. */
+ GOpenidaBinary *binary; /* Version GLib du format */
- result = (py_binary *)type->tp_alloc(type, 0);
+ ret = PyArg_ParseTuple(args, "s", &filename);
+ if (!ret) return Py_None;
+
+ binary = g_openida_binary_new_from_file(filename);
+
+ result = py_loaded_binary_from_c(binary);
+ g_object_unref(binary);
return (PyObject *)result;
@@ -90,38 +76,28 @@ static PyObject *py_binary_new(PyTypeObject *type, PyObject *args, PyObject *kwd
/******************************************************************************
* *
-* Paramètres : binary = objet GLib existant à transposer. *
+* Paramètres : binary = instance existante GLib. *
* *
-* Description : Crée un nouvel objet Python de type 'py_binary'. *
+* Description : Crée un nouvel objet Python de type 'LoadedBinary'. *
* *
-* Retour : - *
+* Retour : Instance Python mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
-PyObject *py_binary_new_from_existing(GOpenidaBinary *binary)
+PyObject *py_loaded_binary_from_c(GOpenidaBinary *binary)
{
- py_binary *result; /* Instance à retourner */
- PyTypeObject *type; /* Type Python à instancier */
-
- result = (py_binary *)g_object_get_data(G_OBJECT(binary), "python_object");
-
- if (result == NULL)
- {
- type = get_analysis_binary_type();
+ PyObject *module; /* Module d'appartenance */
+ PyTypeObject *type; /* Type Python correspondant */
- result = (py_binary *)type->tp_alloc(type, 0);
+ module = PyImport_ImportModule("pychrysalide.analysis");
+ type = (PyTypeObject*)PyObject_GetAttrString(module, "LoadedBinary");
+ Py_DECREF(module);
- result->binary = binary;
- g_object_ref(binary);
+ pychrysalide_set_instance_data(G_OBJECT(binary), type);
- g_object_set_data(G_OBJECT(binary), "python_object", result);
-
- }
- else Py_INCREF((PyObject *)result);
-
- return (PyObject *)result;
+ return pygobject_new(G_OBJECT(binary));
}
@@ -129,52 +105,32 @@ PyObject *py_binary_new_from_existing(GOpenidaBinary *binary)
-/******************************************************************************
-* *
-* Paramètres : self = instance manipulée à traiter. *
-* *
-* Description : Fournit le format de fichier reconnu dans le contenu binaire.*
-* *
-* Retour : Nouvelle instance d'objet Python ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_binary_get_format(py_binary *self)
-{
- PyObject *result; /* Liste à retourner */
- GExeFormat *format; /* Format récupéré à convertir */
-
- format = g_openida_binary_get_format(self->binary);
-
- result = py_executable_convert(format);
-
- return result;
-
-}
/******************************************************************************
* *
-* Paramètres : self = instance manipulée à traiter. *
+* Paramètres : self = classe représentant un débogueur. *
+* args = arguments fournis à l'appel. *
* *
-* Description : Fournit les lignes de rendu associé pour Python. *
+* Description : Fournit le fichier correspondant à l'élément binaire. *
* *
-* Retour : Nouvelle instance d'objet Python ou NULL en cas d'échec. *
+* Retour : Nom de fichier avec chemin absolu. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_binary_get_lines(py_binary *self)
+static PyObject *py_loaded_binary_get_filename(PyObject *self, PyObject *args)
{
- PyObject *result; /* Liste à retourner */
- GRenderingLine *lines; /* Liste récupérée à convertir */
+ PyObject *result; /* Trouvailles à retourner */
+ GOpenidaBinary *binary; /* Version native */
+ const char *filename; /* Fichier associé au binaire */
+
+ binary = G_OPENIDA_BINARY(pygobject_get(self));
- lines = g_openida_binary_get_lines(self->binary);
+ filename = g_openida_binary_get_filename(binary);
- result = py_line_new_from_existing(lines);
+ result = PyString_FromString(filename);
return result;
@@ -183,96 +139,59 @@ static PyObject *py_binary_get_lines(py_binary *self)
-
-
-
-
-
-
-
-
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : module = module dont la définition est à compléter. *
* *
-* Description : Fournit le type d'objet 'analysis.binary' pour Python. *
+* Description : Prend en charge l'objet 'pychrysalide.analysis.LoadedBinary'.*
* *
-* Retour : Adresse du type vivant à manipuler. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyTypeObject *get_analysis_binary_type(void)
+bool register_python_loaded_binary(PyObject *module)
{
- static PyTypeObject *result = NULL; /* Type pour objet à retourner */
+ PyObject *pygobj_mod; /* Module Python-GObject */
+ int ret; /* Bilan d'un appel */
- static PyMethodDef py_binary_methods[] = {
+ static PyMethodDef py_loaded_binary_methods[] = {
{
- "get_format", (PyCFunction)py_binary_get_format,
+ "get_filename", (PyCFunction)py_loaded_binary_get_filename,
METH_NOARGS,
- "Give the file format recognized in the binary content."
+ "Provide the filename of the loaded binary."
},
- {
- "get_lines", (PyCFunction)py_binary_get_lines,
- METH_NOARGS,
- "Provide the rendering lines used by the binary."
- },
- { NULL }
- };
-
- static PyGetSetDef py_binary_getset[] = {
{ NULL }
};
- static PyTypeObject py_binary_type = {
+ static PyTypeObject py_loaded_binary_type = {
PyObject_HEAD_INIT(NULL)
- .tp_name = "pyoida.analysis.Binary",
- .tp_basicsize = sizeof(py_binary),
+ .tp_name = "pychrysalide.analysis.LoadedBinary",
+ .tp_basicsize = sizeof(PyGObject),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .tp_doc = "PyOIDA loaded binary to analyse",
+ .tp_doc = "PyChrysalide loaded binary",
- .tp_methods = py_binary_methods,
- .tp_getset = py_binary_getset,
- .tp_new = (newfunc)py_binary_new
+ .tp_methods = py_loaded_binary_methods,
+ .tp_new = (newfunc)py_loaded_binary_new
};
- if (result == NULL) result = &py_binary_type;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : module = module dont la définition est à compléter. *
-* *
-* Description : Ajoute l'objet 'analysis.binary' au module Python. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool add_analysis_binary_to_python_module(PyObject *module)
-{
- PyTypeObject *py_binary_type; /* Type défini pour Python */
- int ret; /* Bilan d'un appel */
+ pygobj_mod = PyImport_ImportModule("gobject");
+ if (pygobj_mod == NULL) return false;
- py_binary_type = get_analysis_binary_type();
+ py_loaded_binary_type.tp_base = (PyTypeObject *)PyObject_GetAttrString(pygobj_mod, "GObject");
+ Py_DECREF(pygobj_mod);
- if (PyType_Ready(py_binary_type) < 0)
+ if (PyType_Ready(&py_loaded_binary_type) < 0)
return false;
- Py_INCREF(py_binary_type);
- ret = PyModule_AddObject(module, "Binary", (PyObject *)py_binary_type);
+ Py_INCREF(&py_loaded_binary_type);
+ ret = PyModule_AddObject(module, "LoadedBinary", (PyObject *)&py_loaded_binary_type);
return (ret == 0);
diff --git a/plugins/pychrysa/analysis/binary.h b/plugins/pychrysa/analysis/binary.h
index 1500a06..cc96a5b 100644
--- a/plugins/pychrysa/analysis/binary.h
+++ b/plugins/pychrysa/analysis/binary.h
@@ -33,11 +33,11 @@
-/* Crée un nouvel objet Python de type 'py_binary'. */
-PyObject *py_binary_new_from_existing(GOpenidaBinary *);
+/* Crée un nouvel objet Python de type 'LoadedBinary'. */
+PyObject *py_loaded_binary_from_c(GOpenidaBinary *);
-/* Ajoute l'objet 'analysis.binary' au module Python. */
-bool add_analysis_binary_to_python_module(PyObject *);
+/* Prend en charge l'objet 'pychrysalide.analysis.LoadedBinary'. */
+bool register_python_loaded_binary(PyObject *);
diff --git a/plugins/pychrysa/analysis/module.c b/plugins/pychrysa/analysis/module.c
index 8f07002..42b888e 100644
--- a/plugins/pychrysa/analysis/module.c
+++ b/plugins/pychrysa/analysis/module.c
@@ -2,7 +2,7 @@
/* OpenIDA - Outil d'analyse de fichiers binaires
* module.c - intégration du répertoire analysis en tant que module
*
- * Copyright (C) 2010 Cyrille Bagard
+ * Copyright (C) 2010-2012 Cyrille Bagard
*
* This file is part of OpenIDA.
*
@@ -26,11 +26,6 @@
#include "binary.h"
-#include "exporter.h"
-#include "line.h"
-#include "roptions.h"
-
-
@@ -56,20 +51,15 @@ bool add_analysis_module_to_python_module(PyObject *super)
{ NULL }
};
- module = Py_InitModule("pyoida.analysis", py_analysis_methods);
+ module = Py_InitModule("pychrysalide.analysis", py_analysis_methods);
if (module == NULL) return false;
Py_INCREF(module);
- ret = PyModule_AddObject(super, "pyoida.analysis", module);
+ ret = PyModule_AddObject(super, "pychrysalide.analysis", module);
result = (ret != 0);
- result &= add_analysis_binary_to_python_module(module);
- result &= add_analysis_exporter_to_python_module(module);
- result &= add_analysis_line_to_python_module(module);
- result &= add_analysis_line_iter_to_python_module(module);
- result &= add_analysis_line_riter_to_python_module(module);
- result &= add_analysis_roptions_to_python_module(module);
+ result &= register_python_loaded_binary(module);
return result;
diff --git a/plugins/pychrysa/debug/debugger.c b/plugins/pychrysa/debug/debugger.c
index 3e42ab7..c27d30d 100644
--- a/plugins/pychrysa/debug/debugger.c
+++ b/plugins/pychrysa/debug/debugger.c
@@ -33,6 +33,9 @@
+/* Crée un nouvel objet Python de type 'BinaryDebugger'. */
+static PyObject *py_binary_debugger_new(PyTypeObject *, PyObject *, PyObject *);
+
/* Fournit les identifiants de tous les threads actifs. */
static PyObject *py_binary_debugger_list_all_threads(PyObject *, PyObject *);
@@ -48,9 +51,9 @@ static PyObject *py_binary_debugger_get_frames_stack(PyObject *, PyObject *);
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
* *
-* Description : Crée un nouvel objet Python de type 'py_rendering_options'. *
+* Description : Crée un nouvel objet Python de type 'BinaryDebugger'. *
* *
-* Retour : - *
+* Retour : Instance Python mise en place. *
* *
* Remarques : - *
* *
@@ -82,7 +85,7 @@ static PyObject *py_binary_debugger_new(PyTypeObject *type, PyObject *args, PyOb
* *
* Description : Crée un nouvel objet Python de type 'BinaryDebugger'. *
* *
-* Retour : - *
+* Retour : Instance Python mise en place. *
* *
* Remarques : - *
* *
@@ -93,7 +96,7 @@ PyObject *py_binary_debugger_from_c(GBinaryDebugger *debugger)
PyObject *module; /* Module d'appartenance */
PyTypeObject *type; /* Type Python correspondant */
- module = PyImport_ImportModule("pyoida.debug");
+ module = PyImport_ImportModule("pychrysalide.debug");
type = (PyTypeObject*)PyObject_GetAttrString(module, "BinaryDebugger");
Py_DECREF(module);
diff --git a/plugins/pychrysa/plugin.c b/plugins/pychrysa/plugin.c
index b04dd09..9dc0c29 100644
--- a/plugins/pychrysa/plugin.c
+++ b/plugins/pychrysa/plugin.c
@@ -72,7 +72,7 @@ static PluginAction g_python_plugin_get_action(const GPythonPlugin *);
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(GPythonPlugin *, GOpenidaBinary *, PluginAction);
+static bool g_python_plugin_execute_on_binary(GPythonPlugin *, GOpenidaBinary *, PluginAction);
/* Exécute une action relative à un débogueur. */
@@ -247,7 +247,7 @@ static void g_python_plugin_init(GPythonPlugin *plugin)
plugin_parent = G_PLUGIN_MODULE(plugin);
- plugin_parent->exec_on_bin = (execute_action_on_binary_fc)g_python_plugin_execute;
+ 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;
}
@@ -331,6 +331,9 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename)
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)->get_action = g_python_plugin_get_action;
G_PLUGIN_MODULE(result)->is_matching = g_python_plugin_is_matching;
@@ -494,38 +497,25 @@ static MatchingFormatAction g_python_plugin_is_matching(const GPythonPlugin *plu
* *
******************************************************************************/
-static bool g_python_plugin_execute(GPythonPlugin *plugin, GOpenidaBinary *binary, PluginAction action)
+static bool g_python_plugin_execute_on_binary(GPythonPlugin *plugin, GOpenidaBinary *binary, PluginAction action)
{
+ bool result; /* Bilan à remonter */
PyObject *args; /* Arguments pour l'appel */
- PyObject *arg; /* Un des arguments de l'appel */
- PyObject *value; /* Valeur obtenue */
-
-
-
- printf("I am running !!");
-
- args = PyTuple_New(1);
-
+ PyObject *value; /* Valeurs obtenues */
- arg = Py_BuildValue("O&", py_binary_new_from_existing, binary);
- PyTuple_SetItem(args, 0, arg);
+ 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->module, "get_instance", args);
-
- if (value != NULL)
- {
- printf("Result of call: %ld\n", PyLong_AsLong(value));
- Py_DECREF(value);
- }
+ value = run_python_method(plugin->instance, "execute_on_binary", args);
+ result = (value == Py_True);
+ Py_XDECREF(value);
Py_DECREF(args);
-
-
-
-
+ return result;
}
@@ -638,6 +628,9 @@ static bool pychrysa_plugin_define_constants(PyObject *dict)
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;
diff --git a/plugins/pychrysa/pychrysa.c b/plugins/pychrysa/pychrysa.c
index 84400a1..7bb1e00 100644
--- a/plugins/pychrysa/pychrysa.c
+++ b/plugins/pychrysa/pychrysa.c
@@ -26,6 +26,7 @@
#include <dirent.h>
#include <pygobject.h>
+#include <string.h>
#include <config.h>
@@ -62,7 +63,27 @@ static PyMethodDef SpamMethods[] = {
/******************************************************************************
* *
-* Paramètres : ref = espace de référencement global. *
+* Paramètres : - *
+* *
+* Description : Précise le nom associé au greffon. *
+* *
+* Retour : Nom à libérer de la mémoire. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *get_plugin_name(void)
+{
+ return strdup("PyChrysalide");
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance représentant le greffon en chargement. *
+* ref = espace de référencement global. *
* *
* Description : Initialise le greffon permettant l'usage de Python. *
* *
@@ -72,7 +93,7 @@ static PyMethodDef SpamMethods[] = {
* *
******************************************************************************/
-bool init_plugin(GObject *ref)
+bool init_plugin(GPluginModule *plugin, GObject *ref)
{
char *paths; /* Emplacements de greffons */
char *path; /* Chemin à fouiller */
@@ -86,7 +107,7 @@ bool init_plugin(GObject *ref)
- GPluginModule *plugin;
+ GPluginModule *pyplugin;
@@ -133,14 +154,18 @@ bool init_plugin(GObject *ref)
filename = stradd(filename, ".");
filename = stradd(filename, "__init__");
- plugin = g_python_plugin_new(entry.d_name, filename);
+ pyplugin = g_python_plugin_new(entry.d_name, filename);
- if (plugin == NULL)
- printf("No suitable Python plugin found in '%s'\n", filename); /* FIXME : LOG(...) */
+ if (pyplugin == NULL)
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("No suitable Python plugin found in '%s'"),
+ filename);
else
{
- printf("ok pour %s\n", filename);
- add_plugin_to_main_list(plugin);
+ g_plugin_module_log_variadic_message(plugin, LMT_PROCESS,
+ _("Loaded Python plugin '<b>%s</b>' from the '<b>%s</b>' directory"),
+ g_plugin_module_get_name(G_PLUGIN_MODULE(pyplugin)), path);
+ add_plugin_to_main_list(pyplugin);
}
free(filename);
@@ -202,8 +227,8 @@ PyMODINIT_FUNC PyInit_pychrysa(void)
{
static struct PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT,
- "pychrysa",
- "pychrysa_doc",
+ "pychrysalide",
+ "pychrysalide_doc",
-1,
SpamMethods
};
@@ -233,7 +258,7 @@ PyMODINIT_FUNC initpychrysa(void)
pygobject_init(-1, -1, -1);
pychrysalide_init_quirks();
- module = Py_InitModule("pychrysa", SpamMethods);
+ module = Py_InitModule("pychrysalide", SpamMethods);
//add_analysis_roptions_to_python_module(module);
add_analysis_module_to_python_module(module);
diff --git a/plugins/pychrysa/pychrysa.h b/plugins/pychrysa/pychrysa.h
index e4605e5..daa0ccf 100644
--- a/plugins/pychrysa/pychrysa.h
+++ b/plugins/pychrysa/pychrysa.h
@@ -34,8 +34,11 @@
+/* Précise le nom associé au greffon. */
+char *get_plugin_name(void);
+
/* Initialise le greffon permettant l'usage de Python. */
-bool init_plugin(GObject *);
+bool init_plugin(GPluginModule *, GObject *);
/* Indique les opérations offertes par un greffon donné. */
PluginAction get_plugin_action(const GPluginModule *);
diff --git a/plugins/python/Makefile.am b/plugins/python/Makefile.am
index efbb704..3583a21 100644
--- a/plugins/python/Makefile.am
+++ b/plugins/python/Makefile.am
@@ -1,2 +1,2 @@
-SUBDIRS = apkfiles
+SUBDIRS = androperms apkfiles
diff --git a/plugins/python/androperms/Makefile.am b/plugins/python/androperms/Makefile.am
new file mode 100644
index 0000000..3d1755c
--- /dev/null
+++ b/plugins/python/androperms/Makefile.am
@@ -0,0 +1,12 @@
+
+andropermsdir = $(datadir)/openida/plugins/python/androperms
+
+androperms_DATA = \
+ __init__.py \
+ androperms.py \
+ defs.py \
+ manifest.py \
+ parser.py \
+ reader.py \
+ stack.py \
+ string.py
diff --git a/plugins/python/androperms/__init__.py b/plugins/python/androperms/__init__.py
new file mode 100644
index 0000000..8a5a159
--- /dev/null
+++ b/plugins/python/androperms/__init__.py
@@ -0,0 +1,2 @@
+
+from androperms import AndroPerms as androperms
diff --git a/plugins/python/androperms/androperms.py b/plugins/python/androperms/androperms.py
new file mode 100644
index 0000000..f85d402
--- /dev/null
+++ b/plugins/python/androperms/androperms.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from pychrysalide import Plugin
+from manifest import AndroidManifest
+from xml.dom import minidom
+
+import zipfile
+
+
+class AndroPerms(Plugin):
+ """List all permissions given to an APK files."""
+
+
+ def get_action(self):
+ """Register the plugin for given actions."""
+
+ return Plugin.PGA_DISASS_PROCESS
+
+
+ def execute_on_binary(self, binary, action):
+ """Process once a binary is disassembled."""
+
+ zf = zipfile.ZipFile(binary.get_filename())
+
+ f = zf.open('AndroidManifest.xml', 'r')
+ data = f.read()
+ f.closed
+
+ manifest = AndroidManifest(data)
+ xml = minidom.parseString(manifest.getXML())
+
+ print
+ print "Permissions for ", binary.get_filename(), " :"
+ print "-------------"
+ print
+
+ for p in xml.getElementsByTagName("uses-permission"):
+ print p.getAttribute("android:name")
+
+ print
diff --git a/plugins/python/androperms/defs.py b/plugins/python/androperms/defs.py
new file mode 100644
index 0000000..e23511e
--- /dev/null
+++ b/plugins/python/androperms/defs.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+
+ATTRIBUTE_IX_NAMESPACE_URI = 0
+ATTRIBUTE_IX_NAME = 1
+ATTRIBUTE_IX_VALUE_STRING = 2
+ATTRIBUTE_IX_VALUE_TYPE = 3
+ATTRIBUTE_IX_VALUE_DATA = 4
+ATTRIBUTE_LENGHT = 5
+
+TYPE_NULL = 0
+TYPE_REFERENCE = 1
+TYPE_ATTRIBUTE = 2
+TYPE_STRING = 3
+TYPE_FLOAT = 4
+TYPE_DIMENSION = 5
+TYPE_FRACTION = 6
+TYPE_FIRST_INT = 16
+TYPE_INT_DEC = 16
+TYPE_INT_HEX = 17
+TYPE_INT_BOOLEAN = 18
+TYPE_FIRST_COLOR_INT = 28
+TYPE_INT_COLOR_ARGB8 = 28
+TYPE_INT_COLOR_RGB8 = 29
+TYPE_INT_COLOR_ARGB4 = 30
+TYPE_INT_COLOR_RGB4 = 31
+TYPE_LAST_COLOR_INT = 31
+TYPE_LAST_INT = 31
+
+RADIX_MULTS = [ 0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010 ]
+DIMENSION_UNITS = [ "px", "dip", "sp", "pt", "in", "mm", "", "" ]
+FRACTION_UNITS = [ "%", "%p", "", "", "", "", "", "" ]
+COMPLEX_UNIT_MASK = 15
+
+
+CHUNK_AXML_FILE = 0x00080003
+CHUNK_RESOURCEIDS = 0x00080180
+CHUNK_XML_FIRST = 0x00100100
+CHUNK_XML_START_NAMESPACE = 0x00100100
+CHUNK_XML_END_NAMESPACE = 0x00100101
+CHUNK_XML_START_TAG = 0x00100102
+CHUNK_XML_END_TAG = 0x00100103
+CHUNK_XML_TEXT = 0x00100104
+CHUNK_XML_LAST = 0x00100104
+CHUNK_TYPE = 0x001C0001
+
+
+START_DOCUMENT = 0
+END_DOCUMENT = 1
+START_TAG = 2
+END_TAG = 3
+TEXT = 4
diff --git a/plugins/python/androperms/manifest.py b/plugins/python/androperms/manifest.py
new file mode 100644
index 0000000..63536b2
--- /dev/null
+++ b/plugins/python/androperms/manifest.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from defs import *
+from reader import AXMLReader
+from parser import AXMLParser
+
+
+class AndroidManifest():
+
+
+ def __init__(self, data):
+
+ self._buffer = ""
+
+ reader = AXMLReader(data)
+ parser = AXMLParser(reader)
+
+ has_ns = False
+ empty = False
+
+ while 1 :
+
+ tag = parser.next()
+
+ if tag == START_DOCUMENT :
+ self._buffer += '<?xml version="1.0" encoding="utf-8"?>\n'
+
+ elif tag == START_TAG:
+
+ if empty:
+ self._buffer += '>\n'
+
+ self._buffer += ' ' * (parser._namespaces.getDepth() - 2)
+ self._buffer += "<%s%s" % (parser.getTagPrefix(), parser.getTagName())
+
+ if not has_ns:
+ self._buffer += ' xmlns:%s="%s"' % (parser.getNamespacePrefix(0), parser.getNamespaceUri(0))
+ has_ns = True
+
+ for i in range(0, parser.countAttributes()):
+ self._buffer += ' %s%s="%s"' % (parser.getAttribPrefix(i), parser.getAttribName(i), parser.getAttribValue(i))
+
+ empty = True
+
+ elif tag == END_TAG:
+
+ if empty:
+ self._buffer += '/>\n'
+ empty = False
+
+ else:
+ self._buffer += ' ' * (parser._namespaces.getDepth() - 2)
+ self._buffer += "</%s%s>\n" % (parser.getTagPrefix(), parser.getTagName())
+
+ elif tag == TEXT:
+
+ if empty:
+ self._buffer += '>\n'
+ empty = False
+
+ self._buffer += ' ' * (parser._namespaces.getDepth() - 1)
+ self._buffer += "%s\n" % parser.getText()
+
+ elif tag == END_DOCUMENT :
+ break
+
+ else:
+ break
+
+
+ def getXML(self):
+ """Provide the XML content."""
+
+ return self._buffer
diff --git a/plugins/python/androperms/parser.py b/plugins/python/androperms/parser.py
new file mode 100644
index 0000000..1939bbe
--- /dev/null
+++ b/plugins/python/androperms/parser.py
@@ -0,0 +1,328 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+
+from defs import *
+from stack import NamespaceStack
+from string import StringBlock
+
+from struct import unpack
+
+
+class AXMLParser():
+
+
+ def __init__(self, reader):
+
+ self._reader = reader
+
+ magic = reader.readInt()
+ if magic != CHUNK_AXML_FILE:
+ raise Exception('Bad Magic Number (0x%08lx)!' % magic)
+
+ # chunkSize
+ reader.skipInt()
+
+ self._strings = StringBlock(reader)
+ self._namespaces = NamespaceStack()
+ self._operational = True
+
+ self.resetEventInfo()
+
+
+ def resetEventInfo(self):
+
+ self._event = -1
+ self._line_number = -1
+ self._name = -1
+ self._namespace_uri = -1
+ self._attributes = None
+ self._id_attrib = -1
+ self._class_attrib = -1
+ self._style_attrib = -1
+
+ self._decreaseDepth = False
+
+
+
+ def next(self):
+
+ self.doNext()
+ return self._event
+
+
+ def doNext(self):
+
+
+
+
+
+ event = self._event
+
+
+ while True:
+
+ if self._decreaseDepth:
+ self._decreaseDepth = False
+ self._namespaces.decDepth()
+
+ # Fake END_DOCUMENT event.
+ if event == END_TAG and self._namespaces.getDepth() == 1 and self._namespaces.count() == 0:
+ self._event = END_DOCUMENT
+ break
+
+ if event == START_DOCUMENT:
+ # Fake event, see CHUNK_XML_START_TAG handler.
+ chunk = CHUNK_XML_START_TAG
+
+ else:
+ chunk = self._reader.readInt()
+
+ if chunk == CHUNK_RESOURCEIDS:
+
+ size = self._reader.readInt()
+ if size < 8 or size % 4 != 0:
+ raise Exception('Invalid resource ids size (%d).' % size)
+
+ self._resource_ids = self._reader.readIntArray(size / 4 - 2)
+
+ continue
+
+ if chunk < CHUNK_XML_FIRST or chunk > CHUNK_XML_LAST:
+ raise Exception('Invalid chunk type 0x%08lx.' % chunk)
+
+ # Fake START_DOCUMENT event.
+ if chunk == CHUNK_XML_START_TAG and event == -1:
+ self._event = START_DOCUMENT
+ break
+
+ # Common header.
+ self._reader.skipInt() # chunkSize
+ self._line_number = self._reader.readInt()
+ self._reader.skipInt() # 0xffffffff
+
+ if chunk == CHUNK_XML_START_NAMESPACE or chunk == CHUNK_XML_END_NAMESPACE:
+
+ if chunk == CHUNK_XML_START_NAMESPACE:
+
+ prefix = self._reader.readInt()
+ uri = self._reader.readInt()
+ self._namespaces.push(prefix, uri)
+
+ else:
+
+ self._reader.skipInt() # prefix
+ self._reader.skipInt() # uri
+ self._namespaces.pop()
+
+ continue
+
+ elif chunk == CHUNK_XML_START_TAG:
+
+ self._namespace_uri = self._reader.readInt()
+ self._name = self._reader.readInt()
+ self._reader.skipInt() # flags ?
+
+ attribs_count = self._reader.readInt()
+ self._id_attrib = (attribs_count >> 16) - 1
+ attribs_count &= 0xffff
+
+ self._class_attrib = self._reader.readInt()
+ self._style_attrib = (self._class_attrib >> 16) - 1
+ self._class_attrib = (self._class_attrib & 0xffff) - 1
+
+ self._attributes = self._reader.readIntArray(attribs_count * ATTRIBUTE_LENGHT)
+
+ for i in range(ATTRIBUTE_IX_VALUE_TYPE, len(self._attributes), ATTRIBUTE_LENGHT):
+ self._attributes[i] >>= 24
+
+ self._namespaces.incDepth()
+ self._event = START_TAG
+
+ break
+
+ elif chunk == CHUNK_XML_END_TAG:
+
+ self._namespaceUri = self._reader.readInt()
+ self._name = self._reader.readInt()
+
+ self._event = END_TAG
+ self._decreaseDepth = True
+
+ break
+
+ elif chunk == CHUNK_XML_TEXT:
+
+ self._name = self._reader.readInt()
+ self._reader.skipInt() # ???
+ self._reader.skipInt() # ???
+
+ self._event=TEXT
+
+ break
+
+ else:
+ raise Exception('Unknown chunck (0x%08lx)' % chunk)
+
+
+ ### ESPACES ###
+
+ def getNamespacePrefix(self, index):
+
+ name = self._namespaces.getPrefix(index)
+
+ if name == -1:
+ return ''
+
+ else:
+ return self._strings.getRaw(name)
+
+
+ def getNamespaceUri(self, index):
+
+ name = self._namespaces.getUri(index)
+
+ if name == -1:
+ return ''
+
+ else:
+ return self._strings.getRaw(name)
+
+
+ ### NAMES ###
+
+ def getTagPrefix(self):
+ """Provide the prefix linked to START_TAG or END_TAG."""
+
+ name = self._namespaces.findPrefix(self._namespace_uri)
+
+ if name == -1:
+ return ''
+
+ else:
+ return self._strings.getRaw(name) + ':'
+
+
+ def getTagName(self):
+ """Provide the name linked to START_TAG or END_TAG."""
+
+ if self._name == -1 or (self._event != START_TAG and self._event != END_TAG):
+ raise Exception('Invalid tag name.')
+
+ return self._strings.getRaw(self._name)
+
+
+ def getText(self):
+ """Provide the content linked to TEXT."""
+
+ if self._name == -1 or self._event != START_TEXT:
+ raise Exception('Invalid text content.')
+
+ return self._strings.getRaw(self._name)
+
+
+ ### ATRIBUTES ###
+
+ def countAttributes(self):
+ """Count the properties of the current tag."""
+
+ if self._event != START_TAG:
+ raise Exception('Invalid event.')
+
+ return len(self._attributes) / ATTRIBUTE_LENGHT
+
+
+ def getAttribPrefix(self, index):
+ """Get the prefix of a given attribute."""
+
+ index *= ATTRIBUTE_LENGHT
+
+ if index >= len(self._attributes):
+ raise Exception('Bad attribute index.')
+
+ uri = self._attributes[index + ATTRIBUTE_IX_NAMESPACE_URI]
+ name = self._namespaces.findPrefix(uri)
+
+ if name == -1:
+ return ''
+
+ else:
+ return self._strings.getRaw(name) + ':'
+
+
+ def getAttribName(self, index):
+ """Get the name of a given attribute."""
+
+ index *= ATTRIBUTE_LENGHT
+
+ if index >= len(self._attributes):
+ raise Exception('Bad attribute index.')
+
+ name = self._attributes[index + ATTRIBUTE_IX_NAME]
+
+ if name == -1:
+ return '???'
+
+ else:
+ return self._strings.getRaw(name)
+
+
+ def getAttribValue(self, index):
+ """Get the value of a given attribute."""
+
+ index *= ATTRIBUTE_LENGHT
+
+ if index >= len(self._attributes):
+ raise Exception('Bad attribute index.')
+
+ vtype = self._attributes[index + ATTRIBUTE_IX_VALUE_TYPE]
+ vdata = self._attributes[index + ATTRIBUTE_IX_VALUE_DATA]
+
+ if vtype == TYPE_NULL:
+ return '???'
+
+ elif vtype == TYPE_REFERENCE:
+ return '@%s%08X' % (self.getPackage(vdata), vdata)
+
+ elif vtype == TYPE_ATTRIBUTE:
+ return '?%s%08X' % (self.getPackage(vdata), vdata)
+
+ if vtype == TYPE_STRING:
+ vdata = self._attributes[index + ATTRIBUTE_IX_VALUE_STRING]
+ return self._strings.getRaw(vdata)
+
+ elif vtype == TYPE_FLOAT:
+ return '%f' % unpack('=f', pack('=L', vdata))[0]
+
+ elif vtype == TYPE_DIMENSION:
+ return '%f%s' % (self.complexToFloat(vdata), DIMENSION_UNITS[vdata & COMPLEX_UNIT_MASK])
+
+ elif vtype == TYPE_FRACTION:
+ return '%f%s' % (self.complexToFloat(vdata), FRACTION_UNITS[vdata & COMPLEX_UNIT_MASK])
+
+ elif vtype == TYPE_INT_HEX:
+ return '0x%08x' % vdata
+
+ elif vtype == TYPE_INT_BOOLEAN:
+ if vdata == 0:
+ return 'false'
+ else:
+ return 'true'
+
+ elif vtype >= TYPE_FIRST_COLOR_INT and vtype <= TYPE_LAST_COLOR_INT:
+ return '#%08x' % vdata
+
+ elif vtype >= TYPE_FIRST_INT and vtype <= TYPE_LAST_INT:
+ return str(vdata)
+
+ return "<0x%x, 0x%02x>" % (vdata, vtype)
+
+
+ def complexToFloat(self, xcomplex):
+ return (float)(xcomplex & 0xffffff00) * RADIX_MULTS[(xcomplex >> 4) & 3];
+
+ def getPackage(self, id):
+ if id >> 24 == 1:
+ return "android:"
+ else:
+ return ""
diff --git a/plugins/python/androperms/reader.py b/plugins/python/androperms/reader.py
new file mode 100644
index 0000000..f126850
--- /dev/null
+++ b/plugins/python/androperms/reader.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+
+from struct import unpack
+
+
+class AXMLReader():
+ """Provide various read helpers."""
+
+ def __init__(self, data):
+
+ self._data = data
+
+ self._position = 0
+ self._length = len(self._data)
+
+
+ def readInt(self):
+ """Read a 4-bytes value."""
+
+ self.skipInt()
+
+ value = unpack('<L', self._data[self._position - 4 : self._position])[0]
+
+ return value
+
+
+ def skipInt(self):
+ """Skip a 4-bytes value."""
+
+ self._position += 4
+
+ if self._position > self._length:
+ raise Exception("Reader out of bound (%d > %d)!" % (self._position, self._length))
+
+
+ def readIntArray(self, length):
+ """Read an array composed of 4-bytes values."""
+
+ return [ self.readInt() for i in range(0, length) ]
diff --git a/plugins/python/androperms/stack.py b/plugins/python/androperms/stack.py
new file mode 100644
index 0000000..38431dc
--- /dev/null
+++ b/plugins/python/androperms/stack.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+
+class NamespaceStack():
+
+
+ def __init__(self):
+
+ # self.increaseDepth()
+
+
+ self._depth = 1
+
+ self._prefix_2_uri = {}
+ self._uri_2_prefix = {}
+
+ self._pairs = []
+
+ pass
+
+
+
+ def getDepth(self):
+
+ return self._depth
+
+
+ def incDepth(self):
+
+ self._depth += 1
+
+
+ def decDepth(self):
+
+ self._depth -= 1
+
+
+
+
+
+ def count(self):
+ """Provider the current number of active namespaces."""
+
+ return len(self._pairs)
+
+
+
+
+
+
+
+ def push(self, prefix, uri):
+
+ self._prefix_2_uri[prefix] = uri
+ self._uri_2_prefix[uri] = prefix
+
+ self._pairs.append((prefix, uri))
+
+ #print "PUSH", prefix, uri
+
+
+
+
+ def pop(self):
+
+ self._pairs.pop()
+
+ #print "POP"
+
+
+
+
+
+ def getPrefix(self, index):
+
+ if index < len(self._pairs):
+ return self._pairs[index][0]
+
+ else:
+ return -1
+
+
+ def findPrefix(self, uri):
+
+ if uri in self._uri_2_prefix:
+ return self._uri_2_prefix[uri]
+
+ else:
+ return -1
+
+
+ def getUri(self, index):
+
+ if index < len(self._pairs):
+ return self._pairs[index][1]
+
+ else:
+ return -1
+
+
+ def findUri(self, prefix):
+
+ if prefix in self._prefix_2_uri:
+ return self._prefix_2_uri[prefix]
+
+ else:
+ return -1
diff --git a/plugins/python/androperms/string.py b/plugins/python/androperms/string.py
new file mode 100644
index 0000000..09a7b93
--- /dev/null
+++ b/plugins/python/androperms/string.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python -u
+# -*- coding: utf-8 -*-
+
+
+from defs import CHUNK_TYPE
+
+
+class StringBlock():
+
+
+ def __init__(self, reader):
+
+ magic = reader.readInt()
+ if magic != CHUNK_TYPE:
+ raise Exception("Bad Magic Number (0x%08lx)!" % magic)
+
+ chunk_size = reader.readInt()
+ str_count = reader.readInt()
+ style_offset_count = reader.readInt()
+ reader.readInt() # ???
+ str_offset = reader.readInt()
+ styles_offset = reader.readInt()
+
+ self._str_offsets = reader.readIntArray(str_count);
+ self._style_offsets = reader.readIntArray(style_offset_count);
+
+ if styles_offset == 0:
+ size = chunk_size - str_offset
+ else:
+ size = styles_offset - str_offset
+
+ if size % 4 != 0:
+ raise Exception("String data size is not multiple of 4 (%d)!" % size)
+
+ self._strings = reader.readIntArray(size / 4)
+
+ if styles_offset > 0:
+
+ size = chunk_size - styles_offset
+
+ if size % 4 != 0:
+ raise Exception("Style data size is not multiple of 4 (%d)!" % size)
+
+ self._styles = reader.readIntArray(size / 4)
+
+ self._str_data = [ self.getRaw(i) for i in range(self.count()) ]
+
+
+ def count(self):
+ """Count the number of strings in the current block."""
+
+ return len(self._str_offsets)
+
+
+ def getRaw(self, index):
+ """Provide a raw string (without any styling information) at specified index."""
+
+ if index < 0 or index >= len(self._str_offsets):
+ raise Exception("Invalid Index (%d)!" % index)
+
+ offset = self._str_offsets[index]
+ length = self.getShort(self._strings, offset)
+
+ data = ''
+
+ for i in range(length):
+ offset += 2
+ data += unichr(self.getShort(self._strings, offset))
+
+ return data
+
+
+ def getShort(self, array, offset):
+
+ value = array[offset / 4]
+
+ if ((offset % 4) / 2) == 0:
+ value &= 0xFFFF
+ else:
+ value >>= 16
+
+ return value
diff --git a/plugins/python/apkfiles/apkfiles.py b/plugins/python/apkfiles/apkfiles.py
index 38d0e59..7c05ca9 100644
--- a/plugins/python/apkfiles/apkfiles.py
+++ b/plugins/python/apkfiles/apkfiles.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-from pychrysa import Plugin
+from pychrysalide import Plugin
import zipfile
@@ -9,11 +9,13 @@ import zipfile
class ApkFiles(Plugin):
"""Open and process APK files."""
+
def get_action(self):
"""Register the plugin for given actions."""
return Plugin.PGA_FORMAT_MATCHER
+
def is_matching(self, filename, data):
"""Define if the given file can be handled."""
@@ -22,7 +24,8 @@ class ApkFiles(Plugin):
zf = zipfile.ZipFile(filename)
- if zf.namelist().count('classes.dex') > 0:
+ if zf.namelist().count('classes.dex') > 0 \
+ and zf.namelist().count('AndroidManifest.xml') > 0:
f = zf.open('classes.dex', 'r')
data = f.read()