From c12d6a5d11bf9a2436ff78e393173ca59b6c9c46 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 7 Apr 2021 00:55:54 +0200
Subject: Provide several kinds of hashes for binary files.

---
 configure.ac                     |   2 +
 plugins/Makefile.am              |   1 +
 plugins/bhash/Makefile.am        |  52 +++
 plugins/bhash/core.c             |  74 ++++
 plugins/bhash/core.h             |  38 ++
 plugins/bhash/imphash.c          | 244 +++++++++++++
 plugins/bhash/imphash.h          |  40 +++
 plugins/bhash/python/Makefile.am |  21 ++
 plugins/bhash/python/imphash.c   | 135 ++++++++
 plugins/bhash/python/imphash.h   |  39 +++
 plugins/bhash/python/module.c    |  90 +++++
 plugins/bhash/python/module.h    |  38 ++
 plugins/bhash/python/rich.c      | 194 +++++++++++
 plugins/bhash/python/rich.h      |  39 +++
 plugins/bhash/python/tlsh.c      | 256 ++++++++++++++
 plugins/bhash/python/tlsh.h      |  39 +++
 plugins/bhash/rich.c             | 181 ++++++++++
 plugins/bhash/rich.h             |  44 +++
 plugins/bhash/tlsh.c             | 724 +++++++++++++++++++++++++++++++++++++++
 plugins/bhash/tlsh.h             |  46 +++
 plugins/pe/python/routine.c      |  46 ++-
 plugins/pe/routine.c             |  31 +-
 plugins/pe/routine.h             |   5 +-
 plugins/pe/symbols.c             |   7 +-
 plugins/winordinals/assign.c     |   2 +-
 25 files changed, 2380 insertions(+), 8 deletions(-)
 create mode 100644 plugins/bhash/Makefile.am
 create mode 100644 plugins/bhash/core.c
 create mode 100644 plugins/bhash/core.h
 create mode 100644 plugins/bhash/imphash.c
 create mode 100644 plugins/bhash/imphash.h
 create mode 100644 plugins/bhash/python/Makefile.am
 create mode 100644 plugins/bhash/python/imphash.c
 create mode 100644 plugins/bhash/python/imphash.h
 create mode 100644 plugins/bhash/python/module.c
 create mode 100644 plugins/bhash/python/module.h
 create mode 100644 plugins/bhash/python/rich.c
 create mode 100644 plugins/bhash/python/rich.h
 create mode 100644 plugins/bhash/python/tlsh.c
 create mode 100644 plugins/bhash/python/tlsh.h
 create mode 100644 plugins/bhash/rich.c
 create mode 100644 plugins/bhash/rich.h
 create mode 100644 plugins/bhash/tlsh.c
 create mode 100644 plugins/bhash/tlsh.h

diff --git a/configure.ac b/configure.ac
index d7a6844..291acca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -465,6 +465,8 @@ AC_CONFIG_FILES([Makefile
                  plugins/arm/v7/opcodes/Makefile
                  plugins/arm/v7/operands/Makefile
                  plugins/arm/v7/registers/Makefile
+                 plugins/bhash/Makefile
+                 plugins/bhash/python/Makefile
                  plugins/bootimg/Makefile
                  plugins/bootimg/python/Makefile
                  plugins/dalvik/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 4addee5..608c927 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -9,6 +9,7 @@ endif
 SUBDIRS = \
 	$(PYTHON3_SUBDIRS) \
 	arm \
+	bhash \
 	bootimg \
 	dex \
 	dalvik \
diff --git a/plugins/bhash/Makefile.am b/plugins/bhash/Makefile.am
new file mode 100644
index 0000000..44d1c21
--- /dev/null
+++ b/plugins/bhash/Makefile.am
@@ -0,0 +1,52 @@
+
+lib_LTLIBRARIES = libbhash.la
+
+libdir = $(pluginslibdir)
+
+
+if BUILD_PYTHON3_BINDINGS
+
+PYTHON3_LIBADD =							\
+	python/libbhashpython.la
+
+if BUILD_DISCARD_LOCAL
+
+PYTHON3_LDFLAGS = -Wl,-rpath,$(pluginslibdir) \
+					-L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+else
+
+PYTHON3_LDFLAGS = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs \
+					-L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+endif
+
+PYTHON3_SUBDIRS = python
+
+endif
+
+
+libbhash_la_SOURCES =						\
+	core.h core.c							\
+	imphash.h imphash.c						\
+	tlsh.h tlsh.c							\
+	rich.h rich.c
+
+libbhash_la_LIBADD =					\
+	$(PYTHON3_LIBADD)
+
+libbhash_la_LDFLAGS =					\
+	-L$(top_srcdir)/src/.libs -lchrysacore	\
+	$(PYTHON3_LDFLAGS)
+
+
+devdir = $(includedir)/chrysalide/$(subdir)
+
+dev_HEADERS = $(libbhash_la_SOURCES:%c=)
+
+
+AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src
+
+AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
+
+SUBDIRS = $(PYTHON3_SUBDIRS)
diff --git a/plugins/bhash/core.c b/plugins/bhash/core.c
new file mode 100644
index 0000000..91a0bf2
--- /dev/null
+++ b/plugins/bhash/core.c
@@ -0,0 +1,74 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core.c - prototypes pour le calcul d'empreintes de binaires
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "core.h"
+
+
+#include <config.h>
+#include <plugins/self.h>
+
+
+#ifdef HAVE_PYTHON3_BINDINGS
+#   include "python/module.h"
+#endif
+
+
+#ifdef HAVE_PYTHON3_BINDINGS_
+#   define PG_REQ RL("PyChrysalide")
+#else
+#   define PG_REQ NO_REQ
+#endif
+
+
+
+DEFINE_CHRYSALIDE_PLUGIN("BHash", "Special hash methods for binaries",
+                         PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/formats"),
+                         PG_REQ, AL(PGA_PLUGIN_INIT));
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                                                                             *
+*  Description : Prend acte du chargement du greffon.                         *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
+{
+    bool result;                            /* Bilan à retourner           */
+
+#ifdef HAVE_PYTHON3_BINDINGS
+    result = add_bhash_module_to_python_module();
+#else
+    result = true;
+#endif
+
+    return result;
+
+}
diff --git a/plugins/bhash/core.h b/plugins/bhash/core.h
new file mode 100644
index 0000000..bc6d9e9
--- /dev/null
+++ b/plugins/bhash/core.h
@@ -0,0 +1,38 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core.h - prototypes pour le calcul d'empreintes de binaires
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_BHASH_CORE_H
+#define _PLUGINS_BHASH_CORE_H
+
+
+#include <plugins/plugin.h>
+#include <plugins/plugin-int.h>
+
+
+
+/* Prend acte du chargement du greffon. */
+G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+
+
+
+#endif  /* _PLUGINS_BHASH_CORE_H */
diff --git a/plugins/bhash/imphash.c b/plugins/bhash/imphash.c
new file mode 100644
index 0000000..2352bac
--- /dev/null
+++ b/plugins/bhash/imphash.c
@@ -0,0 +1,244 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * imphash.c - calculs d'empreintes sur la base des importations
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "imphash.h"
+
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <strings.h>
+
+
+#include <common/extstr.h>
+#include <common/sort.h>
+#include <plugins/pe/format.h>
+#include <plugins/pe/routine.h>
+
+
+
+/* Mémorisation d'un symbole importé */
+typedef struct _imported_sym_t
+{
+    size_t index;                           /* Position dans les imports   */
+    char *name;                             /* Désignation pour empreinte  */
+
+} imported_sym_t;
+
+/* Dresse la liste des symboles importés pour un format. */
+static imported_sym_t *list_all_pe_imports_for_hash(const GPeFormat *, size_t *);
+
+/* Etablit une comparaison entre deux importations. */
+static int compare_imports_by_name(const imported_sym_t *, const imported_sym_t *);
+
+/* Etablit une comparaison entre deux importations. */
+static int compare_imports_by_index(const imported_sym_t *, const imported_sym_t *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : format = format chargé dont l'analyse est faite.             *
+*                count  = taille de la liste retournée. [OUT]                 *
+*                                                                             *
+*  Description : Dresse la liste des symboles importés pour un format.        *
+*                                                                             *
+*  Retour      : Liste de symboles mise en place.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static imported_sym_t *list_all_pe_imports_for_hash(const GPeFormat *format, size_t *count)
+{
+    imported_sym_t *result;                 /* Liste de symboles           */
+    GBinFormat *base;                       /* Format basique du binaire   */
+    size_t sym_count;                       /* Nombre de ces symboles      */
+    size_t i;                               /* Boucle de parcours          */
+    GBinSymbol *symbol;                     /* Commodité d'accès           */
+    const char *name;                       /* Désignation actuelle        */
+    const char *library;                    /* Fichier DLL à charger       */
+    char *item;                             /* Nouvelle entrée de la liste */
+    char *dot;                              /* Point à raccourcir          */
+
+    result = NULL;
+    *count = 0;
+
+    base = G_BIN_FORMAT(format);
+
+    g_binary_format_lock_symbols_rd(base);
+
+    sym_count = g_binary_format_count_symbols(base);
+
+    for (i = 0; i < sym_count; i++)
+    {
+        symbol = g_binary_format_get_symbol(base, i);
+
+        if (!G_IS_PE_IMPORTED_ROUTINE(symbol))
+            goto next;
+
+        name = g_binary_routine_get_name(G_BIN_ROUTINE(symbol));
+
+        if (name == NULL)
+            goto next;
+
+        library = g_pe_imported_routine_get_library(G_PE_IMPORTED_ROUTINE(symbol));
+
+        if (library == NULL)
+            goto next;
+
+        item = malloc((strlen(library) + 1 + strlen(name) + 1) * sizeof(char));
+
+        strcpy(item, library);
+
+        dot = strchr(item, '.');
+
+        if (dot != NULL)
+            *dot = '\0';
+
+        strcat(item, ".");
+        strcat(item, name);
+
+        item = strlower(item);
+
+        result = realloc(result, ++(*count) * sizeof(imported_sym_t));
+
+        result[*count - 1].index = g_pe_imported_routine_get_index(G_PE_IMPORTED_ROUTINE(symbol));
+        result[*count - 1].name = item;
+
+ next:
+
+        g_object_unref(G_OBJECT(symbol));
+
+    }
+
+    g_binary_format_unlock_symbols_rd(base);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : p1 = première importation à traiter.                         *
+*                p2 = seconde importation à traiter.                          *
+*                                                                             *
+*  Description : Etablit une comparaison entre deux importations.             *
+*                                                                             *
+*  Retour      : Bilan de la comparaison.                                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int compare_imports_by_name(const imported_sym_t *p1, const imported_sym_t *p2)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = strcmp(p1->name, p2->name);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : p1 = première importation à traiter.                         *
+*                p2 = seconde importation à traiter.                          *
+*                                                                             *
+*  Description : Etablit une comparaison entre deux importations.             *
+*                                                                             *
+*  Retour      : Bilan de la comparaison.                                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int compare_imports_by_index(const imported_sym_t *p1, const imported_sym_t *p2)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = sort_unsigned_long(p1->index, p2->index);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : format = format en place à consulter.                        *
+*                std    = précise si la méthode de calcul est standard.       *
+*                                                                             *
+*  Description : Calcule l'empreinte des importations d'un format PE.         *
+*                                                                             *
+*  Retour      : Empreinte MD5 calculée ou NULL en cas d'échec.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *compute_pe_import_hash(const GPeFormat *format, bool std)
+{
+    char *result;                           /* Empreinte à retourner       */
+    size_t count;                           /* Quantité de symboles        */
+    imported_sym_t *list;                   /* Liste de symboles           */
+    GChecksum *checksum;                    /* Preneur d'empreinte         */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = NULL;
+
+    list = list_all_pe_imports_for_hash(format, &count);
+
+    if (list != NULL)
+    {
+        if (std)
+            qsort(list, count, sizeof(imported_sym_t), (__compar_fn_t)compare_imports_by_name);
+        else
+            qsort(list, count, sizeof(imported_sym_t), (__compar_fn_t)compare_imports_by_index);
+
+        checksum = g_checksum_new(G_CHECKSUM_MD5);
+
+        for (i = 0; i < count; i++)
+        {
+            if (i > 0)
+                g_checksum_update(checksum, (unsigned char *)",", 1);
+
+            g_checksum_update(checksum, (unsigned char *)list[i].name, strlen(list[i].name));
+
+            free(list[i].name);
+
+        }
+
+        result = strdup(g_checksum_get_string(checksum));
+
+        g_checksum_free(checksum);
+
+        free(list);
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/bhash/imphash.h b/plugins/bhash/imphash.h
new file mode 100644
index 0000000..d2f5946
--- /dev/null
+++ b/plugins/bhash/imphash.h
@@ -0,0 +1,40 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * imphash.h - prototypes pour les calculs d'empreintes sur la base des importations
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_BHASH_IMPHASH_H
+#define _PLUGINS_BHASH_IMPHASH_H
+
+
+#include <stdbool.h>
+
+
+#include <plugins/pe/format.h>
+
+
+
+/* Calcule l'empreinte des importations d'un format PE. */
+char *compute_pe_import_hash(const GPeFormat *, bool);
+
+
+
+#endif  /* _PLUGINS_BHASH_IMPHASH_H */
diff --git a/plugins/bhash/python/Makefile.am b/plugins/bhash/python/Makefile.am
new file mode 100644
index 0000000..822a716
--- /dev/null
+++ b/plugins/bhash/python/Makefile.am
@@ -0,0 +1,21 @@
+
+noinst_LTLIBRARIES = libbhashpython.la
+
+libbhashpython_la_SOURCES =			\
+	imphash.h imphash.c				\
+	module.h module.c				\
+	tlsh.h tlsh.c					\
+	rich.h rich.c
+
+libbhashpython_la_LDFLAGS = 
+
+
+devdir = $(includedir)/chrysalide/$(subdir)
+
+dev_HEADERS = $(libbhashpython_la_SOURCES:%c=)
+
+
+AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+	-I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
+
+AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
diff --git a/plugins/bhash/python/imphash.c b/plugins/bhash/python/imphash.c
new file mode 100644
index 0000000..c01628d
--- /dev/null
+++ b/plugins/bhash/python/imphash.c
@@ -0,0 +1,135 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * imphash.c - équivalent Python du fichier "plugins/bhash/imphash.c"
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "imphash.h"
+
+
+#include <pygobject.h>
+
+
+#include <plugins/pe/python/format.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "../imphash.h"
+
+
+
+/* Calcule l'empreinte des importations d'un format PE. */
+static PyObject *py_bhash_compute_pe_import_hash(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = paramètre à récupérer pour le traitement.             *
+*                                                                             *
+*  Description : Calcule l'empreinte des importations d'un format PE.         *
+*                                                                             *
+*  Retour      : Empreinte MD5 calculée ou None en cas d'échec.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bhash_compute_pe_import_hash(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    int std;                                /* Méthode de calcul           */
+    GPeFormat *format;                      /* Format PE à manipuler       */
+    int ret;                                /* Bilan de lecture des args.  */
+    char *digest;                           /* Empreinte calculée          */
+
+#define BHASH_COMPUTE_PE_IMPORT_HASH_METHOD PYTHON_METHOD_DEF           \
+(                                                                       \
+    compute_pe_import_hash, "format, /, std=True",                      \
+    METH_VARARGS, py_bhash,                                             \
+    "Compute the import hash for a given PE format.\n"                  \
+    "\n"                                                                \
+    "The *format* argument is a PE file format provided as a"           \
+    " pychrysalide.format.pe.PeFormat instance and *std* defines the"   \
+    " kind of hash to compute.\n"                                       \
+    "\n"                                                                \
+    "The standard version has been created by Mandiant/FireEye; the"    \
+    " other one is used by the popular pefile Python module.\n"         \
+    "\n"                                                                \
+    "The returned value is a MD5 digest string or *None* in case of"    \
+    " error."                                                           \
+)
+
+    result = NULL;
+
+    std = 1;
+
+    ret = PyArg_ParseTuple(args, "O&|p", convert_to_pe_format, &format, &std);
+    if (!ret) goto exit;
+
+    digest = compute_pe_import_hash(format, std);
+
+    if (digest != NULL)
+    {
+        result = PyUnicode_FromString(digest);
+        free(digest);
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : super = module dont la définition est à compléter.           *
+*                                                                             *
+*  Description : Définit une extension du module 'bhash' à compléter.         *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool populate_bhash_module_with_imphash(PyObject *super)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    static PyMethodDef py_imphash_methods[] = {
+        BHASH_COMPUTE_PE_IMPORT_HASH_METHOD,
+        { NULL }
+    };
+
+    result = register_python_module_methods(super, py_imphash_methods);
+
+    return result;
+
+}
diff --git a/plugins/bhash/python/imphash.h b/plugins/bhash/python/imphash.h
new file mode 100644
index 0000000..fa5ff2c
--- /dev/null
+++ b/plugins/bhash/python/imphash.h
@@ -0,0 +1,39 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * imphash.h - équivalent Python du fichier "plugins/bhash/imphash.h"
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_BHASH_PYTHON_IMPHASH_H
+#define _PLUGINS_BHASH_PYTHON_IMPHASH_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit une extension du module 'bhash' à compléter. */
+bool populate_bhash_module_with_imphash(PyObject *);
+
+
+
+#endif  /* _PLUGINS_BHASH_PYTHON_IMPHASH_H */
diff --git a/plugins/bhash/python/module.c b/plugins/bhash/python/module.c
new file mode 100644
index 0000000..deb5d28
--- /dev/null
+++ b/plugins/bhash/python/module.c
@@ -0,0 +1,90 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.c - intégration du répertoire bhash en tant que module
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "module.h"
+
+
+#include <assert.h>
+#include <Python.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "imphash.h"
+#include "tlsh.h"
+#include "rich.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Ajoute le module 'plugins.bhash' au module Python.           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool add_bhash_module_to_python_module(void)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *super;                        /* Module à compléter          */
+    PyObject *module;                       /* Sous-module mis en place    */
+
+#define PYCHRYSALIDE_PLUGINS_BHASH_DOC                           \
+    "bhash is a module providing several kinds of hashes for binary files."
+
+    static PyModuleDef py_chrysalide_bhash_module = {
+
+        .m_base = PyModuleDef_HEAD_INIT,
+
+        .m_name = "pychrysalide.plugins.bhash",
+        .m_doc = PYCHRYSALIDE_PLUGINS_BHASH_DOC,
+
+        .m_size = -1,
+
+    };
+
+    result = false;
+
+    super = get_access_to_python_module("pychrysalide.plugins");
+
+    module = build_python_module(super, &py_chrysalide_bhash_module);
+
+    result = (module != NULL);
+
+    if (result) result = populate_bhash_module_with_imphash(module);
+    if (result) result = populate_bhash_module_with_tlsh(module);
+    if (result) result = populate_bhash_module_with_rich_header(module);
+
+    assert(result);
+
+    return result;
+
+}
diff --git a/plugins/bhash/python/module.h b/plugins/bhash/python/module.h
new file mode 100644
index 0000000..057cfdf
--- /dev/null
+++ b/plugins/bhash/python/module.h
@@ -0,0 +1,38 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.h - prototypes pour l'intégration du répertoire bhash en tant que module
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_BHASH_PYTHON_MODULE_H
+#define _PLUGINS_BHASH_PYTHON_MODULE_H
+
+
+#include <stdbool.h>
+
+
+
+/* Ajoute le module 'plugins.bhash' au module Python. */
+bool add_bhash_module_to_python_module(void);
+
+
+
+#endif  /* _PLUGINS_BHASH_PYTHON_MODULE_H */
diff --git a/plugins/bhash/python/rich.c b/plugins/bhash/python/rich.c
new file mode 100644
index 0000000..1a8b894
--- /dev/null
+++ b/plugins/bhash/python/rich.c
@@ -0,0 +1,194 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * rich.c - équivalent Python du fichier "plugins/bhash/rich.c"
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "rich.h"
+
+
+#include <pygobject.h>
+
+
+#include <plugins/pe/python/format.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "../rich.h"
+
+
+
+/* Calcule la valeur pour empreinte d'en-tête PE enrichi. */
+static PyObject *py_bhash_compute_pe_rich_header_checksum(PyObject *, PyObject *);
+
+/* Calcule l'empreinte des informations d'en-tête PE enrichi. */
+static PyObject *py_bhash_compute_pe_rich_header_hash(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = paramètre à récupérer pour le traitement.             *
+*                                                                             *
+*  Description : Calcule la valeur pour empreinte d'en-tête PE enrichi.       *
+*                                                                             *
+*  Retour      : None ou empreinte déterminée.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bhash_compute_pe_rich_header_checksum(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GPeFormat *format;                      /* Format PE à manipuler       */
+    int ret;                                /* Bilan de lecture des args.  */
+    uint32_t csum;                          /* Empreinte réalisée          */
+    bool status;                            /* Bilan de l'opération        */
+
+#define BHASH_COMPUTE_PE_RICH_HEADER_CHECKSUM_METHOD PYTHON_METHOD_DEF  \
+(                                                                       \
+    compute_pe_rich_header_checksum, "format, /",                       \
+    METH_VARARGS, py_bhash,                                             \
+    "Compute the expected value for the Rich header checksum of a PE"   \
+    " file.\n"                                                          \
+    "\n"                                                                \
+    "The *format* argument is a PE file format provided as a"           \
+    " pychrysalide.format.pe.PeFormat instance.\n"                      \
+    "\n"                                                                \
+    "The returned value is a 32-bit integer value or *None* in case of" \
+    " error."                                                           \
+)
+
+    result = NULL;
+
+    ret = PyArg_ParseTuple(args, "O&", convert_to_pe_format, &format);
+    if (!ret) goto exit;
+
+    status = compute_pe_rich_header_checksum(format, &csum);
+
+    if (status)
+        result = PyLong_FromUnsignedLong(csum);
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = paramètre à récupérer pour le traitement.             *
+*                                                                             *
+*  Description : Calcule l'empreinte des informations d'en-tête PE enrichi.   *
+*                                                                             *
+*  Retour      : Empreinte MD5 calculée ou None en cas d'échec.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bhash_compute_pe_rich_header_hash(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    int pv;                                 /* Sélection de l'empreinte    */
+    GPeFormat *format;                      /* Format PE à manipuler       */
+    int ret;                                /* Bilan de lecture des args.  */
+    char *digest;                           /* Empreinte calculée          */
+
+#define BHASH_COMPUTE_PE_RICH_HEADER_HASH_METHOD PYTHON_METHOD_DEF      \
+(                                                                       \
+    compute_pe_rich_header_hash, "format, /, pv=True",                  \
+    METH_VARARGS, py_bhash,                                             \
+    "Compute the Rich hash or the RichPV hash for a given PE format.\n" \
+    "\n"                                                                \
+    "The *format* argument is a PE file format provided as a"           \
+    " pychrysalide.format.pe.PeFormat instance and *pv* defines the"    \
+    " kind of hash to compute.\n"                                       \
+    "\n"                                                                \
+    "The returned value is a MD5 digest string or *None* in case of"    \
+    " error."                                                           \
+)
+
+    result = NULL;
+
+    pv = 1;
+
+    ret = PyArg_ParseTuple(args, "O&|p", convert_to_pe_format, &format, &pv);
+    if (!ret) goto exit;
+
+    digest = compute_pe_rich_header_hash(format, pv);
+
+    if (digest != NULL)
+    {
+        result = PyUnicode_FromString(digest);
+        free(digest);
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : super = module dont la définition est à compléter.           *
+*                                                                             *
+*  Description : Définit une extension du module 'bhash' à compléter.         *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool populate_bhash_module_with_rich_header(PyObject *super)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    static PyMethodDef py_rich_header_methods[] = {
+        BHASH_COMPUTE_PE_RICH_HEADER_CHECKSUM_METHOD,
+        BHASH_COMPUTE_PE_RICH_HEADER_HASH_METHOD,
+        { NULL }
+    };
+
+    result = register_python_module_methods(super, py_rich_header_methods);
+
+    return result;
+
+}
diff --git a/plugins/bhash/python/rich.h b/plugins/bhash/python/rich.h
new file mode 100644
index 0000000..45125bc
--- /dev/null
+++ b/plugins/bhash/python/rich.h
@@ -0,0 +1,39 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * rich.h - équivalent Python du fichier "plugins/bhash/rich.h"
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_BHASH_PYTHON_RICH_H
+#define _PLUGINS_BHASH_PYTHON_RICH_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit une extension du module 'bhash' à compléter. */
+bool populate_bhash_module_with_rich_header(PyObject *);
+
+
+
+#endif  /* _PLUGINS_BHASH_PYTHON_RICH_H */
diff --git a/plugins/bhash/python/tlsh.c b/plugins/bhash/python/tlsh.c
new file mode 100644
index 0000000..351327e
--- /dev/null
+++ b/plugins/bhash/python/tlsh.c
@@ -0,0 +1,256 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tlsh.c - équivalent Python du fichier "plugins/bhash/tlsh.c"
+ *
+ * Copyright (C) 2021 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "tlsh.h"
+
+
+#include <pygobject.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+
+
+#include "../tlsh.h"
+
+
+
+/* Calcule l'empreinte TLSH d'un contenu binaire. */
+static PyObject *py_bhash_compute_content_tlsh_hash(PyObject *, PyObject *);
+
+/* Indique si une chaîne représente à priori une empreinte TLSH. */
+static PyObject *py_bhash_is_valid_tlsh_hash(PyObject *, PyObject *);
+
+/* Détermine la similarité entre deux empreintes TLSH. */
+static PyObject *py_bhash_compare_tlsh_hash(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = paramètre à récupérer pour le traitement.             *
+*                                                                             *
+*  Description : Calcule l'empreinte TLSH d'un contenu binaire.               *
+*                                                                             *
+*  Retour      : Empreinte TLSH calculée ou None en cas d'échec.              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bhash_compute_content_tlsh_hash(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    int version;                            /* Affichage de la version ?   */
+    GBinContent *content;                   /* Contenu binaire à traiter   */
+    int ret;                                /* Bilan de lecture des args.  */
+    char *digest;                           /* Empreinte calculée          */
+
+#define BHASH_COMPUTE_CONTENT_TLSH_HASH_METHOD PYTHON_METHOD_DEF        \
+(                                                                       \
+    compute_content_tlsh_hash, "content, /, version=True",              \
+    METH_VARARGS, py_bhash,                                             \
+    "Compute the TLSH compact hash for a given binary content with a"   \
+    " 1-byte checksum.\n"                                               \
+    "\n"                                                                \
+    "The *content* argument is a pychrysalide.analysis.BinContent"      \
+    " instance providing the data to process. The optional *version*"   \
+    " parameter add a 'T?' prefix to the result.\n"                     \
+    "\n"                                                                \
+    "The returned value is a MD5 digest string or *None* in case of"    \
+    " error."                                                           \
+)
+
+    result = NULL;
+
+    version = 1;
+
+    ret = PyArg_ParseTuple(args, "O&|p", convert_to_binary_content, &content, &version);
+    if (!ret) goto exit;
+
+    digest = compute_content_tlsh_hash(content, version);
+
+    if (digest != NULL)
+    {
+        result = PyUnicode_FromString(digest);
+        free(digest);
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = paramètre à récupérer pour le traitement.             *
+*                                                                             *
+*  Description : Indique si une chaîne représente à priori une empreinte TLSH.*
+*                                                                             *
+*  Retour      : Bilan de l'analyse.                                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bhash_is_valid_tlsh_hash(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    const char *h;                          /* Chaîne à considérer         */
+    int ret;                                /* Bilan de lecture des args.  */
+    bool status;                            /* Validité de la chaîne       */
+
+#define BHASH_IS_VALID_TLSH_HASH_METHOD PYTHON_METHOD_DEF               \
+(                                                                       \
+    is_valid_tlsh_hash, "h",                                            \
+    METH_VARARGS, py_bhash,                                             \
+    "Check if a *h* string can be considered as a valid TLSH compact"   \
+    " hash.\n"                                                          \
+    "\n"                                                                \
+    "The returned value is a boolean value."                            \
+)
+
+    result = NULL;
+
+    ret = PyArg_ParseTuple(args, "s", &h);
+    if (!ret) goto exit;
+
+    status = is_valid_tlsh_hash(h);
+
+    result = status ? Py_True : Py_False;
+    Py_INCREF(result);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = paramètres à récupérer pour le traitement.            *
+*                                                                             *
+*  Description : Détermine la similarité entre deux empreintes TLSH.          *
+*                                                                             *
+*  Retour      : Degré de différence relevé ou None en cas d'erreur.          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_bhash_compare_tlsh_hash(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    bool length;                            /* Indication de taille ?      */
+    const char *ha;                         /* Première chaîne à considérer*/
+    const char *hb;                         /* Seconde chaîne à considérer */
+    int ret;                                /* Bilan de lecture des args.  */
+    int32_t diff;                           /* Différence à calculer       */
+    bool status;                            /* Validité de l'opération     */
+
+#define BHASH_COMPARE_TLSH_HASH_METHOD PYTHON_METHOD_DEF                \
+(                                                                       \
+    compare_tlsh_hash, "ha, hb, /, length=True",                        \
+    METH_VARARGS, py_bhash,                                             \
+    "Compare two TLSH compact hashes.\n"                                \
+    "\n"                                                                \
+    "The *ha* and *hb* arguments are strings from which the hashes"     \
+    " will be rebuilt. The"                                             \
+    " pychrysalide.plugins.bhash.compute_content_tlsh_hash() method"    \
+    " can be used to create such strings. The filtering of valid"       \
+    " inputs rely internally on the"                                    \
+    " pychrysalide.plugins.bhash.is_valid_tlsh_hash() function.\n"      \
+    "\n"                                                                \
+    "The *length* argument defines if the TLSH data size hint has to"   \
+    " be considered by the comparison process.\n"                       \
+    "\n"                                                                \
+    "The returned value is a difference level provided as an integer"   \
+    " value or *None* in case of error."                                \
+)
+
+    result = NULL;
+
+    length = 1;
+
+    ret = PyArg_ParseTuple(args, "ss|p", &ha, &hb, &length);
+    if (!ret) goto exit;
+
+    status = compare_tlsh_hash(ha, hb, length, &diff);
+
+    if (status)
+        result = PyLong_FromLong(diff);
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : super = module dont la définition est à compléter.           *
+*                                                                             *
+*  Description : Définit une extension du module 'bhash' à compléter.         *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool populate_bhash_module_with_tlsh(PyObject *super)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    static PyMethodDef py_tlsh_methods[] = {
+        BHASH_COMPUTE_CONTENT_TLSH_HASH_METHOD,
+        BHASH_IS_VALID_TLSH_HASH_METHOD,
+        BHASH_COMPARE_TLSH_HASH_METHOD,
+        { NULL }
+    };
+
+    result = register_python_module_methods(super, py_tlsh_methods);
+
+    return result;
+
+}
diff --git a/plugins/bhash/python/tlsh.h b/plugins/bhash/python/tlsh.h
new file mode 100644
index 0000000..7312b97
--- /dev/null
+++ b/plugins/bhash/python/tlsh.h
@@ -0,0 +1,39 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tlsh.h - équivalent Python du fichier "plugins/bhash/tlsh.h"
+ *
+ * Copyright (C) 2021 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_BHASH_PYTHON_TLSH_H
+#define _PLUGINS_BHASH_PYTHON_TLSH_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit une extension du module 'bhash' à compléter. */
+bool populate_bhash_module_with_tlsh(PyObject *);
+
+
+
+#endif  /* _PLUGINS_BHASH_PYTHON_TLSH_H */
diff --git a/plugins/bhash/rich.c b/plugins/bhash/rich.c
new file mode 100644
index 0000000..b5bed4e
--- /dev/null
+++ b/plugins/bhash/rich.c
@@ -0,0 +1,181 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * rich.c - calculs d'empreintes relatifs aux en-têtes PE enrichis
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "rich.h"
+
+
+#include <format/known.h>
+#include <plugins/pe/rich.h>
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : format = format en place à consulter.                        *
+*                csum   = empreinte à déterminer. [OUT]                       *
+*                                                                             *
+*  Description : Calcule la valeur pour empreinte d'en-tête PE enrichi.       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool compute_pe_rich_header_checksum(const GPeFormat *format, uint32_t *csum)
+{
+    bool result;                            /* Bilan à retourner           */
+    mrange_t area;                          /* Zone couverte par l'en-tête */
+    GBinContent *content;                   /* Contenu à parcourir         */
+    vmpa2t start;                           /* Position de départ          */
+    phys_t offset;                          /* Position du début d'en-tête */
+    const bin_t *data;                      /* Données brutes à analyser   */
+    phys_t i;                               /* Boucle de parcours #0       */
+    comp_id_t *ids;                         /* Identifiants à traiter      */
+    size_t count;                           /* Quantité de ces identifiants*/
+    size_t k;                               /* Boucle de parcours #1       */
+
+    result = g_pe_format_get_rich_header_area(format, &area);
+
+    if (!result)
+        *csum = 0;
+
+    else
+    {
+
+#define rol32(word, shift) \
+        ((uint32_t)word) << (shift & 31) | ((uint32_t)word) >> (32 - (shift & 31))
+
+        /* Première source de calculs */
+
+        content = g_known_format_get_content(G_KNOWN_FORMAT(format));
+
+        g_binary_content_compute_start_pos(content, &start);
+
+        offset = get_phy_addr(get_mrange_addr(&area)) - get_phy_addr(&start);
+
+        *csum = offset;
+
+        data = g_binary_content_get_raw_access(content, &start, offset);
+        if (data == NULL)
+        {
+            g_object_unref(G_OBJECT(content));
+            result = false;
+            goto exit;
+        }
+
+        for (i = 0; i < offset; i++)
+        {
+            /* Saut du champ e_lfanew, non initialisé lors de la construction */
+            if (0x3c <= i && i < 0x40)
+                continue;
+
+            *csum += rol32(data[i], i);
+
+        }
+
+        g_object_unref(G_OBJECT(content));
+
+        /* Seconde source de calculs */
+
+        ids = g_pe_format_get_comp_ids(format, &count);
+
+        for (k = 0; k < count; k++)
+            *csum += rol32((ids[k].prod_id << 16) | ids[k].minor_cv, ids[k].count);
+
+        if (ids != NULL)
+            free(ids);
+
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : format = format en place à consulter.                        *
+*                pv     = forme d'empreinte à construire.                     *
+*                                                                             *
+*  Description : Calcule l'empreinte des informations d'en-tête PE enrichi.   *
+*                                                                             *
+*  Retour      : Empreinte MD5 calculée ou NULL en cas d'échec.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *compute_pe_rich_header_hash(const GPeFormat *format, bool pv)
+{
+    char *result;                           /* Empreinte à retourner       */
+    comp_id_t *ids;                         /* Identifiants à traiter      */
+    size_t count;                           /* Quantité de ces identifiants*/
+    GChecksum *checksum;                    /* Preneur d'empreinte         */
+    size_t i;                               /* Boucle de parcours          */
+    uint32_t value;                         /* Valeur à prendre en compte  */
+
+    ids = g_pe_format_get_comp_ids(format, &count);
+
+    if (ids == NULL)
+        result = NULL;
+
+    else
+    {
+        checksum = g_checksum_new(G_CHECKSUM_MD5);
+
+        if (!pv)
+        {
+            g_checksum_update(checksum, "DanS", 4);
+            g_checksum_update(checksum, "\x00\x00\x00\x00", 4);
+        }
+
+        for (i = 0; i < count; i++)
+        {
+            if (pv && i == 0)
+                continue;
+
+            value = (ids[i].prod_id << 16) | ids[i].minor_cv;
+            g_checksum_update(checksum, (char *)&value, sizeof(value));
+
+            if (!pv)
+            {
+                value = ids[i].count;
+                g_checksum_update(checksum, (char *)&value, sizeof(value));
+            }
+
+        }
+
+        result = strdup(g_checksum_get_string(checksum));
+
+        g_checksum_free(checksum);
+
+        free(ids);
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/bhash/rich.h b/plugins/bhash/rich.h
new file mode 100644
index 0000000..051c9f6
--- /dev/null
+++ b/plugins/bhash/rich.h
@@ -0,0 +1,44 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * rich.h - prototypes pour les calculs d'empreintes relatifs aux en-têtes PE enrichis
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_BHASH_RICH_H
+#define _PLUGINS_BHASH_RICH_H
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+#include <plugins/pe/format.h>
+
+
+
+/* Calcule la valeur pour empreinte d'en-tête PE enrichi. */
+bool compute_pe_rich_header_checksum(const GPeFormat *, uint32_t *);
+
+/* Calcule l'empreinte des informations d'en-tête PE enrichi. */
+char *compute_pe_rich_header_hash(const GPeFormat *, bool);
+
+
+
+#endif  /* _PLUGINS_BHASH_RICH_H */
diff --git a/plugins/bhash/tlsh.c b/plugins/bhash/tlsh.c
new file mode 100644
index 0000000..92064de
--- /dev/null
+++ b/plugins/bhash/tlsh.c
@@ -0,0 +1,724 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tlsh.c - calculs d'empreintes selon l'algorithme TLSH
+ *
+ * Copyright (C) 2021 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "tlsh.h"
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+
+
+#include <common/hex.h>
+#include <common/pearson.h>
+
+
+#define BUCKETS_COUNT 256
+
+#define BUCKETS_USED 128
+
+#define HASH_CODE_SIZE ((BUCKETS_USED * 2) / 8)
+
+#define TLSH_STRING_LEN (2 + 2 + 2 + 2 + HASH_CODE_SIZE * 2)
+
+#define TLSH_LENGTH_MULTIPLIER 12
+#define TLSH_QRATIO_MULTIPLIER 12
+
+
+/* Mémorisation des informations brutes */
+typedef struct _tlsh_info_t
+{
+    phys_t data_length;                     /* Taille des données traitées */
+
+    uint32_t buckets[BUCKETS_COUNT];        /* Bac pour compteurs          */
+    uint8_t checksum;                       /* Empreinte globale           */
+
+    uint32_t q1;                            /* Première valeur pivot (75%) */
+    uint32_t q2;                            /* Deuxième valeur pivot (50%) */
+    uint32_t q3;                            /* Troisième valeur pivot (25%)*/
+
+    uint8_t q1_ratio : 4;                   /* Ratio de portion #1         */
+    uint8_t q2_ratio : 4;                   /* Ratio de portion #2         */
+
+    uint8_t captured_length;                /* Tranche associée à la taille*/
+
+} tlsh_info_t;
+
+/* Récupération des informations d'une empreinte */
+typedef struct _recovered_tlsh_info_t
+{
+    uint8_t checksum;                       /* Empreinte globale           */
+
+    uint8_t captured_length;                /* Tranche associée à la taille*/
+
+    uint8_t q1_ratio : 4;                   /* Ratio de portion #1         */
+    uint8_t q2_ratio : 4;                   /* Ratio de portion #2         */
+
+    uint8_t code[HASH_CODE_SIZE];           /* Coeur de l'empreinte        */
+
+} recovered_tlsh_info_t;
+
+
+/* Détermine l'indice du compteur destiné à un triplet d'octets. */
+static uint8_t define_tlsh_mapping(uint8_t, uint8_t, uint8_t, uint8_t);
+
+/* Définit tous les compteurs associés aux triplets d'octets. */
+static bool fill_tlsh_buckets(const GBinContent *, tlsh_info_t *);
+
+/* Compare deux compteurs de triplets d'octets. */
+static int compare_tlsh_buckets(const uint32_t *, const uint32_t *);
+
+/* Détermine les points de pivot au sein des bacs de compteurs. */
+static void find_tlsh_quartiles(tlsh_info_t *);
+
+/* Construit une empreinte TLSH sur les bases calculées. */
+static char *build_tlsh_hash(const tlsh_info_t *, bool);
+
+/* Reconstruit les informations portées par une empreinte TLSH. */
+static bool recover_tlsh_hash(const char *, recovered_tlsh_info_t *);
+
+/* Calcule une différence entre deux valeurs selon deux axes. */
+static int32_t diff_tlsh_values_two_way(uint32_t, uint32_t, uint32_t);
+
+/* Calcule le degré de différence entre deux octets TLSH. */
+static uint8_t diff_tlsh_bits(uint8_t, uint8_t);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : salt = sel à intégrer à la préparation.                      *
+*                b0   = premier octet à manipuler.                            *
+*                b1   = deuxième octet à manipuler.                           *
+*                b2   = troisième octet à manipuler.                          *
+*                                                                             *
+*  Description : Détermine l'indice du compteur destiné à un triplet d'octets.*
+*                                                                             *
+*  Retour      : Indice du bac de destination pour décompte.                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static uint8_t define_tlsh_mapping(uint8_t salt, uint8_t b0, uint8_t b1, uint8_t b2)
+{
+    uint8_t result;                         /* Valeur à retourner          */
+    const uint8_t *table;                   /* Permutations à utiliser     */
+
+    table = (const uint8_t *)get_pearson_permutations();
+
+    result = table[salt ^ b0];
+    result = table[result ^ b1];
+    result = table[result ^ b2];
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à consulter.                       *
+*                info    = informations à constituer en partie. [OUT]         *
+*                                                                             *
+*  Description : Définit tous les compteurs associés aux triplets d'octets.   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool fill_tlsh_buckets(const GBinContent *content, tlsh_info_t *info)
+{
+    bool result;                            /* Bilan à retourner           */
+    phys_t len;                             /* Taille des données présentes*/
+    vmpa2t start;                           /* Première position de donnée */
+    const uint8_t *data;                    /* Données à parcourir         */
+    phys_t i;                               /* Boucle de parcours          */
+    uint8_t index;                          /* Indice de compteur visé     */
+
+    result = false;
+
+    len = g_binary_content_compute_size(content);
+
+    if (len < 5)
+        goto exit;
+
+    g_binary_content_compute_start_pos(content, &start);
+    data = g_binary_content_get_raw_access(content, &start, len);
+
+    info->data_length = len;
+
+    memset(info->buckets, 0, sizeof(uint32_t) * BUCKETS_COUNT);
+    info->checksum = 0;
+
+    for (i = 0; i <= (len - 5); i++)
+    {
+        info->checksum = define_tlsh_mapping(1, data[i + 4], data[i + 3], info->checksum);
+
+        index = define_tlsh_mapping( 49, data[i + 4], data[i + 3], data[i + 2]);
+        info->buckets[index]++;
+
+        index = define_tlsh_mapping( 12, data[i + 4], data[i + 3], data[i + 1]);
+        info->buckets[index]++;
+
+        index = define_tlsh_mapping( 84, data[i + 4], data[i + 3], data[i + 0]);
+        info->buckets[index]++;
+
+        index = define_tlsh_mapping(178, data[i + 4], data[i + 2], data[i + 1]);
+        info->buckets[index]++;
+
+        index = define_tlsh_mapping(166, data[i + 4], data[i + 2], data[i + 0]);
+        info->buckets[index]++;
+
+        index = define_tlsh_mapping(230, data[i + 4], data[i + 1], data[i + 0]);
+        info->buckets[index]++;
+
+    }
+
+    result = true;
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a = premier bacs de décompte à consulter.                    *
+*                b = second bacs de décompte à consulter.                     *
+*                                                                             *
+*  Description : Compare deux compteurs de triplets d'octets.                 *
+*                                                                             *
+*  Retour      : Bilan de comparaison.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int compare_tlsh_buckets(const uint32_t *a, const uint32_t *b)
+{
+    int result;                             /* Bilan à retourner           */
+
+    if (*a < *b)
+        result = -1;
+
+    else if (*a > *b)
+        result = 1;
+
+    else
+        result = 0;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info = informations à constituer en partie. [OUT]            *
+*                                                                             *
+*  Description : Détermine les points de pivot au sein des bacs de compteurs. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void find_tlsh_quartiles(tlsh_info_t *info)
+{
+    uint32_t copy[BUCKETS_USED];           /* Copie modifiable            */
+
+    memcpy(copy, info->buckets, BUCKETS_USED * sizeof(uint32_t));
+
+    qsort(copy, BUCKETS_USED, sizeof(uint32_t), (__compar_fn_t)compare_tlsh_buckets);
+
+    /**
+     * q1 = quantité telle que 75% des buckets >= q1
+     * q2 = quantité telle que 50% des buckets >= q2
+     * q3 = quantité telle que 25% des buckets >= q3
+     */
+
+    info->q1 = copy[BUCKETS_USED / 4 - 1];
+
+    info->q2 = copy[BUCKETS_USED / 2 - 1];
+
+    info->q3 = copy[(3 * BUCKETS_USED) / 4 - 1];
+
+}
+
+
+
+
+
+#include <math.h>
+
+
+#define LOG_1_5 0.4054651
+#define LOG_1_3 0.26236426
+#define LOG_1_1 0.095310180
+
+
+unsigned char l_capturing(unsigned int len) {
+    int i;
+    if( len <= 656 ) {
+        i = (int) floor( logf((float) len) / LOG_1_5 );
+    } else if( len <= 3199 ) {
+        i = (int) floor( logf((float) len) / LOG_1_3 - 8.72777 );
+    } else {
+        i = (int) floor( logf((float) len) / LOG_1_1 - 62.5472 );
+    }
+
+    return (unsigned char) (i & 0xFF);
+}
+
+
+
+
+
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info    = informations à consulter.                          *
+*                version = affichage de la version ?                          *
+*                                                                             *
+*  Description : Construit une empreinte TLSH sur les bases calculées.        *
+*                                                                             *
+*  Retour      : Empreinte construite ou NULL en cas d'échec.                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *build_tlsh_hash(const tlsh_info_t *info, bool version)
+{
+    char *result;                           /* Empreinte à retourner       */
+    char *pos;                              /* Tête de lecture             */
+    char tmp[HASH_CODE_SIZE * 2];           /* Stockage temporaire         */
+    char *code;                             /* Empreinte des compteurs     */
+    size_t i;                               /* Boucle de parcours          */
+    size_t offset;                          /* Rang d'intervention         */
+
+    static char hex_lookup[] = "0123456789ABCDEF";
+
+    result = malloc(TLSH_STRING_LEN + 1);
+
+    /* Indication de version ? */
+
+    if (version)
+    {
+        result[0] = 'T';
+        result[1] = '1';
+        pos = result + 2;
+    }
+    else
+        pos = result;
+
+    /* Empreinte concise */
+
+    *(pos++) = hex_lookup[info->checksum & 0xf];
+    *(pos++) = hex_lookup[(info->checksum >> 4) & 0xf];
+
+    /* Taille représentée */
+
+    *(pos++) = hex_lookup[info->captured_length & 0xf];
+    *(pos++) = hex_lookup[(info->captured_length >> 4) & 0xf];
+
+    /* Ratios */
+
+    *(pos++) = hex_lookup[info->q1_ratio];
+
+    *(pos++) = hex_lookup[info->q2_ratio];
+
+    /* Empreinte du contenu binaire */
+
+    code = &tmp[HASH_CODE_SIZE - 1];
+
+    for (i = 0; i < BUCKETS_USED; i++)
+    {
+        if ((i % 4) == 0)
+            *code = 0;
+
+        offset = (i % 4) * 2;
+
+        if (info->buckets[i] <= info->q1)
+            ;
+
+        else if (info->buckets[i] <= info->q2)
+            (*code) |= (1 << offset);
+
+        else if (info->buckets[i] <= info->q3)
+            (*code) |= (2 << offset);
+
+        else
+            (*code) |= (3 << offset);
+
+        if (((i + 1) % 4) == 0)
+            code--;
+
+    }
+
+    encode_hex(tmp, HASH_CODE_SIZE, false, pos);
+
+    assert(pos + (HASH_CODE_SIZE * 2) < result + (TLSH_STRING_LEN + 1));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : content = contenu binaire à consulter.                       *
+*                version = affichage de la version ?                          *
+*                                                                             *
+*  Description : Calcule l'empreinte TLSH d'un contenu binaire.               *
+*                                                                             *
+*  Retour      : Empreinte TLSH calculée ou NULL en cas d'échec.              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *compute_content_tlsh_hash(const GBinContent *content, bool version)
+{
+    char *result;                           /* Empreinte à retourner       */
+    bool status;                            /* Bilan d'un appel            */
+    tlsh_info_t info;                       /* Informations brutes         */
+
+    result = NULL;
+
+    status = fill_tlsh_buckets(content, &info);
+    if (!status) goto exit;
+
+    find_tlsh_quartiles(&info);
+
+    if (info.q3 == 0)
+        goto exit;
+
+    info.q1_ratio = ((float)(info.q1 * 100) / (float)info.q3);
+    info.q2_ratio = ((float)(info.q2 * 100) / (float)info.q3);
+
+    info.captured_length = l_capturing(info.data_length);
+
+    result = build_tlsh_hash(&info, version);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : h = chaîne de caratères à valider.                           *
+*                                                                             *
+*  Description : Indique si une chaîne représente à priori une empreinte TLSH.*
+*                                                                             *
+*  Retour      : Bilan de l'analyse.                                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool is_valid_tlsh_hash(const char *h)
+{
+    bool result;                            /* Bilan à renvoyer            */
+    size_t len;                             /* Taille de la chaîne         */
+
+    len = strlen(h);
+
+    if (len == (TLSH_STRING_LEN - 2))
+        result = true;
+
+    else if (len == TLSH_STRING_LEN)
+        result = (h[0] == 'T' && h[1] == '1');
+
+    else
+        result = false;
+
+    // TODO check hex
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : h    = chaîne de caratères à consulter.                      *
+*                info = informations portées par une empreinte TLSH.          *
+*                                                                             *
+*  Description : Reconstruit les informations portées par une empreinte TLSH. *
+*                                                                             *
+*  Retour      : Bilan de la reconstruction.                                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool recover_tlsh_hash(const char *h, recovered_tlsh_info_t *info)
+{
+    bool result;                            /* Bilan à renvoyer            */
+    const char *pos;                        /* Tête de lecture             */
+    uint8_t value;                          /* Valeur récupérée            */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = is_valid_tlsh_hash(h);
+    if (!result) goto exit;
+
+    /* Indication de version ? */
+
+    pos = (h[0] == 'T' ? h + 2 : h);
+
+    /* Empreinte concise */
+
+    result = decode_hex_digit(pos++, &value);
+    assert(result);
+    if (!result) goto exit;
+
+    info->checksum = value;
+
+    result = decode_hex_digit(pos++, &value);
+    assert(result);
+    if (!result) goto exit;
+
+    info->checksum |= (value << 4);
+
+    /* Taille représentée */
+
+    result = decode_hex_digit(pos++, &value);
+    assert(result);
+    if (!result) goto exit;
+
+    info->captured_length = value;
+
+    result = decode_hex_digit(pos++, &value);
+    assert(result);
+    if (!result) goto exit;
+
+    info->captured_length |= (value << 4);
+
+    /* Ratios */
+
+    result = decode_hex_digit(pos++, &value);
+    assert(result);
+    if (!result) goto exit;
+
+    info->q1_ratio = value;
+
+    result = decode_hex_digit(pos++, &value);
+    assert(result);
+    if (!result) goto exit;
+
+    info->q2_ratio = value;
+
+    /* Empreinte du contenu binaire */
+
+    for (i = 0; i < HASH_CODE_SIZE; i++)
+    {
+        result = decode_hex_digit(pos++, &value);
+        assert(result);
+        if (!result) goto exit;
+
+        info->code[HASH_CODE_SIZE - i - 1] = (value << 4);
+
+        result = decode_hex_digit(pos++, &value);
+        assert(result);
+        if (!result) goto exit;
+
+        info->code[HASH_CODE_SIZE - i - 1] |= value;
+
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a     = première valeur à analyser.                          *
+*                b     = seconde valeur à analyser.                           *
+*                range = espace de valeurs à considérer.                      *
+*                                                                             *
+*  Description : Calcule une différence entre deux valeurs selon deux axes.   *
+*                                                                             *
+*  Retour      : Différence déterminée entre les deux valeurs.                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int32_t diff_tlsh_values_two_way(uint32_t a, uint32_t b, uint32_t range)
+{
+    int32_t result;                         /* Différence à retourner      */
+    int32_t diff_1;                         /* Première différence         */
+    int32_t diff_2;                         /* Seconde différence         */
+
+    if (a < b)
+    {
+        diff_1 = b - a;
+        diff_2 = range + a - b;
+    }
+    else
+    {
+        diff_1 = a - b;
+        diff_2 = range + b - a;
+    }
+
+    result = MIN(diff_1, diff_2);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a = premier octet à analyser.                                *
+*                b = second octet à analyser.                                 *
+*                                                                             *
+*  Description : Calcule le degré de différence entre deux octets TLSH.       *
+*                                                                             *
+*  Retour      : Différence déterminée entre les deux octets.                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static uint8_t diff_tlsh_bits(uint8_t a, uint8_t b)
+{
+    uint8_t result;                         /* Valeur à renvoyer           */
+    uint8_t partial;                        /* Différence partielle        */
+
+    result = 0;
+
+    partial = abs(a % 4 - b % 4);
+    result += (partial == 3 ? 6 : partial);
+
+    a /= 4; b /= 4;
+
+    partial = abs(a % 4 - b % 4);
+    result += (partial == 3 ? 6 : partial);
+
+    a /= 4; b /= 4;
+
+    partial = abs(a % 4 - b % 4);
+    result += (partial == 3 ? 6 : partial);
+
+    a /= 4; b /= 4;
+
+    partial = abs(a % 4 - b % 4);
+    result += (partial == 3 ? 6 : partial);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : ha     = première chaîne de caratères à consulter.           *
+*                hb     = première chaîne de caratères à consulter.           *
+*                length = l'indication de taille doit être considérée ?       *
+*                diff   = degré de différence relevé. [OUT]                   *
+*                                                                             *
+*  Description : Détermine la similarité entre deux empreintes TLSH.          *
+*                                                                             *
+*  Retour      : Validité de l'opération menée.                               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool compare_tlsh_hash(const char *ha, const char *hb, bool length, int32_t *diff)
+{
+    bool result;                            /* Validité à retourner        */
+    recovered_tlsh_info_t info_a;           /* Empreinte à manipuler #0    */
+    recovered_tlsh_info_t info_b;           /* Empreinte à manipuler #1    */
+    int32_t partial;                        /* Différence calculée         */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = recover_tlsh_hash(ha, &info_a);
+    if (!result) goto exit;
+
+    result = recover_tlsh_hash(hb, &info_b);
+    if (!result) goto exit;
+
+    *diff = 0;
+
+    /* Empreinte concise */
+
+    if (info_a.checksum != info_b.checksum)
+        *diff += 1;
+
+    /* Taille représentée */
+
+    if (length)
+    {
+        partial = diff_tlsh_values_two_way(info_a.captured_length, info_b.captured_length, 2 << 8);
+
+        if (partial > 1)
+            partial *= TLSH_LENGTH_MULTIPLIER;
+
+        *diff += partial;
+
+    }
+
+    /* Ratios */
+
+    partial = diff_tlsh_values_two_way(info_a.q1_ratio, info_b.q1_ratio, 2 << 4);
+
+    if (partial > 1)
+        partial *= TLSH_QRATIO_MULTIPLIER;
+
+    *diff += partial;
+
+    partial = diff_tlsh_values_two_way(info_a.q2_ratio, info_b.q2_ratio, 2 << 4);
+
+    if (partial > 1)
+        partial *= TLSH_QRATIO_MULTIPLIER;
+
+    *diff += partial;
+
+    /* Empreinte du contenu binaire */
+
+    for (i = 0; i < HASH_CODE_SIZE; i++)
+        *diff += diff_tlsh_bits(info_a.code[i], info_b.code[i]);
+
+ exit:
+
+    return result;
+
+}
diff --git a/plugins/bhash/tlsh.h b/plugins/bhash/tlsh.h
new file mode 100644
index 0000000..d8805ad
--- /dev/null
+++ b/plugins/bhash/tlsh.h
@@ -0,0 +1,46 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tlsh.h - prototypes pour les calculs d'empreintes selon l'algorithme TLSH
+ *
+ * Copyright (C) 2021 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_BHASH_TLSH_H
+#define _PLUGINS_BHASH_TLSH_H
+
+
+#include <stdbool.h>
+
+
+#include <analysis/content.h>
+
+
+
+/* Calcule l'empreinte TLSH d'un contenu binaire. */
+char *compute_content_tlsh_hash(const GBinContent *, bool);
+
+/* Indique si une chaîne représente à priori une empreinte TLSH. */
+bool is_valid_tlsh_hash(const char *);
+
+/* Détermine la similarité entre deux empreintes TLSH. */
+bool compare_tlsh_hash(const char *, const char *, bool, int32_t *);
+
+
+
+#endif  /* _PLUGINS_BHASH_TLSH_H */
diff --git a/plugins/pe/python/routine.c b/plugins/pe/python/routine.c
index 89075ea..cebeb2a 100644
--- a/plugins/pe/python/routine.c
+++ b/plugins/pe/python/routine.c
@@ -62,6 +62,9 @@ static int py_pe_exported_routine_set_ordinal(PyObject *, PyObject *, void *);
     " imported from other PE file symbol."
 
 
+/* Fournit la position du symbole dans les importations. */
+static PyObject *py_pe_imported_routine_get_index(PyObject *, void *);
+
 /* Fournit le fichier DLL visé par une importation de format PE. */
 static PyObject *py_pe_imported_routine_get_library(PyObject *, void *);
 
@@ -285,6 +288,44 @@ int convert_to_pe_exported_routine(PyObject *arg, void *dst)
 *  Paramètres  : self    = objet Python concerné par l'appel.                 *
 *                closure = non utilisé ici.                                   *
 *                                                                             *
+*  Description : Fournit la position du symbole dans les importations.        *
+*                                                                             *
+*  Retour      : Indice positif ou nul.                                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_pe_imported_routine_get_index(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GPeImportedRoutine *routine;            /* Version native              */
+    size_t index;                           /* Position dans les imports   */
+
+#define PE_IMPORTED_ROUTINE_INDEX_ATTRIB PYTHON_GET_DEF_FULL        \
+(                                                                   \
+    index, py_pe_imported_routine,                                  \
+    "Position of the symbol inside the importations table.\n"       \
+    "\n"                                                            \
+    "The returned value is an integer."                             \
+)
+
+    routine = G_PE_IMPORTED_ROUTINE(pygobject_get(self));
+
+    index = g_pe_imported_routine_get_index(routine);
+
+    result = PyLong_FromSize_t(index);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
 *  Description : Fournit le fichier DLL visé par une importation de format PE.*
 *                                                                             *
 *  Retour      : Désignation d'une bibliothèque Windows.                      *
@@ -299,7 +340,7 @@ static PyObject *py_pe_imported_routine_get_library(PyObject *self, void *closur
     GPeImportedRoutine *routine;            /* Version native              */
     const char *library;                    /* Nom de bibliothèque         */
 
-#define PE_IMPORTED_ROUTINE_ORDINAL_ATTRIB PYTHON_GETSET_DEF_FULL   \
+#define PE_IMPORTED_ROUTINE_LIBRARY_ATTRIB PYTHON_GETSET_DEF_FULL   \
 (                                                                   \
     library, py_pe_imported_routine,                                \
     "Imported DLL's name for the symbol.\n"                         \
@@ -373,7 +414,8 @@ PyTypeObject *get_python_pe_imported_routine_type(void)
     };
 
     static PyGetSetDef py_pe_imported_routine_getseters[] = {
-        PE_IMPORTED_ROUTINE_ORDINAL_ATTRIB,
+        PE_IMPORTED_ROUTINE_INDEX_ATTRIB,
+        PE_IMPORTED_ROUTINE_LIBRARY_ATTRIB,
         { NULL }
     };
 
diff --git a/plugins/pe/routine.c b/plugins/pe/routine.c
index 3f2e5ba..5973487 100644
--- a/plugins/pe/routine.c
+++ b/plugins/pe/routine.c
@@ -72,6 +72,7 @@ struct _GPeImportedRoutine
     GPeExportedRoutine parent;              /* A laisser en premier        */
 
     char *library;                          /* Bibliothèque de rattachement*/
+    size_t index;                           /* Position dans les imports   */
 
 };
 
@@ -355,7 +356,8 @@ static void g_pe_imported_routine_finalize(GPeImportedRoutine *routine)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : name = désignation humainement lisible.                      *
+*  Paramètres  : name  = désignation humainement lisible.                     *
+*                index = position du symbole dans les importations.           *
 *                                                                             *
 *  Description : Crée une représentation de routine importée pour format PE.  *
 *                                                                             *
@@ -365,7 +367,7 @@ static void g_pe_imported_routine_finalize(GPeImportedRoutine *routine)
 *                                                                             *
 ******************************************************************************/
 
-GPeImportedRoutine *g_pe_imported_routine_new(const char *name)
+GPeImportedRoutine *g_pe_imported_routine_new(const char *name, size_t index)
 {
     GPeImportedRoutine *result;                    /* Structure à retourner       */
 
@@ -374,6 +376,31 @@ GPeImportedRoutine *g_pe_imported_routine_new(const char *name)
     if (name != NULL)
         g_binary_routine_set_name(G_BIN_ROUTINE(result), strdup(name));
 
+    result->index = index;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : routine = routine ayant pour origine un fichier PE.          *
+*                                                                             *
+*  Description : Fournit la position du symbole dans les importations.        *
+*                                                                             *
+*  Retour      : Indice positif ou nul.                                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+size_t g_pe_imported_routine_get_index(const GPeImportedRoutine *routine)
+{
+    size_t result;                          /* Indice à retourner          */
+
+    result = routine->index;
+
     return result;
 
 }
diff --git a/plugins/pe/routine.h b/plugins/pe/routine.h
index 62faaaa..f0ec71e 100644
--- a/plugins/pe/routine.h
+++ b/plugins/pe/routine.h
@@ -98,7 +98,10 @@ typedef struct _GPeImportedRoutineClass GPeImportedRoutineClass;
 GType g_pe_imported_routine_get_type(void);
 
 /* Crée une représentation de routine importée pour format PE. */
-GPeImportedRoutine *g_pe_imported_routine_new(const char *);
+GPeImportedRoutine *g_pe_imported_routine_new(const char *, size_t);
+
+/* Fournit la position du symbole dans les importations. */
+size_t g_pe_imported_routine_get_index(const GPeImportedRoutine *);
 
 /* Définit le fichier DLL visé par une importation de format PE. */
 void g_pe_imported_routine_set_library(GPeImportedRoutine *, const char *);
diff --git a/plugins/pe/symbols.c b/plugins/pe/symbols.c
index 9217466..94f97a8 100644
--- a/plugins/pe/symbols.c
+++ b/plugins/pe/symbols.c
@@ -196,6 +196,7 @@ static bool load_pe_imported_symbols(GPeFormat *format, wgroup_id_t gid, GtkStat
     GBinFormat *base;                       /* Version basique du format   */
     GExeFormat *exe;                        /* Autre vision du format      */
     const GBinContent *content;             /* Contenu binaire à lire      */
+    size_t counter;                         /* Compteur d'importations     */
     image_import_descriptor *iter;          /* Boucle de parcours          */
     vmpa2t dll;                             /* Nom de la DLL concernée     */
     bool ret;                               /* Bilan d'un traitement       */
@@ -223,6 +224,8 @@ static bool load_pe_imported_symbols(GPeFormat *format, wgroup_id_t gid, GtkStat
 
     content = G_KNOWN_FORMAT(format)->content;
 
+    counter = 0;
+
     for (iter = imports; iter->original_first_thunk != 0; iter++)
     {
         /* Bibliothèque impactée */
@@ -262,7 +265,7 @@ static bool load_pe_imported_symbols(GPeFormat *format, wgroup_id_t gid, GtkStat
 
             if (val64 & 0x8000000000000000)
             {
-                routine = g_pe_imported_routine_new(NULL);
+                routine = g_pe_imported_routine_new(NULL, counter++);
 
                 g_pe_exported_routine_set_ordinal(G_PE_EXPORTED_ROUTINE(routine), val64 & 0xffff);
 
@@ -280,7 +283,7 @@ static bool load_pe_imported_symbols(GPeFormat *format, wgroup_id_t gid, GtkStat
                 hint += 2;
 
                 //routine = g_binary_format_decode_routine(base, hint);
-                routine = g_pe_imported_routine_new((char *)hint);
+                routine = g_pe_imported_routine_new((char *)hint, counter++);
 
             }
 
diff --git a/plugins/winordinals/assign.c b/plugins/winordinals/assign.c
index 16b7eaa..03e1165 100644
--- a/plugins/winordinals/assign.c
+++ b/plugins/winordinals/assign.c
@@ -235,7 +235,7 @@ static GOrdinalResolver *g_ordinal_resolver_new(GPeFormat *format, size_t begin,
 static void g_ordinal_resolver_process(GOrdinalResolver *resolver, GtkStatusStack *status)
 {
     GBinFormat *base;                       /* Format basique du binaire   */
-    size_t i;                               /* Boucle de parcours #1       */
+    size_t i;                               /* Boucle de parcours          */
     GBinSymbol *symbol;                     /* Commodité d'accès           */
     uint16_t ordinal;                       /* Ordinal défini              */
     const char *name;                       /* Désignation actuelle        */
-- 
cgit v0.11.2-87-g4458