/* Chrysalide - Outil d'analyse de fichiers binaires
* any.c - suite d'octets quelconques
*
* 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 "any.h"
#include
#include "any-int.h"
/* ------------------------ DECOMPOSITION DE MOTIF RECHERCHE ------------------------ */
/* Initialise la classe des séries d'octets quelconques. */
static void g_scan_token_node_any_class_init(GScanTokenNodeAnyClass *);
/* Initialise une instance de série d'octets quelconques. */
static void g_scan_token_node_any_init(GScanTokenNodeAny *);
/* Supprime toutes les références externes. */
static void g_scan_token_node_any_dispose(GScanTokenNodeAny *);
/* Procède à la libération totale de la mémoire. */
static void g_scan_token_node_any_finalize(GScanTokenNodeAny *);
/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
/* Inscrit la définition d'un motif dans un moteur de recherche. */
static bool g_scan_token_node_any_enroll(GScanTokenNodeAny *, GScanContext *, GEngineBackend *, size_t, size_t *);
/* Transforme les correspondances locales en trouvailles. */
static void g_scan_token_node_any_check_forward(const GScanTokenNodeAny *, scan_node_check_params_t *, TokenNodeCheckFlags, bool *);
/* Transforme les correspondances locales en trouvailles. */
static void g_scan_token_node_any_check_backward(const GScanTokenNodeAny *, scan_node_check_params_t *, TokenNodeCheckFlags, bool *);
/* ---------------------------------------------------------------------------------- */
/* DECOMPOSITION DE MOTIF RECHERCHE */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour une série d'octets quelconque, vide ou non. */
G_DEFINE_TYPE(GScanTokenNodeAny, g_scan_token_node_any, G_TYPE_SCAN_TOKEN_NODE);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des séries d'octets quelconques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_scan_token_node_any_class_init(GScanTokenNodeAnyClass *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_any_dispose;
object->finalize = (GObjectFinalizeFunc)g_scan_token_node_any_finalize;
node = G_SCAN_TOKEN_NODE_CLASS(klass);
node->apply = (apply_scan_token_node_flags_fc)NULL;
node->visit = (visit_scan_token_node_fc)NULL;
node->enroll = (enroll_scan_token_node_fc)g_scan_token_node_any_enroll;
node->check_forward = (check_scan_token_node_fc)g_scan_token_node_any_check_forward;
node->check_backward = (check_scan_token_node_fc)g_scan_token_node_any_check_backward;
}
/******************************************************************************
* *
* Paramètres : any = instance à initialiser. *
* *
* Description : Initialise une instance de série d'octets quelconques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_scan_token_node_any_init(GScanTokenNodeAny *any)
{
any->min = 0;
any->has_max = false;
}
/******************************************************************************
* *
* Paramètres : any = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_scan_token_node_any_dispose(GScanTokenNodeAny *any)
{
G_OBJECT_CLASS(g_scan_token_node_any_parent_class)->dispose(G_OBJECT(any));
}
/******************************************************************************
* *
* Paramètres : any = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_scan_token_node_any_finalize(GScanTokenNodeAny *any)
{
G_OBJECT_CLASS(g_scan_token_node_any_parent_class)->finalize(G_OBJECT(any));
}
/******************************************************************************
* *
* Paramètres : min = éventuelle quantité minimale à retrouver. *
* max = éventuelle quantité maximale à retrouver. *
* *
* Description : Construit un noeud pointant une série d'octets quelconques. *
* *
* Retour : Mécanismes mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GScanTokenNode *g_scan_token_node_any_new(const phys_t *min, const phys_t *max)
{
GScanTokenNode *result; /* Structure à retourner */
result = g_object_new(G_TYPE_SCAN_TOKEN_NODE_ANY, NULL);
if (!g_scan_token_node_any_create(G_SCAN_TOKEN_NODE_ANY(result), min, max))
g_clear_object(&result);
return result;
}
/******************************************************************************
* *
* Paramètres : any = séquence d'octets quelconques à initialiser pleinement.*
* min = éventuelle quantité minimale à retrouver. *
* max = éventuelle quantité maximale à retrouver. *
* *
* Description : Met en place un noeud pointant une série d'octets. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_scan_token_node_any_create(GScanTokenNodeAny *any, const phys_t *min, const phys_t *max)
{
bool result; /* Bilan à retourner */
result = true;
if (min != NULL)
any->min = *min;
else
any->min = 0;
if (max != NULL)
{
any->max = *max;
result = (any->min <= any->max);
if (result && any->min == any->max)
result = (any->min > 0);
}
any->has_max = (max != NULL);
return result;
}
/******************************************************************************
* *
* 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 */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : node = définition de la bribe à enregistrer. *
* context = contexte de l'analyse à mener. *
* 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_any_enroll(GScanTokenNodeAny *node, GScanContext *context, GEngineBackend *backend, size_t maxsize, size_t *slow)
{
bool result; /* Statut à retourner */
bool forced; /* Inclusion dans un scan ? */
result = true;
forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN);
if (forced)
*slow += (maxsize * 4);
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_any_check_forward(const GScanTokenNodeAny *node, scan_node_check_params_t *params, TokenNodeCheckFlags cflags, bool *skip)
{
ScanTokenNodeFlags flags; /* Particularités du noeud */
bool forced; /* Inclusion dans un scan ? */
phys_t size; /* Quantité d'octets considérés*/
phys_t match_size; /* Taille de correspondance */
phys_t i; /* Boucle de parcours #1 */
match_area_t *space; /* Nouvelle zone à intégrer */
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 #2 */
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;
flags = g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node));
forced = (flags & STNF_MAIN);
assert((!params->initialized && forced) || (params->initialized & !forced));
/**
* La situation forcée correspond au cas particulier d'une définition
* complètement abstraite : ??, ?? ??, etc.
*/
if (forced)
{
size = params->content_end - params->content_start;
if (node->has_max && 0 /* greedy ? */)
{
match_size = node->max;
if (match_size > size)
match_size = node->min;
}
else
match_size = node->min;
/**
* Si le contenu binaire est trop petit pour contenir au moins un enregistrement,
* aucune correspondance n'est enregistrée. Comme le noeud est le principale, ie.
* seul et unique, l'analyse s'arrête ensuite d'elle même.
*
* Si le contenu binaire est suffisamment large, des espaces sont créés et l'analyse
* se termine ensuite. La conservation des espaces n'est donc pas utile, ni réalisée.
*/
if (match_size <= size)
{
size -= match_size;
assert(cflags & TNCF_UPDATE_IN_PLACE);
for (i = 0; i < size; i++)
{
space = g_umem_slice_alloc(params->allocator);
space->start = params->content_start + i;
space->end = space->start + match_size;
add_tail_match_area(space, ¶ms->main_areas);
params->main_count++;
}
}
}
/**
* Situation usuelle : des espaces séparent deux noeuds.
*/
else
{
assert(params->initialized);
/**
* Les espaces existants sont à compléter. La présence de tels espaces
* restant à traiter peut provenir d'un aiguillage imposé par un motif
* tel que :
*
* ( aa ?? ?? | bb cc dd ) [0-5] ee ee
*
* Deux espaces sont à considérer avant de rechercher des octets ee :
* [2-7] et [0-5].
*
* Note : ces espaces peuvent être disjoints.
*
* Si aucun espace n'est en place, un est créé.
*/
extend_node_search_offset(¶ms->offset, node->min, node->max, node->has_max);
/**
* Si un décalage enregistré ne peut être consommé par un noeud, les
* résultats sont étendus ici à minima ou maxima.
*/
if (flags & STNF_LAST)
{
assert(offsets_exist(¶ms->offset));
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);
min_end = params->content_end;
max_end = params->content_start;
updated = false;
ranges = get_node_search_offset_ranges_2(¶ms->offset, &rcount);
for (r = 0; r < rcount; r++)
{
if (ranges[r].min > after)
continue;
updated_edge = area->end + ranges[r].min;
if (updated_edge < min_end)
min_end = updated_edge;
if (ranges[r].has_max)
{
if (ranges[r].max > after)
updated_edge = params->content_end;
else
updated_edge = area->end + ranges[r].max;
if (updated_edge > max_end)
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
}
}
}
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_any_check_backward(const GScanTokenNodeAny *node, scan_node_check_params_t *params, TokenNodeCheckFlags cflags, bool *skip)
{
ScanTokenNodeFlags flags; /* Particularités du noeud */
#ifndef NDEBUG
bool forced; /* Inclusion dans un scan ? */
#endif
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 */
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 lecture normal). Donc l'initialisation a déjà dû avoir lieu.
*/
assert(params->initialized);
flags = g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node));
/**
* 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 = (flags & STNF_MAIN);
assert(!forced);
#endif
/**
* Les considérations pour l'extension des espaces en place sont identiques
* à celles formulées dans la fonction g_scan_token_node_any_check_forward().
*/
extend_node_search_offset(¶ms->offset, node->min, node->max, node->has_max);
/**
* Si un décalage enregistré ne peut être consommé par un noeud, les
* résultats sont étendus ici à minima ou maxima.
*/
if (flags & STNF_FIRST)
{
assert(offsets_exist(¶ms->offset));
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);
min_start = params->content_start;
max_start = params->content_end;
updated = false;
ranges = get_node_search_offset_ranges_2(¶ms->offset, &rcount);
for (r = 0; r < rcount; r++)
{
if (ranges[r].min > before)
continue;
updated_edge = area->start - ranges[r].min;
if (updated_edge > min_start)
min_start = updated_edge;
if (ranges[r].has_max)
{
if (ranges[r].max > before)
updated_edge = params->content_start;
else
updated_edge = area->start - ranges[r].max;
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);
}
}