From ab6b87b7309e2d00926615f6557016bee6ab0b71 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 12 Oct 2023 01:16:21 +0200
Subject: Add two new functions to ROST grammar: modpath and maxcommon.

---
 plugins/apihashing/classics/ror13.c             |  35 +++
 plugins/encodings/rost/base64.c                 |  35 +++
 src/analysis/scan/core.c                        |   4 +
 src/analysis/scan/exprs/handler-int.h           |   6 +-
 src/analysis/scan/exprs/handler.c               | 132 +++++++--
 src/analysis/scan/exprs/handler.h               |   5 +-
 src/analysis/scan/exprs/literal.c               |  52 ++++
 src/analysis/scan/grammar.y                     |   8 +-
 src/analysis/scan/items/Makefile.am             |   2 +
 src/analysis/scan/items/maxcommon.c             | 366 ++++++++++++++++++++++++
 src/analysis/scan/items/maxcommon.h             |  58 ++++
 src/analysis/scan/items/modpath.c               | 301 +++++++++++++++++++
 src/analysis/scan/items/modpath.h               |  58 ++++
 src/analysis/scan/matches/bytes-int.h           |   3 +
 src/analysis/scan/matches/bytes.c               |  56 ++++
 src/analysis/scan/matches/bytes.h               |   6 +
 src/analysis/scan/matches/pending.c             |  55 +++-
 src/analysis/scan/matches/pending.h             |   6 +
 src/analysis/scan/patterns/modifier-int.h       |   4 +
 src/analysis/scan/patterns/modifier.c           |  27 ++
 src/analysis/scan/patterns/modifier.h           |   3 +
 src/analysis/scan/patterns/modifiers/hex.c      |  35 +++
 src/analysis/scan/patterns/modifiers/list.c     |  32 +++
 src/analysis/scan/patterns/modifiers/plain.c    |  35 +++
 src/analysis/scan/patterns/modifiers/rev.c      |  35 +++
 src/analysis/scan/patterns/modifiers/xor.c      |  43 +++
 src/analysis/scan/patterns/token.c              |  28 ++
 src/analysis/scan/patterns/token.h              |   3 +
 src/analysis/scan/patterns/tokens/nodes/plain.c |  42 ++-
 src/analysis/scan/patterns/tokens/nodes/plain.h |   3 +
 src/analysis/scan/rule.c                        |   3 +
 tests/analysis/scan/functions.py                |  26 ++
 32 files changed, 1475 insertions(+), 32 deletions(-)
 create mode 100644 src/analysis/scan/items/maxcommon.c
 create mode 100644 src/analysis/scan/items/maxcommon.h
 create mode 100644 src/analysis/scan/items/modpath.c
 create mode 100644 src/analysis/scan/items/modpath.h

diff --git a/plugins/apihashing/classics/ror13.c b/plugins/apihashing/classics/ror13.c
index 6e063f8..1b2421d 100644
--- a/plugins/apihashing/classics/ror13.c
+++ b/plugins/apihashing/classics/ror13.c
@@ -62,6 +62,9 @@ static uint32_t compute_ror13(const sized_binary_t *);
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_ror13_modifier_transform(const GScanRor13Modifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_ror13_modifier_get_path(const GScanRor13Modifier *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -100,6 +103,7 @@ static void g_scan_ror13_modifier_class_init(GScanRor13ModifierClass *klass)
     modifier->get_name = (get_scan_modifier_name_fc)g_scan_ror13_modifier_get_name;
 
     modifier->transform = (transform_scan_token_fc)g_scan_ror13_modifier_transform;
+    modifier->get_path = (get_modifier_path)g_scan_ror13_modifier_get_path;
 
 }
 
@@ -286,3 +290,34 @@ static bool g_scan_ror13_modifier_transform(const GScanRor13Modifier *modifier,
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_ror13_modifier_get_path(const GScanRor13Modifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (*index > 0)
+    {
+        result = NULL;
+        (*index)--;
+    }
+
+    else
+        result = strdup("rev");
+
+    return result;
+
+}
diff --git a/plugins/encodings/rost/base64.c b/plugins/encodings/rost/base64.c
index 85e268d..15a3ec1 100644
--- a/plugins/encodings/rost/base64.c
+++ b/plugins/encodings/rost/base64.c
@@ -72,6 +72,9 @@ static bool g_scan_base64_modifier_can_handle_arg(const GScanBase64Modifier *, c
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_base64_modifier_transform_with_arg(const GScanBase64Modifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_base64_modifier_get_path(const GScanBase64Modifier *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -112,6 +115,7 @@ static void g_scan_base64_modifier_class_init(GScanBase64ModifierClass *klass)
     modifier->transform = (transform_scan_token_fc)g_scan_base64_modifier_transform;
     modifier->can_handle = (can_token_modifier_handle_arg)g_scan_base64_modifier_can_handle_arg;
     modifier->transform_with = (transform_scan_token_with_fc)g_scan_base64_modifier_transform_with_arg;
+    modifier->get_path = (get_modifier_path)g_scan_base64_modifier_get_path;
 
 }
 
@@ -511,3 +515,34 @@ static bool g_scan_base64_modifier_transform_with_arg(const GScanBase64Modifier
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_base64_modifier_get_path(const GScanBase64Modifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (*index > 3)
+    {
+        result = NULL;
+        (*index) -= 3;
+    }
+
+    else
+        result = strdup("base64");
+
+    return result;
+
+}
diff --git a/src/analysis/scan/core.c b/src/analysis/scan/core.c
index fdee1c7..7b1a455 100644
--- a/src/analysis/scan/core.c
+++ b/src/analysis/scan/core.c
@@ -30,6 +30,8 @@
 
 #include "items/count.h"
 #include "items/datasize.h"
+#include "items/maxcommon.h"
+#include "items/modpath.h"
 #include "items/uint.h"
 #include "items/console/log.h"
 #ifdef INCLUDE_MAGIC_SUPPORT
@@ -249,6 +251,8 @@ bool populate_main_scan_namespace(GScanNamespace *space)
 
     if (result) result = REGISTER_FUNC(space, g_scan_count_function_new());
     if (result) result = REGISTER_FUNC(space, g_scan_datasize_function_new());
+    if (result) result = REGISTER_FUNC(space, g_scan_maxcommon_function_new());
+    if (result) result = REGISTER_FUNC(space, g_scan_modpath_function_new());
 
     if (result) result = REGISTER_FUNC(space, g_scan_uint_function_new(MDS_8_BITS_SIGNED, SRE_LITTLE));
     if (result) result = REGISTER_FUNC(space, g_scan_uint_function_new(MDS_8_BITS_UNSIGNED, SRE_LITTLE));
diff --git a/src/analysis/scan/exprs/handler-int.h b/src/analysis/scan/exprs/handler-int.h
index f707fdb..a38dd03 100644
--- a/src/analysis/scan/exprs/handler-int.h
+++ b/src/analysis/scan/exprs/handler-int.h
@@ -37,7 +37,9 @@ struct _GScanPatternHandler
 {
     GScanExpression parent;                 /* A laisser en premier        */
 
-    GSearchPattern *pattern;                /* Motif associé               */
+    GSearchPattern **patterns;              /* Motifs associés             */
+    size_t count;                           /* Nombre de ces motifs        */
+
     ScanHandlerType type;                   /* Manipulation attendue       */
 
 };
@@ -51,7 +53,7 @@ struct _GScanPatternHandlerClass
 
 
 /* Met en place une manipulation de correspondances établies. */
-bool g_scan_pattern_handler_create(GScanPatternHandler *, GSearchPattern *, ScanHandlerType);
+bool g_scan_pattern_handler_create(GScanPatternHandler *, GSearchPattern ** const, size_t, ScanHandlerType);
 
 
 
diff --git a/src/analysis/scan/exprs/handler.c b/src/analysis/scan/exprs/handler.c
index 1676522..1b6416e 100644
--- a/src/analysis/scan/exprs/handler.c
+++ b/src/analysis/scan/exprs/handler.c
@@ -122,7 +122,9 @@ static void g_scan_pattern_handler_class_init(GScanPatternHandlerClass *klass)
 
 static void g_scan_pattern_handler_init(GScanPatternHandler *handler)
 {
-    handler->pattern = NULL;
+    handler->patterns = NULL;
+    handler->count = 0;
+
     handler->type = SHT_RAW;
 
 }
@@ -142,7 +144,10 @@ static void g_scan_pattern_handler_init(GScanPatternHandler *handler)
 
 static void g_scan_pattern_handler_dispose(GScanPatternHandler *handler)
 {
-    g_clear_object(&handler->pattern);
+    size_t i;                               /* Boucle de parcours          */
+
+    for (i = 0; i < handler->count; i++)
+         g_clear_object(&handler->patterns[i]);
 
     G_OBJECT_CLASS(g_scan_pattern_handler_parent_class)->dispose(G_OBJECT(handler));
 
@@ -170,8 +175,9 @@ static void g_scan_pattern_handler_finalize(GScanPatternHandler *handler)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : pattern = motif à impliquer.                                 *
-*                type    = type de manipulation attendue.                     *
+*  Paramètres  : patterns = motifs à impliquer.                               *
+*                count    = quantité de ces motifs.                           *
+*                type     = type de manipulation attendue.                    *
 *                                                                             *
 *  Description : Met en place une manipulation de correspondances établies.   *
 *                                                                             *
@@ -181,13 +187,13 @@ static void g_scan_pattern_handler_finalize(GScanPatternHandler *handler)
 *                                                                             *
 ******************************************************************************/
 
-GScanExpression *g_scan_pattern_handler_new(GSearchPattern *pattern, ScanHandlerType type)
+GScanExpression *g_scan_pattern_handler_new(GSearchPattern ** const patterns, size_t count, ScanHandlerType type)
 {
     GScanExpression *result;                /* Structure à retourner       */
 
     result = g_object_new(G_TYPE_SCAN_PATTERN_HANDLER, NULL);
 
-    if (!g_scan_pattern_handler_create(G_SCAN_PATTERN_HANDLER(result), pattern, type))
+    if (!g_scan_pattern_handler_create(G_SCAN_PATTERN_HANDLER(result), patterns, count, type))
         g_clear_object(&result);
 
     return result;
@@ -197,9 +203,10 @@ GScanExpression *g_scan_pattern_handler_new(GSearchPattern *pattern, ScanHandler
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : handler = instance à initialiser pleinement.                 *
-*                pattern = motif à impliquer.                                 *
-*                type    = type de manipulation attendue.                     *
+*  Paramètres  : handler  = instance à initialiser pleinement.                *
+*                patterns = motifs à impliquer.                               *
+*                count    = quantité de ces motifs.                           *
+*                type     = type de manipulation attendue.                    *
 *                                                                             *
 *  Description : Met en place une manipulation de correspondances établies.   *
 *                                                                             *
@@ -209,15 +216,22 @@ GScanExpression *g_scan_pattern_handler_new(GSearchPattern *pattern, ScanHandler
 *                                                                             *
 ******************************************************************************/
 
-bool g_scan_pattern_handler_create(GScanPatternHandler *handler, GSearchPattern *pattern, ScanHandlerType type)
+bool g_scan_pattern_handler_create(GScanPatternHandler *handler, GSearchPattern ** const patterns, size_t count, ScanHandlerType type)
 {
     bool result;                            /* Bilan à retourner           */
+    size_t i;                               /* Boucle de parcours          */
 
     result = g_scan_expression_create(G_SCAN_EXPRESSION(handler), SRS_WAIT_FOR_SCAN);
     if (!result) goto exit;
 
-    handler->pattern = pattern;
-    g_object_ref(G_OBJECT(pattern));
+    handler->patterns = malloc(count * sizeof(GSearchPattern *));
+    handler->count = count;
+
+    for (i = 0; i < count; i++)
+    {
+        handler->patterns[i] = patterns[i];
+        g_object_ref(G_OBJECT(patterns[i]));
+    }
 
     handler->type = type;
 
@@ -228,6 +242,63 @@ bool g_scan_pattern_handler_create(GScanPatternHandler *handler, GSearchPattern
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : handler = instance à initialiser pleinement.                 *
+*                ctx     = contexte de suivi de l'analyse courante.           *
+*                count   = quantité de correspondances enregistrées. [OUT]    *
+*                                                                             *
+*  Description : Fournit la liste de toutes les correspondances représentées. *
+*                                                                             *
+*  Retour      : Liste courante de correspondances établies.                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GScanMatch **g_scan_pattern_handler_get_all_matches(const GScanPatternHandler *handler, GScanContext *ctx, size_t *count)
+{
+    GScanMatch **result;                    /* Liste à retourner           */
+    size_t used;                            /* Indice pour le stockage     */
+    size_t i;                               /* Boucle de parcours #1       */
+    size_t partial;                         /* Décompte partiel            */
+    const GScanMatch **matches;             /* Correspondances en place    */
+    size_t k;                               /* Boucle de parcours #2       */
+
+    result = NULL;
+
+    if (!g_scan_pattern_handler_count_items(handler, ctx, count))
+    {
+        *count = 0;
+        goto exit;
+    }
+
+    if (*count == 0)
+        goto exit;
+
+    result = malloc(*count * sizeof(GScanMatch *));
+
+    used = 0;
+
+    for (i = 0; i < handler->count; i++)
+    {
+        matches = g_scan_context_get_full_matches(ctx, handler->patterns[i], &partial);
+
+        for (k = 0; k < partial; k++)
+        {
+            result[used++] = matches[k];
+            g_object_ref(G_OBJECT(matches[k]));
+        }
+
+    }
+
+ exit:
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
@@ -284,9 +355,7 @@ static bool g_scan_pattern_handler_reduce_to_boolean(const GScanPatternHandler *
     bool result;                            /* Bilan à retourner           */
     size_t count;                           /* Quantité de correspondances */
 
-    result = true;
-
-    g_scan_context_get_full_matches(ctx, expr->pattern, &count);
+    result = g_scan_pattern_handler_count_items(expr, ctx, &count);
 
     *out = g_scan_literal_expression_new(LVT_BOOLEAN, (bool []){ count > 0 });
 
@@ -312,12 +381,20 @@ static bool g_scan_pattern_handler_reduce_to_boolean(const GScanPatternHandler *
 static bool g_scan_pattern_handler_count_items(const GScanPatternHandler *expr, GScanContext *ctx, size_t *count)
 {
     bool result;                            /* Bilan à retourner           */
+    size_t partial;                         /* Décompte partiel            */
+    size_t i;                               /* Boucle de parcours          */
 
     result = true;
 
     assert(g_scan_context_is_scan_done(ctx));
 
-    g_scan_context_get_full_matches(ctx, expr->pattern, count);
+    *count = 0;
+
+    for (i = 0; i < expr->count; i++)
+    {
+        g_scan_context_get_full_matches(ctx, expr->patterns[i], &partial);
+        *count += partial;
+    }
 
     return result;
 
@@ -342,6 +419,7 @@ static bool g_scan_pattern_handler_count_items(const GScanPatternHandler *expr,
 static bool g_scan_pattern_handler_get_item(const GScanPatternHandler *expr, size_t index, GScanContext *ctx, GScanExpression **out)
 {
     bool result;                            /* Bilan à retourner           */
+    size_t i;                               /* Boucle de parcours          */
     size_t count;                           /* Quantité de correspondances */
     const GScanMatch **matches;             /* Correspondances en place    */
     const GScanBytesMatch *match;           /* Correspondance ciblée       */
@@ -353,18 +431,32 @@ static bool g_scan_pattern_handler_get_item(const GScanPatternHandler *expr, siz
     const bin_t *data;                      /* Accès aux données brutes    */
     sized_string_t binary;                  /* Conversion de formats       */
 
+    result = false;
+
     assert(g_scan_context_is_scan_done(ctx));
 
-    matches = g_scan_context_get_full_matches(ctx, expr->pattern, &count);
+    /* Identification du motif concerné */
 
-    result = (index < count);
-    if (!result) goto done;
+    for (i = 0; i < expr->count; i++)
+    {
+        matches = g_scan_context_get_full_matches(ctx, expr->patterns[i], &count);
+
+        if (index < count)
+            break;
+        else
+            index -= count;
+
+    }
+
+    if (i == expr->count) goto done;
 
     result = G_IS_SCAN_BYTES_MATCH(matches[index]);
     if (!result) goto done;
 
     match = G_SCAN_BYTES_MATCH(matches[index]);
 
+    /* Traitement adapté de la requête */
+
     len = g_scan_bytes_match_get_location(match, &start, &end);
 
     switch (expr->type)
@@ -376,7 +468,7 @@ static bool g_scan_pattern_handler_get_item(const GScanPatternHandler *expr, siz
 
             data = g_binary_content_get_raw_access(content, &pos, len);
 
-            binary.data = data;
+            binary.static_bin_data = data;
             binary.len = len;
 
             *out = g_scan_literal_expression_new(LVT_STRING, &binary);
diff --git a/src/analysis/scan/exprs/handler.h b/src/analysis/scan/exprs/handler.h
index 8ad700a..36985e7 100644
--- a/src/analysis/scan/exprs/handler.h
+++ b/src/analysis/scan/exprs/handler.h
@@ -59,7 +59,10 @@ typedef enum _ScanHandlerType
 GType g_scan_pattern_handler_get_type(void);
 
 /* Met en place une manipulation de correspondances établies. */
-GScanExpression *g_scan_pattern_handler_new(GSearchPattern *, ScanHandlerType);
+GScanExpression *g_scan_pattern_handler_new(GSearchPattern ** const, size_t, ScanHandlerType);
+
+/* Fournit la liste de toutes les correspondances représentées. */
+GScanMatch **g_scan_pattern_handler_get_all_matches(const GScanPatternHandler *, GScanContext *, size_t *);
 
 
 
diff --git a/src/analysis/scan/exprs/literal.c b/src/analysis/scan/exprs/literal.c
index de7e32a..b1a094c 100644
--- a/src/analysis/scan/exprs/literal.c
+++ b/src/analysis/scan/exprs/literal.c
@@ -62,6 +62,9 @@ static bool g_scan_literal_expression_reduce_to_boolean(const GScanLiteralExpres
 /* Dénombre les éléments portés par une expression. */
 static bool g_scan_literal_expression_count(const GScanLiteralExpression *, GScanContext *, size_t *);
 
+/* Fournit un élément donné issu d'un ensemble constitué. */
+static bool g_scan_literal_expression_get_item(const GScanLiteralExpression *, size_t, GScanContext *, GScanExpression **);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -100,6 +103,7 @@ static void g_scan_literal_expression_class_init(GScanLiteralExpressionClass *kl
     expr->cmp_rich = (compare_expr_rich_fc)g_scan_literal_expression_compare_rich;
     expr->reduce_to_bool = (reduce_expr_to_bool_fc)g_scan_literal_expression_reduce_to_boolean;
     expr->count = (count_scan_expr_fc)g_scan_literal_expression_count;
+    expr->get = (get_scan_expr_fc)g_scan_literal_expression_get_item;
 
 }
 
@@ -659,3 +663,51 @@ static bool g_scan_literal_expression_count(const GScanLiteralExpression *expr,
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : expr  = expression à consulter.                              *
+*                index = indice de l'élément à transférer.                    *
+*                ctx   = contexte de suivi de l'analyse courante.             *
+*                out   = zone d'enregistrement de la réduction opérée. [OUT]  *
+*                                                                             *
+*  Description : Fournit un élément donné issu d'un ensemble constitué.       *
+*                                                                             *
+*  Retour      : Bilan de l'opération : false en cas d'erreur irrécupérable.  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_scan_literal_expression_get_item(const GScanLiteralExpression *expr, size_t index, GScanContext *ctx, GScanExpression **out)
+{
+    bool result;                            /* Bilan à retourner           */
+    sized_string_t ch;                      /* Caractère extrait           */
+
+    switch (expr->value_type)
+    {
+        case LVT_STRING:
+
+            result = (index < expr->value.string.len);
+
+            if (result)
+            {
+                ch.data = expr->value.string.data + index;
+                ch.len = 1;
+
+                *out = g_scan_literal_expression_new(LVT_STRING, &ch);
+
+            }
+
+            break;
+
+        default:
+            result = false;
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/src/analysis/scan/grammar.y b/src/analysis/scan/grammar.y
index 2863e21..36f6df9 100644
--- a/src/analysis/scan/grammar.y
+++ b/src/analysis/scan/grammar.y
@@ -1376,7 +1376,7 @@ relational_expr : cexpression "<" cexpression  { $$ = g_scan_relational_operatio
                            $$ = NULL;
                        else
                        {
-                           $$ = g_scan_pattern_handler_new(__pat, SHT_RAW);
+                           $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_RAW);
                            g_object_unref(G_OBJECT(__pat));
                        }
                    }
@@ -1400,7 +1400,7 @@ relational_expr : cexpression "<" cexpression  { $$ = g_scan_relational_operatio
                            $$ = NULL;
                        else
                        {
-                           $$ = g_scan_pattern_handler_new(__pat, SHT_START);
+                           $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_START);
                            g_object_unref(G_OBJECT(__pat));
                        }
                    }
@@ -1412,7 +1412,7 @@ relational_expr : cexpression "<" cexpression  { $$ = g_scan_relational_operatio
                            $$ = NULL;
                        else
                        {
-                           $$ = g_scan_pattern_handler_new(__pat, SHT_LENGTH);
+                           $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_LENGTH);
                            g_object_unref(G_OBJECT(__pat));
                        }
                    }
@@ -1424,7 +1424,7 @@ relational_expr : cexpression "<" cexpression  { $$ = g_scan_relational_operatio
                            $$ = NULL;
                        else
                        {
-                           $$ = g_scan_pattern_handler_new(__pat, SHT_END);
+                           $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_END);
                            g_object_unref(G_OBJECT(__pat));
                        }
                    }
diff --git a/src/analysis/scan/items/Makefile.am b/src/analysis/scan/items/Makefile.am
index 89ae41e..9263c7b 100644
--- a/src/analysis/scan/items/Makefile.am
+++ b/src/analysis/scan/items/Makefile.am
@@ -14,6 +14,8 @@ endif
 libanalysisscanitems_la_SOURCES =			\
 	count.h count.c							\
 	datasize.h datasize.c					\
+	maxcommon.h maxcommon.c					\
+	modpath.h modpath.c						\
 	uint-int.h								\
 	uint.h uint.c
 
diff --git a/src/analysis/scan/items/maxcommon.c b/src/analysis/scan/items/maxcommon.c
new file mode 100644
index 0000000..a2e0f2a
--- /dev/null
+++ b/src/analysis/scan/items/maxcommon.c
@@ -0,0 +1,366 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * maxcommon.c - détermination de la plus grand occurrence au sein d'un ensemble d'éléments
+ *
+ * Copyright (C) 2023 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "maxcommon.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+
+
+#include "../item-int.h"
+#include "../exprs/literal.h"
+#include "../../../glibext/comparison-int.h"
+
+
+
+/* ---------------------- INTRODUCTION D'UNE NOUVELLE FONCTION ---------------------- */
+
+
+/* Initialise la classe des repérages de plus grande occurrence. */
+static void g_scan_maxcommon_function_class_init(GScanMaxcommonFunctionClass *);
+
+/* Initialise une instance de repérage d'occurrence maximake. */
+static void g_scan_maxcommon_function_init(GScanMaxcommonFunction *);
+
+/* Supprime toutes les références externes. */
+static void g_scan_maxcommon_function_dispose(GScanMaxcommonFunction *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_scan_maxcommon_function_finalize(GScanMaxcommonFunction *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Indique le nom associé à une expression d'évaluation. */
+static char *g_scan_maxcommon_function_get_name(const GScanMaxcommonFunction *);
+
+/* Réduit une expression à une forme plus simple. */
+static bool g_scan_maxcommon_function_run_call(GScanMaxcommonFunction *, GScanExpression **, size_t, GScanContext *, GScanScope *, GObject **);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                        INTRODUCTION D'UNE NOUVELLE FONCTION                        */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un décompte d'élément le plus représenté dans une série. */
+G_DEFINE_TYPE(GScanMaxcommonFunction, g_scan_maxcommon_function, G_TYPE_SCAN_REGISTERED_ITEM);
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des repérages de plus grande occurrence.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_maxcommon_function_class_init(GScanMaxcommonFunctionClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+    GScanRegisteredItemClass *registered;   /* Version de classe parente   */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_maxcommon_function_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_scan_maxcommon_function_finalize;
+
+    registered = G_SCAN_REGISTERED_ITEM_CLASS(klass);
+
+    registered->get_name = (get_registered_item_name_fc)g_scan_maxcommon_function_get_name;
+    registered->run_call = (run_registered_item_call_fc)g_scan_maxcommon_function_run_call;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : func = instance à initialiser.                               *
+*                                                                             *
+*  Description : Initialise une instance de repérage d'occurrence maximake.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_maxcommon_function_init(GScanMaxcommonFunction *func)
+{
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : func = instance d'objet GLib à traiter.                      *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_maxcommon_function_dispose(GScanMaxcommonFunction *func)
+{
+    G_OBJECT_CLASS(g_scan_maxcommon_function_parent_class)->dispose(G_OBJECT(func));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : func = instance d'objet GLib à traiter.                      *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_maxcommon_function_finalize(GScanMaxcommonFunction *func)
+{
+    G_OBJECT_CLASS(g_scan_maxcommon_function_parent_class)->finalize(G_OBJECT(func));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Constitue une fonction de calcul de plus grande occurrence.  *
+*                                                                             *
+*  Retour      : Fonction mise en place.                                      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GScanRegisteredItem *g_scan_maxcommon_function_new(void)
+{
+    GScanRegisteredItem *result;            /* Structure à retourner       */
+
+    result = g_object_new(G_TYPE_SCAN_MAXCOMMON_FUNCTION, NULL);
+
+    return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item = élément d'appel à consulter.                          *
+*                                                                             *
+*  Description : Indique le nom associé à une expression d'évaluation.        *
+*                                                                             *
+*  Retour      : Désignation humaine de l'expression d'évaluation.            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_maxcommon_function_get_name(const GScanMaxcommonFunction *item)
+{
+    char *result;                           /* Désignation à retourner     */
+
+    result = strdup("maxcommon");
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item  = élément d'appel à consulter.                         *
+*                args  = liste d'éventuels arguments fournis.                 *
+*                count = taille de cette liste.                               *
+*                ctx   = contexte de suivi de l'analyse courante.             *
+*                scope = portée courante des variables locales.               *
+*                out   = zone d'enregistrement de la résolution opérée. [OUT] *
+*                                                                             *
+*  Description : Réduit une expression à une forme plus simple.               *
+*                                                                             *
+*  Retour      : Réduction correspondante, expression déjà réduite, ou NULL.  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_scan_maxcommon_function_run_call(GScanMaxcommonFunction *item, GScanExpression **args, size_t count, GScanContext *ctx, GScanScope *scope, GObject **out)
+{
+    bool result;                            /* Bilan à retourner           */
+    size_t used;                            /* Prochain emplacement libre  */
+    GScanExpression **collected;            /* Représentants de groupes    */
+    size_t *scores;                         /* Taille de ces groupes       */
+    size_t i;                               /* Boucle de parcours #1       */
+    size_t k;                               /* Boucle de parcours #2       */
+    bool status;                            /* Bilan de la comparaison     */
+    bool equal;                             /* Egalité établie ?           */
+    size_t arg0_count;                      /* Taille de l'argument unique */
+    GScanExpression *arg0_item;             /*  Elément de cet argument    */
+    size_t best;                            /* Meilleur score identifié    */
+
+    result = (count > 0);
+    if (!result) goto exit;
+
+    used = 0;
+
+    /* Si la série à étudier est directement fournie */
+    if (count > 1)
+    {
+        collected = malloc(count * sizeof(GScanExpression *));
+        scores = malloc(count * sizeof(size_t));
+
+        for (i = 0; i < count; i++)
+        {
+            for (k = 0; k < used; k++)
+            {
+                status = g_comparable_item_compare_rich(G_COMPARABLE_ITEM(args[i]),
+                                                        G_COMPARABLE_ITEM(collected[k]),
+                                                        RCO_EQ, &equal);
+
+                if (status && equal)
+                    break;
+
+            }
+
+            if (k < used)
+                scores[k]++;
+
+            else
+            {
+                collected[used] = args[i];
+                g_object_ref(G_OBJECT(args[i]));
+                scores[used] = 1;
+
+                used++;
+
+            }
+
+        }
+
+    }
+
+    /* Sinon on considère que l'arguement unique porte la liste (idéalement) */
+    else
+    {
+        if (G_IS_SCAN_LITERAL_EXPRESSION(args[0]) || !g_scan_expression_handle_set_features(args[0]))
+        {
+            best = 1;
+            goto quick_unique;
+        }
+
+#ifndef NDEBUG
+        g_scan_expression_count_items(args[0], ctx, &arg0_count);
+#else
+        status = bool g_scan_expression_count_items(args[0], ctx, &arg0_count);
+        assert(status);
+#endif
+
+        collected = malloc(arg0_count * sizeof(GScanExpression *));
+        scores = malloc(arg0_count * sizeof(size_t));
+
+        for (i = 0; i < arg0_count; i++)
+        {
+#ifndef NDEBUG
+            g_scan_expression_get_item(args[0], i, ctx, &arg0_item);
+#else
+            status = g_scan_expression_get_item(args[0], i, ctx, &arg0_item);
+            assert(status);
+#endif
+
+            for (k = 0; k < used; k++)
+            {
+                status = g_comparable_item_compare_rich(G_COMPARABLE_ITEM(arg0_item),
+                                                        G_COMPARABLE_ITEM(collected[k]),
+                                                        RCO_EQ, &equal);
+
+                if (status && equal)
+                    break;
+
+            }
+
+            if (k < used)
+            {
+                g_object_unref(G_OBJECT(arg0_item));
+                scores[k]++;
+            }
+
+            else
+            {
+                collected[used] = arg0_item;
+                scores[used] = 1;
+
+                used++;
+
+            }
+
+        }
+
+    }
+
+    /* Analyse des résultats */
+
+    best = 0;
+
+    for (i = 0; i < used; i++)
+        if (scores[i] > best)
+            best = scores[i];
+
+    for (i = 0; i < used; i++)
+        g_object_unref(G_OBJECT(collected[i]));
+
+    free(collected);
+    free(scores);
+
+ quick_unique:
+
+    assert(best > 0);
+
+    *out = G_OBJECT(g_scan_literal_expression_new(LVT_UNSIGNED_INTEGER, (unsigned long long []){ best }));
+
+ exit:
+
+    return result;
+
+}
diff --git a/src/analysis/scan/items/maxcommon.h b/src/analysis/scan/items/maxcommon.h
new file mode 100644
index 0000000..a834c00
--- /dev/null
+++ b/src/analysis/scan/items/maxcommon.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * maxcommon.h - prototypes pour la détermination de la plus grand occurrence au sein d'un ensemble d'éléments
+ *
+ * Copyright (C) 2023 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _ANALYSIS_SCAN_ITEMS_MAXCOMMON_H
+#define _ANALYSIS_SCAN_ITEMS_MAXCOMMON_H
+
+
+#include <glib-object.h>
+
+
+#include "../item.h"
+
+
+
+#define G_TYPE_SCAN_MAXCOMMON_FUNCTION            g_scan_maxcommon_function_get_type()
+#define G_SCAN_MAXCOMMON_FUNCTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SCAN_MAXCOMMON_FUNCTION, GScanMaxcommonFunction))
+#define G_IS_SCAN_MAXCOMMON_FUNCTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SCAN_MAXCOMMON_FUNCTION))
+#define G_SCAN_MAXCOMMON_FUNCTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SCAN_MAXCOMMON_FUNCTION, GScanMaxcommonFunctionClass))
+#define G_IS_SCAN_MAXCOMMON_FUNCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SCAN_MAXCOMMON_FUNCTION))
+#define G_SCAN_MAXCOMMON_FUNCTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SCAN_MAXCOMMON_FUNCTION, GScanMaxcommonFunctionClass))
+
+
+/* Détermination de la plus grand occurrence au sein d'un ensemble (instance) */
+typedef GScanRegisteredItem GScanMaxcommonFunction;
+
+/* Détermination de la plus grand occurrence au sein d'un ensemble (classe) */
+typedef GScanRegisteredItemClass GScanMaxcommonFunctionClass;
+
+
+/* Indique le type défini pour un décompte d'élément le plus représenté dans une série. */
+GType g_scan_maxcommon_function_get_type(void);
+
+/* Constitue une fonction de calcul de plus grande occurrence. */
+GScanRegisteredItem *g_scan_maxcommon_function_new(void);
+
+
+
+#endif  /* _ANALYSIS_SCAN_ITEMS_MAXCOMMON_H */
diff --git a/src/analysis/scan/items/modpath.c b/src/analysis/scan/items/modpath.c
new file mode 100644
index 0000000..2171bda
--- /dev/null
+++ b/src/analysis/scan/items/modpath.c
@@ -0,0 +1,301 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * modpath.c - récupération des combinaisons de modification de motifs
+ *
+ * Copyright (C) 2023 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "modpath.h"
+
+
+#include "../item-int.h"
+
+
+#include "../exprs/handler.h"
+#include "../exprs/literal.h"
+#include "../exprs/set.h"
+#include "../matches/bytes.h"
+
+
+
+/* ---------------------- INTRODUCTION D'UNE NOUVELLE FONCTION ---------------------- */
+
+
+/* Initialise la classe des énumérations de formules de motifs. */
+static void g_scan_modpath_function_class_init(GScanModpathFunctionClass *);
+
+/* Initialise une instance d'énumération de formules de motifs. */
+static void g_scan_modpath_function_init(GScanModpathFunction *);
+
+/* Supprime toutes les références externes. */
+static void g_scan_modpath_function_dispose(GScanModpathFunction *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_scan_modpath_function_finalize(GScanModpathFunction *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Indique le nom associé à une expression d'évaluation. */
+static char *g_scan_modpath_function_get_name(const GScanModpathFunction *);
+
+/* Réduit une expression à une forme plus simple. */
+static bool g_scan_modpath_function_run_call(GScanModpathFunction *, GScanExpression **, size_t, GScanContext *, GScanScope *, GObject **);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                        INTRODUCTION D'UNE NOUVELLE FONCTION                        */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une énumération de formules créant des motifs. */
+G_DEFINE_TYPE(GScanModpathFunction, g_scan_modpath_function, G_TYPE_SCAN_REGISTERED_ITEM);
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des énumérations de formules de motifs. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_modpath_function_class_init(GScanModpathFunctionClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+    GScanRegisteredItemClass *registered;   /* Version de classe parente   */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_modpath_function_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_scan_modpath_function_finalize;
+
+    registered = G_SCAN_REGISTERED_ITEM_CLASS(klass);
+
+    registered->get_name = (get_registered_item_name_fc)g_scan_modpath_function_get_name;
+    registered->run_call = (run_registered_item_call_fc)g_scan_modpath_function_run_call;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : func = instance à initialiser.                               *
+*                                                                             *
+*  Description : Initialise une instance d'énumération de formules de motifs. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_modpath_function_init(GScanModpathFunction *func)
+{
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : func = instance d'objet GLib à traiter.                      *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_modpath_function_dispose(GScanModpathFunction *func)
+{
+    G_OBJECT_CLASS(g_scan_modpath_function_parent_class)->dispose(G_OBJECT(func));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : func = instance d'objet GLib à traiter.                      *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_scan_modpath_function_finalize(GScanModpathFunction *func)
+{
+    G_OBJECT_CLASS(g_scan_modpath_function_parent_class)->finalize(G_OBJECT(func));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Constitue une fonction d'énumération des formules de motifs. *
+*                                                                             *
+*  Retour      : Fonction mise en place.                                      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GScanRegisteredItem *g_scan_modpath_function_new(void)
+{
+    GScanRegisteredItem *result;            /* Structure à retourner       */
+
+    result = g_object_new(G_TYPE_SCAN_MODPATH_FUNCTION, NULL);
+
+    return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item = élément d'appel à consulter.                          *
+*                                                                             *
+*  Description : Indique le nom associé à une expression d'évaluation.        *
+*                                                                             *
+*  Retour      : Désignation humaine de l'expression d'évaluation.            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_modpath_function_get_name(const GScanModpathFunction *item)
+{
+    char *result;                           /* Désignation à retourner     */
+
+    result = strdup("modpath");
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item  = élément d'appel à consulter.                         *
+*                args  = liste d'éventuels arguments fournis.                 *
+*                count = taille de cette liste.                               *
+*                ctx   = contexte de suivi de l'analyse courante.             *
+*                scope = portée courante des variables locales.               *
+*                out   = zone d'enregistrement de la résolution opérée. [OUT] *
+*                                                                             *
+*  Description : Réduit une expression à une forme plus simple.               *
+*                                                                             *
+*  Retour      : Réduction correspondante, expression déjà réduite, ou NULL.  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_scan_modpath_function_run_call(GScanModpathFunction *item, GScanExpression **args, size_t count, GScanContext *ctx, GScanScope *scope, GObject **out)
+{
+    bool result;                            /* Bilan à retourner           */
+    size_t i;                               /* Boucle de parcours #1       */
+    size_t mcount;                          /* Quantité de correspondances */
+    GScanMatch **matches;                   /* Correspondances établies    */
+    size_t k;                               /* Boucle de parcours #2       */
+    sized_string_t path;                    /* Combinaison à conserver     */
+    GScanExpression *subitem;               /* Nouvel élément à transmettre*/
+
+    /* Validation préalable du type des arguments */
+
+    result = true;
+
+    for (i = 0; i < count && result; i++)
+        result = G_IS_SCAN_PATTERN_HANDLER(args[i]);
+
+    if (!result)
+        goto exit;
+
+    /* Construction des chemins attendus */
+
+    *out = G_OBJECT(g_scan_generic_set_new());
+
+    for (i = 0; i < count; i++)
+    {
+        matches = g_scan_pattern_handler_get_all_matches(G_SCAN_PATTERN_HANDLER(args[i]), ctx, &mcount);
+        if (mcount == 0) continue;
+
+        /**
+         * La série est à priori constituée d'éléments de même type, donc
+         * un test unique suffit.
+         */
+        if (!G_IS_SCAN_BYTES_MATCH(matches[0]))
+        {
+            for (k = 0; k < mcount; k++)
+                g_object_unref(G_OBJECT(matches[k]));
+        }
+
+        else
+        {
+            for (k = 0; k < mcount; k++)
+            {
+                path.data = g_scan_bytes_match_get_modifier_path(G_SCAN_BYTES_MATCH(matches[k]));
+                if (path.data == NULL) continue;
+
+                path.len = strlen(path.data);
+
+                printf(" >> add '%s'\n", path.data);
+
+                subitem = g_scan_literal_expression_new(LVT_STRING, &path);
+
+                g_scan_generic_set_add_item(G_SCAN_GENERIC_SET(*out), subitem);
+
+                g_object_unref(G_OBJECT(subitem));
+
+                exit_szstr(&path);
+
+                g_object_unref(G_OBJECT(matches[k]));
+
+            }
+
+        }
+
+        free(matches);
+
+    }
+
+ exit:
+
+    return result;
+
+}
diff --git a/src/analysis/scan/items/modpath.h b/src/analysis/scan/items/modpath.h
new file mode 100644
index 0000000..3a78ef7
--- /dev/null
+++ b/src/analysis/scan/items/modpath.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * modpath.h - prototypes pour la récupération des combinaisons de modification de motifs
+ *
+ * Copyright (C) 2023 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _ANALYSIS_SCAN_ITEMS_MODPATH_H
+#define _ANALYSIS_SCAN_ITEMS_MODPATH_H
+
+
+#include <glib-object.h>
+
+
+#include "../item.h"
+
+
+
+#define G_TYPE_SCAN_MODPATH_FUNCTION            g_scan_modpath_function_get_type()
+#define G_SCAN_MODPATH_FUNCTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SCAN_MODPATH_FUNCTION, GScanModpathFunction))
+#define G_IS_SCAN_MODPATH_FUNCTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SCAN_MODPATH_FUNCTION))
+#define G_SCAN_MODPATH_FUNCTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SCAN_MODPATH_FUNCTION, GScanModpathFunctionClass))
+#define G_IS_SCAN_MODPATH_FUNCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SCAN_MODPATH_FUNCTION))
+#define G_SCAN_MODPATH_FUNCTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SCAN_MODPATH_FUNCTION, GScanModpathFunctionClass))
+
+
+/* Récupération de formules à l'origine de la construction de motifs (instance) */
+typedef GScanRegisteredItem GScanModpathFunction;
+
+/* Récupération de formules à l'origine de la construction de motifs (classe) */
+typedef GScanRegisteredItemClass GScanModpathFunctionClass;
+
+
+/* Indique le type défini pour une énumération de formules créant des motifs. */
+GType g_scan_modpath_function_get_type(void);
+
+/* Constitue une fonction d'énumération des formules de motifs. */
+GScanRegisteredItem *g_scan_modpath_function_new(void);
+
+
+
+#endif  /* _ANALYSIS_SCAN_ITEMS_MODPATH_H */
diff --git a/src/analysis/scan/matches/bytes-int.h b/src/analysis/scan/matches/bytes-int.h
index 6f7e60b..f57cb9f 100644
--- a/src/analysis/scan/matches/bytes-int.h
+++ b/src/analysis/scan/matches/bytes-int.h
@@ -42,6 +42,9 @@ struct _GScanBytesMatch
     phys_t start;                           /* Début du motif représenté   */
     phys_t len;                             /* Taille du motif représenté  */
 
+    size_t mod_path_index;                  /* Indice de construction      */
+    bool has_mod_path;                      /* Validité du champ précédent */
+
 };
 
 /* Correspondance trouvée avec une chaîne (classe) */
diff --git a/src/analysis/scan/matches/bytes.c b/src/analysis/scan/matches/bytes.c
index de101c4..f0b97fe 100644
--- a/src/analysis/scan/matches/bytes.c
+++ b/src/analysis/scan/matches/bytes.c
@@ -30,6 +30,7 @@
 
 
 #include "bytes-int.h"
+#include "../patterns/token.h"
 #include "../../../common/cpp.h"
 #include "../../../core/logs.h"
 
@@ -121,6 +122,8 @@ static void g_scan_bytes_match_init(GScanBytesMatch *match)
     match->start = VMPA_NO_PHYSICAL;
     match->len = VMPA_NO_PHYSICAL;
 
+    match->has_mod_path = false;
+
 }
 
 
@@ -285,6 +288,59 @@ phys_t g_scan_bytes_match_get_location(const GScanBytesMatch *match, phys_t *sta
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : match = informations de correspondance à compléter.          *
+*                index = indice de la combinaison de modificateurs ciblée.    *
+*                                                                             *
+*  Description : Mémorise l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_scan_bytes_match_remember_modifier_path(GScanBytesMatch *match, size_t index)
+{
+    match->mod_path_index = index;
+    match->has_mod_path = true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : match = informations de correspondance à consulter.          *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison gagnante.      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *g_scan_bytes_match_get_modifier_path(const GScanBytesMatch *match)
+{
+    char *result;                           /* Combinaison à retourner     */
+    GBytesToken *pattern;                   /* Autre version du motif      */
+
+    if (match->has_mod_path)
+    {
+        pattern = G_BYTES_TOKEN(G_SCAN_MATCH(match)->source);
+        result = g_bytes_token_get_modifier_path(pattern, match->mod_path_index);
+    }
+
+    else
+        result = NULL;
+
+    return result;
+
+}
+
+
+
 /* ---------------------------------------------------------------------------------- */
 /*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
 /* ---------------------------------------------------------------------------------- */
diff --git a/src/analysis/scan/matches/bytes.h b/src/analysis/scan/matches/bytes.h
index e599ee4..bd7425d 100644
--- a/src/analysis/scan/matches/bytes.h
+++ b/src/analysis/scan/matches/bytes.h
@@ -60,6 +60,12 @@ GBinContent *g_scan_bytes_match_get_content(const GScanBytesMatch *);
 /* Indique la localisation d'une correspondance établie. */
 phys_t g_scan_bytes_match_get_location(const GScanBytesMatch *, phys_t *, phys_t *);
 
+/* Mémorise l'origine d'une correspondance à partir d'un indice. */
+void g_scan_bytes_match_remember_modifier_path(GScanBytesMatch *, size_t);
+
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+char *g_scan_bytes_match_get_modifier_path(const GScanBytesMatch *);
+
 
 
 #endif  /* _ANALYSIS_SCAN_MATCHES_BYTES_H */
diff --git a/src/analysis/scan/matches/pending.c b/src/analysis/scan/matches/pending.c
index 9ed4de3..57c63d7 100644
--- a/src/analysis/scan/matches/pending.c
+++ b/src/analysis/scan/matches/pending.c
@@ -48,10 +48,6 @@ static int compare_match_area(const match_area_t *, const match_area_t *);
 
 
 
-
-
-
-
 /* ---------------------------------------------------------------------------------- */
 /*                           MEMORISATION D'UNE ZONE BORNEE                           */
 /* ---------------------------------------------------------------------------------- */
@@ -82,6 +78,12 @@ static int compare_match_area(const match_area_t *a, const match_area_t *b)
     if (result == 0)
         result = sort_unsigned_long_long(a->ttl, b->ttl);
 
+    if (result == 0)
+        result = sort_unsigned_long_long(a->has_mod_path, b->has_mod_path);
+
+    if (result == 0)
+        result = sort_unsigned_long_long(a->mod_path_index, b->mod_path_index);
+
     return result;
 
 }
@@ -293,6 +295,51 @@ void add_pending_match(pending_matches_t *matches, phys_t start, phys_t length)
 
     area->ttl = 1;
 
+    area->has_mod_path = false;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : matches = suivi de correspondances à compléter.              *
+*                start   = point de départ d'une nouvelle correspondance.     *
+*                length  = taille de la zone couverte.                        *
+*                index   = indice de construction pour le motif concerné.     *
+*                                                                             *
+*  Description : Ajoute au suivi la définition d'une nouvelle correspondance. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void add_pending_match_with_path(pending_matches_t *matches, phys_t start, phys_t length, size_t index)
+{
+    match_area_t *area;                     /* Zone à initialiser          */
+
+    if (matches->used == matches->allocated)
+    {
+        matches->allocated += PENDING_ALLOC_SIZE;
+
+        matches->areas = realloc(matches->areas, matches->allocated * sizeof(match_area_t));
+
+    }
+
+    area = &matches->areas[matches->used++];
+
+    area->start = start;
+    area->end = start + length;
+
+    assert(matches->content_start <= area->start);
+    assert(area->end <= matches->content_end);
+
+    area->ttl = 1;
+
+    area->mod_path_index = index;
+    area->has_mod_path = true;
+
 }
 
 
diff --git a/src/analysis/scan/matches/pending.h b/src/analysis/scan/matches/pending.h
index 6df01c9..f4ac7a2 100644
--- a/src/analysis/scan/matches/pending.h
+++ b/src/analysis/scan/matches/pending.h
@@ -41,6 +41,9 @@ typedef struct _match_area_t
 
     unsigned long ttl;                      /* Durée de vie pour analyse   */
 
+    size_t mod_path_index;                  /* Indice de construction      */
+    bool has_mod_path;                      /* Validité du champ précédent */
+
 } match_area_t;
 
 /* Suivi de correspondances */
@@ -86,6 +89,9 @@ match_area_t * const  *get_all_pending_matches(const pending_matches_t *, size_t
 /*  Ajoute au suivi la définition d'une nouvelle correspondance. */
 void add_pending_match(pending_matches_t *, phys_t, phys_t);
 
+/* Ajoute au suivi la définition d'une nouvelle correspondance. */
+void add_pending_match_with_path(pending_matches_t *, phys_t, phys_t, size_t);
+
 /* Etend une zone couverte dans le suivi des correspondances. */
 void extend_pending_match_beginning(pending_matches_t *, size_t, phys_t);
 
diff --git a/src/analysis/scan/patterns/modifier-int.h b/src/analysis/scan/patterns/modifier-int.h
index e1de2d8..c9b3a36 100644
--- a/src/analysis/scan/patterns/modifier-int.h
+++ b/src/analysis/scan/patterns/modifier-int.h
@@ -41,6 +41,9 @@ typedef bool (* can_token_modifier_handle_arg) (const GScanTokenModifier *, cons
 /* Transforme une séquence d'octets pour motif de recherche. */
 typedef bool (* transform_scan_token_with_fc) (const GScanTokenModifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+typedef char * (* get_modifier_path) (const GScanTokenModifier *, size_t *);
+
 
 /* Transformation d'une séquence d'octets en une ou plusieurs autres formes (instance) */
 struct _GScanTokenModifier
@@ -59,6 +62,7 @@ struct _GScanTokenModifierClass
     transform_scan_token_fc transform;      /* Opération de transformation */
     can_token_modifier_handle_arg can_handle; /* Support d'argument donné  */
     transform_scan_token_with_fc transform_with; /* Opération encadrée     */
+    get_modifier_path get_path;             /* Rappel d'une combinaison    */
 
 };
 
diff --git a/src/analysis/scan/patterns/modifier.c b/src/analysis/scan/patterns/modifier.c
index 65f671f..9efd404 100644
--- a/src/analysis/scan/patterns/modifier.c
+++ b/src/analysis/scan/patterns/modifier.c
@@ -249,3 +249,30 @@ bool g_scan_token_modifier_transform_with_arg(const GScanTokenModifier *modifier
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *g_scan_token_modifier_get_path(const GScanTokenModifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+    GScanTokenModifierClass *class;         /* Classe à activer            */
+
+    class = G_SCAN_TOKEN_MODIFIER_GET_CLASS(modifier);
+
+    result = class->get_path(modifier, index);
+
+    return result;
+
+}
diff --git a/src/analysis/scan/patterns/modifier.h b/src/analysis/scan/patterns/modifier.h
index ce2589a..9030a72 100644
--- a/src/analysis/scan/patterns/modifier.h
+++ b/src/analysis/scan/patterns/modifier.h
@@ -65,6 +65,9 @@ bool g_scan_token_modifier_can_handle_arg(const GScanTokenModifier *, const modi
 /* Transforme une séquence d'octets pour motif de recherche. */
 bool g_scan_token_modifier_transform_with_arg(const GScanTokenModifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+char *g_scan_token_modifier_get_path(const GScanTokenModifier *, size_t *);
+
 
 
 #endif  /* _ANALYSIS_SCAN_MODIFIER_H */
diff --git a/src/analysis/scan/patterns/modifiers/hex.c b/src/analysis/scan/patterns/modifiers/hex.c
index 849d9ff..4a41c7d 100644
--- a/src/analysis/scan/patterns/modifiers/hex.c
+++ b/src/analysis/scan/patterns/modifiers/hex.c
@@ -58,6 +58,9 @@ static char *g_scan_hex_modifier_get_name(const GScanHexModifier *);
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_hex_modifier_transform(const GScanHexModifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_hex_modifier_get_path(const GScanHexModifier *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -96,6 +99,7 @@ static void g_scan_hex_modifier_class_init(GScanHexModifierClass *klass)
     modifier->get_name = (get_scan_modifier_name_fc)g_scan_hex_modifier_get_name;
 
     modifier->transform = (transform_scan_token_fc)g_scan_hex_modifier_transform;
+    modifier->get_path = (get_modifier_path)g_scan_hex_modifier_get_path;
 
 }
 
@@ -259,3 +263,34 @@ static bool g_scan_hex_modifier_transform(const GScanHexModifier *modifier, cons
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_hex_modifier_get_path(const GScanHexModifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (*index > 0)
+    {
+        result = NULL;
+        (*index)--;
+    }
+
+    else
+        result = strdup("hex");
+
+    return result;
+
+}
diff --git a/src/analysis/scan/patterns/modifiers/list.c b/src/analysis/scan/patterns/modifiers/list.c
index 0390f47..040d2f8 100644
--- a/src/analysis/scan/patterns/modifiers/list.c
+++ b/src/analysis/scan/patterns/modifiers/list.c
@@ -58,6 +58,9 @@ static char *g_scan_modifier_list_get_name(const GScanModifierList *);
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_modifier_list_transform(const GScanModifierList *, const sized_binary_t *, size_t, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_modifier_list_get_path(const GScanModifierList *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -96,6 +99,7 @@ static void g_scan_modifier_list_class_init(GScanModifierListClass *klass)
     modifier->get_name = (get_scan_modifier_name_fc)g_scan_modifier_list_get_name;
 
     modifier->transform = (transform_scan_token_fc)g_scan_modifier_list_transform;
+    modifier->get_path = (get_modifier_path)g_scan_modifier_list_get_path;
 
 }
 
@@ -404,3 +408,31 @@ static bool g_scan_modifier_list_transform(const GScanModifierList *modifier, co
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_modifier_list_get_path(const GScanModifierList *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+    size_t i;                               /* Boucle de parcours #1       */
+
+    result = NULL;
+
+    for (i = 0; i < modifier->count && result == NULL; i++)
+        result = g_scan_token_modifier_get_path(modifier->modifiers[i], index);
+
+    return result;
+
+}
diff --git a/src/analysis/scan/patterns/modifiers/plain.c b/src/analysis/scan/patterns/modifiers/plain.c
index 9ed0d7b..ad09129 100644
--- a/src/analysis/scan/patterns/modifiers/plain.c
+++ b/src/analysis/scan/patterns/modifiers/plain.c
@@ -58,6 +58,9 @@ static char *g_scan_plain_modifier_get_name(const GScanPlainModifier *);
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_plain_modifier_transform(const GScanPlainModifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_plain_modifier_get_path(const GScanPlainModifier *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -96,6 +99,7 @@ static void g_scan_plain_modifier_class_init(GScanPlainModifierClass *klass)
     modifier->get_name = (get_scan_modifier_name_fc)g_scan_plain_modifier_get_name;
 
     modifier->transform = (transform_scan_token_fc)g_scan_plain_modifier_transform;
+    modifier->get_path = (get_modifier_path)g_scan_plain_modifier_get_path;
 
 }
 
@@ -252,3 +256,34 @@ static bool g_scan_plain_modifier_transform(const GScanPlainModifier *modifier,
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_plain_modifier_get_path(const GScanPlainModifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (*index > 0)
+    {
+        result = NULL;
+        (*index)--;
+    }
+
+    else
+        result = strdup("plain");
+
+    return result;
+
+}
diff --git a/src/analysis/scan/patterns/modifiers/rev.c b/src/analysis/scan/patterns/modifiers/rev.c
index 9d74afe..ef4d5fa 100644
--- a/src/analysis/scan/patterns/modifiers/rev.c
+++ b/src/analysis/scan/patterns/modifiers/rev.c
@@ -58,6 +58,9 @@ static char *g_scan_reverse_modifier_get_name(const GScanReverseModifier *);
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_reverse_modifier_transform(const GScanReverseModifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_reverse_modifier_get_path(const GScanReverseModifier *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -96,6 +99,7 @@ static void g_scan_reverse_modifier_class_init(GScanReverseModifierClass *klass)
     modifier->get_name = (get_scan_modifier_name_fc)g_scan_reverse_modifier_get_name;
 
     modifier->transform = (transform_scan_token_fc)g_scan_reverse_modifier_transform;
+    modifier->get_path = (get_modifier_path)g_scan_reverse_modifier_get_path;
 
 }
 
@@ -254,3 +258,34 @@ static bool g_scan_reverse_modifier_transform(const GScanReverseModifier *modifi
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_reverse_modifier_get_path(const GScanReverseModifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (*index > 0)
+    {
+        result = NULL;
+        (*index)--;
+    }
+
+    else
+        result = strdup("rev");
+
+    return result;
+
+}
diff --git a/src/analysis/scan/patterns/modifiers/xor.c b/src/analysis/scan/patterns/modifiers/xor.c
index 53d75a5..d932c82 100644
--- a/src/analysis/scan/patterns/modifiers/xor.c
+++ b/src/analysis/scan/patterns/modifiers/xor.c
@@ -27,6 +27,7 @@
 #include <assert.h>
 #include <malloc.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <string.h>
 
 
@@ -66,6 +67,9 @@ static bool g_scan_xor_modifier_can_handle_arg(const GScanXorModifier *, const m
 /* Transforme une séquence d'octets pour motif de recherche. */
 static bool g_scan_xor_modifier_transform_with_arg(const GScanXorModifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+static char *g_scan_xor_modifier_get_path(const GScanXorModifier *, size_t *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -106,6 +110,7 @@ static void g_scan_xor_modifier_class_init(GScanXorModifierClass *klass)
     modifier->transform = (transform_scan_token_fc)g_scan_xor_modifier_transform;
     modifier->can_handle = (can_token_modifier_handle_arg)g_scan_xor_modifier_can_handle_arg;
     modifier->transform_with = (transform_scan_token_with_fc)g_scan_xor_modifier_transform_with_arg;
+    modifier->get_path = (get_modifier_path)g_scan_xor_modifier_get_path;
 
 }
 
@@ -393,3 +398,41 @@ static bool g_scan_xor_modifier_transform_with_arg(const GScanXorModifier *modif
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : modifier = modificateur à consulter.                         *
+*                index    = indice de la combinaison ciblée. [OUT]            *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_scan_xor_modifier_get_path(const GScanXorModifier *modifier, size_t *index)
+{
+    char *result;                           /* Combinaison à retourner     */
+    int ret;                                /* Bilan intermédiaire         */
+
+    if (*index > 255)
+    {
+        result = NULL;
+        (*index) -= 256;
+    }
+
+    else
+    {
+        ret = asprintf(&result, "xor(0x%02hhx)", (unsigned char)*index);
+
+        if (ret == -1)
+            result = strdup("xor(0x?)");
+
+    }
+
+    return result;
+
+}
diff --git a/src/analysis/scan/patterns/token.c b/src/analysis/scan/patterns/token.c
index 8b3a843..030e4a1 100644
--- a/src/analysis/scan/patterns/token.c
+++ b/src/analysis/scan/patterns/token.c
@@ -30,6 +30,7 @@
 
 
 #include "token-int.h"
+#include "tokens/nodes/plain.h"
 #include "../../../common/cpp.h"
 #include "../../../core/logs.h"
 
@@ -342,6 +343,33 @@ void g_bytes_token_check(const GBytesToken *token, GScanContext *context, GBinCo
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : token   = définition de la bribe à consulter.                *
+*                index = indice de la combinaison de modificateurs ciblée.    *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison gagnante.      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *g_bytes_token_get_modifier_path(const GBytesToken *token, size_t index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (G_IS_SCAN_TOKEN_NODE_PLAIN(token->root))
+        result = g_scan_token_node_plain_get_modifier_path(G_SCAN_TOKEN_NODE_PLAIN(token->root), index);
+    else
+        result = NULL;
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
diff --git a/src/analysis/scan/patterns/token.h b/src/analysis/scan/patterns/token.h
index a71e367..2816bf8 100644
--- a/src/analysis/scan/patterns/token.h
+++ b/src/analysis/scan/patterns/token.h
@@ -64,6 +64,9 @@ bool g_bytes_token_enroll(GBytesToken *, GScanContext *, GEngineBackend *, size_
 /* Transforme les correspondances locales en trouvailles. */
 void g_bytes_token_check(const GBytesToken *, GScanContext *, GBinContent *, pending_matches_t *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+char *g_bytes_token_get_modifier_path(const GBytesToken *, size_t);
+
 
 
 #endif  /* _ANALYSIS_SCAN_PATTERNS_TOKEN_H */
diff --git a/src/analysis/scan/patterns/tokens/nodes/plain.c b/src/analysis/scan/patterns/tokens/nodes/plain.c
index 5d89cf8..7309574 100644
--- a/src/analysis/scan/patterns/tokens/nodes/plain.c
+++ b/src/analysis/scan/patterns/tokens/nodes/plain.c
@@ -277,6 +277,35 @@ ScanPlainNodeFlags g_scan_token_node_plain_get_flags(const GScanTokenNodePlain *
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : node  = définition de la bribe à consulter.                  *
+*                index = indice de la combinaison de modificateurs ciblée.    *
+*                                                                             *
+*  Description : Retrouve l'origine d'une correspondance à partir d'un indice.*
+*                                                                             *
+*  Retour      : Version humainement lisible de la combinaison.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *g_scan_token_node_plain_get_modifier_path(const GScanTokenNodePlain *node, size_t index)
+{
+    char *result;                           /* Combinaison à retourner     */
+
+    if (node->modifier == NULL)
+        result = strdup("plain");
+
+    else
+        result = g_scan_token_modifier_get_path(node->modifier, (size_t []){ index });
+
+    return result;
+
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
@@ -511,6 +540,7 @@ static bool check_scan_token_node_plain_content(const sized_binary_t *raw, const
 static void g_scan_token_node_plain_check_forward(const GScanTokenNodePlain *node, GScanContext *context, GBinContent *content, pending_matches_t *matches, node_search_offset_t *offset, bool not, bool *skip)
 {
     bool initialized;                       /* Initialisation du suivi ?   */
+    bool track_path;                        /* Conservation du chemin      */
     bool nocase;                            /* Pas d'intérêt pour la casse */
     size_t ocount;                          /* Quantité de bornes présentes*/
     size_t i;                               /* Boucle de parcours #1       */
@@ -531,6 +561,10 @@ static void g_scan_token_node_plain_check_forward(const GScanTokenNodePlain *nod
 
     initialized = are_pending_matches_initialized(matches);
 
+    track_path = (G_SCAN_TOKEN_NODE(node)->flags & STNF_MAIN);
+
+    assert((track_path && !initialized) || !track_path);
+
     nocase = (node->flags & SPNF_CASE_INSENSITIVE);
 
     get_node_search_offset_ranges(offset, &ocount);
@@ -568,12 +602,18 @@ static void g_scan_token_node_plain_check_forward(const GScanTokenNodePlain *nod
                 status = check_scan_token_node_plain_content(raw, atom, nocase, new_begin, content);
 
                 if ((status && !not) || (!status && not))
+                {
                     /**
                      * Il ne peut y avoir qu'une seule séquence d'octets à un même
                      * emplacement, donc le couple (new_begin, len) enregistré est
                      * unique.
                      */
-                    add_pending_match(matches, new_begin, raw->len);
+                    if (track_path)
+                        add_pending_match_with_path(matches, new_begin, raw->len, i);
+                    else
+                        add_pending_match(matches, new_begin, raw->len);
+
+                }
 
             }
 
diff --git a/src/analysis/scan/patterns/tokens/nodes/plain.h b/src/analysis/scan/patterns/tokens/nodes/plain.h
index c8f3920..abf71de 100644
--- a/src/analysis/scan/patterns/tokens/nodes/plain.h
+++ b/src/analysis/scan/patterns/tokens/nodes/plain.h
@@ -75,6 +75,9 @@ GScanTokenNode *g_scan_token_node_plain_new(const sized_binary_t *, GScanTokenMo
 /* Indique les propriétés particulières d'un noeud de texte. */
 ScanPlainNodeFlags g_scan_token_node_plain_get_flags(const GScanTokenNodePlain *);
 
+/* Retrouve l'origine d'une correspondance à partir d'un indice. */
+char *g_scan_token_node_plain_get_modifier_path(const GScanTokenNodePlain *, size_t);
+
 
 
 #endif  /* _ANALYSIS_SCAN_PATTERNS_TOKENS_NODES_PLAIN_H */
diff --git a/src/analysis/scan/rule.c b/src/analysis/scan/rule.c
index 77a05b4..5826db9 100644
--- a/src/analysis/scan/rule.c
+++ b/src/analysis/scan/rule.c
@@ -623,6 +623,9 @@ void g_scan_rule_check(GScanRule *rule, GEngineBackend *backend, GScanContext *c
             match = g_scan_bytes_match_new(G_SEARCH_PATTERN(pattern), content,
                                            area->start, area->end - area->start);
 
+            if (area->has_mod_path)
+                g_scan_bytes_match_remember_modifier_path(G_SCAN_BYTES_MATCH(match), area->mod_path_index);
+
             g_scan_context_register_full_match(context, match);
             g_object_unref(G_OBJECT(match));
 
diff --git a/tests/analysis/scan/functions.py b/tests/analysis/scan/functions.py
index e936263..983b8da 100644
--- a/tests/analysis/scan/functions.py
+++ b/tests/analysis/scan/functions.py
@@ -33,6 +33,32 @@ rule test {
             self.check_rule_success(rule, cnt)
 
 
+    def testMaxCommon(self):
+        """Count the largest quantity of same items in a set."""
+
+        cnt = MemoryContent(b'')
+
+        cases = [
+            [ '1', 1 ],
+            [ '1, 2, 3', 1 ],
+            [ '1, 2, 1, 3, 1', 3 ],
+            [ '1, "a", 2, 3, "a"', 2 ],
+        ]
+
+        for c, q in cases:
+
+            rule = '''
+rule test {
+
+   condition:
+      maxcommon(%s) == %u
+
+}
+''' % (c, q)
+
+            self.check_rule_success(rule, cnt)
+
+
     # Modules
     # =======
 
-- 
cgit v0.11.2-87-g4458