/* 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 . */ #include "context.h" #include #include #include #include #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; }