/* Chrysalide - Outil d'analyse de fichiers binaires * browser.c - accès à des définitions Kaitai depuis ROST * * 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 Chrysalide. If not, see . */ #include "browser.h" #include #include #include #include "browser-int.h" #include "../records/bits.h" #include "../records/delayed.h" #include "../records/item.h" #include "../records/list.h" /* ---------------------- PARCOURS DE CORRESPONDANCES ETABLIES ---------------------- */ /* Initialise la classe des parcours de correspondances Kaitai. */ static void g_kaitai_browser_class_init(GKaitaiBrowserClass *); /* Initialise un parcours de correspondances Kaitai. */ static void g_kaitai_browser_init(GKaitaiBrowser *); /* Supprime toutes les références externes. */ static void g_kaitai_browser_dispose(GKaitaiBrowser *); /* Procède à la libération totale de la mémoire. */ static void g_kaitai_browser_finalize(GKaitaiBrowser *); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ /* Indique le nom associé à une expression d'évaluation. */ static char *g_kaitai_browser_get_name(const GKaitaiBrowser *); /* Lance une résolution d'élément à solliciter. */ static bool g_kaitai_browser_resolve(GKaitaiBrowser *, const char *, GScanContext *, GScanScope *, GScanRegisteredItem **); /* Réduit une expression à une forme plus simple. */ static bool g_kaitai_browser_reduce(GKaitaiBrowser *, GScanContext *, GScanScope *, GScanExpression **); /* Effectue une extraction d'élément à partir d'une série. */ static GObject *g_kaitai_browser_extract_at(GKaitaiBrowser *, const GScanExpression *, GScanContext *, GScanScope *); /* ---------------------------------------------------------------------------------- */ /* PARCOURS DE CORRESPONDANCES ETABLIES */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un parcours de correspondances Kaitai pour ROST. */ G_DEFINE_TYPE(GKaitaiBrowser, g_kaitai_browser, G_TYPE_SCAN_REGISTERED_ITEM); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des parcours de correspondances Kaitai. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_browser_class_init(GKaitaiBrowserClass *klass) { GObjectClass *object; /* Autre version de la classe */ GScanRegisteredItemClass *registered; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_browser_dispose; object->finalize = (GObjectFinalizeFunc)g_kaitai_browser_finalize; registered = G_SCAN_REGISTERED_ITEM_CLASS(klass); registered->get_name = (get_registered_item_name_fc)g_kaitai_browser_get_name; registered->resolve = (resolve_registered_item_fc)g_kaitai_browser_resolve; registered->reduce = (reduce_registered_item_fc)g_kaitai_browser_reduce; registered->extract = (extract_registered_item_at)g_kaitai_browser_extract_at; } /****************************************************************************** * * * Paramètres : browser = instance à initialiser. * * * * Description : Initialise un parcours de correspondances Kaitai. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_browser_init(GKaitaiBrowser *browser) { browser->path = NULL; browser->record = NULL; } /****************************************************************************** * * * Paramètres : browser = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_browser_dispose(GKaitaiBrowser *browser) { g_clear_object(&browser->record); G_OBJECT_CLASS(g_kaitai_browser_parent_class)->dispose(G_OBJECT(browser)); } /****************************************************************************** * * * Paramètres : browser = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_browser_finalize(GKaitaiBrowser *browser) { if (browser->path != NULL) free(browser->path); G_OBJECT_CLASS(g_kaitai_browser_parent_class)->finalize(G_OBJECT(browser)); } /****************************************************************************** * * * Paramètres : path = chemin vers l'enregistrement fourni. * * record = correspondance racine à considérer. * * * * Description : Crée un nouveau parcours de correspondances Kaitai. * * * * Retour : Instance mise en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiBrowser *g_kaitai_browser_new(const char *path, GMatchRecord *record) { GKaitaiBrowser *result; /* Structure à retourner */ result = g_object_new(G_TYPE_KAITAI_BROWSER, NULL); if (!g_kaitai_browser_create(result, path, record)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : browser = encadrement d'un parcours de correspondances. * * path = chemin vers l'enregistrement fourni. * * record = correspondance racine à considérer. * * * * Description : Met en place un nouveau parcours de correspondances Kaitai. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_kaitai_browser_create(GKaitaiBrowser *browser, const char *path, GMatchRecord *record) { bool result; /* Bilan à retourner */ result = true; if (path != NULL) browser->path = strdup(path); browser->record = record; g_object_ref(G_OBJECT(browser->record)); return result; } /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : item = élément d'appel à consulter. * * * * Description : Indique le nom associé à une expression d'évaluation. * * * * Retour : Désignation humaine de l'expression d'évaluation. * * * * Remarques : - * * * ******************************************************************************/ static char *g_kaitai_browser_get_name(const GKaitaiBrowser *item) { char *result; /* Désignation à retourner */ int ret; /* Statut de construction */ if (item->path == NULL) result = strdup("kaitai://"); else { ret = asprintf(&result, "kaitai://%s", item->path); assert(ret > 0); if (ret <= 0) result = strdup("kaitai://???"); } return result; } /****************************************************************************** * * * Paramètres : item = élément d'appel à consulter. * * target = désignation de l'objet d'appel à identifier. * * ctx = contexte de suivi de l'analyse courante. * * scope = portée courante des variables locales. * * out = zone d'enregistrement de la résolution opérée. [OUT]* * * * Description : Lance une résolution d'élément à solliciter. * * * * Retour : Bilan de l'opération : false en cas d'erreur irrécupérable. * * * * Remarques : - * * * ******************************************************************************/ static bool g_kaitai_browser_resolve(GKaitaiBrowser *item, const char *target, GScanContext *ctx, GScanScope *scope, GScanRegisteredItem **out) { bool result; /* Bilan à retourner */ GMatchRecord *found; /* Correspondance trouvée */ char *path; int ret; /* Statut de construction */ found = g_match_record_find_by_name(item->record, target, strlen(target), 1); result = (found != NULL); if (result) { ret = asprintf(&path, "%s.%s", item->path, target); assert(ret > 0); if (ret <= 0) path = strdup("!?"); *out = G_SCAN_REGISTERED_ITEM(g_kaitai_browser_new(path, found)); free(path); g_object_unref(G_OBJECT(found)); } return result; } /****************************************************************************** * * * Paramètres : item = élément d'appel à consulter. * * ctx = contexte de suivi de l'analyse courante. * * scope = portée courante des variables locales. * * out = zone d'enregistrement de la réduction opérée. [OUT] * * * * Description : Réduit une expression à une forme plus simple. * * * * Retour : Bilan de l'opération : false en cas d'erreur irrécupérable. * * * * Remarques : - * * * ******************************************************************************/ static bool g_kaitai_browser_reduce(GKaitaiBrowser *item, GScanContext *ctx, GScanScope *scope, GScanExpression **out) { bool result; /* Bilan à retourner */ size_t count; /* Décompte total à considérer */ resolved_value_t value; /* Valeur brute à transformer */ if (G_IS_RECORD_LIST(item->record)) { count = g_record_list_count_records(G_RECORD_LIST(item->record)); *out = g_scan_literal_expression_new(LVT_BOOLEAN, (bool []){ count > 0 }); result = true; } else { if (G_IS_RECORD_BIT_FIELD(item->record)) result = g_record_bit_field_get_value(G_RECORD_BIT_FIELD(item->record), &value); else if (G_IS_RECORD_DELAYED(item->record)) result = g_record_delayed_compute_and_aggregate_value(G_RECORD_DELAYED(item->record), &value); else if (G_IS_RECORD_ITEM(item->record)) result = g_record_item_get_value(G_RECORD_ITEM(item->record), &value); else result = false; if (result) { switch (value.type) { case GVT_UNSIGNED_INTEGER: *out = g_scan_literal_expression_new(LVT_UNSIGNED_INTEGER, &value.unsigned_integer); break; case GVT_SIGNED_INTEGER: *out = g_scan_literal_expression_new(LVT_SIGNED_INTEGER, &value.signed_integer); break; case GVT_FLOAT: /* TODO */ break; case GVT_BOOLEAN: *out = g_scan_literal_expression_new(LVT_BOOLEAN, &value.status); break; case GVT_BYTES: *out = g_scan_literal_expression_new(LVT_STRING, &value.bytes); break; default: break; } EXIT_RESOLVED_VALUE(value); } } return result; } /****************************************************************************** * * * Paramètres : item = élément d'appel à consulter. * * index = indice de l'élément à cibler. * * ctx = contexte de suivi de l'analyse courante. * * scope = portée courante des variables locales. * * * * Description : Effectue une extraction d'élément à partir d'une série. * * * * Retour : Elément de série obtenu ou NULL si erreur irrécupérable. * * * * Remarques : - * * * ******************************************************************************/ static GObject *g_kaitai_browser_extract_at(GKaitaiBrowser *item, const GScanExpression *index, GScanContext *ctx, GScanScope *scope) { GObject *result; /* Elément récupéré à renvoyer */ GScanLiteralExpression *literal; /* Accès direct à l'indice */ LiteralValueType vtype; /* Type de valeur portée */ unsigned long long at; /* Valeur concrète du point */ bool status; /* Bilan d'obtention d'indice */ GRecordList *list; /* Accès direct à la liste */ size_t count; /* Décompte total à considérer */ GMatchRecord *found; /* Correspondance trouvée */ char *path; int ret; /* Statut de construction */ result = NULL; /* Validations préliminaires */ if (!G_IS_RECORD_LIST(item->record)) goto exit; if (!G_IS_SCAN_LITERAL_EXPRESSION(index)) goto exit; literal = G_SCAN_LITERAL_EXPRESSION(index); vtype = g_scan_literal_expression_get_value_type(literal); if (vtype != LVT_UNSIGNED_INTEGER) goto exit; status = g_scan_literal_expression_get_unsigned_integer_value(literal, &at); if (!status) goto exit; list = G_RECORD_LIST(item->record); count = g_record_list_count_records(list); if (at >= count) goto exit; /* Récupération de l'élément visé */ found = g_record_list_get_record(list, at); if (found == NULL) goto exit; ret = asprintf(&path, "%s[%llu]", item->path, at); assert(ret > 0); if (ret <= 0) path = strdup("!?"); result = G_OBJECT(g_kaitai_browser_new(path, found)); free(path); g_object_unref(G_OBJECT(found)); exit: return result; }