/* 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 .
*/
#include "masked.h"
#include
#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
size_t i; /* Boucle de parcours #1 */
const tracked_scan_atom_t *atom; /* Atome correspondant */
GUMemSlice *atoms; /* Localisations des bribes */
const umem_slice_iter_t *aiter; /* Boucle de parcours #2 */
match_area_t *end; /* Borne de fin de parcours */
match_area_t *pos; /* Position courante */
bool status; /* Bilan d'une correspondance */
match_area_t *area; /* Correspondance à valider */
match_area_t *next; /* Correspondance suivante */
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)
{
for (i = 0; i < node->enrolled_count; i++)
{
atom = &node->enrolled_atoms[i];
atoms = g_scan_context_get_atom_matches(params->context, atom->pid);
if (atom->fast_check)
{
for (aiter = g_umem_slice_get_iter(atoms); aiter != NULL; aiter = aiter->next)
{
end = aiter->data_end;
for (pos = aiter->data; pos < end; pos++)
{
/**
* 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.
*/
pos->end = pos->start + atom->len;
if (cflags & TNCF_KEEP_DISCARDED)
{
add_tail_match_area(pos, ¶ms->kept_areas);
params->kept_count++;
}
else if (cflags & TNCF_CREATE_NEW)
{
add_tail_match_area(pos, ¶ms->created_areas);
params->created_count++;
}
else
{
assert(cflags & TNCF_UPDATE_IN_PLACE);
add_tail_match_area(pos, ¶ms->main_areas);
params->main_count++;
}
}
}
}
else
{
for (aiter = g_umem_slice_get_iter(atoms); aiter != NULL; aiter = aiter->next)
{
end = aiter->data_end;
for (pos = aiter->data; pos < end; pos++)
{
status = check_scan_token_node_masked_content(node->bytes, node->len,
pos->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.
*/
pos->end = pos->start + node->len;
if (cflags & TNCF_KEEP_DISCARDED)
{
add_tail_match_area(pos, ¶ms->kept_areas);
params->kept_count++;
}
else if (cflags & TNCF_CREATE_NEW)
{
add_tail_match_area(pos, ¶ms->created_areas);
params->created_count++;
}
else
{
assert(cflags & TNCF_UPDATE_IN_PLACE);
add_tail_match_area(pos, ¶ms->main_areas);
params->main_count++;
}
}
}
}
}
}
}
/**
* 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);
}