From 0c92911504f7d267c913fc8d2069cb87139b390b Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 28 Jul 2016 23:55:02 +0200
Subject: Centralized the checksum computing of binary contents.

---
 ChangeLog                           | 26 ++++++++++++++
 plugins/pychrysa/analysis/content.c |  7 ++--
 src/analysis/binary.c               |  8 ++---
 src/analysis/content-int.h          |  6 ++--
 src/analysis/content.c              | 29 ++++++++++++++--
 src/analysis/content.h              |  2 +-
 src/analysis/contents/file.c        | 35 +++++--------------
 src/analysis/contents/restricted.c  | 42 +++++++++++++++++++++++
 src/analysis/disass/disassembler.c  |  2 +-
 src/analysis/project.c              |  2 +-
 tests/analysis/contents/checksum.py | 67 +++++++++++++++++++++++++++++++++++++
 11 files changed, 181 insertions(+), 45 deletions(-)
 create mode 100644 tests/analysis/contents/checksum.py

diff --git a/ChangeLog b/ChangeLog
index 25f0b0e..0ccf59b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+16-07-28  Cyrille Bagard <nocbos@gmail.com>
+
+	* plugins/pychrysa/analysis/content.c:
+	Update code.
+
+	* src/analysis/binary.c:
+	Typo.
+
+	* src/analysis/content-int.h:
+	* src/analysis/content.c:
+	Centralize the checksum computing of binary contents.
+
+	* src/analysis/content.h:
+	Typo.
+
+	* src/analysis/contents/file.c:
+	* src/analysis/contents/restricted.c:
+	Centralize the checksum computing of binary contents.
+
+	* src/analysis/disass/disassembler.c:
+	* src/analysis/project.c:
+	Typo.
+
+	* tests/analysis/contents/checksum.py:
+	New entry: verify checksums in the test suite.
+
 16-07-23  Cyrille Bagard <nocbos@gmail.com>
 
 	* plugins/pychrysa/analysis/routine.c:
diff --git a/plugins/pychrysa/analysis/content.c b/plugins/pychrysa/analysis/content.c
index 00265e3..d3da2b1 100644
--- a/plugins/pychrysa/analysis/content.c
+++ b/plugins/pychrysa/analysis/content.c
@@ -82,12 +82,9 @@ static PyObject *py_binary_content_get_checksum(PyObject *self, PyObject *args)
 
     content = G_BIN_CONTENT(pygobject_get(self));
 
-    //checksum = g_binary_content_get_cheksum(content);
+    checksum = g_binary_content_get_checksum(content);
 
-    printf("YEAH\n");
-    fflush(NULL);
-
-    result = PyUnicode_FromString("checksum");
+    result = PyUnicode_FromString(checksum);
 
     return result;
 
diff --git a/src/analysis/binary.c b/src/analysis/binary.c
index 20410f2..75c6e14 100644
--- a/src/analysis/binary.c
+++ b/src/analysis/binary.c
@@ -525,7 +525,7 @@ bool g_loaded_binary_save(const GLoadedBinary *binary, xmlDocPtr xdoc, xmlXPathC
     access = stradd(access, "/Main");
 
     content = g_binary_format_get_content(G_BIN_FORMAT(binary->format));
-    hash = g_binary_content_get_cheksum(content);
+    hash = g_binary_content_get_checksum(content);
     g_object_unref(G_OBJECT(content));
 
     result = add_content_to_node(xdoc, context, access, hash);
@@ -541,7 +541,7 @@ bool g_loaded_binary_save(const GLoadedBinary *binary, xmlDocPtr xdoc, xmlXPathC
         debug = g_exe_format_get_debug_info(binary->format, i);
 
         content = g_binary_format_get_content(G_BIN_FORMAT(debug));
-        hash = g_binary_content_get_cheksum(content);
+        hash = g_binary_content_get_checksum(content);
         g_object_unref(G_OBJECT(content));
 
         g_object_unref(G_OBJECT(debug));
@@ -1038,7 +1038,7 @@ static bool g_loaded_binary_connect_internal(GLoadedBinary *binary)
     /* Détermination de l'identifiant */
 
     content = g_binary_format_get_content(G_BIN_FORMAT(binary->format));
-    checksum = g_binary_content_get_cheksum(content);
+    checksum = g_binary_content_get_checksum(content);
     g_object_unref(G_OBJECT(content));
 
     /* Tentative de connexion */
@@ -1114,7 +1114,7 @@ static bool g_loaded_binary_connect_remote(GLoadedBinary *binary)
     /* Détermination de l'identifiant */
 
     content = g_binary_format_get_content(G_BIN_FORMAT(binary->format));
-    checksum = g_binary_content_get_cheksum(content);
+    checksum = g_binary_content_get_checksum(content);
     g_object_unref(G_OBJECT(content));
 
     /* Tentative de connexion */
diff --git a/src/analysis/content-int.h b/src/analysis/content-int.h
index d9f9ef9..24cea81 100644
--- a/src/analysis/content-int.h
+++ b/src/analysis/content-int.h
@@ -35,8 +35,8 @@ typedef const char * (* describe_content_fc) (const GBinContent *, bool);
 /* Ecrit une sauvegarde de contenu binaire dans un fichier XML. */
 typedef bool (* save_content_fc) (const GBinContent *, xmlDocPtr, xmlXPathContextPtr, const char *, const char *);
 
-/* Fournit une empreinte unique (SHA256) pour les données. */
-typedef const gchar * (* get_checksum_fc) (GBinContent *);
+/* Calcule une empreinte unique (SHA256) pour les données. */
+typedef void (* compute_checksum_fc) (GBinContent *, GChecksum *);
 
 /* Détermine le nombre d'octets lisibles. */
 typedef phys_t (* compute_size_fc) (const GBinContent *);
@@ -78,7 +78,7 @@ struct _GBinContentIface
 
     save_content_fc save;                   /* Sauvegarde du contenu       */
 
-    get_checksum_fc get_checksum;           /* Calcul de l'empreinte       */
+    compute_checksum_fc compute_checksum;   /* Calcul de l'empreinte       */
 
     compute_size_fc compute_size;           /* Calcul de la taille totale  */
 
diff --git a/src/analysis/content.c b/src/analysis/content.c
index 01c9c99..4b4645f 100644
--- a/src/analysis/content.c
+++ b/src/analysis/content.c
@@ -24,9 +24,13 @@
 #include "content.h"
 
 
+#include <assert.h>
 #include <string.h>
 
 
+#include <i18n.h>
+
+
 #include "content-int.h"
 #include "contents/file.h"
 
@@ -155,13 +159,32 @@ bool g_binary_content_save(const GBinContent *content, xmlDocPtr xdoc, xmlXPathC
 *                                                                             *
 ******************************************************************************/
 
-const gchar *g_binary_content_get_cheksum(GBinContent *content)
+const gchar *g_binary_content_get_checksum(GBinContent *content)
 {
+    const gchar *result;                    /* Empreinte à retourner       */
+    GChecksum *checksum;                    /* Calcul de l'empreinte       */
     GBinContentIface *iface;                /* Interface utilisée          */
 
-    iface = G_BIN_CONTENT_GET_IFACE(content);
+    checksum = g_object_get_data(G_OBJECT(content), "checksum");
+
+    if (checksum == NULL)
+    {
+        checksum = g_checksum_new(G_CHECKSUM_SHA256);
+        assert(checksum != NULL);
+
+        g_checksum_reset(checksum);
+
+        iface = G_BIN_CONTENT_GET_IFACE(content);
 
-    return iface->get_checksum(content);
+        iface->compute_checksum(content, checksum);
+
+        g_object_set_data_full(G_OBJECT(content), "checksum", checksum, (GDestroyNotify)g_checksum_free);
+
+    }
+
+    result = g_checksum_get_string(checksum);
+
+    return result;
 
 }
 
diff --git a/src/analysis/content.h b/src/analysis/content.h
index 1a61cb7..f1e5bbf 100644
--- a/src/analysis/content.h
+++ b/src/analysis/content.h
@@ -64,7 +64,7 @@ const char *g_binary_content_describe(const GBinContent *, bool);
 bool g_binary_content_save(const GBinContent *, xmlDocPtr, xmlXPathContextPtr, const char *, const char *);
 
 /* Fournit une empreinte unique (SHA256) pour les données. */
-const gchar *g_binary_content_get_cheksum(GBinContent *);
+const gchar *g_binary_content_get_checksum(GBinContent *);
 
 /* Détermine le nombre d'octets lisibles. */
 phys_t g_binary_content_compute_size(const GBinContent *);
diff --git a/src/analysis/contents/file.c b/src/analysis/contents/file.c
index 8416348..95cfd5d 100644
--- a/src/analysis/contents/file.c
+++ b/src/analysis/contents/file.c
@@ -24,7 +24,6 @@
 #include "file.h"
 
 
-#include <assert.h>
 #include <fcntl.h>
 #include <malloc.h>
 #include <string.h>
@@ -49,9 +48,6 @@ struct _GFileContent
     bin_t *data;                            /* Contenu binaire représenté  */
     mrange_t range;                         /* Couverture du binaire       */
 
-    GChecksum *checksum;                    /* Calcul de l'empreinte       */
-    bool cs_computed;                       /* Calcul effectué ?           */
-
 };
 
 /* Contenu de données binaires issues d'un fichier (classe) */
@@ -84,7 +80,7 @@ static const char *g_file_content_describe(const GFileContent *, bool);
 static bool g_file_content_save(const GFileContent *, xmlDocPtr, xmlXPathContextPtr, const char *, const char *);
 
 /* Fournit une empreinte unique (SHA256) pour les données. */
-static const gchar *g_file_content_get_checksum(GFileContent *);
+static void g_file_content_compute_checksum(GFileContent *, GChecksum *);
 
 /* Détermine le nombre d'octets lisibles. */
 static phys_t g_file_content_compute_size(const GFileContent *);
@@ -161,10 +157,6 @@ static void g_file_content_class_init(GFileContentClass *klass)
 
 static void g_file_content_init(GFileContent *content)
 {
-    content->checksum = g_checksum_new(G_CHECKSUM_SHA256);
-    assert(content->checksum != NULL);
-
-    content->cs_computed = false;
 
 }
 
@@ -187,7 +179,7 @@ static void g_file_content_interface_init(GBinContentInterface *iface)
 
     iface->save = (save_content_fc)g_file_content_save;
 
-    iface->get_checksum = (get_checksum_fc)g_file_content_get_checksum;
+    iface->compute_checksum = (compute_checksum_fc)g_file_content_compute_checksum;
 
     iface->compute_size = (compute_size_fc)g_file_content_compute_size;
 
@@ -220,8 +212,6 @@ static void g_file_content_interface_init(GBinContentInterface *iface)
 
 static void g_file_content_dispose(GFileContent *content)
 {
-    g_checksum_free(content->checksum);
-
     G_OBJECT_CLASS(g_file_content_parent_class)->dispose(G_OBJECT(content));
 
 }
@@ -444,29 +434,20 @@ static bool g_file_content_save(const GFileContent *content, xmlDocPtr xdoc, xml
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : content = contenu binaire à venir lire.                      *
+*  Paramètres  : content  = contenu binaire à venir lire.                     *
+*                checksum = empreinte de zone mémoire à compléter.            *
 *                                                                             *
-*  Description : Fournit une empreinte unique (SHA256) pour les données.      *
+*  Description : Calcule une empreinte unique (SHA256) pour les données.      *
 *                                                                             *
-*  Retour      : Chaîne représentant l'empreinte du contenu binaire.          *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static const gchar *g_file_content_get_checksum(GFileContent *content)
+static void g_file_content_compute_checksum(GFileContent *content, GChecksum *checksum)
 {
-    if (!content->cs_computed)
-    {
-        g_checksum_reset(content->checksum);
-
-        g_checksum_update(content->checksum, content->data, get_mrange_length(&content->range));
-
-        content->cs_computed = true;
-
-    }
-
-    return g_checksum_get_string(content->checksum);
+    g_checksum_update(checksum, content->data, get_mrange_length(&content->range));
 
 }
 
diff --git a/src/analysis/contents/restricted.c b/src/analysis/contents/restricted.c
index a8f1763..e342242 100644
--- a/src/analysis/contents/restricted.c
+++ b/src/analysis/contents/restricted.c
@@ -66,6 +66,9 @@ static void g_restricted_content_dispose(GRestrictedContent *);
 /* Procède à la libération totale de la mémoire. */
 static void g_restricted_content_finalize(GRestrictedContent *);
 
+/* Calcule une empreinte unique (SHA256) pour les données. */
+static void g_restricted_content_compute_checksum(GRestrictedContent *, GChecksum *);
+
 /* 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);
 
@@ -156,6 +159,8 @@ static void g_restricted_content_init(GRestrictedContent *content)
 
 static void g_restricted_content_interface_init(GBinContentInterface *iface)
 {
+    iface->compute_checksum = (compute_checksum_fc)g_restricted_content_compute_checksum;
+
     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;
@@ -242,6 +247,43 @@ GBinContent *g_restricted_content_new(GBinContent *content, const mrange_t *rang
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : content  = contenu binaire à venir lire.                     *
+*                checksum = empreinte de zone mémoire à compléter.            *
+*                                                                             *
+*  Description : Calcule une empreinte unique (SHA256) pour les données.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_restricted_content_compute_checksum(GRestrictedContent *content, GChecksum *checksum)
+{
+    vmpa2t start;                           /* Point de départ             */
+    phys_t i;                               /* Boucle de parcours          */
+    vmpa2t iter;                            /* Tête de lecture             */
+    const bin_t *byte;                      /* Octet de données à intégrer */
+
+    copy_vmpa(&start, get_mrange_addr(&content->range));
+
+    for (i = 0; i < get_mrange_length(&content->range); i++)
+    {
+        copy_vmpa(&iter, &start);
+        advance_vmpa(&iter, i);
+
+        byte = g_binary_content_get_raw_access(G_BIN_CONTENT(content->internal), &iter, 1);
+
+        if (byte != NULL)
+            g_checksum_update(checksum, byte, 1);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : content = contenu binaire à venir lire.                      *
 *                addr    = position de la tête de lecture.                    *
 *                length  = quantité d'octets à lire.                          *
diff --git a/src/analysis/disass/disassembler.c b/src/analysis/disass/disassembler.c
index 1499064..7c16dd1 100644
--- a/src/analysis/disass/disassembler.c
+++ b/src/analysis/disass/disassembler.c
@@ -691,7 +691,7 @@ void disassemble_binary(GLoadedBinary *binary, GArchInstruction **instrs, GCodeB
     format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
 
     content = g_binary_format_get_content(format);
-    checksum = g_binary_content_get_cheksum(content);
+    checksum = g_binary_content_get_checksum(content);
     g_object_unref(G_OBJECT(content));
 
     build_disass_prologue(*buffer, g_binary_content_describe(content, true), checksum);
diff --git a/src/analysis/project.c b/src/analysis/project.c
index 1a73e74..bf78a49 100644
--- a/src/analysis/project.c
+++ b/src/analysis/project.c
@@ -528,7 +528,7 @@ GBinContent *g_study_project_find_binary_content_by_hash(GStudyProject *project,
     for (i = 0; i < project->contents_count && result == NULL; i++)
     {
         iter = project->contents[i].content;
-        other = g_binary_content_get_cheksum(iter);
+        other = g_binary_content_get_checksum(iter);
 
         if (strcmp(hash, other) == 0)
         {
diff --git a/tests/analysis/contents/checksum.py b/tests/analysis/contents/checksum.py
new file mode 100644
index 0000000..ba09a3f
--- /dev/null
+++ b/tests/analysis/contents/checksum.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python3-dbg
+# -*- coding: utf-8 -*-
+
+
+# Tests validant le bon calcul d'empreintes.
+
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.analysis.contents import FileContent, RestrictedContent
+from pychrysalide.arch import vmpa, mrange
+import hashlib
+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'AAAABBBBCCCCDDDD')
+
+        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 testFullChecksum(self):
+        """Check checksum of full content."""
+
+        fcnt = FileContent(self._out.name)
+        self.assertIsNotNone(fcnt)
+
+        expected = hashlib.sha256(b'AAAABBBBCCCCDDDD').hexdigest()
+
+        self.assertEqual(fcnt.get_checksum(), expected)
+
+
+    def testPartialChecksum(self):
+        """Check checksum of restricted content."""
+
+        fcnt = FileContent(self._out.name)
+        self.assertIsNotNone(fcnt)
+
+        start = vmpa(4, vmpa.VMPA_NO_VIRTUAL)
+        covered = mrange(start, 4) # 'BBBB'
+
+        rcnt = RestrictedContent(fcnt, covered)
+        self.assertIsNotNone(rcnt)
+
+        expected = hashlib.sha256(b'BBBB').hexdigest()
+
+        self.assertEqual(rcnt.get_checksum(), expected)
-- 
cgit v0.11.2-87-g4458