summaryrefslogtreecommitdiff
path: root/src/analysis/scan/context.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/analysis/scan/context.c')
-rw-r--r--src/analysis/scan/context.c841
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;
+
+}