diff options
Diffstat (limited to 'plugins/kaitai/parsers')
22 files changed, 6461 insertions, 0 deletions
diff --git a/plugins/kaitai/parsers/Makefile.am b/plugins/kaitai/parsers/Makefile.am new file mode 100644 index 0000000..c7e313b --- /dev/null +++ b/plugins/kaitai/parsers/Makefile.am @@ -0,0 +1,25 @@ + +noinst_LTLIBRARIES = libkaitaiparsers.la + +libkaitaiparsers_la_SOURCES = \ + attribute-int.h \ + attribute.h attribute.c \ + enum-int.h \ + enum.h enum.c \ + instance-int.h \ + instance.h instance.c \ + meta-int.h \ + meta.h meta.c \ + struct-int.h \ + struct.h struct.c \ + switch-int.h \ + switch.h switch.c \ + type-int.h \ + type.h type.c + +libkaitaiparsers_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libkaitaiparsers_la_SOURCES:%c=) diff --git a/plugins/kaitai/parsers/attribute-int.h b/plugins/kaitai/parsers/attribute-int.h new file mode 100644 index 0000000..ef64089 --- /dev/null +++ b/plugins/kaitai/parsers/attribute-int.h @@ -0,0 +1,104 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * attribute-int.h - prototypes pour les spécifications internes d'un attribut 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_INT_H +#define _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_INT_H + + +#include "attribute.h" +#include "switch.h" +#include "../parser-int.h" + + + +/* Indique l'étiquette à utiliser pour identifier un attribut. */ +typedef const char * (* get_attribute_label_fc) (const GKaitaiAttribute *); + +/* Spécification d'un attribut Kaitai (instance) */ +struct _GKaitaiAttribute +{ + GKaitaiParser parent; /* A laisser en premier */ + + char *raw_id; /* Identifiant Kaitai */ + char *orig_id; /* Identifiant humain */ + + char *doc; /* Eventuelle documentation */ + + KaitaiAttributePayload payload; /* Forme de la spécialisation */ + + struct + { + /* KAP_FIXED_CONTENT */ + sized_string_t fixed_content; /* Données brutes attendues */ + + /* KAP_BASIC_TYPE */ + struct + { + BaseType basic; /* Type de base */ + + bool is_string; /* Renvoi vers une chaîne */ + + SourceEndian endian; /* Boutisme forcé ? */ + bool has_endian; /* Présence de cette force */ + + }; + + /* KAP_USER_TYPE */ + char *named_type; /* Type particulier */ + + /* KAP_DYNAMIC_TYPE */ + GKaitaiSwitch *switchon; /* Détermination dynamique */ + + }; + + /* KAP_SIZED */ + char *fixed_size; /* Taille déterminée */ + + KaitaiAttributeRepetition repetition; /* Forme de répétition */ + char *repeat_controller; /* Indication sur la répétition*/ + + char *condition; /* Condition de chargement */ + + sized_string_t terminator; /* Marqueur de fin éventuel */ + bool consume; /* Consommation dans le flux */ + bool include; /* Intégration de ce marqueur */ + bool eos_error; /* Gestion des erreurs en bout */ + +}; + +/* Spécification d'un attribut Kaitai (classe) */ +struct _GKaitaiAttributeClass +{ + GKaitaiParserClass parent; /* A laisser en premier */ + + get_attribute_label_fc get_label; /* Désignation d'une étiquette */ + +}; + + +/* Met en place un lecteur d'attribut Kaitai. */ +bool g_kaitai_attribute_create(GKaitaiAttribute *, GYamlNode *, bool); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_INT_H */ diff --git a/plugins/kaitai/parsers/attribute.c b/plugins/kaitai/parsers/attribute.c new file mode 100644 index 0000000..c61fe99 --- /dev/null +++ b/plugins/kaitai/parsers/attribute.c @@ -0,0 +1,2074 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * attribute.c - spécification d'un attribut 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 <http://www.gnu.org/licenses/>. + */ + + +#include "attribute.h" + + +#include <assert.h> +#include <string.h> + + +#include <analysis/contents/restricted.h> +#include <plugins/yaml/pair.h> + + +#include "attribute-int.h" +#include "../expression.h" +#include "../scope.h" +#include "../records/empty.h" +#include "../records/item.h" +#include "../records/list.h" + + + +/* -------------------- CORRESPONDANCE ENTRE ATTRIBUT ET BINAIRE -------------------- */ + + +/* Initialise la classe des attributs de spécification Kaitai. */ +static void g_kaitai_attribute_class_init(GKaitaiAttributeClass *); + +/* Initialise un attribut de spécification Kaitai. */ +static void g_kaitai_attribute_init(GKaitaiAttribute *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_attribute_dispose(GKaitaiAttribute *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_attribute_finalize(GKaitaiAttribute *); + +/* Traduit en type concret une chaîne de caractères. */ +static bool g_kaitai_attribute_resolve_type(GKaitaiAttribute *, const char *); + +/* Valide la cohérence des informations portées par l'attribut. */ +static bool g_kaitai_attribute_check(const GKaitaiAttribute *); + +/* Copie le coeur de la définition d'un lecteur d'attribut. */ +static GKaitaiAttribute *g_kaitai_attribute_dup_for(const GKaitaiAttribute *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Parcourt un contenu binaire selon des spécifications Kaitai. */ +static bool _g_kaitai_attribute_parse_content(GKaitaiAttribute *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **); + +/* Extrait d'un contenu une série d'octets avec terminaison. */ +static bool g_kaitai_attribute_parse_terminated_bytes(GKaitaiAttribute *, const kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **); + +/* Détermine la zone de couverture finale d'une correspondance. */ +static bool g_kaitai_attribute_compute_maybe_terminated_range(const GKaitaiAttribute *, const kaitai_scope_t *, const GBinContent *, const vmpa2t *, phys_t *, mrange_t *); + +/* Parcourt un contenu binaire selon des spécifications Kaitai. */ +static bool g_kaitai_attribute_parse_content(GKaitaiAttribute *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **); + + + +/* ---------------------------------------------------------------------------------- */ +/* CORRESPONDANCE ENTRE ATTRIBUT ET BINAIRE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un attribut de la spécification Kaitai. */ +G_DEFINE_TYPE(GKaitaiAttribute, g_kaitai_attribute, G_TYPE_KAITAI_PARSER); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des attributs de spécification Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_attribute_class_init(GKaitaiAttributeClass *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_attribute_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_attribute_finalize; + + parser = G_KAITAI_PARSER_CLASS(klass); + + parser->parse = (parse_kaitai_fc)g_kaitai_attribute_parse_content; + + klass->get_label = g_kaitai_attribute_get_raw_id; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = instance à initialiser. * +* * +* Description : Initialise un attribut de spécification Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_attribute_init(GKaitaiAttribute *attrib) +{ + attrib->raw_id = NULL; + attrib->orig_id = NULL; + + attrib->doc = NULL; + + attrib->payload = KAP_UNINITIALIZED; + + attrib->repetition = KAR_NO_REPETITION; + attrib->repeat_controller = NULL; + + attrib->condition = NULL; + + init_szstr(&attrib->terminator); + attrib->consume = true; + attrib->include = false; + attrib->eos_error = true; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_attribute_dispose(GKaitaiAttribute *attrib) +{ + if (attrib->payload & KAP_DYNAMIC_TYPE) + g_clear_object(&attrib->switchon); + + G_OBJECT_CLASS(g_kaitai_attribute_parent_class)->dispose(G_OBJECT(attrib)); + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_attribute_finalize(GKaitaiAttribute *attrib) +{ + if (attrib->raw_id != NULL) + free(attrib->raw_id); + + if (attrib->orig_id != NULL) + free(attrib->orig_id); + + if (attrib->doc != NULL) + free(attrib->doc); + + if (attrib->payload & KAP_FIXED_CONTENT) + exit_szstr(&attrib->fixed_content); + + else if (attrib->payload & KAP_USER_TYPE) + free(attrib->named_type); + + if (attrib->fixed_size != NULL) + free(attrib->fixed_size); + + if (attrib->repeat_controller != NULL) + free(attrib->repeat_controller); + + if (attrib->condition != NULL) + free(attrib->condition); + + exit_szstr(&attrib->terminator); + + G_OBJECT_CLASS(g_kaitai_attribute_parent_class)->finalize(G_OBJECT(attrib)); + +} + + +/****************************************************************************** +* * +* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Construit un lecteur d'attribut Kaitai. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiAttribute *g_kaitai_attribute_new(GYamlNode *parent) +{ + GKaitaiAttribute *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_KAITAI_ATTRIBUTE, NULL); + + if (!g_kaitai_attribute_create(result, parent, true)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à initialiser pleinement.* +* parent = noeud Yaml contenant l'attribut à constituer. * +* need_id = encadre la présence d'un champ "id". * +* * +* Description : Met en place un lecteur d'attribut Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_attribute_create(GKaitaiAttribute *attrib, GYamlNode *parent, bool need_id) +{ + bool result; /* Bilan à retourner */ + GYamlNode *node; /* Noeud particulier présent */ + const char *value; /* Valeur Yaml particulière */ + char *rebuilt_value; /* Valeur Yaml rassemblée */ + kaitai_scope_t fake; /* Contexte de circonstance */ + resolved_value_t bytes; /* Données brutes obtenues */ + GYamlNode *other_node; /* Autre noeud nécessaire */ + + result = false; + + /* Identifiant obligatoire */ + + node = g_yaml_node_find_first_by_path(parent, "/id"); + + 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_id; + } + + attrib->raw_id = strdup(value); + + g_object_unref(G_OBJECT(node)); + + } + + else if (need_id) + goto bad_id; + + /* Identifiant facultatif */ + + node = g_yaml_node_find_first_by_path(parent, "/-orig-id"); + + 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_id; + } + + attrib->orig_id = strdup(value); + + g_object_unref(G_OBJECT(node)); + + } + + /* Eventuelle documentation */ + + node = g_yaml_node_find_first_by_path(parent, "/doc"); + + 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_doc; + } + + attrib->doc = strdup(value); + + g_object_unref(G_OBJECT(node)); + + } + + /* Champ contents */ + + node = g_yaml_node_find_first_by_path(parent, "/contents"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + rebuilt_value = g_yaml_pair_aggregate_value(G_YAML_PAIR(node)); + + if (rebuilt_value == NULL) + { + g_object_unref(G_OBJECT(node)); + goto bad_content; + } + + fake.meta = NULL; + fake.root = NULL; + fake.parent = NULL; + fake.last = NULL; + + if (!resolve_kaitai_expression_as_bytes(&fake, rebuilt_value, strlen(rebuilt_value), &bytes)) + { + free(rebuilt_value); + g_object_unref(G_OBJECT(node)); + goto bad_content; + } + + free(rebuilt_value); + + attrib->fixed_content = bytes.bytes; + + g_object_unref(G_OBJECT(node)); + + attrib->payload |= KAP_FIXED_CONTENT; + + } + + /* Charge portée par un type */ + + node = g_yaml_node_find_first_by_path(parent, "/type"); + + if (node != NULL) + { + if (attrib->payload & KAP_FIXED_CONTENT) + { + printf("Can not handle fixed content and type definition at the same time for an attribute.\n"); + goto bad_definition; + } + + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + if (g_kaitai_attribute_resolve_type(attrib, value)) + attrib->payload |= KAP_BASIC_TYPE; + + else + { + attrib->named_type = strdup(value); + attrib->payload |= KAP_USER_TYPE; + } + + } + + else + { + attrib->switchon = g_kaitai_switch_new(parent, attrib); + if (attrib->switchon == NULL) goto bad_definition; + + attrib->payload |= KAP_DYNAMIC_TYPE; + + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Répétitions contrôlées ? */ + + node = g_yaml_node_find_first_by_path(parent, "/repeat"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + if (strcmp(value, "eos") == 0) + attrib->repetition = KAR_END_OF_STREAM; + + else if (strcmp(value, "expr") == 0) + { + other_node = g_yaml_node_find_first_by_path(parent, "/repeat-expr"); + + if (other_node != NULL) + { + if (G_IS_YAML_PAIR(other_node)) + { + value = g_yaml_pair_get_value(G_YAML_PAIR(other_node)); + + if (value != NULL) + { + attrib->repetition = KAR_EXPRESSION; + attrib->repeat_controller = strdup(value); + } + else + printf("Expected repeat expression\n"); + + } + + g_object_unref(G_OBJECT(other_node)); + + } + + } + + else if (strcmp(value, "until") == 0) + { + other_node = g_yaml_node_find_first_by_path(parent, "/repeat-until"); + + if (other_node != NULL) + { + assert(G_IS_YAML_PAIR(other_node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(other_node)); + + if (value != NULL) + { + attrib->repetition = KAR_UNTIL; + attrib->repeat_controller = strdup(value); + } + else + printf("Expected repeat expression\n"); + + } + + g_object_unref(G_OBJECT(other_node)); + + } + + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Intégration sous condition ? */ + + node = g_yaml_node_find_first_by_path(parent, "/if"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + attrib->condition = strdup(value); + + g_object_unref(G_OBJECT(node)); + + } + + /* Taille fixée ? */ + + node = g_yaml_node_find_first_by_path(parent, "/size"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + attrib->fixed_size = strdup(value); + attrib->payload |= KAP_SIZED; + } + + g_object_unref(G_OBJECT(node)); + + if ((attrib->payload & KAP_SIZED) == 0) + goto bad_content; + + } + + /* Prise en considération d'une taille maximale */ + + node = g_yaml_node_find_first_by_path(parent, "/size-eos"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL && strcmp(value, "true") == 0) + { + if (attrib->payload != KAP_UNINITIALIZED) + /* printf warning */; + + attrib->payload |= KAP_SIZED_EOS; + + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Champ terminator */ + + node = g_yaml_node_find_first_by_path(parent, "/terminator"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + rebuilt_value = g_yaml_pair_aggregate_value(G_YAML_PAIR(node)); + + if (rebuilt_value == NULL) + { + g_object_unref(G_OBJECT(node)); + goto bad_content; + } + + fake.meta = NULL; + fake.root = NULL; + fake.parent = NULL; + fake.last = NULL; + + if (!resolve_kaitai_expression_as_bytes(&fake, rebuilt_value, strlen(rebuilt_value), &bytes)) + { + free(rebuilt_value); + g_object_unref(G_OBJECT(node)); + goto bad_content; + } + + free(rebuilt_value); + + if (attrib->terminator.data != NULL) + printf("A ending content has already been specified (implicitly by the strz type)"); + + else + { + attrib->terminator.data = bytes.bytes.data; + attrib->terminator.len = bytes.bytes.len; + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Champ consume */ + + node = g_yaml_node_find_first_by_path(parent, "/consume"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + if (strcmp(value, "true") == 0) + attrib->consume = true; + + else if (strcmp(value, "false") == 0) + attrib->consume = false; + + else + printf("Unsupported value for the 'consume' property (expecting true of false)"); + + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Champ include */ + + node = g_yaml_node_find_first_by_path(parent, "/include"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + if (strcmp(value, "true") == 0) + attrib->include = true; + + else if (strcmp(value, "false") == 0) + attrib->include = false; + + else + printf("Unsupported value for the 'include' property (expecting true of false)"); + + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Champ eos-error */ + + node = g_yaml_node_find_first_by_path(parent, "/eos-error"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + if (strcmp(value, "true") == 0) + attrib->eos_error = true; + + if (strcmp(value, "false") == 0) + attrib->eos_error = false; + + else + printf("Unsupported value for the 'eos_error' property (expecting true of false)"); + + } + + g_object_unref(G_OBJECT(node)); + + } + + /* Validation finale */ + + result = g_kaitai_attribute_check(attrib); + + bad_definition: + + bad_doc: + bad_id: + bad_content: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = attribut Kaitai en cours de constitution. * +* desc = chaîne de caractère à interpréter en type. * +* * +* Description : Traduit en type concret une chaîne de caractères. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_kaitai_attribute_resolve_type(GKaitaiAttribute *attrib, const char *desc) +{ + bool result; /* Bilan à retourner */ + + result = true; + + attrib->basic = BTP_INVALID; + attrib->has_endian = false; + + /** + * Cf. définition des types de base existants : + * http://doc.kaitai.io/user_guide.html#_fixed_size_structures + */ + +#define RESOLVE_ENDIAN \ + if (desc[2] == 'l') \ + { \ + if (desc[3] == 'e') \ + { \ + attrib->endian = SRE_LITTLE; \ + attrib->has_endian = true; \ + } \ + } \ + else if (desc[2] == 'b') \ + { \ + if (desc[3] == 'e') \ + { \ + attrib->endian = SRE_BIG; \ + attrib->has_endian = true; \ + } \ + } \ + + /* Analyse de la chaîne fournie */ + + switch (desc[0]) + { + case 'f': + switch (desc[1]) + { + case '4': + attrib->basic = BTP_754R_32; + RESOLVE_ENDIAN; + break; + + case '8': + attrib->basic = BTP_754R_64; + RESOLVE_ENDIAN; + break; + + default: + result = false; + break; + + } + break; + + case 's': + switch (desc[1]) + { + case '1': + attrib->basic = BTP_CHAR; + RESOLVE_ENDIAN; + break; + + case '2': + attrib->basic = BTP_SHORT; + RESOLVE_ENDIAN; + break; + + case '4': + attrib->basic = BTP_INT; + RESOLVE_ENDIAN; + break; + + case '8': + attrib->basic = BTP_LONG_LONG; + RESOLVE_ENDIAN; + break; + + case 't': + if (desc[2] == 'r') + { + attrib->basic = BTP_CHAR; + attrib->is_string = true; + if (desc[3] == 'z') + { + attrib->terminator.data = strdup(""); + attrib->terminator.len = 1; + } + } + else + result = false; + break; + + default: + result = false; + break; + + } + break; + + case 'u': + switch (desc[1]) + { + case '1': + attrib->basic = BTP_UCHAR; + RESOLVE_ENDIAN; + break; + + case '2': + attrib->basic = BTP_USHORT; + RESOLVE_ENDIAN; + break; + + case '4': + attrib->basic = BTP_UINT; + RESOLVE_ENDIAN; + break; + + case '8': + attrib->basic = BTP_ULONG_LONG; + RESOLVE_ENDIAN; + break; + + default: + result = false; + break; + + } + break; + + default: + result = false; + break; + + } + + /* Vérification d'une comparaison complète */ + if (result) + switch (attrib->basic) + { + case BTP_CHAR: + if (attrib->is_string) + { + if (attrib->terminator.data != NULL) + result = (desc[4] == 0); + else + result = (desc[3] == 0); + } + else + { + if (attrib->has_endian) + result = (desc[4] == 0); + else + result = (desc[2] == 0); + } + break; + + default: + if (attrib->has_endian) + result = (desc[4] == 0); + else + result = (desc[2] == 0); + break; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = attribut Kaitai à valider. * +* * +* Description : Valide la cohérence des informations portées par l'attribut. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_kaitai_attribute_check(const GKaitaiAttribute *attrib) +{ + bool result; /* Bilan à retourner */ + + result = true; + + /** + * Une lecture de tous les octets restants ne doit correspondre qu'à des octets bruts. + */ + if (attrib->payload & KAP_SIZED_EOS && attrib->payload != KAP_SIZED_EOS) + { + result = (attrib->payload & KAP_BASIC_TYPE) && attrib->is_string; + + if (!result) + { + printf("Reading all the remaining bytes should only produce bytes."); + result = true; + } + + } + + /** + * Une chaîne (type str[z]) doit comporter une séquence de terminaison. + */ + if ((attrib->payload & KAP_BASIC_TYPE) && attrib->is_string) + { + result = (attrib->terminator.data != NULL) || (attrib->payload & (KAP_SIZED | KAP_SIZED_EOS)); + + if (!result) + { + printf("An unsized string (str type with no size attribute) has to be link to a terminator sequence."); + goto exit; + } + + } + + /** + * Si une séquence d'octets finaux est spécifiées, alors l'attribut + * doit correspondre à un type str[z] (lecture) ou de taille fixée + * (validation post-lecture). + */ + if (attrib->terminator.data != NULL) + { + result = ((attrib->payload & ~(KAP_FIXED_CONTENT | KAP_BASIC_TYPE | KAP_SIZED)) == 0); + + if (result && (attrib->payload & KAP_BASIC_TYPE)) + result = attrib->is_string; + + if (!result) + { + printf("A useless terminator is specified."); + result = true; + goto exit; + } + + } + + /** + * Il n'est pas possible d'inclure un marqueur de fin sans le consommer. + */ + if (!attrib->consume && attrib->include) + { + result = false; + printf("It is not possible to include a terminator without consuming it."); + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à dupliquer. * +* type = type utilisateur à associer au nouvel attribut. * +* * +* Description : Dérive un lecteur d'attribut Kaitai pour un type utilisateur.* +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiAttribute *g_kaitai_attribute_dup_for_user_type(const GKaitaiAttribute *attrib, const char *type) +{ + GKaitaiAttribute *result; /* Structure à retourner */ + + result = g_kaitai_attribute_dup_for(attrib); + + result->payload = KAP_USER_TYPE; + + result->named_type = strdup(type); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à dupliquer. * +* * +* Description : Copie le coeur de la définition d'un lecteur d'attribut. * +* * +* Retour : Nouvelle instance à compléter. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GKaitaiAttribute *g_kaitai_attribute_dup_for(const GKaitaiAttribute *attrib) +{ + GKaitaiAttribute *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_KAITAI_ATTRIBUTE, NULL); + + /** + * Il n'y a rien à copier dans la structure parente. + * + * Les travaux de copie ne portent ainsi que sur le présent attribut. + */ + + result->raw_id = strdup(attrib->raw_id); + + if (attrib->orig_id != NULL) + result->orig_id = strdup(attrib->orig_id); + + if (attrib->doc != NULL) + result->doc = strdup(attrib->doc); + + if (attrib->fixed_size != NULL) + result->fixed_size = strdup(attrib->fixed_size); + + result->repetition = attrib->repetition; + + if (attrib->repeat_controller != NULL) + result->repeat_controller = strdup(attrib->repeat_controller); + + if (attrib->condition != NULL) + result->condition = strdup(attrib->condition); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* * +* Description : Indique l'étiquette à utiliser pour identifier un attribut. * +* * +* Retour : Valeur brute de l'identifiant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_attribute_get_label(const GKaitaiAttribute *attrib) +{ + const char *result; /* Valeur à renvoyer */ + GKaitaiAttributeClass *class; /* Classe de l'instance */ + + class = G_KAITAI_ATTRIBUTE_GET_CLASS(attrib); + + result = class->get_label(attrib); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* * +* Description : Indique la désignation brute d'un identifiant Kaitai. * +* * +* Retour : Valeur brute de l'identifiant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_attribute_get_raw_id(const GKaitaiAttribute *attrib) +{ + char *result; /* Valeur à renvoyer */ + + result = attrib->raw_id; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* * +* Description : Indique la désignation originelle d'un identifiant Kaitai. * +* * +* Retour : Valeur originelle de l'identifiant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_attribute_get_original_id(const GKaitaiAttribute *attrib) +{ + char *result; /* Valeur à renvoyer */ + + result = attrib->orig_id; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* * +* Description : Fournit une éventuelle documentation concernant l'attribut. * +* * +* Retour : Description enregistrée ou NULL si absente. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_attribute_get_doc(const GKaitaiAttribute *attrib) +{ + char *result; /* Valeur à renvoyer */ + + result = attrib->doc; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* * +* Description : Indique la nature de la charge représentée par l'attribut. * +* * +* Retour : Forme de contenu représenté par le lecteur d'attribut. * +* * +* Remarques : - * +* * +******************************************************************************/ + +KaitaiAttributePayload g_kaitai_attribute_get_payload(const GKaitaiAttribute *attrib) +{ + KaitaiAttributePayload result; /* Type de charge à renvoyer */ + + result = attrib->payload; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* basic = type de base Kaitai reconnu par le lecteur. [OUT]* +* is_string = nature du type BTP_CHAR en sortie. [OUT] * +* * +* Description : Précise un éventuel type de base reconnu par le lecteur. * +* * +* Retour : Validité du type renseigné en argument. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_attribute_get_basic_type(const GKaitaiAttribute *attrib, BaseType *basic, bool *is_string) +{ + bool result; /* Validité à retourner */ + + result = (attrib->payload & KAP_BASIC_TYPE); + + if (result) + { + *basic = attrib->basic; + *is_string = attrib->is_string; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* content = contenu binaire à venir lire. * +* range = espace disponible pour la lecture. * +* out = tableau d'octets retournés. [OUT] * +* len = taille de ce tableau alloué. [OUT] * +* * +* Description : Lit les octets d'une chaîne représentée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_attribute_read_truncated_bytes(const GKaitaiAttribute *attrib, const GBinContent *content, const mrange_t *range, bin_t **out, size_t *len) +{ + bool result; /* Bilan à retourner */ + vmpa2t tmppos; /* Localisation modifiable */ + const bin_t *data; /* Accès aux données brutes */ + + result = false; + + if ((attrib->payload & KAP_SIZED) == 0) + goto bad_type; + + copy_vmpa(&tmppos, get_mrange_addr(range)); + + *len = get_mrange_length(range); + + data = g_binary_content_get_raw_access(content, &tmppos, *len); + + *out = malloc(sizeof(bin_t) * (*len + 1)); + + memcpy(*out, data, *len); + (*out)[*len] = '\0'; + + result = true; + + bad_type: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* * +* Description : Détermine si l'attribue porte une valeur entière signée. * +* * +* Retour : Bilan de la consultation : true si un entier signé est visé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_attribute_handle_signed_integer(const GKaitaiAttribute *attrib) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if ((attrib->payload & KAP_BASIC_TYPE) == 0) + goto bad_type; + + switch (attrib->basic) + { + case BTP_CHAR: + case BTP_SHORT: + case BTP_INT: + case BTP_LONG_LONG: + result = true; + break; + + default: + break; + + } + + bad_type: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. * +* content = contenu binaire à venir lire. * +* range = espace de lecture. * +* endian = boustime des données à respecter. * +* out = valeur à sauvegarder sous une forme générique.[OUT]* +* * +* Description : Lit la valeur d'un élément Kaitai entier représenté. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_attribute_read_value(const GKaitaiAttribute *attrib, const GBinContent *content, const mrange_t *range, SourceEndian endian, resolved_value_t *out) +{ + bool result; /* Bilan à retourner */ + vmpa2t tmppos; /* Localisation modifiable */ + const bin_t *data; /* Données brutes restituées */ + int8_t stmp8; /* Valeur de 8 bits lue */ + uint8_t tmp8; /* Valeur de 8 bits lue */ + int16_t stmp16; /* Valeur de 16 bits lue */ + uint16_t tmp16; /* Valeur de 16 bits lue */ + int32_t stmp32; /* Valeur de 32 bits lue */ + uint32_t tmp32; /* Valeur de 32 bits lue */ + int64_t stmp64; /* Valeur de 64 bits lue */ + uint64_t tmp64; /* Valeur de 64 bits lue */ + + result = false; + + if (attrib->payload & (KAP_FIXED_CONTENT | KAP_SIZED | KAP_SIZED_EOS)) + { + copy_vmpa(&tmppos, get_mrange_addr(range)); + + data = g_binary_content_get_raw_access(content, &tmppos, get_mrange_length(range)); + result = (data != NULL); + + if (result) + { + out->type = GVT_BYTES; + + out->bytes.len = get_mrange_length(range); + + out->bytes.data = malloc(out->bytes.len); + memcpy(out->bytes.data, data, out->bytes.len); + + } + + } + + else if (attrib->payload & KAP_BASIC_TYPE) + { + copy_vmpa(&tmppos, get_mrange_addr(range)); + + switch (attrib->basic) + { + case BTP_CHAR: + if (attrib->is_string) + { + copy_vmpa(&tmppos, get_mrange_addr(range)); + + data = g_binary_content_get_raw_access(content, &tmppos, get_mrange_length(range)); + result = (data != NULL); + + if (result) + { + out->type = GVT_BYTES; + + out->bytes.len = get_mrange_length(range); + + out->bytes.data = malloc(out->bytes.len); + memcpy(out->bytes.data, data, out->bytes.len); + + } + + } + else + { + assert(get_mrange_length(range) == 1); + result = g_binary_content_read_s8(content, &tmppos, &stmp8); + out->type = GVT_SIGNED_INTEGER; + out->signed_integer = stmp8; + } + break; + + case BTP_UCHAR: + assert(get_mrange_length(range) == 1); + result = g_binary_content_read_u8(content, &tmppos, &tmp8); + out->type = GVT_UNSIGNED_INTEGER; + out->unsigned_integer = tmp8; + break; + + case BTP_SHORT: + assert(get_mrange_length(range) == 2); + result = g_binary_content_read_s16(content, &tmppos, endian, &stmp16); + out->type = GVT_SIGNED_INTEGER; + out->signed_integer = stmp16; + break; + + case BTP_USHORT: + assert(get_mrange_length(range) == 2); + result = g_binary_content_read_u16(content, &tmppos, endian, &tmp16); + out->type = GVT_UNSIGNED_INTEGER; + out->unsigned_integer = tmp16; + break; + + case BTP_INT: + assert(get_mrange_length(range) == 4); + result = g_binary_content_read_s32(content, &tmppos, endian, &stmp32); + out->type = GVT_SIGNED_INTEGER; + out->signed_integer = stmp32; + break; + + case BTP_UINT: + assert(get_mrange_length(range) == 4); + result = g_binary_content_read_u32(content, &tmppos, endian, &tmp32); + out->type = GVT_UNSIGNED_INTEGER; + out->unsigned_integer = tmp32; + break; + + case BTP_LONG_LONG: + assert(get_mrange_length(range) == 8); + result = g_binary_content_read_s64(content, &tmppos, endian, &stmp64); + out->type = GVT_SIGNED_INTEGER; + out->signed_integer = stmp64; + break; + + case BTP_ULONG_LONG: + assert(get_mrange_length(range) == 8); + result = g_binary_content_read_u64(content, &tmppos, endian, &tmp64); + out->type = GVT_UNSIGNED_INTEGER; + out->unsigned_integer = tmp64; + break; + + default: + break; + + } + + } + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : attrib = structure Kaitai en cours de parcours. * +* locals = variables locales pour les résolutions de types. * +* content = données binaires à analyser et traduire. * +* pos = 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_attribute_parse_content(GKaitaiAttribute *attrib, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record) +{ + bool result; /* Bilan à retourner */ + resolved_value_t authorized; /* Validation des traitements */ + + mrange_t work_range; /* Définition de cette aire */ + GBinContent *work_area; /* Aire de travail */ + bool has_empty_size; /* Mémorise une taille nulle */ + + + //unsigned long long value; /* Valeur entière finale */ + //bool status; /* Bilan d'une conversion */ + + + vmpa2t tmp; /* Position de travail */ + phys_t diff; /* Différentiel de positions */ + resolved_value_t resolved; /* Valeur entière obtenue */ + phys_t max_size; /* Taille maximale imposée */ + + + const bin_t *data; /* Données à comparer */ + GKaitaiType *user_type; /* Définition particulière */ + + + mrange_t range; /* Couverture appliquée */ + SourceEndian endian; /* Boutisme à observer */ + phys_t cur_diff; /* Avancée de lecture courante */ + + + result = false; + *record = NULL; + + /* Lecture soumise à condition ? */ + + if (attrib->condition != NULL) + { + result = resolve_kaitai_expression_as_boolean(locals, + attrib->condition, + strlen(attrib->condition), + &authorized); + + if (!result || !authorized.status) + goto exit; + + } + + /* Zone de travail restreinte */ + + g_binary_content_compute_end_pos(content, &tmp); + diff = compute_vmpa_diff(pos, &tmp); + + if (attrib->payload & KAP_SIZED) + { + result = resolve_kaitai_expression_as_integer(locals, + attrib->fixed_size, + strlen(attrib->fixed_size), + &resolved); + + if (result) + { + if (resolved.type == GVT_UNSIGNED_INTEGER) + max_size = resolved.unsigned_integer; + else + { + assert(resolved.type == GVT_SIGNED_INTEGER); + + if (resolved.signed_integer < 0) + result = false; + else + max_size = resolved.signed_integer; + + } + + if (result) + result = (diff >= max_size); + + if (!result) + printf("Need more data!\n"); + + if (result && max_size < diff) + diff = max_size; + + } + + if (!result) + goto exit; + + init_mrange(&work_range, pos, diff); + work_area = g_restricted_content_new_ro(content, &work_range); + + has_empty_size = (diff == 0); + + } + else + { + work_area = content; + has_empty_size = false; + } + + /* Etablissement d'une zone de correspondance */ + + if (attrib->payload == KAP_UNINITIALIZED) + assert(false); + + else if (attrib->payload & KAP_SIZED_EOS) + result = true; + + else if (attrib->payload & KAP_FIXED_CONTENT) + { + if (diff >= attrib->fixed_content.len) + { + copy_vmpa(&tmp, pos); + + data = g_binary_content_get_raw_access(work_area, &tmp, attrib->fixed_content.len); + assert(data != NULL); + + result = (memcmp(data, attrib->fixed_content.data, attrib->fixed_content.len) == 0); + + if (result) + diff = attrib->fixed_content.len; + + } + + } + + else if (attrib->payload & KAP_BASIC_TYPE) + { + switch (attrib->basic) + { + case BTP_CHAR: + case BTP_UCHAR: + if (attrib->is_string) + { + if ((attrib->payload & KAP_SIZED) == 0) + result = g_kaitai_attribute_parse_terminated_bytes(attrib, locals, work_area, pos, record); + } + else + { + result = (diff >= 1); + diff = 1; + } + break; + + case BTP_SHORT: + case BTP_USHORT: + result = (diff >= 2); + diff = 2; + break; + + case BTP_INT: + case BTP_UINT: + case BTP_754R_32: + result = (diff >= 4); + diff = 4; + break; + + case BTP_LONG_LONG: + case BTP_ULONG_LONG: + case BTP_754R_64: + result = (diff >= 8); + diff = 8; + break; + + default: + break; + + } + + } + + else if (attrib->payload & KAP_USER_TYPE) + { + user_type = find_sub_type(locals, attrib->named_type); + + if (user_type != NULL) + { + result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(user_type), + locals, work_area, pos, record); + + if (result) + /** + * Le type utilisateur dérive du type GKaitaiStruct, qui ne possède pas + * d'identifiant propre. La correspondance produite est ainsi nominalement + * anonyme, ce qui empêche toute résolution. + * + * Le rattachement de l'étiquette de l'attribut d'origine est donc forcée ici. + */ + g_match_record_fix_creator(*record, G_KAITAI_PARSER(attrib)); + + + g_object_unref(G_OBJECT(user_type)); + + } + + } + + else if (attrib->payload & KAP_DYNAMIC_TYPE) + result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(attrib->switchon), locals, work_area, pos, record); + + else if (attrib->payload & KAP_SIZED) + { + /* Cas déjà traité en début de fonction */ + + } + + /* Enregistrement de la correspondance */ + + if (result && *record == NULL) + { + /** + * On choisit de laisser la création de correspondances nulles. + * + * Cela permet de disposer de la présence de champs valides, même vides + * (cf. "4.10.3. Repeat until condition is met") + */ + + /* if (diff > 0) */ + { + result = g_kaitai_attribute_compute_maybe_terminated_range(attrib, locals, content, pos, &diff, &range); + + if (result) + { + if (has_empty_size) + *record = G_MATCH_RECORD(g_record_empty_new(G_KAITAI_PARSER(attrib), content, pos)); + + else + { + if (attrib->has_endian) + endian = attrib->endian; + else + endian = g_kaitai_meta_get_endian(locals->meta); + + *record = G_MATCH_RECORD(g_record_item_new(attrib, work_area, &range, endian)); + + if (*record != NULL) + advance_vmpa(pos, diff); + else + result = false; + + } + + } + + } + + } + + /* Libération de zone de travail restreinte ? */ + + if (attrib->payload & KAP_SIZED) + { + cur_diff = compute_vmpa_diff(get_mrange_addr(&work_range), pos); + + /* Pour GCC... */ + max_size = get_mrange_length(&work_range); + + if (cur_diff < max_size) + advance_vmpa(pos, max_size - cur_diff); + + assert(work_area != content); + g_object_unref(G_OBJECT(work_area)); + + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = structure Kaitai en cours de parcours. * +* locals = variables locales pour les résolutions de types. * +* content = données binaires à analyser et traduire. * +* pos = tête de lecture courante. [OUT] * +* record = noeud d'arborescence d'éléments rencontrés. [OUT] * +* * +* Description : Extrait d'un contenu une série d'octets avec terminaison. * +* * +* Retour : Bilan de l'opératon : true pour continuer, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_kaitai_attribute_parse_terminated_bytes(GKaitaiAttribute *attrib, const kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record) +{ + bool result; /* Bilan à retourner */ + sized_string_t marker; /* Marqueur potentiel à tester */ + vmpa2t iter; /* Tête de lecture courante */ + vmpa2t end; /* Fin du parcours possible */ + vmpa2t tmp; /* Position à mouvante */ + phys_t diff; /* Avancée de lecture courante */ + mrange_t range; /* Couverture appliquée */ + SourceEndian endian; /* Boutisme à observer */ + + result = false; + + /* Recherche du marqueur de fin */ + + marker.len = attrib->terminator.len; + + copy_vmpa(&iter, pos); + g_binary_content_compute_end_pos(content, &end); + + while (cmp_vmpa_by_phy(&iter, &end) < 0) + { + copy_vmpa(&tmp, &iter); + + marker.data = (char *)g_binary_content_get_raw_access(content, &tmp, marker.len); + if (marker.data == NULL) break; + + if (szmemcmp(&marker, &attrib->terminator) == 0) + { + result = true; + break; + } + + advance_vmpa(&iter, 1); + + } + + /* Si la recherche a abouti */ + + if (result) + { + diff = compute_vmpa_diff(pos, &iter); + + if (attrib->include) + diff += marker.len; + + init_mrange(&range, pos, diff); + + if (attrib->has_endian) + endian = attrib->endian; + else + endian = g_kaitai_meta_get_endian(locals->meta); + + *record = G_MATCH_RECORD(g_record_item_new(attrib, content, &range, endian)); + + copy_vmpa(pos, &iter); + + if (attrib->consume) + advance_vmpa(pos, marker.len); + + } + + /* Sinon l'absence de marqueur est-elle tolérée ? */ + + else if (!attrib->eos_error) + { + diff = compute_vmpa_diff(pos, &end); + + init_mrange(&range, pos, diff); + + if (attrib->has_endian) + endian = attrib->endian; + else + endian = g_kaitai_meta_get_endian(locals->meta); + + *record = G_MATCH_RECORD(g_record_item_new(attrib, content, &range, endian)); + + copy_vmpa(pos, &end); + + result = true; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = structure Kaitai en cours de parcours. * +* locals = variables locales pour les résolutions de types. * +* content = données binaires à analyser et traduire. * +* pos = tête de lecture courante. * +* maxsize = taille maximale de la zone de correspondance. [OUT]* +* range = zone de couverture à officialiser. [OUT] * +* * +* Description : Détermine la zone de couverture finale d'une correspondance. * +* * +* Retour : Bilan de l'opératon : true pour continuer, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_kaitai_attribute_compute_maybe_terminated_range(const GKaitaiAttribute *attrib, const kaitai_scope_t *locals, const GBinContent *content, const vmpa2t *pos, phys_t *maxsize, mrange_t *range) +{ + bool result; /* Bilan à retourner */ + sized_string_t marker; /* Marqueur potentiel à tester */ + vmpa2t iter; /* Tête de lecture courante */ + vmpa2t end; /* Fin du parcours possible */ + vmpa2t tmp; /* Position à mouvante */ + phys_t diff; /* Avancée de lecture courante */ + + if (attrib->terminator.data == NULL) + { + init_mrange(range, pos, *maxsize); + result = true; + } + + else + { + result = false; + + if (attrib->terminator.len > *maxsize) + goto exit; + + /* Recherche du marqueur de fin */ + + marker.len = attrib->terminator.len; + + copy_vmpa(&iter, pos); + + copy_vmpa(&tmp, pos); + advance_vmpa(&tmp, *maxsize - marker.len); + + while (cmp_vmpa_by_phy(&iter, &end) <= 0) + { + copy_vmpa(&tmp, &iter); + + marker.data = (char *)g_binary_content_get_raw_access(content, &tmp, marker.len); + if (marker.data == NULL) break; + + if (szmemcmp(&marker, &attrib->terminator) == 0) + { + result = true; + break; + } + + advance_vmpa(&iter, 1); + + } + + /* Si la recherche a abouti */ + + if (result) + { + diff = compute_vmpa_diff(pos, &iter); + + if (attrib->include) + init_mrange(range, pos, diff + marker.len); + else + init_mrange(range, pos, diff); + + assert((diff + marker.len) <= *maxsize); + + if (attrib->consume) + *maxsize = diff + marker.len; + else + *maxsize = diff; + + } + + /* Sinon l'absence de marqueur est-elle tolérée ? */ + + else if (!attrib->eos_error) + { + init_mrange(range, pos, *maxsize); + result = true; + } + + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = structure Kaitai en cours de parcours. * +* locals = variables locales pour les résolutions de types. * +* content = données binaires à analyser et traduire. * +* pos = 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_attribute_parse_content(GKaitaiAttribute *attrib, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record) +{ + bool result; /* Bilan à retourner */ + resolved_value_t authorized; /* Validation des traitements */ + GRecordList *list; /* Constitution d'une liste */ + vmpa2t end; /* Position maximale du flux */ + phys_t diff; /* Différentiel de positions */ + GMatchRecord *child; /* Element de liste à intégrer */ + resolved_value_t resolved; /* Valeur entière obtenue */ + unsigned long long count; /* Nombre d'itérations à mener */ + unsigned long long i; /* Boucle de parcours */ + resolved_value_t loop; /* Poursuite des lectures ? */ + + if (attrib->repetition == KAR_NO_REPETITION) + result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, record); + + else + { + /* Lecture soumise à condition ? */ + + if (attrib->condition != NULL) + { + result = resolve_kaitai_expression_as_boolean(locals, + attrib->condition, + strlen(attrib->condition), + &authorized); + + if (!result || !authorized.status) + goto exit; + + } + + list = g_record_list_new(attrib, content, pos); + + switch (attrib->repetition) + { + case KAR_END_OF_STREAM: + + result = true; + + g_binary_content_compute_end_pos(content, &end); + diff = compute_vmpa_diff(pos, &end); + + while (diff > 0) + { + result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, &child); + if (!result) break; + + g_record_list_add_record(list, child); + remember_last_record(locals, child); + + diff = compute_vmpa_diff(pos, &end); + + } + + break; + + case KAR_EXPRESSION: + + result = resolve_kaitai_expression_as_integer(locals, + attrib->repeat_controller, + strlen(attrib->repeat_controller), + &resolved); + + if (resolved.type == GVT_UNSIGNED_INTEGER) + count = resolved.unsigned_integer; + else + { + assert(resolved.type == GVT_SIGNED_INTEGER); + + if (resolved.signed_integer < 0) + { + result = false; + break; + } + + count = resolved.signed_integer; + + } + + for (i = 0; i < count; i++) + { + result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, &child); + if (!result) break; + + g_record_list_add_record(list, child); + remember_last_record(locals, child); + + } + + break; + + case KAR_UNTIL: + + do + { + result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, &child); + if (!result) break; + + g_record_list_add_record(list, child); + remember_last_record(locals, child); + + result = resolve_kaitai_expression_as_boolean(locals, + attrib->repeat_controller, + strlen(attrib->repeat_controller), + &loop); + if (!result) break; + + } + while (!loop.status); + + break; + + default: + break; + + } + + if (!result) g_clear_object(&list); + + *record = G_MATCH_RECORD(list); + + } + + exit: + + return result; + +} diff --git a/plugins/kaitai/parsers/attribute.h b/plugins/kaitai/parsers/attribute.h new file mode 100644 index 0000000..38b78d7 --- /dev/null +++ b/plugins/kaitai/parsers/attribute.h @@ -0,0 +1,154 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * attribute.h - prototypes pour la spécification d'un attribut 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H +#define _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include <analysis/content.h> +#include <analysis/types/basic.h> +#include <plugins/yaml/node.h> + + +#include "../expression.h" + + + +#define G_TYPE_KAITAI_ATTRIBUTE g_kaitai_attribute_get_type() +#define G_KAITAI_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_ATTRIBUTE, GKaitaiAttribute)) +#define G_IS_KAITAI_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_ATTRIBUTE)) +#define G_KAITAI_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_ATTRIBUTE, GKaitaiAttributeClass)) +#define G_IS_KAITAI_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_ATTRIBUTE)) +#define G_KAITAI_ATTRIBUTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_ATTRIBUTE, GKaitaiAttributeClass)) + + +/* Spécification d'un attribut Kaitai (instance) */ +typedef struct _GKaitaiAttribute GKaitaiAttribute; + +/* Spécification d'un attribut Kaitai (classe) */ +typedef struct _GKaitaiAttributeClass GKaitaiAttributeClass; + + +/* Type de charge associée à un attribut */ +typedef enum _KaitaiAttributePayload +{ + KAP_UNINITIALIZED = (0 << 0), /* Type non initialisé */ + + KAP_FIXED_CONTENT = (1 << 0), /* Contenu brut attendu */ + KAP_BASIC_TYPE = (1 << 1), /* Type prédéfini */ + KAP_USER_TYPE = (1 << 2), /* Type personnalisé */ + KAP_DYNAMIC_TYPE = (1 << 3), /* Type dynmatique */ + KAP_SIZED = (1 << 4), /* Bourrage dimensionné */ + KAP_SIZED_EOS = (1 << 5), /* Bourrage final */ + +} KaitaiAttributePayload; + +/* Types de base reconnus par Kaitai */ +typedef enum _KaitaiBasicType +{ + KBT_U1, /* Octet non signé */ + KBT_U2, /* Mot de 16 bits non signé */ + KBT_U2LE, /* Mot de 16 bits non signé */ + KBT_U2BE, /* Mot de 16 bits non signé */ + KBT_U4, /* Mot de 32 bits non signé */ + KBT_U4LE, /* Mot de 32 bits non signé */ + KBT_U4BE, /* Mot de 32 bits non signé */ + KBT_U8, /* Mot de 64 bits non signé */ + KBT_U8LE, /* Mot de 64 bits non signé */ + KBT_U8BE, /* Mot de 64 bits non signé */ + KBT_S1, /* Octet signé */ + KBT_S2, /* Mot de 16 bits signé */ + KBT_S2LE, /* Mot de 16 bits signé */ + KBT_S2BE, /* Mot de 16 bits signé */ + KBT_S4, /* Mot de 32 bits signé */ + KBT_S4LE, /* Mot de 32 bits signé */ + KBT_S4BE, /* Mot de 32 bits signé */ + KBT_S8, /* Mot de 64 bits signé */ + KBT_S8LE, /* Mot de 64 bits signé */ + KBT_S8BE, /* Mot de 64 bits signé */ + KBT_F4, /* Flottant sur 32 bits */ + KBT_F4BE, /* Flottant sur 32 bits */ + KBT_F4LE, /* Flottant sur 32 bits */ + KBT_F8, /* Flottant sur 64 bits */ + KBT_F8BE, /* Flottant sur 64 bits */ + KBT_F8LE, /* Flottant sur 64 bits */ + KBT_STR, /* Chaîne de caractères */ + KBT_STRZ, /* Chaîne de caractères + '\0' */ + +} KaitaiBasicType; + +/* Formes de répétition d'une lecture d'attribut */ +typedef enum _KaitaiAttributeRepetition +{ + KAR_NO_REPETITION, /* Aucune forme de répétition */ + + KAR_END_OF_STREAM, /* Redites autant que possible */ + KAR_EXPRESSION, /* Répétitions selon quantité */ + KAR_UNTIL, /* Répétitions sous condition */ + +} KaitaiAttributeRepetition; + + +/* Indique le type défini pour un attribut de la spécification Kaitai. */ +GType g_kaitai_attribute_get_type(void); + +/* Construit un lecteur d'attribut Kaitai. */ +GKaitaiAttribute *g_kaitai_attribute_new(GYamlNode *); + +/* Dérive un lecteur d'attribut Kaitai pour un type utilisateur. */ +GKaitaiAttribute *g_kaitai_attribute_dup_for_user_type(const GKaitaiAttribute *, const char *); + +/* Indique l'étiquette à utiliser pour identifier un attribut. */ +const char *g_kaitai_attribute_get_label(const GKaitaiAttribute *); + +/* Indique la désignation brute d'un identifiant Kaitai. */ +const char *g_kaitai_attribute_get_raw_id(const GKaitaiAttribute *); + +/* Indique la désignation originelle d'un identifiant Kaitai. */ +const char *g_kaitai_attribute_get_original_id(const GKaitaiAttribute *); + +/* Fournit une éventuelle documentation concernant l'attribut. */ +const char *g_kaitai_attribute_get_doc(const GKaitaiAttribute *); + +/* Indique la nature de la charge représentée par l'attribut. */ +KaitaiAttributePayload g_kaitai_attribute_get_payload(const GKaitaiAttribute *); + +/* Précise un éventuel type de base reconnu par le lecteur. */ +bool g_kaitai_attribute_get_basic_type(const GKaitaiAttribute *, BaseType *, bool *); + +/* Lit les octets d'une chaîne représentée. */ +bool g_kaitai_attribute_read_truncated_bytes(const GKaitaiAttribute *, const GBinContent *, const mrange_t *, bin_t **, size_t *); + +/* Détermine si l'attribue porte une valeur entière signée. */ +bool g_kaitai_attribute_handle_signed_integer(const GKaitaiAttribute *); + +/* Lit la valeur d'un élément Kaitai entier représenté. */ +bool g_kaitai_attribute_read_value(const GKaitaiAttribute *, const GBinContent *, const mrange_t *, SourceEndian, resolved_value_t *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H */ diff --git a/plugins/kaitai/parsers/enum-int.h b/plugins/kaitai/parsers/enum-int.h new file mode 100644 index 0000000..b62ae41 --- /dev/null +++ b/plugins/kaitai/parsers/enum-int.h @@ -0,0 +1,79 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * enum-int.h - prototypes internes pour la gestion des énumérations 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef PLUGINS_KAITAI_PARSERS_ENUM_INT_H +#define PLUGINS_KAITAI_PARSERS_ENUM_INT_H + + +#include "enum.h" + + + +/* ------------------------- MANIPULATION D'UNE ENUMERATION ------------------------- */ + + +/* Mémorisation d'une valeur d'énumération */ +typedef struct _enum_value_t +{ + resolved_value_t value; /* Valeur entière représentée */ + char *label; /* Elément associé à une valeur*/ + char *doc; /* Eventuelle documentation */ + +} enum_value_t; + + + +/* ----------------------- GESTION D'UN GROUPE D'ENUMERATIONS ----------------------- */ + + +/* Définition d'un ensemble d'énumérations Kaitai (instance) */ +struct _GKaitaiEnum +{ + GObject parent; /* A laisser en premier */ + + char *name; /* Désignation de l'énumération*/ + + enum_value_t **cases_v2l; /* Choix indexés par valeur */ + size_t cases_v2l_count; /* Quantité de ces choix */ + + enum_value_t **cases_l2v; /* Choix indexés par étiquette */ + size_t cases_l2v_count; /* Quantité de ces choix */ + + enum_value_t *defcase; /* Choix par défaut ou NULL */ + +}; + +/* Définition d'un ensemble d'énumérations Kaitai (classe) */ +struct _GKaitaiEnumClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un groupe d'énumérations Kaitai. */ +bool g_kaitai_enum_create(GKaitaiEnum *, GYamlNode *); + + + +#endif /* PLUGINS_KAITAI_PARSERS_ENUM_INT_H */ diff --git a/plugins/kaitai/parsers/enum.c b/plugins/kaitai/parsers/enum.c new file mode 100644 index 0000000..267aaba --- /dev/null +++ b/plugins/kaitai/parsers/enum.c @@ -0,0 +1,765 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * enum.h - gestion des énumérations 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 <http://www.gnu.org/licenses/>. + */ + + +#include "enum.h" + + +#include <malloc.h> +#include <string.h> + + +#include <i18n.h> + + +#include <common/extstr.h> +#include <common/sort.h> +#include <core/logs.h> +#include <plugins/yaml/collection.h> +#include <plugins/yaml/pair.h> + + +#include "enum-int.h" + + + +/* ------------------------- MANIPULATION D'UNE ENUMERATION ------------------------- */ + + +/* Construit une valeur d'énumération à partir d'indications. */ +static enum_value_t *build_enum_value(GYamlNode *, bool *); + +/* Supprime de la mémoire une valeur d'énumération. */ +static void delete_enum_value(enum_value_t *); + +/* Etablit la comparaison entre deux valeurs d'énumération. */ +static int compare_enum_values_by_value(const enum_value_t **, const enum_value_t **); + +/* Etablit la comparaison entre deux noms d'énumération. */ +static int compare_enum_values_by_label(const enum_value_t **, const enum_value_t **); + +/* Etablit la comparaison entre deux noms d'énumération. */ +static int compare_enum_values_by_sized_label(const sized_string_t *, const enum_value_t **); + + + +/* ----------------------- GESTION D'UN GROUPE D'ENUMERATIONS ----------------------- */ + + +/* Initialise la classe des groupes d'énumérations Kaitai. */ +static void g_kaitai_enum_class_init(GKaitaiEnumClass *); + +/* Initialise un groupe d'énumérations Kaitai. */ +static void g_kaitai_enum_init(GKaitaiEnum *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_enum_dispose(GKaitaiEnum *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_enum_finalize(GKaitaiEnum *); + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION D'UNE ENUMERATION */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : node = noeud Yaml à venir lire. * +* defcase = indique si une valeur par défaut est visée. [OUT] * +* * +* Description : Construit une valeur d'énumération à partir d'indications. * +* * +* Retour : Structure de valeur mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static enum_value_t *build_enum_value(GYamlNode *node, bool *defcase) +{ + enum_value_t *result; /* Valeur à retourner */ + const char *key; /* Clef d'une énumération */ + kaitai_scope_t fake; /* Contexte de circonstance */ + resolved_value_t kval; /* Valeur à indexer */ + const char *value; /* Valeur Yaml particulière */ + char *path; /* Chemin d'accès suivant */ + GYamlNode *children; /* Sous-noeuds rattachés */ + GYamlNode *sub; /* Sous-noeud à considérer */ + + result = NULL; + + *defcase = false; + + if (!G_IS_YAML_PAIR(node)) + goto bad_node; + + /* Identification de la valeur énumérative */ + + key = g_yaml_pair_get_key(G_YAML_PAIR(node)); + + if (strcmp(key, "_") == 0) + { + /** + * Exemple de choix par défaut : + * http://doc.kaitai.io/user_guide.html#tlv + */ + + kval.type = GVT_UNSIGNED_INTEGER; + kval.unsigned_integer = ~0llu; + + *defcase = true; + + } + + else + { + fake.meta = NULL; + fake.root = NULL; + fake.parent = NULL; + fake.last = NULL; + + if (!resolve_kaitai_expression_as_integer(&fake, key, strlen(key), &kval)) + goto bad_node; + + } + + /* Récupération des éléments associés à la valeur */ + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + { + result = malloc(sizeof(enum_value_t)); + + result->value = kval; + result->label = strdup(value); + result->doc = NULL; + + } + else + { + /** + * Les énumérations peuvent comporter un commentaire associé + * sous forme d'un élément de documentation complémentaire. + * + * Cf. http://doc.kaitai.io/user_guide.html#verbose-enums + */ + + asprintf(&path, "/%s/", key); + children = g_yaml_node_find_first_by_path(node, path); + free(path); + + if (!G_IS_YAML_COLLEC(children)) + goto bad_value; + + /* Identifiant */ + + sub = g_yaml_node_find_first_by_path(children, "/id"); + + if (!G_IS_YAML_PAIR(sub)) + goto bad_sub_value; + + value = g_yaml_pair_get_value(G_YAML_PAIR(sub)); + + if (value == NULL) + goto bad_sub_value; + + result = malloc(sizeof(enum_value_t)); + + result->value = kval; + result->label = strdup(value); + result->doc = NULL; + + g_object_unref(G_OBJECT(sub)); + + /* Documentation */ + + sub = g_yaml_node_find_first_by_path(children, "/doc"); + + if (!G_IS_YAML_PAIR(sub)) + goto bad_sub_value; + + value = g_yaml_pair_get_value(G_YAML_PAIR(sub)); + + if (value == NULL) + goto bad_sub_value; + + result->doc = strdup(value); + + bad_sub_value: + + g_clear_object(&sub); + + g_object_unref(G_OBJECT(children)); + + bad_value: + + ; + + } + + bad_node: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = valeur à traiter. * +* * +* Description : Supprime de la mémoire une valeur d'énumération. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void delete_enum_value(enum_value_t *value) +{ + EXIT_RESOLVED_VALUE(value->value); + + free(value->label); + + if (value->doc != NULL) + free(value->doc); + + free(value); + +} + + +/****************************************************************************** +* * +* Paramètres : a = premières informations à consulter. * +* b = secondes informations à consulter. * +* * +* Description : Etablit la comparaison entre deux valeurs d'énumération. * +* * +* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int compare_enum_values_by_value(const enum_value_t **a, const enum_value_t **b) +{ + int result; /* Bilan à retourner */ + const resolved_value_t *value_a; /* Raccouri d'accès pour a */ + const resolved_value_t *value_b; /* Raccouri d'accès pour b */ + + value_a = &(*a)->value; + value_b = &(*b)->value; + + if (value_a->type == GVT_UNSIGNED_INTEGER && value_b->type == GVT_UNSIGNED_INTEGER) + result = sort_unsigned_long_long(value_a->unsigned_integer, value_b->unsigned_integer); + + else if (value_a->type == GVT_UNSIGNED_INTEGER && value_b->type == GVT_UNSIGNED_INTEGER) + result = sort_signed_long_long(value_a->signed_integer, value_b->signed_integer); + + else + { + /** + * Le code Python a deux options : soit fournir un équivalent à la + * structure resolved_value_t lors de l'appel correspondant à cette + * fonction compare_enum_values_by_value(), soit fournir un nombre + * directement. + * + * Comme PyArg_ParseTuple() est obligée de trancher entre non-signé + * et signé, le parti est pris de considérer le non-signé coté Python. + * On s'adapte en conséquence ici. + * + * La structure resolved_value_t est une union, donc les valeurs + * sont potientiellement au mauvais format mais bien présentes. + */ + + /** + * result = sort_unsigned_long_long(value_a->type, value_b->type); + */ + + result = sort_unsigned_long_long(value_a->unsigned_integer, value_b->unsigned_integer); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premières informations à consulter. * +* b = secondes informations à consulter. * +* * +* Description : Etablit la comparaison entre deux noms d'énumération. * +* * +* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int compare_enum_values_by_label(const enum_value_t **a, const enum_value_t **b) +{ + int result; /* Bilan à retourner */ + + result = strcmp((*a)->label, (*b)->label); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : l = premières informations à consulter. * +* b = secondes informations à consulter. * +* * +* Description : Etablit la comparaison entre deux noms d'énumération. * +* * +* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int compare_enum_values_by_sized_label(const sized_string_t *l, const enum_value_t **b) +{ + int result; /* Bilan à retourner */ + + result = szstrcmp(l, (*b)->label); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION D'UN GROUPE D'ENUMERATIONS */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un ensemble d'énumérations Kaitai. */ +G_DEFINE_TYPE(GKaitaiEnum, g_kaitai_enum, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des groupes d'énumérations Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_enum_class_init(GKaitaiEnumClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_enum_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_enum_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = instance à initialiser. * +* * +* Description : Initialise un groupe d'énumérations Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_enum_init(GKaitaiEnum *kenum) +{ + kenum->name = NULL; + + kenum->cases_v2l = NULL; + kenum->cases_v2l_count = 0; + + kenum->cases_l2v = NULL; + kenum->cases_l2v_count = 0; + + kenum->defcase = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_enum_dispose(GKaitaiEnum *kenum) +{ + G_OBJECT_CLASS(g_kaitai_enum_parent_class)->dispose(G_OBJECT(kenum)); + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_enum_finalize(GKaitaiEnum *kenum) +{ + size_t i; /* Boucle de parcours */ + + if (kenum->name != NULL) + free(kenum->name); + + for (i = 0; i < kenum->cases_v2l_count; i++) + delete_enum_value(kenum->cases_v2l[i]); + + if (kenum->cases_v2l != NULL) + free(kenum->cases_v2l); + + if (kenum->cases_l2v != NULL) + free(kenum->cases_l2v); + + if (kenum->defcase != NULL) + delete_enum_value(kenum->defcase); + + G_OBJECT_CLASS(g_kaitai_enum_parent_class)->finalize(G_OBJECT(kenum)); + +} + + +/****************************************************************************** +* * +* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Construit un groupe d'énumérations Kaitai. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiEnum *g_kaitai_enum_new(GYamlNode *parent) +{ + GKaitaiEnum *result; /* Identifiant à retourner */ + + result = g_object_new(G_TYPE_KAITAI_ENUM, NULL); + + if (!g_kaitai_enum_create(result, parent)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = groupe d'énumérations à initialiser pleinement. * +* parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Met en place un groupe d'énumérations Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_enum_create(GKaitaiEnum *kenum, GYamlNode *parent) +{ + bool result; /* Bilan à retourner */ + char *path; /* Chemin des valeurs */ + 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 */ + bool defcase; /* Définition par défaut ? */ + enum_value_t *value; /* Valeur énumérative nouvelle */ + bool found; /* Présence de partage existant*/ + size_t index; /* Indice du point d'insertion */ + + result = false; + + /* Récupération du nom */ + + if (!G_IS_YAML_PAIR(parent)) goto exit; + + kenum->name = strdup(g_yaml_pair_get_key(G_YAML_PAIR(parent))); + + /* Association de valeurs */ + + path = strdup("/"); + path = stradd(path, kenum->name); + path = stradd(path, "/"); + + collec = g_yaml_node_find_first_by_path(parent, path); + + free(path); + + 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) + { + for (i = 0; i < count; i++) + { + value = build_enum_value(nodes[i], &defcase); + if (value == NULL) break; + + if (defcase) + { + if (kenum->defcase != NULL) + { + log_variadic_message(LMT_WARNING, + _("Multiple definition of the defaut value for the enumeration '%s'"), + kenum->name); + + delete_enum_value(value); + break; + + } + + /** + * Exemple de choix par défaut : + * http://doc.kaitai.io/user_guide.html#tlv + */ + + kenum->defcase = value; + + } + + else + { + kenum->cases_v2l = qinsert(kenum->cases_v2l, &kenum->cases_v2l_count, sizeof(enum_value_t *), + (__compar_fn_t)compare_enum_values_by_value, &value); + + found = bsearch_index(&value, kenum->cases_l2v, kenum->cases_l2v_count, sizeof(enum_value_t *), + (__compar_fn_t)compare_enum_values_by_label, &index); + + if (found) + log_variadic_message(LMT_WARNING, + _("Multiple occurrence of the label %s in the enumeration '%s'"), + value->label, kenum->name); + + else + kenum->cases_l2v = _qinsert(kenum->cases_l2v, &kenum->cases_l2v_count, sizeof(enum_value_t *), + &value, index); + + } + + g_object_unref(G_OBJECT(nodes[i])); + + } + + result = (i == count); + + for (; i < count; i++) + g_object_unref(G_OBJECT(nodes[i])); + + free(nodes); + + } + + g_object_unref(G_OBJECT(collec)); + + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = groupe d'énumérations à consulter. * +* * +* Description : Fournit le nom principal d'une énumération. * +* * +* Retour : Désignation de l'énumération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_enum_get_name(const GKaitaiEnum *kenum) +{ + const char *result; /* Chaîne à retourner */ + + result = kenum->name; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = groupe d'énumérations à consulter. * +* label = étiquette de l'élément constant à traduire. * +* value = valeur concrète correspondante. [OUT] * +* * +* Description : Traduit une étiquette brute en constante d'énumération. * +* * +* Retour : Bilan de la conversion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_enum_find_value(const GKaitaiEnum *kenum, const sized_string_t *label, resolved_value_t *value) +{ + bool result; /* Présence à retourner */ + size_t index; /* Indice du point d'insertion */ + + result = bsearch_index(label, kenum->cases_l2v, kenum->cases_l2v_count, sizeof(enum_value_t *), + (__compar_fn_t)compare_enum_values_by_sized_label, &index); + + if (result) + *value = kenum->cases_l2v[index]->value; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = groupe d'énumérations à consulter. * +* value = valeur concrète à transformer. * +* prefix = détermine l'ajout d'un préfixe éventuel. * +* * +* Description : Traduit une constante d'énumération en étiquette brute. * +* * +* Retour : Désignation ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_kaitai_enum_find_label(const GKaitaiEnum *kenum, const resolved_value_t *value, bool prefix) +{ + char *result; /* Etiquette à retourner */ + enum_value_t faked; /* Copie d'élément recherché */ + bool found; /* Présence de partage existant*/ + size_t index; /* Indice du point d'insertion */ + const enum_value_t *item; /* Elément retrouvé par valeur */ + + faked.value = *value; + + found = bsearch_index(&faked, kenum->cases_v2l, kenum->cases_v2l_count, sizeof(enum_value_t *), + (__compar_fn_t)compare_enum_values_by_value, &index); + + if (found) + item = kenum->cases_l2v[index]; + else + item = kenum->defcase; + + if (item != NULL) + { + if (prefix) + asprintf(&result, "%s::%s", kenum->name, item->label); + else + result = strdup(item->label); + } + + else + result = NULL; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : kenum = groupe d'énumérations à consulter. * +* value = valeur concrète à transformer. * +* * +* Description : Traduit une constante d'énumération en documentation. * +* * +* Retour : Documentation associée à la valeur indiquée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_kaitai_enum_find_documentation(const GKaitaiEnum *kenum, const resolved_value_t *value) +{ + char *result; /* Documentation à retourner */ + enum_value_t faked; /* Copie d'élément recherché */ + bool found; /* Présence de partage existant*/ + size_t index; /* Indice du point d'insertion */ + const enum_value_t *item; /* Elément retrouvé par valeur */ + + faked.value = *value; + + found = bsearch_index(&faked, kenum->cases_v2l, kenum->cases_v2l_count, sizeof(enum_value_t *), + (__compar_fn_t)compare_enum_values_by_value, &index); + + if (found) + item = kenum->cases_l2v[index]; + else + item = kenum->defcase; + + if (item != NULL) + result = strdup(item->doc); + else + result = NULL; + + return result; + +} diff --git a/plugins/kaitai/parsers/enum.h b/plugins/kaitai/parsers/enum.h new file mode 100644 index 0000000..9e4bf2a --- /dev/null +++ b/plugins/kaitai/parsers/enum.h @@ -0,0 +1,76 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * enum.h - prototypes pour la gestion des énumérations 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_ENUM_H +#define _PLUGINS_KAITAI_PARSERS_ENUM_H + + +#include <glib-object.h> +#include <stdbool.h> +#include <stdint.h> + + +#include <common/szstr.h> +#include <plugins/yaml/node.h> + + +#include "../expression.h" + + + +#define G_TYPE_KAITAI_ENUM g_kaitai_enum_get_type() +#define G_KAITAI_ENUM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_ENUM, GKaitaiEnum)) +#define G_IS_KAITAI_ENUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_ENUM)) +#define G_KAITAI_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_ENUM, GKaitaiEnumClass)) +#define G_IS_KAITAI_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_ENUM)) +#define G_KAITAI_ENUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_ENUM, GKaitaiEnumClass)) + + +/* Définition d'un ensemble d'énumérations Kaitai (instance) */ +typedef struct _GKaitaiEnum GKaitaiEnum; + +/* Définition d'un ensemble d'énumérations Kaitai (classe) */ +typedef struct _GKaitaiEnumClass GKaitaiEnumClass; + + +/* Indique le type défini pour un ensemble d'énumérations Kaitai. */ +GType g_kaitai_enum_get_type(void); + +/* Construit un groupe d'énumérations Kaitai. */ +GKaitaiEnum *g_kaitai_enum_new(GYamlNode *); + +/* Fournit le nom principal d'une énumération. */ +const char *g_kaitai_enum_get_name(const GKaitaiEnum *); + +/* Traduit une étiquette brute en constante d'énumération. */ +bool g_kaitai_enum_find_value(const GKaitaiEnum *, const sized_string_t *, resolved_value_t *); + +/* Traduit une constante d'énumération en étiquette brute. */ +char *g_kaitai_enum_find_label(const GKaitaiEnum *, const resolved_value_t *, bool); + +/* Traduit une constante d'énumération en documentation. */ +char *g_kaitai_enum_find_documentation(const GKaitaiEnum *, const resolved_value_t *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_ENUM_H */ diff --git a/plugins/kaitai/parsers/instance-int.h b/plugins/kaitai/parsers/instance-int.h new file mode 100644 index 0000000..6f098b4 --- /dev/null +++ b/plugins/kaitai/parsers/instance-int.h @@ -0,0 +1,59 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instance-int.h - prototypes pour les spécifications internes d'une instance Kaitai + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_INSTANCE_INT_H +#define _PLUGINS_KAITAI_PARSERS_INSTANCE_INT_H + + +#include "attribute-int.h" +#include "instance.h" + + + +/* Spécification d'une instance Kaitai (instance) */ +struct _GKaitaiInstance +{ + GKaitaiAttribute parent; /* A laisser en premier */ + + char *name; /* Nom attribué à l'instance */ + + char *io; /* Contenu binaire forcé */ + char *pos; /* Position forcée */ + char *value; /* Formule pour calcul */ + +}; + +/* Spécification d'une instance Kaitai (classe) */ +struct _GKaitaiInstanceClass +{ + GKaitaiAttributeClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un lecteur d'instance Kaitai. */ +bool g_kaitai_instance_create(GKaitaiInstance *, GYamlNode *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_INSTANCE_INT_H */ diff --git a/plugins/kaitai/parsers/instance.c b/plugins/kaitai/parsers/instance.c new file mode 100644 index 0000000..63db224 --- /dev/null +++ b/plugins/kaitai/parsers/instance.c @@ -0,0 +1,478 @@ + +/* 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 <http://www.gnu.org/licenses/>. + */ + + +#include "instance.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include <plugins/yaml/pair.h> + + +#include "instance-int.h" +#include "../expression.h" +#include "../records/value.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 *, vmpa2t *, 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. * +* 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. * +* pos = 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, vmpa2t *pos, GMatchRecord **record) +{ + bool result; /* Bilan à retourner */ + GBinContent *work_area; /* Aire de travail */ + GKaitaiStream *stream; /* Flux de données pour Kaitai */ + resolved_value_t offset; /* Position à adopter */ + vmpa2t forced_pos; /* Tete de lecture constituée */ + GKaitaiParserClass *class; /* Classe parente à solliciter */ + + if (inst->value != NULL) + { + *record = G_MATCH_RECORD(g_record_value_new(inst, locals)); + + result = (*record != NULL); + + } + + else + { + /* Contenu particulier */ + + if (inst->io == NULL) + work_area = content; + + else + { + result = resolve_kaitai_expression_as_stream(locals, inst->io, strlen(inst->io), &stream); + if (!result) 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); + + result = resolve_kaitai_expression_as_integer(locals, inst->pos, strlen(inst->pos), &offset); + if (!result) 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) + { + result = false; + goto exit_with_content; + } + + advance_vmpa(&forced_pos, offset.signed_integer); + + } + + /* Lecture */ + + class = G_KAITAI_PARSER_CLASS(g_kaitai_instance_parent_class); + + result = class->parse(G_KAITAI_PARSER(inst), locals, work_area, &forced_pos, record); + + exit_with_content: + + if (work_area != content) + g_object_unref(G_OBJECT(work_area)); + + } + + exit: + + return result; + +} diff --git a/plugins/kaitai/parsers/instance.h b/plugins/kaitai/parsers/instance.h new file mode 100644 index 0000000..a9aee9a --- /dev/null +++ b/plugins/kaitai/parsers/instance.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instance.h - prototypes pour la spécification d'une instance Kaitai + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_INSTANCE_H +#define _PLUGINS_KAITAI_PARSERS_INSTANCE_H + + +#include <glib-object.h> + + +#include <plugins/yaml/node.h> + + +#include "../expression.h" +#include "../scope.h" + + + +#define G_TYPE_KAITAI_INSTANCE g_kaitai_instance_get_type() +#define G_KAITAI_INSTANCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_INSTANCE, GKaitaiInstance)) +#define G_IS_KAITAI_INSTANCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_INSTANCE)) +#define G_KAITAI_INSTANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_INSTANCE, GKaitaiInstanceClass)) +#define G_IS_KAITAI_INSTANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_INSTANCE)) +#define G_KAITAI_INSTANCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_INSTANCE, GKaitaiInstanceClass)) + + +/* Spécification d'une instance Kaitai (instance) */ +typedef struct _GKaitaiInstance GKaitaiInstance; + +/* Spécification d'une instance Kaitai (classe) */ +typedef struct _GKaitaiInstanceClass GKaitaiInstanceClass; + + +/* Indique le type défini pour une instance de la spécification Kaitai. */ +GType g_kaitai_instance_get_type(void); + +/* Construit un lecteur d'instance Kaitai. */ +GKaitaiInstance *g_kaitai_instance_new(GYamlNode *); + +/* Indique le nom attribué à une instance Kaitai. */ +const char *g_kaitai_instance_get_name(const GKaitaiInstance *); + +/* Détermine la valeur d'un élément Kaitai entier calculé. */ +bool g_kaitai_instance_compute_value(const GKaitaiInstance *, const kaitai_scope_t *, resolved_value_t *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_INSTANCE_H */ diff --git a/plugins/kaitai/parsers/meta-int.h b/plugins/kaitai/parsers/meta-int.h new file mode 100644 index 0000000..7d847ef --- /dev/null +++ b/plugins/kaitai/parsers/meta-int.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * meta-int.h - prototypes internes pour la description globale d'une définition 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef PLUGINS_KAITAI_PARSERS_META_INT_H +#define PLUGINS_KAITAI_PARSERS_META_INT_H + + +#include "meta.h" + + + +/* Description globale d'une définition Kaitai (instance) */ +struct _GKaitaiMeta +{ + GObject parent; /* A laisser en premier */ + + char *id; /* Identifiant attribué */ + char *title; /* Désignation de la définition*/ + + SourceEndian endian; /* Boutisme par défaut */ + +}; + +/* Description globale d'une définition Kaitai (classe) */ +struct _GKaitaiMetaClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une description globale Kaitai. */ +bool g_kaitai_meta_create(GKaitaiMeta *, GYamlNode *); + + + +#endif /* PLUGINS_KAITAI_PARSERS_META_INT_H */ diff --git a/plugins/kaitai/parsers/meta.c b/plugins/kaitai/parsers/meta.c new file mode 100644 index 0000000..dc30c73 --- /dev/null +++ b/plugins/kaitai/parsers/meta.c @@ -0,0 +1,319 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * meta.c - description globale d'une définition Kaitai + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#include "meta.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include <plugins/yaml/pair.h> + + +#include "meta-int.h" + + + +/* Initialise la classe des descriptions globales Kaitai. */ +static void g_kaitai_meta_class_init(GKaitaiMetaClass *); + +/* Initialise une description globale de définition Kaitai. */ +static void g_kaitai_meta_init(GKaitaiMeta *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_meta_dispose(GKaitaiMeta *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_meta_finalize(GKaitaiMeta *); + + + +/* Indique le type défini pour une description globale Kaitai. */ +G_DEFINE_TYPE(GKaitaiMeta, g_kaitai_meta, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des descriptions globales Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_meta_class_init(GKaitaiMetaClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_meta_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_meta_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : meta = instance à initialiser. * +* * +* Description : Initialise une description globale de définition Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_meta_init(GKaitaiMeta *meta) +{ + meta->id = NULL; + meta->title = NULL; + + meta->endian = SRE_LITTLE; + +} + + +/****************************************************************************** +* * +* Paramètres : meta = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_meta_dispose(GKaitaiMeta *meta) +{ + G_OBJECT_CLASS(g_kaitai_meta_parent_class)->dispose(G_OBJECT(meta)); + +} + + +/****************************************************************************** +* * +* Paramètres : meta = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_meta_finalize(GKaitaiMeta *meta) +{ + if (meta->id != NULL) + free(meta->id); + + if (meta->title != NULL) + free(meta->title); + + G_OBJECT_CLASS(g_kaitai_meta_parent_class)->finalize(G_OBJECT(meta)); + +} + + +/****************************************************************************** +* * +* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Construit une description globale Kaitai. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiMeta *g_kaitai_meta_new(GYamlNode *parent) +{ + GKaitaiMeta *result; /* Identifiant à retourner */ + + result = g_object_new(G_TYPE_KAITAI_META, NULL); + + if (!g_kaitai_meta_create(result, parent)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : meta = description globale à initialiser pleinement. * +* parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Met en place une description globale Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_meta_create(GKaitaiMeta *meta, GYamlNode *parent) +{ + bool result; /* Bilan à retourner */ + GYamlNode *node; /* Noeud particulier présent */ + const char *value; /* Valeur Yaml particulière */ + + result = true; + + /* Identifiant */ + + node = g_yaml_node_find_first_by_path(parent, "/meta/id"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + meta->id = strdup(value); + + g_object_unref(G_OBJECT(node)); + + } + + /* Titre */ + + node = g_yaml_node_find_first_by_path(parent, "/meta/title"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value != NULL) + meta->title = strdup(value); + + g_object_unref(G_OBJECT(node)); + + } + + /* Boutisme */ + + node = g_yaml_node_find_first_by_path(parent, "/meta/endian"); + + if (node != NULL) + { + assert(G_IS_YAML_PAIR(node)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (strcmp(value, "le") == 0) + meta->endian = SRE_LITTLE; + + else if (strcmp(value, "be") == 0) + meta->endian = SRE_BIG; + + g_object_unref(G_OBJECT(node)); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : meta = description globale à consulter. * +* * +* Description : Fournit l'identifié associé à une définiton Kaitai. * +* * +* Retour : Identifiant de définition complète ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_meta_get_id(const GKaitaiMeta *meta) +{ + const char *result; /* Chaîne à retourner */ + + result = meta->id; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : meta = description globale à consulter. * +* * +* Description : Fournit la désignation humaine d'une définiton Kaitai. * +* * +* Retour : Intitulé de définition OU NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_meta_get_title(const GKaitaiMeta *meta) +{ + const char *result; /* Chaîne à retourner */ + + result = meta->title; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : meta = description globale à consulter. * +* * +* Description : Indique le boustime observé par défaut par une définiton. * +* * +* Retour : Boustime, petit par défaut. * +* * +* Remarques : - * +* * +******************************************************************************/ + +SourceEndian g_kaitai_meta_get_endian(const GKaitaiMeta *meta) +{ + SourceEndian result; /* Chaîne à retourner */ + + result = meta->endian; + + return result; + +} diff --git a/plugins/kaitai/parsers/meta.h b/plugins/kaitai/parsers/meta.h new file mode 100644 index 0000000..3797823 --- /dev/null +++ b/plugins/kaitai/parsers/meta.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * meta.h - prototypes pour la description globale d'une définition Kaitai + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_META_H +#define _PLUGINS_KAITAI_PARSERS_META_H + + +#include <glib-object.h> + + +#include <common/endianness.h> +#include <plugins/yaml/node.h> + + + +#define G_TYPE_KAITAI_META g_kaitai_meta_get_type() +#define G_KAITAI_META(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_META, GKaitaiMeta)) +#define G_IS_KAITAI_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_META)) +#define G_KAITAI_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_META, GKaitaiMetaClass)) +#define G_IS_KAITAI_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_META)) +#define G_KAITAI_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_META, GKaitaiMetaClass)) + + +/* Description globale d'une définition Kaitai (instance) */ +typedef struct _GKaitaiMeta GKaitaiMeta; + +/* Description globale d'une définition Kaitai (classe) */ +typedef struct _GKaitaiMetaClass GKaitaiMetaClass; + + +/* Indique le type défini pour une description globale Kaitai. */ +GType g_kaitai_meta_get_type(void); + +/* Construit une description globale Kaitai. */ +GKaitaiMeta *g_kaitai_meta_new(GYamlNode *); + +/* Fournit l'identifié associé à une définiton Kaitai. */ +const char *g_kaitai_meta_get_id(const GKaitaiMeta *); + +/* Fournit la désignation humaine d'une définiton Kaitai. */ +const char *g_kaitai_meta_get_title(const GKaitaiMeta *); + +/* Indique le boustime observé par défaut par une définiton. */ +SourceEndian g_kaitai_meta_get_endian(const GKaitaiMeta *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_META_H */ diff --git a/plugins/kaitai/parsers/struct-int.h b/plugins/kaitai/parsers/struct-int.h new file mode 100644 index 0000000..f34be32 --- /dev/null +++ b/plugins/kaitai/parsers/struct-int.h @@ -0,0 +1,75 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * struct-int.h - prototypes internes pour la 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef PLUGINS_KAITAI_PARSERS_STRUCT_INT_H +#define PLUGINS_KAITAI_PARSERS_STRUCT_INT_H + + +#include "attribute.h" +#include "instance.h" +#include "struct.h" +#include "../parser-int.h" + + + +/* Spécification d'une structure Kaitai (instance) */ +struct _GKaitaiStruct +{ + GKaitaiParser parent; /* A laisser en premier */ + + GKaitaiMeta *meta; /* Description globale */ + + GKaitaiAttribute **seq_items; /* Sous-attributs présents */ + size_t seq_items_count; /* Quantité de ces attributs */ + + GKaitaiType **types; /* Types particuliers définis */ + size_t types_count; /* Quantité de ces types */ + + GKaitaiInstance **instances; /* Instances prises en charge */ + size_t instances_count; /* Quantité de ces instances */ + + GKaitaiEnum **enums; /* Enumérations locales */ + size_t enums_count; /* Quantité de ces énumérations*/ + +}; + +/* Spécification d'une structure Kaitai (classe) */ +struct _GKaitaiStructClass +{ + GKaitaiParserClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un interpréteur de définitions Kaitai. */ +bool g_kaitai_structure_create_from_text(GKaitaiStruct *, const char *); + +/* Met en place un interpréteur de définitions Kaitai. */ +bool g_kaitai_structure_create_from_file(GKaitaiStruct *, const char *); + +/* Met en place un lecteur de définitions Kaitai. */ +bool g_kaitai_structure_create(GKaitaiStruct *, GYamlNode *); + + + +#endif /* PLUGINS_KAITAI_PARSERS_STRUCT_INT_H */ diff --git a/plugins/kaitai/parsers/struct.c b/plugins/kaitai/parsers/struct.c new file mode 100644 index 0000000..6d97110 --- /dev/null +++ b/plugins/kaitai/parsers/struct.c @@ -0,0 +1,782 @@ + +/* 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 <http://www.gnu.org/licenses/>. + */ + + +#include "struct.h" + + +#include <assert.h> +#include <string.h> + + +#include <plugins/yaml/collection.h> +#include <plugins/yaml/parser.h> + + +#include "struct-int.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 un ensemble de définitions Kaitai. */ +static bool g_kaitai_structure_load(GKaitaiStruct *, GYamlNode *); + + + +/* --------------------- 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 *, vmpa2t *, 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->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->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_load(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 */ + + root = parse_yaml_from_file(filename); + + if (root != NULL) + { + result = g_kaitai_structure_load(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. * +* root = racine YAML à parcourir. * +* * +* Description : Charge un ensemble de définitions Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_kaitai_structure_load(GKaitaiStruct *kstruct, GYamlNode *root) +{ + bool result; /* Bilan à retourner */ + + result = g_kaitai_structure_create(kstruct, root); + + 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 */ + bool failed; /* Détection d'un échec */ + + result = false; + + /* Informations générales */ + + kstruct->meta = g_kaitai_meta_new(parent); + assert(kstruct->meta != NULL); + + /* 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) + { + kstruct->types = calloc(count, sizeof(GKaitaiType *)); + kstruct->types_count = count; + + for (i = 0; i < count; i++) + { + kstruct->types[i] = g_kaitai_type_new(nodes[i]); + if (kstruct->types[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 = 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 (szstrcmp(name, other) == 0) + { + 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; + } + + } + + 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 */ + bool status; /* Bilan de l'analyse */ + + g_binary_content_compute_start_pos(content, &pos); + + init_record_scope(&locals, kstruct->meta); + + status = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct), &locals, content, &pos, &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. * +* pos = 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, vmpa2t *pos, 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, pos)); + + 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; + + for (i = 0; i < kstruct->seq_items_count; i++) + { + result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->seq_items[i]), + locals, content, pos, &child); + if (!result) goto exit; + + if (child != NULL) + { + g_record_group_add_record(group, child); + g_object_unref(G_OBJECT(child)); + } + + } + + for (i = 0; i < kstruct->instances_count; i++) + { + result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->instances[i]), + locals, content, pos, &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; + + } + + return result; + +} diff --git a/plugins/kaitai/parsers/struct.h b/plugins/kaitai/parsers/struct.h new file mode 100644 index 0000000..4a2397a --- /dev/null +++ b/plugins/kaitai/parsers/struct.h @@ -0,0 +1,80 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * struct.h - prototypes pour la 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_STRUCT_H +#define _PLUGINS_KAITAI_PARSERS_STRUCT_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include <analysis/content.h> + + +#include "enum.h" +#include "meta.h" +#include "type.h" +#include "../record.h" + + + +#define G_TYPE_KAITAI_STRUCT g_kaitai_structure_get_type() +#define G_KAITAI_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_STRUCT, GKaitaiStruct)) +#define G_IS_KAITAI_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_STRUCT)) +#define G_KAITAI_STRUCT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_STRUCT, GKaitaiStructClass)) +#define G_IS_KAITAI_STRUCT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_STRUCT)) +#define G_KAITAI_STRUCT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_STRUCT, GKaitaiStructClass)) + + +/* Spécification d'une structure Kaitai (instance) */ +typedef struct _GKaitaiStruct GKaitaiStruct; + +/* Spécification d'une structure Kaitai (classe) */ +typedef struct _GKaitaiStructClass GKaitaiStructClass; + + +/* Indique le type défini pour une structure Kaitai. */ +GType g_kaitai_structure_get_type(void); + +/* Crée un nouvel interpréteur de structure Kaitai. */ +GKaitaiStruct *g_kaitai_structure_new_from_text(const char *); + +/* Crée un nouvel interpréteur de structure Kaitai. */ +GKaitaiStruct *g_kaitai_structure_new_from_file(const char *); + +/* Fournit la description globale d'une définition Kaitai. */ +GKaitaiMeta *g_kaitai_structure_get_meta(const GKaitaiStruct *); + +/* Recherche la définition d'un type nouveau pour Kaitai. */ +GKaitaiType *g_kaitai_structure_find_sub_type(const GKaitaiStruct *, const char *); + +/* Fournit un ensemble d'énumérations locales de la structure. */ +GKaitaiEnum *g_kaitai_structure_get_enum(const GKaitaiStruct *, const sized_string_t *); + +/* Parcourt un contenu binaire selon une description Kaitai. */ +GMatchRecord *g_kaitai_structure_parse(GKaitaiStruct *, GBinContent *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_STRUCT_H */ diff --git a/plugins/kaitai/parsers/switch-int.h b/plugins/kaitai/parsers/switch-int.h new file mode 100644 index 0000000..a087e49 --- /dev/null +++ b/plugins/kaitai/parsers/switch-int.h @@ -0,0 +1,78 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * switch-int.h - prototypes internes pour la gestion des énumérations 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef PLUGINS_KAITAI_PARSERS_SWITCH_INT_H +#define PLUGINS_KAITAI_PARSERS_SWITCH_INT_H + + +#include "switch.h" +#include "../parser-int.h" + + + +/* ------------------------ BASCULE DYNAMIQUE SELON CONTEXTE ------------------------ */ + + +/* Mémorisation d'une valeur d'énumération */ +typedef struct _switch_case_t +{ + char *value; /* Valeur d'association */ + char *type; /* Désignation du type associé */ + +} switch_case_t; + + + +/* ----------------------- SELECTION DYNAMIQUE DE TYPE KAITAI ----------------------- */ + + +/* Sélection d'un type selon un contexte (instance) */ +struct _GKaitaiSwitch +{ + GKaitaiParser parent; /* A laisser en premier */ + + char *target; /* Source de bascule */ + + switch_case_t **cases; /* Choix de types potentiels */ + size_t count; /* Quantité de ces choix */ + + switch_case_t *defcase; /* Choix par défaut ou NULL */ + + GKaitaiAttribute *generic; /* Attribut à dériver */ + +}; + +/* Sélection d'un type selon un contexte (classe) */ +struct _GKaitaiSwitchClass +{ + GKaitaiParserClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une sélection dynamique de type Kaitai. */ +bool g_kaitai_switch_create(GKaitaiSwitch *, GYamlNode *, GKaitaiAttribute *); + + + +#endif /* PLUGINS_KAITAI_PARSERS_SWITCH_INT_H */ diff --git a/plugins/kaitai/parsers/switch.c b/plugins/kaitai/parsers/switch.c new file mode 100644 index 0000000..c823f27 --- /dev/null +++ b/plugins/kaitai/parsers/switch.c @@ -0,0 +1,706 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * switch.h - gestion des énumérations 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 <http://www.gnu.org/licenses/>. + */ + + +#include "switch.h" + + +#include <assert.h> +#include <errno.h> +#include <malloc.h> +#include <stdlib.h> +#include <string.h> + + +#include <i18n.h> + + +#include <common/extstr.h> +#include <common/sort.h> +#include <core/logs.h> +#include <plugins/yaml/pair.h> + + +#include "switch-int.h" +#include "../expression.h" + + + +/* ------------------------ BASCULE DYNAMIQUE SELON CONTEXTE ------------------------ */ + + +/* Construit une valeur d'énumération à partir d'indications. */ +static switch_case_t *build_switch_case(const GYamlNode *, bool *); + +/* Supprime de la mémoire une bascule selon contexte. */ +static void delete_switch_case(switch_case_t *); + +/* Détermine si le cas correspond à une valeur de bascule. */ +static const char *is_suitable_switch_case_for_bytes(const switch_case_t *, const resolved_value_t *); + +/* Détermine si le cas correspond à une valeur de bascule. */ +static const char *is_suitable_switch_case_for_integer(const switch_case_t *, kaitai_scope_t *, const resolved_value_t *); + + + +/* ----------------------- SELECTION DYNAMIQUE DE TYPE KAITAI ----------------------- */ + + +/* Initialise la classe des sélections dynamiques de types. */ +static void g_kaitai_switch_class_init(GKaitaiSwitchClass *); + +/* Initialise une sélection dynamique de type Kaitai. */ +static void g_kaitai_switch_init(GKaitaiSwitch *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_switch_dispose(GKaitaiSwitch *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_switch_finalize(GKaitaiSwitch *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Parcourt un contenu binaire selon des spécifications Kaitai. */ +static bool g_kaitai_switch_parse_content(GKaitaiSwitch *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **); + + + +/* ---------------------------------------------------------------------------------- */ +/* BASCULE DYNAMIQUE SELON CONTEXTE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : node = noeud Yaml à venir lire. * +* defcase = indique si une valeur par défaut est visée. [OUT] * +* * +* Description : Construit une valeur d'énumération à partir d'indications. * +* * +* Retour : Structure de valeur mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static switch_case_t *build_switch_case(const GYamlNode *node, bool *defcase) +{ + switch_case_t *result; /* Enregistrement à retourner */ + const char *key; /* Clef d'une conversion */ + const char *value; /* Valeur Yaml particulière */ + + result = NULL; + + if (!G_IS_YAML_PAIR(node)) + goto exit; + + key = g_yaml_pair_get_key(G_YAML_PAIR(node)); + value = g_yaml_pair_get_value(G_YAML_PAIR(node)); + + if (value == NULL) + goto exit; + + result = malloc(sizeof(switch_case_t)); + + result->value = strdup(key); + result->type = strdup(value); + + *defcase = (strcmp(key, "_") == 0); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : swcase = valeur à traiter. * +* * +* Description : Supprime de la mémoire une bascule selon contexte. * +* * +* Retour : - * +* * +* Remarques : - * +* * +*****************************************************************************/ + +static void delete_switch_case(switch_case_t *swcase) +{ + free(swcase->value); + + free(swcase->type); + + free(swcase); + +} + + +/****************************************************************************** +* * +* Paramètres : swcase = valeur à analyser. * +* value = valeur à comparer. * +* * +* Description : Détermine si le cas correspond à une valeur de bascule. * +* * +* Retour : Type à utiliser ou NULL si aucune correspondance établie. * +* * +* Remarques : - * +* * +*****************************************************************************/ + +static const char *is_suitable_switch_case_for_bytes(const switch_case_t *swcase, const resolved_value_t *value) +{ + const char *result; /* Désignation à retourner */ + sized_string_t key; /* Changement de format */ + bool valid; /* Validité des opérations */ + int ret; /* Bilan d'une comparaison */ + + result = NULL; + + key.data = swcase->value; + key.len = strlen(swcase->value); + + valid = (key.len > 2); + + if (valid) + valid = (swcase->value[0] == '"' || swcase->value[0] == '\''); + + if (valid) + { + valid = (key.data[0] == key.data[key.len - 1]); + + key.data++; + key.len -= 2; + + } + + if (valid) + { + if (value->type == GVT_BYTES) + { + ret = szmemcmp(&key, &value->bytes); + + if (ret == 0) + result = swcase->type; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : swcase = valeur à analyser. * +* locals = variables locales pour les résolutions de types. * +* value = valeur à comparer. * +* * +* Description : Détermine si le cas correspond à une valeur de bascule. * +* * +* Retour : Type à utiliser ou NULL si aucune correspondance établie. * +* * +* Remarques : - * +* * +*****************************************************************************/ + +static const char *is_suitable_switch_case_for_integer(const switch_case_t *swcase, kaitai_scope_t *locals, const resolved_value_t *value) +{ + const char *result; /* Désignation à retourner */ + bool valid; /* Validité des opérations */ + resolved_value_t key; /* Changement de format */ + unsigned long long unsigned_conv; /* Valeur convertie #1 */ + long long signed_conv; /* Valeur convertie #2 */ + + result = NULL; + + valid = (swcase->value[0] != '"' && swcase->value[0] != '\''); + + if (valid) + { + if (strchr(swcase->value, ':') != NULL) + { + valid = resolve_kaitai_expression_as_integer(locals, swcase->value, strlen(swcase->value), &key); + + if (valid) + { + if (key.type == GVT_UNSIGNED_INTEGER) + { + if (value->type == GVT_UNSIGNED_INTEGER) + { + if (key.unsigned_integer == value->unsigned_integer) + result = swcase->type; + } + else + { + if (key.unsigned_integer == value->signed_integer) + result = swcase->type; + } + } + else + { + if (value->type == GVT_UNSIGNED_INTEGER) + { + if (key.signed_integer == value->unsigned_integer) + result = swcase->type; + } + else + { + if (key.signed_integer == value->signed_integer) + result = swcase->type; + } + } + + } + + } + + else + { + if (value->type == GVT_UNSIGNED_INTEGER) + { + unsigned_conv = strtoull(swcase->value, NULL, 10); + + valid = (errno != ERANGE && errno != EINVAL); + + if (valid && unsigned_conv == value->unsigned_integer) + result = swcase->type; + + } + else + { + signed_conv = strtoll(swcase->value, NULL, 10); + + valid = (errno != ERANGE && errno != EINVAL); + + if (valid && signed_conv == value->signed_integer) + result = swcase->type; + + } + + } + + } + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* SELECTION DYNAMIQUE DE TYPE KAITAI */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un choix dynamique de type Kaitai. */ +G_DEFINE_TYPE(GKaitaiSwitch, g_kaitai_switch, G_TYPE_KAITAI_PARSER); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des sélections dynamiques de types. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_switch_class_init(GKaitaiSwitchClass *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_switch_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_switch_finalize; + + parser = G_KAITAI_PARSER_CLASS(klass); + + parser->parse = (parse_kaitai_fc)g_kaitai_switch_parse_content; + +} + + +/****************************************************************************** +* * +* Paramètres : kswitch = instance à initialiser. * +* * +* Description : Initialise une sélection dynamique de type Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_switch_init(GKaitaiSwitch *kswitch) +{ + kswitch->target = NULL; + + kswitch->cases = NULL; + kswitch->count = 0; + + kswitch->defcase = NULL; + + kswitch->generic = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : kswitch = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_switch_dispose(GKaitaiSwitch *kswitch) +{ + g_clear_object(&kswitch->generic); + + G_OBJECT_CLASS(g_kaitai_switch_parent_class)->dispose(G_OBJECT(kswitch)); + +} + + +/****************************************************************************** +* * +* Paramètres : kswitch = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_switch_finalize(GKaitaiSwitch *kswitch) +{ + size_t i; /* Boucle de parcours */ + + if (kswitch->target != NULL) + free(kswitch->target); + + for (i = 0; i < kswitch->count; i++) + delete_switch_case(kswitch->cases[i]); + + if (kswitch->cases != NULL) + free(kswitch->cases); + + if (kswitch->defcase != NULL) + delete_switch_case(kswitch->defcase); + + G_OBJECT_CLASS(g_kaitai_switch_parent_class)->finalize(G_OBJECT(kswitch)); + +} + + +/****************************************************************************** +* * +* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. * +* generic = lecteur d'attribut Kaitai à dériver. * +* * +* Description : Construit une sélection dynamique de type Kaitai. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiSwitch *g_kaitai_switch_new(GYamlNode *parent, GKaitaiAttribute *generic) +{ + GKaitaiSwitch *result; /* Identifiant à retourner */ + + result = g_object_new(G_TYPE_KAITAI_SWITCH, NULL); + + if (!g_kaitai_switch_create(result, parent, generic)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : kswitch = sélectionneur de type à initialiser pleinement. * +* parent = noeud Yaml contenant l'attribut à constituer. * +* generic = lecteur d'attribut Kaitai à dériver. * +* * +* Description : Met en place une sélection dynamique de type Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_switch_create(GKaitaiSwitch *kswitch, GYamlNode *parent, GKaitaiAttribute *generic) +{ + bool result; /* Bilan à retourner */ + GYamlNode *node; /* Noeud de définition */ + GYamlNode *subnode; /* Noeud de précisions */ + const char *value; /* Valeur Yaml particulière */ + GYamlNode *collec; /* Liste de noeuds à traiter */ + GYamlNode **subnodes; /* Eventuels noeuds trouvés */ + size_t count; /* Quantité de ces noeuds */ + size_t i; /* Boucle de parcours */ + bool defcase; /* Définition par défaut ? */ + switch_case_t *swcase; /* Bascule à noter */ + + result = false; + + node = g_yaml_node_find_first_by_path(parent, "/type/"); + if (node == NULL) goto exit; + + /* Source de la bascule */ + + subnode = g_yaml_node_find_first_by_path(node, "/switch-on"); + assert(G_IS_YAML_PAIR(subnode)); + + value = g_yaml_pair_get_value(G_YAML_PAIR(subnode)); + if (value == NULL) + { + g_object_unref(G_OBJECT(subnode)); + goto bad_definition; + } + + kswitch->target = strdup(value); + + g_object_unref(G_OBJECT(subnode)); + + /* Conditions de bascule */ + + collec = g_yaml_node_find_first_by_path(node, "/cases/"); + if (collec == NULL) goto bad_definition; + if (!G_IS_YAML_COLLEC(collec)) goto bad_definition; + + subnodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count); + + g_object_unref(G_OBJECT(collec)); + + if (count == 0) goto bad_definition; + + for (i = 0; i < count; i++) + { + swcase = build_switch_case(subnodes[i], &defcase); + if (swcase == NULL) break; + + g_object_unref(G_OBJECT(subnodes[i])); + + kswitch->cases = realloc(kswitch->cases, ++kswitch->count * sizeof(switch_case_t *)); + kswitch->cases[kswitch->count - 1] = swcase; + + } + + result = (i == count); + + for (; i < count; i++) + g_object_unref(G_OBJECT(subnodes[i])); + + if (subnodes != NULL) + free(subnodes); + + /* Fin des procédures */ + + if (result) + { + kswitch->generic = generic; + g_object_ref(G_OBJECT(generic)); + } + + bad_definition: + + g_object_unref(G_OBJECT(node)); + + exit: + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : kswitch = structure Kaitai en cours de parcours. * +* locals = variables locales pour les résolutions de types. * +* content = données binaires à analyser et traduire. * +* pos = 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_switch_parse_content(GKaitaiSwitch *kswitch, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record) +{ + bool result; /* Bilan à retourner */ + GMatchRecord *reference; /* Correspondance à utiliser */ + GKaitaiParser *creator; /* Lecteur d'origine */ + KaitaiAttributePayload payload; /* Type de charge supportée */ + BaseType basic; /* Type de base reconnu */ + bool is_string; /* Type lié à une chaîne ? */ +#ifndef NDEBUG + bool status; /* Bilan d'une consultation */ +#endif + const char *final_type; /* Type à utiliser au final */ + resolved_value_t value; /* Valeur de cible entière */ + size_t i; /* Boucle de parcours */ + GKaitaiAttribute *attrib; /* Lecteur approprié */ + + result = false; + + /* Détermination de la forme de comparaison */ + + reference = g_match_record_find_by_name(locals->parent, + kswitch->target, strlen(kswitch->target), + DIRECT_SEARCH_DEEP_LEVEL); + + if (reference == NULL) + goto exit; + + creator = g_match_record_get_creator(reference); + + if (creator == NULL) + goto exit_with_ref; + + if (!G_IS_KAITAI_ATTRIBUTE(creator)) + goto exit_with_creator; + + payload = g_kaitai_attribute_get_payload(G_KAITAI_ATTRIBUTE(creator)); + + if ((payload & KAP_BASIC_TYPE) == 0) + goto exit_with_creator; + +#ifndef NDEBUG + status = g_kaitai_attribute_get_basic_type(G_KAITAI_ATTRIBUTE(creator), &basic, &is_string); + assert(status); +#else + g_kaitai_attribute_get_basic_type(G_KAITAI_ATTRIBUTE(creator), &basic, &is_string); +#endif + + /* Détermination du type visé */ + + final_type = NULL; + + if (is_string) + { + result = resolve_kaitai_expression_as_bytes(locals, + kswitch->target, + strlen(kswitch->target), + &value); + if (!result) goto exit_with_creator; + + for (i = 0; i < kswitch->count; i++) + { + final_type = is_suitable_switch_case_for_bytes(kswitch->cases[i], &value); + + if (final_type != NULL) + break; + + } + + } + + else + { + if (basic == BTP_UCHAR || basic == BTP_USHORT || basic == BTP_UINT || basic == BTP_ULONG_LONG) + { + result = resolve_kaitai_expression_as_integer(locals, + kswitch->target, + strlen(kswitch->target), + &value); + if (!result) goto exit_with_creator; + + for (i = 0; i < kswitch->count; i++) + { + final_type = is_suitable_switch_case_for_integer(kswitch->cases[i], locals, &value); + + if (final_type != NULL) + break; + + } + + } + + else + printf("other type: %u\n", basic); + + } + + if (final_type == NULL && kswitch->defcase != NULL) + final_type = kswitch->defcase->type; + + /* Mise en place d'un attribut et analyse */ + + if (final_type != NULL) + { + attrib = g_kaitai_attribute_dup_for_user_type(kswitch->generic, final_type); + + result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(attrib), locals, content, pos, record); + + g_object_unref(G_OBJECT(attrib)); + + } + + exit_with_creator: + + g_object_unref(G_OBJECT(creator)); + + exit_with_ref: + + g_object_unref(G_OBJECT(reference)); + + exit: + + return result; + +} diff --git a/plugins/kaitai/parsers/switch.h b/plugins/kaitai/parsers/switch.h new file mode 100644 index 0000000..c45237a --- /dev/null +++ b/plugins/kaitai/parsers/switch.h @@ -0,0 +1,61 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * switch.h - prototypes pour la gestion des énumérations 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_SWITCH_H +#define _PLUGINS_KAITAI_PARSERS_SWITCH_H + + +#include <glib-object.h> + + +#include <plugins/yaml/node.h> + + +#include "attribute.h" + + + +#define G_TYPE_KAITAI_SWITCH g_kaitai_switch_get_type() +#define G_KAITAI_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_SWITCH, GKaitaiSwitch)) +#define G_IS_KAITAI_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_SWITCH)) +#define G_KAITAI_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_SWITCH, GKaitaiSwitchClass)) +#define G_IS_KAITAI_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_SWITCH)) +#define G_KAITAI_SWITCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_SWITCH, GKaitaiSwitchClass)) + + +/* Sélection d'un type selon un contexte (instance) */ +typedef struct _GKaitaiSwitch GKaitaiSwitch; + +/* Sélection d'un type selon un contexte (classe) */ +typedef struct _GKaitaiSwitchClass GKaitaiSwitchClass; + + +/* Indique le type défini pour un choix dynamique de type Kaitai. */ +GType g_kaitai_switch_get_type(void); + +/* Construit une sélection dynamique de type Kaitai. */ +GKaitaiSwitch *g_kaitai_switch_new(GYamlNode *, GKaitaiAttribute *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_SWITCH_H */ diff --git a/plugins/kaitai/parsers/type-int.h b/plugins/kaitai/parsers/type-int.h new file mode 100644 index 0000000..4a4d939 --- /dev/null +++ b/plugins/kaitai/parsers/type-int.h @@ -0,0 +1,55 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * type-int.h - prototypes internes pour la définition d'un type particulier pour Kaitai + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef PLUGINS_KAITAI_PARSERS_TYPE_INT_H +#define PLUGINS_KAITAI_PARSERS_TYPE_INT_H + + +#include "struct-int.h" +#include "type.h" + + + +/* Définition d'un type particulier nouveau pour Kaitai (instance) */ +struct _GKaitaiType +{ + GKaitaiStruct parent; /* A laisser en premier */ + + char *name; /* Nom du type particulier */ + +}; + +/* Définition d'un type particulier nouveau pour Kaitai (classe) */ +struct _GKaitaiTypeClass +{ + GKaitaiStructClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un lecteur de type pour Kaitai. */ +bool g_kaitai_type_create(GKaitaiType *, GYamlNode *); + + + +#endif /* PLUGINS_KAITAI_PARSERS_TYPE_INT_H */ diff --git a/plugins/kaitai/parsers/type.c b/plugins/kaitai/parsers/type.c new file mode 100644 index 0000000..30d0373 --- /dev/null +++ b/plugins/kaitai/parsers/type.c @@ -0,0 +1,236 @@ + +/* 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 <http://www.gnu.org/licenses/>. + */ + + +#include "type.h" + + +#include <malloc.h> +#include <string.h> +#include <plugins/yaml/pair.h> + + +#include "type-int.h" +#include "../parser.h" + + + +/* Initialise la classe des types particuliers pour Kaitai. */ +static void g_kaitai_type_class_init(GKaitaiTypeClass *); + +/* Initialise un type particulier pour Kaitai. */ +static void g_kaitai_type_init(GKaitaiType *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_type_dispose(GKaitaiType *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_type_finalize(GKaitaiType *); + + + +/* Indique le type défini pour un type particulier pour Kaitai. */ +G_DEFINE_TYPE(GKaitaiType, g_kaitai_type, G_TYPE_KAITAI_STRUCT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des types particuliers pour Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_type_class_init(GKaitaiTypeClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_type_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_type_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : kstruct = instance à initialiser. * +* * +* Description : Initialise un type particulier pour Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_type_init(GKaitaiType *type) +{ + type->name = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : type = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_type_dispose(GKaitaiType *type) +{ + G_OBJECT_CLASS(g_kaitai_type_parent_class)->dispose(G_OBJECT(type)); + +} + + +/****************************************************************************** +* * +* Paramètres : type = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_type_finalize(GKaitaiType *type) +{ + if (type->name != NULL) + free(type->name); + + G_OBJECT_CLASS(g_kaitai_type_parent_class)->finalize(G_OBJECT(type)); + +} + + +/****************************************************************************** +* * +* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Construit un lecteur de type pour Kaitai. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiType *g_kaitai_type_new(GYamlNode *parent) +{ + GKaitaiType *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_KAITAI_TYPE, NULL); + + if (!g_kaitai_type_create(result, parent)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = lecteur de type Kaitai à initialiser pleinement. * +* parent = noeud Yaml contenant l'attribut à constituer. * +* * +* Description : Met en place un lecteur de type pour Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_type_create(GKaitaiType *type, 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 */ + + result = false; + + /* Extraction du nom */ + + if (!G_IS_YAML_PAIR(parent)) + goto exit; + + name = g_yaml_pair_get_key(G_YAML_PAIR(parent)); + + type->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_structure_create(G_KAITAI_STRUCT(type), sub); + + g_object_unref(G_OBJECT(sub)); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = définition de type particulier à consulter. * +* * +* Description : Indique le nom de scène du type représenté. * +* * +* Retour : Désignation humaine. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_kaitai_type_get_name(const GKaitaiType *type) +{ + const char *result; /* Nom à retourner */ + + result = type->name; + + return result; + +} diff --git a/plugins/kaitai/parsers/type.h b/plugins/kaitai/parsers/type.h new file mode 100644 index 0000000..0656c64 --- /dev/null +++ b/plugins/kaitai/parsers/type.h @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * type.h - prototypes pour la définition d'un type particulier pour Kaitai + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_TYPE_H +#define _PLUGINS_KAITAI_PARSERS_TYPE_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include <plugins/yaml/node.h> + + + +#define G_TYPE_KAITAI_TYPE g_kaitai_type_get_type() +#define G_KAITAI_TYPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_TYPE, GKaitaiType)) +#define G_IS_KAITAI_TYPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_TYPE)) +#define G_KAITAI_TYPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_TYPE, GKaitaiTypeClass)) +#define G_IS_KAITAI_TYPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_TYPE)) +#define G_KAITAI_TYPE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_TYPE, GKaitaiTypeClass)) + + +/* Définition d'un type particulier nouveau pour Kaitai (instance) */ +typedef struct _GKaitaiType GKaitaiType; + +/* Définition d'un type particulier nouveau pour Kaitai (classe) */ +typedef struct _GKaitaiTypeClass GKaitaiTypeClass; + + +/* Indique le type défini pour un type particulier pour Kaitai. */ +GType g_kaitai_type_get_type(void); + +/* Construit un lecteur de type pour Kaitai. */ +GKaitaiType *g_kaitai_type_new(GYamlNode *); + +/* Indique le nom de scène du type représenté. */ +const char *g_kaitai_type_get_name(const GKaitaiType *); + + + +#endif /* _PLUGINS_KAITAI_PARSERS_TYPE_H */ |