/* Chrysalide - Outil d'analyse de fichiers binaires * struct.c - définition d'une structure Kaitai * * Copyright (C) 2019 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 "struct.h" #include #include #include #include #include "struct-int.h" #include "../import.h" #include "../parser.h" #include "../records/empty.h" #include "../records/group.h" /* ---------------------- LECTURE D'UNE TRANCHE DE DEFINITIONS ---------------------- */ /* Initialise la classe des structuts de spécification Kaitai. */ static void g_kaitai_structure_class_init(GKaitaiStructClass *); /* Initialise un structut de spécification Kaitai. */ static void g_kaitai_structure_init(GKaitaiStruct *); /* Supprime toutes les références externes. */ static void g_kaitai_structure_dispose(GKaitaiStruct *); /* Procède à la libération totale de la mémoire. */ static void g_kaitai_structure_finalize(GKaitaiStruct *); /* Charge les éventuelles dépendances de la définition. */ static bool g_kaitai_structure_load_imports(GKaitaiStruct *); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ /* Parcourt un contenu binaire selon des spécifications Kaitai. */ static bool g_kaitai_structure_parse_content(GKaitaiStruct *, kaitai_scope_t *, GBinContent *, ext_vmpa_t *, GMatchRecord **); /* ---------------------------------------------------------------------------------- */ /* LECTURE D'UNE TRANCHE DE DEFINITIONS */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un structut de la spécification Kaitai. */ G_DEFINE_TYPE(GKaitaiStruct, g_kaitai_structure, G_TYPE_KAITAI_PARSER); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des structuts de spécification Kaitai. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_structure_class_init(GKaitaiStructClass *klass) { GObjectClass *object; /* Autre version de la classe */ GKaitaiParserClass *parser; /* Version parente de la classe*/ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_structure_dispose; object->finalize = (GObjectFinalizeFunc)g_kaitai_structure_finalize; parser = G_KAITAI_PARSER_CLASS(klass); parser->parse = (parse_kaitai_fc)g_kaitai_structure_parse_content; } /****************************************************************************** * * * Paramètres : kstruct = instance à initialiser. * * * * Description : Initialise un structure de spécification Kaitai. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_structure_init(GKaitaiStruct *kstruct) { kstruct->filename = NULL; kstruct->meta = NULL; kstruct->seq_items = NULL; kstruct->seq_items_count = 0; kstruct->types = NULL; kstruct->types_count = 0; kstruct->instances = NULL; kstruct->instances_count = 0; kstruct->enums = NULL; kstruct->enums_count = 0; } /****************************************************************************** * * * Paramètres : kstruct = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_structure_dispose(GKaitaiStruct *kstruct) { size_t i; /* Boucle de parcours */ g_clear_object(&kstruct->meta); for (i = 0; i < kstruct->seq_items_count; i++) g_clear_object(&kstruct->seq_items[i]); for (i = 0; i < kstruct->types_count; i++) g_clear_object(&kstruct->types[i]); for (i = 0; i < kstruct->instances_count; i++) g_clear_object(&kstruct->instances[i]); for (i = 0; i < kstruct->enums_count; i++) g_clear_object(&kstruct->enums[i]); G_OBJECT_CLASS(g_kaitai_structure_parent_class)->dispose(G_OBJECT(kstruct)); } /****************************************************************************** * * * Paramètres : kstruct = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_structure_finalize(GKaitaiStruct *kstruct) { if (kstruct->filename != NULL) free(kstruct->filename); if (kstruct->seq_items != NULL) free(kstruct->seq_items); if (kstruct->types != NULL) free(kstruct->types); if (kstruct->instances != NULL) free(kstruct->instances); if (kstruct->enums != NULL) free(kstruct->enums); G_OBJECT_CLASS(g_kaitai_structure_parent_class)->finalize(G_OBJECT(kstruct)); } /****************************************************************************** * * * Paramètres : text = définitions textuelles d'un contenu brut. * * * * Description : Crée un nouvel interpréteur de structure Kaitai. * * * * Retour : Instance mise en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiStruct *g_kaitai_structure_new_from_text(const char *text) { GKaitaiStruct *result; /* Structure à retourner */ result = g_object_new(G_TYPE_KAITAI_STRUCT, NULL); if (!g_kaitai_structure_create_from_text(result, text)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : kstruct = lecteur de définition à initialiser pleinement. * * text = définitions textuelles d'un contenu brut. * * * * Description : Met en place un interpréteur de définitions Kaitai. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_kaitai_structure_create_from_text(GKaitaiStruct *kstruct, const char *text) { bool result; /* Bilan à retourner */ GYamlNode *root; /* Noeud racine YAML */ root = parse_yaml_from_text(text, strlen(text)); if (root != NULL) { result = g_kaitai_structure_create(kstruct, root); g_object_unref(G_OBJECT(root)); } else { fprintf(stderr, "The provided YAML content seems invalid"); result = false; } return result; } /****************************************************************************** * * * Paramètres : filename = chemin vers des définitions de règles. * * * * Description : Crée un nouvel interpréteur de structure Kaitai. * * * * Retour : Instance mise en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiStruct *g_kaitai_structure_new_from_file(const char *filename) { GKaitaiStruct *result; /* Structure à retourner */ result = g_object_new(G_TYPE_KAITAI_STRUCT, NULL); if (!g_kaitai_structure_create_from_file(result, filename)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : kstruct = lecteur de définition à initialiser pleinement. * * filename = chemin vers des définitions de règles. * * * * Description : Met en place un interpréteur de définitions Kaitai. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_kaitai_structure_create_from_file(GKaitaiStruct *kstruct, const char *filename) { bool result; /* Bilan à retourner */ GYamlNode *root; /* Noeud racine YAML */ kstruct->filename = strdup(filename); root = parse_yaml_from_file(filename); if (root != NULL) { result = g_kaitai_structure_create(kstruct, root); g_object_unref(G_OBJECT(root)); } else { fprintf(stderr, "The provided YAML content seems invalid"); result = false; } return result; } /****************************************************************************** * * * Paramètres : kstruct = lecteur de définition à initialiser pleinement. * * parent = noeud Yaml contenant l'attribut à constituer. * * * * Description : Met en place un lecteur de définitions Kaitai. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_kaitai_structure_create(GKaitaiStruct *kstruct, GYamlNode *parent) { bool result; /* Bilan à retourner */ GYamlNode *collec; /* Liste de noeuds à traiter */ GYamlNode **nodes; /* Eventuels noeuds trouvés */ size_t count; /* Quantité de ces noeuds */ size_t i; /* Boucle de parcours */ size_t first; /* Premier emplacement dispo. */ bool failed; /* Détection d'un échec */ result = false; /* Informations générales */ kstruct->meta = g_kaitai_meta_new(parent); assert(kstruct->meta != NULL); result = g_kaitai_structure_load_imports(kstruct); if (!result) goto bad_loading; /* Séquence */ collec = g_yaml_node_find_first_by_path(parent, "/seq/"); if (collec != NULL) { if (G_IS_YAML_COLLEC(collec)) nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count); else count = 0; if (count > 0) { kstruct->seq_items = calloc(count, sizeof(GKaitaiAttribute *)); kstruct->seq_items_count = count; for (i = 0; i < count; i++) { kstruct->seq_items[i] = g_kaitai_attribute_new(nodes[i]); if (kstruct->seq_items[i] == NULL) break; g_object_unref(G_OBJECT(nodes[i])); } failed = (i < count); for (; i < count; i++) g_object_unref(G_OBJECT(nodes[i])); free(nodes); if (failed) goto bad_loading; } g_object_unref(G_OBJECT(collec)); } /* Types particuliers éventuels */ collec = g_yaml_node_find_first_by_path(parent, "/types/"); if (collec != NULL) { if (G_IS_YAML_COLLEC(collec)) nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count); else count = 0; if (count > 0) { first = kstruct->types_count; kstruct->types_count += count; kstruct->types = realloc(kstruct->types, kstruct->types_count * sizeof(GKaitaiType *)); for (i = 0; i < count; i++) { kstruct->types[first + i] = g_kaitai_type_new(nodes[i]); if (kstruct->types[first + i] == NULL) break; g_object_unref(G_OBJECT(nodes[i])); } failed = (i < count); for (; i < count; i++) g_object_unref(G_OBJECT(nodes[i])); free(nodes); if (failed) goto bad_loading; } g_object_unref(G_OBJECT(collec)); } /* Instances éventuelles */ collec = g_yaml_node_find_first_by_path(parent, "/instances/"); if (collec != NULL) { if (G_IS_YAML_COLLEC(collec)) nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count); else count = 0; if (count > 0) { kstruct->instances = calloc(count, sizeof(GKaitaiInstance *)); kstruct->instances_count = count; for (i = 0; i < count; i++) { kstruct->instances[i] = g_kaitai_instance_new(nodes[i]); if (kstruct->instances[i] == NULL) break; g_object_unref(G_OBJECT(nodes[i])); } failed = (i < count); for (; i < count; i++) g_object_unref(G_OBJECT(nodes[i])); free(nodes); if (failed) goto bad_loading; } g_object_unref(G_OBJECT(collec)); } /* Enumérations éventuelles */ collec = g_yaml_node_find_first_by_path(parent, "/enums/"); if (collec != NULL) { if (G_IS_YAML_COLLEC(collec)) nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count); else count = 0; if (count > 0) { kstruct->enums = calloc(count, sizeof(GKaitaiEnum *)); kstruct->enums_count = count; for (i = 0; i < count; i++) { kstruct->enums[i] = g_kaitai_enum_new(nodes[i]); if (kstruct->enums[i] == NULL) break; g_object_unref(G_OBJECT(nodes[i])); } failed = (i < count); for (; i < count; i++) g_object_unref(G_OBJECT(nodes[i])); free(nodes); if (failed) goto bad_loading; } g_object_unref(G_OBJECT(collec)); } /* Sortie heureuse */ result = true; bad_loading: return result; } /****************************************************************************** * * * Paramètres : kstruct = lecteur de définition à initialiser pleinement. * * * * Description : Charge les éventuelles dépendances de la définition. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_kaitai_structure_load_imports(GKaitaiStruct *kstruct) { bool result; /* Bilan d'opération à renvoyer*/ const char * const *dependencies; /* Liste d'imports requis */ size_t count; /* Quantité de ces imports */ size_t i; /* Boucle de parcours */ GKaitaiType *imported; /* Structure importée */ result = true; dependencies = g_kaitai_meta_get_dependencies(kstruct->meta, &count); for (i = 0; i < count; i++) { imported = import_kaitai_definition(dependencies[i], kstruct->filename); if (imported == NULL) break; kstruct->types_count++; kstruct->types = realloc(kstruct->types, kstruct->types_count * sizeof(GKaitaiType *)); kstruct->types[kstruct->types_count - 1] = imported; } result = (i == count); return result; } /****************************************************************************** * * * Paramètres : kstruct = structure Kaitai à consulter. * * * * Description : Fournit la description globale d'une définition Kaitai. * * * * Retour : Description de la définition Kaitai courante. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiMeta *g_kaitai_structure_get_meta(const GKaitaiStruct *kstruct) { GKaitaiMeta *result; /* Informations à retourner */ result = kstruct->meta; if (result != NULL) g_object_ref(G_OBJECT(result)); return result; } /****************************************************************************** * * * Paramètres : kstruct = structure Kaitai en cours de parcours. * * name = désignation principale des énumérations ciblées. * * * * Description : Fournit un ensemble d'énumérations locales de la structure. * * * * Retour : Enumérations locales ou NULL si non trouvée. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiEnum *g_kaitai_structure_get_enum(const GKaitaiStruct *kstruct, const sized_string_t *name) { GKaitaiEnum *result; /* Instance à retourner */ size_t i; /* Boucle de parcours */ const char *other; /* Autre désignation à comparer*/ result = NULL; for (i = 0; i < kstruct->enums_count; i++) { other = g_kaitai_enum_get_name(kstruct->enums[i]); if (strncmp(name->data, other, name->len) == 0) // FIXME { result = kstruct->enums[i]; g_object_ref(G_OBJECT(result)); break; } } return result; } /****************************************************************************** * * * Paramètres : kstruct = structure Kaitai en cours de parcours. * * name = désignation du type particulier ciblé. * * * * Description : Recherche la définition d'un type nouveau pour Kaitai. * * * * Retour : Type prêt à emploi ou NULL si non trouvé. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiType *g_kaitai_structure_find_sub_type(const GKaitaiStruct *kstruct, const char *name) { GKaitaiType *result; /* Instance à retourner */ size_t i; /* Boucle de parcours */ const char *other; /* Autre désignation à comparer*/ result = NULL; for (i = 0; i < kstruct->types_count; i++) { other = g_kaitai_type_get_name(kstruct->types[i]); if (strcmp(name, other) == 0) { result = kstruct->types[i]; g_object_ref(G_OBJECT(result)); break; } result = g_kaitai_structure_find_sub_type(G_KAITAI_STRUCT(kstruct->types[i]), name); if (result != NULL) break; } return result; } /****************************************************************************** * * * Paramètres : kstruct = structure Kaitai en cours de parcours. * * content = contenu binaire en cours de traitement. * * * * Description : Parcourt un contenu binaire selon une description Kaitai. * * * * Retour : Arborescence d'éléments rencontrés selon les spécifications. * * * * Remarques : - * * * ******************************************************************************/ GMatchRecord *g_kaitai_structure_parse(GKaitaiStruct *kstruct, GBinContent *content) { GMatchRecord *result; /* Arborescence à retourner */ vmpa2t pos; /* Tête de lecture */ kaitai_scope_t locals; /* Variables locales */ ext_vmpa_t epos; /* Tête de lecture complète */ g_binary_content_compute_start_pos(content, &pos); init_record_scope(&locals, kstruct->meta); init_evmpa_from_vmpa(&epos, &pos); g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct), &locals, content, &epos, &result); return result; } /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : kstruct = structure Kaitai en cours de parcours. * * locals = variables locales pour les résolutions de types. * * content = données binaires à analyser et traduire. * * epos = tête de lecture courante. [OUT] * * record = noeud d'arborescence d'éléments rencontrés. [OUT] * * * * Description : Parcourt un contenu binaire selon des spécifications Kaitai. * * * * Retour : Bilan de l'opératon : true pour continuer, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool g_kaitai_structure_parse_content(GKaitaiStruct *kstruct, kaitai_scope_t *locals, GBinContent *content, ext_vmpa_t *epos, GMatchRecord **record) { bool result; /* Bilan à retourner */ GRecordGroup *group; /* Ensemble à constituer */ GMatchRecord *old; /* Sauvegarde de valeur */ size_t i; /* Boucle de parcours */ GMatchRecord *child; /* Nouvel élément mis en place */ result = true; /* Si le groupe est vide */ if ((kstruct->seq_items_count + kstruct->instances_count) == 0) { *record = G_MATCH_RECORD(g_record_empty_new(G_KAITAI_PARSER(kstruct), content, &epos->base)); if (locals->root == NULL) locals->root = *record; } /* Sinon on construit selon les définitions fournies */ else { group = g_record_group_new(kstruct, content); *record = G_MATCH_RECORD(group); if (locals->root == NULL) locals->root = *record; old = locals->parent; locals->parent = *record; /** * Les instances sont à charger avant les éléments fixes car * des références au premières peuvent être attendues dans ces derniers. * * Les évolutions de la tête de lecture n'ont en revanche normalement * pas d'incidence sur le chargement des éléments fixes. */ for (i = 0; i < kstruct->instances_count; i++) { result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->instances[i]), locals, content, epos, &child); if (!result) goto exit; if (child != NULL) { g_record_group_add_record(group, child); g_object_unref(G_OBJECT(child)); } } /** * Seconde phase. */ locals->parent = *record; for (i = 0; i < kstruct->seq_items_count; i++) { result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->seq_items[i]), locals, content, epos, &child); if (!result) goto exit; if (child != NULL) { g_record_group_add_record(group, child); g_object_unref(G_OBJECT(child)); } } exit: locals->parent = old; if (!result) g_clear_object(record); } return result; }