From 72b6d6efcea936ac806528f3453a3107c7807131 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 8 Nov 2023 08:10:27 +0100
Subject: Make the hexadecimal range processing stronger.

---
 src/analysis/scan/grammar.y                        | 35 ++++++++-
 src/analysis/scan/patterns/tokens/nodes/any-int.h  |  2 +-
 src/analysis/scan/patterns/tokens/nodes/any.c      | 28 ++++++-
 src/analysis/scan/patterns/tokens/nodes/any.h      |  3 +
 src/analysis/scan/patterns/tokens/nodes/sequence.c | 86 +++++++++++++++++++++-
 src/analysis/scan/patterns/tokens/nodes/sequence.h |  6 ++
 tests/analysis/scan/fuzzing.py                     | 39 +++++++++-
 7 files changed, 190 insertions(+), 9 deletions(-)

diff --git a/src/analysis/scan/grammar.y b/src/analysis/scan/grammar.y
index 84eda7a..801898f 100644
--- a/src/analysis/scan/grammar.y
+++ b/src/analysis/scan/grammar.y
@@ -274,6 +274,7 @@ YY_DECL;
 
 %type <pattern> hex_pattern
 %type <node> hex_tokens
+%type <node> _hex_tokens
 %type <node> hex_token
 %type <node> hex_range
 %type <node> hex_choices
@@ -726,14 +727,37 @@ YY_DECL;
                    }
                    ;
 
-        hex_tokens : hex_token
+        hex_tokens : _hex_tokens
+                   {
+                       $$ = $1;
+
+                       if (G_IS_SCAN_TOKEN_NODE_SEQUENCE($$))
+                       {
+                           if (g_scan_token_node_sequence_count(G_SCAN_TOKEN_NODE_SEQUENCE($$)) == 1)
+                           {
+                               GScanTokenNode *node;
+
+                               node = g_scan_token_node_sequence_get(G_SCAN_TOKEN_NODE_SEQUENCE($$), 0);
+
+                               g_object_unref(G_OBJECT($$));
+
+                               $$ = node;
+
+                           }
+
+                       }
+
+                   }
+                   ;
+
+       _hex_tokens : hex_token
                    {
                        if ($1 == NULL) YYERROR;
 
                        $$ = $1;
 
                    }
-                   | hex_tokens hex_token
+                   | _hex_tokens hex_token
                    {
                        if ($2 == NULL) YYERROR;
 
@@ -793,7 +817,14 @@ YY_DECL;
                    }
                    | hex_range
                    {
+                       if ($1 == NULL)
+                       {
+                           raise_error(_("Unable to build hexadecimal range"));
+                           YYERROR;
+                       }
+
                        $$ = $1;
+
                    }
                    | "~" hex_token
                    {
diff --git a/src/analysis/scan/patterns/tokens/nodes/any-int.h b/src/analysis/scan/patterns/tokens/nodes/any-int.h
index 705aab3..dd2e2e7 100644
--- a/src/analysis/scan/patterns/tokens/nodes/any-int.h
+++ b/src/analysis/scan/patterns/tokens/nodes/any-int.h
@@ -52,7 +52,7 @@ struct _GScanTokenNodeAnyClass
 };
 
 
-/* Met en place un un noeud pointant une série d'octets. */
+/* Met en place un noeud pointant une série d'octets. */
 bool g_scan_token_node_any_create(GScanTokenNodeAny *, const phys_t *, const phys_t *);
 
 
diff --git a/src/analysis/scan/patterns/tokens/nodes/any.c b/src/analysis/scan/patterns/tokens/nodes/any.c
index e5fb1d7..6233cb4 100644
--- a/src/analysis/scan/patterns/tokens/nodes/any.c
+++ b/src/analysis/scan/patterns/tokens/nodes/any.c
@@ -196,7 +196,7 @@ GScanTokenNode *g_scan_token_node_any_new(const phys_t *min, const phys_t *max)
 *                min = éventuelle quantité minimale à retrouver.              *
 *                max = éventuelle quantité maximale à retrouver.              *
 *                                                                             *
-*  Description : Met en place un un noeud pointant une série d'octets.        *
+*  Description : Met en place un noeud pointant une série d'octets.           *
 *                                                                             *
 *  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
@@ -221,6 +221,9 @@ bool g_scan_token_node_any_create(GScanTokenNodeAny *any, const phys_t *min, con
 
         result = (any->min <= any->max);
 
+        if (result && any->min == any->max)
+            result = (any->min > 0);
+
     }
 
     any->has_max = (max != NULL);
@@ -230,6 +233,29 @@ bool g_scan_token_node_any_create(GScanTokenNodeAny *any, const phys_t *min, con
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : any   = séquence d'octets quelconques à étendre.             *
+*                extra = étendue supplémentaire à intégrer.                   *
+*                                                                             *
+*  Description : Etend un noeud pointant une série d'octets.                  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_scan_token_node_any_merge(GScanTokenNodeAny *any, GScanTokenNodeAny *extra)
+{
+    any->min += extra->min;
+
+    if (any->has_max && extra->has_max)
+        any->max += extra->max;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
diff --git a/src/analysis/scan/patterns/tokens/nodes/any.h b/src/analysis/scan/patterns/tokens/nodes/any.h
index 6a5628a..9b2233f 100644
--- a/src/analysis/scan/patterns/tokens/nodes/any.h
+++ b/src/analysis/scan/patterns/tokens/nodes/any.h
@@ -53,6 +53,9 @@ GType g_scan_token_node_any_get_type(void);
 /* Construit un noeud pointant une série d'octets quelconques. */
 GScanTokenNode *g_scan_token_node_any_new(const phys_t *, const phys_t *);
 
+/* Etend un noeud pointant une série d'octets. */
+void g_scan_token_node_any_merge(GScanTokenNodeAny *, GScanTokenNodeAny *);
+
 
 
 #endif  /* _ANALYSIS_SCAN_PATTERNS_TOKENS_NODES_ANY_H */
diff --git a/src/analysis/scan/patterns/tokens/nodes/sequence.c b/src/analysis/scan/patterns/tokens/nodes/sequence.c
index ad332fc..91307bf 100644
--- a/src/analysis/scan/patterns/tokens/nodes/sequence.c
+++ b/src/analysis/scan/patterns/tokens/nodes/sequence.c
@@ -24,6 +24,7 @@
 #include "sequence.h"
 
 
+#include "any.h"
 #include "sequence-int.h"
 
 
@@ -224,7 +225,7 @@ bool g_scan_token_node_sequence_create(GScanTokenNodeSequence *sequence, GScanTo
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : sequence = ensemble de noeuds à compléter.                   *
-*                child  = nouveau noeud à intégrer.                           *
+*                child    = nouveau noeud à intégrer.                         *
 *                                                                             *
 *  Description : Ajoute un noeud à aux décompositions séquentielles de motif. *
 *                                                                             *
@@ -236,10 +237,87 @@ bool g_scan_token_node_sequence_create(GScanTokenNodeSequence *sequence, GScanTo
 
 void g_scan_token_node_sequence_add(GScanTokenNodeSequence *sequence, GScanTokenNode *child)
 {
-    sequence->children = realloc(sequence->children, ++sequence->count * sizeof(GScanTokenNode *));
+    bool processed;                         /* Intégration traitée ?       */
+    GScanTokenNode *last;                   /* Dernier noeud inscrit       */
 
-    sequence->children[sequence->count - 1] = child;
-    g_object_ref(G_OBJECT(child));
+    processed = false;
+
+    if (sequence->count > 0)
+    {
+        last = sequence->children[sequence->count - 1];
+
+        if (G_IS_SCAN_TOKEN_NODE_ANY(last) && G_IS_SCAN_TOKEN_NODE_ANY(child))
+        {
+            g_scan_token_node_any_merge(G_SCAN_TOKEN_NODE_ANY(last), G_SCAN_TOKEN_NODE_ANY(child));
+            processed = true;
+        }
+
+    }
+
+    if (!processed)
+    {
+        sequence->children = realloc(sequence->children, ++sequence->count * sizeof(GScanTokenNode *));
+
+        sequence->children[sequence->count - 1] = child;
+        g_object_ref(G_OBJECT(child));
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : sequence = ensemble de noeuds à consulter.                   *
+*                                                                             *
+*  Description : Indique le nombre de noeuds intégrés dans la séquence.       *
+*                                                                             *
+*  Retour      : Nombre de noeuds représentés.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+size_t g_scan_token_node_sequence_count(const GScanTokenNodeSequence *sequence)
+{
+    size_t result;                          /* Quantité à retourner        */
+
+    result = sequence->count;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : sequence = ensemble de noeuds à consulter.                   *
+*                index    = indice du noeud à retourner.                      *
+*                                                                             *
+*  Description : Fournit un noeud donné d'une décomposition séquentielle.     *
+*                                                                             *
+*  Retour      : Noeud inclus dans l'ensemble ou NULL si mauvais indice.      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GScanTokenNode *g_scan_token_node_sequence_get(const GScanTokenNodeSequence *sequence, size_t index)
+{
+    GScanTokenNode *result;                 /* Instance à retourner        */
+
+    assert(index < sequence->count);
+
+    if (index < sequence->count)
+    {
+        result = sequence->children[index];
+        g_object_ref(G_OBJECT(result));
+    }
+
+    else
+        result = NULL;
+
+    return result;
 
 }
 
diff --git a/src/analysis/scan/patterns/tokens/nodes/sequence.h b/src/analysis/scan/patterns/tokens/nodes/sequence.h
index fc181c6..12df9d1 100644
--- a/src/analysis/scan/patterns/tokens/nodes/sequence.h
+++ b/src/analysis/scan/patterns/tokens/nodes/sequence.h
@@ -56,6 +56,12 @@ GScanTokenNode *g_scan_token_node_sequence_new(GScanTokenNode *);
 /* Ajoute un noeud à aux décompositions séquentielles de motif. */
 void g_scan_token_node_sequence_add(GScanTokenNodeSequence *, GScanTokenNode *);
 
+/* Indique le nombre de noeuds intégrés dans la séquence. */
+size_t g_scan_token_node_sequence_count(const GScanTokenNodeSequence *);
+
+/* Fournit un noeud donné d'une décomposition séquentielle. */
+GScanTokenNode *g_scan_token_node_sequence_get(const GScanTokenNodeSequence *, size_t);
+
 
 
 #endif  /* _ANALYSIS_SCAN_PATTERNS_TOKENS_NODES_SEQUENCE_H */
diff --git a/tests/analysis/scan/fuzzing.py b/tests/analysis/scan/fuzzing.py
index 5d99c35..1957f72 100644
--- a/tests/analysis/scan/fuzzing.py
+++ b/tests/analysis/scan/fuzzing.py
@@ -176,7 +176,9 @@ rule test {
 }
 '''
 
-        self.check_rule_failure(rule)
+        with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'):
+
+            scanner = ContentScanner(rule)
 
 
     def testAllocations(self):
@@ -212,3 +214,38 @@ rule test {
 '''
 
         self.check_rule_success(rule, cnt)
+
+
+    def testValidHexRangeMerge(self):
+        """Merge valid hexadecimal ranges."""
+
+        rule = '''
+rule test {
+
+   bytes:
+      $a = { [0] ?? }
+
+   condition:
+      $a
+
+}
+'''
+
+        with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'):
+
+            scanner = ContentScanner(rule)
+
+
+        rule = '''
+rule test {
+
+   bytes:
+      $a = { [2] ?? }
+
+   condition:
+      $a
+
+}
+'''
+
+        self.check_rule_failure(rule)
-- 
cgit v0.11.2-87-g4458