summaryrefslogtreecommitdiff
path: root/src/analysis/scan/patterns/tokens/nodes/masked.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/analysis/scan/patterns/tokens/nodes/masked.c')
-rw-r--r--src/analysis/scan/patterns/tokens/nodes/masked.c1135
1 files changed, 1135 insertions, 0 deletions
diff --git a/src/analysis/scan/patterns/tokens/nodes/masked.c b/src/analysis/scan/patterns/tokens/nodes/masked.c
new file mode 100644
index 0000000..5194cb8
--- /dev/null
+++ b/src/analysis/scan/patterns/tokens/nodes/masked.c
@@ -0,0 +1,1135 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * masked.c - gestion d'une recherche de motif partielle
+ *
+ * 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 "masked.h"
+
+
+#include <assert.h>
+
+
+#include "masked-int.h"
+#include "../../backends/bitap.h"
+
+
+
+/* ------------------------ DECOMPOSITION DE MOTIF RECHERCHE ------------------------ */
+
+
+/* Initialise la classe des bribes de motif partielles. */
+static void g_scan_token_node_masked_class_init(GScanTokenNodeMaskedClass *);
+
+/* Initialise une instance de bribe de motif partielle. */
+static void g_scan_token_node_masked_init(GScanTokenNodeMasked *);
+
+/* Supprime toutes les références externes. */
+static void g_scan_token_node_masked_dispose(GScanTokenNodeMasked *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_scan_token_node_masked_finalize(GScanTokenNodeMasked *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Parcourt une arborescence de noeuds et y relève des éléments. */
+static void g_scan_token_node_masked_visit(GScanTokenNodeMasked *, scan_tree_points_t *);
+
+/* Inscrit la définition d'un motif dans un moteur de recherche. */
+static bool g_scan_token_node_masked_enroll(GScanTokenNodeMasked *, GEngineBackend *, size_t, size_t *);
+
+/* Récupère un identifiant final pour un atome d'octets. */
+static bool g_scan_token_node_masked_build_id(GScanTokenNodeMasked *, GEngineBackend *);
+
+/* Détermine si un contenu d'intérêt est présent à une position. */
+static bool check_scan_token_node_masked_content(const masked_byte_t *, size_t, phys_t, GBinContent *);
+
+/* Transforme les correspondances locales en trouvailles. */
+static void g_scan_token_node_masked_check_forward(const GScanTokenNodeMasked *, scan_node_check_params_t *, TokenNodeCheckFlags, bool *);
+
+/* Transforme les correspondances locales en trouvailles. */
+static void g_scan_token_node_masked_check_backward(const GScanTokenNodeMasked *, scan_node_check_params_t *, TokenNodeCheckFlags, bool *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DECOMPOSITION DE MOTIF RECHERCHE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un noeud représentant une bribe partielle à retrouver. */
+G_DEFINE_TYPE(GScanTokenNodeMasked, g_scan_token_node_masked, G_TYPE_SCAN_TOKEN_NODE);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des bribes de motif partielles. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_class_init(GScanTokenNodeMaskedClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GScanTokenNodeClass *node; /* Version de classe parente */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_token_node_masked_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_scan_token_node_masked_finalize;
+
+ node = G_SCAN_TOKEN_NODE_CLASS(klass);
+
+ node->apply = (apply_scan_token_node_flags_fc)NULL;
+ node->visit = (visit_scan_token_node_fc)g_scan_token_node_masked_visit;
+ node->enroll = (enroll_scan_token_node_fc)g_scan_token_node_masked_enroll;
+ node->build_id = (build_scan_token_node_id_fc)g_scan_token_node_masked_build_id;
+ node->check_forward = (check_scan_token_node_fc)g_scan_token_node_masked_check_forward;
+ node->check_backward = (check_scan_token_node_fc)g_scan_token_node_masked_check_backward;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : masked = instance à initialiser. *
+* *
+* Description : Initialise une instance de bribe de motif partielle. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_init(GScanTokenNodeMasked *masked)
+{
+ masked->bytes = NULL;
+ masked->len = 0;
+
+ masked->raw = NULL;
+ masked->raw_count = 0;
+
+ masked->enrolled_atoms = NULL;
+ masked->enrolled_count = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : masked = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_dispose(GScanTokenNodeMasked *masked)
+{
+ G_OBJECT_CLASS(g_scan_token_node_masked_parent_class)->dispose(G_OBJECT(masked));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : masked = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_finalize(GScanTokenNodeMasked *masked)
+{
+ size_t i; /* Boucle de parcours */
+
+ if (masked->bytes != NULL)
+ free(masked->bytes);
+
+ for (i = 0; i < masked->raw_count; i++)
+ exit_szstr(&masked->raw[i]);
+
+ if (masked->raw != NULL)
+ free(masked->raw);
+
+ if (masked->enrolled_atoms != NULL)
+ free(masked->enrolled_atoms);
+
+ G_OBJECT_CLASS(g_scan_token_node_masked_parent_class)->finalize(G_OBJECT(masked));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : byte = valeur masquée à intégrer. *
+* *
+* Description : Construit une bribe de motif partielle. *
+* *
+* Retour : Mécanismes mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GScanTokenNode *g_scan_token_node_masked_new(const masked_byte_t *byte)
+{
+ GScanTokenNode *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_SCAN_TOKEN_NODE_MASKED, NULL);
+
+ if (!g_scan_token_node_masked_create(G_SCAN_TOKEN_NODE_MASKED(result), byte))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : masked = bribe partielle à initialiser pleinement. *
+* byte = valeur masquée à intégrer. *
+* *
+* Description : Met en place une bribe de motif partielle. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_scan_token_node_masked_create(GScanTokenNodeMasked *masked, const masked_byte_t *byte)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ g_scan_token_node_masked_add(masked, byte);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : masked = ensemble de noeuds à compléter. *
+* byte = valeur masquée à intégrer. *
+* *
+* Description : Enregistre la valeur d'octet à rechercher avec son masque. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_scan_token_node_masked_add(GScanTokenNodeMasked *masked, const masked_byte_t *byte)
+{
+ assert((byte->value & 0x0f) == 0 || (byte->value & 0xf0) == 0);
+
+ masked->bytes = realloc(masked->bytes, ++masked->len * sizeof(masked_byte_t));
+
+ masked->bytes[masked->len - 1] = *byte;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : node = point de départ du parcours à effectuer. *
+* points = points capitaux de l'arborescence. [OUT] *
+* *
+* Description : Parcourt une arborescence de noeuds et y relève des éléments.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_visit(GScanTokenNodeMasked *node, scan_tree_points_t *points)
+{
+ GScanTokenNodeMasked *other; /* Concurrence à mesurer */
+
+ if (points->best_masked == NULL)
+ points->best_masked = G_SCAN_TOKEN_NODE(node);
+
+ else
+ {
+ other = G_SCAN_TOKEN_NODE_MASKED(points->best_masked);
+
+ if (node->len > other->len)
+ points->best_masked = G_SCAN_TOKEN_NODE(node);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = définition de la bribe à enregistrer. *
+* backend = moteur de recherche à préchauffer. *
+* maxsize = taille max. des atomes (mise en commun optimisée). *
+* slow = niveau de ralentissement induit (0 = idéal). [OUT] *
+* *
+* Description : Inscrit la définition d'un motif dans un moteur de recherche.*
+* *
+* Retour : Bilan de l'opération à renvoyer. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_scan_token_node_masked_enroll(GScanTokenNodeMasked *node, GEngineBackend *backend, size_t maxsize, size_t *slow)
+{
+ bool result; /* Statut à retourner */
+ bool forced; /* Inclusion dans un scan ? */
+ //size_t len_to_enroll; /* Taille à considérer */
+ size_t i; /* Boucle de parcours */
+
+ result = true;
+
+ forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN);
+
+ if (forced)
+ {
+ *slow += (maxsize * 2);
+
+ node->raw = make_atoms_from_masked_bytes(node->bytes, node->len, &node->raw_count);
+
+ /**
+ * Dans le cas bien précis de l'usage de l'algorithme Bitap pour les recherches
+ * dans le contenu binaire à analyser, on tire parti du coût nul des recherches
+ * multiples pour une même position.
+ */
+
+ if (G_IS_BITAP_BACKEND(backend))
+ {
+ //len_to_enroll = (node->len < maxsize ? node->len : maxsize);
+
+ /* TODO */
+ assert(false);
+
+
+ node->enrolled_count = 1;
+
+ }
+
+ else
+ {
+ node->enrolled_atoms = malloc(node->raw_count * sizeof(tracked_scan_atom_t));
+ node->enrolled_count = node->raw_count;
+
+ for (i = 0; i < node->enrolled_count && result; i++)
+ {
+ find_best_atom(&node->raw[i], maxsize, &node->enrolled_atoms[i], NULL);
+
+ /**
+ * Correction : si l'atome ne représente qu'une vue partielle,
+ * la validation rapide ne peut s'appliquer.
+ */
+ if (node->enrolled_atoms[i].fast_check)
+ node->enrolled_atoms[i].fast_check = (node->enrolled_atoms[i].len == node->len);
+
+ result = enroll_prepared_atom(&node->raw[i], backend, &node->enrolled_atoms[i]);
+
+ }
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = définition de la bribe à peaufiner. *
+* backend = moteur de recherche à préchauffer. *
+* *
+* Description : Récupère un identifiant final pour un atome d'octets. *
+* *
+* Retour : Bilan de l'opération à renvoyer. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_scan_token_node_masked_build_id(GScanTokenNodeMasked *node, GEngineBackend *backend)
+{
+ bool result; /* Statut à retourner */
+ size_t i; /* Boucle de parcours #1 */
+
+ result = true;
+
+ for (i = 0; i < node->enrolled_count && result; i++)
+ result = build_atom_pattern_id(&node->enrolled_atoms[i], backend);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : bytes = octets partiels avec leur masque à interpréter. *
+* len = quantité d'octets à interpréter. *
+* start = point d'analyse à respecter. *
+* content = accès au contenu brut pour vérifications (optim.) *
+* *
+* Description : Détermine si un contenu d'intérêt est présent à une position.*
+* *
+* Retour : Bilan de l'analyse : true pour une correspondance. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool check_scan_token_node_masked_content(const masked_byte_t *bytes, size_t len, phys_t start, GBinContent *content)
+{
+ bool result; /* Bilan à retourner */
+ vmpa2t pos; /* Position dans les données */
+ const bin_t *ptr; /* Accès aux données brutes */
+ size_t i; /* Boucle de parcours */
+
+ result = false;
+
+ init_vmpa(&pos, start, VMPA_NO_VIRTUAL);
+
+ ptr = g_binary_content_get_raw_access(content, &pos, len);
+
+ for (i = 0; i < len; i++)
+ {
+ if ((ptr[i] & bytes[i].mask) != bytes[i].value)
+ break;
+ }
+
+ result = (i == len);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = définition de la bribe à manipuler. *
+* params = accès direct aux éléments utiles aux validations. *
+* cflags = altérations de traitement à respecter. *
+* skip = détermine si l'analyse est différée. [OUT] *
+* *
+* Description : Transforme les correspondances locales en trouvailles. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_check_forward(const GScanTokenNodeMasked *node, scan_node_check_params_t *params, TokenNodeCheckFlags cflags, bool *skip)
+{
+#ifndef NDEBUG
+ bool forced; /* Inclusion dans un scan ? */
+#endif
+ match_area_t **areas; /* Liste de zones à constituer */
+ size_t *count; /* Taille de cette liste */
+ bool copy; /* Besoin d'une copie ? */
+ bool inverted; /* Inversion des bilans ? */
+ size_t i; /* Boucle de parcours #1 */
+ const tracked_scan_atom_t *atom; /* Atome correspondant */
+ match_area_t *atoms; /* Localisations des bribes */
+ bool first_round; /* Premier tour de traitement */
+ match_area_t *area; /* Correspondance à valider */
+ match_area_t *next; /* Correspondance suivante */
+ phys_t start; /* Début potentiel de motif */
+ bool status; /* Bilan d'une correspondance */
+ phys_t after; /* Espace disposible après */
+ phys_t min_end; /* Fin la plus proche possible */
+ phys_t max_end; /* Fin la plus éloignée trouvée*/
+ bool updated; /* Existence de correspondance */
+ size_t rcount; /* Quantité de bornes présentes*/
+ const node_offset_range_t *ranges; /* Bornes d'espace à parcourir */
+ size_t r; /* Boucle de parcours #xxxxxxxxxxxx*/
+ phys_t p; /* Boucle de parcours #xxxxxxxxxxxx*/
+ phys_t updated_edge; /* Nouvelle bordure de motif */
+ match_area_t *new_area; /* Copie de correspondance */
+
+ if (0x0) printf("=================== CHECK :: %s (skip? %d)\n", __FUNCTION__, *skip);
+
+ if (*skip)
+ return;
+
+ /**
+ * Si l'analyse arrive à un ou plusieurs octets masqués, soit il s'agit du
+ * premier noeud, et la génération d'atomes a été forcée pour obtenir des
+ * points de départ, soit des correspondances ont été établies au préalable,
+ * et il ne doit alors pas y avoir d'atome mis en place (si l'initialisation
+ * ne provient pas d'une mise en place artificielle par une inversion NOT).
+ */
+#ifndef NDEBUG
+ forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN);
+ assert((!params->initialized && forced) || (params->initialized && (!forced || cflags & TNCF_KEEP_DISCARDED)));
+#endif
+
+ if (!params->initialized)
+ {
+ /* Destinations établies une fois pour toutes */
+
+ if (cflags & TNCF_KEEP_DISCARDED)
+ {
+ areas = &params->kept_areas;
+ count = &params->kept_count;
+
+ copy = false;
+ inverted = true;
+
+ }
+
+ else if (cflags & TNCF_CREATE_NEW)
+ {
+ areas = &params->created_areas;
+ count = &params->created_count;
+
+ copy = true;
+ inverted = false;
+
+ }
+
+ else
+ {
+ assert(cflags & TNCF_UPDATE_IN_PLACE);
+
+ areas = &params->main_areas;
+ count = &params->main_count;
+
+ copy = false;
+ inverted = false;
+
+ }
+
+ /* Parcours des combinaisons enregistrées */
+
+ for (i = 0; i < node->enrolled_count; i++)
+ {
+ atom = &node->enrolled_atoms[i];
+
+ atoms = g_scan_context_get_atom_matches(params->context, atom->pid);
+
+ first_round = (*count == 0);
+
+ if (atom->fast_check)
+ {
+ /**
+ * Toutes les correspondances sont validées d'office car le motif identifié
+ * correspondant au motif complet.
+ */
+
+ if (!inverted)
+ {
+ for_each_match_area(area, atoms)
+ {
+ /**
+ * La modification de la zone d'origine est possible dans tous les cas
+ * car cette zone a été allouée de façon dédiée à un type de correspondances
+ * et ne sera pas réutilisée comme autre source de correspondance ailleurs.
+ */
+
+ assert(area->end >= atom->len);
+
+ area->start = area->end - atom->len;
+
+ (*count)++;
+
+ }
+
+ }
+
+ else
+ atoms = NULL;
+
+ }
+
+ else
+ {
+ for_each_match_area_safe(area, &atoms, next)
+ {
+ start = area->end - atom->len - atom->pos;
+
+ status = check_scan_token_node_masked_content(node->bytes, node->len,
+ start, params->content);
+
+ if (status)
+ {
+ /**
+ * La modification de la zone d'origine est possible dans tous les cas
+ * car cette zone a été allouée de façon dédiée à un type de correspondances
+ * et ne sera pas réutilisée comme autre source de correspondance ailleurs.
+ */
+
+ if (!inverted)
+ {
+ area->start = start;
+ area->end += atom->rem;
+
+ (*count)++;
+
+ }
+ else
+ del_match_area(area, &atoms);
+
+ }
+
+ else
+ {
+ /**
+ * Les principes de modifications restent valables, même inversés.
+ */
+ if (inverted)
+ {
+ area->start = start;
+ area->end += atom->rem;
+
+ (*count)++;
+
+ }
+ else
+ del_match_area(area, &atoms);
+
+ }
+
+ }
+
+
+ }
+
+ /* Mise à jour de la liste */
+
+ if (atoms != NULL)
+ {
+ if (first_round)
+ *areas = atoms;
+
+ else
+ merge_match_areas(areas, &atoms);
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Poursuite des traitements sur des correspondances déjà amorcées, impliquant
+ * des comparaisons entières de motifs.
+ */
+ else
+ {
+ if (0x0) printf(" LIST : %p (sz = %zu)\n", params->main_areas, params->main_count);
+
+ for_each_match_area_safe(area, &params->main_areas, next)
+ {
+ assert(area->end <= params->content_end);
+
+ after = params->content_end - area->end;
+
+ if (0x0) printf("---------- iter @ %u -> %u\n", (unsigned int)area->start, (unsigned int)area->end);
+
+ /**
+ * S'il s'avère qu'il existe de multiples correspondances dans l'espace
+ * analysé, c'est la prise en compte d'une éventuelle avarice quant aux
+ * distances consommées qui va sélectionner la position d'une bribe de
+ * correspondance retenue.
+ *
+ * Par exemple, deux correspondances '?1 ?1 [1-3] ?2 ?2' peuvent être
+ * valides pour un même contenu :
+ *
+ * aa.bbb -> correspondance 'aa.bb'
+ * ^
+ *
+ * aa.bbb -> correspondance 'aa..bb'
+ * ^
+ */
+
+ min_end = params->content_end;
+ max_end = params->content_start;
+
+ updated = false;
+
+ /* Souplesse dans les positions ? */
+ if (offsets_exist(&params->offset))
+ {
+ ranges = get_node_search_offset_ranges_2(&params->offset, &rcount);
+
+ for (r = 0; r < rcount; r++)
+ {
+ assert(ranges[r].has_max);
+ assert((ranges[r].max - ranges[r].min) <= MAX_RANGE_FOR_MANUAL_CHECK);
+
+ for (p = ranges[r].min; p <= ranges[r].max; p++)
+ {
+ /**
+ * Si la fin d'une correspondance potentielle est trop près de
+ * la fin du contenu binaire et ne peut contenir le motif
+ * représenté, alors la corresponance est écartée sans appel.
+ */
+ if ((p + node->len) > after)
+ break;
+
+ status = check_scan_token_node_masked_content(node->bytes, node->len,
+ area->end + p, params->content);
+
+ if (status)
+ {
+ updated_edge = area->end + p + node->len;
+
+ if (updated_edge < min_end)
+ min_end = updated_edge;
+
+ if (updated_edge > max_end)
+ max_end = updated_edge;
+
+ updated = true;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /* Position immédiatement attendue */
+ else
+ {
+ /**
+ * Si la fin d'une correspondance potentielle est trop près de
+ * la fin du contenu binaire et ne peut contenir le motif
+ * représenté, alors la corresponance est écartée sans appel.
+ */
+ if (node->len <= after)
+ {
+ status = check_scan_token_node_masked_content(node->bytes, node->len,
+ area->end, params->content);
+
+ if (status)
+ {
+ updated_edge = area->end + node->len;
+
+ min_end = updated_edge;
+ max_end = updated_edge;
+
+ updated = true;
+
+ }
+
+ }
+
+ }
+
+ if (updated)
+ {
+ /**
+ * Si seuls les rejets sont d'intérêt, les correspondances établies
+ * ne se voient pas mises à jours ni retirées.
+ */
+
+ if ((cflags & TNCF_KEEP_DISCARDED) == 0)
+ {
+ if (cflags & TNCF_UPDATE_IN_PLACE)
+ area->end = (1 /* greedy */ ? min_end : max_end);
+
+ else if (cflags & TNCF_CREATE_NEW)
+ {
+ new_area = g_umem_slice_alloc(params->allocator);
+
+ *new_area = *area;
+
+ new_area->end = (1 /* greedy */ ? min_end : max_end);
+
+ add_tail_match_area(new_area, &params->created_areas);
+ params->created_count++;
+
+ }
+
+#ifndef NDEBUG
+ else
+ assert(false);
+#endif
+
+ }
+
+ }
+
+ else
+ {
+ /**
+ * Si la liste principale doit être mise à jour...
+ */
+
+ if (cflags & TNCF_UPDATE_IN_PLACE)
+ {
+ del_match_area(area, &params->main_areas);
+ assert(params->main_count > 0);
+ params->main_count--;
+ }
+
+ /**
+ * Au cas où le rejet est d'intérêt, l'absence de correspondance
+ * est conservée dans une liste dédiée.
+ */
+
+ if (cflags & TNCF_KEEP_DISCARDED)
+ {
+ if (cflags & TNCF_UPDATE_IN_PLACE)
+ {
+ add_tail_match_area(area, &params->kept_areas);
+ params->kept_count++;
+ }
+
+ else if (cflags & TNCF_CREATE_NEW)
+ {
+ new_area = g_umem_slice_alloc(params->allocator);
+
+ *new_area = *area;
+
+ new_area->end = (1 /* greedy */ ? min_end : max_end);
+
+ add_tail_match_area(new_area, &params->kept_areas);
+ params->kept_count++;
+
+ }
+
+#ifndef NDEBUG
+ else
+ assert(false);
+#endif
+
+ }
+
+ }
+
+ }
+
+ }
+
+ params->initialized = true;
+
+ disable_all_ranges_in_node_search_offset(&params->offset);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = définition de la bribe à manipuler. *
+* params = accès direct aux éléments utiles aux validations. *
+* cflags = altérations de traitement à respecter. *
+* skip = détermine si l'analyse est différée. [OUT] *
+* *
+* Description : Transforme les correspondances locales en trouvailles. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_scan_token_node_masked_check_backward(const GScanTokenNodeMasked *node, scan_node_check_params_t *params, TokenNodeCheckFlags cflags, bool *skip)
+{
+#ifndef NDEBUG
+ bool forced; /* Inclusion dans un scan ? */
+#endif
+
+
+
+ bool status; /* Bilan d'une correspondance */
+
+
+ match_area_t *area; /* Correspondance à valider */
+ match_area_t *next; /* Correspondance suivante */
+ phys_t before; /* Espace disposible avant */
+ phys_t min_start; /* Début le plus proche trouvé */
+ phys_t max_start; /* Début le plus distant trouvé*/
+ bool updated; /* Existence de correspondance */
+ size_t rcount; /* Quantité de bornes présentes*/
+ const node_offset_range_t *ranges; /* Bornes d'espace à parcourir */
+ size_t r; /* Boucle de parcours #xxxxxxxxxxxx*/
+ phys_t p; /* Boucle de parcours #xxxxxxxxxxxx*/
+ phys_t updated_edge; /* Nouvelle bordure de motif */
+ match_area_t *new_area; /* Copie de correspondance */
+
+ if (0x0) printf("=================== CHECK :: %s (skip? %d)\n", __FUNCTION__, *skip);
+
+ if (*skip)
+ return;
+
+ /**
+ * En lecture à rebourd, au moins un noeud a été solicité pour analyse (lors
+ * du sens de lecteur normal). Donc l'initialisation a déjà dû avoir lieu.
+ */
+ assert(params->initialized);
+
+ /**
+ * Si les recherches associées au noeud ont été forcées, alors les traitements
+ * liés ont déjà été effectués, et l'appel de cette fonction aurait dû être sauté.
+ */
+#ifndef NDEBUG
+ forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN);
+ assert(!forced);
+#endif
+
+
+
+
+ /**
+ * .............
+ */
+ if (0)
+ {
+
+
+ ;
+
+
+
+ }
+
+ /**
+ * Poursuite des traitements sur des correspondances déjà amorcées, impliquant
+ * des comparaisons entières de motifs.
+ */
+ else
+ {
+ if (0x0) printf(" LIST : %p (sz = %zu)\n", params->main_areas, params->main_count);
+
+ for_each_match_area_safe(area, &params->main_areas, next)
+ {
+ assert(params->content_start <= area->start);
+
+ before = area->start - params->content_start;
+
+ if (0x0) printf("---------- iter @ %u -> %u\n", (unsigned int)area->start, (unsigned int)area->end);
+
+ /**
+ * Il ne peut y avoir qu'une seule séquence d'octets à un même
+ * emplacement. En revanche, des modificateurs peuvent construire
+ * possédant une même base, mais offrant des suffixes différents
+ * (par exemple, un marqueur nul UTF-16 final en complément).
+ *
+ * L'ensemble des combinaisons produites doit ainsi être exploré.
+ */
+
+ min_start = params->content_start;
+ max_start = params->content_end;
+
+ updated = false;
+
+ /* Souplesse dans les positions ? */
+ if (offsets_exist(&params->offset))
+ {
+ ranges = get_node_search_offset_ranges_2(&params->offset, &rcount);
+
+ for (r = 0; r < rcount; r++)
+ {
+ assert(ranges[r].has_max);
+ assert((ranges[r].max - ranges[r].min) <= MAX_RANGE_FOR_MANUAL_CHECK);
+
+ for (p = ranges[r].min; p <= ranges[r].max; p++)
+ {
+ /**
+ * Si la fin d'une correspondance potentielle est trop près de
+ * la fin du contenu binaire et ne peut contenir le motif
+ * représenté, alors la corresponance est écartée sans appel.
+ */
+ if ((p + node->len) > before)
+ break;
+
+ status = check_scan_token_node_masked_content(node->bytes, node->len,
+ area->start - node->len - p,
+ params->content);
+
+ if (status)
+ {
+ updated_edge = area->start - node->len - p;
+
+ if (updated_edge > min_start)
+ min_start = updated_edge;
+
+ if (updated_edge < max_start)
+ max_start = updated_edge;
+
+ updated = true;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /* Position immédiatement attendue */
+ else
+ {
+ /**
+ * Si la fin d'une correspondance potentielle est trop près du
+ * début du contenu binaire et ne peut contenir le motif
+ * représenté, alors la corresponance est écartée sans appel.
+ */
+ if (node->len <= before)
+ {
+ status = check_scan_token_node_masked_content(node->bytes, node->len,
+ area->start - node->len,
+ params->content);
+
+ if (status)
+ {
+ updated_edge = area->start - node->len;
+
+ if (updated_edge > min_start)
+ min_start = updated_edge;
+
+ if (updated_edge < max_start)
+ max_start = updated_edge;
+
+ updated = true;
+
+ }
+
+ }
+
+ }
+
+ if (updated)
+ {
+ /**
+ * Si seuls les rejets sont d'intérêt, les correspondances établies
+ * ne se voient pas mises à jours ni retirées.
+ */
+
+ if ((cflags & TNCF_KEEP_DISCARDED) == 0)
+ {
+ if (cflags & TNCF_UPDATE_IN_PLACE)
+ area->start = (1 /* greedy */ ? min_start : max_start);
+
+ else if (cflags & TNCF_CREATE_NEW)
+ {
+ new_area = g_umem_slice_alloc(params->allocator);
+
+ *new_area = *area;
+
+ new_area->start = (1 /* greedy */ ? min_start : max_start);
+
+ add_tail_match_area(new_area, &params->created_areas);
+ params->created_count++;
+
+ }
+
+#ifndef NDEBUG
+ else
+ assert(false);
+#endif
+
+ }
+
+ }
+
+ else
+ {
+ /**
+ * Si la liste principale doit être mise à jour...
+ */
+
+ if (cflags & TNCF_UPDATE_IN_PLACE)
+ {
+ del_match_area(area, &params->main_areas);
+ assert(params->main_count > 0);
+ params->main_count--;
+ }
+
+ /**
+ * Au cas où le rejet est d'intérêt, l'absence de correspondance
+ * est conservée dans une liste dédiée.
+ */
+
+ if (cflags & TNCF_KEEP_DISCARDED)
+ {
+ if (cflags & TNCF_UPDATE_IN_PLACE)
+ {
+ add_tail_match_area(area, &params->kept_areas);
+ params->kept_count++;
+ }
+
+ else if (cflags & TNCF_CREATE_NEW)
+ {
+ new_area = g_umem_slice_alloc(params->allocator);
+
+ *new_area = *area;
+
+ new_area->start = (1 /* greedy */ ? min_start : max_start);
+
+ add_tail_match_area(new_area, &params->kept_areas);
+ params->kept_count++;
+
+ }
+
+#ifndef NDEBUG
+ else
+ assert(false);
+#endif
+
+ }
+
+ }
+
+ }
+
+ }
+
+ disable_all_ranges_in_node_search_offset(&params->offset);
+
+}