From 7591f34e7e9d5dd0f20d242394628ab4bcd0a430 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 12 Nov 2017 21:49:22 +0100
Subject: Improved the type of data read from loaded contents.

---
 ChangeLog                             |  8 ++++
 plugins/pychrysa/analysis/content.c   | 62 ++++++++++++++++++++++--
 tests/analysis/contents/restricted.py | 88 +++++++++++++++++++++++++++++++----
 3 files changed, 144 insertions(+), 14 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index aa699e0..3424bbf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 17-11-12  Cyrille Bagard <nocbos@gmail.com>
 
+	* plugins/pychrysa/analysis/content.c:
+	Improve the type of data read from loaded contents.
+
+	* tests/analysis/contents/restricted.py:
+	Update and extend the test suite.
+
+17-11-12  Cyrille Bagard <nocbos@gmail.com>
+
 	* plugins/pychrysa/analysis/binary.c:
 	Update the Python API.
 
diff --git a/plugins/pychrysa/analysis/content.c b/plugins/pychrysa/analysis/content.c
index 5565129..95f88f6 100644
--- a/plugins/pychrysa/analysis/content.c
+++ b/plugins/pychrysa/analysis/content.c
@@ -46,6 +46,9 @@ static PyObject *py_binary_content_get_checksum(PyObject *, PyObject *);
 /* Détermine le nombre d'octets lisibles. */
 static PyObject *py_binary_content_compute_size(PyObject *, PyObject *);
 
+/* Fournit une portion des données représentées. */
+static PyObject *py_binary_content_read_raw(PyObject *, PyObject *);
+
 /* Lit un nombre non signé sur un octet. */
 static PyObject *py_binary_content_read_u8(PyObject *, PyObject *);
 
@@ -125,6 +128,52 @@ static PyObject *py_binary_content_compute_size(PyObject *self, PyObject *args)
 *  Paramètres  : self = contenu binaire à manipuler.                          *
 *                args = non utilisé ici.                                      *
 *                                                                             *
+*  Description : Fournit une portion des données représentées.                *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_binary_content_read_raw(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    GBinContent *content;                   /* Version GLib du format      */
+    PyObject *addr_obj;                     /* Objet pour une position     */
+    vmpa2t *addr;                           /* Position interne associée   */
+    unsigned long long length;              /* Quantité de données à lire  */
+    int ret;                                /* Bilan de lecture des args.  */
+    const bin_t *val;                             /* Valeur lue à faire suivre   */
+
+    content = G_BIN_CONTENT(pygobject_get(self));
+    assert(content != NULL);
+
+    ret = PyArg_ParseTuple(args, "OK", &addr_obj, &length);
+    if (!ret) return NULL;
+
+    addr = get_internal_vmpa(addr_obj);
+    assert(addr != NULL);
+
+    val = g_binary_content_get_raw_access(content, addr, length);
+    if (val == NULL)
+    {
+        PyErr_SetString(PyExc_Exception, _("Invalid read access."));
+        return NULL;
+    }
+
+    result = PyBytes_FromStringAndSize((char *)val, length);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = contenu binaire à manipuler.                          *
+*                args = non utilisé ici.                                      *
+*                                                                             *
 *  Description : Lit un nombre non signé sur un octet.                        *
 *                                                                             *
 *  Retour      : Bilan de l'opération.                                        *
@@ -159,7 +208,7 @@ static PyObject *py_binary_content_read_u8(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    result = PyBytes_FromStringAndSize((char *)&val, 1);
+    result = PyLong_FromUnsignedLong(val);
 
     return result;
 
@@ -206,7 +255,7 @@ static PyObject *py_binary_content_read_u16(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    result = PyBytes_FromStringAndSize((char *)&val, 2);
+    result = PyLong_FromUnsignedLong(val);
 
     return result;
 
@@ -253,7 +302,7 @@ static PyObject *py_binary_content_read_u32(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    result = PyBytes_FromStringAndSize((char *)&val, 4);
+    result = PyLong_FromUnsignedLong(val);
 
     return result;
 
@@ -299,7 +348,7 @@ static PyObject *py_binary_content_read_u64(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    result = PyBytes_FromStringAndSize((char *)&val, 8);
+    result = PyLong_FromUnsignedLongLong(val);
 
     return result;
 
@@ -332,6 +381,11 @@ PyTypeObject *get_python_binary_content_type(void)
             "compute_size($self, /)\n--\n\nCompute the quantity of readable bytes."
         },
         {
+            "read_raw", py_binary_content_read_raw,
+            METH_VARARGS,
+            "read_raw($self, addr, length, /)\n--\n\nRead bytes from a given position."
+        },
+        {
             "read_u8", py_binary_content_read_u8,
             METH_VARARGS,
             "read_u8($self, addr, /)\n--\n\nRead an unsigned byte from a given position."
diff --git a/tests/analysis/contents/restricted.py b/tests/analysis/contents/restricted.py
index e8d3e07..6484299 100644
--- a/tests/analysis/contents/restricted.py
+++ b/tests/analysis/contents/restricted.py
@@ -58,15 +58,39 @@ class TestRestrictedContent(ChrysalideTestCase):
         self.assertIsNotNone(rcnt)
 
         val = rcnt.read_u8(start)
-        self.assertEqual(val, b'\x15')
+        self.assertEqual(val, 0x15)
 
         val = rcnt.read_u8(start)
-        self.assertEqual(val, b'\x16')
+        self.assertEqual(val, 0x16)
 
         val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
-        self.assertEqual(val, b'\x17\x18')
+        self.assertEqual(val, 0x1817)
 
         val = rcnt.read_u32(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, 0x24232221)
+
+
+    def testReadRawAccess(self):
+        """Check valid raw 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_raw(start, 1)
+        self.assertEqual(val, b'\x15')
+
+        val = rcnt.read_raw(start, 1)
+        self.assertEqual(val, b'\x16')
+
+        val = rcnt.read_raw(start, 2)
+        self.assertEqual(val, b'\x17\x18')
+
+        val = rcnt.read_raw(start, 4)
         self.assertEqual(val, b'\x21\x22\x23\x24')
 
 
@@ -83,34 +107,78 @@ class TestRestrictedContent(ChrysalideTestCase):
 
         start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u8(start)
-        self.assertEqual(val, b'\x15')
+        self.assertEqual(val, 0x15)
 
         start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
-        self.assertEqual(val, b'\x15\x16')
+        self.assertEqual(val, 0x1615)
 
         start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u32(start, vmpa.SRE_LITTLE)
-        self.assertEqual(val, b'\x15\x16\x17\x18')
+        self.assertEqual(val, 0x18171615)
 
         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')
+        self.assertEqual(val, 0x2423222118171615)
 
         start = vmpa(23, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u8(start)
-        self.assertEqual(val, b'\x28')
+        self.assertEqual(val, 0x28)
 
         start = vmpa(22, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u16(start, vmpa.SRE_LITTLE)
-        self.assertEqual(val, b'\x27\x28')
+        self.assertEqual(val, 0x2827)
 
         start = vmpa(20, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u32(start, vmpa.SRE_LITTLE)
-        self.assertEqual(val, b'\x25\x26\x27\x28')
+        self.assertEqual(val, 0x28272625)
 
         start = vmpa(16, vmpa.VMPA_NO_VIRTUAL)
         val = rcnt.read_u64(start, vmpa.SRE_LITTLE)
+        self.assertEqual(val, 0x2827262524232221)
+
+
+    def testBorderLineRawAccess(self):
+        """Check valid border line raw 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_raw(start, 1)
+        self.assertEqual(val, b'\x15')
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 2)
+        self.assertEqual(val, b'\x15\x16')
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 4)
+        self.assertEqual(val, b'\x15\x16\x17\x18')
+
+        start = vmpa(12, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 8)
+        self.assertEqual(val, b'\x15\x16\x17\x18\x21\x22\x23\x24')
+
+        start = vmpa(23, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 1)
+        self.assertEqual(val, b'\x28')
+
+        start = vmpa(22, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 2)
+        self.assertEqual(val, b'\x27\x28')
+
+        start = vmpa(20, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 4)
+        self.assertEqual(val, b'\x25\x26\x27\x28')
+
+        start = vmpa(16, vmpa.VMPA_NO_VIRTUAL)
+        val = rcnt.read_raw(start, 8)
         self.assertEqual(val, b'\x21\x22\x23\x24\x25\x26\x27\x28')
 
 
-- 
cgit v0.11.2-87-g4458