From 5eab5f1bf3665e948e2054817fb688963dc86935 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Wed, 24 May 2023 02:38:15 +0200 Subject: Define a first implementation of Kaitai parsing. --- configure.ac | 6 + plugins/Makefile.am | 1 + plugins/kaitai/Makefile.am | 92 ++ plugins/kaitai/array-int.h | 51 + plugins/kaitai/array.c | 376 +++++ plugins/kaitai/array.h | 76 + plugins/kaitai/core.c | 79 + plugins/kaitai/core.h | 38 + plugins/kaitai/expression.h | 134 ++ plugins/kaitai/grammar.y | 1854 +++++++++++++++++++++ plugins/kaitai/parser-int.h | 55 + plugins/kaitai/parser.c | 159 ++ plugins/kaitai/parser.h | 63 + plugins/kaitai/parsers/Makefile.am | 25 + plugins/kaitai/parsers/attribute-int.h | 104 ++ plugins/kaitai/parsers/attribute.c | 2074 ++++++++++++++++++++++++ plugins/kaitai/parsers/attribute.h | 154 ++ plugins/kaitai/parsers/enum-int.h | 79 + plugins/kaitai/parsers/enum.c | 765 +++++++++ plugins/kaitai/parsers/enum.h | 76 + plugins/kaitai/parsers/instance-int.h | 59 + plugins/kaitai/parsers/instance.c | 478 ++++++ plugins/kaitai/parsers/instance.h | 68 + plugins/kaitai/parsers/meta-int.h | 57 + plugins/kaitai/parsers/meta.c | 319 ++++ plugins/kaitai/parsers/meta.h | 68 + plugins/kaitai/parsers/struct-int.h | 75 + plugins/kaitai/parsers/struct.c | 782 +++++++++ plugins/kaitai/parsers/struct.h | 80 + plugins/kaitai/parsers/switch-int.h | 78 + plugins/kaitai/parsers/switch.c | 706 ++++++++ plugins/kaitai/parsers/switch.h | 61 + plugins/kaitai/parsers/type-int.h | 55 + plugins/kaitai/parsers/type.c | 236 +++ plugins/kaitai/parsers/type.h | 62 + plugins/kaitai/python/Makefile.am | 25 + plugins/kaitai/python/array.c | 265 +++ plugins/kaitai/python/array.h | 45 + plugins/kaitai/python/module.c | 132 ++ plugins/kaitai/python/module.h | 41 + plugins/kaitai/python/parser.c | 205 +++ plugins/kaitai/python/parser.h | 45 + plugins/kaitai/python/parsers/Makefile.am | 19 + plugins/kaitai/python/parsers/attribute.c | 420 +++++ plugins/kaitai/python/parsers/attribute.h | 45 + plugins/kaitai/python/parsers/enum.c | 468 ++++++ plugins/kaitai/python/parsers/enum.h | 45 + plugins/kaitai/python/parsers/instance.c | 280 ++++ plugins/kaitai/python/parsers/instance.h | 45 + plugins/kaitai/python/parsers/meta.c | 366 +++++ plugins/kaitai/python/parsers/meta.h | 45 + plugins/kaitai/python/parsers/module.c | 124 ++ plugins/kaitai/python/parsers/module.h | 41 + plugins/kaitai/python/parsers/struct.c | 376 +++++ plugins/kaitai/python/parsers/struct.h | 45 + plugins/kaitai/python/parsers/type.c | 278 ++++ plugins/kaitai/python/parsers/type.h | 45 + plugins/kaitai/python/record.c | 420 +++++ plugins/kaitai/python/record.h | 45 + plugins/kaitai/python/records/Makefile.am | 18 + plugins/kaitai/python/records/empty.c | 286 ++++ plugins/kaitai/python/records/empty.h | 45 + plugins/kaitai/python/records/group.c | 305 ++++ plugins/kaitai/python/records/group.h | 45 + plugins/kaitai/python/records/item.c | 394 +++++ plugins/kaitai/python/records/item.h | 45 + plugins/kaitai/python/records/list.c | 336 ++++ plugins/kaitai/python/records/list.h | 45 + plugins/kaitai/python/records/module.c | 122 ++ plugins/kaitai/python/records/module.h | 41 + plugins/kaitai/python/records/value.c | 335 ++++ plugins/kaitai/python/records/value.h | 45 + plugins/kaitai/python/scope.c | 542 +++++++ plugins/kaitai/python/scope.h | 51 + plugins/kaitai/python/stream.c | 278 ++++ plugins/kaitai/python/stream.h | 45 + plugins/kaitai/record-int.h | 73 + plugins/kaitai/record.c | 408 +++++ plugins/kaitai/record.h | 88 + plugins/kaitai/records/Makefile.am | 21 + plugins/kaitai/records/empty-int.h | 57 + plugins/kaitai/records/empty.c | 236 +++ plugins/kaitai/records/empty.h | 58 + plugins/kaitai/records/group-int.h | 58 + plugins/kaitai/records/group.c | 382 +++++ plugins/kaitai/records/group.h | 65 + plugins/kaitai/records/item-int.h | 58 + plugins/kaitai/records/item.c | 309 ++++ plugins/kaitai/records/item.h | 65 + plugins/kaitai/records/list-int.h | 60 + plugins/kaitai/records/list.c | 424 +++++ plugins/kaitai/records/list.h | 71 + plugins/kaitai/records/value-int.h | 57 + plugins/kaitai/records/value.c | 336 ++++ plugins/kaitai/records/value.h | 65 + plugins/kaitai/scope.c | 257 +++ plugins/kaitai/scope.h | 72 + plugins/kaitai/stream-int.h | 55 + plugins/kaitai/stream.c | 237 +++ plugins/kaitai/stream.h | 65 + plugins/kaitai/tokens.l | 327 ++++ tests/plugins/kaitai.py | 2474 +++++++++++++++++++++++++++++ 102 files changed, 22666 insertions(+) create mode 100644 plugins/kaitai/Makefile.am create mode 100644 plugins/kaitai/array-int.h create mode 100644 plugins/kaitai/array.c create mode 100644 plugins/kaitai/array.h create mode 100644 plugins/kaitai/core.c create mode 100644 plugins/kaitai/core.h create mode 100644 plugins/kaitai/expression.h create mode 100644 plugins/kaitai/grammar.y create mode 100644 plugins/kaitai/parser-int.h create mode 100644 plugins/kaitai/parser.c create mode 100644 plugins/kaitai/parser.h create mode 100644 plugins/kaitai/parsers/Makefile.am create mode 100644 plugins/kaitai/parsers/attribute-int.h create mode 100644 plugins/kaitai/parsers/attribute.c create mode 100644 plugins/kaitai/parsers/attribute.h create mode 100644 plugins/kaitai/parsers/enum-int.h create mode 100644 plugins/kaitai/parsers/enum.c create mode 100644 plugins/kaitai/parsers/enum.h create mode 100644 plugins/kaitai/parsers/instance-int.h create mode 100644 plugins/kaitai/parsers/instance.c create mode 100644 plugins/kaitai/parsers/instance.h create mode 100644 plugins/kaitai/parsers/meta-int.h create mode 100644 plugins/kaitai/parsers/meta.c create mode 100644 plugins/kaitai/parsers/meta.h create mode 100644 plugins/kaitai/parsers/struct-int.h create mode 100644 plugins/kaitai/parsers/struct.c create mode 100644 plugins/kaitai/parsers/struct.h create mode 100644 plugins/kaitai/parsers/switch-int.h create mode 100644 plugins/kaitai/parsers/switch.c create mode 100644 plugins/kaitai/parsers/switch.h create mode 100644 plugins/kaitai/parsers/type-int.h create mode 100644 plugins/kaitai/parsers/type.c create mode 100644 plugins/kaitai/parsers/type.h create mode 100644 plugins/kaitai/python/Makefile.am create mode 100644 plugins/kaitai/python/array.c create mode 100644 plugins/kaitai/python/array.h create mode 100644 plugins/kaitai/python/module.c create mode 100644 plugins/kaitai/python/module.h create mode 100644 plugins/kaitai/python/parser.c create mode 100644 plugins/kaitai/python/parser.h create mode 100644 plugins/kaitai/python/parsers/Makefile.am create mode 100644 plugins/kaitai/python/parsers/attribute.c create mode 100644 plugins/kaitai/python/parsers/attribute.h create mode 100644 plugins/kaitai/python/parsers/enum.c create mode 100644 plugins/kaitai/python/parsers/enum.h create mode 100644 plugins/kaitai/python/parsers/instance.c create mode 100644 plugins/kaitai/python/parsers/instance.h create mode 100644 plugins/kaitai/python/parsers/meta.c create mode 100644 plugins/kaitai/python/parsers/meta.h create mode 100644 plugins/kaitai/python/parsers/module.c create mode 100644 plugins/kaitai/python/parsers/module.h create mode 100644 plugins/kaitai/python/parsers/struct.c create mode 100644 plugins/kaitai/python/parsers/struct.h create mode 100644 plugins/kaitai/python/parsers/type.c create mode 100644 plugins/kaitai/python/parsers/type.h create mode 100644 plugins/kaitai/python/record.c create mode 100644 plugins/kaitai/python/record.h create mode 100644 plugins/kaitai/python/records/Makefile.am create mode 100644 plugins/kaitai/python/records/empty.c create mode 100644 plugins/kaitai/python/records/empty.h create mode 100644 plugins/kaitai/python/records/group.c create mode 100644 plugins/kaitai/python/records/group.h create mode 100644 plugins/kaitai/python/records/item.c create mode 100644 plugins/kaitai/python/records/item.h create mode 100644 plugins/kaitai/python/records/list.c create mode 100644 plugins/kaitai/python/records/list.h create mode 100644 plugins/kaitai/python/records/module.c create mode 100644 plugins/kaitai/python/records/module.h create mode 100644 plugins/kaitai/python/records/value.c create mode 100644 plugins/kaitai/python/records/value.h create mode 100644 plugins/kaitai/python/scope.c create mode 100644 plugins/kaitai/python/scope.h create mode 100644 plugins/kaitai/python/stream.c create mode 100644 plugins/kaitai/python/stream.h create mode 100644 plugins/kaitai/record-int.h create mode 100644 plugins/kaitai/record.c create mode 100644 plugins/kaitai/record.h create mode 100644 plugins/kaitai/records/Makefile.am create mode 100644 plugins/kaitai/records/empty-int.h create mode 100644 plugins/kaitai/records/empty.c create mode 100644 plugins/kaitai/records/empty.h create mode 100644 plugins/kaitai/records/group-int.h create mode 100644 plugins/kaitai/records/group.c create mode 100644 plugins/kaitai/records/group.h create mode 100644 plugins/kaitai/records/item-int.h create mode 100644 plugins/kaitai/records/item.c create mode 100644 plugins/kaitai/records/item.h create mode 100644 plugins/kaitai/records/list-int.h create mode 100644 plugins/kaitai/records/list.c create mode 100644 plugins/kaitai/records/list.h create mode 100644 plugins/kaitai/records/value-int.h create mode 100644 plugins/kaitai/records/value.c create mode 100644 plugins/kaitai/records/value.h create mode 100644 plugins/kaitai/scope.c create mode 100644 plugins/kaitai/scope.h create mode 100644 plugins/kaitai/stream-int.h create mode 100644 plugins/kaitai/stream.c create mode 100644 plugins/kaitai/stream.h create mode 100644 plugins/kaitai/tokens.l create mode 100644 tests/plugins/kaitai.py diff --git a/configure.ac b/configure.ac index f4fe90b..eb4693b 100644 --- a/configure.ac +++ b/configure.ac @@ -631,6 +631,12 @@ AC_CONFIG_FILES([Makefile plugins/java/Makefile plugins/javadesc/Makefile plugins/javadesc/python/Makefile + plugins/kaitai/Makefile + plugins/kaitai/parsers/Makefile + plugins/kaitai/records/Makefile + plugins/kaitai/python/Makefile + plugins/kaitai/python/parsers/Makefile + plugins/kaitai/python/records/Makefile plugins/libcsem/Makefile plugins/lnxsyscalls/Makefile plugins/mobicore/Makefile diff --git a/plugins/Makefile.am b/plugins/Makefile.am index be0bd88..7ce4229 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -24,6 +24,7 @@ SUBDIRS = \ fmtp \ itanium \ javadesc \ + kaitai \ mobicore \ pe \ yaml \ diff --git a/plugins/kaitai/Makefile.am b/plugins/kaitai/Makefile.am new file mode 100644 index 0000000..cbc0f25 --- /dev/null +++ b/plugins/kaitai/Makefile.am @@ -0,0 +1,92 @@ + +BUILT_SOURCES = grammar.h + + +# On évite d'utiliser les variables personnalisées de type *_la_[YL]FLAGS +# afin de conserver des noms de fichiers simples, ie sans le nom de la +# bibliothèque de sortie en préfixe. + +AM_YFLAGS = -v -d -p kaitai_ -Wno-yacc -Wcounterexamples + +AM_LFLAGS = -P kaitai_ -o lex.yy.c --header-file=tokens.h \ + -Dyyget_lineno=kaitai_get_lineno \ + -Dyy_scan_bytes=kaitai__scan_bytes \ + -Dyy_delete_buffer=kaitai__delete_buffer + +lib_LTLIBRARIES = libkaitai.la + +libdir = $(pluginslibdir) + + +if BUILD_PYTHON_PACKAGE + +RUN_PATH = -Wl,-rpath,'$$ORIGIN/../chrysalide-libs' + +endif + +if BUILD_PYTHON3_BINDINGS + +PYTHON3_LIBADD = python/libkaitaipython.la + +if BUILD_DISCARD_LOCAL + +if BUILD_PYTHON_PACKAGE +PYTHON3_RUN_PATH = -Wl,-rpath,'$$ORIGIN/..' +else +PYTHON3_RUN_PATH = -Wl,-rpath,'$$ORIGIN' +endif + +else + +PYTHON3_RUN_PATH = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs + +endif + +PYTHON3_LDFLAGS = $(PYTHON3_RUN_PATH) -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so + +PYTHON3_SUBDIRS = python + +endif + + +libkaitai_la_SOURCES = \ + array-int.h \ + array.h array.c \ + core.h core.c \ + expression.h \ + parser-int.h \ + parser.h parser.c \ + record-int.h \ + record.h record.c \ + scope.h scope.c \ + stream-int.h \ + stream.h stream.c \ + tokens.l \ + grammar.y + +libkaitai_la_LIBADD = \ + parsers/libkaitaiparsers.la \ + records/libkaitairecords.la \ + $(PYTHON3_LIBADD) + +libkaitai_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src + +libkaitai_la_LDFLAGS = \ + -L$(top_srcdir)/src/.libs -lchrysacore \ + $(PYTHON3_LDFLAGS) + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libkaitai_la_SOURCES:%c=) + + +# Automake fait les choses à moitié +CLEANFILES = grammar.h grammar.c grammar.output tokens.c tokens.h + +# Pareil : de tous les fichiers générés, seule la sortie de Flex saute pour les distributions ! +EXTRA_DIST = tokens.h + + +# misc +SUBDIRS = parsers records $(PYTHON3_SUBDIRS) diff --git a/plugins/kaitai/array-int.h b/plugins/kaitai/array-int.h new file mode 100644 index 0000000..123b16d --- /dev/null +++ b/plugins/kaitai/array-int.h @@ -0,0 +1,51 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * array-int.h - prototypes pour les données associées à un flux de données 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 . + */ + + +#ifndef PLUGINS_KAITAI_ARRAY_INT_H +#define PLUGINS_KAITAI_ARRAY_INT_H + + +#include "array.h" + + + +/* Tableau rassemblant des éléments divers (instance) */ +struct _GKaitaiArray +{ + GObject parent; /* A laisser en premier */ + + resolved_value_t *items; /* Eléments du tableau */ + size_t count; /* Quantité de ces éléments */ + +}; + +/* Tableau rassemblant des éléments divers (classe) */ +struct _GKaitaiArrayClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + + +#endif /* PLUGINS_KAITAI_ARRAY_INT_H */ diff --git a/plugins/kaitai/array.c b/plugins/kaitai/array.c new file mode 100644 index 0000000..86f0856 --- /dev/null +++ b/plugins/kaitai/array.c @@ -0,0 +1,376 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * array.c - données associées à un flux de données 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 . + */ + + +#include "array.h" + + +#include +#include +#include +#include + + +#include "array-int.h" +#include "expression.h" + + + +/* Initialise la classe des flux de données pour Kaitai. */ +static void g_kaitai_array_class_init(GKaitaiArrayClass *); + +/* Initialise un flux de données accessibles à Kaitai. */ +static void g_kaitai_array_init(GKaitaiArray *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_array_dispose(GKaitaiArray *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_array_finalize(GKaitaiArray *); + +/* Détermine la taille de la séquence d'octets du tableau. */ +static bool g_kaitai_array_compute_bytes_length(const GKaitaiArray *, size_t *); + + + +/* Indique le type défini pour un tableau rassemblant des éléments Kaitai. */ +G_DEFINE_TYPE(GKaitaiArray, g_kaitai_array, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des tableau d'éléments Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_array_class_init(GKaitaiArrayClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_array_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_array_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : array = instance à initialiser. * +* * +* Description : Initialise un tableau rassemblant des éléments divers. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_array_init(GKaitaiArray *array) +{ + array->items = NULL; + array->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : array = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_array_dispose(GKaitaiArray *array) +{ + G_OBJECT_CLASS(g_kaitai_array_parent_class)->dispose(G_OBJECT(array)); + +} + + +/****************************************************************************** +* * +* Paramètres : array = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_array_finalize(GKaitaiArray *array) +{ + G_OBJECT_CLASS(g_kaitai_array_parent_class)->finalize(G_OBJECT(array)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Constitue une amorce de tableau pour rassembler des éléments.* +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiArray *g_kaitai_array_new(void) +{ + GKaitaiArray *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_KAITAI_ARRAY, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau Kaitai à consulter. * +* * +* Description : Dénombre le nombre d'éléments enregistrés. * +* * +* Retour : Taille du tableau manipulé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +size_t g_kaitai_array_count_items(const GKaitaiArray *array) +{ + size_t result; /* Quantité à retourner */ + + result = array->count; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau Kaitai à compléter. * +* item = élément Kaitai à archiver. * +* * +* Description : Intègre un élément supplémentaire dans un tableau Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_kaitai_array_append_item(GKaitaiArray *array, const resolved_value_t *item) +{ + array->items = realloc(array->items, ++array->count * sizeof(resolved_value_t)); + + COPY_RESOLVED_VALUE(array->items[array->count - 1], *item); + +} + + +/****************************************************************************** +* * +* Paramètres : list = ensemble de correspondances attribut/binaire. * +* index = indice de la correspondance visée. * +* item = élément archivé dans le talbeau à fournir. [OUT] * +* * +* Description : Fournit un élément ciblé dans un tableau Kaitai. * +* * +* Retour : Validité de l'emplacmeent pour élément à renseigner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_array_get_item(const GKaitaiArray *array, size_t index, resolved_value_t *item) +{ + bool result; /* Bilan à retourner */ + + result = (index < array->count); + + if (result) + COPY_RESOLVED_VALUE(*item, array->items[index]); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau Kaitai à consulter. * +* length = nombre d'octets représentés. [OUT] * +* * +* Description : Détermine la taille de la séquence d'octets du tableau. * +* * +* Retour : true si le tableau peut être converti en octets, ou false. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_kaitai_array_compute_bytes_length(const GKaitaiArray *array, size_t *length) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + const resolved_value_t *item; /* Elément en cours d'analyse */ + size_t extra; /* Taille d'un sous-tableau */ + + result = true; + + *length = 0; + + for (i = 0; i < array->count && result; i++) + { + item = &array->items[i]; + + switch (item->type) + { + case GVT_UNSIGNED_INTEGER: + result = (item->unsigned_integer <= UCHAR_MAX); + if (result) (*length)++; + break; + + case GVT_SIGNED_INTEGER: + result = (0 <= item->signed_integer && item->signed_integer <= SCHAR_MAX); + if (result) (*length)++; + break; + + case GVT_BYTES: + *length += item->bytes.len; + break; + + case GVT_ARRAY: + result = g_kaitai_array_compute_bytes_length(item->array, &extra); + if (result) *length += extra; + break; + + default: + result = false; + break; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau Kaitai à consulter. * +* bytes = conversion en série d'octets équivalent. [OUT] * +* * +* Description : Convertit un tableau d'éléments en séquence d'octets. * +* * +* Retour : true si une série d'octets a pu être constituée, ou false. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_array_convert_to_bytes(const GKaitaiArray *array, sized_string_t *bytes) +{ + bool result; /* Bilan à retourner */ + size_t length; /* Taille de la chaîne finale */ + size_t i; /* Boucle de parcours */ + const resolved_value_t *item; /* Elément en cours d'analyse */ + char *iter; /* Tête d'écriture */ + sized_string_t extra; /* Données d'un sous-tableau */ + + /* Détermination de la taille finale */ + + result = g_kaitai_array_compute_bytes_length(array, &length); + + /* Construction d'une chaîne d'octets si possible */ + + if (result) + { + bytes->data = malloc(length * sizeof(char)); + bytes->len = length; + + iter = bytes->data; + + for (i = 0; i < array->count; i++) + { + item = &array->items[i]; + + switch (item->type) + { + case GVT_UNSIGNED_INTEGER: + *iter = item->unsigned_integer; + iter++; + break; + + case GVT_SIGNED_INTEGER: + *iter = item->signed_integer; + iter++; + break; + + case GVT_BYTES: + memcpy(iter, item->bytes.data, item->bytes.len); + iter += item->bytes.len; + break; + + case GVT_ARRAY: + result = g_kaitai_array_convert_to_bytes(item->array, &extra); + assert(result); + + memcpy(iter, extra.data, extra.len); + iter += extra.len; + + exit_szstr(&extra); + break; + + default: + break; + + } + + } + + } + + return result; + +} diff --git a/plugins/kaitai/array.h b/plugins/kaitai/array.h new file mode 100644 index 0000000..e93c7c0 --- /dev/null +++ b/plugins/kaitai/array.h @@ -0,0 +1,76 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * array.h - prototypes pour les données associées à un flux de données 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 . + */ + + +#ifndef PLUGINS_KAITAI_ARRAY_H +#define PLUGINS_KAITAI_ARRAY_H + + +#include +#include + + +#include + + + +/* expression.h : informations transportées par une expression */ +typedef struct _resolved_value_t resolved_value_t; + + + +#define G_TYPE_KAITAI_ARRAY g_kaitai_array_get_type() +#define G_KAITAI_ARRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_ARRAY, GKaitaiArray)) +#define G_IS_KAITAI_ARRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_ARRAY)) +#define G_KAITAI_ARRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_ARRAY, GKaitaiArrayClass)) +#define G_IS_KAITAI_ARRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_ARRAY)) +#define G_KAITAI_ARRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_ARRAY, GKaitaiArrayClass)) + + +/* Tableau rassemblant des éléments divers (instance) */ +typedef struct _GKaitaiArray GKaitaiArray; + +/* Tableau rassemblant des éléments divers (classe) */ +typedef struct _GKaitaiArrayClass GKaitaiArrayClass; + + +/* Indique le type défini pour un tableau rassemblant des éléments Kaitai. */ +GType g_kaitai_array_get_type(void); + +/* Constitue une amorce de tableau pour rassembler des éléments. */ +GKaitaiArray *g_kaitai_array_new(void); + +/* Dénombre le nombre d'éléments enregistrés. */ +size_t g_kaitai_array_count_items(const GKaitaiArray *); + +/* Intègre un élément supplémentaire dans un tableau Kaitai. */ +void g_kaitai_array_append_item(GKaitaiArray *, const resolved_value_t *); + +/* Fournit un élément ciblé dans un tableau Kaitai. */ +bool g_kaitai_array_get_item(const GKaitaiArray *, size_t, resolved_value_t *); + +/* Convertit un tableau d'éléments en séquence d'octets. */ +bool g_kaitai_array_convert_to_bytes(const GKaitaiArray *, sized_string_t *); + + + +#endif /* PLUGINS_KAITAI_ARRAY_H */ diff --git a/plugins/kaitai/core.c b/plugins/kaitai/core.c new file mode 100644 index 0000000..65424a5 --- /dev/null +++ b/plugins/kaitai/core.c @@ -0,0 +1,79 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core.c - prise en charge des descriptions de binaires au format Kaitai + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "core.h" + + +#include + + +#ifdef HAVE_PYTHON3_BINDINGS +# include "python/module.h" +#endif + + +#ifdef HAVE_PYTHON3_BINDINGS +# define PG_REQ RL("PyChrysalide") +#else +# define PG_REQ NO_REQ +#endif + + + +DEFINE_CHRYSALIDE_PLUGIN("Kaitai", "Content parser using Kaitai structure definitions", + PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/kaitai"), + PG_REQ, AL(PGA_PLUGIN_INIT)); + + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte du chargement du greffon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) +{ + bool result; /* Bilan à retourner */ + + result = true; + +#ifdef HAVE_PYTHON3_BINDINGS + + if (result) + result = add_kaitai_module_to_python_module(); + + if (result) + result = populate_kaitai_module(); + +#endif + + return result; + +} diff --git a/plugins/kaitai/core.h b/plugins/kaitai/core.h new file mode 100644 index 0000000..61241f4 --- /dev/null +++ b/plugins/kaitai/core.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core.h - prototypes pour la prise en charge des descriptions de binaires au format 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 . + */ + + +#ifndef _PLUGINS_KAITAI_CORE_H +#define _PLUGINS_KAITAI_CORE_H + + +#include +#include + + + +/* Prend acte du chargement du greffon. */ +G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); + + + +#endif /* _PLUGINS_KAITAI_CORE_H */ diff --git a/plugins/kaitai/expression.h b/plugins/kaitai/expression.h new file mode 100644 index 0000000..06cf9cf --- /dev/null +++ b/plugins/kaitai/expression.h @@ -0,0 +1,134 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * expression.h - déclarations de prototypes utiles aux résolutions d'expressions + * + * Copyright (C) 2022 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#ifndef _PLUGINS_KAITAI_EXPRESSION_H +#define _PLUGINS_KAITAI_EXPRESSION_H + + +#include +#include + + +#include + + +#include "array.h" +#include "scope.h" +#include "stream.h" + + + +/* Type de valeur résolue */ +typedef enum _GrammarValueType +{ + GVT_ERROR, /* Erreur remontée */ + GVT_UNSIGNED_INTEGER, /* Valeur entière #1 */ + GVT_SIGNED_INTEGER, /* Valeur entière #2 */ + GVT_FLOAT, /* Valeur fractionnée */ + GVT_BOOLEAN, /* Valeur booléenne */ + GVT_BYTES, /* Série d'octets dynamique */ + GVT_ARRAY, /* Tableau d'éléments divers */ + GVT_RECORD, /* Correspondance en place */ + GVT_STREAM, /* Flux de données */ + +} GrammarValueType; + +/* Informations transportées par une expression */ +typedef struct _resolved_value_t +{ + GrammarValueType type; /* Type de valeur portée */ + + union + { + unsigned long long unsigned_integer;/* Valeur entière #1 */ + signed long long signed_integer; /* Valeur entière #2 */ + double floating_number; /* Valeur à virgule flottante */ + bool status; /* Valeur à deux états */ + sized_string_t bytes; /* Série d'octets */ + + GKaitaiArray *array; /* Tableau d'éléments divers */ + GMatchRecord *record; /* Correspondance désignée */ + GKaitaiStream *stream; /* Flux de données pour Kaitai */ + + }; + +} resolved_value_t; + + +#define COPY_RESOLVED_VALUE(dst, src) \ + do \ + { \ + (dst) = (src); \ + switch ((dst).type) \ + { \ + case GVT_ARRAY: \ + g_object_ref(G_OBJECT((dst).array)); \ + break; \ + case GVT_RECORD: \ + g_object_ref(G_OBJECT((dst).record)); \ + break; \ + case GVT_STREAM: \ + g_object_ref(G_OBJECT((dst).stream)); \ + break; \ + default: \ + break; \ + } \ + } \ + while (0) + + +#define EXIT_RESOLVED_VALUE(v) \ + switch ((v).type) \ + { \ + case GVT_ARRAY: \ + g_clear_object(&(v).array); \ + break; \ + case GVT_RECORD: \ + g_clear_object(&(v).record); \ + break; \ + case GVT_STREAM: \ + g_clear_object(&(v).stream); \ + break; \ + default: \ + break; \ + } + + +/* Interprète une expression en une valeur quelconque. */ +bool resolve_kaitai_expression_as_any(const kaitai_scope_t *, const char *, size_t, resolved_value_t *); + +/* Interprète une expression en valeur ciblée entière. */ +bool resolve_kaitai_expression_as_integer(const kaitai_scope_t *, const char *, size_t, resolved_value_t *); + +/* Interprète une expression en valeur ciblée booléenne. */ +bool resolve_kaitai_expression_as_boolean(const kaitai_scope_t *, const char *, size_t, resolved_value_t *); + +/* Interprète une expression en série d'octets. */ +bool resolve_kaitai_expression_as_bytes(const kaitai_scope_t *, const char *, size_t, resolved_value_t *); + +/* Interprète une expression en flux de données pour Kaitai. */ +bool resolve_kaitai_expression_as_stream(const kaitai_scope_t *, const char *, size_t, GKaitaiStream **); + + + +#endif /* _PLUGINS_KAITAI_EXPRESSION_H */ diff --git a/plugins/kaitai/grammar.y b/plugins/kaitai/grammar.y new file mode 100644 index 0000000..9e31113 --- /dev/null +++ b/plugins/kaitai/grammar.y @@ -0,0 +1,1854 @@ + +%{ + +#include "expression.h" +#include "tokens.h" + + +/* Affiche un message d'erreur suite à l'analyse en échec. */ +static int yyerror(yyscan_t, const kaitai_scope_t *, resolved_value_t *, const char *); + +/* Interprète une expression en une valeur quelconque. */ +static bool _resolve_kaitai_expression_as_any(const kaitai_scope_t *, const char *, size_t, resolved_value_t *); + +/* Traduit les éventuels champs impliqués dans une expression. */ +static bool reduce_resolved_kaitai_expression(resolved_value_t *); + + +%} + + +%code requires { + +#define YY_TYPEDEF_YY_SCANNER_T +typedef void *yyscan_t; + +#include +#include +#include +#include +#include + +#include "expression.h" +#include "record.h" +#include "records/item.h" +#include "records/list.h" +#include "records/value.h" + +} + +%union { + + resolved_value_t value; /* Valeur portée */ + + unsigned long long unsigned_integer; /* Valeur entière #1 */ + signed long long signed_integer; /* Valeur entière #2 */ + double floating_number; /* Valeur à virgule flottante */ + sized_string_t sized_cstring; /* Chaîne de caractères */ + char byte; /* Octet unique */ + +} + + +/** + * Cf. + * http://stackoverflow.com/questions/34418381/how-to-reference-lex-or-parse-parameters-in-flex-rules/34420950 + */ + +%define api.pure full + +%parse-param { yyscan_t yyscanner } { const kaitai_scope_t *locals } { resolved_value_t *resolved } +%lex-param { yyscan_t yyscanner } + +%code provides { + +#define YY_DECL \ + int kaitai_lex(YYSTYPE *yylval_param, yyscan_t yyscanner) + +YY_DECL; + + + +#define SET_ERR(out) \ + out.type = GVT_ERROR + +#define EXIT_WITH_ERR(out, lbl) \ + do \ + { \ + SET_ERR(out); \ + goto exit_ ## lbl; \ + } \ + while (0) + +#define CHECK_TYPE(arg, tp, out, lbl) \ + if (arg.type != tp) EXIT_WITH_ERR(out, lbl) + +#define CHECK_TYPES(arg, tp1, tp2, out, lbl) \ + if (arg.type != tp1 && arg.type != tp2) EXIT_WITH_ERR(out, lbl) + +#define REDUCE_EXPR(arg, out, lbl) \ + if (!reduce_resolved_kaitai_expression(&arg)) \ + EXIT_WITH_ERR(out, lbl) + +#define REDUCE_NUMERIC_EXPR(arg, out, lbl) \ + if (!reduce_resolved_kaitai_expression(&arg)) \ + EXIT_WITH_ERR(out, lbl); \ + if (arg.type == GVT_SIGNED_INTEGER && arg.signed_integer >= 0) \ + { \ + arg.unsigned_integer = arg.signed_integer; \ + arg.type = GVT_UNSIGNED_INTEGER; \ + } + + +#define ARITHMETIC_ADD_CODE(op1, op2, out, lbl) \ + switch (op1.type) \ + { \ + case GVT_UNSIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.unsigned_integer = op1.unsigned_integer + op2.unsigned_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + if (op1.unsigned_integer < -op2.signed_integer) \ + { \ + out.signed_integer = op1.unsigned_integer + op2.signed_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else \ + { \ + out.unsigned_integer = op1.unsigned_integer + op2.signed_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.unsigned_integer + op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_SIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + if (-op1.signed_integer > op2.unsigned_integer) \ + { \ + out.signed_integer = op1.signed_integer + op2.unsigned_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else \ + { \ + out.unsigned_integer = op1.signed_integer + op2.unsigned_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.signed_integer = op1.signed_integer + op2.signed_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.signed_integer + op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_FLOAT: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.floating_number = op1.floating_number + op2.unsigned_integer; \ + out.type = GVT_FLOAT; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.floating_number = op1.floating_number + op2.signed_integer; \ + out.type = GVT_FLOAT; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.floating_number + op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + default: \ + EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + } + + +#define ARITHMETIC_SUB_CODE(op1, op2, out, lbl) \ + switch (op1.type) \ + { \ + case GVT_UNSIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + if (op1.unsigned_integer < op2.unsigned_integer) \ + { \ + out.signed_integer = op1.unsigned_integer - op2.unsigned_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else \ + { \ + out.unsigned_integer = op1.unsigned_integer - op2.unsigned_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.unsigned_integer = op1.unsigned_integer - op2.signed_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.unsigned_integer - op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_SIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.signed_integer = op1.signed_integer - op2.unsigned_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + if (op1.signed_integer < op2.signed_integer) \ + { \ + out.signed_integer = op1.signed_integer - op2.signed_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else \ + { \ + out.unsigned_integer = op1.signed_integer - op2.signed_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.signed_integer - op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_FLOAT: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.floating_number = op1.floating_number - op2.unsigned_integer; \ + out.type = GVT_FLOAT; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.floating_number = op1.floating_number - op2.signed_integer; \ + out.type = GVT_FLOAT; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.floating_number - op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + default: \ + EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + } + + +#define ARITHMETIC_GENOP_CODE(op1, op2, _meth_, out, lbl) \ + switch (op1.type) \ + { \ + case GVT_UNSIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.unsigned_integer = op1.unsigned_integer _meth_ op2.unsigned_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.signed_integer = op1.unsigned_integer _meth_ op2.signed_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.unsigned_integer _meth_ op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_SIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.unsigned_integer = op1.signed_integer _meth_ op2.unsigned_integer; \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.unsigned_integer = op1.signed_integer _meth_ op2.signed_integer; \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.signed_integer _meth_ op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_FLOAT: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.unsigned_integer = op1.floating_number _meth_ op2.unsigned_integer; \ + out.type = GVT_FLOAT; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.signed_integer = op1.floating_number _meth_ op2.signed_integer; \ + out.type = GVT_FLOAT; \ + } \ + else if (op2.type == GVT_FLOAT) \ + { \ + out.floating_number = op1.floating_number _meth_ op2.floating_number; \ + out.type = GVT_FLOAT; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + default: \ + EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + } + + +/** + * Cf. https://stackoverflow.com/questions/11720656/modulo-operation-with-negative-numbers/52529440#52529440 + */ +#define EUCLIDEAN_MODULO(a, b, r) \ + r = a % (signed long long)b; \ + if (r < 0) \ + r = (b < 0) ? r - b : r + b; \ + + +#define ARITHMETIC_MOD_CODE(op1, op2, out, lbl) \ + switch (op1.type) \ + { \ + case GVT_UNSIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + EUCLIDEAN_MODULO(op1.unsigned_integer, op2.unsigned_integer, out.unsigned_integer); \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + EUCLIDEAN_MODULO(op1.unsigned_integer, op2.signed_integer, out.signed_integer); \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_SIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + EUCLIDEAN_MODULO(op1.signed_integer, op2.unsigned_integer, out.signed_integer); \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + EUCLIDEAN_MODULO(op1.signed_integer, op2.signed_integer, out.signed_integer); \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + default: \ + EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + } + + +#define RELATIONAL_CODE(op1, op2, _meth_, out, lbl) \ + switch (op1.type) \ + { \ + case GVT_UNSIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.status = (op1.unsigned_integer _meth_ op2.unsigned_integer); \ + out.type = GVT_BOOLEAN; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.status = (op1.unsigned_integer _meth_ op2.signed_integer); \ + out.type = GVT_BOOLEAN; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_SIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.status = (op1.signed_integer _meth_ op2.unsigned_integer); \ + out.type = GVT_BOOLEAN; \ + } \ + else if (op2.type == GVT_SIGNED_INTEGER) \ + { \ + out.status = (op1.signed_integer _meth_ op2.signed_integer); \ + out.type = GVT_BOOLEAN; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_BYTES: \ + if (op2.type == GVT_BYTES) \ + { \ + int __ret; \ + __ret = szmemcmp(&op1.bytes, &op2.bytes); \ + out.status = (__ret _meth_ 0); \ + out.type = GVT_BOOLEAN; \ + } \ + else if (op2.type == GVT_ARRAY) \ + { \ + sized_string_t __abytes_2; \ + int __ret; \ + if (!g_kaitai_array_convert_to_bytes(op2.array, &__abytes_2)) \ + EXIT_WITH_ERR(out, lbl); \ + __ret = szmemcmp(&op1.bytes, &__abytes_2); \ + exit_szstr(&__abytes_2); \ + out.status = (__ret _meth_ 0); \ + out.type = GVT_BOOLEAN; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_ARRAY: \ + if (op2.type == GVT_BYTES) \ + { \ + sized_string_t __abytes_1; \ + int __ret; \ + if (!g_kaitai_array_convert_to_bytes(op1.array, &__abytes_1)) \ + EXIT_WITH_ERR(out, lbl); \ + __ret = szmemcmp(&__abytes_1, &op2.bytes); \ + exit_szstr(&__abytes_1); \ + out.status = (__ret _meth_ 0); \ + out.type = GVT_BOOLEAN; \ + } \ + else if (op2.type == GVT_ARRAY) \ + { \ + sized_string_t __abytes_1; \ + sized_string_t __abytes_2; \ + int __ret; \ + if (!g_kaitai_array_convert_to_bytes(op1.array, &__abytes_1)) \ + EXIT_WITH_ERR(out, lbl); \ + if (!g_kaitai_array_convert_to_bytes(op2.array, &__abytes_2)) \ + { \ + exit_szstr(&__abytes_1); \ + EXIT_WITH_ERR(out, lbl); \ + } \ + __ret = szmemcmp(&__abytes_1, &__abytes_2); \ + exit_szstr(&__abytes_1); \ + exit_szstr(&__abytes_2); \ + out.status = (__ret _meth_ 0); \ + out.type = GVT_BOOLEAN; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_STREAM: \ + if (op2.type == GVT_STREAM) \ + { \ + GBinContent *__cnt_1; \ + GBinContent *__cnt_2; \ + __cnt_1 = g_kaitai_stream_get_content(op1.stream); \ + __cnt_2 = g_kaitai_stream_get_content(op2.stream); \ + out.status = (__cnt_1 _meth_ __cnt_2); \ + out.type = GVT_BOOLEAN; \ + g_object_unref(G_OBJECT(__cnt_1)); \ + g_object_unref(G_OBJECT(__cnt_2)); \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + default: \ + EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + } + + +#define BITWISE_CODE(op1, op2, _meth_, out, lbl) \ + switch (op1.type) \ + { \ + case GVT_UNSIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.unsigned_integer = (op1.unsigned_integer _meth_ op2.unsigned_integer); \ + out.type = GVT_UNSIGNED_INTEGER; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + case GVT_SIGNED_INTEGER: \ + if (op2.type == GVT_UNSIGNED_INTEGER) \ + { \ + out.signed_integer = (op1.signed_integer _meth_ op2.unsigned_integer); \ + out.type = GVT_SIGNED_INTEGER; \ + } \ + else EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + default: \ + EXIT_WITH_ERR(out, lbl); \ + break; \ + \ + } + + + + +} + + +%token UNSIGNED_INTEGER +%token SIGNED_INTEGER +%token FLOAT + +%token IDENTIFIER +%token RAW_BYTES +%token RAW_BYTE +%token RAW_BYTES_WITH_ENDING_DOT +%token PLAIN_BYTES + +%token ENCODING_NAME + + +%token PLUS "+" +%token MINUS "-" +%token MUL "*" +%token DIV "/" +%token MOD "%" + +%token LT "<" +%token LE "<=" +%token EQ "==" +%token NE "!=" +%token GT ">" +%token GE ">=" + +%token SHIFT_LEFT "<<" +%token SHIFT_RIGHT ">>" +%token BIT_AND "&" +%token BIT_OR "|" +%token BIT_XOR "^" + +%token NOT "not" +%token AND "and" +%token OR "or" + +%token PAREN_O "(" +%token PAREN_C ")" +%token HOOK_O "[" +%token HOOK_C "]" +%token COMMA "," +%token DOT "." + +%token QMARK "?" +%token COLON ":" +%token DOUBLE_COLON "::" + +%token METH_SIZE ".size" +%token METH_LENGTH ".length" +%token METH_REVERSE ".reverse" +%token METH_SUBSTRING ".substring" +%token METH_TO_I ".to_i" +%token METH_TO_I_RAD ".to_i(" +%token METH_TO_S ".to_s" +%token METH_TO_S_ENC ".to_s(" + +%token ROOT "_root" +%token PARENT "_parent" +%token LAST "_" +%token METH_IO "._io" + +%token TRUE_CONST "true" +%token FALSE_CONST "false" + + + //%type operand +%type any_expr + //%type arithm_expr + //%type arithm_op + +%type boolean + + + +%type arithmetic_expr +%type relational_expr +%type logical_expr +%type bitwise_expr +%type ternary_expr + +%type convert_2_bytes +%type convert_2_integer + + +%type integer + +%type float + +%type bytes + +%type bytes_concat +%type raw_bytes + + +%type array +%type array_items + + +%type field +%type enumeration +%type stream + + + +%destructor { printf("----------------------freeing %p...\n", &$$), fflush(NULL); } <*> + + + //%type INTEGER + // + + //%type arithm_expr + //%type arithm_op + + //%type bool_expr + //%type relational_op logical_op ternary_op + + + //%type constant + + + + + +/** + * Cf. https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence + */ + + + + + +%left "?" ":" + +%left OR +%left "and" + +/* 13 */ +%left "|" + +/* 12 */ +%left "^" + +/* 11 */ +%left "&" + +%left LT LE EQ NE GT GE + +/* 7 */ +%left "<<" ">>" + + +%right NOT + +%left PLUS MINUS +%left "*" +%left DIV MOD + + +%left "[" + + + + +%left ".size" +%left ".length" +%left ".reverse" +%left ".substring" +%left ".to_i" +%left ".to_i(" +%left ".to_s" +%left ".to_s(" + +%left "._io" + +%left "." + +/* 1 */ +%right "::" + + +%% + + expressions : any_expr { *resolved = $1; } + ; + + any_expr : boolean { $$ = $1; } + | bytes { $$ = $1; } + | integer { $$ = $1; } + | float { $$ = $1; } + | array { $$ = $1; } + | field { $$ = $1; } + | enumeration { $$ = $1; } + | stream { $$ = $1; } + | arithmetic_expr { $$ = $1; } + | relational_expr { $$ = $1; } + | logical_expr { $$ = $1; } + | bitwise_expr { $$ = $1; } + | ternary_expr { $$ = $1; } + | convert_2_bytes { $$ = $1; } + | convert_2_integer { $$ = $1; } + | "(" any_expr ")" { $$ = $2; } + ; + + +/* Expressions impliquants formules et opérandes */ + + arithmetic_expr : any_expr "+" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_plus); + REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_plus); + + if ($1.type == GVT_BYTES && $3.type == GVT_BYTES) + { + $$.bytes.len = $1.bytes.len + $3.bytes.len; + $$.bytes.data = malloc($$.bytes.len); + + memcpy($$.bytes.data, $1.bytes.data, $1.bytes.len); + memcpy($$.bytes.data + $1.bytes.len, $3.bytes.data, $3.bytes.len); + + $$.type = GVT_BYTES; + + } + + else if ($1.type == GVT_BYTES && $3.type == GVT_ARRAY) + { + sized_string_t __abytes_2; + + if (!g_kaitai_array_convert_to_bytes($3.array, &__abytes_2)) + EXIT_WITH_ERR($$, arithmetic_expr_plus); + + $$.bytes.len = $1.bytes.len + __abytes_2.len; + $$.bytes.data = malloc($$.bytes.len); + + memcpy($$.bytes.data, $1.bytes.data, $1.bytes.len); + memcpy($$.bytes.data + $1.bytes.len, __abytes_2.data, __abytes_2.len); + + $$.type = GVT_BYTES; + + exit_szstr(&__abytes_2); + + } + + else + { + ARITHMETIC_ADD_CODE($1, $3, $$, arithmetic_expr_plus); + } + + exit_arithmetic_expr_plus: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "-" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_minus); + REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_minus); + ARITHMETIC_SUB_CODE($1, $3, $$, arithmetic_expr_minus); + exit_arithmetic_expr_minus: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "*" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_mul); + REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_mul); + ARITHMETIC_GENOP_CODE($1, $3, *, $$, arithmetic_expr_mul); + exit_arithmetic_expr_mul: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "/" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_div); + REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_div); + ARITHMETIC_GENOP_CODE($1, $3, /, $$, arithmetic_expr_div); + exit_arithmetic_expr_div: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "%" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_mod); + REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_mod); + ARITHMETIC_MOD_CODE($1, $3, $$, arithmetic_expr_mod); + exit_arithmetic_expr_mod: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + ; + + + relational_expr : any_expr "<" any_expr + { + REDUCE_EXPR($1, $$, relational_expr_lt); + REDUCE_EXPR($3, $$, relational_expr_lt); + RELATIONAL_CODE($1, $3, <, $$, relational_expr_lt); + exit_relational_expr_lt: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "<=" any_expr + { + REDUCE_EXPR($1, $$, relational_expr_le); + REDUCE_EXPR($3, $$, relational_expr_le); + RELATIONAL_CODE($1, $3, <=, $$, relational_expr_le); + exit_relational_expr_le: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "==" any_expr + { + REDUCE_EXPR($1, $$, relational_expr_eq); + REDUCE_EXPR($3, $$, relational_expr_eq); + RELATIONAL_CODE($1, $3, ==, $$, relational_expr_eq); + exit_relational_expr_eq: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "!=" any_expr + { + REDUCE_EXPR($1, $$, relational_expr_ne); + REDUCE_EXPR($3, $$, relational_expr_ne); + RELATIONAL_CODE($1, $3, !=, $$, relational_expr_ne); + exit_relational_expr_ne: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr ">" any_expr + { + REDUCE_EXPR($1, $$, relational_expr_gt); + REDUCE_EXPR($3, $$, relational_expr_gt); + RELATIONAL_CODE($1, $3, >, $$, relational_expr_gt); + exit_relational_expr_gt: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr ">=" any_expr + { + REDUCE_EXPR($1, $$, relational_expr_ge); + REDUCE_EXPR($3, $$, relational_expr_ge); + RELATIONAL_CODE($1, $3, >=, $$, relational_expr_ge); + exit_relational_expr_ge: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + ; + + + bitwise_expr : any_expr "<<" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_shift_left); + REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_shift_left); + BITWISE_CODE($1, $3, <<, $$, bitwise_expr_shift_left); + exit_bitwise_expr_shift_left: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr ">>" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_shift_right); + REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_shift_right); + BITWISE_CODE($1, $3, >>, $$, bitwise_expr_shift_right); + exit_bitwise_expr_shift_right: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "&" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_and); + REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_and); + BITWISE_CODE($1, $3, &, $$, bitwise_expr_and); + exit_bitwise_expr_and: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "|" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_or); + REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_or); + BITWISE_CODE($1, $3, |, $$, bitwise_expr_or); + exit_bitwise_expr_or: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "^" any_expr + { + REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_xor); + REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_xor); + BITWISE_CODE($1, $3, ^, $$, bitwise_expr_xor); + exit_bitwise_expr_xor: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + ; + + + logical_expr : "not" any_expr + { + REDUCE_EXPR($2, $$, logical_expr_not); + CHECK_TYPE($2, GVT_BOOLEAN, $$, logical_expr_not); + $$.status = !$2.status; + $$.type = GVT_BOOLEAN; + exit_logical_expr_not: + EXIT_RESOLVED_VALUE($2); + } + | any_expr "and" any_expr + { + REDUCE_EXPR($1, $$, logical_expr_and); + CHECK_TYPE($1, GVT_BOOLEAN, $$, logical_expr_and); + REDUCE_EXPR($3, $$, logical_expr_and); + CHECK_TYPE($3, GVT_BOOLEAN, $$, logical_expr_and); + $$.status = $1.status && $3.status; + $$.type = GVT_BOOLEAN; + exit_logical_expr_and: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + | any_expr "or" any_expr + { + REDUCE_EXPR($1, $$, logical_expr_or); + CHECK_TYPE($1, GVT_BOOLEAN, $$, logical_expr_or); + REDUCE_EXPR($3, $$, logical_expr_or); + CHECK_TYPE($3, GVT_BOOLEAN, $$, logical_expr_or); + $$.status = $1.status || $3.status; + $$.type = GVT_BOOLEAN; + exit_logical_expr_or: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + ; + + + ternary_expr : any_expr "?" any_expr ":" any_expr + { + REDUCE_EXPR($1, $$, ternary_expr); + CHECK_TYPE($1, GVT_BOOLEAN, $$, ternary_expr); + if ($1.status) + COPY_RESOLVED_VALUE($$, $3); + else + COPY_RESOLVED_VALUE($$, $5); + exit_ternary_expr: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + EXIT_RESOLVED_VALUE($5); + } + ; + + +/* Conversions et méthodes particulières de types */ + + convert_2_bytes : any_expr ".to_s" + { + int __ret; + + if ($1.type == GVT_UNSIGNED_INTEGER) + { + __ret = asprintf(&$$.bytes.data, "%llu", $1.unsigned_integer); + if (__ret == -1) EXIT_WITH_ERR($$, convert_2_bytes_to_s); + + $$.bytes.len = __ret; + $$.type = GVT_BYTES; + + } + else if ($1.type == GVT_SIGNED_INTEGER) + { + __ret = asprintf(&$$.bytes.data, "%lld", $1.signed_integer); + if (__ret == -1) EXIT_WITH_ERR($$, convert_2_bytes_to_s); + + $$.bytes.len = __ret; + $$.type = GVT_BYTES; + + } + else + EXIT_WITH_ERR($$, convert_2_bytes_to_s); + + exit_convert_2_bytes_to_s: + EXIT_RESOLVED_VALUE($1); + } + | any_expr ".to_s(" ENCODING_NAME ")" + { + /** + * Cf. https://fossies.org/linux/libiconv/man/iconv_open.3.html + */ + + char *__fromcode; + gsize __bytes_read; + gsize __bytes_written; + + if ($1.type != GVT_BYTES) + EXIT_WITH_ERR($$, convert_2_bytes_to_s_encoding); + + __fromcode = strndup($3.data, $3.len); + + $$.bytes.data = g_convert($1.bytes.data, $1.bytes.len, + __fromcode, "", &__bytes_read, &__bytes_written, NULL); + + free(__fromcode); + + if (__bytes_read != $1.bytes.len) + EXIT_WITH_ERR($$, convert_2_bytes_to_s_encoding); + + $$.bytes.len = __bytes_written; + $$.type = GVT_BYTES; + + exit_convert_2_bytes_to_s_encoding: + EXIT_RESOLVED_VALUE($1); + } + ; + + + convert_2_integer : any_expr ".length" + { + if ($1.type != GVT_BYTES) + EXIT_WITH_ERR($$, convert_2_integer_to_s); + + $$.unsigned_integer = $1.bytes.len; + $$.type = GVT_UNSIGNED_INTEGER; + + exit_convert_2_integer_to_s: + EXIT_RESOLVED_VALUE($1); + } + | any_expr ".to_i" + { + char *__n; + char *__end; + + if ($1.type == GVT_FLOAT) + { + if ($1.floating_number < 0) + { + $$.signed_integer = $1.floating_number; + $$.type = GVT_SIGNED_INTEGER; + } + else + { + $$.unsigned_integer = $1.floating_number; + $$.type = GVT_UNSIGNED_INTEGER; + } + + } + + else if ($1.type == GVT_BOOLEAN) + { + $$.unsigned_integer = $1.status ? 1 : 0; + $$.type = GVT_UNSIGNED_INTEGER; + } + + else if ($1.type == GVT_BYTES) + { + if ($1.bytes.len == 0) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + __n = malloc($1.bytes.len + 1); + memcpy(__n, $1.bytes.data, $1.bytes.len); + __n[$1.bytes.len] = '\0'; + + if ($1.bytes.data[0] == '-') + { + if ($1.bytes.len == 1) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + $$.signed_integer = strtoll(__n, &__end, 10); + $$.type = GVT_SIGNED_INTEGER; + + if (errno == EINVAL || errno == ERANGE) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + if (__end != &__n[$1.bytes.len]) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + } + else + { + if ($1.bytes.len == 1) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + $$.unsigned_integer = strtoull(__n, &__end, 10); + $$.type = GVT_UNSIGNED_INTEGER; + + if (errno == EINVAL || errno == ERANGE) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + if (__end != &__n[$1.bytes.len]) + EXIT_WITH_ERR($$, convert_2_integer_to_i); + + } + + free(__n); + + } + + else EXIT_WITH_ERR($$, convert_2_integer_to_i); + + exit_convert_2_integer_to_i: + EXIT_RESOLVED_VALUE($1); + } + | any_expr ".to_i(" any_expr ")" + { + int __base; + char *__n; + char *__end; + + if ($1.type == GVT_BYTES) + { + if ($1.bytes.len == 0) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + if ($3.type == GVT_UNSIGNED_INTEGER) + { + __base = $3.unsigned_integer; + if (__base < 2) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + } + else if ($3.type == GVT_SIGNED_INTEGER) + { + __base = $3.signed_integer; + if (__base < 2) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + } + else EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + __n = malloc($1.bytes.len + 1); + memcpy(__n, $1.bytes.data, $1.bytes.len); + __n[$1.bytes.len] = '\0'; + + if ($1.bytes.data[0] == '-') + { + if ($1.bytes.len == 1) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + $$.signed_integer = strtoll(__n, &__end, __base); + $$.type = GVT_SIGNED_INTEGER; + + if (errno == EINVAL || errno == ERANGE) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + if (__end != &__n[$1.bytes.len]) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + } + else + { + if ($1.bytes.len == 1) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + $$.unsigned_integer = strtoull(__n, &__end, __base); + $$.type = GVT_UNSIGNED_INTEGER; + + if (errno == EINVAL || errno == ERANGE) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + if (__end != &__n[$1.bytes.len]) + EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + } + + free(__n); + + } + + else EXIT_WITH_ERR($$, convert_2_integer_to_i_base); + + exit_convert_2_integer_to_i_base: + EXIT_RESOLVED_VALUE($1); + } + | any_expr ".size" + { + GRecordList *__list; + + if ($1.type != GVT_RECORD) EXIT_WITH_ERR($$, convert_2_integer_size); + if (!G_IS_RECORD_LIST($1.record)) EXIT_WITH_ERR($$, convert_2_integer_size); + + __list = G_RECORD_LIST($1.record); + + $$.unsigned_integer = g_record_list_count_records(__list); + $$.type = GVT_UNSIGNED_INTEGER; + + exit_convert_2_integer_size: + EXIT_RESOLVED_VALUE($1); + } + ; + + +/* Types de base */ + + boolean : "true" + { + $$.status = true; + $$.type = GVT_BOOLEAN; + } + | "false" + { + $$.status = false; + $$.type = GVT_BOOLEAN; + } + ; + + + integer : UNSIGNED_INTEGER + { + $$.unsigned_integer = $1; + $$.type = GVT_UNSIGNED_INTEGER; + } + | SIGNED_INTEGER + { + $$.signed_integer = $1; + $$.type = GVT_SIGNED_INTEGER; + } + ; + + + float : FLOAT + { + $$.floating_number = $1; + $$.type = GVT_FLOAT; + } + ; + + + bytes : bytes_concat { $$ = $1; } + | PLAIN_BYTES + { + $$.bytes.len = $1.len; + $$.bytes.data = malloc($1.len); + memcpy($$.bytes.data, $1.data, $1.len); + $$.type = GVT_BYTES; + } + | any_expr ".reverse" + { + size_t __i; + + CHECK_TYPE($1, GVT_BYTES, $$, bytes_reverse); + + $$.bytes.data = malloc($1.bytes.len); + $$.bytes.len = $1.bytes.len; + + for (__i = 0; __i < $1.bytes.len; __i++) + $$.bytes.data[__i] = $1.bytes.data[$1.bytes.len - __i - 1]; + + $$.type = GVT_BYTES; + + exit_bytes_reverse: + EXIT_RESOLVED_VALUE($1); + } + | any_expr ".substring" "(" any_expr "," any_expr ")" + { + unsigned long long __from; + unsigned long long __to; + + REDUCE_NUMERIC_EXPR($4, $$, bytes_reverse); + CHECK_TYPES($4, GVT_UNSIGNED_INTEGER, GVT_SIGNED_INTEGER, $$, bytes_substring); + REDUCE_NUMERIC_EXPR($6, $$, bytes_reverse); + CHECK_TYPES($6, GVT_UNSIGNED_INTEGER, GVT_SIGNED_INTEGER, $$, bytes_substring); + + __from = ($4.type == GVT_UNSIGNED_INTEGER ? $4.unsigned_integer : $4.signed_integer); + __to = ($6.type == GVT_UNSIGNED_INTEGER ? $6.unsigned_integer : $6.signed_integer); + + if (__from > __to) EXIT_WITH_ERR($$, bytes_substring); + if (__to >= $1.bytes.len) EXIT_WITH_ERR($$, bytes_substring); + + $$.bytes.len = __to - __from + 1; + $$.bytes.data = malloc($$.bytes.len); + + memcpy($$.bytes.data, &$1.bytes.data[__from], $$.bytes.len); + + $$.type = GVT_BYTES; + + exit_bytes_substring: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($4); + EXIT_RESOLVED_VALUE($6); + } + ; + + bytes_concat : raw_bytes { $$ = $1; }; + | bytes_concat raw_bytes + { + $$.bytes.len = $1.bytes.len + $2.bytes.len; + $$.bytes.data = malloc($$.bytes.len); + memcpy($$.bytes.data, $1.bytes.data, $1.bytes.len); + memcpy($$.bytes.data + $1.bytes.len, $2.bytes.data, $2.bytes.len); + $$.type = GVT_BYTES; + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($2); + } + ; + + raw_bytes : RAW_BYTES + { + $$.bytes.len = $1.len; + $$.bytes.data = malloc($1.len); + memcpy($$.bytes.data, $1.data, $1.len); + $$.type = GVT_BYTES; + } + | RAW_BYTE + { + $$.bytes.len = 1; + $$.bytes.data = malloc(1); + $$.bytes.data[0] = $1; + $$.type = GVT_BYTES; + } + | RAW_BYTES_WITH_ENDING_DOT + { + $$.bytes.len = $1.len; + $$.bytes.data = malloc($1.len); + memcpy($$.bytes.data, $1.data, $1.len); + $$.type = GVT_BYTES; + } + ; + + +/* Tableau d'éléments variés */ + + array : "[" "]" + { + $$.array = g_kaitai_array_new(); + $$.type = GVT_ARRAY; + } + | "[" array_items "]" + { + $$ = $2; + } + ; + + + array_items : any_expr + { + $$.array = g_kaitai_array_new(); + $$.type = GVT_ARRAY; + + g_kaitai_array_append_item($$.array, &$1); + + EXIT_RESOLVED_VALUE($1); + + } + | array_items "," any_expr + { + $$ = $1; + g_kaitai_array_append_item($$.array, &$3); + EXIT_RESOLVED_VALUE($3); + } + ; + + +/* Accès aux objets Kaitai manipulés */ + + field : IDENTIFIER + { + $$.record = g_match_record_find_by_name(locals->parent, + $1.data, $1.len, + DIRECT_SEARCH_DEEP_LEVEL); + + if ($$.record != NULL) + $$.type = GVT_RECORD; + + /* Si aucune correspondance, le contenu brut est utilisé */ + else + { + $$.bytes.len = $1.len; + $$.bytes.data = malloc($1.len); + memcpy($$.bytes.data, $1.data, $1.len); + $$.type = GVT_BYTES; + } + + } + | "_root" + { + $$.record = get_root_record(locals); + if ($$.record == NULL) SET_ERR($$); + else $$.type = GVT_RECORD; + } + | "_parent" + { + $$.record = get_parent_record(locals); + if ($$.record == NULL) SET_ERR($$); + else $$.type = GVT_RECORD; + } + | "_" + { + $$.record = get_last_record(locals); + if ($$.record == NULL) SET_ERR($$); + else $$.type = GVT_RECORD; + } + | any_expr "." IDENTIFIER + { + if ($1.type != GVT_RECORD) + EXIT_WITH_ERR($$, field_dot); + + $$.record = g_match_record_find_by_name($1.record, + $3.data, $3.len, + DIRECT_SEARCH_DEEP_LEVEL); + + if ($$.record == NULL) + EXIT_WITH_ERR($$, field_dot); + + $$.type = GVT_RECORD; + + exit_field_dot: + EXIT_RESOLVED_VALUE($1); + } + | any_expr "[" any_expr "]" + { + size_t __index; + GRecordList *__list; + size_t __count; + GKaitaiArray *__array; + + /* Indice de l'élément auquel accéder */ + + REDUCE_NUMERIC_EXPR($3, $$, field_indexed); + + if ($3.type == GVT_UNSIGNED_INTEGER) + __index = $3.unsigned_integer; + else + EXIT_WITH_ERR($$, field_indexed); + + /* Série à consulter */ + + REDUCE_EXPR($1, $$, field_indexed); + + if ($1.type == GVT_RECORD && G_IS_RECORD_LIST($1.record)) + { + __list = G_RECORD_LIST($1.record); + __count = g_record_list_count_records(__list); + + if (__index >= __count) + EXIT_WITH_ERR($$, field_indexed); + + $$.record = g_record_list_get_record(__list, __index); + + if ($$.record == NULL) + EXIT_WITH_ERR($$, field_indexed); + + $$.type = GVT_RECORD; + + } + + else if ($1.type == GVT_ARRAY) + { + __array = G_KAITAI_ARRAY($1.array); + __count = g_kaitai_array_count_items(__array); + + if (__index >= __count) + EXIT_WITH_ERR($$, field_indexed); + + if (!g_kaitai_array_get_item(__array, __index, &$$)) + EXIT_WITH_ERR($$, field_indexed); + + } + + else + EXIT_WITH_ERR($$, field_indexed); + + exit_field_indexed: + EXIT_RESOLVED_VALUE($1); + EXIT_RESOLVED_VALUE($3); + } + ; + + + enumeration : IDENTIFIER "::" IDENTIFIER + { + if (!g_match_record_resolve_enum(locals->parent, &$1, &$3, &$$)) + SET_ERR($$); + } + + + stream : any_expr "._io" + { + GBinContent *__content; + mrange_t __range; + + if ($1.type != GVT_RECORD) + EXIT_WITH_ERR($$, stream_io); + + __content = g_match_record_get_content($1.record); + g_match_record_get_range($1.record, &__range); + + $$.stream = g_kaitai_stream_new(__content, get_mrange_addr(&__range)); + $$.type = GVT_STREAM; + + g_object_unref(G_OBJECT(__content)); + + exit_stream_io: + EXIT_RESOLVED_VALUE($1); + } + ; + + +%% + + +/****************************************************************************** +* * +* Paramètres : yyscanner = décodeur impliqué dans le processus. * +* locals = variables locales pour les résolutions de types. * +* out = valeur entière résultante. [OUT] * +* msg = message d'erreur. * +* * +* Description : Affiche un message d'erreur suite à l'analyse en échec. * +* * +* Retour : 0 * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int yyerror(yyscan_t yyscanner, const kaitai_scope_t *locals, resolved_value_t *resolved, const char *msg) +{ + printf("YYERROR line %d: %s\n", yyget_lineno(yyscanner), msg); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* out = valeur générique résultante. [OUT] * +* * +* Description : Interprète une expression en une valeur quelconque. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool _resolve_kaitai_expression_as_any(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out) +{ + bool result; /* Bilan à renvoyer */ + yyscan_t lexstate; /* Gestion d'analyse lexicale */ + char *real_text; /* Zone de travail effective */ + size_t real_length; /* Taille associée */ + YY_BUFFER_STATE state; /* Contexte d'analyse */ + int status; /* Bilan d'une analyse */ + + result = false; + + kaitai_lex_init(&lexstate); + + assert(length > 0); + + if (text[length - 1] == '.') + { + /** + * Si le contenu à analyser se termine par un point, la position finale + * de ce point est prise en compte. Pour ce faire, le marqueur "$" des + * expressions régulières est sollicité. Hors, ce dernier n'est reconnu + * que pour le caractère "\n" terminant une ligne. + * + * On l'ajoute donc artificiellement. + */ + + real_length = length + 1; + + real_text = malloc(real_length); + memcpy(real_text, text, length); + real_text[length] = '\n'; + + } + else + { + real_text = (char *)text; + real_length = length; + } + + state = kaitai__scan_bytes(real_text, real_length, lexstate); + + if (text[length - 1] == '.') + free(real_text); + + status = yyparse(lexstate, locals, out); + + result = (status == EXIT_SUCCESS); + + yy_delete_buffer(state, lexstate); + + kaitai_lex_destroy(lexstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* out = valeur générique résultante. [OUT] * +* * +* Description : Interprète une expression en une valeur quelconque. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool resolve_kaitai_expression_as_any(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out) +{ + bool result; /* Bilan à renvoyer */ + + result = _resolve_kaitai_expression_as_any(locals, text, length, out); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : in_out = expression résolue traitée. [OUT] * +* * +* Description : Traduit les éventuels champs impliqués dans une expression. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool reduce_resolved_kaitai_expression(resolved_value_t *in_out) +{ + bool result; /* Bilan à renvoyer */ + resolved_value_t deeper; /* Précision supplémentaire */ + + result = true; + + while (result && in_out->type == GVT_RECORD) + { + if (G_IS_RECORD_VALUE(in_out->record)) + result = g_record_value_compute_value(G_RECORD_VALUE(in_out->record), &deeper); + + else if (G_IS_RECORD_ITEM(in_out->record)) + result = g_record_item_get_value(G_RECORD_ITEM(in_out->record), &deeper); + + else + break; + + if (result) + { + EXIT_RESOLVED_VALUE(*in_out); + *in_out = deeper; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* out = valeur entière résultante. [OUT] * +* * +* Description : Interprète une expression en valeur ciblée entière. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool resolve_kaitai_expression_as_integer(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out) +{ + bool result; /* Bilan à renvoyer */ + + result = _resolve_kaitai_expression_as_any(locals, text, length, out); + + if (result) + result = reduce_resolved_kaitai_expression(out); + + if (result) + result = (out->type == GVT_UNSIGNED_INTEGER || out->type == GVT_SIGNED_INTEGER); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* out = valeur booléenne résultante. [OUT] * +* * +* Description : Interprète une expression en valeur ciblée booléenne. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool resolve_kaitai_expression_as_boolean(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out) +{ + bool result; /* Bilan à renvoyer */ + + result = _resolve_kaitai_expression_as_any(locals, text, length, out); + + if (result) + result = reduce_resolved_kaitai_expression(out); + + if (result && out->type != GVT_BOOLEAN) + { + EXIT_RESOLVED_VALUE(*out); + result = false; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* out = valeur booléenne résultante. [OUT] * +* * +* Description : Interprète une expression en série d'octets. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool resolve_kaitai_expression_as_bytes(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out) +{ + bool result; /* Bilan à renvoyer */ + char ch; /* Caractère unique spécifié */ + sized_string_t converted; /* Conversion finale ? */ + + result = _resolve_kaitai_expression_as_any(locals, text, length, out); + + if (result) + result = reduce_resolved_kaitai_expression(out); + + if (result) + { + if (out->type == GVT_UNSIGNED_INTEGER) + { + ch = out->unsigned_integer; + result = (ch <= 0xff); + + if (result) + { + EXIT_RESOLVED_VALUE(*out); + + out->bytes.data = malloc(sizeof(char)); + out->bytes.data[0] = ch; + out->bytes.len = 1; + out->type = GVT_BYTES; + + } + + } + + else if (out->type == GVT_ARRAY) + { + result = g_kaitai_array_convert_to_bytes(out->array, &converted); + + if (result) + { + EXIT_RESOLVED_VALUE(*out); + + out->bytes = converted; + out->type = GVT_BYTES; + + } + + } + + } + + if (result && out->type != GVT_BYTES) + { + EXIT_RESOLVED_VALUE(*out); + result = false; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* stream = flux de données pour Kaitai résultant. [OUT] * +* * +* Description : Interprète une expression en flux de données pour Kaitai. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool resolve_kaitai_expression_as_stream(const kaitai_scope_t *locals, const char *text, size_t length, GKaitaiStream **stream) +{ + bool result; /* Bilan à renvoyer */ + resolved_value_t out; /* Elément générique obtenu */ + + result = _resolve_kaitai_expression_as_any(locals, text, length, &out); + + if (result) + { + assert(out.type == GVT_STREAM); + *stream = out.stream; + } + else + *stream = NULL; + + return result; + +} diff --git a/plugins/kaitai/parser-int.h b/plugins/kaitai/parser-int.h new file mode 100644 index 0000000..4ddb0f9 --- /dev/null +++ b/plugins/kaitai/parser-int.h @@ -0,0 +1,55 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * parser-int.h - prototypes pour les spécifications internes d'un lecteur 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 . + */ + + +#ifndef PLUGINS_KAITAI_PARSER_INT_H +#define PLUGINS_KAITAI_PARSER_INT_H + + +#include "parser.h" + + + +/* Parcourt un contenu binaire selon des spécifications Kaitai. */ +typedef bool (* parse_kaitai_fc) (GKaitaiParser *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **); + + + +/* Spécification d'un lecteur Kaitai (instance) */ +struct _GKaitaiParser +{ + GObject parent; /* A laisser en premier */ + +}; + +/* Spécification d'un lecteur Kaitai (classe) */ +struct _GKaitaiParserClass +{ + GObjectClass parent; /* A laisser en premier */ + + parse_kaitai_fc parse; /* Phase d'analyse de contenu */ + +}; + + + +#endif /* PLUGINS_KAITAI_PARSER_INT_H */ diff --git a/plugins/kaitai/parser.c b/plugins/kaitai/parser.c new file mode 100644 index 0000000..77c15b3 --- /dev/null +++ b/plugins/kaitai/parser.c @@ -0,0 +1,159 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * parser.c - spécification d'un lecteur Kaitai + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "parser.h" + + +#include "parser-int.h" + + + +/* Initialise la classe des lecteurs de spécification Kaitai. */ +static void g_kaitai_parser_class_init(GKaitaiParserClass *); + +/* Initialise un lecteur de spécification Kaitai. */ +static void g_kaitai_parser_init(GKaitaiParser *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_parser_dispose(GKaitaiParser *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_parser_finalize(GKaitaiParser *); + + + +/* Indique le type défini pour un lecteur de spécification Kaitai. */ +G_DEFINE_TYPE(GKaitaiParser, g_kaitai_parser, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des lecteurs de spécification Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_parser_class_init(GKaitaiParserClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_parser_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_parser_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : parser = instance à initialiser. * +* * +* Description : Initialise un lecteur de spécification Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_parser_init(GKaitaiParser *parser) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : parser = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_parser_dispose(GKaitaiParser *parser) +{ + G_OBJECT_CLASS(g_kaitai_parser_parent_class)->dispose(G_OBJECT(parser)); + +} + + +/****************************************************************************** +* * +* Paramètres : parser = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_parser_finalize(GKaitaiParser *parser) +{ + G_OBJECT_CLASS(g_kaitai_parser_parent_class)->finalize(G_OBJECT(parser)); + +} + + +/****************************************************************************** +* * +* Paramètres : parser = 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 : - * +* * +******************************************************************************/ + +bool g_kaitai_parser_parse_content(GKaitaiParser *parser, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record) +{ + bool result; /* Bilan à retourner */ + GKaitaiParserClass *class; /* Classe de l'instance */ + + class = G_KAITAI_PARSER_GET_CLASS(parser); + + result = class->parse(parser, locals, content, pos, record); + + if (result) + remember_last_record(locals, *record); + + return result; + +} diff --git a/plugins/kaitai/parser.h b/plugins/kaitai/parser.h new file mode 100644 index 0000000..4c17087 --- /dev/null +++ b/plugins/kaitai/parser.h @@ -0,0 +1,63 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * parser.h - prototypes pour la spécification d'un lecteur 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 . + */ + + +#ifndef PLUGINS_KAITAI_PARSER_H +#define PLUGINS_KAITAI_PARSER_H + + +#include +#include + + +#include + + +#include "record.h" +#include "scope.h" + + + +#define G_TYPE_KAITAI_PARSER g_kaitai_parser_get_type() +#define G_KAITAI_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_PARSER, GKaitaiParser)) +#define G_IS_KAITAI_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_PARSER)) +#define G_KAITAI_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_PARSER, GKaitaiParserClass)) +#define G_IS_KAITAI_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_PARSER)) +#define G_KAITAI_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_PARSER, GKaitaiParserClass)) + + +/* Spécification d'un lecteur Kaitai (instance) */ +typedef struct _GKaitaiParser GKaitaiParser; + +/* Spécification d'un lecteur Kaitai (classe) */ +typedef struct _GKaitaiParserClass GKaitaiParserClass; + + +/* Indique le type défini pour un lecteur de spécification Kaitai. */ +GType g_kaitai_parser_get_type(void); + +/* Parcourt un contenu binaire selon des spécifications Kaitai. */ +bool g_kaitai_parser_parse_content(GKaitaiParser *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **); + + + +#endif /* PLUGINS_KAITAI_PARSER_H */ 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 . + */ + + +#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 . + */ + + +#include "attribute.h" + + +#include +#include + + +#include +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H +#define _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H + + +#include +#include + + +#include +#include +#include + + +#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 . + */ + + +#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 . + */ + + +#include "enum.h" + + +#include +#include + + +#include + + +#include +#include +#include +#include +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_ENUM_H +#define _PLUGINS_KAITAI_PARSERS_ENUM_H + + +#include +#include +#include + + +#include +#include + + +#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 . + */ + + +#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 . + */ + + +#include "instance.h" + + +#include +#include +#include + + +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_INSTANCE_H +#define _PLUGINS_KAITAI_PARSERS_INSTANCE_H + + +#include + + +#include + + +#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 . + */ + + +#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 . + */ + + +#include "meta.h" + + +#include +#include +#include + + +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_META_H +#define _PLUGINS_KAITAI_PARSERS_META_H + + +#include + + +#include +#include + + + +#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 . + */ + + +#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 . + */ + + +#include "struct.h" + + +#include +#include + + +#include +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_STRUCT_H +#define _PLUGINS_KAITAI_PARSERS_STRUCT_H + + +#include +#include + + +#include + + +#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 . + */ + + +#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 . + */ + + +#include "switch.h" + + +#include +#include +#include +#include +#include + + +#include + + +#include +#include +#include +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_SWITCH_H +#define _PLUGINS_KAITAI_PARSERS_SWITCH_H + + +#include + + +#include + + +#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 . + */ + + +#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 . + */ + + +#include "type.h" + + +#include +#include +#include + + +#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 . + */ + + +#ifndef _PLUGINS_KAITAI_PARSERS_TYPE_H +#define _PLUGINS_KAITAI_PARSERS_TYPE_H + + +#include +#include + + +#include + + + +#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 */ diff --git a/plugins/kaitai/python/Makefile.am b/plugins/kaitai/python/Makefile.am new file mode 100644 index 0000000..ab40744 --- /dev/null +++ b/plugins/kaitai/python/Makefile.am @@ -0,0 +1,25 @@ + +noinst_LTLIBRARIES = libkaitaipython.la + +libkaitaipython_la_SOURCES = \ + array.h array.c \ + module.h module.c \ + parser.h parser.c \ + record.h record.c \ + scope.h scope.c \ + stream.h stream.c + +libkaitaipython_la_LIBADD = \ + parsers/libkaitaipythonparsers.la \ + records/libkaitaipythonrecords.la + +libkaitaipython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libkaitaipython_la_SOURCES:%c=) + + +SUBDIRS = parsers records diff --git a/plugins/kaitai/python/array.c b/plugins/kaitai/python/array.c new file mode 100644 index 0000000..4973c76 --- /dev/null +++ b/plugins/kaitai/python/array.c @@ -0,0 +1,265 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * array.h - équivalent Python du fichier "plugins/kaitai/array.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "array.h" + + +#include + + +#include +#include +#include + + +#include "../array-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_array, G_TYPE_KAITAI_ARRAY); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_array_init(PyObject *, PyObject *, PyObject *); + +/* Convertit un tableau Kaitai en série d'octets si possible. */ +static PyObject *py_kaitai_array___bytes__(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_array_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define KAITAI_ARRAY_DOC \ + "KaitaiArray defines an array for collecting various Kaitai items." \ + "\n" \ + "Instances can be created using following constructor:\n" \ + "\n" \ + " KaitaiArray()" \ + "\n" \ + "In this implementation, arrays do not have to carry items all" \ + " belonging to the same type. Access and conversions to bytes are" \ + " handled and checked at runtime." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant un binaire. * +* args = arguments fournis à l'appel. * +* * +* Description : Convertit un tableau Kaitai en série d'octets si possible. * +* * +* Retour : Série d'octets ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_array___bytes__(PyObject *self, PyObject *args) +{ + PyObject *result; /* Représentation à renvoyer */ + GKaitaiArray *array; /* Tableau à manipuler */ + sized_string_t bytes; /* Version en série d'octets */ + bool status; /* Bilan de la conversion */ + +#define KAITAI_ARRAY_AS_BYTES_METHOD PYTHON_METHOD_DEF \ +( \ + __bytes__, "$self, /", \ + METH_NOARGS, py_kaitai_array, \ + "Provide a bytes representation of the array, when possible" \ + " and without implementing the Python buffer protocol.\n" \ + "\n" \ + "THe result is bytes or a *TypeError* exception is raised if" \ + " the array is not suitable for a conversion to bytes." \ +) + + array = G_KAITAI_ARRAY(pygobject_get(self)); + + status = g_kaitai_array_convert_to_bytes(array, &bytes); + + if (status) + { + result = PyBytes_FromStringAndSize(bytes.data, bytes.len); + exit_szstr(&bytes); + } + else + { + PyErr_SetString(PyExc_TypeError, "unable to convert the Kaitai array to bytes"); + result = NULL; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_array_type(void) +{ + static PyMethodDef py_kaitai_array_methods[] = { + KAITAI_ARRAY_AS_BYTES_METHOD, + { NULL } + }; + + static PyGetSetDef py_kaitai_array_getseters[] = { + { NULL } + }; + + static PyTypeObject py_kaitai_array_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.KaitaiArray", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = KAITAI_ARRAY_DOC, + + .tp_methods = py_kaitai_array_methods, + .tp_getset = py_kaitai_array_getseters, + + .tp_init = py_kaitai_array_init, + .tp_new = py_kaitai_array_new, + + }; + + return &py_kaitai_array_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiArray. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_array_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'KaitaiArray' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_array_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_ARRAY, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en tableau d'éléments Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_array(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_array_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai array"); + break; + + case 1: + *((GKaitaiArray **)dst) = G_KAITAI_ARRAY(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/array.h b/plugins/kaitai/python/array.h new file mode 100644 index 0000000..aeba541 --- /dev/null +++ b/plugins/kaitai/python/array.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * array.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/array.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_ARRAY_H +#define _PLUGINS_KAITAI_PYTHON_ARRAY_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_array_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.KaitaiArray'. */ +bool ensure_python_kaitai_array_is_registered(void); + +/* Tente de convertir en tableau d'éléments Kaitai. */ +int convert_to_kaitai_array(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_ARRAY_H */ diff --git a/plugins/kaitai/python/module.c b/plugins/kaitai/python/module.c new file mode 100644 index 0000000..fa1f9c2 --- /dev/null +++ b/plugins/kaitai/python/module.c @@ -0,0 +1,132 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire kaitai en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module.h" + + +#include + + +#include +#include + + +#include "array.h" +#include "parser.h" +#include "record.h" +#include "scope.h" +#include "stream.h" +#include "parsers/module.h" +#include "records/module.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Ajoute le module 'plugins.kaitai' au module Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_kaitai_module_to_python_module(void) +{ + bool result; /* Bilan à retourner */ + PyObject *super; /* Module à compléter */ + PyObject *module; /* Sous-module mis en place */ + +#define PYCHRYSALIDE_PLUGINS_KAITAI_DOC \ + "kaitai is a module trying to reverse some of the effects produced by ProGuard.\n" \ + "\n" \ + "Its action is focused on reverting name obfuscation by running binary diffing against" \ + " OpenSource packages from the AOSP." + + static PyModuleDef py_chrysalide_kaitai_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.plugins.kaitai", + .m_doc = PYCHRYSALIDE_PLUGINS_KAITAI_DOC, + + .m_size = -1, + + }; + + result = false; + + super = get_access_to_python_module("pychrysalide.plugins"); + + module = build_python_module(super, &py_chrysalide_kaitai_module); + + result = (module != NULL); + + assert(result); + + if (result) result = add_kaitai_parsers_module(); + if (result) result = add_kaitai_records_module(); + + if (!result) + Py_XDECREF(module); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'plugins.kaitai'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_kaitai_module(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_kaitai_array_is_registered(); + if (result) result = ensure_python_kaitai_parser_is_registered(); + if (result) result = ensure_python_match_record_is_registered(); + if (result) result = ensure_python_kaitai_scope_is_registered(); + if (result) result = ensure_python_kaitai_stream_is_registered(); + + if (result) result = populate_kaitai_parsers_module(); + if (result) result = populate_kaitai_records_module(); + + assert(result); + + return result; + +} diff --git a/plugins/kaitai/python/module.h b/plugins/kaitai/python/module.h new file mode 100644 index 0000000..939de07 --- /dev/null +++ b/plugins/kaitai/python/module.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire kaitai en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_MODULE_H +#define _PLUGINS_KAITAI_PYTHON_MODULE_H + + +#include + + + +/* Ajoute le module 'plugins.kaitai' au module Python. */ +bool add_kaitai_module_to_python_module(void); + +/* Intègre les objets du module 'plugins.kaitai'. */ +bool populate_kaitai_module(void); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_MODULE_H */ diff --git a/plugins/kaitai/python/parser.c b/plugins/kaitai/python/parser.c new file mode 100644 index 0000000..067d3b0 --- /dev/null +++ b/plugins/kaitai/python/parser.c @@ -0,0 +1,205 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * parser.h - équivalent Python du fichier "plugins/kaitai/parser.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "parser.h" + + +#include + + +#include +#include + + +#include "../parser.h" + + + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(kaitai_parser, G_TYPE_KAITAI_PARSER, NULL); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_parser_init(PyObject *, PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_parser_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define KAITAI_PARSER_DOC \ + "KaitaiParser is the class providing support for parsing binary contents" \ + " using a special declarative language." \ + "\n" \ + "It is the Python bindings for a C implementation of the specifications" \ + " described at http://kaitai.io/." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_parser_type(void) +{ + static PyMethodDef py_kaitai_parser_methods[] = { + { NULL } + }; + + static PyGetSetDef py_kaitai_parser_getseters[] = { + { NULL } + }; + + static PyTypeObject py_kaitai_parser_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.KaitaiParser", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = KAITAI_PARSER_DOC, + + .tp_methods = py_kaitai_parser_methods, + .tp_getset = py_kaitai_parser_getseters, + + .tp_init = py_kaitai_parser_init, + .tp_new = py_kaitai_parser_new, + + }; + + return &py_kaitai_parser_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiParser.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_parser_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'KaitaiParser' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_parser_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_PARSER, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en lecteur de données Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_parser(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_parser_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai parser"); + break; + + case 1: + *((GKaitaiParser **)dst) = G_KAITAI_PARSER(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parser.h b/plugins/kaitai/python/parser.h new file mode 100644 index 0000000..f4b6c96 --- /dev/null +++ b/plugins/kaitai/python/parser.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * parser.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parser.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSER_H +#define _PLUGINS_KAITAI_PYTHON_PARSER_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_parser_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.KaitaiParser'. */ +bool ensure_python_kaitai_parser_is_registered(void); + +/* Tente de convertir en lecteur de données Kaitai. */ +int convert_to_kaitai_parser(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSER_H */ diff --git a/plugins/kaitai/python/parsers/Makefile.am b/plugins/kaitai/python/parsers/Makefile.am new file mode 100644 index 0000000..4c418af --- /dev/null +++ b/plugins/kaitai/python/parsers/Makefile.am @@ -0,0 +1,19 @@ + +noinst_LTLIBRARIES = libkaitaipythonparsers.la + +libkaitaipythonparsers_la_SOURCES = \ + attribute.h attribute.c \ + enum.h enum.c \ + instance.h instance.c \ + meta.h meta.c \ + module.h module.c \ + struct.h struct.c \ + type.h type.c + +libkaitaipythonparsers_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libkaitaipythonparsers_la_SOURCES:%c=) diff --git a/plugins/kaitai/python/parsers/attribute.c b/plugins/kaitai/python/parsers/attribute.c new file mode 100644 index 0000000..c8ea314 --- /dev/null +++ b/plugins/kaitai/python/parsers/attribute.c @@ -0,0 +1,420 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * attribute.h - équivalent Python du fichier "plugins/kaitai/parsers/attribute.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "attribute.h" + + +#include +#include + + +#include +#include +#include +#include + + +#include "../parser.h" +#include "../../parsers/attribute-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_attribute, G_TYPE_KAITAI_ATTRIBUTE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_attribute_init(PyObject *, PyObject *, PyObject *); + +/* Indique la désignation brute d'un identifiant Kaitai. */ +static PyObject *py_kaitai_attribute_get_raw_id(PyObject *, void *); + +/* Indique la désignation originelle d'un identifiant Kaitai. */ +static PyObject *py_kaitai_attribute_get_original_id(PyObject *, void *); + +/* Fournit une éventuelle documentation concernant l'attribut. */ +static PyObject *py_kaitai_attribute_get_doc(PyObject *, void *); + +/* Détermine si l'attribue porte une valeur entière signée. */ +static PyObject *py_kaitai_attribute_get_handle_signed_integer(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_attribute_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GYamlNode *parent; /* Noeud Yaml de l'attribut */ + int ret; /* Bilan de lecture des args. */ + GKaitaiAttribute *attrib; /* Création GLib à transmettre */ + +#define KAITAI_ATTRIBUTE_DOC \ + "KaitaiAttribute is the class providing support for parsing binary" \ + " contents using a special declarative language." \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KaitaiAttribute(parent)" \ + "\n" \ + "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \ + " to Yaml data to load.\n" \ + "\n" \ + "The class is the Python bindings for a C implementation of the Attribute" \ + " structure described at https://doc.kaitai.io/ksy_diagram.html." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self)); + + if (!g_kaitai_attribute_create(attrib, parent, true)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai attribute.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique la désignation brute d'un identifiant Kaitai. * +* * +* Retour : Valeur brute de l'identifiant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_attribute_get_raw_id(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiAttribute *attrib; /* Version native de l'attribut*/ + const char *value; /* Valeur à transmettre */ + +#define KAITAI_ATTRIBUTE_RAW_ID_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + raw_id, py_kaitai_attribute, \ + "Raw value used by Kaitai to identify one attribute" \ + " among others.\n" \ + "\n" \ + "The returned indentifier is a string value." \ +) + + attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self)); + + value = g_kaitai_attribute_get_raw_id(attrib); + assert(value != NULL); + + result = PyUnicode_FromString(value); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique la désignation originelle d'un identifiant Kaitai. * +* * +* Retour : Valeur originelle de l'identifiant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_attribute_get_original_id(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiAttribute *attrib; /* Version native de l'attribut*/ + const char *value; /* Valeur à transmettre */ + +#define KAITAI_ATTRIBUTE_ORIGINAL_ID_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + original_id, py_kaitai_attribute, \ + "Optional alternative identifier for the attribute, as seen in" \ + " the original specifications.\n" \ + "\n" \ + "The returned value is a string or *None*." \ +) + + attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self)); + + value = g_kaitai_attribute_get_original_id(attrib); + + if (value == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + result = PyUnicode_FromString(value); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit une éventuelle documentation concernant l'attribut. * +* * +* Retour : Description enregistrée ou None si absente. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_attribute_get_doc(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiAttribute *attrib; /* Version native de l'attribut*/ + const char *doc; /* Documentation à transmettre */ + +#define KAITAI_ATTRIBUTE_DOC_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + doc, py_kaitai_attribute, \ + "Optional documentation for the attribute.\n" \ + "\n" \ + "The returned value is a string or *None*." \ +) + + attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self)); + + doc = g_kaitai_attribute_get_doc(attrib); + + if (doc == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + result = PyUnicode_FromString(doc); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* 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 : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_attribute_get_handle_signed_integer(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiAttribute *attrib; /* Version native de l'attribut*/ + bool status; /* Bilan d'une consultation */ + +#define KAITAI_ATTRIBUTE_HANDLE_SIGNED_INTEGER_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + handle_signed_integer, py_kaitai_attribute, \ + "Sign of the carried integer value, if any: positive or negative?\n" \ + "\n" \ + "This status is provided as a boolean value." \ +) + + attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self)); + + status = g_kaitai_attribute_handle_signed_integer(attrib); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_attribute_type(void) +{ + static PyMethodDef py_kaitai_attribute_methods[] = { + { NULL } + }; + + static PyGetSetDef py_kaitai_attribute_getseters[] = { + KAITAI_ATTRIBUTE_RAW_ID_ATTRIB, + KAITAI_ATTRIBUTE_ORIGINAL_ID_ATTRIB, + KAITAI_ATTRIBUTE_DOC_ATTRIB, + KAITAI_ATTRIBUTE_HANDLE_SIGNED_INTEGER_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_attribute_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiAttribute", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = KAITAI_ATTRIBUTE_DOC, + + .tp_methods = py_kaitai_attribute_methods, + .tp_getset = py_kaitai_attribute_getseters, + + .tp_init = py_kaitai_attribute_init, + .tp_new = py_kaitai_attribute_new, + + }; + + return &py_kaitai_attribute_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide....KaitaiAttribute. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_attribute_is_registered(void) +{ + PyTypeObject *type; /* Type 'KaitaiAttribute' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_attribute_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_kaitai_parser_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_ATTRIBUTE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en attribut de données Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_attribute(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_attribute_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai attribute"); + break; + + case 1: + *((GKaitaiAttribute **)dst) = G_KAITAI_ATTRIBUTE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parsers/attribute.h b/plugins/kaitai/python/parsers/attribute.h new file mode 100644 index 0000000..931e769 --- /dev/null +++ b/plugins/kaitai/python/parsers/attribute.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * attribute.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/attribute.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_ATTRIBUTE_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_ATTRIBUTE_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_attribute_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiAttribute'. */ +bool ensure_python_kaitai_attribute_is_registered(void); + +/* Tente de convertir en attribut de données Kaitai. */ +int convert_to_kaitai_attribute(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_ATTRIBUTE_H */ diff --git a/plugins/kaitai/python/parsers/enum.c b/plugins/kaitai/python/parsers/enum.c new file mode 100644 index 0000000..9200c6f --- /dev/null +++ b/plugins/kaitai/python/parsers/enum.c @@ -0,0 +1,468 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * enum.h - équivalent Python du fichier "plugins/kaitai/parsers/enum.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "enum.h" + + +#include +#include + + +#include +#include +#include +#include + + +#include "../../parsers/enum-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_enum, G_TYPE_KAITAI_ENUM); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_enum_init(PyObject *, PyObject *, PyObject *); + +/* Traduit une étiquette brute en constante d'énumération. */ +static PyObject *py_kaitai_enum_find_value(PyObject *, PyObject *); + +/* Traduit une constante d'énumération en étiquette brute. */ +static PyObject *py_kaitai_enum_find_label(PyObject *, PyObject *); + +/* Traduit une constante d'énumération en documentation. */ +static PyObject *py_kaitai_enum_find_documentation(PyObject *, PyObject *); + +/* Fournit le nom principal d'une énumération. */ +static PyObject *py_kaitai_enum_get_name(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_enum_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GYamlNode *parent; /* Noeud Yaml de l'attribut */ + int ret; /* Bilan de lecture des args. */ + GKaitaiEnum *kenum; /* Création GLib à transmettre */ + +#define KAITAI_ENUM_DOC \ + "The KaitaiEnum class maps integer constants to symbolic names using" \ + " Kaitai definitions.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KaitaiEnum(parent)" \ + "\n" \ + "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \ + " to Yaml data to load.\n" \ + "\n" \ + "The class is the Python bindings for a C implementation of the EnumSpec" \ + " structure described at https://doc.kaitai.io/ksy_diagram.html." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + kenum = G_KAITAI_ENUM(pygobject_get(self)); + + if (!g_kaitai_enum_create(kenum, parent)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai enumeration.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance de l'énumération Kaitai à manipuler. * +* args = arguments fournis à l'appel. * +* * +* Description : Traduit une étiquette brute en constante d'énumération. * +* * +* Retour : Valeur retrouvée ou None en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_enum_find_value(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + const char *label; /* Etiquette à rechercher */ + int ret; /* Bilan de lecture des args. */ + GKaitaiEnum *kenum; /* Enumération Kaitai courante */ + sized_string_t cstr; /* CHaîne avec sa longueur */ + bool status; /* Bilan de la conversion */ + resolved_value_t value; /* valeur à transformer */ + +#define KAITAI_ENUM_FIND_VALUE_METHOD PYTHON_METHOD_DEF \ +( \ + find_value, "$self, label", \ + METH_VARARGS, py_kaitai_enum, \ + "Translate a given enumeration label into its relative value.\n" \ + "\n" \ + "The *label* argument is expected to be a string.\n" \ + "\n" \ + "The result is an integer or *None* in case of resolution failure." \ +) + + ret = PyArg_ParseTuple(args, "s", &label); + if (!ret) return NULL; + + kenum = G_KAITAI_ENUM(pygobject_get(self)); + + cstr.data = (char *)label; + cstr.len = strlen(label); + + status = g_kaitai_enum_find_value(kenum, &cstr, &value); + + if (status) + { + if (value.type == GVT_UNSIGNED_INTEGER) + result = PyLong_FromUnsignedLongLong(value.unsigned_integer); + else + { + assert(value.type == GVT_SIGNED_INTEGER); + result = PyLong_FromLongLong(value.signed_integer); + } + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance de l'énumération Kaitai à manipuler. * +* args = arguments fournis à l'appel. * +* * +* Description : Traduit une constante d'énumération en étiquette brute. * +* * +* Retour : Désignation ou None en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_enum_find_label(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + int prefix; /* Préfixe attendu ? */ + resolved_value_t value; /* valeur à transformer */ + int ret; /* Bilan de lecture des args. */ + GKaitaiEnum *kenum; /* Enumération Kaitai courante */ + char *label; /* Etiquette reconstruite */ + +#define KAITAI_ENUM_FIND_LABEL_METHOD PYTHON_METHOD_DEF \ +( \ + find_label, "$self, value, / , prefix=False", \ + METH_VARARGS, py_kaitai_enum, \ + "Provide the label linked to a constant value within the current" \ + " enumeration.\n" \ + "\n" \ + "The *value* is a simple integer, and *prefix* is a boolean indicating" \ + " if the result has to integrate the enumeration name as a prefix.\n" \ + "\n" \ + "The result is a string or *None* in case of resolution failure." \ +) + + prefix = 0; + + ret = PyArg_ParseTuple(args, "K|p", &value.unsigned_integer, prefix); + if (!ret) return NULL; + + kenum = G_KAITAI_ENUM(pygobject_get(self)); + + value.type = GVT_UNSIGNED_INTEGER; + label = g_kaitai_enum_find_label(kenum, &value, prefix); + + if (label != NULL) + { + result = PyUnicode_FromString(label); + free(label); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance de l'énumération Kaitai à manipuler. * +* args = arguments fournis à l'appel. * +* * +* Description : Traduit une constante d'énumération en documentation. * +* * +* Retour : Documentation associée à la valeur indiquée ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_enum_find_documentation(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + resolved_value_t value; /* valeur à transformer */ + int ret; /* Bilan de lecture des args. */ + GKaitaiEnum *kenum; /* Enumération Kaitai courante */ + char *doc; /* Documentation obtenue */ + +#define KAITAI_ENUM_FIND_DOCUMENTATION_METHOD PYTHON_METHOD_DEF \ +( \ + find_documentation, "$self, value", \ + METH_VARARGS, py_kaitai_enum, \ + "Provide the optional documentation linked to a constant value within" \ + " the current enumeration.\n" \ + "\n" \ + "The *value* is a simple integer.\n" \ + "\n" \ + "The result is a string or *None* if no documentation is registered" \ + " for the provided value." \ +) + + ret = PyArg_ParseTuple(args, "K", &value.unsigned_integer); + if (!ret) return NULL; + + kenum = G_KAITAI_ENUM(pygobject_get(self)); + + value.type = GVT_UNSIGNED_INTEGER; + doc = g_kaitai_enum_find_documentation(kenum, &value); + + if (doc != NULL) + { + result = PyUnicode_FromString(doc); + free(doc); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit le nom principal d'une énumération. * +* * +* Retour : Désignation de l'énumération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_enum_get_name(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiEnum *kenum; /* Version native de l'objet */ + const char *name; /* Valeur à transmettre */ + +#define KAITAI_ENUM_NAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + name, py_kaitai_enum, \ + "Name of the enumeration group, as a string value." \ +) + + kenum = G_KAITAI_ENUM(pygobject_get(self)); + + name = g_kaitai_enum_get_name(kenum); + + result = PyUnicode_FromString(name); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_enum_type(void) +{ + static PyMethodDef py_kaitai_enum_methods[] = { + KAITAI_ENUM_FIND_VALUE_METHOD, + KAITAI_ENUM_FIND_LABEL_METHOD, + KAITAI_ENUM_FIND_DOCUMENTATION_METHOD, + { NULL } + }; + + static PyGetSetDef py_kaitai_enum_getseters[] = { + KAITAI_ENUM_NAME_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_enum_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiEnum", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = KAITAI_ENUM_DOC, + + .tp_methods = py_kaitai_enum_methods, + .tp_getset = py_kaitai_enum_getseters, + + .tp_init = py_kaitai_enum_init, + .tp_new = py_kaitai_enum_new + + }; + + return &py_kaitai_enum_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiEnum. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_enum_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'KaitaiEnum' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_enum_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_ENUM, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en ensemble d'énumérations Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_enum(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_enum_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai enumeration"); + break; + + case 1: + *((GKaitaiEnum **)dst) = G_KAITAI_ENUM(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parsers/enum.h b/plugins/kaitai/python/parsers/enum.h new file mode 100644 index 0000000..7172e69 --- /dev/null +++ b/plugins/kaitai/python/parsers/enum.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * enum.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/enum.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_ENUM_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_ENUM_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_enum_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiEnum'. */ +bool ensure_python_kaitai_enum_is_registered(void); + +/* Tente de convertir en ensemble d'énumérations Kaitai. */ +int convert_to_kaitai_enum(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_ENUM_H */ diff --git a/plugins/kaitai/python/parsers/instance.c b/plugins/kaitai/python/parsers/instance.c new file mode 100644 index 0000000..d55b58c --- /dev/null +++ b/plugins/kaitai/python/parsers/instance.c @@ -0,0 +1,280 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instance.h - équivalent Python du fichier "plugins/kaitai/parsers/instance.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "instance.h" + + +#include +#include + + +#include +#include +#include +#include + + +#include "attribute.h" +#include "../../parsers/instance-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_instance, G_TYPE_KAITAI_INSTANCE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_instance_init(PyObject *, PyObject *, PyObject *); + +/* Indique le nom attribué à une instance Kaitai. */ +static PyObject *py_kaitai_instance_get_name(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_instance_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GYamlNode *parent; /* Noeud Yaml de l'attribut */ + int ret; /* Bilan de lecture des args. */ + GKaitaiInstance *attrib; /* Création GLib à transmettre */ + +#define KAITAI_INSTANCE_DOC \ + "KaitaiInstance is the class providing support for Kaitai computed" \ + " values.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KaitaiInstance(parent)" \ + "\n" \ + "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \ + " to Yaml data to load.\n" \ + "\n" \ + "The class is the Python bindings for a C implementation of the Instance" \ + " structure described at https://doc.kaitai.io/ksy_diagram.html." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + attrib = G_KAITAI_INSTANCE(pygobject_get(self)); + + if (!g_kaitai_instance_create(attrib, parent)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai instance.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le nom attribué à une instance Kaitai. * +* * +* Retour : Désignation pointant l'instance. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_instance_get_name(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiInstance *inst; /* Version native de l'instance*/ + const char *name; /* Désignation à transmettre */ + +#define KAITAI_INSTANCE_NAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + name, py_kaitai_instance, \ + "Name used by Kaitai to identify the instance" \ + " among others.\n" \ + "\n" \ + "The returned indentifier is a string value." \ +) + + inst = G_KAITAI_INSTANCE(pygobject_get(self)); + + name = g_kaitai_instance_get_name(inst); + assert(name != NULL); + + result = PyUnicode_FromString(name); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_instance_type(void) +{ + static PyMethodDef py_kaitai_instance_methods[] = { + { NULL } + }; + + static PyGetSetDef py_kaitai_instance_getseters[] = { + KAITAI_INSTANCE_NAME_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_instance_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiInstance", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = KAITAI_INSTANCE_DOC, + + .tp_methods = py_kaitai_instance_methods, + .tp_getset = py_kaitai_instance_getseters, + + .tp_init = py_kaitai_instance_init, + .tp_new = py_kaitai_instance_new, + + }; + + return &py_kaitai_instance_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide....KaitaiInstance. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_instance_is_registered(void) +{ + PyTypeObject *type; /* Type 'KaitaiInstance' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_instance_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_kaitai_attribute_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_INSTANCE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_instance(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_instance_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai instance"); + break; + + case 1: + *((GKaitaiInstance **)dst) = G_KAITAI_INSTANCE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parsers/instance.h b/plugins/kaitai/python/parsers/instance.h new file mode 100644 index 0000000..8a0a6cf --- /dev/null +++ b/plugins/kaitai/python/parsers/instance.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * instance.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/instance.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_INSTANCE_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_INSTANCE_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_instance_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiInstance'. */ +bool ensure_python_kaitai_instance_is_registered(void); + +/* Tente de convertir en instance Kaitai. */ +int convert_to_kaitai_instance(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_INSTANCE_H */ diff --git a/plugins/kaitai/python/parsers/meta.c b/plugins/kaitai/python/parsers/meta.c new file mode 100644 index 0000000..3432640 --- /dev/null +++ b/plugins/kaitai/python/parsers/meta.c @@ -0,0 +1,366 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * meta.h - équivalent Python du fichier "plugins/kaitai/parsers/meta.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "meta.h" + + +#include + + +#include +#include +#include +#include +#include + + +#include "../../parsers/meta-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_meta, G_TYPE_KAITAI_META); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_meta_init(PyObject *, PyObject *, PyObject *); + +/* Fournit l'identifié associé à une définiton Kaitai. */ +static PyObject *py_kaitai_meta_get_id(PyObject *, void *); + +/* Fournit la désignation humaine d'une définiton Kaitai. */ +static PyObject *py_kaitai_meta_get_title(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_meta_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GYamlNode *parent; /* Noeud Yaml de l'attribut */ + int ret; /* Bilan de lecture des args. */ + GKaitaiMeta *kmeta; /* Création GLib à transmettre */ + +#define KAITAI_META_DOC \ + "The KaitaiMeta class stores general information about a Kaitai definition,"\ + " such as required imports or the default endianness for reading values.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KaitaiMeta(parent)" \ + "\n" \ + "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \ + " to Yaml data to load.\n" \ + "\n" \ + "The class is the Python bindings for a C implementation of the MetaSpec" \ + " structure described at https://doc.kaitai.io/ksy_diagram.html." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + kmeta = G_KAITAI_META(pygobject_get(self)); + + if (!g_kaitai_meta_create(kmeta, parent)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai global description.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit l'identifié associé à une définiton Kaitai. * +* * +* Retour : Identifiant de définition complète ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_meta_get_id(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiMeta *meta; /* Version native de l'objet */ + const char *id; /* Valeur à transmettre */ + +#define KAITAI_META_ID_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + id, py_kaitai_meta, \ + "Identifier for the Kaitai definition, as a string" \ + " value or *None* if any." \ +) + + meta = G_KAITAI_META(pygobject_get(self)); + + id = g_kaitai_meta_get_id(meta); + + if (id != NULL) + result = PyUnicode_FromString(id); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la désignation humaine d'une définiton Kaitai. * +* * +* Retour : Intitulé de définition OU None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_meta_get_title(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiMeta *meta; /* Version native de l'objet */ + const char *title; /* Valeur à transmettre */ + +#define KAITAI_META_TITLE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + title, py_kaitai_meta, \ + "Humain description for the Kaitai definition, as a" \ + " string value or *None* if any." \ +) + + meta = G_KAITAI_META(pygobject_get(self)); + + title = g_kaitai_meta_get_title(meta); + + if (title != NULL) + result = PyUnicode_FromString(title); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la désignation humaine d'une définiton Kaitai. * +* * +* Retour : Intitulé de définition OU None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_meta_get_endian(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiMeta *meta; /* Version native de l'objet */ + SourceEndian endian; /* Valeur à transmettre */ + +#define KAITAI_META_ENDIAN_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + endian, py_kaitai_meta, \ + "Default endianness for the Kaitai definition, as a" \ + " pychrysalide.analysis.BinContent.SourceEndian value." \ +) + + meta = G_KAITAI_META(pygobject_get(self)); + + endian = g_kaitai_meta_get_endian(meta); + + result = cast_with_constants_group_from_type(get_python_binary_content_type(), "SourceEndian", endian); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_meta_type(void) +{ + static PyMethodDef py_kaitai_meta_methods[] = { + { NULL } + }; + + static PyGetSetDef py_kaitai_meta_getseters[] = { + KAITAI_META_ID_ATTRIB, + KAITAI_META_TITLE_ATTRIB, + KAITAI_META_ENDIAN_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_meta_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiMeta", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = KAITAI_META_DOC, + + .tp_methods = py_kaitai_meta_methods, + .tp_getset = py_kaitai_meta_getseters, + + .tp_init = py_kaitai_meta_init, + .tp_new = py_kaitai_meta_new + + }; + + return &py_kaitai_meta_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiMeta. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_meta_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'KaitaiMeta' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_meta_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_META, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en description globale Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_meta(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_meta_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai global description"); + break; + + case 1: + *((GKaitaiMeta **)dst) = G_KAITAI_META(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parsers/meta.h b/plugins/kaitai/python/parsers/meta.h new file mode 100644 index 0000000..383cad9 --- /dev/null +++ b/plugins/kaitai/python/parsers/meta.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * meta.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/meta.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_META_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_META_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_meta_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiMeta'. */ +bool ensure_python_kaitai_meta_is_registered(void); + +/* Tente de convertir en description globale Kaitai. */ +int convert_to_kaitai_meta(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_META_H */ diff --git a/plugins/kaitai/python/parsers/module.c b/plugins/kaitai/python/parsers/module.c new file mode 100644 index 0000000..549f728 --- /dev/null +++ b/plugins/kaitai/python/parsers/module.c @@ -0,0 +1,124 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire parsers en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module.h" + + +#include + + +#include +#include + + +#include "attribute.h" +#include "enum.h" +#include "instance.h" +#include "meta.h" +#include "struct.h" +#include "type.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Ajoute le module 'plugins.kaitai.parsers' au module Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_kaitai_parsers_module(void) +{ + bool result; /* Bilan à retourner */ + PyObject *super; /* Module à compléter */ + PyObject *module; /* Sous-module mis en place */ + +#define PYCHRYSALIDE_PLUGINS_KAITAI_PARSERS_DOC \ + "This module provides implementation for several Kaitai" \ + " definitions parsers." + + static PyModuleDef py_chrysalide_kaitai_parsers_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.plugins.kaitai.parsers", + .m_doc = PYCHRYSALIDE_PLUGINS_KAITAI_PARSERS_DOC, + + .m_size = -1, + + }; + + result = false; + + super = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + module = build_python_module(super, &py_chrysalide_kaitai_parsers_module); + + result = (module != NULL); + + assert(result); + + if (!result) + Py_XDECREF(module); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'plugins.kaitai.parsers'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_kaitai_parsers_module(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_kaitai_attribute_is_registered(); + if (result) result = ensure_python_kaitai_enum_is_registered(); + if (result) result = ensure_python_kaitai_instance_is_registered(); + if (result) result = ensure_python_kaitai_meta_is_registered(); + if (result) result = ensure_python_kaitai_structure_is_registered(); + if (result) result = ensure_python_kaitai_type_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/kaitai/python/parsers/module.h b/plugins/kaitai/python/parsers/module.h new file mode 100644 index 0000000..d0fdd66 --- /dev/null +++ b/plugins/kaitai/python/parsers/module.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire parsers en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_MODULE_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_MODULE_H + + +#include + + + +/* Ajoute le module 'plugins.kaitai.parsers' au module Python. */ +bool add_kaitai_parsers_module(void); + +/* Intègre les objets du module 'plugins.kaitai.parsers'. */ +bool populate_kaitai_parsers_module(void); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_MODULE_H */ diff --git a/plugins/kaitai/python/parsers/struct.c b/plugins/kaitai/python/parsers/struct.c new file mode 100644 index 0000000..900cd1b --- /dev/null +++ b/plugins/kaitai/python/parsers/struct.c @@ -0,0 +1,376 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * struct.h - équivalent Python du fichier "plugins/kaitai/struct.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "struct.h" + + +#include + + +#include +#include +#include +#include + + +#include "../parser.h" +#include "../../parsers/struct-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_structure, G_TYPE_KAITAI_STRUCT); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_structure_init(PyObject *, PyObject *, PyObject *); + +/* Parcourt un contenu binaire selon une description Kaitai. */ +static PyObject *py_kaitai_structure_parse(PyObject *, PyObject *); + +/* Fournit la désignation humaine d'une définiton Kaitai. */ +static PyObject *py_kaitai_structure_get_meta(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_structure_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + const char *text; /* Contenu de règles à traiter */ + const char *filename; /* Fichier de définitions */ + int ret; /* Bilan de lecture des args. */ + GKaitaiStruct *kstruct; /* Création GLib à transmettre */ + + static char *kwlist[] = { "text", "filename", NULL }; + +#define KAITAI_STRUCT_DOC \ + "KaitaiStruct is the class providing support for parsing binary contents" \ + " using a special declarative language." \ + "\n" \ + "Instances can be created using one of the following constructors:\n" \ + "\n" \ + " KaitaiStruct(text=str)" \ + "\n" \ + " KaitaiStruct(filename=str)" \ + "\n" \ + "Where *text* is a string containg a markup content to parse; the" \ + " *filename* argument is an alternative string for a path pointing to the" \ + " same kind of content. This path can be a real filename or a resource" \ + " URI." \ + "\n" \ + "It is the Python bindings for a C implementation of the specifications" \ + " described at http://kaitai.io/." + + /* Récupération des paramètres */ + + text = NULL; + filename = NULL; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "|ss", kwlist, &text, &filename); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + kstruct = G_KAITAI_STRUCT(pygobject_get(self)); + + if (text != NULL) + { + if (!g_kaitai_structure_create_from_text(kstruct, text)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai structure.")); + return -1; + } + + } + + else if (filename != NULL) + { + if (!g_kaitai_structure_create_from_file(kstruct, filename)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai structure.")); + return -1; + } + + } + + else + { + PyErr_SetString(PyExc_ValueError, _("Unable to create empty Kaitai structure.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance de l'interpréteur Kaitai à manipuler. * +* args = arguments fournis à l'appel. * +* * +* Description : Parcourt un contenu binaire selon une description Kaitai. * +* * +* Retour : Arborescence d'éléments rencontrés selon les spécifications. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_structure_parse(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + GBinContent *content; /* Contenu binaire à traiter */ + int ret; /* Bilan de lecture des args. */ + GKaitaiStruct *kstruct; /* Interpréteur Kaitai courant */ + GMatchRecord *record; /* Ensemble de correspondances */ + +#define KAITAI_STRUCTURE_PARSE_METHOD PYTHON_METHOD_DEF \ +( \ + parse, "$self, content", \ + METH_VARARGS, py_kaitai_structure, \ + "Parse a binary content with the loaded specifications." \ + "\n" \ + "The content has to be a pychrysalide.analysis.BinContent instance.\n" \ + "\n" \ + "The result is *None* if the parsing failed, or a" \ + " pychrysalide.plugins.kaitai.MatchRecord object for each attribute" \ + " met." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_binary_content, &content); + if (!ret) return NULL; + + kstruct = G_KAITAI_STRUCT(pygobject_get(self)); + + record = g_kaitai_structure_parse(kstruct, content); + + if (record != NULL) + { + result = pygobject_new(G_OBJECT(record)); + g_object_unref(G_OBJECT(record)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la désignation humaine d'une définiton Kaitai. * +* * +* Retour : Intitulé de définition OU None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_structure_get_meta(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiStruct *kstruct; /* Version native de l'objet */ + GKaitaiMeta *meta; /* Informations à transmettre */ + +#define KAITAI_STRUCTURE_META_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + meta, py_kaitai_structure, \ + "Global description provided for the Kaitai definition, as a" \ + " pychrysalide.plugins.kaitai.parsers.KaitaiMeta instance." \ +) + + kstruct = G_KAITAI_STRUCT(pygobject_get(self)); + + meta = g_kaitai_structure_get_meta(kstruct); + + if (meta != NULL) + { + result = pygobject_new(G_OBJECT(meta)); + g_object_unref(G_OBJECT(meta)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_structure_type(void) +{ + static PyMethodDef py_kaitai_structure_methods[] = { + KAITAI_STRUCTURE_PARSE_METHOD, + { NULL } + }; + + static PyGetSetDef py_kaitai_structure_getseters[] = { + KAITAI_STRUCTURE_META_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_structure_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiStruct", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = KAITAI_STRUCT_DOC, + + .tp_methods = py_kaitai_structure_methods, + .tp_getset = py_kaitai_structure_getseters, + + .tp_init = py_kaitai_structure_init, + .tp_new = py_kaitai_structure_new + + }; + + return &py_kaitai_structure_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiStruct.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_structure_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'KaitaiStruct' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_structure_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_kaitai_parser_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_STRUCT, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en structure de données Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_structure(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_structure_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai structure"); + break; + + case 1: + *((GKaitaiStruct **)dst) = G_KAITAI_STRUCT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parsers/struct.h b/plugins/kaitai/python/parsers/struct.h new file mode 100644 index 0000000..872f744 --- /dev/null +++ b/plugins/kaitai/python/parsers/struct.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * struct.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/struct.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_STRUCT_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_STRUCT_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_structure_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiStruct'. */ +bool ensure_python_kaitai_structure_is_registered(void); + +/* Tente de convertir en structure de données Kaitai. */ +int convert_to_kaitai_structure(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_STRUCT_H */ diff --git a/plugins/kaitai/python/parsers/type.c b/plugins/kaitai/python/parsers/type.c new file mode 100644 index 0000000..64a3419 --- /dev/null +++ b/plugins/kaitai/python/parsers/type.c @@ -0,0 +1,278 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * type.h - équivalent Python du fichier "plugins/kaitai/parsers/type.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "type.h" + + +#include +#include + + +#include +#include +#include +#include + + +#include "struct.h" +#include "../../parsers/type-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_type, G_TYPE_KAITAI_TYPE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_type_init(PyObject *, PyObject *, PyObject *); + +/* Indique le nom de scène du type représenté. */ +static PyObject *py_kaitai_type_get_name(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_type_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GYamlNode *parent; /* Noeud Yaml de l'attribut */ + int ret; /* Bilan de lecture des args. */ + GKaitaiType *attrib; /* Création GLib à transmettre */ + +#define KAITAI_TYPE_DOC \ + "The KaitaiType class provides support for user-defined type used in" \ + " Kaitai definitions.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KaitaiType(parent)" \ + "\n" \ + "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \ + " to Yaml data to load.\n" \ + "\n" \ + "The class is the Python bindings for a C implementation of the TypesSpec" \ + " structure described at https://doc.kaitai.io/ksy_diagram.html." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + attrib = G_KAITAI_TYPE(pygobject_get(self)); + + if (!g_kaitai_type_create(attrib, parent)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai type.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le nom de scène du type représenté. * +* * +* Retour : Désignation humaine. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_type_get_name(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiType *type; /* Version native du type */ + const char *name; /* Désignation à transmettre */ + +#define KAITAI_TYPE_NAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + name, py_kaitai_type, \ + "Name of the user-defined type, provided as a unique" \ + " string value." \ +) + + type = G_KAITAI_TYPE(pygobject_get(self)); + + name = g_kaitai_type_get_name(type); + assert(name != NULL); + + result = PyUnicode_FromString(name); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_type_type(void) +{ + static PyMethodDef py_kaitai_type_methods[] = { + { NULL } + }; + + static PyGetSetDef py_kaitai_type_getseters[] = { + KAITAI_TYPE_NAME_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_type_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiType", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = KAITAI_TYPE_DOC, + + .tp_methods = py_kaitai_type_methods, + .tp_getset = py_kaitai_type_getseters, + + .tp_init = py_kaitai_type_init, + .tp_new = py_kaitai_type_new, + + }; + + return &py_kaitai_type_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide....parsers.KaitaiType. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_type_is_registered(void) +{ + PyTypeObject *type; /* Type 'KaitaiType' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_type_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_kaitai_structure_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_TYPE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en type particulier pour Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_type(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_type_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai type"); + break; + + case 1: + *((GKaitaiType **)dst) = G_KAITAI_TYPE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/parsers/type.h b/plugins/kaitai/python/parsers/type.h new file mode 100644 index 0000000..320bc71 --- /dev/null +++ b/plugins/kaitai/python/parsers/type.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * type.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/type.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_TYPE_H +#define _PLUGINS_KAITAI_PYTHON_PARSERS_TYPE_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_type_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiType'. */ +bool ensure_python_kaitai_type_is_registered(void); + +/* Tente de convertir en type particulier pour Kaitai. */ +int convert_to_kaitai_type(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_TYPE_H */ diff --git a/plugins/kaitai/python/record.c b/plugins/kaitai/python/record.c new file mode 100644 index 0000000..4194a9a --- /dev/null +++ b/plugins/kaitai/python/record.c @@ -0,0 +1,420 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * record.h - équivalent Python du fichier "plugins/kaitai/record.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "record.h" + + +#include + + +#include +#include +#include + + +#include "parser.h" +#include "../record.h" + + + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(match_record, G_TYPE_MATCH_RECORD, NULL); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_match_record_init(PyObject *, PyObject *, PyObject *); + +/* Modifie la référence au créateur de la correspondance. */ +static int py_match_record_set_creator(PyObject *, PyObject *, void *); + +/* Renvoie vers le lecteur à l'origine de la correspondance. */ +static PyObject *py_match_record_get_creator(PyObject *, void *); + +/* Fournit le contenu lié à une correspondance établie. */ +static PyObject *py_match_record_get_content(PyObject *, void *); + +/* Calcule ou fournit la zone couverte par une correspondance. */ +static PyObject *py_match_record_get_range(PyObject *, void *); + +/* Lit les octets bruts couverts par une correspondance. */ +static PyObject *py_match_record_get_raw_bytes(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_match_record_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define MATCH_RECORD_DOC \ + "MatchRecord is an abstract class providing mainly location and raw" \ + " data of an area which has matched a part of a binary content." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = correspondance à manipuler. * +* value = lecteur à l'origine de la correspondance. * +* closure = adresse non utilisée ici. * +* * +* Description : Modifie la référence au créateur de la correspondance. * +* * +* Retour : Bilan de la définition. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_match_record_set_creator(PyObject *self, PyObject *value, void *closure) +{ + int result; /* Bilan à renvoyer */ + GMatchRecord *record; /* Version GLib de l'objet */ + GKaitaiParser *parser; /* Lecteur à l'origine */ + + record = G_MATCH_RECORD(pygobject_get(self)); + + if (!convert_to_kaitai_parser(value, &parser)) + result = -1; + + else + { + g_match_record_fix_creator(record, parser); + result = 0; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = correspondance à manipuler. * +* closure = adresse non utilisée ici. * +* * +* Description : Renvoie vers le lecteur à l'origine de la correspondance. * +* * +* Retour : Lecteur à l'origine de la création. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_match_record_get_creator(PyObject *self, void *closure) +{ + PyObject *result; /* Instance à retourner */ + GMatchRecord *record; /* Version GLib de l'objet */ + GKaitaiParser *parser; /* Lecteur à l'origine */ + +#define MATCH_RECORD_CREATOR_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + creator, py_match_record, \ + "Provide or define the pychrysalide.plugins.kaitai.KaitaiParser instance" \ + " which has created the record.\n" \ + "\n" \ + "This field should not be defined after the record creation in most cases." \ +) + + record = G_MATCH_RECORD(pygobject_get(self)); + + parser = g_match_record_get_creator(record); + + result = pygobject_new(G_OBJECT(parser)); + g_object_unref(parser); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit le contenu lié à une correspondance établie. * +* * +* Retour : Contenu binaire associé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_match_record_get_content(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GMatchRecord *record; /* Conservation à consulter */ + GBinContent *content; /* Contenu associé */ + +#define MATCH_RECORD_CONTENT_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + content, py_match_record, \ + "pychrysalide.analysis.BinContent instance linked to" \ + " the match record." \ +) + + record = G_MATCH_RECORD(pygobject_get(self)); + content = g_match_record_get_content(record); + + if (content != NULL) + { + result = pygobject_new(G_OBJECT(content)); + g_object_unref(G_OBJECT(content)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : Zone de couverture déterminée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_match_record_get_range(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GMatchRecord *record; /* Conservation à consulter */ + mrange_t range; /* Couverture courante */ + +#define MATCH_RECORD_RANGE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + range, py_match_record, \ + "Area of the matched data for the parsed attribute" \ + " against a given binary content.\n" \ + "\n" \ + "This property is a pychrysalide.arch.mrange instance." \ +) + + record = G_MATCH_RECORD(pygobject_get(self)); + g_match_record_get_range(record, &range); + + result = build_from_internal_mrange(&range); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Lit les octets bruts couverts par une correspondance. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_match_record_get_raw_bytes(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GMatchRecord *record; /* Conservation à consulter */ + bin_t *out; /* Données brutes à transmettre*/ + size_t len; /* Quantité de ces données */ + +#define MATCH_RECORD_RAW_BYTES_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + raw_bytes, py_match_record, \ + "Raw bytes from the area covered by the record." \ +) + + record = G_MATCH_RECORD(pygobject_get(self)); + + g_match_record_read_raw_bytes(record, &out, &len); + + result = PyBytes_FromStringAndSize((char *)out, len); + free(out); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_match_record_type(void) +{ + static PyMethodDef py_match_record_methods[] = { + { NULL } + }; + + static PyGetSetDef py_match_record_getseters[] = { + MATCH_RECORD_CREATOR_ATTRIB, + MATCH_RECORD_CONTENT_ATTRIB, + MATCH_RECORD_RANGE_ATTRIB, + MATCH_RECORD_RAW_BYTES_ATTRIB, + { NULL } + }; + + static PyTypeObject py_match_record_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.MatchRecord", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = MATCH_RECORD_DOC, + + .tp_methods = py_match_record_methods, + .tp_getset = py_match_record_getseters, + + .tp_init = py_match_record_init, + .tp_new = py_match_record_new, + + }; + + return &py_match_record_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...MatchRecord. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_match_record_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'MatchRecord' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_match_record_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_MATCH_RECORD, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en conservation de correspondance. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_match_record(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_match_record_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to match record"); + break; + + case 1: + *((GMatchRecord **)dst) = G_MATCH_RECORD(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/record.h b/plugins/kaitai/python/record.h new file mode 100644 index 0000000..edf75fc --- /dev/null +++ b/plugins/kaitai/python/record.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * record.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/record.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORD_H +#define _PLUGINS_KAITAI_PYTHON_RECORD_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_match_record_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.MatchRecord'. */ +bool ensure_python_match_record_is_registered(void); + +/* Tente de convertir en conservation de correspondance. */ +int convert_to_match_record(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORD_H */ diff --git a/plugins/kaitai/python/records/Makefile.am b/plugins/kaitai/python/records/Makefile.am new file mode 100644 index 0000000..1413228 --- /dev/null +++ b/plugins/kaitai/python/records/Makefile.am @@ -0,0 +1,18 @@ + +noinst_LTLIBRARIES = libkaitaipythonrecords.la + +libkaitaipythonrecords_la_SOURCES = \ + empty.h empty.c \ + group.h group.c \ + item.h item.c \ + list.h list.c \ + module.h module.c \ + value.h value.c + +libkaitaipythonrecords_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libkaitaipythonrecords_la_SOURCES:%c=) diff --git a/plugins/kaitai/python/records/empty.c b/plugins/kaitai/python/records/empty.c new file mode 100644 index 0000000..9861a39 --- /dev/null +++ b/plugins/kaitai/python/records/empty.c @@ -0,0 +1,286 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * empty.c - équivalent Python du fichier "plugins/kaitai/parsers/empty.c" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "empty.h" + + +#include +#include + + +#include +#include +#include +#include +#include + + +#include "../parser.h" +#include "../record.h" +#include "../../records/empty-int.h" + + + +CREATE_DYN_CONSTRUCTOR(record_empty, G_TYPE_RECORD_EMPTY); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_record_empty_init(PyObject *, PyObject *, PyObject *); + +/* Produit une absence de valeur pour la correspondance. */ +static PyObject *py_record_empty_get_value(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_record_empty_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GKaitaiParser *parser; /* Analyseur défini créateur */ + GBinContent *content; /* Contenu binaire manipulé */ + vmpa2t *pos; /* Tête de lecture courante */ + int ret; /* Bilan de lecture des args. */ + GRecordEmpty *empty; /* Création GLib à transmettre */ + +#define RECORD_EMPTY_DOC \ + "The RecordEmpty object reflects absolutely no match and should only get" \ + " in some rare cases.\n" \ + "\n" \ + "Instances can be created using following constructor:\n" \ + "\n" \ + " RecordEmpty(parser, content, pos)" \ + "\n" \ + "Where *parser* is the creator of the record, as a" \ + " pychrysalide.plugins.kaitai.KaitaiParser instance, *content* is a" \ + " pychrysalide.analysis.BinContent instance providing the processed data" \ + " and *pos* defines the current reading location, as a" \ + " pychrysalide.arch.vmpa value." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&O&O&", + convert_to_kaitai_parser, &parser, + convert_to_binary_content, &content, + convert_any_to_vmpa, &pos); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + empty = G_RECORD_EMPTY(pygobject_get(self)); + + if (!g_record_empty_create(empty, parser, content, pos)) + { + clean_vmpa_arg(pos); + + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai stream.")); + return -1; + + } + + clean_vmpa_arg(pos); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Produit une absence de valeur pour la correspondance. * +* * +* Retour : None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_record_empty_get_value(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + +#define RECORD_EMPTY_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + value, py_record_empty, \ + "Always *None*.\n" \ + "\n" \ + "This attribute is only provided to mimic other" \ + " record types." \ +) + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_record_empty_type(void) +{ + static PyMethodDef py_record_empty_methods[] = { + { NULL } + }; + + static PyGetSetDef py_record_empty_getseters[] = { + RECORD_EMPTY_VALUE_ATTRIB, + { NULL } + }; + + static PyTypeObject py_record_empty_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.records.RecordEmpty", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = RECORD_EMPTY_DOC, + + .tp_methods = py_record_empty_methods, + .tp_getset = py_record_empty_getseters, + + .tp_init = py_record_empty_init, + .tp_new = py_record_empty_new, + + }; + + return &py_record_empty_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide...records.RecordEmpty. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_record_empty_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'RecordEmpty' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_record_empty_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.records"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_match_record_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_RECORD_EMPTY, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en zone de correspondance vide. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_record_empty(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_record_empty_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record empty"); + break; + + case 1: + *((GRecordEmpty **)dst) = G_RECORD_EMPTY(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/records/empty.h b/plugins/kaitai/python/records/empty.h new file mode 100644 index 0000000..ecd5fc9 --- /dev/null +++ b/plugins/kaitai/python/records/empty.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * empty.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/empty.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_EMPTY_H +#define _PLUGINS_KAITAI_PYTHON_RECORDS_EMPTY_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_record_empty_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordEmpty'. */ +bool ensure_python_record_empty_is_registered(void); + +/* Tente de convertir en zone de correspondance vide. */ +int convert_to_record_empty(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_EMPTY_H */ diff --git a/plugins/kaitai/python/records/group.c b/plugins/kaitai/python/records/group.c new file mode 100644 index 0000000..a050043 --- /dev/null +++ b/plugins/kaitai/python/records/group.c @@ -0,0 +1,305 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * group.h - équivalent Python du fichier "plugins/kaitai/parsers/group.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "group.h" + + +#include +#include + + +#include +#include +#include +#include +#include +#include + + +#include "../record.h" +#include "../parsers/struct.h" +#include "../../records/group-int.h" + + + +CREATE_DYN_CONSTRUCTOR(record_group, G_TYPE_RECORD_GROUP); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_record_group_init(PyObject *, PyObject *, PyObject *); + +/* Assure l'encadrement des accès aux champs d'une séquence. */ +static PyObject *py_record_group_getattr(PyObject *, char *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_record_group_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GKaitaiStruct *kstruct; /* Séquence définie créatrice */ + GBinContent *content; /* Contenu binaire analysé */ + int ret; /* Bilan de lecture des args. */ + GRecordGroup *group; /* Création GLib à transmettre */ + +#define RECORD_GROUP_DOC \ + "The RecordGroup class stores a map of parsed attributes with their" \ + " relative values. Each of theses Kaitai attributes can be accessed as" \ + " usual Python attribute.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " RecordGroup(kstruct, content)" \ + "\n" \ + "Where the *kstruct* refers to a" \ + " pychrysalide.plugins.kaitai.parsers.KaitaiStructure instance as the" \ + " creator of the newly created object, and *content* points to a" \ + " pychrysalide.analysis.BinContent instance." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&O&", + convert_to_kaitai_structure, &kstruct, + convert_to_binary_content, &content); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + group = G_RECORD_GROUP(pygobject_get(self)); + + if (!g_record_group_create(group, kstruct, content)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create record group.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = structure C convertie en Python. * +* name = nom du champ auquel un accès est demandé. * +* * +* Description : Assure l'encadrement des accès aux champs d'une séquence. * +* * +* Retour : Valeur du champ demandé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_record_group_getattr(PyObject *self, char *name) +{ + PyObject *result; /* Elément à retourner */ + GRecordGroup *group; /* Version native de l'objet */ + GMatchRecord *found; /* Sous-élément identifié */ + PyObject *w; /* Conversion du nom de champ */ + PyTypeObject *tp; /* Type de l'objet manipulé */ + + group = G_RECORD_GROUP(pygobject_get(self)); + + found = g_match_record_find_by_name(G_MATCH_RECORD(group), name, strlen(name), DIRECT_SEARCH_DEEP_LEVEL); + + if (found != NULL) + { + result = pygobject_new(G_OBJECT(found)); + g_object_unref(G_OBJECT(found)); + } + + else + { + w = PyUnicode_InternFromString(name); + if (w == NULL) return NULL; + + tp = Py_TYPE(self); + + if (tp->tp_base->tp_getattro != NULL) + result = tp->tp_base->tp_getattro(self, w); + + else + { + PyErr_Format(PyExc_AttributeError, + "type object '%.50s' has no attribute '%U'", + tp->tp_name, name); + result = NULL; + } + + Py_DECREF(w); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_record_group_type(void) +{ + static PyMethodDef py_record_group_methods[] = { + { NULL } + }; + + static PyGetSetDef py_record_group_getseters[] = { + { NULL } + }; + + static PyTypeObject py_record_group_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.records.RecordGroup", + .tp_basicsize = sizeof(PyGObject), + + .tp_getattr = py_record_group_getattr, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = RECORD_GROUP_DOC, + + .tp_methods = py_record_group_methods, + .tp_getset = py_record_group_getseters, + + .tp_init = py_record_group_init, + .tp_new = py_record_group_new, + + }; + + return &py_record_group_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide...records.RecordGroup. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_record_group_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'RecordGroup' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_record_group_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.records"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_match_record_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_RECORD_GROUP, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en correspondances attribut/binaire. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_record_group(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_record_group_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record group"); + break; + + case 1: + *((GRecordGroup **)dst) = G_RECORD_GROUP(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/records/group.h b/plugins/kaitai/python/records/group.h new file mode 100644 index 0000000..3e12ffc --- /dev/null +++ b/plugins/kaitai/python/records/group.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * group.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/group.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_GROUP_H +#define _PLUGINS_KAITAI_PYTHON_RECORDS_GROUP_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_record_group_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordGroup'. */ +bool ensure_python_record_group_is_registered(void); + +/* Tente de convertir en correspondances attribut/binaire. */ +int convert_to_record_group(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_GROUP_H */ diff --git a/plugins/kaitai/python/records/item.c b/plugins/kaitai/python/records/item.c new file mode 100644 index 0000000..84c2c58 --- /dev/null +++ b/plugins/kaitai/python/records/item.c @@ -0,0 +1,394 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * item.h - équivalent Python du fichier "plugins/kaitai/parsers/item.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "item.h" + + +#include +#include + + +#include +#include +#include +#include +#include +#include + + +#include "../record.h" +#include "../parsers/attribute.h" +#include "../../records/item-int.h" + + + +CREATE_DYN_CONSTRUCTOR(record_item, G_TYPE_RECORD_ITEM); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_record_item_init(PyObject *, PyObject *, PyObject *); + +/* Lit la série d'octets d'un élément Kaitai entier représenté. */ +static PyObject *py_record_item_get_truncated_bytes(PyObject *, void *); + +/* Lit la valeur d'un élément Kaitai entier représenté. */ +static PyObject *py_record_item_get_value(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_record_item_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GKaitaiAttribute *attrib; /* Attribut défini créateur */ + GBinContent *content; /* Contenu binaire analysé */ + mrange_t range; /* Espace couvert */ + SourceEndian endian; /* Boutisme à observer */ + int ret; /* Bilan de lecture des args. */ + GRecordItem *item; /* Création GLib à transmettre */ + +#define RECORD_ITEM_DOC \ + "The RecordItem class remembers a match between a described attribute and" \ + " its concret value read from parsed binary data." \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " RecordItem(content, range, endian, attrib)" \ + "\n" \ + "Where the *attrib* arguments refers to a" \ + " pychrysalide.plugins.kaitai.parsers.KaitaiAttribute instance as the" \ + " creator of the newly created object, *content* points to a" \ + " pychrysalide.analysis.BinContent instance, *range* is a" \ + " pychrysalide.arch.mrange object, *endian* states with a" \ + " pychrysalide.analysis.BinContent.SourceEndian hint the endianness used" \ + " to read integer values." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&O&O&", + convert_to_kaitai_attribute, &attrib, + convert_to_binary_content, &content, + convert_any_to_mrange, &range, + convert_to_binary_content, &endian); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + item = G_RECORD_ITEM(pygobject_get(self)); + + if (!g_record_item_create(item, attrib, content, &range, endian)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create record item.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Lit la série d'octets d'un élément Kaitai entier représenté. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_record_item_get_truncated_bytes(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GRecordItem *item; /* Version native de l'élément */ + bool status; /* Bilan d'opération */ + bin_t *out; /* Données brutes à transmettre*/ + size_t len; /* Quantité de ces données */ + +#define RECORD_ITEM_TRUNCATED_BYTES_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + truncated_bytes, py_record_item, \ + "Raw bytes carried by the item (truncated if a separator" \ + " is defined in the linked attribute), or None if irrelevant" \ + " regarding to the type of the attribute." \ +) + + item = G_RECORD_ITEM(pygobject_get(self)); + + status = g_record_item_get_truncated_bytes(item, &out, &len); + + if (status) + { + result = PyBytes_FromStringAndSize((char *)out, len); + free(out); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Lit la valeur d'un élément Kaitai entier représenté. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_record_item_get_value(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GRecordItem *item; /* Version native de l'élément */ + resolved_value_t resolved; /* Valeur sous forme générique */ + bool status; /* Bilan d'opération */ + +#define RECORD_ITEM_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + value, py_record_item, \ + "Carried value (as integer, bytes), or None in case of error." \ +) + + result = NULL; + + item = G_RECORD_ITEM(pygobject_get(self)); + + status = g_record_item_get_value(item, &resolved); + + if (status) + switch (resolved.type) + { + case GVT_ERROR: + assert(false); + PyErr_Format(PyExc_RuntimeError, + _("Error got while parsing Kaitai definition should not have been exported!")); + result = NULL; + break; + + case GVT_UNSIGNED_INTEGER: + result = PyLong_FromUnsignedLongLong(resolved.unsigned_integer); + break; + + case GVT_SIGNED_INTEGER: + result = PyLong_FromLongLong(resolved.signed_integer); + break; + + case GVT_FLOAT: + result = PyFloat_FromDouble(resolved.floating_number); + break; + + case GVT_BOOLEAN: + result = resolved.status ? Py_True : Py_False; + Py_INCREF(result); + break; + + case GVT_BYTES: + result = PyBytes_FromStringAndSize(resolved.bytes.data, resolved.bytes.len); + exit_szstr(&resolved.bytes); + break; + + case GVT_ARRAY: + result = pygobject_new(G_OBJECT(resolved.array)); + break; + + case GVT_RECORD: + result = pygobject_new(G_OBJECT(resolved.record)); + break; + + case GVT_STREAM: + result = pygobject_new(G_OBJECT(resolved.stream)); + break; + + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_record_item_type(void) +{ + static PyMethodDef py_record_item_methods[] = { + { NULL } + }; + + static PyGetSetDef py_record_item_getseters[] = { + RECORD_ITEM_TRUNCATED_BYTES_ATTRIB, + RECORD_ITEM_VALUE_ATTRIB, + { NULL } + }; + + static PyTypeObject py_record_item_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.records.RecordItem", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = RECORD_ITEM_DOC, + + .tp_methods = py_record_item_methods, + .tp_getset = py_record_item_getseters, + + .tp_init = py_record_item_init, + .tp_new = py_record_item_new, + + }; + + return &py_record_item_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide....records.RecordItem. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_record_item_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'RecordItem' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_record_item_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.records"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_match_record_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_RECORD_ITEM, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en correspondance attribut/binaire. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_record_item(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_record_item_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record item"); + break; + + case 1: + *((GRecordItem **)dst) = G_RECORD_ITEM(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/records/item.h b/plugins/kaitai/python/records/item.h new file mode 100644 index 0000000..bde8a55 --- /dev/null +++ b/plugins/kaitai/python/records/item.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * item.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/item.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_ITEM_H +#define _PLUGINS_KAITAI_PYTHON_RECORDS_ITEM_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_record_item_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordItem'. */ +bool ensure_python_record_item_is_registered(void); + +/* Tente de convertir en correspondance attribut/binaire. */ +int convert_to_record_item(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_ITEM_H */ diff --git a/plugins/kaitai/python/records/list.c b/plugins/kaitai/python/records/list.c new file mode 100644 index 0000000..d2eecbb --- /dev/null +++ b/plugins/kaitai/python/records/list.c @@ -0,0 +1,336 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * list.h - équivalent Python du fichier "plugins/kaitai/parsers/list.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "list.h" + + +#include +#include + + +#include +#include +#include +#include +#include +#include + + +#include "../record.h" +#include "../parsers/attribute.h" +#include "../../records/list-int.h" + + + +CREATE_DYN_CONSTRUCTOR(record_list, G_TYPE_RECORD_LIST); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_record_list_init(PyObject *, PyObject *, PyObject *); + +/* Dénombre le nombre de correspondances enregistrées. */ +static Py_ssize_t py_record_list_sq_length(PyObject *); + +/* Fournit un élément ciblé dans la liste de correspondances. */ +static PyObject *py_record_list_sq_item(PyObject *, Py_ssize_t); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_record_list_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + GKaitaiAttribute *attrib; /* Attribut défini créateur */ + GBinContent *content; /* Contenu binaire analysé */ + vmpa2t *addr; /* Adresse de symbole à ajouter*/ + int ret; /* Bilan de lecture des args. */ + GRecordList *list; /* Création GLib à transmettre */ + +#define RECORD_LIST_DOC \ + "The RecordList class collects a list of parsed attributes with their" \ + " relative values. Each of theses Kaitai attributes can be accessed as" \ + " subscriptable Python attribute.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " RecordList(content, attrib)" \ + "\n" \ + "Where the *attrib* argument refers to the" \ + " pychrysalide.plugins.kaitai.parsers.KaitaiAttribute instance used to" \ + " create each record contained by the list and *content* points to a" \ + " pychrysalide.analysis.BinContent instance." + + result = 0; + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&O&", + convert_to_kaitai_attribute, &attrib, + convert_to_binary_content, &content, + convert_any_to_vmpa, &addr); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) + { + result = -1; + goto exit; + } + + /* Eléments de base */ + + list = G_RECORD_LIST(pygobject_get(self)); + + if (!g_record_list_create(list, attrib, content, addr)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create record list.")); + result = -1; + goto exit; + } + + exit: + + clean_vmpa_arg(addr); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance Python manipulée. * +* * +* Description : Dénombre le nombre de correspondances enregistrées. * +* * +* Retour : Taille de la liste représentée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static Py_ssize_t py_record_list_sq_length(PyObject *self) +{ + Py_ssize_t result; /* Quantité à retourner */ + GRecordList *list; /* Version native de l'objet */ + + list = G_RECORD_LIST(pygobject_get(self)); + + result = g_record_list_count_records(list); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = structure C convertie en Python. * +* index = indice de la correspondance visée. * +* * +* Description : Fournit un élément ciblé dans la liste de correspondances. * +* * +* Retour : Instance de correspondance particulière, voire None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_record_list_sq_item(PyObject *self, Py_ssize_t index) +{ + PyObject *result; /* Instance à retourner */ + GRecordList *list; /* Version native de l'objet */ + GMatchRecord *record; /* Correspondance retrouvée */ + + list = G_RECORD_LIST(pygobject_get(self)); + + record = g_record_list_get_record(list, index); + + if (record != NULL) + { + result = pygobject_new(G_OBJECT(record)); + g_object_unref(G_OBJECT(record)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_record_list_type(void) +{ + static PySequenceMethods py_record_list_sequence_methods = { + + .sq_length = py_record_list_sq_length, + .sq_item = py_record_list_sq_item, + + }; + + static PyMethodDef py_record_list_methods[] = { + { NULL } + }; + + static PyGetSetDef py_record_list_getseters[] = { + { NULL } + }; + + static PyTypeObject py_record_list_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.records.RecordList", + .tp_basicsize = sizeof(PyGObject), + + .tp_as_sequence = &py_record_list_sequence_methods, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = RECORD_LIST_DOC, + + .tp_methods = py_record_list_methods, + .tp_getset = py_record_list_getseters, + + .tp_init = py_record_list_init, + .tp_new = py_record_list_new, + + }; + + return &py_record_list_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide....records.RecordList. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_record_list_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'RecordList' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_record_list_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.records"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_match_record_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_RECORD_LIST, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en correspondance attribut/binaire. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_record_list(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_record_list_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record list"); + break; + + case 1: + *((GRecordList **)dst) = G_RECORD_LIST(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/records/list.h b/plugins/kaitai/python/records/list.h new file mode 100644 index 0000000..53572a9 --- /dev/null +++ b/plugins/kaitai/python/records/list.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * list.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/list.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_LIST_H +#define _PLUGINS_KAITAI_PYTHON_RECORDS_LIST_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_record_list_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordList'. */ +bool ensure_python_record_list_is_registered(void); + +/* Tente de convertir en correspondance attribut/binaire. */ +int convert_to_record_list(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_LIST_H */ diff --git a/plugins/kaitai/python/records/module.c b/plugins/kaitai/python/records/module.c new file mode 100644 index 0000000..ea33c31 --- /dev/null +++ b/plugins/kaitai/python/records/module.c @@ -0,0 +1,122 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire records en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module.h" + + +#include + + +#include +#include + + +#include "empty.h" +#include "group.h" +#include "item.h" +#include "list.h" +#include "value.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Ajoute le module 'plugins.kaitai.records' au module Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_kaitai_records_module(void) +{ + bool result; /* Bilan à retourner */ + PyObject *super; /* Module à compléter */ + PyObject *module; /* Sous-module mis en place */ + +#define PYCHRYSALIDE_PLUGINS_KAITAI_RECORDS_DOC \ + "This module is providing objects used to link structure" \ + " definitions with their data." + + static PyModuleDef py_chrysalide_kaitai_records_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.plugins.kaitai.records", + .m_doc = PYCHRYSALIDE_PLUGINS_KAITAI_RECORDS_DOC, + + .m_size = -1, + + }; + + result = false; + + super = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + module = build_python_module(super, &py_chrysalide_kaitai_records_module); + + result = (module != NULL); + + assert(result); + + if (!result) + Py_XDECREF(module); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'plugins.kaitai.records'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_kaitai_records_module(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_record_empty_is_registered(); + if (result) result = ensure_python_record_group_is_registered(); + if (result) result = ensure_python_record_item_is_registered(); + if (result) result = ensure_python_record_list_is_registered(); + if (result) result = ensure_python_record_value_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/kaitai/python/records/module.h b/plugins/kaitai/python/records/module.h new file mode 100644 index 0000000..ff631dc --- /dev/null +++ b/plugins/kaitai/python/records/module.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire records en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_MODULE_H +#define _PLUGINS_KAITAI_PYTHON_RECORDS_MODULE_H + + +#include + + + +/* Ajoute le module 'plugins.kaitai.records' au module Python. */ +bool add_kaitai_records_module(void); + +/* Intègre les objets du module 'plugins.kaitai.records'. */ +bool populate_kaitai_records_module(void); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_MODULE_H */ diff --git a/plugins/kaitai/python/records/value.c b/plugins/kaitai/python/records/value.c new file mode 100644 index 0000000..bd4ad74 --- /dev/null +++ b/plugins/kaitai/python/records/value.c @@ -0,0 +1,335 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * value.c - équivalent Python du fichier "plugins/kaitai/parsers/value.c" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "value.h" + + +#include +#include + + +#include +#include +#include +#include +#include +#include + + +#include "../record.h" +#include "../scope.h" +#include "../parsers/instance.h" +#include "../../records/value-int.h" + + + +CREATE_DYN_CONSTRUCTOR(record_value, G_TYPE_RECORD_VALUE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_record_value_init(PyObject *, PyObject *, PyObject *); + +/* Lit la valeur d'un élément Kaitai entier représenté. */ +static PyObject *py_record_value_get_value(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_record_value_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GKaitaiInstance *inst; /* Instance définie créatrice */ + kaitai_scope_t *locals; /* Environnement local */ + int ret; /* Bilan de lecture des args. */ + GRecordValue *value; /* Création GLib à transmettre */ + +#define RECORD_VALUE_DOC \ + "The RecordValue class stores a link to an instance used to compute a" \ + " given value." \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " RecordValue(inst, locals)" \ + "\n" \ + "Where the *inst* arguments refers to a" \ + " pychrysalide.plugins.kaitai.parsers.KaitaiInstance instance as the" \ + " creator of the newly created object, *locals* points to a" \ + " pychrysalide.plugins.kaitai.KaitaiScope structure used as current scope." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&O&", + convert_to_kaitai_instance, &inst, + convert_to_kaitai_scope, &locals); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + value = G_RECORD_VALUE(pygobject_get(self)); + + if (!g_record_value_create(value, inst, locals)) + { + PyErr_SetString(PyExc_ValueError, _("Unable to create record value.")); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Lit la valeur d'un élément Kaitai entier représenté. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_record_value_get_value(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GRecordValue *value; /* Version native de l'élément */ + resolved_value_t resolved; /* Valeur sous forme générique */ + bool status; /* Bilan d'opération */ + +#define RECORD_VALUE_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + value, py_record_value, \ + "Carried value (as integer, bytes), or None in case of error." \ +) + + result = NULL; + + value = G_RECORD_VALUE(pygobject_get(self)); + + status = g_record_value_compute_and_aggregate_value(value, &resolved); + + if (status) + switch (resolved.type) + { + case GVT_ERROR: + assert(false); + PyErr_Format(PyExc_RuntimeError, + _("Error got while parsing Kaitai definition should not have been exported!")); + result = NULL; + break; + + case GVT_UNSIGNED_INTEGER: + result = PyLong_FromUnsignedLongLong(resolved.unsigned_integer); + break; + + case GVT_SIGNED_INTEGER: + result = PyLong_FromLongLong(resolved.signed_integer); + break; + + case GVT_FLOAT: + result = PyFloat_FromDouble(resolved.floating_number); + break; + + case GVT_BOOLEAN: + result = resolved.status ? Py_True : Py_False; + Py_INCREF(result); + break; + + case GVT_BYTES: + result = PyBytes_FromStringAndSize(resolved.bytes.data, resolved.bytes.len); + exit_szstr(&resolved.bytes); + break; + + case GVT_ARRAY: + result = pygobject_new(G_OBJECT(resolved.array)); + break; + + case GVT_RECORD: + result = pygobject_new(G_OBJECT(resolved.record)); + break; + + case GVT_STREAM: + result = pygobject_new(G_OBJECT(resolved.stream)); + break; + + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_record_value_type(void) +{ + static PyMethodDef py_record_value_methods[] = { + { NULL } + }; + + static PyGetSetDef py_record_value_getseters[] = { + RECORD_VALUE_VALUE_ATTRIB, + { NULL } + }; + + static PyTypeObject py_record_value_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.records.RecordValue", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = RECORD_VALUE_DOC, + + .tp_methods = py_record_value_methods, + .tp_getset = py_record_value_getseters, + + .tp_init = py_record_value_init, + .tp_new = py_record_value_new, + + }; + + return &py_record_value_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide...records.RecordValue. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_record_value_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'RecordValue' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_record_value_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai.records"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_match_record_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_RECORD_VALUE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en valeur calculée. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_record_value(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_record_value_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record value"); + break; + + case 1: + *((GRecordValue **)dst) = G_RECORD_VALUE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/records/value.h b/plugins/kaitai/python/records/value.h new file mode 100644 index 0000000..16cadcb --- /dev/null +++ b/plugins/kaitai/python/records/value.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * value.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/value.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_VALUE_H +#define _PLUGINS_KAITAI_PYTHON_RECORDS_VALUE_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_record_value_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordValue'. */ +bool ensure_python_record_value_is_registered(void); + +/* Tente de convertir en valeur calculée. */ +int convert_to_record_value(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_VALUE_H */ diff --git a/plugins/kaitai/python/scope.c b/plugins/kaitai/python/scope.c new file mode 100644 index 0000000..b11dc81 --- /dev/null +++ b/plugins/kaitai/python/scope.c @@ -0,0 +1,542 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * scope.c - équivalent Python du fichier "plugins/kaitai/scope.c" + * + * Copyright (C) 2020 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "scope.h" + + +#include +#include + + +#include +#include + + +#include "record.h" +#include "parsers/meta.h" +#include "../record.h" +#include "../parsers/meta.h" + + + +/* Rassemblement de données d'un paquet */ +typedef struct _py_kaitai_scope_t +{ + PyObject_HEAD /* A laisser en premier */ + + kaitai_scope_t *native; /* Tampon de données lié */ + +} py_kaitai_scope_t; + + +/* Libère de la mémoire un objet Python 'py_kaitai_scope_t'. */ +static void py_kaitai_scope_dealloc(py_kaitai_scope_t *); + +/* Initialise un objet Python de type 'py_kaitai_scope_t'. */ +static int py_kaitai_scope_init(py_kaitai_scope_t *, PyObject *, PyObject *); + +/* Conserve le souvenir de la dernière correspondance effectuée. */ +static PyObject *py_kaitai_scope_remember_last_record(PyObject *, PyObject *); + +/* Recherche la définition d'un type nouveau pour Kaitai. */ +static PyObject *py_kaitai_scope_find_sub_type(PyObject *, PyObject *); + +/* Retourne le souvenir d'une correspondance racine. */ +static PyObject *py_kaitai_scope_get_root_record(PyObject *, void *); + +/* Retourne le souvenir de la correspondance parente effectuée. */ +static PyObject *py_kaitai_scope_get_parent_record(PyObject *, void *); + +/* Retourne le souvenir de la dernière correspondance effectuée. */ +static PyObject *py_kaitai_scope_get_last_record(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = tampon de données à supprimer. * +* * +* Description : Libère de la mémoire un objet Python 'py_kaitai_scope_t'. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_kaitai_scope_dealloc(py_kaitai_scope_t *self) +{ + reset_record_scope(self->native); + + Py_TYPE(self)->tp_free((PyObject *)self); + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance d'objet à initialiser. * +* args = arguments passés pour l'appel. * +* kwds = mots clefs éventuellement fournis en complément. * +* * +* Description : Initialise un objet Python de type 'py_kaitai_scope_t'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_scope_init(py_kaitai_scope_t *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + GKaitaiMeta *meta; /* Informations globale */ + int ret; /* Bilan de lecture des args. */ + +#define KAITAI_SCOPE_DOC \ + "The KaitaiScope object stores a local environment which freezes" \ + " a particular state of the Kaitai parser. It allows the dynamic" \ + " resolving of values contained in a Kaitai expression.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KaitaiScope(meta)" \ + "\n" \ + "Where *meta* is a pychrysalide.plugins.kaitai.parsers.KaitaiMeta" \ + " instance pointing to global information about the Kaitai" \ + " definition." + + ret = PyArg_ParseTuple(args, "O&", convert_to_kaitai_meta, &meta); + if (!ret) return -1; + + self->native = malloc(sizeof(kaitai_scope_t)); + + init_record_scope(self->native, meta); + + result = 0; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = environnement local à manipuler. * +* args = arguments fournis pour la conduite de l'opération. * +* * +* Description : Conserve le souvenir de la dernière correspondance effectuée.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_scope_remember_last_record(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à faire remonter */ + GMatchRecord *record; /* Correspondance à utiliser */ + int ret; /* Bilan de lecture des args. */ + py_kaitai_scope_t *locals; /* Instance à manipuler */ + +#define KAITAI_SCOPE_REMEMBER_LAST_RECORD_METHOD PYTHON_METHOD_DEF \ +( \ + remember_last_record, "$self, record, /", \ + METH_VARARGS, py_kaitai_scope, \ + "Store a record as the last parsed record.\n" \ + "\n" \ + "This *record* is expected to be a" \ + " pychrysalide.plugins.kaitai.MatchRecord instance." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_match_record, &record); + if (!ret) return NULL; + + locals = (py_kaitai_scope_t *)self; + + remember_last_record(locals->native, record); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = environnement local à manipuler. * +* args = arguments fournis pour la conduite de l'opération. * +* * +* Description : Recherche la définition d'un type nouveau pour Kaitai. * +* * +* Retour : Type prêt à emploi ou NULL si non trouvé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_scope_find_sub_type(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à faire remonter */ + const char *name; /* Désignation à retrouver */ + int ret; /* Bilan de lecture des args. */ + py_kaitai_scope_t *locals; /* Instance à manipuler */ + GKaitaiType *type; /* Définition à identifier */ + +#define KAITAI_SCOPE_FIND_SUB_TYPE_METHOD PYTHON_METHOD_DEF \ +( \ + find_sub_type, "$self, name, /", \ + METH_VARARGS, py_kaitai_scope, \ + "Retrieve the type structure linked to a given name.\n" \ + "\n" \ + "This *name* has to be a string.\n" \ + "\n" \ + "The result is a known" \ + " pychrysalide.plugins.kaitai.parsers.KaitaiType instance" \ + " or *None* if the name has not been registered during" \ + " the parsing." \ +) + + ret = PyArg_ParseTuple(args, "s", &name); + if (!ret) return NULL; + + locals = (py_kaitai_scope_t *)self; + + type = find_sub_type(locals->native, name); + + result = pygobject_new(G_OBJECT(type)); + g_object_unref(G_OBJECT(type)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = environnement local à consulter. * +* closure = adresse non utilisée ici. * +* * +* Description : Retourne le souvenir d'une correspondance racine. * +* * +* Retour : Dernière correspondance établie ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_scope_get_root_record(PyObject *self, void *closure) +{ + PyObject *result; /* Conversion à retourner */ + py_kaitai_scope_t *locals; /* Instance à manipuler */ + GMatchRecord *record; /* Correspondance à convertir */ + +#define KAITAI_SCOPE_ROOT_RECORD_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + root_record, py_kaitai_scope, \ + "Provide the first record for a parsed content.\n" \ + "\n" \ + "The result is a pychrysalide.plugins.kaitai.MatchRecord" \ + " instance or *None*." \ +) + + locals = (py_kaitai_scope_t *)self; + + record = get_root_record(locals->native); + + if (record != NULL) + { + result = pygobject_new(G_OBJECT(record)); + g_object_unref(G_OBJECT(record)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = environnement local à consulter. * +* closure = adresse non utilisée ici. * +* * +* Description : Retourne le souvenir de la correspondance parente effectuée. * +* * +* Retour : Dernière correspondance établie ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_scope_get_parent_record(PyObject *self, void *closure) +{ + PyObject *result; /* Conversion à retourner */ + py_kaitai_scope_t *locals; /* Instance à manipuler */ + GMatchRecord *record; /* Correspondance à convertir */ + +#define KAITAI_SCOPE_PARENT_RECORD_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + parent_record, py_kaitai_scope, \ + "Provide the current parent record for a parsed content.\n" \ + "\n" \ + "The result is a pychrysalide.plugins.kaitai.MatchRecord" \ + " instance or *None*." \ +) + + locals = (py_kaitai_scope_t *)self; + + record = get_parent_record(locals->native); + + if (record != NULL) + { + result = pygobject_new(G_OBJECT(record)); + g_object_unref(G_OBJECT(record)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = environnement local à consulter. * +* closure = adresse non utilisée ici. * +* * +* Description : Retourne le souvenir de la dernière correspondance effectuée.* +* * +* Retour : Dernière correspondance établie ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_scope_get_last_record(PyObject *self, void *closure) +{ + PyObject *result; /* Conversion à retourner */ + py_kaitai_scope_t *locals; /* Instance à manipuler */ + GMatchRecord *record; /* Correspondance à convertir */ + +#define KAITAI_SCOPE_LAST_RECORD_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + last_record, py_kaitai_scope, \ + "Provide the last createdrecord for a parsed content.\n" \ + "\n" \ + "The result is a pychrysalide.plugins.kaitai.MatchRecord" \ + " instance or *None*." \ +) + + locals = (py_kaitai_scope_t *)self; + + record = get_last_record(locals->native); + + if (record != NULL) + { + result = pygobject_new(G_OBJECT(record)); + g_object_unref(G_OBJECT(record)); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_scope_type(void) +{ + static PyMethodDef py_kaitai_scope_methods[] = { + KAITAI_SCOPE_REMEMBER_LAST_RECORD_METHOD, + KAITAI_SCOPE_FIND_SUB_TYPE_METHOD, + { NULL } + }; + + static PyGetSetDef py_kaitai_scope_getseters[] = { + KAITAI_SCOPE_ROOT_RECORD_ATTRIB, + KAITAI_SCOPE_PARENT_RECORD_ATTRIB, + KAITAI_SCOPE_LAST_RECORD_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_scope_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.KaitaiScope", + .tp_basicsize = sizeof(py_kaitai_scope_t), + + .tp_dealloc = (destructor)py_kaitai_scope_dealloc, + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = KAITAI_SCOPE_DOC, + + .tp_methods = py_kaitai_scope_methods, + .tp_getset = py_kaitai_scope_getseters, + + .tp_init = (initproc)py_kaitai_scope_init, + .tp_new = PyType_GenericNew, + + }; + + return &py_kaitai_scope_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.common.PackedBuffer'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_scope_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'PackedBuffer' */ + PyObject *module; /* Module à recompléter */ + + type = get_python_kaitai_scope_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + if (PyType_Ready(type) != 0) + return false; + + module = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + if (!register_python_module_object(module, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = structure interne à copier en objet Python. * +* * +* Description : Convertit une structure 'kaitai_scope_t' en objet Python. * +* * +* Retour : Object Python résultant de la conversion opérée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *build_from_internal_kaitai_scope(const kaitai_scope_t *locals) +{ + PyObject *result; /* Instance à retourner */ + PyTypeObject *type; /* Type à instancier */ + + type = get_python_kaitai_scope_type(); + + result = PyObject_CallObject((PyObject *)type, NULL); + + copy_record_scope(((py_kaitai_scope_t *)result)->native, locals); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en environnement local pour Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_scope(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_scope_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai scope"); + break; + + case 1: + *((kaitai_scope_t **)dst) = ((py_kaitai_scope_t *)arg)->native; + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/scope.h b/plugins/kaitai/python/scope.h new file mode 100644 index 0000000..9353b06 --- /dev/null +++ b/plugins/kaitai/python/scope.h @@ -0,0 +1,51 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * scope.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/scope.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_SCOPE_H +#define _PLUGINS_KAITAI_PYTHON_SCOPE_H + + +#include +#include + + +#include "../scope.h" + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_scope_type(void); + +/* Prend en charge l'objet 'pychrysalide.common.PackedBuffer'. */ +bool ensure_python_kaitai_scope_is_registered(void); + +/* Convertit une structure 'kaitai_scope_t' en objet Python. */ +PyObject *build_from_internal_kaitai_scope(const kaitai_scope_t *); + +/* Tente de convertir en environnement local pour Kaitai. */ +int convert_to_kaitai_scope(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_SCOPE_H */ diff --git a/plugins/kaitai/python/stream.c b/plugins/kaitai/python/stream.c new file mode 100644 index 0000000..985e3c3 --- /dev/null +++ b/plugins/kaitai/python/stream.c @@ -0,0 +1,278 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.h - équivalent Python du fichier "plugins/kaitai/stream.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "stream.h" + + +#include + + +#include +#include +#include +#include +#include + + +#include "../stream-int.h" + + + +CREATE_DYN_CONSTRUCTOR(kaitai_stream, G_TYPE_KAITAI_STREAM); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_kaitai_stream_init(PyObject *, PyObject *, PyObject *); + +/* Détermine si la fin des données a été atteinte. */ +static PyObject *py_kaitai_stream_get_eof(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_kaitai_stream_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GBinContent *content; /* Contenu binaire manipulé */ + vmpa2t *pos; /* Tête de lecture courante */ + int ret; /* Bilan de lecture des args. */ + GKaitaiStream *stream; /* Création GLib à transmettre */ + +#define KAITAI_STREAM_DOC \ + "KaitaiStream collects all the information useful for the processing of" \ + " binary data." \ + "\n" \ + "Instances can be created using following constructor:\n" \ + "\n" \ + " KaitaiStream(content, pos)" \ + "\n" \ + "Where *content* is a pychrysalide.analysis.BinContent instance providing" \ + " the processed data and *pos* defines the current reading location, as a" \ + " pychrysalide.arch.vmpa value." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&O&", convert_to_binary_content, &content, convert_any_to_vmpa, &pos); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + stream = G_KAITAI_STREAM(pygobject_get(self)); + + if (!g_kaitai_stream_create(stream, content, pos)) + { + clean_vmpa_arg(pos); + + PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai stream.")); + return -1; + + } + + clean_vmpa_arg(pos); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si la fin des données a été atteinte. * +* * +* Retour : True si la tête de lecture est en position finale, ou False. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_kaitai_stream_get_eof(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GKaitaiStream *stream; /* Version native dyu flux */ + bool status; /* Etat de la position courante*/ + +#define KAITAI_STREAM_EOF_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + eof, py_kaitai_stream, \ + "Boolean value stating if the end of the stream" \ + " has been reached or not." \ +) + + stream = G_KAITAI_STREAM(pygobject_get(self)); + + status = g_kaitai_stream_has_reached_eof(stream); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_kaitai_stream_type(void) +{ + static PyMethodDef py_kaitai_stream_methods[] = { + { NULL } + }; + + static PyGetSetDef py_kaitai_stream_getseters[] = { + KAITAI_STREAM_EOF_ATTRIB, + { NULL } + }; + + static PyTypeObject py_kaitai_stream_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.kaitai.KaitaiStream", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = KAITAI_STREAM_DOC, + + .tp_methods = py_kaitai_stream_methods, + .tp_getset = py_kaitai_stream_getseters, + + .tp_init = py_kaitai_stream_init, + .tp_new = py_kaitai_stream_new, + + }; + + return &py_kaitai_stream_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiStream.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_kaitai_stream_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'KaitaiStream' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_kaitai_stream_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins.kaitai"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_STREAM, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en flux de données pour Kaitai. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_kaitai_stream(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_stream_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai stream"); + break; + + case 1: + *((GKaitaiStream **)dst) = G_KAITAI_STREAM(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/kaitai/python/stream.h b/plugins/kaitai/python/stream.h new file mode 100644 index 0000000..4f61358 --- /dev/null +++ b/plugins/kaitai/python/stream.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/stream.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_KAITAI_PYTHON_STREAM_H +#define _PLUGINS_KAITAI_PYTHON_STREAM_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_kaitai_stream_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.KaitaiStream'. */ +bool ensure_python_kaitai_stream_is_registered(void); + +/* Tente de convertir en flux de données pour Kaitai. */ +int convert_to_kaitai_stream(PyObject *, void *); + + + +#endif /* _PLUGINS_KAITAI_PYTHON_STREAM_H */ diff --git a/plugins/kaitai/record-int.h b/plugins/kaitai/record-int.h new file mode 100644 index 0000000..5ce5b2c --- /dev/null +++ b/plugins/kaitai/record-int.h @@ -0,0 +1,73 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * record-int.h - prototypes internes pour la mémorisation d'une correspondance avec 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 . + */ + + +#ifndef PLUGINS_KAITAI_RECORD_INT_H +#define PLUGINS_KAITAI_RECORD_INT_H + + +#include "record.h" + + +#include "parser.h" + + + +/* Calcule ou fournit la zone couverte par une correspondance. */ +typedef void (* get_record_range_fc) (const GMatchRecord *, mrange_t *); + +/* Recherche la correspondance associée à un identifiant. */ +typedef GMatchRecord * (* find_record_by_name_fc) (GMatchRecord *, const char *, size_t, unsigned int); + +/* Transforme une énumération en constante entière. */ +typedef bool (* resolve_record_enum_fc) (const GMatchRecord *, const sized_string_t *, const sized_string_t *, resolved_value_t *); + + +/* Correspondance entre un attribut et du binaire (instance) */ +struct _GMatchRecord +{ + GObject parent; /* A laisser en premier */ + + GKaitaiParser *creator; /* Lecteur à l'origine */ + + GBinContent *content; /* Contenu binaire analysé */ + +}; + +/* Correspondance entre un attribut et du binaire (classe) */ +struct _GMatchRecordClass +{ + GObjectClass parent; /* A laisser en premier */ + + get_record_range_fc get_range; /* Fourniture de couverture */ + find_record_by_name_fc find; /* Recherche selon identifiant */ + resolve_record_enum_fc resolve; /* Conversion d'une énumération*/ + +}; + + +/* Met en place une correspondance entre attribut et binaire. */ +bool g_match_record_create(GMatchRecord *, GKaitaiParser *, GBinContent *); + + + +#endif /* PLUGINS_KAITAI_RECORD_INT_H */ diff --git a/plugins/kaitai/record.c b/plugins/kaitai/record.c new file mode 100644 index 0000000..5717b17 --- /dev/null +++ b/plugins/kaitai/record.c @@ -0,0 +1,408 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * record.c - définition d'une correspondance avec 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 . + */ + + +#include "record.h" + + +#include + + +#include "expression.h" +#include "record-int.h" +#include "parsers/attribute.h" + + + +/* Initialise la classe des correspondances avec du binaire. */ +static void g_match_record_class_init(GMatchRecordClass *); + +/* Initialise une correspondance avec du binaire. */ +static void g_match_record_init(GMatchRecord *); + +/* Supprime toutes les références externes. */ +static void g_match_record_dispose(GMatchRecord *); + +/* Procède à la libération totale de la mémoire. */ +static void g_match_record_finalize(GMatchRecord *); + +/* Recherche la correspondance associée à un identifiant. */ +static GMatchRecord *_g_match_record_find_by_name(GMatchRecord *, const char *, size_t, unsigned int); + + + +/* Indique le type défini pour une correspondance avec du binaire. */ +G_DEFINE_TYPE(GMatchRecord, g_match_record, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des correspondances avec du binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_match_record_class_init(GMatchRecordClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_match_record_dispose; + object->finalize = (GObjectFinalizeFunc)g_match_record_finalize; + + klass->find = (find_record_by_name_fc)_g_match_record_find_by_name; + +} + + +/****************************************************************************** +* * +* Paramètres : record = instance à initialiser. * +* * +* Description : Initialise une correspondance avec du binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_match_record_init(GMatchRecord *record) +{ + record->creator = NULL; + + record->content = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : record = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_match_record_dispose(GMatchRecord *record) +{ + G_OBJECT_CLASS(g_match_record_parent_class)->dispose(G_OBJECT(record)); + +} + + +/****************************************************************************** +* * +* Paramètres : record = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_match_record_finalize(GMatchRecord *record) +{ + G_OBJECT_CLASS(g_match_record_parent_class)->finalize(G_OBJECT(record)); + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à initialiser pleinement. * +* creator = lecteur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* * +* Description : Met en place une correspondance entre attribut et binaire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_match_record_create(GMatchRecord *record, GKaitaiParser *creator, GBinContent *content) +{ + bool result; /* Bilan à retourner */ + + result = true; + + record->creator = creator; + g_object_ref(G_OBJECT(creator)); + + record->content = content; + + if (content != NULL) + g_object_ref(G_OBJECT(content)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à consulter. * +* * +* Description : Renvoie vers le lecteur à l'origine de la correspondance. * +* * +* Retour : Lecteur à l'origine de la création. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiParser *g_match_record_get_creator(const GMatchRecord *record) +{ + GKaitaiParser *result; /* Instance à retourner */ + + result = record->creator; + + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à modifier. * +* creator = lecteur à l'origine de la correspondance. * +* * +* Description : Modifie la référence au créateur de la correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_match_record_fix_creator(GMatchRecord *record, GKaitaiParser *creator) +{ + g_object_unref(G_OBJECT(record->creator)); + + record->creator = creator; + g_object_ref(G_OBJECT(creator)); + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à consulter. * +* * +* Description : Fournit le contenu lié à une correspondance établie. * +* * +* Retour : Contenu binaire associé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinContent *g_match_record_get_content(const GMatchRecord *record) +{ + GBinContent *result; /* Instance à retourner */ + + result = record->content; + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à consulter. * +* range = zone de couverture déterminée. [OUT] * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_match_record_get_range(const GMatchRecord *record, mrange_t *range) +{ + GMatchRecordClass *class; /* Classe de l'instance */ + + class = G_MATCH_RECORD_GET_CLASS(record); + + class->get_range(record, range); + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance établie à consulter. * +* out = tableau d'octets retournés. [OUT] * +* len = taille de ce tableau alloué. [OUT] * +* * +* Description : Lit les octets bruts couverts par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_match_record_read_raw_bytes(const GMatchRecord *record, bin_t **out, size_t *len) +{ + mrange_t range; /* Zone de correspondance */ + const bin_t *data; /* Accès aux données brutes */ + + g_match_record_get_range(record, &range); + + *len = get_mrange_length(&range); + + data = g_binary_content_get_raw_access(record->content, get_mrange_addr(&range), *len); + assert(data != NULL); + + *out = malloc(sizeof(bin_t) * (*len + 1)); + + memcpy(*out, data, *len); + (*out)[*len] = '\0'; + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à consulter. * +* name = désignation de l'élément recherché. * +* len = taille de cette désignation. * +* level = profondeur maximale à atteindre (fond : 0). * +* * +* Description : Recherche la correspondance associée à un identifiant. * +* * +* Retour : Correspondance trouvée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GMatchRecord *_g_match_record_find_by_name(GMatchRecord *record, const char *name, size_t len, unsigned int level) +{ + GMatchRecord *result; /* Trouvaille à retourner */ + const char *label; /* Etiquette à manipuler */ + size_t label_len; /* Taille de cette étiquette */ + + result = NULL; + + if (G_IS_KAITAI_ATTRIBUTE(record->creator)) + { + label = g_kaitai_attribute_get_label(G_KAITAI_ATTRIBUTE(record->creator)); + + label_len = strlen(label); + + if (label_len == len && strncmp(label, name, len) == 0) + { + result = record; + g_object_ref(G_OBJECT(result)); + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à consulter. * +* name = désignation de l'élément recherché. * +* len = taille de cette désignation. * +* level = profondeur maximale à atteindre (fond : 0). * +* * +* Description : Recherche la correspondance associée à un identifiant. * +* * +* Retour : Correspondance trouvée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GMatchRecord *g_match_record_find_by_name(GMatchRecord *record, const char *name, size_t len, unsigned int level) +{ + GMatchRecord *result; /* Trouvaille à retourner */ + GMatchRecordClass *class; /* Classe de l'instance */ + + class = G_MATCH_RECORD_GET_CLASS(record); + + result = class->find(record, name, len, level); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : record = correspondance à consulter. * +* name = désignation de l'élément recherché. * +* label = étiquette de l'élément constant à traduire. * +* value = valeur entière correspondante. [OUT] * +* * +* Description : Transforme une énumération en constante entière. * +* * +* Retour : Bilan de l'opération : true si la résolution est réalisée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_match_record_resolve_enum(const GMatchRecord *record, const sized_string_t *name, const sized_string_t *label, resolved_value_t *value) +{ + bool result; /* Bilan à retourner */ + GMatchRecordClass *class; /* Classe de l'instance */ + + class = G_MATCH_RECORD_GET_CLASS(record); + + if (class->resolve == NULL) + result = false; + + else + result = class->resolve(record, name, label, value); + + return result; + +} diff --git a/plugins/kaitai/record.h b/plugins/kaitai/record.h new file mode 100644 index 0000000..7db8187 --- /dev/null +++ b/plugins/kaitai/record.h @@ -0,0 +1,88 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * record.h - prototypes pour la définition d'une correspondance avec 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORD_H +#define _PLUGINS_KAITAI_RECORD_H + + +#include + + +#include +#include +#include + + + +/* Depuis parser.h : spécification d'un lecteur Kaitai (instance) */ +typedef struct _GKaitaiParser GKaitaiParser; + +/* Depuis expression.h : informations transportées par une expression */ +typedef struct _resolved_value_t resolved_value_t; + + + +#define G_TYPE_MATCH_RECORD g_match_record_get_type() +#define G_MATCH_RECORD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_MATCH_RECORD, GMatchRecord)) +#define G_IS_MATCH_RECORD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_MATCH_RECORD)) +#define G_MATCH_RECORD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_MATCH_RECORD, GMatchRecordClass)) +#define G_IS_MATCH_RECORD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_MATCH_RECORD)) +#define G_MATCH_RECORD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_MATCH_RECORD, GMatchRecordClass)) + + +/* Correspondance entre un attribut et du binaire (instance) */ +typedef struct _GMatchRecord GMatchRecord; + +/* Correspondance entre un attribut et du binaire (classe) */ +typedef struct _GMatchRecordClass GMatchRecordClass; + + +/* Indique le type défini pour une correspondance avec du binaire. */ +GType g_match_record_get_type(void); + +/* Renvoie vers le lecteur à l'origine de la correspondance. */ +GKaitaiParser *g_match_record_get_creator(const GMatchRecord *); + +/* Modifie la référence au créateur de la correspondance. */ +void g_match_record_fix_creator(GMatchRecord *, GKaitaiParser *); + +/* Fournit le contenu lié à une correspondance établie. */ +GBinContent *g_match_record_get_content(const GMatchRecord *); + +/* Calcule ou fournit la zone couverte par une correspondance. */ +void g_match_record_get_range(const GMatchRecord *, mrange_t *); + +/* Lit les octets bruts couverts par une correspondance. */ +void g_match_record_read_raw_bytes(const GMatchRecord *, bin_t **, size_t *); + +#define DIRECT_SEARCH_DEEP_LEVEL 1 + +/* Recherche la correspondance associée à un identifiant. */ +GMatchRecord *g_match_record_find_by_name(GMatchRecord *, const char *, size_t, unsigned int); + +/* Transforme une énumération en constante entière. */ +bool g_match_record_resolve_enum(const GMatchRecord *, const sized_string_t *, const sized_string_t *, resolved_value_t *); + + + +#endif /* _PLUGINS_KAITAI_RECORD_H */ diff --git a/plugins/kaitai/records/Makefile.am b/plugins/kaitai/records/Makefile.am new file mode 100644 index 0000000..c11e823 --- /dev/null +++ b/plugins/kaitai/records/Makefile.am @@ -0,0 +1,21 @@ + +noinst_LTLIBRARIES = libkaitairecords.la + +libkaitairecords_la_SOURCES = \ + empty-int.h \ + empty.h empty.c \ + group-int.h \ + group.h group.c \ + item-int.h \ + item.h item.c \ + list-int.h \ + list.h list.c \ + value-int.h \ + value.h value.c + +libkaitairecords_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libkaitairecords_la_SOURCES:%c=) diff --git a/plugins/kaitai/records/empty-int.h b/plugins/kaitai/records/empty-int.h new file mode 100644 index 0000000..2c6cefa --- /dev/null +++ b/plugins/kaitai/records/empty-int.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * empty-int.h - prototypes internes pour la notification d'une absence de correspondance attendue + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_EMPTY_INT_H +#define _PLUGINS_KAITAI_RECORDS_EMPTY_INT_H + + +#include "empty.h" + + +#include "../record-int.h" + + + +/* Marque d'une zone de correspondance vide (instance) */ +struct _GRecordEmpty +{ + GMatchRecord parent; /* A laisser en premier */ + + vmpa2t pos; /* Début d'une zone vide */ + +}; + +/* Marque d'une zone de correspondance vide (classe) */ +struct _GRecordEmptyClass +{ + GMatchRecordClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une zone de correspondance vide. */ +bool g_record_empty_create(GRecordEmpty *, GKaitaiParser *, GBinContent *, const vmpa2t *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_EMPTY_INT_H */ diff --git a/plugins/kaitai/records/empty.c b/plugins/kaitai/records/empty.c new file mode 100644 index 0000000..e5121e1 --- /dev/null +++ b/plugins/kaitai/records/empty.c @@ -0,0 +1,236 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * empty.c - conservation d'une correspondance entre attribut et binaire + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "empty.h" + + +#include +#include +#include + + +#include "empty-int.h" + + + +/* ------------------ DEFINITION D'UNE ZONE DE CORRESPONDANCE VIDE ------------------ */ + + +/* Initialise la classe des zones de correspondance vides. */ +static void g_record_empty_class_init(GRecordEmptyClass *); + +/* Initialise une zone de correspondance vide. */ +static void g_record_empty_init(GRecordEmpty *); + +/* Supprime toutes les références externes. */ +static void g_record_empty_dispose(GRecordEmpty *); + +/* Procède à la libération totale de la mémoire. */ +static void g_record_empty_finalize(GRecordEmpty *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Calcule ou fournit la zone couverte par une correspondance. */ +static void g_record_empty_get_range(const GRecordEmpty *, mrange_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UNE ZONE DE CORRESPONDANCE VIDE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une zone de correspondance vide. */ +G_DEFINE_TYPE(GRecordEmpty, g_record_empty, G_TYPE_MATCH_RECORD); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des zones de correspondance vides. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_empty_class_init(GRecordEmptyClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GMatchRecordClass *record; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_record_empty_dispose; + object->finalize = (GObjectFinalizeFunc)g_record_empty_finalize; + + record = G_MATCH_RECORD_CLASS(klass); + + record->get_range = (get_record_range_fc)g_record_empty_get_range; + +} + + +/****************************************************************************** +* * +* Paramètres : empty = instance à initialiser. * +* * +* Description : Initialise une zone de correspondance vide. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_empty_init(GRecordEmpty *empty) +{ + init_vmpa(&empty->pos, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + +} + + +/****************************************************************************** +* * +* Paramètres : empty = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_empty_dispose(GRecordEmpty *empty) +{ + G_OBJECT_CLASS(g_record_empty_parent_class)->dispose(G_OBJECT(empty)); + +} + + +/****************************************************************************** +* * +* Paramètres : empty = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_empty_finalize(GRecordEmpty *empty) +{ + G_OBJECT_CLASS(g_record_empty_parent_class)->finalize(G_OBJECT(empty)); + +} + + +/****************************************************************************** +* * +* Paramètres : parser = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* pos = emplacement de la zone vide à construire. * +* * +* Description : Crée une zone de correspondance vide à une position donnée. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GRecordEmpty *g_record_empty_new(GKaitaiParser *parser, GBinContent *content, const vmpa2t *pos) +{ + GRecordEmpty *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_RECORD_EMPTY, NULL); + + if (!g_record_empty_create(result, parser, content, pos)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : empty = correspondance à initialiser pleinement. * +* parser = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* pos = emplacement de la zone vide à construire. * +* * +* Description : Met en place une zone de correspondance vide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_empty_create(GRecordEmpty *empty, GKaitaiParser *parser, GBinContent *content, const vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + + result = g_match_record_create(G_MATCH_RECORD(empty), parser, content); + + if (result) + copy_vmpa(&empty->pos, pos); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : empty = correspondance à consulter. * +* range = zone de couverture déterminée. [OUT] * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_empty_get_range(const GRecordEmpty *empty, mrange_t *range) +{ + init_mrange(range, &empty->pos, 0); + +} diff --git a/plugins/kaitai/records/empty.h b/plugins/kaitai/records/empty.h new file mode 100644 index 0000000..4e89b62 --- /dev/null +++ b/plugins/kaitai/records/empty.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * empty.h - prototypes pour la notification d'une absence de correspondance attendue + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_EMPTY_H +#define _PLUGINS_KAITAI_RECORDS_EMPTY_H + + +#include + + +#include "../record.h" + + + +#define G_TYPE_RECORD_EMPTY g_record_empty_get_type() +#define G_RECORD_EMPTY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_EMPTY, GRecordEmpty)) +#define G_IS_RECORD_EMPTY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_EMPTY)) +#define G_RECORD_EMPTY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_EMPTY, GRecordEmptyClass)) +#define G_IS_RECORD_EMPTY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_EMPTY)) +#define G_RECORD_EMPTY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_EMPTY, GRecordEmptyClass)) + + +/* Marque d'une zone de correspondance vide (instance) */ +typedef struct _GRecordEmpty GRecordEmpty; + +/* Marque d'une zone de correspondance vide (classe) */ +typedef struct _GRecordEmptyClass GRecordEmptyClass; + + +/* Indique le type défini pour une zone de correspondance vide. */ +GType g_record_empty_get_type(void); + +/* Crée une zone de correspondance vide à une position donnée. */ +GRecordEmpty *g_record_empty_new(GKaitaiParser *, GBinContent *, const vmpa2t *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_EMPTY_H */ diff --git a/plugins/kaitai/records/group-int.h b/plugins/kaitai/records/group-int.h new file mode 100644 index 0000000..1fd0162 --- /dev/null +++ b/plugins/kaitai/records/group-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * group-int.h - prototypes internes pour la conservation d'un groupe de correspondance avec du binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_GROUP_INT_H +#define _PLUGINS_KAITAI_RECORDS_GROUP_INT_H + + +#include "group.h" + + +#include "../record-int.h" + + + +/* Groupe de correspondances établies entre attributs et binaire (instance) */ +struct _GRecordGroup +{ + GMatchRecord parent; /* A laisser en premier */ + + GMatchRecord **children; /* Sous-correspondances */ + size_t count; /* Taille de cette série */ + +}; + +/* Groupe de correspondances établies entre attributs et binaire (classe) */ +struct _GRecordGroupClass +{ + GMatchRecordClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une série de correspondances attribut/binaire. */ +bool g_record_group_create(GRecordGroup *, GKaitaiStruct *, GBinContent *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_GROUP_INT_H */ diff --git a/plugins/kaitai/records/group.c b/plugins/kaitai/records/group.c new file mode 100644 index 0000000..13327c8 --- /dev/null +++ b/plugins/kaitai/records/group.c @@ -0,0 +1,382 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * group.c - conservation d'un groupe de correspondance avec du binaire + * + * Copyright (C) 2023 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "group.h" + + +#include + + +#include "group-int.h" +#include "../parsers/attribute.h" + + + +/* ------------------ DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES ------------------ */ + + +/* Initialise la classe des groupes de correspondances. */ +static void g_record_group_class_init(GRecordGroupClass *); + +/* Initialise une série de correspondances attributs/binaire. */ +static void g_record_group_init(GRecordGroup *); + +/* Supprime toutes les références externes. */ +static void g_record_group_dispose(GRecordGroup *); + +/* Procède à la libération totale de la mémoire. */ +static void g_record_group_finalize(GRecordGroup *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Calcule ou fournit la zone couverte par une correspondance. */ +static void g_record_group_get_range(const GRecordGroup *, mrange_t *); + +/* Recherche la correspondance associée à un identifiant. */ +static GMatchRecord *g_record_group_find_by_name(GRecordGroup *, const char *, size_t, unsigned int); + +/* Transforme une énumération en constante entière. */ +static bool g_record_group_resolve_enum(const GRecordGroup *, const sized_string_t *, const sized_string_t *, resolved_value_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une série de correspondances entre attributes et binaire. */ +G_DEFINE_TYPE(GRecordGroup, g_record_group, G_TYPE_MATCH_RECORD); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des groupes de correspondances. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_group_class_init(GRecordGroupClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GMatchRecordClass *record; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_record_group_dispose; + object->finalize = (GObjectFinalizeFunc)g_record_group_finalize; + + record = G_MATCH_RECORD_CLASS(klass); + + record->get_range = (get_record_range_fc)g_record_group_get_range; + record->find = (find_record_by_name_fc)g_record_group_find_by_name; + record->resolve = (resolve_record_enum_fc)g_record_group_resolve_enum; + +} + + +/****************************************************************************** +* * +* Paramètres : group = instance à initialiser. * +* * +* Description : Initialise une série de correspondances attributs/binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_group_init(GRecordGroup *group) +{ + group->children = NULL; + group->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : group = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_group_dispose(GRecordGroup *group) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < group->count; i++) + g_clear_object(&group->children[i]); + + G_OBJECT_CLASS(g_record_group_parent_class)->dispose(G_OBJECT(group)); + +} + + +/****************************************************************************** +* * +* Paramètres : group = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_group_finalize(GRecordGroup *group) +{ + if (group->children != NULL) + free(group->children); + + G_OBJECT_CLASS(g_record_group_parent_class)->finalize(G_OBJECT(group)); + +} + + +/****************************************************************************** +* * +* Paramètres : content = contenu binaire lié à la correspondance. * +* kstruct = analyseur à l'origine de la correspondance. * +* * +* Description : Crée une nouvelle série de correspondances attribut/binaire. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GRecordGroup *g_record_group_new(GKaitaiStruct *kstruct, GBinContent *content) +{ + GRecordGroup *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_RECORD_GROUP, NULL); + + if (!g_record_group_create(result, kstruct, content)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : group = correspondance à initialiser pleinement. * +* kstruct = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* * +* Description : Met en place une série de correspondances attribut/binaire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_group_create(GRecordGroup *group, GKaitaiStruct *kstruct, GBinContent *content) +{ + bool result; /* Bilan à retourner */ + + result = g_match_record_create(G_MATCH_RECORD(group), G_KAITAI_PARSER(kstruct), content); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : group = ensemble de correspondances attribut/binaire. * +* record = sous-corresponde à intégrer. * +* * +* Description : Ajoute une correspondance supplémentaire à une série. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_record_group_add_record(GRecordGroup *group, GMatchRecord *record) +{ + group->children = realloc(group->children, ++group->count * sizeof(GMatchRecord)); + + group->children[group->count - 1] = record; + g_object_ref(G_OBJECT(record)); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : group = correspondance à consulter. * +* range = zone de couverture déterminée. [OUT] * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_group_get_range(const GRecordGroup *group, mrange_t *range) +{ + vmpa2t start; /* Position de départ */ + mrange_t range_0; /* Première zone couverte */ + mrange_t range_n; /* Dernière zone couverte */ + vmpa2t end; /* Position de d'arrivée */ + phys_t length; /* Taille de zone couverte */ + + if (group->count == 0) + { + init_vmpa(&start, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + init_mrange(range, &start, VMPA_NO_PHYSICAL); + } + + else + { + g_match_record_get_range(group->children[0], &range_0); + g_match_record_get_range(group->children[group->count - 1], &range_n); + + copy_vmpa(&start, get_mrange_addr(&range_0)); + + compute_mrange_end_addr(&range_n, &end); + length = compute_vmpa_diff(&start, &end); + + init_mrange(range, &start, length); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : group = ensemble de correspondances attribut/binaire. * +* name = désignation de l'élément recherché. * +* len = taille de cette désignation. * +* level = profondeur maximale à atteindre (fond : 0). * +* * +* Description : Recherche la correspondance associée à un identifiant. * +* * +* Retour : Correspondance trouvée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GMatchRecord *g_record_group_find_by_name(GRecordGroup *group, const char *name, size_t len, unsigned int level) +{ + GMatchRecord *result; /* Correspondance à renvoyer */ + GMatchRecordClass *class; /* Classe parente normalisée */ + size_t i; /* Boucle de parcours */ + + class = G_MATCH_RECORD_CLASS(g_record_group_parent_class); + + /** + * Le cas d'un type utilisateur peut rattacher un attribut Kaitai à un groupe... + */ + result = class->find(G_MATCH_RECORD(group), name, len, level); + + if (level > 0) + { + level--; + + for (i = 0; i < group->count && result == NULL; i++) + result = g_match_record_find_by_name(group->children[i], name, len, level); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : group = ensemble de correspondances attribut/binaire. * +* name = désignation de l'élément recherché. * +* label = étiquette de l'élément constant à traduire. * +* value = valeur entière correspondante. [OUT] * +* * +* Description : Transforme une énumération en constante entière. * +* * +* Retour : Bilan de l'opération : true si la résolution est réalisée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_record_group_resolve_enum(const GRecordGroup *group, const sized_string_t *name, const sized_string_t *label, resolved_value_t *value) +{ + bool result; /* Bilan à retourner */ + GMatchRecord *base; /* Autre version du groupe */ + size_t i; /* Boucle de parcours */ + GKaitaiEnum *kenum; /* Enumération à consulter */ + + result = false; + + base = G_MATCH_RECORD(group); + + if (G_IS_KAITAI_STRUCT(base->creator)) + { + kenum = g_kaitai_structure_get_enum(G_KAITAI_STRUCT(base->creator), name); + + if (kenum != NULL) + { + result = g_kaitai_enum_find_value(kenum, label, value); + g_object_unref(G_OBJECT(kenum)); + } + + } + + for (i = 0; i < group->count && !result; i++) + result = g_match_record_resolve_enum(group->children[i], name, label, value); + + return result; + +} diff --git a/plugins/kaitai/records/group.h b/plugins/kaitai/records/group.h new file mode 100644 index 0000000..454dade --- /dev/null +++ b/plugins/kaitai/records/group.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * group.h - prototypes pour la conservation d'un groupe de correspondance avec du binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_GROUP_H +#define _PLUGINS_KAITAI_RECORDS_GROUP_H + + +#include + + +#include + + +#include "../record.h" +#include "../parsers/struct.h" + + + +#define G_TYPE_RECORD_GROUP g_record_group_get_type() +#define G_RECORD_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_GROUP, GRecordGroup)) +#define G_IS_RECORD_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_GROUP)) +#define G_RECORD_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_GROUP, GRecordGroupClass)) +#define G_IS_RECORD_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_GROUP)) +#define G_RECORD_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_GROUP, GRecordGroupClass)) + + +/* Groupe de correspondances établies entre attributs et binaire (instance) */ +typedef struct _GRecordGroup GRecordGroup; + +/* Groupe de correspondances établies entre attributs et binaire (classe) */ +typedef struct _GRecordGroupClass GRecordGroupClass; + + +/* Indique le type défini pour une série de correspondances entre attributes et binaire. */ +GType g_record_group_get_type(void); + +/* Crée une nouvelle série de correspondances attribut/binaire. */ +GRecordGroup *g_record_group_new(GKaitaiStruct *, GBinContent *); + +/* Ajoute une correspondance supplémentaire à une série. */ +void g_record_group_add_record(GRecordGroup *, GMatchRecord *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_GROUP_H */ diff --git a/plugins/kaitai/records/item-int.h b/plugins/kaitai/records/item-int.h new file mode 100644 index 0000000..56e0a41 --- /dev/null +++ b/plugins/kaitai/records/item-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * item-int.h - prototypes internes pour la conservation d'une correspondance entre attribut et binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_ITEM_INT_H +#define _PLUGINS_KAITAI_RECORDS_ITEM_INT_H + + +#include "item.h" + + +#include "../record-int.h" + + + +/* Correspondance établie entre un attribut et du binaire (instance) */ +struct _GRecordItem +{ + GMatchRecord parent; /* A laisser en premier */ + + mrange_t range; /* Zone de binaire couverte */ + SourceEndian endian; /* Boutisme des données imposé */ + +}; + +/* Correspondance établie entre un attribut et du binaire (classe) */ +struct _GRecordItemClass +{ + GMatchRecordClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une correspondance entre attribut et binaire. */ +bool g_record_item_create(GRecordItem *, GKaitaiAttribute *, GBinContent *, const mrange_t *, SourceEndian); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_ITEM_INT_H */ diff --git a/plugins/kaitai/records/item.c b/plugins/kaitai/records/item.c new file mode 100644 index 0000000..0bcf9f7 --- /dev/null +++ b/plugins/kaitai/records/item.c @@ -0,0 +1,309 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * item.c - conservation d'une correspondance entre attribut et binaire + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "item.h" + + +#include +#include + + +#include "item-int.h" + + + +/* -------------------- DEFINITION D'UNE CORRESPONDANCE UNITAIRE -------------------- */ + + +/* Initialise la classe des correspondances attribut/binaire. */ +static void g_record_item_class_init(GRecordItemClass *); + +/* Initialise une correspondance entre attribut et binaire. */ +static void g_record_item_init(GRecordItem *); + +/* Supprime toutes les références externes. */ +static void g_record_item_dispose(GRecordItem *); + +/* Procède à la libération totale de la mémoire. */ +static void g_record_item_finalize(GRecordItem *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Calcule ou fournit la zone couverte par une correspondance. */ +static void g_record_item_get_range(const GRecordItem *, mrange_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UNE CORRESPONDANCE UNITAIRE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une correspondance entre un attribut et du binaire. */ +G_DEFINE_TYPE(GRecordItem, g_record_item, G_TYPE_MATCH_RECORD); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des correspondances attribut/binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_item_class_init(GRecordItemClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GMatchRecordClass *record; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_record_item_dispose; + object->finalize = (GObjectFinalizeFunc)g_record_item_finalize; + + record = G_MATCH_RECORD_CLASS(klass); + + record->get_range = (get_record_range_fc)g_record_item_get_range; + +} + + +/****************************************************************************** +* * +* Paramètres : item = instance à initialiser. * +* * +* Description : Initialise une correspondance entre attribut et binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_item_init(GRecordItem *item) +{ + copy_mrange(&item->range, UNUSED_MRANGE_PTR); + +} + + +/****************************************************************************** +* * +* Paramètres : item = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_item_dispose(GRecordItem *item) +{ + G_OBJECT_CLASS(g_record_item_parent_class)->dispose(G_OBJECT(item)); + +} + + +/****************************************************************************** +* * +* Paramètres : item = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_item_finalize(GRecordItem *item) +{ + G_OBJECT_CLASS(g_record_item_parent_class)->finalize(G_OBJECT(item)); + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* range = zone couverture par la correspondance. * +* endian = boustime des données à respecter. * +* * +* Description : Crée une nouvelle correspondance entre attribut et binaire. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GRecordItem *g_record_item_new(GKaitaiAttribute *attrib, GBinContent *content, const mrange_t *range, SourceEndian endian) +{ + GRecordItem *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_RECORD_ITEM, NULL); + + if (!g_record_item_create(result, attrib, content, range, endian)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : item = correspondance à initialiser pleinement. * +* attrib = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* range = zone couverte par la correspondance. * +* endian = boustime des données à respecter. * +* * +* Description : Met en place une correspondance entre attribut et binaire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_item_create(GRecordItem *item, GKaitaiAttribute *attrib, GBinContent *content, const mrange_t *range, SourceEndian endian) +{ + bool result; /* Bilan à retourner */ + + result = g_match_record_create(G_MATCH_RECORD(item), G_KAITAI_PARSER(attrib), content); + + if (result) + { + copy_mrange(&item->range, range); + + item->endian = endian; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : item = correspondance à consulter. * +* out = tableau d'octets retournés. [OUT] * +* len = taille de ce tableau alloué. [OUT] * +* * +* Description : Lit la série d'octets d'un élément Kaitai entier représenté. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_item_get_truncated_bytes(const GRecordItem *item, bin_t **out, size_t *len) +{ + bool result; /* Bilan à retourner */ + GKaitaiParser *parser; /* Attribut associé à l'élément*/ + + parser = g_match_record_get_creator(G_MATCH_RECORD(item)); + assert(G_IS_KAITAI_ATTRIBUTE(parser)); + + result = g_kaitai_attribute_read_truncated_bytes(G_KAITAI_ATTRIBUTE(parser), + G_MATCH_RECORD(item)->content, + &item->range, + out, len); + + g_object_unref(G_OBJECT(parser)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : item = correspondance à consulter. * +* 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_record_item_get_value(const GRecordItem *item, resolved_value_t *out) +{ + bool result; /* Bilan à retourner */ + GKaitaiParser *parser; /* Attribut associé à l'élément*/ + + parser = g_match_record_get_creator(G_MATCH_RECORD(item)); + assert(G_IS_KAITAI_ATTRIBUTE(parser)); + + result = g_kaitai_attribute_read_value(G_KAITAI_ATTRIBUTE(parser), + G_MATCH_RECORD(item)->content, + &item->range, + item->endian, out); + + g_object_unref(G_OBJECT(parser)); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : item = correspondance à consulter. * +* range = zone de couverture déterminée. [OUT] * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_item_get_range(const GRecordItem *item, mrange_t *range) +{ + copy_mrange(range, &item->range); + +} diff --git a/plugins/kaitai/records/item.h b/plugins/kaitai/records/item.h new file mode 100644 index 0000000..1286a89 --- /dev/null +++ b/plugins/kaitai/records/item.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * item.h - prototypes pour la conservation d'une correspondance entre attribut et binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_ITEM_H +#define _PLUGINS_KAITAI_RECORDS_ITEM_H + + +#include + + +#include "../record.h" +#include "../parsers/attribute.h" + + + +#define G_TYPE_RECORD_ITEM g_record_item_get_type() +#define G_RECORD_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_ITEM, GRecordItem)) +#define G_IS_RECORD_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_ITEM)) +#define G_RECORD_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_ITEM, GRecordItemClass)) +#define G_IS_RECORD_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_ITEM)) +#define G_RECORD_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_ITEM, GRecordItemClass)) + + +/* Correspondance établie entre un attribut et du binaire (instance) */ +typedef struct _GRecordItem GRecordItem; + +/* Correspondance établie entre un attribut et du binaire (classe) */ +typedef struct _GRecordItemClass GRecordItemClass; + + +/* Indique le type défini pour une correspondance entre un attribut et du binaire. */ +GType g_record_item_get_type(void); + +/* Crée une nouvelle correspondance entre attribut et binaire. */ +GRecordItem *g_record_item_new(GKaitaiAttribute *, GBinContent *, const mrange_t *, SourceEndian); + +/* Lit la série d'octets d'un élément Kaitai entier représenté. */ +bool g_record_item_get_truncated_bytes(const GRecordItem *, bin_t **, size_t *); + +/* Lit la valeur d'un élément Kaitai entier représenté. */ +bool g_record_item_get_value(const GRecordItem *, resolved_value_t *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_ITEM_H */ diff --git a/plugins/kaitai/records/list-int.h b/plugins/kaitai/records/list-int.h new file mode 100644 index 0000000..88b411d --- /dev/null +++ b/plugins/kaitai/records/list-int.h @@ -0,0 +1,60 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * list-int.h - prototypes internes pour la conservation d'une liste de correspondance avec du binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_LIST_INT_H +#define _PLUGINS_KAITAI_RECORDS_LIST_INT_H + + +#include "list.h" + + +#include "../record-int.h" + + + +/* Liste de correspondances établies entre attributs et binaire (instance) */ +struct _GRecordList +{ + GMatchRecord parent; /* A laisser en premier */ + + vmpa2t pos; /* Début de zone */ + + GMatchRecord **children; /* Sous-correspondances */ + size_t count; /* Taille de cette série */ + +}; + +/* Liste de correspondances établies entre attributs et binaire (classe) */ +struct _GRecordListClass +{ + GMatchRecordClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une série de correspondances attribut/binaire. */ +bool g_record_list_create(GRecordList *, GKaitaiAttribute *, GBinContent *, const vmpa2t *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_LIST_INT_H */ diff --git a/plugins/kaitai/records/list.c b/plugins/kaitai/records/list.c new file mode 100644 index 0000000..1a36bf5 --- /dev/null +++ b/plugins/kaitai/records/list.c @@ -0,0 +1,424 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * list.c - conservation d'une liste de correspondance avec du binaire + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "list.h" + + +#include +#include + + +#include "list-int.h" + + + +/* ------------------ DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES ------------------ */ + + +/* Initialise la classe des listes de correspondances. */ +static void g_record_list_class_init(GRecordListClass *); + +/* Initialise une série de correspondances attributs/binaire. */ +static void g_record_list_init(GRecordList *); + +/* Supprime toutes les références externes. */ +static void g_record_list_dispose(GRecordList *); + +/* Procède à la libération totale de la mémoire. */ +static void g_record_list_finalize(GRecordList *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Calcule ou fournit la zone couverte par une correspondance. */ +static void g_record_list_get_range(const GRecordList *, mrange_t *); + +/* Recherche la correspondance associée à un identifiant. */ +static GMatchRecord *g_record_list_find_by_name(GRecordList *, const char *, size_t, unsigned int); + +/* Transforme une énumération en constante entière. */ +static bool g_record_list_resolve_enum(const GRecordList *, const sized_string_t *, const sized_string_t *, resolved_value_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une série de correspondances entre attributes et binaire. */ +G_DEFINE_TYPE(GRecordList, g_record_list, G_TYPE_MATCH_RECORD); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des listes de correspondances. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_list_class_init(GRecordListClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GMatchRecordClass *record; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_record_list_dispose; + object->finalize = (GObjectFinalizeFunc)g_record_list_finalize; + + record = G_MATCH_RECORD_CLASS(klass); + + record->get_range = (get_record_range_fc)g_record_list_get_range; + record->find = (find_record_by_name_fc)g_record_list_find_by_name; + record->resolve = (resolve_record_enum_fc)g_record_list_resolve_enum; + +} + + +/****************************************************************************** +* * +* Paramètres : list = instance à initialiser. * +* * +* Description : Initialise une série de correspondances attributs/binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_list_init(GRecordList *list) +{ + list->children = NULL; + list->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : list = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_list_dispose(GRecordList *list) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < list->count; i++) + g_clear_object(&list->children[i]); + + G_OBJECT_CLASS(g_record_list_parent_class)->dispose(G_OBJECT(list)); + +} + + +/****************************************************************************** +* * +* Paramètres : list = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_list_finalize(GRecordList *list) +{ + if (list->children != NULL) + free(list->children); + + G_OBJECT_CLASS(g_record_list_parent_class)->finalize(G_OBJECT(list)); + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* pos = début de la zone de couverture de la liste. * +* * +* Description : Crée une nouvelle série de correspondances attribut/binaire. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GRecordList *g_record_list_new(GKaitaiAttribute *attrib, GBinContent *content, const vmpa2t *pos) +{ + GRecordList *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_RECORD_LIST, NULL); + + if (!g_record_list_create(result, attrib, content, pos)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : list = correspondance à initialiser pleinement. * +* attrib = analyseur à l'origine de la correspondance. * +* content = contenu binaire lié à la correspondance. * +* * +* Description : Met en place une série de correspondances attribut/binaire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_list_create(GRecordList *list, GKaitaiAttribute *attrib, GBinContent *content, const vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + + result = g_match_record_create(G_MATCH_RECORD(list), G_KAITAI_PARSER(attrib), content); + + if (result) + copy_vmpa(&list->pos, pos); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : list = ensemble de correspondances attribut/binaire. * +* * +* Description : Dénombre le nombre de correspondances enregistrées. * +* * +* Retour : Taille de la liste représentée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +size_t g_record_list_count_records(const GRecordList *list) +{ + size_t result; /* Quantité à retourner */ + + result = list->count; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : list = ensemble de correspondances attribut/binaire. * +* record = sous-corresponde à intégrer. * +* * +* Description : Ajoute une correspondance supplémentaire à une série. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_record_list_add_record(GRecordList *list, GMatchRecord *record) +{ + list->children = realloc(list->children, ++list->count * sizeof(GMatchRecord)); + + list->children[list->count - 1] = record; + g_object_ref(G_OBJECT(record)); + +} + + +/****************************************************************************** +* * +* Paramètres : list = ensemble de correspondances attribut/binaire. * +* index = indice de la correspondance visée. * +* * +* Description : Fournit un élément ciblé dans la liste de correspondances. * +* * +* Retour : Instance de correspondance particulière, voire NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GMatchRecord *g_record_list_get_record(const GRecordList *list, size_t index) +{ + GMatchRecord *result; /* Instance à retourner */ + + if (index < list->count) + { + result = list->children[index]; + g_object_ref(G_OBJECT(result)); + } + else + result = NULL; + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : list = correspondance à consulter. * +* range = zone de couverture déterminée. [OUT] * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_list_get_range(const GRecordList *list, mrange_t *range) +{ + vmpa2t start; /* Position de départ */ + mrange_t range_0; /* Première zone couverte */ + mrange_t range_n; /* Dernière zone couverte */ + vmpa2t end; /* Position de d'arrivée */ + phys_t length; /* Taille de zone couverte */ + + assert(list->count > 0); + + if (list->count == 0) + { + init_vmpa(&start, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + init_mrange(range, &start, VMPA_NO_PHYSICAL); + } + + else + { + g_match_record_get_range(list->children[0], &range_0); + g_match_record_get_range(list->children[list->count - 1], &range_n); + + copy_vmpa(&start, get_mrange_addr(&range_0)); + + compute_mrange_end_addr(&range_n, &end); + length = compute_vmpa_diff(&start, &end); + + init_mrange(range, &start, length); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : list = ensemble de correspondances attribut/binaire. * +* name = désignation de l'élément recherché. * +* len = taille de cette désignation. * +* level = profondeur maximale à atteindre (fond : 0). * +* * +* Description : Recherche la correspondance associée à un identifiant. * +* * +* Retour : Correspondance trouvée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GMatchRecord *g_record_list_find_by_name(GRecordList *list, const char *name, size_t len, unsigned int level) +{ + GMatchRecord *result; /* Correspondance à renvoyer */ + GMatchRecordClass *class; /* Classe parente normalisée */ + size_t i; /* Boucle de parcours */ + + class = G_MATCH_RECORD_CLASS(g_record_list_parent_class); + + result = class->find(G_MATCH_RECORD(list), name, len, level); + + if (level > 0) + { + for (i = 0; i < list->count && result == NULL; i++) + result = g_match_record_find_by_name(list->children[i], name, len, level); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : list = ensemble de correspondances attribut/binaire. * +* name = désignation de l'élément recherché. * +* label = étiquette de l'élément constant à traduire. * +* value = valeur entière correspondante. [OUT] * +* * +* Description : Transforme une énumération en constante entière. * +* * +* Retour : Bilan de l'opération : true si la résolution est réalisée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_record_list_resolve_enum(const GRecordList *list, const sized_string_t *name, const sized_string_t *label, resolved_value_t *value) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + /** + * Comme les types peuvent être sélectionnés dynamiquement, le parcours + * de l'ensemble des sous-noeuds doit être effectué. + */ + + result = false; + + for (i = 0; i < list->count && !result; i++) + result = g_match_record_resolve_enum(list->children[i], name, label, value); + + return result; + +} diff --git a/plugins/kaitai/records/list.h b/plugins/kaitai/records/list.h new file mode 100644 index 0000000..03e593e --- /dev/null +++ b/plugins/kaitai/records/list.h @@ -0,0 +1,71 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * list.h - prototypes pour la conservation d'une liste de correspondance avec du binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_LIST_H +#define _PLUGINS_KAITAI_RECORDS_LIST_H + + +#include + + +#include + + +#include "../record.h" +#include "../parsers/attribute.h" + + + +#define G_TYPE_RECORD_LIST g_record_list_get_type() +#define G_RECORD_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_LIST, GRecordList)) +#define G_IS_RECORD_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_LIST)) +#define G_RECORD_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_LIST, GRecordListClass)) +#define G_IS_RECORD_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_LIST)) +#define G_RECORD_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_LIST, GRecordListClass)) + + +/* Liste de correspondances établies entre attributs et binaire (instance) */ +typedef struct _GRecordList GRecordList; + +/* Liste de correspondances établies entre attributs et binaire (classe) */ +typedef struct _GRecordListClass GRecordListClass; + + +/* Indique le type défini pour une série de correspondances entre attributes et binaire. */ +GType g_record_list_get_type(void); + +/* Crée une nouvelle série de correspondances attribut/binaire. */ +GRecordList *g_record_list_new(GKaitaiAttribute *, GBinContent *, const vmpa2t *); + +/* Dénombre le nombre de correspondances enregistrées. */ +size_t g_record_list_count_records(const GRecordList *); + +/* Ajoute une correspondance supplémentaire à une série. */ +void g_record_list_add_record(GRecordList *, GMatchRecord *); + +/* Fournit un élément ciblé dans la liste de correspondances. */ +GMatchRecord *g_record_list_get_record(const GRecordList *, size_t); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_LIST_H */ diff --git a/plugins/kaitai/records/value-int.h b/plugins/kaitai/records/value-int.h new file mode 100644 index 0000000..6a84a7f --- /dev/null +++ b/plugins/kaitai/records/value-int.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * value-int.h - prototypes internes pour la conservation d'une instance virtuelle + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_VALUE_INT_H +#define _PLUGINS_KAITAI_RECORDS_VALUE_INT_H + + +#include "value.h" + + +#include "../record-int.h" + + + +/* Valeur calculée selon des correspondances parallèles (instance) */ +struct _GRecordValue +{ + GMatchRecord parent; /* A laisser en premier */ + + kaitai_scope_t locals; /* Sauvegarde de contexte */ + +}; + +/* Valeur calculée selon des correspondances parallèles (classe) */ +struct _GRecordValueClass +{ + GMatchRecordClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une valeur calculée selon des correspondances. */ +bool g_record_value_create(GRecordValue *, GKaitaiInstance *, const kaitai_scope_t *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_VALUE_INT_H */ diff --git a/plugins/kaitai/records/value.c b/plugins/kaitai/records/value.c new file mode 100644 index 0000000..cafe5c3 --- /dev/null +++ b/plugins/kaitai/records/value.c @@ -0,0 +1,336 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * value.c - conservation d'une correspondance entre attribut et binaire + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "value.h" + + +#include +#include +#include + + +#include "value-int.h" +#include "../parsers/attribute.h" + + + +/* -------------------- DEFINITION D'UNE CORRESPONDANCE UNITAIRE -------------------- */ + + +/* Initialise la classe des valeurs purement calculées. */ +static void g_record_value_class_init(GRecordValueClass *); + +/* Initialise une correspondance entre attribut et binaire. */ +static void g_record_value_init(GRecordValue *); + +/* Supprime toutes les références externes. */ +static void g_record_value_dispose(GRecordValue *); + +/* Procède à la libération totale de la mémoire. */ +static void g_record_value_finalize(GRecordValue *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Calcule ou fournit la zone couverte par une correspondance. */ +static void g_record_value_get_range(const GRecordValue *, mrange_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UNE CORRESPONDANCE UNITAIRE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une valeur calculée selon des correspondances établies. */ +G_DEFINE_TYPE(GRecordValue, g_record_value, G_TYPE_MATCH_RECORD); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des valeurs purement calculées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_value_class_init(GRecordValueClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GMatchRecordClass *record; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_record_value_dispose; + object->finalize = (GObjectFinalizeFunc)g_record_value_finalize; + + record = G_MATCH_RECORD_CLASS(klass); + + record->get_range = (get_record_range_fc)g_record_value_get_range; + +} + + +/****************************************************************************** +* * +* Paramètres : value = instance à initialiser. * +* * +* Description : Initialise une correspondance entre attribut et binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_value_init(GRecordValue *value) +{ + init_record_scope(&value->locals, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : value = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_value_dispose(GRecordValue *value) +{ + reset_record_scope(&value->locals); + + G_OBJECT_CLASS(g_record_value_parent_class)->dispose(G_OBJECT(value)); + +} + + +/****************************************************************************** +* * +* Paramètres : value = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_value_finalize(GRecordValue *value) +{ + G_OBJECT_CLASS(g_record_value_parent_class)->finalize(G_OBJECT(value)); + +} + + +/****************************************************************************** +* * +* Paramètres : inst = analyseur à l'origine de la correspondance. * +* locals = correspondances courantes pour résolutions. * +* * +* Description : Crée une nouvelle valeur calculée à partir d'une instance. * +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GRecordValue *g_record_value_new(GKaitaiInstance *inst, const kaitai_scope_t *locals) +{ + GRecordValue *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_RECORD_VALUE, NULL); + + if (!g_record_value_create(result, inst, locals)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = correspondance à initialiser pleinement. * +* inst = analyseur à l'origine de la correspondance. * +* locals = correspondances courantes pour résolutions. * +* * +* Description : Met en place une valeur calculée à partir d'une instance. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_value_create(GRecordValue *value, GKaitaiInstance *inst, const kaitai_scope_t *locals) +{ + bool result; /* Bilan à retourner */ + + result = g_match_record_create(G_MATCH_RECORD(value), G_KAITAI_PARSER(inst), NULL); + + if (result) + copy_record_scope(&value->locals, locals); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = correspondance à consulter. * +* value = valeur à sauvegarder sous une forme générique. [OUT] * +* * +* Description : Détermine la valeur d'un élément Kaitai calculé. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_value_compute_value(const GRecordValue *value, resolved_value_t *out) +{ + bool result; /* Bilan à retourner */ + GKaitaiParser *parser; /* Instance liée à l'élément */ + + parser = g_match_record_get_creator(G_MATCH_RECORD(value)); + assert(G_IS_KAITAI_ATTRIBUTE(parser)); + + result = g_kaitai_instance_compute_value(G_KAITAI_INSTANCE(parser), + &value->locals, + out); + + g_object_unref(G_OBJECT(parser)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : value = correspondance à consulter. * +* value = valeur à sauvegarder sous une forme générique. [OUT] * +* * +* Description : Détermine et ajuste la valeur d'un élément Kaitai calculé. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_record_value_compute_and_aggregate_value(const GRecordValue *value, resolved_value_t *out) +{ + bool result; /* Bilan à retourner */ + GKaitaiParser *parser; /* Instance liée à l'élément */ + sized_string_t converted; /* Conversion finale ? */ + + parser = g_match_record_get_creator(G_MATCH_RECORD(value)); + assert(G_IS_KAITAI_ATTRIBUTE(parser)); + + result = g_kaitai_instance_compute_value(G_KAITAI_INSTANCE(parser), + &value->locals, + out); + + g_object_unref(G_OBJECT(parser)); + + if (result) + { + /** + * Lorsque c'est possible, les tableaux Kaitai sont transformés en série + * d'octets. + * + * Même si les tableaux ont une grande portée en interne des règles + * Kaitai (par exemple pour constituer une table de constantes de + * référence), il en est différemment à l'extérieur du processus de + * traitement : les tableaux sont le plus souvent destinés à manipuler + * les octets représentés directement (par exemple : + * "contents: [0xca, 0xfe, 0xba, 0xbe]"). + * + * Pour les valeurs d'instance dont le type n'est pas explicite, + * le choix est fait de tenter de simplifier la vie de l'utilisateur + * en lui fournissant directement les octets qu'il attend probablement + * plutôt qu'un tableau contenant des octets à extraire. + */ + + if (out->type == GVT_ARRAY) + { + if (g_kaitai_array_convert_to_bytes(out->array, &converted)) + { + EXIT_RESOLVED_VALUE(*out); + + out->bytes = converted; + out->type = GVT_BYTES; + + } + + } + + } + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : value = correspondance à consulter. * +* range = zone de couverture déterminée. [OUT] * +* * +* Description : Calcule ou fournit la zone couverte par une correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_record_value_get_range(const GRecordValue *value, mrange_t *range) +{ + copy_mrange(range, UNUSED_MRANGE_PTR); + +} diff --git a/plugins/kaitai/records/value.h b/plugins/kaitai/records/value.h new file mode 100644 index 0000000..8ee9cdd --- /dev/null +++ b/plugins/kaitai/records/value.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * value.h - prototypes pour la conservation d'une correspondance entre attribut et binaire + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_RECORDS_VALUE_H +#define _PLUGINS_KAITAI_RECORDS_VALUE_H + + +#include + + +#include "../record.h" +#include "../parsers/instance.h" + + + +#define G_TYPE_RECORD_VALUE g_record_value_get_type() +#define G_RECORD_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_VALUE, GRecordValue)) +#define G_IS_RECORD_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_VALUE)) +#define G_RECORD_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_VALUE, GRecordValueClass)) +#define G_IS_RECORD_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_VALUE)) +#define G_RECORD_VALUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_VALUE, GRecordValueClass)) + + +/* Valeur calculée selon des correspondances parallèles (instance) */ +typedef struct _GRecordValue GRecordValue; + +/* Valeur calculée selon des correspondances parallèles (classe) */ +typedef struct _GRecordValueClass GRecordValueClass; + + +/* Indique le type défini pour une valeur calculée selon des correspondances établies. */ +GType g_record_value_get_type(void); + +/* Crée une nouvelle valeur calculée à partir d'une instance. */ +GRecordValue *g_record_value_new(GKaitaiInstance *, const kaitai_scope_t *); + +/* Détermine la valeur d'un élément Kaitai entier calculé. */ +bool g_record_value_compute_value(const GRecordValue *, resolved_value_t *); + +/* Détermine et ajuste la valeur d'un élément Kaitai calculé. */ +bool g_record_value_compute_and_aggregate_value(const GRecordValue *, resolved_value_t *); + + + +#endif /* _PLUGINS_KAITAI_RECORDS_VALUE_H */ diff --git a/plugins/kaitai/scope.c b/plugins/kaitai/scope.c new file mode 100644 index 0000000..6d1d47a --- /dev/null +++ b/plugins/kaitai/scope.c @@ -0,0 +1,257 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * scope.c - recherches d'éléments de lecture + * + * Copyright (C) 2023 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "scope.h" + + +#include "parsers/struct.h" + + + +/****************************************************************************** +* * +* Paramètres : locals = contexte de variables locales à initialiser. * +* meta = informations générales à disposition. * +* * +* Description : Initialise un contexte pour correspondances Kaitai établies. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void init_record_scope(kaitai_scope_t *locals, GKaitaiMeta *meta) +{ + locals->meta = meta; + + if (meta != NULL) + g_object_ref(G_OBJECT(meta)); + + locals->root = NULL; + locals->parent = NULL; + locals->last = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = contexte de variables locales à réinitialiser. * +* * +* Description : Vide un contexte de correspondances Kaitai établies. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void reset_record_scope(kaitai_scope_t *locals) +{ + g_clear_object(&locals->meta); + + g_clear_object(&locals->root); + g_clear_object(&locals->parent); + g_clear_object(&locals->last); + +} + + +/****************************************************************************** +* * +* Paramètres : dest = contexte de variables locales à initialiser. * +* src = contexte de variables locales à copier. * +* * +* Description : Copie un contexte de correspondances Kaitai établies. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void copy_record_scope(kaitai_scope_t *dest, const kaitai_scope_t *src) +{ + reset_record_scope(dest); + +#define COPY_SCOPE_ITEM(itm) \ + dest->itm = src->itm; \ + if (dest->itm != NULL) \ + g_object_ref(G_OBJECT(dest->itm)); + + COPY_SCOPE_ITEM(meta); + + COPY_SCOPE_ITEM(root); + COPY_SCOPE_ITEM(parent); + COPY_SCOPE_ITEM(last); + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* * +* Description : Retourne le souvenir d'une correspondance racine. * +* * +* Retour : Dernière correspondance établie ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GMatchRecord *get_root_record(const kaitai_scope_t *locals) +{ + GMatchRecord *result; /* Instance à retourner */ + + result = locals->root; + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* * +* Description : Retourne le souvenir de la correspondance parente effectuée. * +* * +* Retour : Dernière correspondance établie ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GMatchRecord *get_parent_record(const kaitai_scope_t *locals) +{ + GMatchRecord *result; /* Instance à retourner */ + + result = locals->parent; + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* record = dernière correspondance établie. * +* * +* Description : Conserve le souvenir de la dernière correspondance effectuée.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void remember_last_record(kaitai_scope_t *locals, GMatchRecord *record) +{ + g_clear_object(&locals->last); + + locals->last = record; + + if (record != NULL) + g_object_ref(G_OBJECT(record)); + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* * +* Description : Retourne le souvenir de la dernière correspondance effectuée.* +* * +* Retour : Dernière correspondance établie ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GMatchRecord *get_last_record(const kaitai_scope_t *locals) +{ + GMatchRecord *result; /* Instance à retourner */ + + result = locals->last; + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : locals = variables locales pour les résolutions de types. * +* 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 *find_sub_type(const kaitai_scope_t *locals, const char *name) +{ + GKaitaiType *result; /* Instance à retourner */ + size_t i; /* Boucle de parcours */ + GKaitaiParser *parser; /* Lecteur d'origine */ + + GMatchRecord *list[] = { locals->root, locals->parent, locals->last }; + + result = NULL; + + for (i = 0; i < 3; i++) + { + if (list[i] == NULL) + continue; + + parser = g_match_record_get_creator(list[i]); + + if (G_IS_KAITAI_STRUCT(parser)) + result = g_kaitai_structure_find_sub_type(G_KAITAI_STRUCT(parser), name); + + g_object_unref(G_OBJECT(parser)); + + if (result != NULL) + break; + + } + + return result; + +} diff --git a/plugins/kaitai/scope.h b/plugins/kaitai/scope.h new file mode 100644 index 0000000..5dc52bf --- /dev/null +++ b/plugins/kaitai/scope.h @@ -0,0 +1,72 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * scope.h - prototypes pour les recherches d'éléments de lecture + * + * 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 . + */ + + +#ifndef _PLUGINS_KAITAI_SCOPE_H +#define _PLUGINS_KAITAI_SCOPE_H + + +#include "record.h" +#include "parsers/meta.h" +#include "parsers/type.h" + + + +/* Accès aux différentes variables de contexte */ +typedef struct _kaitai_scope_t +{ + GKaitaiMeta *meta; /* Informations globales */ + + GMatchRecord *root; /* Variable "_root" */ + GMatchRecord *parent; /* Variable "_parent" */ + GMatchRecord *last; /* Variable "_" */ + +} kaitai_scope_t; + + +/* Initialise un contexte pour correspondances Kaitai établies. */ +void init_record_scope(kaitai_scope_t *, GKaitaiMeta *); + +/* Vide un contexte de correspondances Kaitai établies. */ +void reset_record_scope(kaitai_scope_t *); + +/* Copie un contexte de correspondances Kaitai établies. */ +void copy_record_scope(kaitai_scope_t *, const kaitai_scope_t *); + +/* Retourne le souvenir d'une correspondance racine. */ +GMatchRecord *get_root_record(const kaitai_scope_t *); + +/* Retourne le souvenir de la correspondance parente effectuée. */ +GMatchRecord *get_parent_record(const kaitai_scope_t *); + +/* Conserve le souvenir de la dernière correspondance effectuée. */ +void remember_last_record(kaitai_scope_t *, GMatchRecord *); + +/* Retourne le souvenir de la dernière correspondance effectuée. */ +GMatchRecord *get_last_record(const kaitai_scope_t *); + +/* Recherche la définition d'un type nouveau pour Kaitai. */ +GKaitaiType *find_sub_type(const kaitai_scope_t *, const char *); + + + +#endif /* _PLUGINS_KAITAI_SCOPE_H */ diff --git a/plugins/kaitai/stream-int.h b/plugins/kaitai/stream-int.h new file mode 100644 index 0000000..50b71df --- /dev/null +++ b/plugins/kaitai/stream-int.h @@ -0,0 +1,55 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream-int.h - prototypes pour les données associées à un flux de données 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 . + */ + + +#ifndef PLUGINS_KAITAI_STREAM_INT_H +#define PLUGINS_KAITAI_STREAM_INT_H + + +#include "stream.h" + + + +/* Flux de données à disposition d'une analyse Kaitai (instance) */ +struct _GKaitaiStream +{ + GObject parent; /* A laisser en premier */ + + GBinContent *content; /* Contenu brut manipulé */ + vmpa2t pos; /* Tête de lecture dans le flux*/ + +}; + +/* Flux de données à disposition d'une analyse Kaitai (classe) */ +struct _GKaitaiStreamClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un flux de données pour Kaitai. */ +bool g_kaitai_stream_create(GKaitaiStream *, GBinContent *, const vmpa2t *); + + + +#endif /* PLUGINS_KAITAI_STREAM_INT_H */ diff --git a/plugins/kaitai/stream.c b/plugins/kaitai/stream.c new file mode 100644 index 0000000..66d0f8e --- /dev/null +++ b/plugins/kaitai/stream.c @@ -0,0 +1,237 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.c - données associées à un flux de données 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 . + */ + + +#include "stream.h" + + +#include "stream-int.h" + + + +/* Initialise la classe des flux de données pour Kaitai. */ +static void g_kaitai_stream_class_init(GKaitaiStreamClass *); + +/* Initialise un flux de données accessibles à Kaitai. */ +static void g_kaitai_stream_init(GKaitaiStream *); + +/* Supprime toutes les références externes. */ +static void g_kaitai_stream_dispose(GKaitaiStream *); + +/* Procède à la libération totale de la mémoire. */ +static void g_kaitai_stream_finalize(GKaitaiStream *); + + + +/* Indique le type défini pour un flux de données manipulé par une lecture Kaitai. */ +G_DEFINE_TYPE(GKaitaiStream, g_kaitai_stream, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des flux de données pour Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_stream_class_init(GKaitaiStreamClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_stream_dispose; + object->finalize = (GObjectFinalizeFunc)g_kaitai_stream_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance à initialiser. * +* * +* Description : Initialise un flux de données accessibles à Kaitai. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_stream_init(GKaitaiStream *stream) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_stream_dispose(GKaitaiStream *stream) +{ + G_OBJECT_CLASS(g_kaitai_stream_parent_class)->dispose(G_OBJECT(stream)); + +} + + +/****************************************************************************** +* * +* Paramètres : stream = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_kaitai_stream_finalize(GKaitaiStream *stream) +{ + G_OBJECT_CLASS(g_kaitai_stream_parent_class)->finalize(G_OBJECT(stream)); + +} + + +/****************************************************************************** +* * +* Paramètres : content = contenu binaire parcouru par une analyse Kaitai. * +* pos = tête de lecture courante. * +* * +* Description : Rassemble les éléments constituant un flux de données Kaitai.* +* * +* Retour : Instance mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GKaitaiStream *g_kaitai_stream_new(GBinContent *content, const vmpa2t *pos) +{ + GKaitaiStream *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_KAITAI_STREAM, NULL); + + if (!g_kaitai_stream_create(result, content, pos)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : attrib = lecteur d'attribut Kaitai à initialiser pleinement.* +* content = contenu binaire parcouru par une analyse Kaitai. * +* pos = tête de lecture courante. * +* * +* Description : Met en place un flux de données pour Kaitai. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_stream_create(GKaitaiStream *stream, GBinContent *content, const vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + + result = true; + + stream->content = content; + g_object_ref(G_OBJECT(content)); + + copy_vmpa(&stream->pos, pos); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux de données Kaitai à consulter. * +* * +* Description : Indique le contenu de données binaires lié au flux Kaitai. * +* * +* Retour : Contenu binaire associé au flux de données. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinContent *g_kaitai_stream_get_content(const GKaitaiStream *stream) +{ + GBinContent *result; /* Instance à renvoyer */ + + result = stream->content; + + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stream = flux de données Kaitai à consulter. * +* * +* Description : Détermine si la fin des données a été atteinte. * +* * +* Retour : true si la tête de lecture est en position finale, ou false. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_kaitai_stream_has_reached_eof(const GKaitaiStream *stream) +{ + bool result; /* Bilan à retourner */ + vmpa2t end; /* Position finale du flux */ + int ret; /* Bilan d'une comparaison */ + + g_binary_content_compute_end_pos(stream->content, &end); + + ret = cmp_vmpa_by_phy(&stream->pos, &end); + + result = (ret == 0); + + return result; + +} diff --git a/plugins/kaitai/stream.h b/plugins/kaitai/stream.h new file mode 100644 index 0000000..ee82c6d --- /dev/null +++ b/plugins/kaitai/stream.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * stream.h - prototypes pour les données associées à un flux de données 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 . + */ + + +#ifndef PLUGINS_KAITAI_STREAM_H +#define PLUGINS_KAITAI_STREAM_H + + +#include +#include + + +#include + + + +#define G_TYPE_KAITAI_STREAM g_kaitai_stream_get_type() +#define G_KAITAI_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_STREAM, GKaitaiStream)) +#define G_IS_KAITAI_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_STREAM)) +#define G_KAITAI_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_STREAM, GKaitaiStreamClass)) +#define G_IS_KAITAI_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_STREAM)) +#define G_KAITAI_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_STREAM, GKaitaiStreamClass)) + + +/* Flux de données à disposition d'une analyse Kaitai (instance) */ +typedef struct _GKaitaiStream GKaitaiStream; + +/* Flux de données à disposition d'une analyse Kaitai (classe) */ +typedef struct _GKaitaiStreamClass GKaitaiStreamClass; + + +/* Indique le type défini pour un flux de données manipulé par une lecture Kaitai. */ +GType g_kaitai_stream_get_type(void); + +/* Rassemble les éléments constituant un flux de données Kaitai. */ +GKaitaiStream *g_kaitai_stream_new(GBinContent *, const vmpa2t *); + +/* Indique le contenu de données binaires lié au flux Kaitai. */ +GBinContent *g_kaitai_stream_get_content(const GKaitaiStream *); + +/* Détermine si la fin des données a été atteinte. */ +bool g_kaitai_stream_has_reached_eof(const GKaitaiStream *); + + + +#endif /* PLUGINS_KAITAI_STREAM_H */ diff --git a/plugins/kaitai/tokens.l b/plugins/kaitai/tokens.l new file mode 100644 index 0000000..3ddf40d --- /dev/null +++ b/plugins/kaitai/tokens.l @@ -0,0 +1,327 @@ + +%top { + +#include +#include +#include + +#include + +#include "grammar.h" + +} + + +%{ + +#define PUSH_STATE(s) yy_push_state(s, yyscanner) +#define POP_STATE yy_pop_state(yyscanner) + +%} + + +%option bison-bridge reentrant +%option stack +%option nounput + //%option noinput +%option noyywrap +%option noyy_top_state +%option yylineno +%option never-interactive + + +%x encoding +%x escaped_str +%x plain_str + + +%% + + +%{ + + /* no init C code */ + +%} + + +"+" { return PLUS; } +"-" { return MINUS; } +"*" { return MUL; } +"/" { return DIV; } +"%" { return MOD; } + +"<" { return LT; } +"<=" { return LE; } +"==" { return EQ; } +"!=" { return NE; } +">" { return GT; } +">=" { return GE; } + +"<<" { return SHIFT_LEFT; } +">>" { return SHIFT_RIGHT; } +"&" { return BIT_AND; } +"|" { return BIT_OR; } +"^" { return BIT_XOR; } + +"not" { return NOT; } +"and" { return AND; } +"or" { return OR; } + +"(" { return PAREN_O; } +")" { return PAREN_C; } +"[" { return HOOK_O; } +"]" { return HOOK_C; } +"," { return COMMA; } +"." { return DOT; } + +"?" { return QMARK; } +":" { return COLON; } +"::" { return DOUBLE_COLON; } + +".size" { return METH_SIZE; } +".length" { return METH_LENGTH; } +".reverse" { return METH_REVERSE; } +".substring" { return METH_SUBSTRING; } +".to_i" { return METH_TO_I; } +".to_i(" { return METH_TO_I_RAD; } +".to_s" { return METH_TO_S; } +".to_s(" { PUSH_STATE(encoding); return METH_TO_S_ENC; } + +"_root" { return ROOT; } +"_parent" { return PARENT; } +"_" { return LAST; } +"._io" { return METH_IO; } + +"true" { return TRUE_CONST; } +"false" { return FALSE_CONST; } + + +%{ /* Lecteurs de valeurs entières */ %} + +0[bB][01]+ { + char *__end; + yylval->unsigned_integer = strtoull(yytext + 2, &__end, 2); + if (__end != (yytext + yyleng)) + YY_FATAL_ERROR("failed to parse integer"); + return UNSIGNED_INTEGER; + } + +0[bB][01]{1,4}(_[01]{4})+ { + char *__tmp; + char *__end; + __tmp = strdup(yytext); + __tmp = strrpl(__tmp, "_", ""); + yylval->unsigned_integer = strtoull(__tmp + 2, &__end, 2); + if (__end != (__tmp + strlen(__tmp))) + { + free(__tmp); + YY_FATAL_ERROR("failed to parse integer"); + } + else free(__tmp); + return UNSIGNED_INTEGER; + } + +(0|[1-9][0-9]*) { + char *__end; + yylval->unsigned_integer = strtoull(yytext, &__end, 10); + if (__end != (yytext + yyleng)) + YY_FATAL_ERROR("failed to parse integer"); + return UNSIGNED_INTEGER; + } + +[1-9][0-9]{0,2}(_[1-9][0-9]{2})+ { + char *__tmp; + char *__end; + __tmp = strdup(yytext); + __tmp = strrpl(__tmp, "_", ""); + yylval->unsigned_integer = strtoull(__tmp, &__end, 10); + if (__end != (__tmp + strlen(__tmp))) + { + free(__tmp); + YY_FATAL_ERROR("failed to parse integer"); + } + else free(__tmp); + return UNSIGNED_INTEGER; + } + +-(0|[1-9][0-9]*) { + char *__end; + yylval->signed_integer = strtoll(yytext, &__end, 10); + if (__end != (yytext + yyleng)) + YY_FATAL_ERROR("failed to parse integer"); + return SIGNED_INTEGER; + } + +-[1-9][0-9]{0,2}(_[1-9][0-9]{2})+ { + char *__tmp; + char *__end; + __tmp = strdup(yytext); + __tmp = strrpl(__tmp, "_", ""); + yylval->signed_integer = strtoll(__tmp, &__end, 10); + if (__end != (__tmp + strlen(__tmp))) + { + free(__tmp); + YY_FATAL_ERROR("failed to parse integer"); + } + else free(__tmp); + return SIGNED_INTEGER; + } + +0[xX][0-9a-fA-F]+ { + char *__end; + yylval->unsigned_integer = strtoull(yytext, &__end, 16); + if (__end != (yytext + yyleng)) + YY_FATAL_ERROR("failed to parse integer"); + return UNSIGNED_INTEGER; + } + +0[xX][0-9a-fA-F]{1,4}(_[0-9a-fA-F]{4})+ { + char *__tmp; + char *__end; + __tmp = strdup(yytext); + __tmp = strrpl(__tmp, "_", ""); + yylval->unsigned_integer = strtoull(__tmp, &__end, 16); + if (__end != (__tmp + strlen(__tmp))) + { + free(__tmp); + YY_FATAL_ERROR("failed to parse integer"); + } + else free(__tmp); + return UNSIGNED_INTEGER; + } + + + +-?(0|[1-9][0-9]*\.[0-9]+) { + char *__end; + yylval->floating_number = strtod(yytext, &__end); + if (__end != (yytext + yyleng)) + YY_FATAL_ERROR("failed to parse float"); + return FLOAT; + } + + +%{ /* Paramètre d'encodage */ %} + +["'][-_A-Za-z0-9 ]+["'] { + yylval->sized_cstring.data = yytext + 1; + yylval->sized_cstring.len = yyleng - 2; + return ENCODING_NAME; + } + +")" { POP_STATE; return PAREN_C; } + + +[a-z][a-z0-9_]* { + yylval->sized_cstring.data = yytext; + yylval->sized_cstring.len = yyleng; + return IDENTIFIER; + } + +[^\\\[\],"'()\.: ]+ { + yylval->sized_cstring.data = yytext; + yylval->sized_cstring.len = yyleng; + return PLAIN_BYTES; + } + + +%{ /* Lecteurs des tableaux de définition d'octets */ %} + +"\"" { PUSH_STATE(escaped_str); } + + +[^\\"]+ { + yylval->sized_cstring.data = yytext; + yylval->sized_cstring.len = yyleng; + return RAW_BYTES; + } + +"\\a" { yylval->byte = '\a'; return RAW_BYTE; } +"\\b" { yylval->byte = '\b'; return RAW_BYTE; } +"\\t" { yylval->byte = '\t'; return RAW_BYTE; } +"\\n" { yylval->byte = '\n'; return RAW_BYTE; } +"\\v" { yylval->byte = '\v'; return RAW_BYTE; } +"\\f" { yylval->byte = '\f'; return RAW_BYTE; } +"\\r" { yylval->byte = '\r'; return RAW_BYTE; } +"\\e" { yylval->byte = '\e'; return RAW_BYTE; } +"\\\"" { yylval->byte = '"'; return RAW_BYTE; } +"\\'" { yylval->byte = '\''; return RAW_BYTE; } +"\\\\" { yylval->byte = '\\'; return RAW_BYTE; } +"\\0" { yylval->byte = '\0'; return RAW_BYTE; } + +\\[0-9]{1,3} { + char __tmp[4]; + memcpy(__tmp, yytext + 1, yyleng - 1); + __tmp[yyleng] = '\0'; + yylval->byte = strtoull(__tmp, NULL, 8); + return RAW_BYTE; + } + +"\"" { POP_STATE; } + + + + +"'" { PUSH_STATE(plain_str); } + +[^']+ { + yylval->sized_cstring.data = yytext; + yylval->sized_cstring.len = yyleng; + return PLAIN_BYTES; + } + +['] { POP_STATE; } + +[.]$ { +#ifndef NDEBUG + int ch; +#endif + yylval->sized_cstring.data = yytext; + yylval->sized_cstring.len = yyleng; +#ifndef NDEBUG + ch = input(yyscanner); + assert(ch == '\n'); +#else + input(yyscanner); +#endif + return RAW_BYTES_WITH_ENDING_DOT; + } + +[^\\\[\],"'()\.: ]+[.]$ { +#ifndef NDEBUG + int ch; +#endif + yylval->sized_cstring.data = yytext; + yylval->sized_cstring.len = yyleng; +#ifndef NDEBUG + ch = input(yyscanner); + assert(ch == '\n'); +#else + input(yyscanner); +#endif + return RAW_BYTES_WITH_ENDING_DOT; + } + + +%{ /* Actions par défaut */ %} + +<*>[ \t\n]+ { } + +<*>. { + char *msg; + int ret; + ret = asprintf(&msg, + "Unhandled token in rule definition: '%s'", + yytext); + if (ret == -1) + YY_FATAL_ERROR("Unhandled token in undisclosed rule definition"); + else + { + YY_FATAL_ERROR(msg); + free(msg); + } + } + + +%% diff --git a/tests/plugins/kaitai.py b/tests/plugins/kaitai.py new file mode 100644 index 0000000..b1e8881 --- /dev/null +++ b/tests/plugins/kaitai.py @@ -0,0 +1,2474 @@ +#!/usr/bin/python3-dbg +# -*- coding: utf-8 -*- + +import locale + +from chrysacase import ChrysalideTestCase +from pychrysalide.analysis.contents import MemoryContent +from pychrysalide.plugins.kaitai.parsers import KaitaiStruct + + +class TestKaitaiStruct(ChrysalideTestCase): + """TestCase for the KaitaiStruct parsing.""" + + + @classmethod + def setUpClass(cls): + + super(TestKaitaiStruct, cls).setUpClass() + + cls.log('Setting locale suitable for floats...') + + cls._old_locale = locale.getlocale(locale.LC_NUMERIC) + + locale.setlocale(locale.LC_NUMERIC, 'C') + + + @classmethod + def tearDownClass(cls): + + super(TestKaitaiStruct, cls).tearDownClass() + + cls.log('Reverting locale...') + + locale.setlocale(locale.LC_NUMERIC, cls._old_locale) + + + + ################################# + ### 4. Kaitai Struct language + ################################# + + + def testKaitaiFixedLength(self): + """Load fixed-size structures.""" + + # Cf. 4.1. Fixed-size structures + + definitions = ''' +meta: + id: mydesc + title: My Long Title + endian: be +seq: + - id: field0 + type: u4 +''' + + kstruct = KaitaiStruct(definitions) + + self.assertEqual(kstruct.meta.id, 'mydesc') + self.assertEqual(kstruct.meta.title, 'My Long Title') + + content = MemoryContent(b'\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 4) + self.assertEqual(parsed.field0.value, 0x01020304) + + definitions = ''' +meta: + endian: le +seq: + - id: field0 + type: u4 + - id: field1 + type: u4be +''' + + kstruct = KaitaiStruct(definitions) + + self.assertIsNone(kstruct.meta.id) + self.assertIsNone(kstruct.meta.title) + + content = MemoryContent(b'\x01\x02\x03\x04\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 4) + self.assertEqual(parsed.field0.value, 0x04030201) + + self.assertEqual(parsed.field1.range.length, 4) + self.assertEqual(parsed.field1.value, 0x01020304) + + + definitions = ''' +seq: + - id: field0 + type: u1 + - id: field1 + size: 2 + - id: field2 + size: field0 + 1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04\x05') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 1) + self.assertEqual(parsed.field0.value, 0x01) + + self.assertEqual(parsed.field1.range.length, 2) + self.assertEqual(parsed.field1.truncated_bytes, b'\x02\x03') + + self.assertEqual(parsed.field2.range.length, 2) + self.assertEqual(parsed.field2.truncated_bytes, b'\x04\x05') + + + def testDocstrings(self): + """Handle Kaitai documentation.""" + + # Cf. 4.2. Docstrings + + definitions = ''' +seq: + - id: rating + type: s4 + doc: Rating, can be negative +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x02\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.rating.creator.doc, 'Rating, can be negative') + + + def testKaitaiContents(self): + """Read various forms of fixed content.""" + + # Cf. 4.3. Checking for "magic" signatures + + definitions = ''' +seq: + - id: field0 + contents: [ 0, 0x10, '22', "50 ] +''' + + # ValueError: Unable to create Kaitai structure. + with self.assertRaisesRegex(ValueError, "Unable to create Kaitai structure"): + kstruct = KaitaiStruct(definitions) + self.assertIsNotNone(kstruct) + + + definitions = ''' +seq: + - id: field0 + contents: [ 0x41, 66, 'CD' ] +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABCD') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 4) + + self.assertEqual(parsed.field0.value, b'ABCD') + + + definitions = ''' +seq: + - id: field0 + contents: ABCD +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABCD') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 4) + + + definitions = ''' +seq: + - id: field0 + contents: "ABCD" +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABCD') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 4) + + + definitions = ''' +seq: + - id: field0 + contents: + - 0x41 + - "B" + - CD +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABCD') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.range.length, 4) + + + def testVariableLengthStructures(self): + """Parse variable-length structures.""" + + # Cf. 4.4. Variable-length structures + + definitions = ''' +seq: + - id: my_len + type: u1 + - id: my_str + type: str + size: my_len +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x03ABC') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_len.value, 3) + + self.assertEqual(parsed.my_str.value, b'ABC') + + + definitions = ''' +seq: + - id: my_len + type: u1 + - id: my_str + type: str + size: my_len * 2 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x03ABCDEF') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_len.value, 3) + + self.assertEqual(parsed.my_str.value, b'ABCDEF') + + + definitions = ''' +seq: + - id: field0 + size-eos: true +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x02\x03') + + parsed = kstruct.parse(content) + + self.assertEqual(content, parsed.content) + + self.assertEqual(parsed.range.addr.phys, 0) + self.assertEqual(parsed.range.length, len(content.data)) + + + def testDelimitedStructures(self): + """Parse delimited structures.""" + + # Cf. 4.5. Delimited structures + + definitions = ''' +seq: + - id: my_string + type: str + terminator: 0 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABC\x00DEF') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_string.value, b'ABC') + + + definitions = ''' +seq: + - id: my_string + type: strz +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABC\x00DEF') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_string.value, b'ABC') + + + definitions = ''' +seq: + - id: name + type: str + size: 8 + terminator: 0 + - id: guard + size: 1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'ABC\x00\x00\x00\x00\x00x\x00') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.name.value, b'ABC') + + self.assertEqual(parsed.guard.value, b'x') + + + def __passed__testEnums(self): + """Parse delimited structures.""" + + # Cf. 4.6. Enums (named integer constants) + + pass + + + def testSubTypes(self): + """Includes subtypes definitions.""" + + # Cf. 4.7. Substructures (subtypes) + + definitions = ''' +seq: + - id: field0 + type: custom_type + - id: field1 + type: custom_type + - id: field2 + type: custom_type +types: + custom_type: + seq: + - id: len + type: u1 + - id: value + size: len +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\xaa\x02\xbb\xbb\x03\xcc\xcc\xcc') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.len.value, 1) + self.assertEqual(parsed.field0.value.truncated_bytes, b'\xaa') + + self.assertEqual(parsed.field1.len.value, 2) + self.assertEqual(parsed.field1.value.truncated_bytes, b'\xbb\xbb') + + self.assertEqual(parsed.field2.len.value, 3) + self.assertEqual(parsed.field2.value.truncated_bytes, b'\xcc\xcc\xcc') + + + def testOtherAttributesAccess(self): + """Access attributes in other types.""" + + # Cf. 4.8. Accessing attributes in other types + + definitions = ''' +seq: + - id: header + type: main_header + - id: body + size: header.body_len +types: + main_header: + seq: + - id: magic + contents: FMT + - id: body_len + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'FMT\x04\xaa\xbb\xcc\xdd') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.header.magic.raw_bytes, b'FMT') + self.assertEqual(parsed.header.magic.range.length, 3) + + self.assertEqual(parsed.header.body_len.value, 4) + + self.assertEqual(parsed.body.raw_bytes, b'\xaa\xbb\xcc\xdd') + self.assertEqual(parsed.body.range.length, 4) + + + def testConditionals(self): + """Read Kaitai values according to previous loaded values.""" + + # Cf. 4.9. Conditionals + + definitions = ''' +seq: + - id: field1 + type: u1 + - id: field2 + type: u1 + - id: field3 + type: u1 + if: field1 + field2 > 10 + - id: field4 + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field1.value, 0x01) + self.assertEqual(parsed.field2.value, 0x02) + self.assertFalse(hasattr(parsed, 'field3')) + self.assertEqual(parsed.field4.value, 0x03) + + + definitions = ''' +seq: + - id: field1 + type: u1 + - id: field2 + type: u1 + - id: field3 + type: u1 + if: field1 + field2 > 1 + - id: field4 + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field1.value, 0x01) + self.assertEqual(parsed.field2.value, 0x02) + self.assertTrue(hasattr(parsed, 'field3')) + self.assertEqual(parsed.field4.value, 0x04) + + + definitions = ''' +seq: + - id: field1 + type: u1 + - id: field2 + type: u1 + - id: field3 + type: u1 + if: field1 + field2 == threshold::three + - id: field4 + type: u1 +enums: + threshold: + 1: one + 2: two + 3: three +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field1.value, 0x01) + self.assertEqual(parsed.field2.value, 0x02) + self.assertTrue(hasattr(parsed, 'field3')) + self.assertEqual(parsed.field4.value, 0x04) + + + def testRepeatedReadUntilEOS(self): + """Read items until the end of the stream.""" + + # Cf. 4.10.1. Repeat until end of stream + + definitions = ''' +seq: + - id: field0 + type: u2be + repeat: eos +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x00\x02\x00\x03\x00\x04\x00') + + parsed = kstruct.parse(content) + + self.assertEqual(len(parsed.field0), len(content.data) / 2) + + for i in range(4): + self.assertEqual(parsed.field0[i].value, (i + 1) << 8) + + + def testRepeatedReadAccordingToCounter(self): + """Repeat read of items for a nomber of times.""" + + # Cf. 4.10.2. Repeat for a number of times + + definitions = ''' +seq: + - id: field0 + type: u1 + - id: field1 + type: u1 + repeat: expr + repeat-expr: 1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x01') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.value, 0x01) + + self.assertEqual(len(parsed.field1), 1) + + for i in range(1): + self.assertEqual(parsed.field1[i].value, i + 1) + + definitions = ''' +seq: + - id: field0 + type: u1 + - id: field1 + type: u1 + - id: field2 + type: u2 + repeat: expr + repeat-expr: field0 + field1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x01\x00\x02\x00\x03\x00') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field0.value, 0x01) + self.assertEqual(parsed.field1.value, 0x02) + + self.assertEqual(len(parsed.field2), 3) + + for i in range(3): + self.assertEqual(parsed.field2[i].value, i + 1) + + + def testRepeatUntilConditionIsMet(self): + """Repeat until condition is met.""" + + # Cf. 4.10.3. Repeat until condition is met + + definitions = ''' +seq: + - id: numbers + type: u1 + repeat: until + repeat-until: _ == 0xff + - id: extra + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\xff\xcc') + + parsed = kstruct.parse(content) + + self.assertEqual(len(parsed.numbers), 3) + + for i in range(2): + self.assertEqual(parsed.numbers[i].value, i + 1) + + self.assertEqual(parsed.numbers[2].value, 0xff) + + self.assertEqual(parsed.extra.value, 0xcc) + + definitions = ''' +seq: + - id: records + type: buffer_with_len + repeat: until + repeat-until: _.len == 0 +types: + buffer_with_len: + seq: + - id: len + type: u1 + - id: value + size: len +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x02\xaa\xaa\x01\xbb\x00') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.records[0].len.value, 2) + self.assertEqual(parsed.records[0].value.raw_bytes, b'\xaa\xaa') + + self.assertEqual(parsed.records[1].len.value, 1) + self.assertEqual(parsed.records[1].value.raw_bytes, b'\xbb') + + self.assertEqual(parsed.records[2].len.value, 0) + self.assertEqual(parsed.records[2].value.raw_bytes, b'') + + + def testParseTLVImplementation(self): + """Parse a typical TLV implementation.""" + + # Cf. 4.11. Typical TLV implementation (switching types on an expression) + + definitions = ''' +seq: + - id: record + type: rec_def + repeat: eos +types: + rec_def: + seq: + - id: rec_type + type: u1 + - id: len + type: u1 + - id: body + size: len + type: + switch-on: rec_type + cases: + 1: rec_type_1 + 2: rec_type_2 + rec_type_1: + seq: + - id: field1 + type: u1 + rec_type_2: + seq: + - id: field2 + type: u2 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x01\xaa\x02\x02\xcc\xbb') + + parsed = kstruct.parse(content) + + self.assertEqual(len(parsed.record), 2) + + self.assertEqual(parsed.record[0].rec_type.value, 1) + self.assertEqual(parsed.record[0].len.value, 1) + self.assertEqual(parsed.record[0].body.field1.value, 0xaa) + + self.assertEqual(parsed.record[1].rec_type.value, 2) + self.assertEqual(parsed.record[1].len.value, 2) + self.assertEqual(parsed.record[1].body.field2.value, 0xbbcc) + + + def testInstanceWithDataBeyondTheSequence(self): + """Build instances with data beyond the sequence.""" + + # Cf. 4.12. Instances: data beyond the sequence + + definitions = ''' +instances: + some_integer: + pos: 0x4 + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04\x05\x06\x07\x08') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.some_integer.value, 5) + + + definitions = ''' +seq: + - id: file_offset + type: u1 + - id: file_size + type: u1 +instances: + body: + pos: file_offset + size: file_size +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x04\x02\x90\x90ABCD') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.file_offset.value, 4) + + self.assertEqual(parsed.file_size.value, 2) + + self.assertEqual(parsed.body.value, b'AB') + + + def testValueInstances(self): + """Build value instances""" + + # Cf. 4.13. Value instances + + definitions = ''' +seq: + - id: length + type: u1 + - id: extra + type: u1 +instances: + length_extended: + value: length * 3 + extra +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.length.value, 1) + + self.assertEqual(parsed.extra.value, 2) + + self.assertEqual(parsed.length_extended.value, 5) + + + def testBitSizedIntegers(self): + """Read bit-sized integers.""" + + # Cf. 4.14. Bit-sized integers + + definitions = ''' +seq: + - id: packed_1 + type: u1 +instances: + version: + value: (packed_1 & 0b11110000) >> 4 + len_header: + value: packed_1 & 0b00001111 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x9a') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.packed_1.value, 0x9a) + + self.assertEqual(parsed.version.value, 0x9) + + self.assertEqual(parsed.len_header.value, 0xa) + + + def __passed__testBitSizedIntegersBigEndian(self): + """Read bit-sized integers.""" + + # Cf. 4.14.1. Big-endian order + + pass + + + def __passed__testBitSizedIntegersLittleEndian(self): + """Read bit-sized integers.""" + + # Cf. 4.14.2. Little-endian order + + pass + + + def __passed__testBitSizedIntegersSpecifiedEndianness(self): + """Read bit-sized integers with specified bit endianness.""" + + # Cf. 4.14.3. Specifying bit endianness + + pass + + + + ################################# + ### 5. Streams and substreams + ################################# + + + def testTotalSizeLimit(self): + """Limit total size of structure.""" + + # Cf. 5.1. Limiting total size of structure + + definitions = ''' +seq: + - id: body_len + type: u1 + - id: random + size: 2 + - id: comment + size: body_len - 2 + - id: extra + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x05\x01\x02---\xbb') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.body_len.value, 0x05) + + self.assertEqual(parsed.random.raw_bytes, b'\x01\x02') + + self.assertEqual(parsed.comment.raw_bytes, b'---') + + self.assertEqual(parsed.extra.raw_bytes, b'\xbb') + + + definitions = ''' +seq: + - id: body_len + type: u1 + - id: body + type: record_body + size: body_len + - id: extra + type: u1 +types: + record_body: + seq: + - id: random + size: 2 + - id: comment + size-eos: true +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x05\x01\x02---\xbb') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.body_len.value, 0x05) + + self.assertEqual(parsed.body.random.raw_bytes, b'\x01\x02') + + self.assertEqual(parsed.body.comment.raw_bytes, b'---') + + self.assertEqual(parsed.extra.raw_bytes, b'\xbb') + + + def testRepeatSizeLimit(self): + """Repeating until total size reaches limit.""" + + # Cf. 5.2. Repeating until total size reaches limit + + content = MemoryContent(b'\x03\x00\x01\x02\xbb') + + definitions = ''' +seq: + - id: total_len + type: u1 + - id: files + type: file_entries + size: total_len + - id: extra + type: u1 +types: + file_entries: + seq: + - id: entries + type: entry + repeat: eos + entry: + seq: + - id: index + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.total_len.value, 3) + + self.assertEqual(len(parsed.files.entries), 3) + + for i in range(3): + self.assertEqual(parsed.files.entries[i].index.value, i) + + self.assertEqual(parsed.extra.value, 0xbb) + + + def testRelativePositioning(self): + """Parse with relative positioning.""" + + # Cf. 5.3. Relative positioning + + content = MemoryContent(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\0xe\x0f') + + definitions = ''' +seq: + - id: some_header + size: 4 + - id: body + type: block + size: 12 +types: + block: + seq: + - id: foo + type: u1 + instances: + some_bytes_in_the_middle: + pos: 4 + size: 4 +''' + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.some_header.value, b'\x00\x01\x02\x03') + self.assertEqual(parsed.body.foo.value, 0x04) + + self.assertEqual(parsed.body.some_bytes_in_the_middle.value, b'\x08\x09\x0a\x0b') + + + def testAbsolutePositioning(self): + """Read from absolute position.""" + + # Cf. 5.4. Absolute positioning + + content = MemoryContent(b'\x06\x03\x00\x00\x00\x00\x01\x02\x03\xbb') + + definitions = ''' +seq: + - id: items + size: 10 + type: entry + repeat: eos +types: + entry: + seq: + - id: ofs_body + type: u1 + - id: len_body + type: u1 + instances: + body: + io: _root._io + pos: ofs_body + size: len_body +''' + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.items[0].ofs_body.value, 6) + self.assertEqual(parsed.items[0].len_body.value, 3) + + self.assertEqual(parsed.items[0].body.value, b'\x01\x02\x03') + + + def testSubstreamChoice(self): + """Choose a substream.""" + + # Cf. 5.5. Choosing a substream + + content = MemoryContent(b'\xaa\xaa\xaa\xaa\x01\x02\x03\x04\x05\x06\x07\x08\x02\x03') + + definitions = ''' +seq: + - id: global_header + size: 4 + - id: block_one + type: big_container + size: 8 + - id: block_two + type: smaller_container + size: 2 +types: + big_container: + seq: + - id: some_header + size: 8 + smaller_container: + seq: + - id: ofs_in_big + type: u1 + - id: len_in_big + type: u1 + instances: + something_in_big: + io: _root.block_one._io + pos: ofs_in_big + size: len_in_big +''' + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.block_two.ofs_in_big.value, 2) + self.assertEqual(parsed.block_two.len_in_big.value, 3) + + self.assertEqual(parsed.block_two.something_in_big.value, b'\x03\x04\x05') + + + def __passed__testContentPreProcessing(self): + """Process content before parsing.""" + + # Cf. 5.6. Processing: dealing with compressed, obfuscated and encrypted data + + pass + + + + ############################## + ### 6. Expression language + ############################## + + + def testBasicDataTypes(self): + """Handle basic data types.""" + + # Cf. 6.1. Basic data types + + definitions = ''' +seq: + - id: field1 + type: u1 + - id: field2 + type: u2 + - id: field4 + type: u4 + - id: field8 + type: u8 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x02\x04\x04\x04\x04\x08\x08\x08\x08\x08\x08\x08\x08') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field1.range.length, 1) + self.assertEqual(parsed.field2.range.length, 2) + self.assertEqual(parsed.field4.range.length, 4) + self.assertEqual(parsed.field8.range.length, 8) + + definitions = ''' +seq: + - id: field1 + type: u1 + - id: field4 + type: u4le + - id: field4bis + type: u4be +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04\x05\x02\x03\x04\x05') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field1.value, 0x01) + self.assertEqual(parsed.field4.value, 0x05040302) + self.assertEqual(parsed.field4bis.value, 0x02030405) + + + definitions = ''' +instances: + number1: + value: 0xdead_cafe + number2: + value: 0xdead_cafe_dead_cafe + number3: + value: 12_345_678 + number4: + value: 0b10100011 + number5: + value: 0b1010_0011_1010_0011 +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.number1.value, 0xdeadcafe) + + self.assertEqual(parsed.number2.value, 0xdeadcafedeadcafe) + + self.assertEqual(parsed.number3.value, 12345678) + + self.assertEqual(parsed.number4.value, 0b10100011) + + self.assertEqual(parsed.number5.value, 0b1010001110100011) + + + definitions = ''' +seq: + - id: op0 + type: u1 +instances: + result: + value: 0xdeadcafe + op0 + result2: + value: 0XdeadCAFE + op0 +''' + + content = MemoryContent(b'\x00') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result.value, 0xdeadcafe) + + self.assertEqual(parsed.result2.value, 0xdeadcafe) + + + definitions = ''' +instances: + bytes1: + value: [] + bytes2: + value: [ ] + bytes3: + value: [ 0x90 ] + bytes4: + value: [foo, 0, A, 0xa, 42] +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.bytes1.value, b'') + + self.assertEqual(parsed.bytes2.value, b'') + + self.assertEqual(parsed.bytes3.value, b'\x90') + + self.assertEqual(parsed.bytes4.value, b'\x66\x6f\x6f\x00\x41\x0a\x2a') + + + definitions = ''' +instances: + escaped: + value: '[ "\\a\\b\\t\\n\\v\\f", "\\0", 0, " \\r\\e\\\"\\123" ]' +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.escaped.value, b'\x07\x08\x09\x0a\x0b\x0c\x00\x00 \x0d\x1b\x22\x53') + + + definitions_0 = r''' +instances: + escaped: + value: "[ \"\\a\\b\\t\\n\\v\\f\", \"\\0\", 0, \"\\r\\e\\\"'\\123\" ]" +''' + + definitions_1 = r''' +instances: + escaped: + value: [ "\\a\\b\\t\\n\\v\\f", "\\0", 0, "\\r\\e\\\"'\\123" ] +''' + + definitions_2 = ''' +instances: + escaped: + value: [ "\\\\a\\\\b\\\\t\\\\n\\\\v\\\\f", "\\\\0", 0, "\\\\r\\\\e\\\\\\"'\\\\123" ] +''' + + for d in [ definitions_0, definitions_1, definitions_2 ]: + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(d) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.escaped.value, b'\x07\x08\x09\x0a\x0b\x0c\x00\x00\x0d\x1b\x22\x27\x53') + + + def __passed__testUserDefinedTypes(self): + """Create user-defined types.""" + + # Cf. 6.2.1. User-defined types + + pass + + + def testArrays(self): + """Create various arrays.""" + + # Cf. 6.2.2. Arrays + + definitions = ''' +instances: + result_0: + value: "[]" + result_1: + value: "[CAFE, 0, BABE]" + result_2: + value: "[CAFE, 0, BABE] == 'CAFE' + [ 0x00 ] + 'BABE'" + result_3: + value: "[CAFE, 0, BABE] == [ 0x43, 0x41, 0x46, 0x45, 0x00, 0x42, 0x41, 0x42, 0x45 ]" + result_4: + value: "[foo, 0, A, 0xa, 42] == [ 0x66, 0x6f, 0x6f, 0x00, 0x41, 0x0a, 0x2a ]" + result_5: + value: "[1, 0x55, '▒,3', 3] == [ 0x01, 0x55, 0xe2, 0x96, 0x92, 0x2c, 0x33, 0x03 ]" +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value, b'') + + self.assertEqual(parsed.result_1.value, b'CAFE\x00BABE') + + + definitions = ''' +seq: + - id: indexes + type: u1 + repeat: eos +instances: + table: + value: "[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]" + ref: + value: indexes + result_0: + value: table + result_1: + value: ref + result_2: + value: table[indexes[0]][indexes[1] - 1] + result_3: + value: table[indexes[0]][ref[1]] +''' + + content = MemoryContent(b'\x01\x02\x03\x04') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value.value, b'\x01\x02\x03\x04\x05\x06\x07\x08\x09') + + self.assertEqual(type(parsed.result_1).__name__, 'RecordValue') # result_1 + self.assertEqual(type(parsed.result_1.value).__name__, 'RecordValue') # result_1.ref + self.assertEqual(type(parsed.result_1.value.value).__name__, 'RecordList') # result_1.ref.table + + self.assertEqual(parsed.result_1.value.value[3].value, 0x04) + + self.assertEqual(parsed.result_2.value, 5) + + self.assertEqual(parsed.result_3.value, 6) + + + def testArithmeticOperators(self): + """Compute with arithmetic operators.""" + + # Cf. 6.3.1. Arithmetic operators + + definitions = ''' +seq: + - id: op0 + type: u1 + - id: op1 + type: u1 +instances: + result_0: + value: op0 + op1 * 3 + result_1: + value: (2 + op0) * op1 + result_2: + value: 7 * 2.0 + result_3: + value: 7 / 2.0 + result_4: + value: -5 % 3 + result_5: + value: 4 % 3 + result_6: + value: 6 - 3 - -4.0 +''' + + content = MemoryContent(b'\x02\x03\x04\x05') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value, 11) + + self.assertEqual(parsed.result_1.value, 12) + + self.assertEqual(parsed.result_2.value, 14.0) + + self.assertEqual(parsed.result_3.value, 3.5) + + self.assertEqual(parsed.result_4.value, 1) + + self.assertEqual(parsed.result_5.value, 1) + + self.assertEqual(parsed.result_6.value, 7.0) + + + definitions = ''' +seq: + - id: base + size: 3 +instances: + result_0: + value: "'xXx ' + base + ' -- %< --'" +''' + + content = MemoryContent(b'ABC') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value, b'xXx ABC -- %< --') + + + definitions = ''' +seq: + - id: nums + type: u1 + repeat: eos +instances: + computed: + value: nums[0] + nums[3] + computed2: + value: nums[0] * nums.size + nums[3] + computed3: + value: nums[0] * nums[nums.size - 1] +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x03\x04') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.computed.value, 5) + + self.assertEqual(parsed.computed2.value, 8) + + self.assertEqual(parsed.computed3.value, 4) + + + def testRelationalOperators(self): + """Compute with relational operators.""" + + # Cf. 6.3.2. Relational operators + + definitions = ''' +seq: + - id: op0 + type: u1 + - id: op1 + type: u1 + - id: op2 + size: 3 +instances: + result0: + value: op0 == op1 + result1: + value: op0 != op1 + result2: + value: op2 == 'ABC' + result3: + value: op2 < 'ABCD' + result4: + value: (op0 + 1) >= op1 + result5: + value: "(op0 + 1) == 'ABC'.length" +''' + + content = MemoryContent(b'\x02\x03ABCD') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertFalse(parsed.result0.value) + + self.assertTrue(parsed.result1.value) + + self.assertTrue(parsed.result2.value) + + self.assertTrue(parsed.result3.value) + + self.assertTrue(parsed.result4.value) + + self.assertTrue(parsed.result5.value) + + + def testBitwiseOperators(self): + """Compute with bitwise operators.""" + + # Cf. 6.3.3. Bitwise operators + + definitions = ''' +seq: + - id: op0 + type: u1 + - id: op1 + type: u1 + - id: op2 + type: u1 +instances: + result_0: + value: op0 & op1 + result_1: + value: op1 << op0 >> 1 + result_2: + value: (op2 | 0x80) >> 1 +''' + + content = MemoryContent(b'\x02\x07\x01') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value, 0x2) + + self.assertEqual(parsed.result_1.value, 14) + + self.assertEqual(parsed.result_2.value, 0x40) + + + def testLogicalOperators(self): + """Compute with logical boolean operators.""" + + # Cf. 6.3.4. Logical (boolean) operators + + definitions = ''' +seq: + - id: op0 + type: u1 + - id: op1 + type: u1 +instances: + result_0: + value: (op0 > 0) and not false + result_1: + value: op0 == 1 or op1 == 2 +''' + + content = MemoryContent(b'\x01\x02') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertTrue(parsed.result_0.value) + + self.assertTrue(parsed.result_1.value) + + + def testTernaryOperator(self): + """Offer challenges to the ternary operator.""" + + # Cf. 6.3.5. Ternary (if-then-else) operator + + definitions = ''' +seq: + - id: op0 + type: u1 + - id: op1 + type: u1 + - id: op2 + type: u1 +instances: + result_0: + value: 'op0 == 0x80 ? op1 + 1 : op1 * op2' + result_1: + value: 'op0 < 0x80 ? op1 + 1 : op1 * op2' + result_1: + value: 'op0 < 0x80 ? op1 + 1 : op1 * op2' + result_2: + value: '(op0 + 0x10) >= 0x90 ? true : 123' + result_3: + value: '(op0 + 0x10) >= 0x90 and false ? true : 123' +''' + + content = MemoryContent(b'\x80\x03\x04') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value, 4) + + self.assertEqual(parsed.result_1.value, 12) + + self.assertTrue(parsed.result_2.value) + + self.assertEqual(parsed.result_3.value, 123) + + + def testIntegersMethods(self): + """Run methods from integers.""" + + # Cf. 6.4.1. Integers + + definitions = ''' +instances: + bytes1: + value: 123.to_s == "123" and -123.to_s == '-123' +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertTrue(parsed.bytes1.value) + + + def testFloatsMethods(self): + """Run methods from floating numbers.""" + + # Cf. 6.4.2. Floating point numbers + + definitions = ''' +instances: + result_0: + value: 2.32.to_i == 2 and -7.0.to_i == -7 +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertTrue(parsed.result_0.value) + + + def XXXtestByteArraysAndStringsMethods(self): + """Run methods from byte arrays and strings.""" + + # Cf. 6.4.3. Byte arrays + # 6.4.4. Strings + + definitions = ''' +instances: + result_1: + value: '[].length == 0' + result_2: + value: "'edcba'.reverse == 'XXabcdeXX'.substring(2, 6)" + result_3: + value: "'123'.to_i == 123 and '-123'.to_i == -123" + result_4: + value: "[ 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x63, 0x69, 0xc3, 0xb3, 0x6e, 0x2e, 0x73, 0x78, 0x69 ].to_s('utf-8')" + result_5: + value: "'1010'.to_i(2) == 10 and 'cc'.to_i(16) == 204" +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertTrue(parsed.result_1.value) + + self.assertTrue(parsed.result_2.value) + + self.assertTrue(parsed.result_3.value) + + # Cf. https://docs.gtk.org/glib/character-set.html + # https://developer-old.gnome.org/glib/stable/glib-Character-Set-Conversion.html#g-convert + self.assertEqual(parsed.result_4.value.decode('utf-8'), 'Presentación.sxi') + + self.assertTrue(parsed.result_5.value) + + + def __passed__testEnumsMethods(self): + """Run methods from booleans.""" + + # Cf. 6.4.5. Enums + + pass + + + def testBooleansMethods(self): + """Run methods from booleans.""" + + # Cf. 6.4.6. Booleans + + definitions = ''' +instances: + result_0: + value: true.to_i == 1 + result_1: + value: (1 == 2).to_i == 0 +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertTrue(parsed.result_0.value) + + self.assertTrue(parsed.result_1.value) + + + def testUserDefinedTypes(self): + """Retrieve user-defined types.""" + + # Cf. 6.4.7. User-defined types + + definitions = ''' +instances: + result_0: + value: _root +''' + + content = MemoryContent(b'') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.result_0.value, parsed) + + + def __passed__testArraysMethods(self): + """Run methods from arrays.""" + + # Cf. 6.4.8. Array types + + pass + + + def __passed__testStreamsMethods(self): + """Run methods from streams.""" + + # Cf. 6.4.9. Streams + + pass + + + + ############################## + ### 7. Advanced techniques + ############################## + + + def testSwitchOverStrings(self): + """Switch over strings.""" + + # Cf. 7.1.1. Switching over strings + + definitions = ''' +seq: + - id: rec_type + type: strz + - id: body + type: + switch-on: rec_type + cases: + '"KETCHUP"': rec_type_1 + '"MUSTARD"': rec_type_2 + '"GUACAMOLE"': rec_type_3 +types: + rec_type_1: + instances: + direct: + value: 1 + rec_type_2: + instances: + direct: + value: 2 + rec_type_3: + instances: + direct: + value: 3 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'GUACAMOLE\x00') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.rec_type.value, b'GUACAMOLE') + + self.assertEqual(parsed.body.direct.value, 3) + + + def testSwitchOverEnums(self): + """Switch over enumerations.""" + + # Cf. 7.1.2. Switching over enums + + definitions = ''' +seq: + - id: rec_type + type: u1 + enum: media + - id: body + type: + switch-on: rec_type + cases: + 'media::cdrom': rec_type_1 + 'media::dvdrom': rec_type_2 + 'media::cassette': rec_type_3 +types: + rec_type_1: + instances: + direct: + value: 1 + rec_type_2: + instances: + direct: + value: 2 + rec_type_3: + instances: + direct: + value: 3 +enums: + media: + 1: cdrom + 2: dvdrom + 3: cassette +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.rec_type.value, 1) + + self.assertEqual(parsed.body.direct.value, 1) + + + def testFourCC(self): + """Recognize four character code.""" + + # Cf. 7.1.3. FourCC + + definitions = ''' +seq: + - id: fourcc + type: u4le + enum: pixel_formats + - id: len + type: u1 + - id: body + size: len + type: + switch-on: fourcc + cases: + 'pixel_formats::rgb2': block_rgb2 + 'pixel_formats::rle4': block_rle4 + 'pixel_formats::rle8': block_rle8 +types: + block_rgb2: + instances: + direct: + value: 2 + block_rle4: + instances: + direct: + value: 4 + block_rle8: + instances: + direct: + value: 8 +enums: + pixel_formats: + 0x32424752: rgb2 + 0x34454C52: rle4 + 0x38454C52: rle8 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'RLE4\x05ABCDE') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.fourcc.value, 0x34454C52) + + self.assertEqual(parsed.len.value, 0x5) + + self.assertEqual(parsed.body.direct.value, 4) + + + def testNothing(self): + """Do nothing.""" + + # Cf. 7.2. Do nothing + + definitions = ''' +seq: + - id: field_0 + size: 1 + - id: field_1 + type: dummy_1 + - id: field_2 + type: dummy_2 + - id: field_3 + type: dummy_3 + - id: field_4 + type: dummy_4 + - id: field_5 + size: 1 +types: + # One can use empty JSON object syntax to avoid specifying any of + # `seq`, `instances`, etc, sections. + dummy_1: {} + # One can use explicit doc to note that there's nothing there. + dummy_2: + doc: This type is intentionally left blank. + # One can use empty `seq` or `instances` or `types` section, any + # other empty sections, or any combination of thereof. + dummy_3: + seq: [] + instances: {} + types: {} + # One can use a very explicit notion of the fact that we want to parse 0 bytes. + dummy_4: + seq: + - id: no_value + size: 0 +''' + + content = MemoryContent(b'az') + + kstruct = KaitaiStruct(definitions) + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.field_0.value, b'a') + + self.assertEqual(type(parsed.field_1).__name__, 'RecordEmpty') + self.assertEqual(parsed.field_1.range.length, 0) + + self.assertEqual(type(parsed.field_2).__name__, 'RecordEmpty') + self.assertEqual(parsed.field_2.range.length, 0) + + self.assertEqual(type(parsed.field_3).__name__, 'RecordEmpty') + self.assertEqual(parsed.field_3.range.length, 0) + + self.assertEqual(type(parsed.field_4.no_value).__name__, 'RecordEmpty') + self.assertEqual(parsed.field_4.no_value.range.length, 0) + + self.assertEqual(parsed.field_5.value, b'z') + + + def testConsumeIncludeTerminators(self): + """Consume and/or include terminators.""" + + # Cf. 7.3.1. Terminator: consume or include? + + definitions = ''' +seq: + - id: str1 + type: str + terminator: 0x2e # `.` + - id: str2 + type: str + terminator: 0x2e # `.` +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'foo.bar.') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.str1.value, b'foo') + + self.assertEqual(parsed.str2.value, b'bar') + + + definitions = ''' +seq: + - id: str1 + type: str + terminator: 0x2e # `.` + include: true + - id: str2 + type: str + terminator: 0x2e # `.` + eos-error: false +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'foo.bar') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.str1.value, b'foo.') + + self.assertEqual(parsed.str2.value, b'bar') + + + definitions = ''' +seq: + - id: str1 + type: str + terminator: 0x2e # `.` + consume: false + - id: the_rest + type: str + size-eos: true +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'foo.bar.') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.str1.value, b'foo') + + self.assertEqual(parsed.the_rest.value, b'.bar.') + + + definitions = ''' +seq: + - id: str1 + type: str + terminator: . + - id: the_rest + type: str + size-eos: true +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'foo.bar.') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.str1.value, b'foo') + + self.assertEqual(parsed.the_rest.value, b'bar.') + + + definitions = ''' +seq: + - id: str1 + type: str + terminator: xxx. + - id: the_rest + type: str + size-eos: true +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'fooxxx.bar.') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.str1.value, b'foo') + + self.assertEqual(parsed.the_rest.value, b'bar.') + + + def testIgnoreErrorsInDelimitedStructures(self): + """Ignore errors in delimited structures.""" + + # Cf. 7.3.2. Ignoring errors in delimited structures + + definitions = ''' +seq: + - id: my_string + type: str + terminator: 0 + eos-error: false +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x61\x62\x63\x00\x64\x65\x66') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_string.value, b'abc') + + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x61\x62\x63\x00') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_string.value, b'abc') + + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x61\x62\x63') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.my_string.value, b'abc') + + + def __passed__testImportTypesFromOtherFiles(self): + """Import types from other files.""" + + # Cf. 7.4. Importing types from other files + + pass + + + def __passed__testPlugExternalCodeForOpaqueTypes(self): + """Plug external code for opaque types.""" + + # Cf. 7.5. Opaque types: plugging in external code + + pass + + + def __passed__testCustomProcessingRoutines(self): + """Handle custom processing routines.""" + + # Cf. 7.6. Custom processing routines + + pass + + + def __passed__testParentTypeEnforcing(self): + """Enforce parent type.""" + + # Cf. 7.7. Enforcing parent type + + pass + + + def testTypecasting(self): + """Ensure there is no need for typecasting.""" + + # Cf. 7.8. Typecasting + + definitions = ''' +seq: + - id: num_sections + type: u1 + - id: sections + type: section + repeat: expr + repeat-expr: num_sections +types: + section: + seq: + - id: sect_type + type: u1 + - id: body + type: + switch-on: sect_type + cases: + 1: sect_header + 2: sect_color_data + sect_header: + seq: + - id: width + type: u1 + - id: height + type: u1 + sect_color_data: + seq: + - id: rgb + size: 3 +instances: + check_0: + value: sections[0].body.width * sections[0].body.height + check_1: + value: sections[1].body.rgb + check_2: + value: sections[2].body.width * sections[2].body.height + check_3: + value: sections[3].body.rgb +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x04\x01\x02\x04\x02ABC\x01\x03\x05\x02UVW') + + parsed = kstruct.parse(content) + + # Vérifications externes + + self.assertEqual(parsed.num_sections.value, 4) + + self.assertEqual(len(parsed.sections), 4) + + self.assertEqual(parsed.sections[0].body.width.value + parsed.sections[0].body.height.value, 6) + + self.assertEqual(parsed.sections[1].body.rgb.value, b'ABC') + + self.assertEqual(parsed.sections[2].body.width.value + parsed.sections[2].body.height.value, 8) + + self.assertEqual(parsed.sections[3].body.rgb.value, b'UVW') + + # Vérifications internes + + self.assertEqual(parsed.check_0.value, 8) + + self.assertEqual(parsed.check_1.value.value, b'ABC') + + self.assertEqual(parsed.check_2.value, 15) + + self.assertEqual(parsed.check_3.value.value, b'UVW') + + + + ########################## + ### 8. Common pitfalls + ########################## + + + def testReadTypeWithSubstream(self): + """Read user-type with substream.""" + + # Cf. 8.1. Specifying size creates a substream + + definitions = ''' +seq: + - id: header + size: 4 + - id: block + type: block + size: 4 # <= important size designation, creates a substream +instances: + byte_3: + pos: 3 + type: u1 +types: + block: + instances: + byte_3: + pos: 3 + type: u1 + byte_3_alt: + io: _root._io # <= thanks to this, always points to a byte in main stream + pos: 3 + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x00\x01\x02\x03\x04\x05\x06\x07') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.header.value, b'\x00\x01\x02\x03') + + self.assertEqual(parsed.byte_3.value, 0x03) + + self.assertEqual(parsed.block.byte_3.value, 0x07) + + self.assertEqual(parsed.block.byte_3_alt.value, 0x03) + + + definitions = ''' +seq: + - id: header + size: 4 + - id: block + type: block +instances: + byte_3: + pos: 3 + type: u1 +types: + block: + instances: + byte_3: + pos: 3 + type: u1 + byte_3_alt: + io: _root._io # <= thanks to this, always points to a byte in main stream + pos: 3 + type: u1 +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x00\x01\x02\x03\x04\x05\x06\x07') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.header.value, b'\x00\x01\x02\x03') + + self.assertEqual(parsed.byte_3.value, 0x03) + + self.assertEqual(parsed.block.byte_3.value, 0x03) + + self.assertEqual(parsed.block.byte_3_alt.value, 0x03) + + + def testReadTypeWithoutSubstream(self): + """Read user-type without substream.""" + + # Cf. 8.2. Not specifying size does not create a substream + + definitions = ''' +seq: + - id: header + size: 2 + - id: block_as_type1 + type: type1 + size: 2 # <= important, creates a substream +types: + type1: + seq: + - id: val1 + size: 2 + type2: + seq: + - id: val2 + size: 2 +instances: + block_as_type2: + io: block_as_type1._io + pos: 0 + type: type2 + internal_check: + value: block_as_type2._io == _root._io +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'aabb') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.header.value, b'aa') + + self.assertEqual(parsed.block_as_type1.val1.value, b'bb') + + self.assertEqual(parsed.block_as_type2.val2.value, b'bb') + + self.assertFalse(parsed.internal_check.value) + + + definitions = ''' +seq: + - id: header + size: 2 + - id: block_as_type1 + type: type1 +types: + type1: + seq: + - id: val1 + size: 2 + type2: + seq: + - id: val2 + size: 2 +instances: + block_as_type2: + io: block_as_type1._io + pos: 0 + type: type2 + internal_check: + value: block_as_type2._io == _root._io +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'aabb') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.header.value, b'aa') + + self.assertEqual(parsed.block_as_type1.val1.value, b'bb') + + self.assertEqual(parsed.block_as_type2.val2.value, b'aa') + + self.assertTrue(parsed.internal_check.value) + + + def __passed__testSizedProcess(self): + """Provide a sized data to processing.""" + + # Cf. 8.3. Applying process without a size + + pass + + + def __passed__testRelatedKeys(self): + """Check refering keys and their related YAML nodes.""" + + # Cf. 8.4. Keys relating to the whole array and to each element in repeated attributes + + pass + + + + ####################### + ### x. Extra checks + ####################### + + + def testMssingField(self): + """Raise error on missing field.""" + + definitions = ''' +seq: + - id: field0 + size-eos: true +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\x01\x02\x02\x03') + + parsed = kstruct.parse(content) + self.assertIsNotNone(parsed) + + self.assertEqual(parsed.field0.creator.raw_id, 'field0') + + self.assertEqual(parsed.field0.value, b'\x01\x02\x02\x03') + + # AttributeError: 'pychrysalide.plugins.kaitai.records.RecordList' object has no attribute 'xxxx' + with self.assertRaisesRegex(AttributeError, "object has no attribute 'xxxx'"): + print(parsed.xxxx) + + + def testLEB128Values(self): + """Read some Little Endian Base 128 values.""" + + definitions = ''' +seq: + - id: groups + type: group + repeat: until + repeat-until: not _.has_next +types: + group: + -webide-representation: '{value}' + doc: | + One byte group, clearly divided into 7-bit "value" chunk and 1-bit "continuation" flag. + seq: + - id: b + type: u1 + instances: + has_next: + value: (b & 0b1000_0000) != 0 + doc: If true, then we have more bytes to read + value: + value: b & 0b0111_1111 + doc: The 7-bit (base128) numeric value chunk of this group +instances: + len: + value: groups.size + value: + value: >- + groups[0].value + + (len >= 2 ? (groups[1].value << 7) : 0) + + (len >= 3 ? (groups[2].value << 14) : 0) + + (len >= 4 ? (groups[3].value << 21) : 0) + + (len >= 5 ? (groups[4].value << 28) : 0) + + (len >= 6 ? (groups[5].value << 35) : 0) + + (len >= 7 ? (groups[6].value << 42) : 0) + + (len >= 8 ? (groups[7].value << 49) : 0) + doc: Resulting unsigned value as normal integer + sign_bit: + value: '1 << (7 * len - 1)' + value_signed: + value: '(value ^ sign_bit) - sign_bit' + doc-ref: https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend +''' + + kstruct = KaitaiStruct(definitions) + + content = MemoryContent(b'\xe5\x8e\x26') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.len.value, 3) + + self.assertEqual(parsed.value.value, parsed.value_signed.value) + + self.assertEqual(parsed.value.value, 624485) + + + content = MemoryContent(b'\xc0\xbb\x78') + + parsed = kstruct.parse(content) + + self.assertEqual(parsed.len.value, 3) + + self.assertNotEqual(parsed.value.value, parsed.value_signed.value) + + self.assertEqual(parsed.value_signed.value, -123456) -- cgit v0.11.2-87-g4458