diff options
Diffstat (limited to 'src/analysis/scan/patterns/tokens/nodes/masked.c')
-rw-r--r-- | src/analysis/scan/patterns/tokens/nodes/masked.c | 1135 |
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 = ¶ms->kept_areas; + count = ¶ms->kept_count; + + copy = false; + inverted = true; + + } + + else if (cflags & TNCF_CREATE_NEW) + { + areas = ¶ms->created_areas; + count = ¶ms->created_count; + + copy = true; + inverted = false; + + } + + else + { + assert(cflags & TNCF_UPDATE_IN_PLACE); + + areas = ¶ms->main_areas; + count = ¶ms->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, ¶ms->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(¶ms->offset)) + { + ranges = get_node_search_offset_ranges_2(¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->kept_areas); + params->kept_count++; + + } + +#ifndef NDEBUG + else + assert(false); +#endif + + } + + } + + } + + } + + params->initialized = true; + + disable_all_ranges_in_node_search_offset(¶ms->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, ¶ms->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(¶ms->offset)) + { + ranges = get_node_search_offset_ranges_2(¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->kept_areas); + params->kept_count++; + + } + +#ifndef NDEBUG + else + assert(false); +#endif + + } + + } + + } + + } + + disable_all_ranges_in_node_search_offset(¶ms->offset); + +} |