diff options
Diffstat (limited to 'src/analysis/scan/context.c')
-rw-r--r-- | src/analysis/scan/context.c | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/src/analysis/scan/context.c b/src/analysis/scan/context.c new file mode 100644 index 0000000..7929f9c --- /dev/null +++ b/src/analysis/scan/context.c @@ -0,0 +1,841 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * context.c - suivi d'analyses via contextes + * + * Copyright (C) 2022 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 "context.h" + + +#include <assert.h> +#include <malloc.h> +#include <stdlib.h> +#include <string.h> + + +#include "context-int.h" +#include "exprs/literal.h" +#include "matches/area.h" +#include "matches/bytes.h" +#include "../../common/sort.h" + + + +/* --------------------- MEMORISATION DE PROGRESSIONS D'ANALYSE --------------------- */ + + +/* Initialise la classe des contextes de suivi d'analyses. */ +static void g_scan_context_class_init(GScanContextClass *); + +/* Initialise une instance de contexte de suivi d'analyse. */ +static void g_scan_context_init(GScanContext *); + +/* Supprime toutes les références externes. */ +static void g_scan_context_dispose(GScanContext *); + +/* Procède à la libération totale de la mémoire. */ +static void g_scan_context_finalize(GScanContext *); + +#ifndef __USE_TABLE_FOR_MATCHES + +/* Compare un lien entre motif et correspondances avec un autre. */ +static int compare_matched_pattern(const matched_pattern_t *, const matched_pattern_t *); + +#endif + + + +/* ---------------------------------------------------------------------------------- */ +/* MEMORISATION DE PROGRESSIONS D'ANALYSE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un contexte de suivi d'analyse. */ +G_DEFINE_TYPE(GScanContext, g_scan_context, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des contextes de suivi d'analyses. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_context_class_init(GScanContextClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_context_dispose; + object->finalize = (GObjectFinalizeFunc)g_scan_context_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à initialiser. * +* * +* Description : Initialise une instance de contexte de suivi d'analyse. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_context_init(GScanContext *context) +{ + context->options = NULL; + + context->content = NULL; + context->scan_done = false; + + context->match_allocator = g_umem_slice_new(sizeof(match_area_t)); + context->match_storages = NULL; + context->storages_count = 0; + +#ifdef __USE_TABLE_FOR_MATCHES + context->full_trackers = g_hash_table_new_full(NULL, NULL, NULL/*g_object_unref*/, g_object_unref); +#else + context->full_trackers = NULL; + context->full_allocated = 0; + context->full_count = 0; +#endif + + context->global = true; + + context->conditions = NULL; + context->cond_count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_context_dispose(GScanContext *context) +{ +#ifndef __USE_TABLE_FOR_MATCHES + matched_pattern_t *iter; /* Boucle de parcours #1 */ + matched_pattern_t *max; /* Borne de fin de parcours */ +#endif + size_t i; /* Boucle de parcours #2 */ + + g_clear_object(&context->options); + + g_clear_object(&context->content); + + g_clear_object(&context->match_allocator); + + if (context->full_trackers != NULL) + { +#ifdef __USE_TABLE_FOR_MATCHES + + g_hash_table_destroy(context->full_trackers); + context->full_trackers = NULL; + +#else + + iter = context->full_trackers; + max = iter + context->full_count; + + for (; iter < max; iter++) + g_object_unref(G_OBJECT(iter->matches)); + + free(context->full_trackers); + context->full_trackers = NULL; + +#endif + + } + + for (i = 0; i < context->cond_count; i++) + g_clear_object(&context->conditions[i].expr); + + G_OBJECT_CLASS(g_scan_context_parent_class)->dispose(G_OBJECT(context)); + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_context_finalize(GScanContext *context) +{ + size_t i; /* Boucle de parcours */ + + if (context->match_storages != NULL) + free(context->match_storages); + + if (context->conditions != NULL) + { + for (i = 0; i < context->cond_count; i++) + free(context->conditions[i].name); + + free(context->conditions); + + } + + G_OBJECT_CLASS(g_scan_context_parent_class)->finalize(G_OBJECT(context)); + +} + + +/****************************************************************************** +* * +* Paramètres : options = ensemble d'options d'analyses à respecter. * +* * +* Description : Définit un contexte pour suivi d'analyse. * +* * +* Retour : Fonction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GScanContext *g_scan_context_new(GScanOptions *options) +{ + GScanContext *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_SCAN_CONTEXT, NULL); + + result->options = options; + g_object_ref(G_OBJECT(options)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à consulter. * +* * +* Description : Fournit l'ensemble des options à respecter pour les analyses.* +* * +* Retour : Ensemble d'options en vigueur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GScanOptions *g_scan_context_get_options(const GScanContext *context) +{ + GScanOptions *result; /* Ensemble à retourner */ + + result = context->options; + + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à consulter. * +* content = contenu binaire en cours d'analyse. * +* ids_count = nombre d'identifiants enregistrés. * +* * +* Description : Définit le contenu principal à analyser. * +* * +* Retour : Content binaire associé au context. * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_scan_context_set_content(GScanContext *context, GBinContent *content, size_t ids_count) +{ + g_clear_object(&context->content); + + context->content = content; + + g_object_ref(G_OBJECT(content)); + + context->match_storages = calloc(ids_count, sizeof(match_area_t *)); + context->storages_count = ids_count; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à consulter. * +* * +* Description : Fournit une référence au contenu principal analysé. * +* * +* Retour : Content binaire associé au context. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinContent *g_scan_context_get_content(const GScanContext *context) +{ + GBinContent *result; /* Instance à retourner */ + + result = context->content; + + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à consulter. * +* * +* Description : Indique si la phase d'analyse de contenu est terminée. * +* * +* Retour : true si la phase de scan est terminée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_scan_context_is_scan_done(const GScanContext *context) +{ + bool result; /* Statut à retourner */ + + result = context->scan_done; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à mettre à jour. * +* * +* Description : Note que la phase d'analyse de contenu est terminée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_scan_context_mark_scan_as_done(GScanContext *context) +{ + context->scan_done = true; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à mettre à jour. * +* id = identifiant du motif trouvé. * +* end = position finale d'une correspondance partielle. * +* * +* Description : Retourne tous les correspondances partielles notées. * +* * +* Retour : Liste interne des localisations conservées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_scan_context_store_atom_match_end(GScanContext *context, patid_t id, phys_t end) +{ + match_area_t *new; /* Nouvel enregistrement */ + + new = g_umem_slice_alloc(context->match_allocator); + + new->end = end + 1; + + add_tail_match_area(new, &context->match_storages[id]); + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à mettre à jour. * +* id = identifiant du motif trouvé. * +* * +* Description : Retourne tous les correspondances partielles notées. * +* * +* Retour : Liste interne des localisations conservées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +match_area_t *g_scan_context_get_atom_matches(const GScanContext *context, patid_t id) +{ + match_area_t *result; /* Liste constituée à renvoyer */ + + result = context->match_storages[id]; + + return result; + +} + + +#ifndef __USE_TABLE_FOR_MATCHES + +/****************************************************************************** +* * +* Paramètres : a = premier lien motif/correspondances à comparer. * +* b = second lien motif/correspondances à comparer. * +* * +* Description : Compare un lien entre motif et correspondances avec un autre.* +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int compare_matched_pattern(const matched_pattern_t *a, const matched_pattern_t *b) +{ + int result; /* Bilan à renvoyer */ + + assert(sizeof(unsigned long) == sizeof(void *)); + + result = sort_unsigned_long((unsigned long)a->pattern, (unsigned long)b->pattern); + + return result; + +} + +#endif + + +/****************************************************************************** +* * +* Paramètres : context = instance à mettre à jour. * +* pattern = definition initiale d'un motif recherché. * +* matches = mémorisation de correspondances établies. * +* * +* Description : Enregistre toutes les correspondances établies pour un motif.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_scan_context_register_full_matches(GScanContext *context, GSearchPattern *pattern, GScanMatches *matches) +{ +#ifndef NDEBUG + GSearchPattern *matches_pattern; /* Clef d'un suivi */ +#endif +#ifndef __USE_TABLE_FOR_MATCHES + matched_pattern_t new; /* Nouvel enregistrement */ +#endif + +#ifndef NDEBUG + + matches_pattern = g_scan_matches_get_source(matches); + + assert(matches_pattern == pattern); + + g_object_unref(G_OBJECT(matches_pattern)); + +#endif + +#ifdef __USE_TABLE_FOR_MATCHES + + assert(!g_hash_table_contains(context->full_trackers, pattern)); + + //g_object_ref(G_OBJECT(pattern)); /* TODO : REMME */ + g_object_ref(G_OBJECT(matches)); + + g_hash_table_insert(context->full_trackers, pattern, matches); + +#else + + new.pattern = pattern; + new.matches = matches; + + g_object_ref(G_OBJECT(matches)); + + context->full_trackers = qinsert_managed(context->full_trackers, &context->full_count, &context->full_allocated, + sizeof(matched_pattern_t), (__compar_fn_t)compare_matched_pattern, + &new); + +#endif + + g_scan_matches_attach(matches, context, pattern); + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à mettre à jour. * +* pattern = motif dont des correspondances sont à retrouver. * +* * +* Description : Fournit la liste de toutes les correspondances pour un motif.* +* * +* Retour : Liste courante de correspondances établies. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GScanMatches *g_scan_context_get_full_matches(const GScanContext *context, const GSearchPattern *pattern) +{ + GScanMatches *result; /* Correspondance à renvoyer */ +#ifndef __USE_TABLE_FOR_MATCHES + matched_pattern_t target; /* Lien ciblé */ + matched_pattern_t *found; /* Lien trouvé */ +#endif + +#ifdef __USE_TABLE_FOR_MATCHES + + result = g_hash_table_lookup(context->full_trackers, pattern); + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + +#else + + target.pattern = pattern; + + found = bsearch(&target, context->full_trackers, context->full_count, + sizeof(matched_pattern_t), (__compar_fn_t)compare_matched_pattern); + + if (found == NULL) + result = NULL; + + else + { + result = found->matches; + g_object_ref(G_OBJECT(result)); + } + +#endif + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = instance à mettre à jour. * +* pattern = motif dont des correspondances sont à retrouver. * +* * +* Description : Dénombre les correspondances associées à un motif. * +* * +* Retour : Quantité de correspondances établies pour un motif entier. * +* * +* Remarques : - * +* * +******************************************************************************/ + +size_t g_scan_context_count_full_matches(const GScanContext *context, const GSearchPattern *pattern) +{ + size_t result; /* Quantité à retourner */ +#ifdef __USE_TABLE_FOR_MATCHES + GScanMatches *matches; /* Ensemble de Correspondances */ +#else + matched_pattern_t target; /* Lien ciblé */ + matched_pattern_t *found; /* Lien trouvé */ +#endif + +#ifdef __USE_TABLE_FOR_MATCHES + + matches = g_hash_table_lookup(context->full_trackers, pattern); + + if (matches != NULL) + result = g_scan_matches_count(matches); + else + result = 0; + +#else + + target.pattern = pattern; + + found = bsearch(&target, context->full_trackers, context->full_count, + sizeof(matched_pattern_t), (__compar_fn_t)compare_matched_pattern); + + if (found == NULL) + result = 0; + else + result = g_scan_matches_count(found->matches); + +#endif + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = mémoire de résultats d'analyse à compléter. * +* name = désignation de la règle ciblée. * +* expr = expression de condition à réduire. * +* * +* Description : Intègre une condition de correspondance pour règle. * +* * +* Retour : Bilan final d'une intégration (false si nom déjà présent). * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_scan_context_set_rule_condition(GScanContext *context, const char *name, GScanExpression *expr) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + rule_condition_t *new; /* Nouvel élément à intégrer */ + + result = true; + + /* Recherche d'antécédent */ + + for (i = 0; i < context->cond_count; i++) + if (strcmp(name, context->conditions[i].name) == 0) + { + result = false; + break; + } + + /* Ajout d'un nouvel élément ? */ + + if (result) + { + context->conditions = realloc(context->conditions, ++context->cond_count * sizeof(rule_condition_t)); + + new = &context->conditions[context->cond_count - 1]; + + new->name = strdup(name); + new->name_hash = fnv_64a_hash(name); + + new->expr = expr; + g_object_ref(G_OBJECT(expr)); + new->final_reduced = false; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = mémoire de résultats d'analyse à consulter. * +* name = désignation de la règle ciblée. * +* * +* Description : Indique si un nom donné correspond à une règle. * +* * +* Retour : Bilan de la présence d'une règle désignée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_scan_context_has_rule_for_name(const GScanContext *context, const char *name) +{ + bool result; /* Bilan à retourner */ + fnv64_t hash; /* Empreinte du nom à retrouver*/ + size_t i; /* Boucle de parcours */ + const rule_condition_t *cond; /* Condition connue du contexte*/ + + result = false; + + hash = fnv_64a_hash(name); + + for (i = 0; i < context->cond_count; i++) + { + cond = context->conditions + i; + + if (cond->name_hash != hash) + continue; + + if (strcmp(cond->name, name) == 0) + { + result = true; + break; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = mémoire de résultats d'analyse à consulter. * +* * +* Description : Indique le bilan des règles globales. * +* * +* Retour : Bilan global des analyses menées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_scan_context_has_global_match(const GScanContext *context) +{ + bool result; /* Bilan global à retourner */ + + result = context->global; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : context = mémoire de résultats d'analyse à actualiser. * +* global = bilan global des analyses menées. * +* * +* Description : Définit le bilan des règles globales. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_scan_context_set_global_match(GScanContext *context, bool global) +{ + context->global = global; + +} + + +/****************************************************************************** +* * +* Paramètres : context = mémoire de résultats d'analyse à consulter. * +* name = désignation de la règle ciblée. * +* * +* Description : Indique si une correspondance globale a pu être établie. * +* * +* Retour : Bilan final d'une analyse (false par défaut). * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_scan_context_has_match_for_rule(GScanContext *context, const char *name) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + rule_condition_t *cond; /* Condition à considérer */ + GScanScope *scope; /* Définition de portées */ + GScanExpression *new; /* Nouvelle expression réduite */ + ScanReductionState state; /* Statut d'une réduction */ + bool valid; /* Validité d'une récupération */ + + result = false; + + if (!context->global) + goto exit; + + /* Recherche de la règle visée */ + + cond = NULL; + + for (i = 0; i < context->cond_count; i++) + if (strcmp(name, context->conditions[i].name) == 0) + { + cond = &context->conditions[i]; + break; + } + + if (cond == NULL) + goto exit; + + /* Tentative de réduction finale */ + + if (!cond->final_reduced) + { + scope = g_scan_scope_new(name); + + state = g_scan_expression_reduce(cond->expr, context, scope, &new); + if (state == SRS_UNRESOLVABLE) goto exit_reduction; + + g_object_unref(G_OBJECT(cond->expr)); + cond->expr = new; + + valid = g_scan_expression_reduce_to_boolean(cond->expr, context, scope, &new); + if (!valid || new == NULL) goto exit_reduction; + + g_object_unref(G_OBJECT(cond->expr)); + cond->expr = new; + + cond->final_reduced = true; + + exit_reduction: + + g_object_unref(G_OBJECT(scope)); + + } + + /* Tentative de récupération d'un bilan final */ + + if (cond->final_reduced) + { + valid = g_scan_literal_expression_get_boolean_value(G_SCAN_LITERAL_EXPRESSION(cond->expr), &result); + + if (!valid) + { + assert(!result); + result = false; + } + + } + + exit: + + return result; + +} |