From 545d0490f6fbb397da66410f534670c52bfcc5da Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 25 Nov 2015 23:26:53 +0000
Subject: Implemented restricted contents and created test cases.

git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@608 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
---
 ChangeLog                                       |  46 ++
 plugins/pychrysa/analysis/content.c             |  27 +-
 plugins/pychrysa/analysis/contents/Makefile.am  |   3 +-
 plugins/pychrysa/analysis/contents/module.c     |   3 +-
 plugins/pychrysa/analysis/contents/restricted.c | 227 +++++++++
 plugins/pychrysa/analysis/contents/restricted.h |  42 ++
 src/analysis/contents/Makefile.am               |   3 +-
 src/analysis/contents/restricted.c              | 613 ++++++++++++++++++++++++
 src/analysis/contents/restricted.h              |  58 +++
 tests/analysis/__init__.py                      |   0
 tests/analysis/contents/__init__.py             |   0
 tests/analysis/contents/restricted.py           | 141 ++++++
 tests/arch/__init__.py                          |   0
 tests/arch/vmpa.py                              |  35 +-
 tests/chrysacase.py                             |  48 ++
 tests/format/__init__.py                        |   0
 tests/format/elf/Makefile                       |   5 +-
 tests/format/elf/__init__.py                    |   0
 tests/format/elf/non_existing_binary.py         |  20 +-
 tests/format/elf/oob_section_name.py            |  36 +-
 tests/run.sh                                    |   8 +
 tests/test.py                                   |  39 --
 22 files changed, 1271 insertions(+), 83 deletions(-)
 create mode 100644 plugins/pychrysa/analysis/contents/restricted.c
 create mode 100644 plugins/pychrysa/analysis/contents/restricted.h
 create mode 100644 src/analysis/contents/restricted.c
 create mode 100644 src/analysis/contents/restricted.h
 create mode 100644 tests/analysis/__init__.py
 create mode 100644 tests/analysis/contents/__init__.py
 create mode 100644 tests/analysis/contents/restricted.py
 create mode 100644 tests/arch/__init__.py
 create mode 100644 tests/chrysacase.py
 create mode 100644 tests/format/__init__.py
 create mode 100644 tests/format/elf/__init__.py
 create mode 100755 tests/run.sh
 delete mode 100644 tests/test.py

diff --git a/ChangeLog b/ChangeLog
index d67f749..e6b8c54 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,49 @@
+15-11-26  Cyrille Bagard <nocbos@gmail.com>
+
+	* plugins/pychrysa/analysis/content.c:
+	Set description strings for errors as needed.
+
+	* plugins/pychrysa/analysis/contents/Makefile.am:
+	Add the new 'restricted.[ch]' files to
+	libpychrysaanalysiscontents_la_SOURCES.
+
+	* plugins/pychrysa/analysis/contents/module.c:
+	Update code.
+
+	* plugins/pychrysa/analysis/contents/restricted.c:
+	* plugins/pychrysa/analysis/contents/restricted.h:
+	New entries: implement restricted contents for Python.
+
+	* src/analysis/contents/Makefile.am:
+	Add the new 'restricted.[ch]' files to libanalysiscontents_la_SOURCES.
+
+	* src/analysis/contents/restricted.c:
+	* src/analysis/contents/restricted.h:
+	New entries: implement restricted contents.
+
+	* tests/analysis/contents/__init__.py:
+	* tests/analysis/contents/restricted.py:
+	* tests/analysis/__init__.py:
+	* tests/arch/__init__.py:
+	New entries: create test cases for a Python test suite.
+
+	* tests/arch/vmpa.py:
+	Update code.
+
+	* tests/chrysacase.py:
+	* tests/format/elf/__init__.py:
+
+	* tests/format/elf/Makefile:
+	* tests/format/elf/non_existing_binary.py:
+	* tests/format/elf/oob_section_name.py:
+	Update code.
+
+	* tests/format/__init__.py:
+	* tests/run.sh:
+
+	* tests/test.py:
+	Deleted entry.
+
 15-11-12  Cyrille Bagard <nocbos@gmail.com>
 
 	* plugins/ropgadgets/select.c:
diff --git a/plugins/pychrysa/analysis/content.c b/plugins/pychrysa/analysis/content.c
index bd25589..00265e3 100644
--- a/plugins/pychrysa/analysis/content.c
+++ b/plugins/pychrysa/analysis/content.c
@@ -29,6 +29,9 @@
 #include <pygobject.h>
 
 
+#include <i18n.h>
+
+
 #include <analysis/content.h>
 #include <common/endianness.h>
 
@@ -154,7 +157,11 @@ static PyObject *py_binary_content_read_u8(PyObject *self, PyObject *args)
     assert(addr != NULL);
 
     status = g_binary_content_read_u8(content, addr, &val);
-    if (!status) return NULL;
+    if (!status)
+    {
+        PyErr_SetString(PyExc_Exception, _("Invalid read access."));
+        return NULL;
+    }
 
     result = PyBytes_FromStringAndSize((char *)&val, 1);
 
@@ -197,7 +204,11 @@ static PyObject *py_binary_content_read_u16(PyObject *self, PyObject *args)
     assert(addr != NULL);
 
     status = g_binary_content_read_u16(content, addr, endianness, &val);
-    if (!status) return NULL;
+    if (!status)
+    {
+        PyErr_SetString(PyExc_Exception, _("Invalid read access."));
+        return NULL;
+    }
 
     result = PyBytes_FromStringAndSize((char *)&val, 2);
 
@@ -239,7 +250,11 @@ static PyObject *py_binary_content_read_u32(PyObject *self, PyObject *args)
     assert(addr != NULL);
 
     status = g_binary_content_read_u32(content, addr, endianness, &val);
-    if (!status) return NULL;
+    if (!status)
+    {
+        PyErr_SetString(PyExc_Exception, _("Invalid read access."));
+        return NULL;
+    }
 
     result = PyBytes_FromStringAndSize((char *)&val, 4);
 
@@ -281,7 +296,11 @@ static PyObject *py_binary_content_read_u64(PyObject *self, PyObject *args)
     assert(addr != NULL);
 
     status = g_binary_content_read_u64(content, addr, endianness, &val);
-    if (!status) return NULL;
+    if (!status)
+    {
+        PyErr_SetString(PyExc_Exception, _("Invalid read access."));
+        return NULL;
+    }
 
     result = PyBytes_FromStringAndSize((char *)&val, 8);
 
diff --git a/plugins/pychrysa/analysis/contents/Makefile.am b/plugins/pychrysa/analysis/contents/Makefile.am
index ff835e3..3cd00a6 100644
--- a/plugins/pychrysa/analysis/contents/Makefile.am
+++ b/plugins/pychrysa/analysis/contents/Makefile.am
@@ -3,7 +3,8 @@ noinst_LTLIBRARIES = libpychrysaanalysiscontents.la
 
 libpychrysaanalysiscontents_la_SOURCES = \
 	file.h file.c						\
-	module.h module.c
+	module.h module.c					\
+	restricted.h restricted.c
 
 libpychrysaanalysiscontents_la_LDFLAGS = 
 
diff --git a/plugins/pychrysa/analysis/contents/module.c b/plugins/pychrysa/analysis/contents/module.c
index f97ba27..366a158 100644
--- a/plugins/pychrysa/analysis/contents/module.c
+++ b/plugins/pychrysa/analysis/contents/module.c
@@ -29,6 +29,7 @@
 
 
 #include "file.h"
+#include "restricted.h"
 
 
 
@@ -80,8 +81,8 @@ bool add_analysis_contents_module_to_python_module(PyObject *super)
 
     result &= register_python_binary_content(module);
 
-
     result &= register_python_file_content(module);
+    result &= register_python_restricted_content(module);
 
  loading_failed:
 
diff --git a/plugins/pychrysa/analysis/contents/restricted.c b/plugins/pychrysa/analysis/contents/restricted.c
new file mode 100644
index 0000000..0b8c7da
--- /dev/null
+++ b/plugins/pychrysa/analysis/contents/restricted.c
@@ -0,0 +1,227 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * restricted.c - prototypes pour l'équivalent Python du fichier "analysis/contents/restricted.c"
+ *
+ * Copyright (C) 2015 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  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 "restricted.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+
+
+#include <analysis/contents/restricted.h>
+
+
+#include "../content.h"
+#include "../../arch/vmpa.h"
+
+
+
+/* Crée un nouvel objet Python de type 'BinContent'. */
+static PyObject *py_restricted_content_new(PyTypeObject *, PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type de l'objet à instancier.                         *
+*                args = arguments fournis à l'appel.                          *
+*                kwds = arguments de type key=val fournis.                    *
+*                                                                             *
+*  Description : Crée un nouvel objet Python de type 'BinContent'.            *
+*                                                                             *
+*  Retour      : Instance Python mise en place.                               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_restricted_content_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    PyObject *content_obj;                  /* Objet pour le contenu       */
+    PyObject *range_obj;                    /* Objet pour la restriction   */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBinContent *content;                   /* Instance GLib correspondante*/
+    mrange_t *range;                        /* Restriction à appliquer     */
+    GBinContent *restricted;                /* Création GLib à transmettre */
+
+    ret = PyArg_ParseTuple(args, "OO", &content_obj, &range_obj);
+    if (!ret) return NULL;
+
+    ret = PyObject_IsInstance(content_obj, (PyObject *)get_python_binary_content_type());
+    if (!ret)
+    {
+        PyErr_SetString(PyExc_TypeError, _("The first argument must be an instance of BinContent."));
+        return NULL;
+    }
+
+    ret = PyObject_IsInstance(range_obj, (PyObject *)get_python_mrange_type());
+    if (!ret)
+    {
+        PyErr_SetString(PyExc_TypeError, _("The second argument must be an instance of mrange."));
+        return NULL;
+    }
+
+    content = G_BIN_CONTENT(pygobject_get(content_obj));
+
+    range = get_internal_mrange(range_obj);
+
+    restricted = g_restricted_content_new(content, range);
+
+    result = pygobject_new(G_OBJECT(restricted));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Fournit un accès à une définition de type à diffuser.        *
+*                                                                             *
+*  Retour      : Définition d'objet pour Python.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+
+
+PyMethodDef py_restricted_content_methods[] = {
+    { NULL }
+};
+
+PyGetSetDef py_restricted_content_getseters[] = {
+    { NULL }
+};
+
+PyTypeObject py_restricted_content_type = {
+
+    PyVarObject_HEAD_INIT(NULL, 0)
+
+    .tp_name        = "pychrysalide.analysis.contents.RestrictedContent",
+    .tp_basicsize   = sizeof(PyGObject),
+
+    .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+    .tp_doc         = "PyChrysalide binary restricted content",
+
+    /*
+      .tp_methods     = py_restricted_content_methods,
+      .tp_getset      = py_restricted_content_getseters,
+      .tp_new         = (newfunc)py_restricted_content_new
+    */
+    .tp_new         = (newfunc)py_restricted_content_new
+
+};
+
+
+
+PyTypeObject *get_python_restricted_content_type(void)
+{
+#if 0
+    static PyMethodDef py_restricted_content_methods[] = {
+        { NULL }
+    };
+
+    static PyGetSetDef py_restricted_content_getseters[] = {
+        { NULL }
+    };
+
+    static PyTypeObject py_restricted_content_type = {
+
+        PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name        = "pychrysalide.analysis.contents.RestrictedContent",
+        .tp_basicsize   = sizeof(PyGObject),
+
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | 1 << 9,
+
+        .tp_doc         = "PyChrysalide binary restricted content",
+
+        /*
+        .tp_methods     = py_restricted_content_methods,
+        .tp_getset      = py_restricted_content_getseters,
+        .tp_new         = (newfunc)py_restricted_content_new
+        */
+
+    };
+#endif
+    return &py_restricted_content_type;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : module = module dont la définition est à compléter.          *
+*                                                                             *
+*  Description : Prend en charge l'objet 'pychrysalide.glibext.BinContent'.   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+#include "../content.h"//////////////////////////////////////////
+bool register_python_restricted_content(PyObject *module)
+{
+    PyTypeObject *py_restricted_content_type;   /* Type Python 'BinContent'    */
+    int ret;                                /* Bilan d'un appel            */
+    PyObject *dict;                         /* Dictionnaire du module      */
+
+    py_restricted_content_type = get_python_restricted_content_type();
+
+    //py_restricted_content_type->tp_base = &PyGObject_Type;
+    //py_restricted_content_type->tp_basicsize = py_restricted_content_type->tp_base->tp_basicsize;
+
+    /*
+    if (PyType_Ready(py_restricted_content_type) != 0)
+        return false;
+    */
+
+    /*
+    Py_INCREF(py_restricted_content_type);
+    ret = PyModule_AddObject(module, "RestrictedContent", (PyObject *)py_restricted_content_type);
+    if (ret != 0) return false;
+    */
+
+    dict = PyModule_GetDict(module);
+    pygobject_register_class(dict, "RestrictedContent", G_TYPE_RESTRICTED_CONTENT, py_restricted_content_type,
+                             Py_BuildValue("(O)", &PyGObject_Type/*py_restricted_content_type->tp_base*/,
+                                   get_python_binary_content_type()));
+
+    /*
+    if (PyType_Ready(py_restricted_content_type) != 0)
+        return false;
+    */
+
+
+    return true;
+
+}
diff --git a/plugins/pychrysa/analysis/contents/restricted.h b/plugins/pychrysa/analysis/contents/restricted.h
new file mode 100644
index 0000000..24f2697
--- /dev/null
+++ b/plugins/pychrysa/analysis/contents/restricted.h
@@ -0,0 +1,42 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * restricted.h - prototypes pour l'équivalent Python du fichier "analysis/contents/restricted.h"
+ *
+ * Copyright (C) 2015 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  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
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSA_ANALYSIS_CONTENTS_RESTRICTED_H
+#define _PLUGINS_PYCHRYSA_ANALYSIS_CONTENTS_RESTRICTED_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_restricted_content_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.BinContent'. */
+bool register_python_restricted_content(PyObject *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSA_ANALYSIS_CONTENTS_RESTRICTED_H */
diff --git a/src/analysis/contents/Makefile.am b/src/analysis/contents/Makefile.am
index e2eec74..2580357 100755
--- a/src/analysis/contents/Makefile.am
+++ b/src/analysis/contents/Makefile.am
@@ -2,7 +2,8 @@
 noinst_LTLIBRARIES  = libanalysiscontents.la
 
 libanalysiscontents_la_SOURCES =		\
-	file.h file.c
+	file.h file.c						\
+	restricted.h restricted.c
 
 libanalysiscontents_la_LIBADD =	
 
diff --git a/src/analysis/contents/restricted.c b/src/analysis/contents/restricted.c
new file mode 100644
index 0000000..a8f1763
--- /dev/null
+++ b/src/analysis/contents/restricted.c
@@ -0,0 +1,613 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * restricted.c - chargement de données binaires à partir d'un contenu restreint
+ *
+ * Copyright (C) 2015 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "restricted.h"
+
+
+#include <assert.h>
+#include <string.h>
+
+
+#include "../content-int.h"
+
+
+
+/* Contenu de données binaires issues d'un contenu restreint (instance) */
+struct _GRestrictedContent
+{
+    GObject parent;                         /* A laisser en premier        */
+
+    GBinContent *internal;                  /* Contenu de sous-traitance   */
+
+    mrange_t range;                         /* Restriction de couverture   */
+
+};
+
+/* Contenu de données binaires issues d'un contenu restreint (classe) */
+struct _GRestrictedContentClass
+{
+    GObjectClass parent;                    /* A laisser en premier        */
+
+};
+
+
+/* Initialise la classe des contenus de données binaires. */
+static void g_restricted_content_class_init(GRestrictedContentClass *);
+
+/* Initialise une instance de contenu de données binaires. */
+static void g_restricted_content_init(GRestrictedContent *);
+
+/* Procède à l'initialisation de l'interface de lecture. */
+static void g_restricted_content_interface_init(GBinContentInterface *);
+
+/* Supprime toutes les références externes. */
+static void g_restricted_content_dispose(GRestrictedContent *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_restricted_content_finalize(GRestrictedContent *);
+
+/* Donne accès à une portion des données représentées. */
+static const bin_t *g_restricted_content_get_raw_access(const GRestrictedContent *, vmpa2t *, phys_t);
+
+/* Fournit une portion des données représentées. */
+static bool g_restricted_content_read_raw(const GRestrictedContent *, vmpa2t *, phys_t, bin_t *);
+
+/* Lit un nombre non signé sur quatre bits. */
+static bool g_restricted_content_read_u4(const GRestrictedContent *, vmpa2t *, bool *, uint8_t *);
+
+/* Lit un nombre non signé sur un octet. */
+static bool g_restricted_content_read_u8(const GRestrictedContent *, vmpa2t *, uint8_t *);
+
+/* Lit un nombre non signé sur deux octets. */
+static bool g_restricted_content_read_u16(const GRestrictedContent *, vmpa2t *, SourceEndian, uint16_t *);
+
+/* Lit un nombre non signé sur quatre octets. */
+static bool g_restricted_content_read_u32(const GRestrictedContent *, vmpa2t *, SourceEndian, uint32_t *);
+
+/* Lit un nombre non signé sur huit octets. */
+static bool g_restricted_content_read_u64(const GRestrictedContent *, vmpa2t *, SourceEndian, uint64_t *);
+
+/* Lit un nombre non signé encodé au format LEB128. */
+static bool g_restricted_content_read_uleb128(const GRestrictedContent *, vmpa2t *, uleb128_t *);
+
+/* Lit un nombre signé encodé au format LEB128. */
+static bool g_restricted_content_read_leb128(const GRestrictedContent *, vmpa2t *, leb128_t *);
+
+
+
+/* Indique le type défini par la GLib pour les contenus de données. */
+G_DEFINE_TYPE_WITH_CODE(GRestrictedContent, g_restricted_content, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(G_TYPE_BIN_CONTENT, g_restricted_content_interface_init))
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des contenus de données binaires.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_restricted_content_class_init(GRestrictedContentClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_restricted_content_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_restricted_content_finalize;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = instance à initialiser.                            *
+*                                                                             *
+*  Description : Initialise une instance de contenu de données binaires.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_restricted_content_init(GRestrictedContent *content)
+{
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : iface = interface GLib à initialiser.                        *
+*                                                                             *
+*  Description : Procède à l'initialisation de l'interface de lecture.        *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_restricted_content_interface_init(GBinContentInterface *iface)
+{
+    iface->get_raw_access = (get_raw_access_fc)g_restricted_content_get_raw_access;
+
+    iface->read_raw = (read_raw_fc)g_restricted_content_read_raw;
+    iface->read_u4 = (read_u4_fc)g_restricted_content_read_u4;
+    iface->read_u8 = (read_u8_fc)g_restricted_content_read_u8;
+    iface->read_u16 = (read_u16_fc)g_restricted_content_read_u16;
+    iface->read_u32 = (read_u32_fc)g_restricted_content_read_u32;
+    iface->read_u64 = (read_u64_fc)g_restricted_content_read_u64;
+
+    iface->read_uleb128 = (read_uleb128_fc)g_restricted_content_read_uleb128;
+    iface->read_leb128 = (read_leb128_fc)g_restricted_content_read_leb128;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = instance d'objet GLib à traiter.                   *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_restricted_content_dispose(GRestrictedContent *content)
+{
+    g_object_unref(G_OBJECT(content->internal));
+
+    G_OBJECT_CLASS(g_restricted_content_parent_class)->dispose(G_OBJECT(content));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = instance d'objet GLib à traiter.                   *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_restricted_content_finalize(GRestrictedContent *content)
+{
+    G_OBJECT_CLASS(g_restricted_content_parent_class)->finalize(G_OBJECT(content));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire où puiser les données à fournir.   *
+*                range   = espace de restrictions pour les accès.             *
+*                                                                             *
+*  Description : Charge en mémoire le contenu d'un contenu restreint.         *
+*                                                                             *
+*  Retour      : Représentation de contenu à manipuler ou NULL en cas d'échec.*
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GBinContent *g_restricted_content_new(GBinContent *content, const mrange_t *range)
+{
+    GRestrictedContent *result;              /* Structure à retourner      */
+
+    result = g_object_new(G_TYPE_RESTRICTED_CONTENT, NULL);
+
+    result->internal = content;
+    g_object_ref(G_OBJECT(result->internal));
+
+    copy_mrange(&result->range, range);
+
+    return G_BIN_CONTENT(result);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                length  = quantité d'octets à lire.                          *
+*                                                                             *
+*  Description : Donne accès à une portion des données représentées.          *
+*                                                                             *
+*  Retour      : Pointeur vers les données à lire ou NULL en cas d'échec.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static const bin_t *g_restricted_content_get_raw_access(const GRestrictedContent *content, vmpa2t *addr, phys_t length)
+{
+    const bin_t *result;                    /* Données utiles à renvoyer   */
+    mrange_t requested;                     /* Espace demandé en lecture   */
+
+    init_mrange(&requested, addr, length);
+
+    if (!mrange_contains_mrange(&content->range, &requested))
+    {
+        result = NULL;
+        goto bad_range;
+    }
+
+    result = g_binary_content_get_raw_access(content->internal, addr, length);
+
+ bad_range:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                length  = quantité d'octets à lire.                          *
+*                out     = réceptacle disponible pour ces données. [OUT]      *
+*                                                                             *
+*  Description : Fournit une portion des données représentées.                *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_raw(const GRestrictedContent *content, vmpa2t *addr, phys_t length, bin_t *out)
+{
+    bool result;                            /* Bilan à remonter            */
+    const bin_t *data;                      /* Pointeur vers données utiles*/
+
+    data = g_restricted_content_get_raw_access(content, addr, length);
+
+    if (data != NULL)
+    {
+        result = true;
+        memcpy(out, data, length);
+    }
+    else
+        result = false;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                low     = position éventuelle des 4 bits visés. [OUT]        *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre non signé sur quatre bits.                     *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_u4(const GRestrictedContent *content, vmpa2t *addr, bool *low, uint8_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+    bool old_low;                           /* Côté de l'octet traité      */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+    old_low = *low;
+
+    result = g_binary_content_read_u4(content->internal, addr, low, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        *low = old_low;
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre non signé sur un octet.                        *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_u8(const GRestrictedContent *content, vmpa2t *addr, uint8_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+
+    result = g_binary_content_read_u8(content->internal, addr, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                endian  = ordre des bits dans la source.                     *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre non signé sur deux octets.                     *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_u16(const GRestrictedContent *content, vmpa2t *addr, SourceEndian endian, uint16_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+
+    result = g_binary_content_read_u16(content->internal, addr, endian, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                endian  = ordre des bits dans la source.                     *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre non signé sur quatre octets.                   *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_u32(const GRestrictedContent *content, vmpa2t *addr, SourceEndian endian, uint32_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+
+    result = g_binary_content_read_u32(content->internal, addr, endian, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                endian  = ordre des bits dans la source.                     *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre non signé sur huit octets.                     *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_u64(const GRestrictedContent *content, vmpa2t *addr, SourceEndian endian, uint64_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+
+    result = g_binary_content_read_u64(content->internal, addr, endian, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre non signé encodé au format LEB128.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_uleb128(const GRestrictedContent *content, vmpa2t *addr, uleb128_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+
+    result = g_binary_content_read_uleb128(content->internal, addr, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à venir lire.                      *
+*                addr    = position de la tête de lecture.                    *
+*                val     = lieu d'enregistrement de la lecture. [OUT]         *
+*                                                                             *
+*  Description : Lit un nombre signé encodé au format LEB128.                 *
+*                                                                             *
+*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_restricted_content_read_leb128(const GRestrictedContent *content, vmpa2t *addr, leb128_t *val)
+{
+    bool result;                            /* Bilan de lecture à renvoyer */
+    vmpa2t old;                             /* Copie de sauvegarde         */
+
+    if (!mrange_contains_addr(&content->range, addr))
+    {
+        result = false;
+        goto bad_range;
+    }
+
+    copy_vmpa(&old, addr);
+
+    result = g_binary_content_read_leb128(content->internal, addr, val);
+
+    if (result && !mrange_contains_addr_inclusive(&content->range, addr))
+    {
+        copy_vmpa(addr, &old);
+        result = false;
+    }
+
+ bad_range:
+
+    return result;
+
+}
diff --git a/src/analysis/contents/restricted.h b/src/analysis/contents/restricted.h
new file mode 100644
index 0000000..3cf5d60
--- /dev/null
+++ b/src/analysis/contents/restricted.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * restricted.h - prototypes pour le chargement de données binaires à partir d'un contenu restreint
+ *
+ * Copyright (C) 2015 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _ANALYSIS_CONTENTS_RESTRICTED_H
+#define _ANALYSIS_CONTENTS_RESTRICTED_H
+
+
+#include <glib-object.h>
+
+
+#include "../content.h"
+
+
+
+#define G_TYPE_RESTRICTED_CONTENT             (g_restricted_content_get_type())
+#define G_RESTRICTED_CONTENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RESTRICTED_CONTENT, GRestrictedContent))
+#define G_IS_RESTRICTED_CONTENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RESTRICTED_CONTENT))
+#define G_RESTRICTED_CONTENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RESTRICTED_CONTENT, GRestrictedContentClass))
+#define G_IS_RESTRICTED_CONTENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RESTRICTED_CONTENT))
+#define G_RESTRICTED_CONTENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RESTRICTED_CONTENT, GRestrictedContentClass))
+
+
+/* Contenu de données binaires issues d'un contenu restreint (instance) */
+typedef struct _GRestrictedContent GRestrictedContent;
+
+/* Contenu de données binaires issues d'un contenu restreint (classe) */
+typedef struct _GRestrictedContentClass GRestrictedContentClass;
+
+
+/* Indique le type défini par la GLib pour les contenus de données. */
+GType g_restricted_content_get_type(void);
+
+/* Charge en mémoire le contenu d'un contenu restreint. */
+GBinContent *g_restricted_content_new(GBinContent *, const mrange_t *);
+
+
+
+#endif  /* _ANALYSIS_CONTENTS_RESTRICTED_H */
diff --git a/tests/analysis/__init__.py b/tests/analysis/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/analysis/contents/__init__.py b/tests/analysis/contents/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/analysis/contents/restricted.py b/tests/analysis/contents/restricted.py
new file mode 100644
index 0000000..e8d3e07
--- /dev/null
+++ b/tests/analysis/contents/restricted.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python3-dbg
+# -*- coding: utf-8 -*-
+
+
+# Tests minimalistes pour valider l'intégration des contenus restreints
+# depuis Python.
+
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.analysis.contents import FileContent, RestrictedContent
+from pychrysalide.arch import vmpa, mrange
+import tempfile
+
+
+class TestRestrictedContent(ChrysalideTestCase):
+    """TestCase for analysis.contents.RestrictedContent."""
+
+    @classmethod
+    def setUpClass(cls):
+
+        super(TestRestrictedContent, cls).setUpClass()
+
+        cls._out = tempfile.NamedTemporaryFile()
+
+        cls._out.write(b'\x01\x02\x03\x04')
+        cls._out.write(b'\x05\x06\x07\x08')
+        cls._out.write(b'\x11\x12\x13\x14')
+        cls._out.write(b'\x15\x16\x17\x18')
+        cls._out.write(b'\x21\x22\x23\x24')
+        cls._out.write(b'\x25\x26\x27\x28')
+        cls._out.write(b'\x31\x32\x33\x34')
+        cls._out.write(b'\x35\x36\x37\x38')
+
+        cls._out.flush()
+
+        cls.log('Using temporary file "%s"' % cls._out.name)
+
+
+    @classmethod
+    def tearDownClass(cls):
+
+        super(TestRestrictedContent, cls).tearDownClass()
+
+        cls.log('Delete file "%s"' % cls._out.name)
+
+        cls._out.close()
+
+
+    def testReadAccess(self):
+        """Check valid accesses to restricted content."""
+
+        fcnt = FileContent(self._out.name)
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        covered = mrange(start, 12) # 0x15 ... 0x28
+
+        rcnt = RestrictedContent(fcnt, covered)
+        self.assertIsNotNone(rcnt)
+
+        val = rcnt.read_u8(start)
+        self.assertEqual(val, b'\x15')
+
+        val = rcnt.read_u8(start)
+        self.assertEqual(val, b'\x16')
+
+        val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x17\x18')
+
+        val = rcnt.read_u32(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x21\x22\x23\x24')
+
+
+    def testBorderLineAccess(self):
+        """Check valid border line accesses to restricted content."""
+
+        fcnt = FileContent(self._out.name)
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        covered = mrange(start, 12) # 0x15 ... 0x28
+
+        rcnt = RestrictedContent(fcnt, covered)
+        self.assertIsNotNone(rcnt)
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u8(start)
+        self.assertEqual(val, b'\x15')
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x15\x16')
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u32(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x15\x16\x17\x18')
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u64(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x15\x16\x17\x18\x21\x22\x23\x24')
+
+        start = vmpa(23, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u8(start)
+        self.assertEqual(val, b'\x28')
+
+        start = vmpa(22, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x27\x28')
+
+        start = vmpa(20, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u32(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x25\x26\x27\x28')
+
+        start = vmpa(16, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_u64(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, b'\x21\x22\x23\x24\x25\x26\x27\x28')
+
+
+    def testWrongAccess(self):
+        """Check invalid accesses to restricted content."""
+
+        fcnt = FileContent(self._out.name)
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        covered = mrange(start, 12) # 0x15 ... 0x28
+
+        rcnt = RestrictedContent(fcnt, covered)
+        self.assertIsNotNone(rcnt)
+
+        with self.assertRaisesRegex(Exception, 'Invalid read access.'):
+
+            start = vmpa(1, vmpa.VMPA_NO_VIRTUAL)
+            val = rcnt.read_u8(start)
+
+        with self.assertRaisesRegex(Exception, 'Invalid read access.'):
+
+            start = vmpa(11, vmpa.VMPA_NO_VIRTUAL)
+            val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
+
+        with self.assertRaisesRegex(Exception, 'Invalid read access.'):
+
+            start = vmpa(23, vmpa.VMPA_NO_VIRTUAL)
+            val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
diff --git a/tests/arch/__init__.py b/tests/arch/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/arch/vmpa.py b/tests/arch/vmpa.py
index 9814b20..def61ea 100755
--- a/tests/arch/vmpa.py
+++ b/tests/arch/vmpa.py
@@ -1,32 +1,35 @@
 #!/usr/bin/python3-dbg
 # -*- coding: utf-8 -*-
 
-import pychrysalide
-from pychrysalide.arch import vmpa
-from test import TestSuite
+
+# Tests minimalistes pour valider l'intégration des adresses et espaces mémoire
+# depuis Python.
 
 
-########################
+from chrysacase import ChrysalideTestCase
+from pychrysalide.arch import vmpa
 
-TestSuite.print_sep()
 
-addr = vmpa()
+class TestVmpa(ChrysalideTestCase):
+    """TestCase for arch.vmpa."""
 
-print('repr():', repr(addr))
-print('str(): ', str(addr))
+    def testInit(self):
+        """VMPA values are left uninitialized by default."""
 
-########################
+        v = vmpa()
 
-TestSuite.print_sep()
+        self.assertIsNone(v.phys)
+        self.assertIsNone(v.virt)
 
-TestSuite.check_true('Create a virtual memory or physical address', lambda: vmpa())
 
-v = vmpa()
+    def testAdd(self):
+        """Verify the commutative property of addition."""
 
-TestSuite.check_true('VMPA values are left uninitialized by default', lambda: v.phy == None and v.virt == None)
+        a = vmpa(0, 0) + 1
 
-a = vmpa(0, 0) + 1
+        b = 1 + vmpa(0, 0)
 
-b = 1 + vmpa(0, 0)
+        c = vmpa(1, 1)
 
-TestSuite.check_true('Verify the commutative property of addition', lambda: a == b)
+        self.assertEqual(a, b)
+        self.assertEqual(a, c)
diff --git a/tests/chrysacase.py b/tests/chrysacase.py
new file mode 100644
index 0000000..26625e7
--- /dev/null
+++ b/tests/chrysacase.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python3-dbg
+# -*- coding: utf-8 -*-
+
+
+import unittest
+import os
+import sys
+
+
+class ChrysalideTestCase(unittest.TestCase):
+    """Base class for all Chrysalide test cases."""
+
+    @classmethod
+    def setUpClass(cls):
+
+        fullname = sys.modules[ChrysalideTestCase.__module__].__file__
+        filename = os.path.basename(fullname)
+
+        cls._baselen = len(fullname) - len(filename)
+
+
+    def shortDescription(self):
+        """Discard the direct use of __doc__ strings."""
+
+        return None
+
+
+    def __str__(self):
+        """Display the description of the current tested case."""
+
+        fullname = sys.modules[self.__class__.__module__].__file__
+
+        origin = fullname[self._baselen:]
+
+        title = self._testMethodDoc and self._testMethodDoc or self._testMethodName
+
+        return '[%s] "%s"' % (origin, title)
+
+
+    @classmethod
+    def log(cls, msg):
+        """Display an information message."""
+
+        fullname = sys.modules[cls.__module__].__file__
+
+        origin = fullname[cls._baselen:]
+
+        print('[%s] *** %s ***' % (origin, msg))
diff --git a/tests/format/__init__.py b/tests/format/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/format/elf/Makefile b/tests/format/elf/Makefile
index b14ff47..c32392f 100644
--- a/tests/format/elf/Makefile
+++ b/tests/format/elf/Makefile
@@ -1,11 +1,8 @@
 
-EXECUTABLES=tiny oob_section_name
+EXECUTABLES=oob_section_name
 
 all: $(EXECUTABLES)
 
-tiny: tiny.o
-	$(ARM_CROSS)objcopy $< -O binary $@
-
 oob_section_name: oob_section_name.o
 	$(ARM_CROSS)objcopy $< -O binary $@
 
diff --git a/tests/format/elf/__init__.py b/tests/format/elf/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/format/elf/non_existing_binary.py b/tests/format/elf/non_existing_binary.py
index 47c9028..6111f03 100644
--- a/tests/format/elf/non_existing_binary.py
+++ b/tests/format/elf/non_existing_binary.py
@@ -5,22 +5,20 @@
 # Eprouve quelques mécanismes de construction côté Python.
 
 
-import pychrysalide
-
+from chrysacase import ChrysalideTestCase
 from pychrysalide.analysis.contents import FileContent
 from pychrysalide.format.elf import ElfFormat
 
-cnt = FileContent("non_existing_binary")
 
-print(cnt)
+class TestNonExistingBinary(ChrysalideTestCase):
+    """TestCase for non existent binary loading."""
 
-print(cnt == None)
+    def testNonExistent(self):
+        """Try to load a non existent binary without crashing."""
 
-try:
-    fmt = ElfFormat(cnt)
-except TypeError as e:
-    fmt = None
+        cnt = FileContent('non_existing_binary')
+        self.assertIsNone(cnt)
 
-print(fmt)
+        with self.assertRaisesRegex(TypeError, 'The argument must be an instance of BinContent.'):
 
-print(fmt == None)
+            fmt = ElfFormat(cnt)
diff --git a/tests/format/elf/oob_section_name.py b/tests/format/elf/oob_section_name.py
index da58e29..8f91efd 100644
--- a/tests/format/elf/oob_section_name.py
+++ b/tests/format/elf/oob_section_name.py
@@ -10,15 +10,39 @@
 # lors de l'accès concret, au moment de l'appel à strlen().
 
 
-import pychrysalide
-
+from chrysacase import ChrysalideTestCase
 from pychrysalide.analysis.contents import FileContent
 from pychrysalide.format.elf import ElfFormat
+import os
+import sys
+
+
+class TestNonExistingBinary(ChrysalideTestCase):
+    """TestCase for corrupted ELF binaries with wrong section names."""
+
+    @classmethod
+    def setUpClass(cls):
+
+        super(TestNonExistingBinary, cls).setUpClass()
+
+        cls.log('Compile binary "oob_section_name" if needed...')
+
+        fullname = sys.modules[cls.__module__].__file__
+        dirpath = os.path.dirname(fullname)
+
+        os.system('make -C %s oob_section_name 2>&1 > /dev/null' % dirpath)
+
+
+    def testOOBSectionName(self):
+        """Avoid crashing when dealing with OutOfBound section names."""
 
-cnt = FileContent("oob_section_name")
+        fullname = sys.modules[self.__class__.__module__].__file__
+        filename = os.path.basename(fullname)
 
-fmt = ElfFormat(cnt)
+        baselen = len(fullname) - len(filename)
 
-print(fmt)
+        cnt = FileContent(fullname[:baselen] + 'oob_section_name')
+        self.assertIsNotNone(cnt)
 
-print(isinstance(fmt, ElfFormat))
+        fmt = ElfFormat(cnt)
+        self.assertIsInstance(fmt, ElfFormat)
diff --git a/tests/run.sh b/tests/run.sh
new file mode 100755
index 0000000..24f586d
--- /dev/null
+++ b/tests/run.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [ -z "$ARM_CROSS" ]; then
+    echo "ARM_CROSS is not set!"
+    exit 1
+fi
+
+LANG=C python3 -m unittest discover -v -p '*py'
diff --git a/tests/test.py b/tests/test.py
deleted file mode 100644
index 895c4f6..0000000
--- a/tests/test.py
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/python3-dbg
-# -*- coding: utf-8 -*-
-
-
-
-class TestSuite:
-
-
-
-
-
-
-
-
-    def print_sep():
-        """Print a separator line."""
-
-        print('------------------------')
-
-
-    def check_true(desc, code):
-        """Check if an expression is true."""
-
-        try:
-            test = code()
-        except:
-            test = False
-
-        if test:
-            print('[+] %s: OK' % desc)
-        else:
-            print('[+] %s: nok...' % desc)
-            raise Exception('Unexpected result!')
-
-
-
-
-
-
-- 
cgit v0.11.2-87-g4458