/* Chrysalide - Outil d'analyse de fichiers binaires * instance.c - spécification d'une instance 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 "instance.h" #include #include #include #include #include "instance-int.h" #include "../expression.h" #include "../records/delayed.h" /* -------------------- CORRESPONDANCE ENTRE INSTANCE ET BINAIRE -------------------- */ /* Initialise la classe des instances de spécification Kaitai. */ static void g_kaitai_instance_class_init(GKaitaiInstanceClass *); /* Initialise une instance de spécification Kaitai. */ static void g_kaitai_instance_init(GKaitaiInstance *); /* Supprime toutes les références externes. */ static void g_kaitai_instance_dispose(GKaitaiInstance *); /* Procède à la libération totale de la mémoire. */ static void g_kaitai_instance_finalize(GKaitaiInstance *); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ /* Parcourt un contenu binaire selon des spécifications Kaitai. */ static bool g_kaitai_instance_parse_content(GKaitaiInstance *, kaitai_scope_t *, GBinContent *, ext_vmpa_t *, GMatchRecord **); /* ---------------------------------------------------------------------------------- */ /* CORRESPONDANCE ENTRE INSTANCE ET BINAIRE */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour une instance de la spécification Kaitai. */ G_DEFINE_TYPE(GKaitaiInstance, g_kaitai_instance, G_TYPE_KAITAI_ATTRIBUTE); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des instances de spécification Kaitai. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_instance_class_init(GKaitaiInstanceClass *klass) { GObjectClass *object; /* Autre version de la classe */ GKaitaiParserClass *parser; /* Ancêtre parent de la classe */ GKaitaiAttributeClass *attrib; /* Version parente de la classe*/ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_instance_dispose; object->finalize = (GObjectFinalizeFunc)g_kaitai_instance_finalize; parser = G_KAITAI_PARSER_CLASS(klass); parser->parse = (parse_kaitai_fc)g_kaitai_instance_parse_content; attrib = G_KAITAI_ATTRIBUTE_CLASS(klass); attrib->get_label = (get_attribute_label_fc)g_kaitai_instance_get_name; } /****************************************************************************** * * * Paramètres : inst = instance à initialiser. * * * * Description : Initialise une instance de spécification Kaitai. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_instance_init(GKaitaiInstance *inst) { inst->name = NULL; inst->io = NULL; inst->pos = NULL; inst->value = NULL; } /****************************************************************************** * * * Paramètres : inst = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_instance_dispose(GKaitaiInstance *inst) { G_OBJECT_CLASS(g_kaitai_instance_parent_class)->dispose(G_OBJECT(inst)); } /****************************************************************************** * * * Paramètres : inst = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_kaitai_instance_finalize(GKaitaiInstance *inst) { if (inst->name != NULL) free(inst->name); if (inst->io != NULL) free(inst->io); if (inst->pos != NULL) free(inst->pos); if (inst->value != NULL) free(inst->value); G_OBJECT_CLASS(g_kaitai_instance_parent_class)->finalize(G_OBJECT(inst)); } /****************************************************************************** * * * Paramètres : parent = noeud Yaml contenant l'instance à constituer. * * * * Description : Construit un lecteur d'instance Kaitai. * * * * Retour : Instance mise en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GKaitaiInstance *g_kaitai_instance_new(GYamlNode *parent) { GKaitaiInstance *result; /* Structure à retourner */ result = g_object_new(G_TYPE_KAITAI_INSTANCE, NULL); if (!g_kaitai_instance_create(result, parent)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : inst = lecteur d'instance Kaitai à initialiser pleinement. * * parent = noeud Yaml contenant l'instance à constituer. * * * * Description : Met en place un lecteur d'instance Kaitai. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_kaitai_instance_create(GKaitaiInstance *inst, GYamlNode *parent) { bool result; /* Bilan à retourner */ const char *name; /* Désignation du type */ char *sub_path; /* Chemin d'accès suivant */ GYamlNode *sub; /* Contenu Yaml d'un type */ GYamlNode *node; /* Noeud particulier présent */ const char *value; /* Valeur Yaml particulière */ result = false; /* Extraction du nom */ if (!G_IS_YAML_PAIR(parent)) goto exit; name = g_yaml_pair_get_key(G_YAML_PAIR(parent)); inst->name = strdup(name); /* Extraction des bases du type */ asprintf(&sub_path, "/%s/", name); sub = g_yaml_node_find_first_by_path(parent, sub_path); free(sub_path); if (sub == NULL) goto exit; result = g_kaitai_attribute_create(G_KAITAI_ATTRIBUTE(inst), sub, false); /* Eventuel contenu imposé */ node = g_yaml_node_find_first_by_path(sub, "/io"); if (node != NULL) { assert(G_IS_YAML_PAIR(node)); value = g_yaml_pair_get_value(G_YAML_PAIR(node)); if (value == NULL) { g_object_unref(G_OBJECT(node)); goto bad_loading; } inst->io = strdup(value); g_object_unref(G_OBJECT(node)); } /* Eventuelle positiion imposée */ node = g_yaml_node_find_first_by_path(sub, "/pos"); if (node != NULL) { assert(G_IS_YAML_PAIR(node)); value = g_yaml_pair_get_value(G_YAML_PAIR(node)); if (value == NULL) { g_object_unref(G_OBJECT(node)); goto bad_loading; } inst->pos = strdup(value); g_object_unref(G_OBJECT(node)); } /* Eventuelle formule de calcul d'une valeur */ node = g_yaml_node_find_first_by_path(sub, "/value"); if (node != NULL) { assert(G_IS_YAML_PAIR(node)); inst->value = g_yaml_pair_aggregate_value(G_YAML_PAIR(node)); g_object_unref(G_OBJECT(node)); if (inst->value == NULL) goto bad_loading; } bad_loading: g_object_unref(G_OBJECT(sub)); exit: if (result) result = (inst->pos != NULL || inst->value != NULL); return result; } /****************************************************************************** * * * Paramètres : inst = lecteur d'instance Kaitai à consulter. * * * * Description : Indique le nom attribué à une instance Kaitai. * * * * Retour : Désignation pointant l'instance. * * * * Remarques : - * * * ******************************************************************************/ const char *g_kaitai_instance_get_name(const GKaitaiInstance *inst) { char *result; /* Valeur à renvoyer */ result = inst->name; return result; } /****************************************************************************** * * * Paramètres : inst = lecteur d'instance Kaitai à consulter. * * locals = variables locales pour les résolutions de types. * * content = contenu binaire lié à la correspondance. * * * * Description : Détermine la valeur effective d'un élément Kaitai dynamique. * * * * Retour : valeur à sauvegarder sous une forme générique. * * * * Remarques : - * * * ******************************************************************************/ GMatchRecord *g_kaitai_instance_compute_real_record(const GKaitaiInstance *inst, const kaitai_scope_t *locals, GBinContent *content) { GMatchRecord *result; /* Enregistrement à retourner */ GBinContent *work_area; /* Aire de travail */ GKaitaiStream *stream; /* Flux de données pour Kaitai */ bool status; /* Bilan intermédiaire */ vmpa2t forced_pos; /* Tete de lecture constituée */ resolved_value_t offset; /* Position à adopter */ GKaitaiParserClass *class; /* Classe parente à solliciter */ ext_vmpa_t epos; /* Tête de lecture complète */ result = NULL; if (inst->value == NULL) { /* Contenu particulier */ if (inst->io == NULL) work_area = content; else { status = resolve_kaitai_expression_as_stream(locals, inst->io, strlen(inst->io), &stream); if (!status) goto exit; work_area = g_kaitai_stream_get_content(stream); g_object_unref(G_OBJECT(stream)); } /* Tête de lecture */ g_binary_content_compute_start_pos(work_area, &forced_pos); status = resolve_kaitai_expression_as_integer(locals, inst->pos, strlen(inst->pos), &offset); if (!status) goto exit_with_content; if (offset.type == GVT_UNSIGNED_INTEGER) advance_vmpa(&forced_pos, offset.unsigned_integer); else { assert(offset.type == GVT_SIGNED_INTEGER); if (offset.signed_integer < 0) { status = false; goto exit_with_content; } advance_vmpa(&forced_pos, offset.signed_integer); } /* Lecture */ class = G_KAITAI_PARSER_CLASS(g_kaitai_instance_parent_class); init_evmpa_from_vmpa(&epos, &forced_pos); class->parse(G_KAITAI_PARSER(inst), locals, work_area, &epos, &result); exit_with_content: if (work_area != content) g_object_unref(G_OBJECT(work_area)); } exit: return result; } /****************************************************************************** * * * Paramètres : inst = lecteur d'instance Kaitai à consulter. * * locals = variables locales pour les résolutions de types. * * value = valeur à sauvegarder sous une forme générique. [OUT]* * * * Description : Détermine la valeur d'un élément Kaitai entier calculé. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_kaitai_instance_compute_value(const GKaitaiInstance *inst, const kaitai_scope_t *locals, resolved_value_t *value) { bool result; /* Bilan à retourner */ if (inst->value == NULL) result = false; else result = resolve_kaitai_expression_as_any(locals, inst->value, strlen(inst->value), value); return result; } /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : inst = 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_instance_parse_content(GKaitaiInstance *inst, kaitai_scope_t *locals, GBinContent *content, ext_vmpa_t *epos, GMatchRecord **record) { bool result; /* Bilan à retourner */ *record = G_MATCH_RECORD(g_record_delayed_new(inst, locals, inst->value == NULL ? content : NULL)); result = (*record != NULL); return result; }