From ab6b87b7309e2d00926615f6557016bee6ab0b71 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Thu, 12 Oct 2023 01:16:21 +0200 Subject: Add two new functions to ROST grammar: modpath and maxcommon. --- plugins/apihashing/classics/ror13.c | 35 +++ plugins/encodings/rost/base64.c | 35 +++ src/analysis/scan/core.c | 4 + src/analysis/scan/exprs/handler-int.h | 6 +- src/analysis/scan/exprs/handler.c | 132 +++++++-- src/analysis/scan/exprs/handler.h | 5 +- src/analysis/scan/exprs/literal.c | 52 ++++ src/analysis/scan/grammar.y | 8 +- src/analysis/scan/items/Makefile.am | 2 + src/analysis/scan/items/maxcommon.c | 366 ++++++++++++++++++++++++ src/analysis/scan/items/maxcommon.h | 58 ++++ src/analysis/scan/items/modpath.c | 301 +++++++++++++++++++ src/analysis/scan/items/modpath.h | 58 ++++ src/analysis/scan/matches/bytes-int.h | 3 + src/analysis/scan/matches/bytes.c | 56 ++++ src/analysis/scan/matches/bytes.h | 6 + src/analysis/scan/matches/pending.c | 55 +++- src/analysis/scan/matches/pending.h | 6 + src/analysis/scan/patterns/modifier-int.h | 4 + src/analysis/scan/patterns/modifier.c | 27 ++ src/analysis/scan/patterns/modifier.h | 3 + src/analysis/scan/patterns/modifiers/hex.c | 35 +++ src/analysis/scan/patterns/modifiers/list.c | 32 +++ src/analysis/scan/patterns/modifiers/plain.c | 35 +++ src/analysis/scan/patterns/modifiers/rev.c | 35 +++ src/analysis/scan/patterns/modifiers/xor.c | 43 +++ src/analysis/scan/patterns/token.c | 28 ++ src/analysis/scan/patterns/token.h | 3 + src/analysis/scan/patterns/tokens/nodes/plain.c | 42 ++- src/analysis/scan/patterns/tokens/nodes/plain.h | 3 + src/analysis/scan/rule.c | 3 + tests/analysis/scan/functions.py | 26 ++ 32 files changed, 1475 insertions(+), 32 deletions(-) create mode 100644 src/analysis/scan/items/maxcommon.c create mode 100644 src/analysis/scan/items/maxcommon.h create mode 100644 src/analysis/scan/items/modpath.c create mode 100644 src/analysis/scan/items/modpath.h diff --git a/plugins/apihashing/classics/ror13.c b/plugins/apihashing/classics/ror13.c index 6e063f8..1b2421d 100644 --- a/plugins/apihashing/classics/ror13.c +++ b/plugins/apihashing/classics/ror13.c @@ -62,6 +62,9 @@ static uint32_t compute_ror13(const sized_binary_t *); /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_ror13_modifier_transform(const GScanRor13Modifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_ror13_modifier_get_path(const GScanRor13Modifier *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -100,6 +103,7 @@ static void g_scan_ror13_modifier_class_init(GScanRor13ModifierClass *klass) modifier->get_name = (get_scan_modifier_name_fc)g_scan_ror13_modifier_get_name; modifier->transform = (transform_scan_token_fc)g_scan_ror13_modifier_transform; + modifier->get_path = (get_modifier_path)g_scan_ror13_modifier_get_path; } @@ -286,3 +290,34 @@ static bool g_scan_ror13_modifier_transform(const GScanRor13Modifier *modifier, return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_ror13_modifier_get_path(const GScanRor13Modifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + + if (*index > 0) + { + result = NULL; + (*index)--; + } + + else + result = strdup("rev"); + + return result; + +} diff --git a/plugins/encodings/rost/base64.c b/plugins/encodings/rost/base64.c index 85e268d..15a3ec1 100644 --- a/plugins/encodings/rost/base64.c +++ b/plugins/encodings/rost/base64.c @@ -72,6 +72,9 @@ static bool g_scan_base64_modifier_can_handle_arg(const GScanBase64Modifier *, c /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_base64_modifier_transform_with_arg(const GScanBase64Modifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_base64_modifier_get_path(const GScanBase64Modifier *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -112,6 +115,7 @@ static void g_scan_base64_modifier_class_init(GScanBase64ModifierClass *klass) modifier->transform = (transform_scan_token_fc)g_scan_base64_modifier_transform; modifier->can_handle = (can_token_modifier_handle_arg)g_scan_base64_modifier_can_handle_arg; modifier->transform_with = (transform_scan_token_with_fc)g_scan_base64_modifier_transform_with_arg; + modifier->get_path = (get_modifier_path)g_scan_base64_modifier_get_path; } @@ -511,3 +515,34 @@ static bool g_scan_base64_modifier_transform_with_arg(const GScanBase64Modifier return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_base64_modifier_get_path(const GScanBase64Modifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + + if (*index > 3) + { + result = NULL; + (*index) -= 3; + } + + else + result = strdup("base64"); + + return result; + +} diff --git a/src/analysis/scan/core.c b/src/analysis/scan/core.c index fdee1c7..7b1a455 100644 --- a/src/analysis/scan/core.c +++ b/src/analysis/scan/core.c @@ -30,6 +30,8 @@ #include "items/count.h" #include "items/datasize.h" +#include "items/maxcommon.h" +#include "items/modpath.h" #include "items/uint.h" #include "items/console/log.h" #ifdef INCLUDE_MAGIC_SUPPORT @@ -249,6 +251,8 @@ bool populate_main_scan_namespace(GScanNamespace *space) if (result) result = REGISTER_FUNC(space, g_scan_count_function_new()); if (result) result = REGISTER_FUNC(space, g_scan_datasize_function_new()); + if (result) result = REGISTER_FUNC(space, g_scan_maxcommon_function_new()); + if (result) result = REGISTER_FUNC(space, g_scan_modpath_function_new()); if (result) result = REGISTER_FUNC(space, g_scan_uint_function_new(MDS_8_BITS_SIGNED, SRE_LITTLE)); if (result) result = REGISTER_FUNC(space, g_scan_uint_function_new(MDS_8_BITS_UNSIGNED, SRE_LITTLE)); diff --git a/src/analysis/scan/exprs/handler-int.h b/src/analysis/scan/exprs/handler-int.h index f707fdb..a38dd03 100644 --- a/src/analysis/scan/exprs/handler-int.h +++ b/src/analysis/scan/exprs/handler-int.h @@ -37,7 +37,9 @@ struct _GScanPatternHandler { GScanExpression parent; /* A laisser en premier */ - GSearchPattern *pattern; /* Motif associé */ + GSearchPattern **patterns; /* Motifs associés */ + size_t count; /* Nombre de ces motifs */ + ScanHandlerType type; /* Manipulation attendue */ }; @@ -51,7 +53,7 @@ struct _GScanPatternHandlerClass /* Met en place une manipulation de correspondances établies. */ -bool g_scan_pattern_handler_create(GScanPatternHandler *, GSearchPattern *, ScanHandlerType); +bool g_scan_pattern_handler_create(GScanPatternHandler *, GSearchPattern ** const, size_t, ScanHandlerType); diff --git a/src/analysis/scan/exprs/handler.c b/src/analysis/scan/exprs/handler.c index 1676522..1b6416e 100644 --- a/src/analysis/scan/exprs/handler.c +++ b/src/analysis/scan/exprs/handler.c @@ -122,7 +122,9 @@ static void g_scan_pattern_handler_class_init(GScanPatternHandlerClass *klass) static void g_scan_pattern_handler_init(GScanPatternHandler *handler) { - handler->pattern = NULL; + handler->patterns = NULL; + handler->count = 0; + handler->type = SHT_RAW; } @@ -142,7 +144,10 @@ static void g_scan_pattern_handler_init(GScanPatternHandler *handler) static void g_scan_pattern_handler_dispose(GScanPatternHandler *handler) { - g_clear_object(&handler->pattern); + size_t i; /* Boucle de parcours */ + + for (i = 0; i < handler->count; i++) + g_clear_object(&handler->patterns[i]); G_OBJECT_CLASS(g_scan_pattern_handler_parent_class)->dispose(G_OBJECT(handler)); @@ -170,8 +175,9 @@ static void g_scan_pattern_handler_finalize(GScanPatternHandler *handler) /****************************************************************************** * * -* Paramètres : pattern = motif à impliquer. * -* type = type de manipulation attendue. * +* Paramètres : patterns = motifs à impliquer. * +* count = quantité de ces motifs. * +* type = type de manipulation attendue. * * * * Description : Met en place une manipulation de correspondances établies. * * * @@ -181,13 +187,13 @@ static void g_scan_pattern_handler_finalize(GScanPatternHandler *handler) * * ******************************************************************************/ -GScanExpression *g_scan_pattern_handler_new(GSearchPattern *pattern, ScanHandlerType type) +GScanExpression *g_scan_pattern_handler_new(GSearchPattern ** const patterns, size_t count, ScanHandlerType type) { GScanExpression *result; /* Structure à retourner */ result = g_object_new(G_TYPE_SCAN_PATTERN_HANDLER, NULL); - if (!g_scan_pattern_handler_create(G_SCAN_PATTERN_HANDLER(result), pattern, type)) + if (!g_scan_pattern_handler_create(G_SCAN_PATTERN_HANDLER(result), patterns, count, type)) g_clear_object(&result); return result; @@ -197,9 +203,10 @@ GScanExpression *g_scan_pattern_handler_new(GSearchPattern *pattern, ScanHandler /****************************************************************************** * * -* Paramètres : handler = instance à initialiser pleinement. * -* pattern = motif à impliquer. * -* type = type de manipulation attendue. * +* Paramètres : handler = instance à initialiser pleinement. * +* patterns = motifs à impliquer. * +* count = quantité de ces motifs. * +* type = type de manipulation attendue. * * * * Description : Met en place une manipulation de correspondances établies. * * * @@ -209,15 +216,22 @@ GScanExpression *g_scan_pattern_handler_new(GSearchPattern *pattern, ScanHandler * * ******************************************************************************/ -bool g_scan_pattern_handler_create(GScanPatternHandler *handler, GSearchPattern *pattern, ScanHandlerType type) +bool g_scan_pattern_handler_create(GScanPatternHandler *handler, GSearchPattern ** const patterns, size_t count, ScanHandlerType type) { bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ result = g_scan_expression_create(G_SCAN_EXPRESSION(handler), SRS_WAIT_FOR_SCAN); if (!result) goto exit; - handler->pattern = pattern; - g_object_ref(G_OBJECT(pattern)); + handler->patterns = malloc(count * sizeof(GSearchPattern *)); + handler->count = count; + + for (i = 0; i < count; i++) + { + handler->patterns[i] = patterns[i]; + g_object_ref(G_OBJECT(patterns[i])); + } handler->type = type; @@ -228,6 +242,63 @@ bool g_scan_pattern_handler_create(GScanPatternHandler *handler, GSearchPattern } +/****************************************************************************** +* * +* Paramètres : handler = instance à initialiser pleinement. * +* ctx = contexte de suivi de l'analyse courante. * +* count = quantité de correspondances enregistrées. [OUT] * +* * +* Description : Fournit la liste de toutes les correspondances représentées. * +* * +* Retour : Liste courante de correspondances établies. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GScanMatch **g_scan_pattern_handler_get_all_matches(const GScanPatternHandler *handler, GScanContext *ctx, size_t *count) +{ + GScanMatch **result; /* Liste à retourner */ + size_t used; /* Indice pour le stockage */ + size_t i; /* Boucle de parcours #1 */ + size_t partial; /* Décompte partiel */ + const GScanMatch **matches; /* Correspondances en place */ + size_t k; /* Boucle de parcours #2 */ + + result = NULL; + + if (!g_scan_pattern_handler_count_items(handler, ctx, count)) + { + *count = 0; + goto exit; + } + + if (*count == 0) + goto exit; + + result = malloc(*count * sizeof(GScanMatch *)); + + used = 0; + + for (i = 0; i < handler->count; i++) + { + matches = g_scan_context_get_full_matches(ctx, handler->patterns[i], &partial); + + for (k = 0; k < partial; k++) + { + result[used++] = matches[k]; + g_object_ref(G_OBJECT(matches[k])); + } + + } + + exit: + + return result; + +} + + /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ @@ -284,9 +355,7 @@ static bool g_scan_pattern_handler_reduce_to_boolean(const GScanPatternHandler * bool result; /* Bilan à retourner */ size_t count; /* Quantité de correspondances */ - result = true; - - g_scan_context_get_full_matches(ctx, expr->pattern, &count); + result = g_scan_pattern_handler_count_items(expr, ctx, &count); *out = g_scan_literal_expression_new(LVT_BOOLEAN, (bool []){ count > 0 }); @@ -312,12 +381,20 @@ static bool g_scan_pattern_handler_reduce_to_boolean(const GScanPatternHandler * static bool g_scan_pattern_handler_count_items(const GScanPatternHandler *expr, GScanContext *ctx, size_t *count) { bool result; /* Bilan à retourner */ + size_t partial; /* Décompte partiel */ + size_t i; /* Boucle de parcours */ result = true; assert(g_scan_context_is_scan_done(ctx)); - g_scan_context_get_full_matches(ctx, expr->pattern, count); + *count = 0; + + for (i = 0; i < expr->count; i++) + { + g_scan_context_get_full_matches(ctx, expr->patterns[i], &partial); + *count += partial; + } return result; @@ -342,6 +419,7 @@ static bool g_scan_pattern_handler_count_items(const GScanPatternHandler *expr, static bool g_scan_pattern_handler_get_item(const GScanPatternHandler *expr, size_t index, GScanContext *ctx, GScanExpression **out) { bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ size_t count; /* Quantité de correspondances */ const GScanMatch **matches; /* Correspondances en place */ const GScanBytesMatch *match; /* Correspondance ciblée */ @@ -353,18 +431,32 @@ static bool g_scan_pattern_handler_get_item(const GScanPatternHandler *expr, siz const bin_t *data; /* Accès aux données brutes */ sized_string_t binary; /* Conversion de formats */ + result = false; + assert(g_scan_context_is_scan_done(ctx)); - matches = g_scan_context_get_full_matches(ctx, expr->pattern, &count); + /* Identification du motif concerné */ - result = (index < count); - if (!result) goto done; + for (i = 0; i < expr->count; i++) + { + matches = g_scan_context_get_full_matches(ctx, expr->patterns[i], &count); + + if (index < count) + break; + else + index -= count; + + } + + if (i == expr->count) goto done; result = G_IS_SCAN_BYTES_MATCH(matches[index]); if (!result) goto done; match = G_SCAN_BYTES_MATCH(matches[index]); + /* Traitement adapté de la requête */ + len = g_scan_bytes_match_get_location(match, &start, &end); switch (expr->type) @@ -376,7 +468,7 @@ static bool g_scan_pattern_handler_get_item(const GScanPatternHandler *expr, siz data = g_binary_content_get_raw_access(content, &pos, len); - binary.data = data; + binary.static_bin_data = data; binary.len = len; *out = g_scan_literal_expression_new(LVT_STRING, &binary); diff --git a/src/analysis/scan/exprs/handler.h b/src/analysis/scan/exprs/handler.h index 8ad700a..36985e7 100644 --- a/src/analysis/scan/exprs/handler.h +++ b/src/analysis/scan/exprs/handler.h @@ -59,7 +59,10 @@ typedef enum _ScanHandlerType GType g_scan_pattern_handler_get_type(void); /* Met en place une manipulation de correspondances établies. */ -GScanExpression *g_scan_pattern_handler_new(GSearchPattern *, ScanHandlerType); +GScanExpression *g_scan_pattern_handler_new(GSearchPattern ** const, size_t, ScanHandlerType); + +/* Fournit la liste de toutes les correspondances représentées. */ +GScanMatch **g_scan_pattern_handler_get_all_matches(const GScanPatternHandler *, GScanContext *, size_t *); diff --git a/src/analysis/scan/exprs/literal.c b/src/analysis/scan/exprs/literal.c index de7e32a..b1a094c 100644 --- a/src/analysis/scan/exprs/literal.c +++ b/src/analysis/scan/exprs/literal.c @@ -62,6 +62,9 @@ static bool g_scan_literal_expression_reduce_to_boolean(const GScanLiteralExpres /* Dénombre les éléments portés par une expression. */ static bool g_scan_literal_expression_count(const GScanLiteralExpression *, GScanContext *, size_t *); +/* Fournit un élément donné issu d'un ensemble constitué. */ +static bool g_scan_literal_expression_get_item(const GScanLiteralExpression *, size_t, GScanContext *, GScanExpression **); + /* ---------------------------------------------------------------------------------- */ @@ -100,6 +103,7 @@ static void g_scan_literal_expression_class_init(GScanLiteralExpressionClass *kl expr->cmp_rich = (compare_expr_rich_fc)g_scan_literal_expression_compare_rich; expr->reduce_to_bool = (reduce_expr_to_bool_fc)g_scan_literal_expression_reduce_to_boolean; expr->count = (count_scan_expr_fc)g_scan_literal_expression_count; + expr->get = (get_scan_expr_fc)g_scan_literal_expression_get_item; } @@ -659,3 +663,51 @@ static bool g_scan_literal_expression_count(const GScanLiteralExpression *expr, return result; } + + +/****************************************************************************** +* * +* Paramètres : expr = expression à consulter. * +* index = indice de l'élément à transférer. * +* ctx = contexte de suivi de l'analyse courante. * +* out = zone d'enregistrement de la réduction opérée. [OUT] * +* * +* Description : Fournit un élément donné issu d'un ensemble constitué. * +* * +* Retour : Bilan de l'opération : false en cas d'erreur irrécupérable. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_scan_literal_expression_get_item(const GScanLiteralExpression *expr, size_t index, GScanContext *ctx, GScanExpression **out) +{ + bool result; /* Bilan à retourner */ + sized_string_t ch; /* Caractère extrait */ + + switch (expr->value_type) + { + case LVT_STRING: + + result = (index < expr->value.string.len); + + if (result) + { + ch.data = expr->value.string.data + index; + ch.len = 1; + + *out = g_scan_literal_expression_new(LVT_STRING, &ch); + + } + + break; + + default: + result = false; + break; + + } + + return result; + +} diff --git a/src/analysis/scan/grammar.y b/src/analysis/scan/grammar.y index 2863e21..36f6df9 100644 --- a/src/analysis/scan/grammar.y +++ b/src/analysis/scan/grammar.y @@ -1376,7 +1376,7 @@ relational_expr : cexpression "<" cexpression { $$ = g_scan_relational_operatio $$ = NULL; else { - $$ = g_scan_pattern_handler_new(__pat, SHT_RAW); + $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_RAW); g_object_unref(G_OBJECT(__pat)); } } @@ -1400,7 +1400,7 @@ relational_expr : cexpression "<" cexpression { $$ = g_scan_relational_operatio $$ = NULL; else { - $$ = g_scan_pattern_handler_new(__pat, SHT_START); + $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_START); g_object_unref(G_OBJECT(__pat)); } } @@ -1412,7 +1412,7 @@ relational_expr : cexpression "<" cexpression { $$ = g_scan_relational_operatio $$ = NULL; else { - $$ = g_scan_pattern_handler_new(__pat, SHT_LENGTH); + $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_LENGTH); g_object_unref(G_OBJECT(__pat)); } } @@ -1424,7 +1424,7 @@ relational_expr : cexpression "<" cexpression { $$ = g_scan_relational_operatio $$ = NULL; else { - $$ = g_scan_pattern_handler_new(__pat, SHT_END); + $$ = g_scan_pattern_handler_new((GSearchPattern *[]) { __pat }, 1, SHT_END); g_object_unref(G_OBJECT(__pat)); } } diff --git a/src/analysis/scan/items/Makefile.am b/src/analysis/scan/items/Makefile.am index 89ae41e..9263c7b 100644 --- a/src/analysis/scan/items/Makefile.am +++ b/src/analysis/scan/items/Makefile.am @@ -14,6 +14,8 @@ endif libanalysisscanitems_la_SOURCES = \ count.h count.c \ datasize.h datasize.c \ + maxcommon.h maxcommon.c \ + modpath.h modpath.c \ uint-int.h \ uint.h uint.c diff --git a/src/analysis/scan/items/maxcommon.c b/src/analysis/scan/items/maxcommon.c new file mode 100644 index 0000000..a2e0f2a --- /dev/null +++ b/src/analysis/scan/items/maxcommon.c @@ -0,0 +1,366 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * maxcommon.c - détermination de la plus grand occurrence au sein d'un ensemble d'éléments + * + * 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 Foobar. If not, see . + */ + + +#include "maxcommon.h" + + +#include +#include + + +#include "../item-int.h" +#include "../exprs/literal.h" +#include "../../../glibext/comparison-int.h" + + + +/* ---------------------- INTRODUCTION D'UNE NOUVELLE FONCTION ---------------------- */ + + +/* Initialise la classe des repérages de plus grande occurrence. */ +static void g_scan_maxcommon_function_class_init(GScanMaxcommonFunctionClass *); + +/* Initialise une instance de repérage d'occurrence maximake. */ +static void g_scan_maxcommon_function_init(GScanMaxcommonFunction *); + +/* Supprime toutes les références externes. */ +static void g_scan_maxcommon_function_dispose(GScanMaxcommonFunction *); + +/* Procède à la libération totale de la mémoire. */ +static void g_scan_maxcommon_function_finalize(GScanMaxcommonFunction *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Indique le nom associé à une expression d'évaluation. */ +static char *g_scan_maxcommon_function_get_name(const GScanMaxcommonFunction *); + +/* Réduit une expression à une forme plus simple. */ +static bool g_scan_maxcommon_function_run_call(GScanMaxcommonFunction *, GScanExpression **, size_t, GScanContext *, GScanScope *, GObject **); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTRODUCTION D'UNE NOUVELLE FONCTION */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un décompte d'élément le plus représenté dans une série. */ +G_DEFINE_TYPE(GScanMaxcommonFunction, g_scan_maxcommon_function, G_TYPE_SCAN_REGISTERED_ITEM); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des repérages de plus grande occurrence.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_maxcommon_function_class_init(GScanMaxcommonFunctionClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GScanRegisteredItemClass *registered; /* Version de classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_maxcommon_function_dispose; + object->finalize = (GObjectFinalizeFunc)g_scan_maxcommon_function_finalize; + + registered = G_SCAN_REGISTERED_ITEM_CLASS(klass); + + registered->get_name = (get_registered_item_name_fc)g_scan_maxcommon_function_get_name; + registered->run_call = (run_registered_item_call_fc)g_scan_maxcommon_function_run_call; + +} + + +/****************************************************************************** +* * +* Paramètres : func = instance à initialiser. * +* * +* Description : Initialise une instance de repérage d'occurrence maximake. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_maxcommon_function_init(GScanMaxcommonFunction *func) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : func = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_maxcommon_function_dispose(GScanMaxcommonFunction *func) +{ + G_OBJECT_CLASS(g_scan_maxcommon_function_parent_class)->dispose(G_OBJECT(func)); + +} + + +/****************************************************************************** +* * +* Paramètres : func = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_maxcommon_function_finalize(GScanMaxcommonFunction *func) +{ + G_OBJECT_CLASS(g_scan_maxcommon_function_parent_class)->finalize(G_OBJECT(func)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Constitue une fonction de calcul de plus grande occurrence. * +* * +* Retour : Fonction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GScanRegisteredItem *g_scan_maxcommon_function_new(void) +{ + GScanRegisteredItem *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_SCAN_MAXCOMMON_FUNCTION, NULL); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : item = élément d'appel à consulter. * +* * +* Description : Indique le nom associé à une expression d'évaluation. * +* * +* Retour : Désignation humaine de l'expression d'évaluation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_maxcommon_function_get_name(const GScanMaxcommonFunction *item) +{ + char *result; /* Désignation à retourner */ + + result = strdup("maxcommon"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : item = élément d'appel à consulter. * +* args = liste d'éventuels arguments fournis. * +* count = taille de cette liste. * +* ctx = contexte de suivi de l'analyse courante. * +* scope = portée courante des variables locales. * +* out = zone d'enregistrement de la résolution opérée. [OUT] * +* * +* Description : Réduit une expression à une forme plus simple. * +* * +* Retour : Réduction correspondante, expression déjà réduite, ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_scan_maxcommon_function_run_call(GScanMaxcommonFunction *item, GScanExpression **args, size_t count, GScanContext *ctx, GScanScope *scope, GObject **out) +{ + bool result; /* Bilan à retourner */ + size_t used; /* Prochain emplacement libre */ + GScanExpression **collected; /* Représentants de groupes */ + size_t *scores; /* Taille de ces groupes */ + size_t i; /* Boucle de parcours #1 */ + size_t k; /* Boucle de parcours #2 */ + bool status; /* Bilan de la comparaison */ + bool equal; /* Egalité établie ? */ + size_t arg0_count; /* Taille de l'argument unique */ + GScanExpression *arg0_item; /* Elément de cet argument */ + size_t best; /* Meilleur score identifié */ + + result = (count > 0); + if (!result) goto exit; + + used = 0; + + /* Si la série à étudier est directement fournie */ + if (count > 1) + { + collected = malloc(count * sizeof(GScanExpression *)); + scores = malloc(count * sizeof(size_t)); + + for (i = 0; i < count; i++) + { + for (k = 0; k < used; k++) + { + status = g_comparable_item_compare_rich(G_COMPARABLE_ITEM(args[i]), + G_COMPARABLE_ITEM(collected[k]), + RCO_EQ, &equal); + + if (status && equal) + break; + + } + + if (k < used) + scores[k]++; + + else + { + collected[used] = args[i]; + g_object_ref(G_OBJECT(args[i])); + scores[used] = 1; + + used++; + + } + + } + + } + + /* Sinon on considère que l'arguement unique porte la liste (idéalement) */ + else + { + if (G_IS_SCAN_LITERAL_EXPRESSION(args[0]) || !g_scan_expression_handle_set_features(args[0])) + { + best = 1; + goto quick_unique; + } + +#ifndef NDEBUG + g_scan_expression_count_items(args[0], ctx, &arg0_count); +#else + status = bool g_scan_expression_count_items(args[0], ctx, &arg0_count); + assert(status); +#endif + + collected = malloc(arg0_count * sizeof(GScanExpression *)); + scores = malloc(arg0_count * sizeof(size_t)); + + for (i = 0; i < arg0_count; i++) + { +#ifndef NDEBUG + g_scan_expression_get_item(args[0], i, ctx, &arg0_item); +#else + status = g_scan_expression_get_item(args[0], i, ctx, &arg0_item); + assert(status); +#endif + + for (k = 0; k < used; k++) + { + status = g_comparable_item_compare_rich(G_COMPARABLE_ITEM(arg0_item), + G_COMPARABLE_ITEM(collected[k]), + RCO_EQ, &equal); + + if (status && equal) + break; + + } + + if (k < used) + { + g_object_unref(G_OBJECT(arg0_item)); + scores[k]++; + } + + else + { + collected[used] = arg0_item; + scores[used] = 1; + + used++; + + } + + } + + } + + /* Analyse des résultats */ + + best = 0; + + for (i = 0; i < used; i++) + if (scores[i] > best) + best = scores[i]; + + for (i = 0; i < used; i++) + g_object_unref(G_OBJECT(collected[i])); + + free(collected); + free(scores); + + quick_unique: + + assert(best > 0); + + *out = G_OBJECT(g_scan_literal_expression_new(LVT_UNSIGNED_INTEGER, (unsigned long long []){ best })); + + exit: + + return result; + +} diff --git a/src/analysis/scan/items/maxcommon.h b/src/analysis/scan/items/maxcommon.h new file mode 100644 index 0000000..a834c00 --- /dev/null +++ b/src/analysis/scan/items/maxcommon.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * maxcommon.h - prototypes pour la détermination de la plus grand occurrence au sein d'un ensemble d'éléments + * + * 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 Foobar. If not, see . + */ + + +#ifndef _ANALYSIS_SCAN_ITEMS_MAXCOMMON_H +#define _ANALYSIS_SCAN_ITEMS_MAXCOMMON_H + + +#include + + +#include "../item.h" + + + +#define G_TYPE_SCAN_MAXCOMMON_FUNCTION g_scan_maxcommon_function_get_type() +#define G_SCAN_MAXCOMMON_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SCAN_MAXCOMMON_FUNCTION, GScanMaxcommonFunction)) +#define G_IS_SCAN_MAXCOMMON_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SCAN_MAXCOMMON_FUNCTION)) +#define G_SCAN_MAXCOMMON_FUNCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SCAN_MAXCOMMON_FUNCTION, GScanMaxcommonFunctionClass)) +#define G_IS_SCAN_MAXCOMMON_FUNCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SCAN_MAXCOMMON_FUNCTION)) +#define G_SCAN_MAXCOMMON_FUNCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SCAN_MAXCOMMON_FUNCTION, GScanMaxcommonFunctionClass)) + + +/* Détermination de la plus grand occurrence au sein d'un ensemble (instance) */ +typedef GScanRegisteredItem GScanMaxcommonFunction; + +/* Détermination de la plus grand occurrence au sein d'un ensemble (classe) */ +typedef GScanRegisteredItemClass GScanMaxcommonFunctionClass; + + +/* Indique le type défini pour un décompte d'élément le plus représenté dans une série. */ +GType g_scan_maxcommon_function_get_type(void); + +/* Constitue une fonction de calcul de plus grande occurrence. */ +GScanRegisteredItem *g_scan_maxcommon_function_new(void); + + + +#endif /* _ANALYSIS_SCAN_ITEMS_MAXCOMMON_H */ diff --git a/src/analysis/scan/items/modpath.c b/src/analysis/scan/items/modpath.c new file mode 100644 index 0000000..2171bda --- /dev/null +++ b/src/analysis/scan/items/modpath.c @@ -0,0 +1,301 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * modpath.c - récupération des combinaisons de modification de motifs + * + * 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 Foobar. If not, see . + */ + + +#include "modpath.h" + + +#include "../item-int.h" + + +#include "../exprs/handler.h" +#include "../exprs/literal.h" +#include "../exprs/set.h" +#include "../matches/bytes.h" + + + +/* ---------------------- INTRODUCTION D'UNE NOUVELLE FONCTION ---------------------- */ + + +/* Initialise la classe des énumérations de formules de motifs. */ +static void g_scan_modpath_function_class_init(GScanModpathFunctionClass *); + +/* Initialise une instance d'énumération de formules de motifs. */ +static void g_scan_modpath_function_init(GScanModpathFunction *); + +/* Supprime toutes les références externes. */ +static void g_scan_modpath_function_dispose(GScanModpathFunction *); + +/* Procède à la libération totale de la mémoire. */ +static void g_scan_modpath_function_finalize(GScanModpathFunction *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Indique le nom associé à une expression d'évaluation. */ +static char *g_scan_modpath_function_get_name(const GScanModpathFunction *); + +/* Réduit une expression à une forme plus simple. */ +static bool g_scan_modpath_function_run_call(GScanModpathFunction *, GScanExpression **, size_t, GScanContext *, GScanScope *, GObject **); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTRODUCTION D'UNE NOUVELLE FONCTION */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une énumération de formules créant des motifs. */ +G_DEFINE_TYPE(GScanModpathFunction, g_scan_modpath_function, G_TYPE_SCAN_REGISTERED_ITEM); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des énumérations de formules de motifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_modpath_function_class_init(GScanModpathFunctionClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GScanRegisteredItemClass *registered; /* Version de classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_modpath_function_dispose; + object->finalize = (GObjectFinalizeFunc)g_scan_modpath_function_finalize; + + registered = G_SCAN_REGISTERED_ITEM_CLASS(klass); + + registered->get_name = (get_registered_item_name_fc)g_scan_modpath_function_get_name; + registered->run_call = (run_registered_item_call_fc)g_scan_modpath_function_run_call; + +} + + +/****************************************************************************** +* * +* Paramètres : func = instance à initialiser. * +* * +* Description : Initialise une instance d'énumération de formules de motifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_modpath_function_init(GScanModpathFunction *func) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : func = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_modpath_function_dispose(GScanModpathFunction *func) +{ + G_OBJECT_CLASS(g_scan_modpath_function_parent_class)->dispose(G_OBJECT(func)); + +} + + +/****************************************************************************** +* * +* Paramètres : func = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_scan_modpath_function_finalize(GScanModpathFunction *func) +{ + G_OBJECT_CLASS(g_scan_modpath_function_parent_class)->finalize(G_OBJECT(func)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Constitue une fonction d'énumération des formules de motifs. * +* * +* Retour : Fonction mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GScanRegisteredItem *g_scan_modpath_function_new(void) +{ + GScanRegisteredItem *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_SCAN_MODPATH_FUNCTION, NULL); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : item = élément d'appel à consulter. * +* * +* Description : Indique le nom associé à une expression d'évaluation. * +* * +* Retour : Désignation humaine de l'expression d'évaluation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_modpath_function_get_name(const GScanModpathFunction *item) +{ + char *result; /* Désignation à retourner */ + + result = strdup("modpath"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : item = élément d'appel à consulter. * +* args = liste d'éventuels arguments fournis. * +* count = taille de cette liste. * +* ctx = contexte de suivi de l'analyse courante. * +* scope = portée courante des variables locales. * +* out = zone d'enregistrement de la résolution opérée. [OUT] * +* * +* Description : Réduit une expression à une forme plus simple. * +* * +* Retour : Réduction correspondante, expression déjà réduite, ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_scan_modpath_function_run_call(GScanModpathFunction *item, GScanExpression **args, size_t count, GScanContext *ctx, GScanScope *scope, GObject **out) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + size_t mcount; /* Quantité de correspondances */ + GScanMatch **matches; /* Correspondances établies */ + size_t k; /* Boucle de parcours #2 */ + sized_string_t path; /* Combinaison à conserver */ + GScanExpression *subitem; /* Nouvel élément à transmettre*/ + + /* Validation préalable du type des arguments */ + + result = true; + + for (i = 0; i < count && result; i++) + result = G_IS_SCAN_PATTERN_HANDLER(args[i]); + + if (!result) + goto exit; + + /* Construction des chemins attendus */ + + *out = G_OBJECT(g_scan_generic_set_new()); + + for (i = 0; i < count; i++) + { + matches = g_scan_pattern_handler_get_all_matches(G_SCAN_PATTERN_HANDLER(args[i]), ctx, &mcount); + if (mcount == 0) continue; + + /** + * La série est à priori constituée d'éléments de même type, donc + * un test unique suffit. + */ + if (!G_IS_SCAN_BYTES_MATCH(matches[0])) + { + for (k = 0; k < mcount; k++) + g_object_unref(G_OBJECT(matches[k])); + } + + else + { + for (k = 0; k < mcount; k++) + { + path.data = g_scan_bytes_match_get_modifier_path(G_SCAN_BYTES_MATCH(matches[k])); + if (path.data == NULL) continue; + + path.len = strlen(path.data); + + printf(" >> add '%s'\n", path.data); + + subitem = g_scan_literal_expression_new(LVT_STRING, &path); + + g_scan_generic_set_add_item(G_SCAN_GENERIC_SET(*out), subitem); + + g_object_unref(G_OBJECT(subitem)); + + exit_szstr(&path); + + g_object_unref(G_OBJECT(matches[k])); + + } + + } + + free(matches); + + } + + exit: + + return result; + +} diff --git a/src/analysis/scan/items/modpath.h b/src/analysis/scan/items/modpath.h new file mode 100644 index 0000000..3a78ef7 --- /dev/null +++ b/src/analysis/scan/items/modpath.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * modpath.h - prototypes pour la récupération des combinaisons de modification de motifs + * + * 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 Foobar. If not, see . + */ + + +#ifndef _ANALYSIS_SCAN_ITEMS_MODPATH_H +#define _ANALYSIS_SCAN_ITEMS_MODPATH_H + + +#include + + +#include "../item.h" + + + +#define G_TYPE_SCAN_MODPATH_FUNCTION g_scan_modpath_function_get_type() +#define G_SCAN_MODPATH_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SCAN_MODPATH_FUNCTION, GScanModpathFunction)) +#define G_IS_SCAN_MODPATH_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SCAN_MODPATH_FUNCTION)) +#define G_SCAN_MODPATH_FUNCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SCAN_MODPATH_FUNCTION, GScanModpathFunctionClass)) +#define G_IS_SCAN_MODPATH_FUNCTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SCAN_MODPATH_FUNCTION)) +#define G_SCAN_MODPATH_FUNCTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SCAN_MODPATH_FUNCTION, GScanModpathFunctionClass)) + + +/* Récupération de formules à l'origine de la construction de motifs (instance) */ +typedef GScanRegisteredItem GScanModpathFunction; + +/* Récupération de formules à l'origine de la construction de motifs (classe) */ +typedef GScanRegisteredItemClass GScanModpathFunctionClass; + + +/* Indique le type défini pour une énumération de formules créant des motifs. */ +GType g_scan_modpath_function_get_type(void); + +/* Constitue une fonction d'énumération des formules de motifs. */ +GScanRegisteredItem *g_scan_modpath_function_new(void); + + + +#endif /* _ANALYSIS_SCAN_ITEMS_MODPATH_H */ diff --git a/src/analysis/scan/matches/bytes-int.h b/src/analysis/scan/matches/bytes-int.h index 6f7e60b..f57cb9f 100644 --- a/src/analysis/scan/matches/bytes-int.h +++ b/src/analysis/scan/matches/bytes-int.h @@ -42,6 +42,9 @@ struct _GScanBytesMatch phys_t start; /* Début du motif représenté */ phys_t len; /* Taille du motif représenté */ + size_t mod_path_index; /* Indice de construction */ + bool has_mod_path; /* Validité du champ précédent */ + }; /* Correspondance trouvée avec une chaîne (classe) */ diff --git a/src/analysis/scan/matches/bytes.c b/src/analysis/scan/matches/bytes.c index de101c4..f0b97fe 100644 --- a/src/analysis/scan/matches/bytes.c +++ b/src/analysis/scan/matches/bytes.c @@ -30,6 +30,7 @@ #include "bytes-int.h" +#include "../patterns/token.h" #include "../../../common/cpp.h" #include "../../../core/logs.h" @@ -121,6 +122,8 @@ static void g_scan_bytes_match_init(GScanBytesMatch *match) match->start = VMPA_NO_PHYSICAL; match->len = VMPA_NO_PHYSICAL; + match->has_mod_path = false; + } @@ -285,6 +288,59 @@ phys_t g_scan_bytes_match_get_location(const GScanBytesMatch *match, phys_t *sta } +/****************************************************************************** +* * +* Paramètres : match = informations de correspondance à compléter. * +* index = indice de la combinaison de modificateurs ciblée. * +* * +* Description : Mémorise l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_scan_bytes_match_remember_modifier_path(GScanBytesMatch *match, size_t index) +{ + match->mod_path_index = index; + match->has_mod_path = true; + +} + + +/****************************************************************************** +* * +* Paramètres : match = informations de correspondance à consulter. * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison gagnante. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_scan_bytes_match_get_modifier_path(const GScanBytesMatch *match) +{ + char *result; /* Combinaison à retourner */ + GBytesToken *pattern; /* Autre version du motif */ + + if (match->has_mod_path) + { + pattern = G_BYTES_TOKEN(G_SCAN_MATCH(match)->source); + result = g_bytes_token_get_modifier_path(pattern, match->mod_path_index); + } + + else + result = NULL; + + return result; + +} + + + /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ diff --git a/src/analysis/scan/matches/bytes.h b/src/analysis/scan/matches/bytes.h index e599ee4..bd7425d 100644 --- a/src/analysis/scan/matches/bytes.h +++ b/src/analysis/scan/matches/bytes.h @@ -60,6 +60,12 @@ GBinContent *g_scan_bytes_match_get_content(const GScanBytesMatch *); /* Indique la localisation d'une correspondance établie. */ phys_t g_scan_bytes_match_get_location(const GScanBytesMatch *, phys_t *, phys_t *); +/* Mémorise l'origine d'une correspondance à partir d'un indice. */ +void g_scan_bytes_match_remember_modifier_path(GScanBytesMatch *, size_t); + +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +char *g_scan_bytes_match_get_modifier_path(const GScanBytesMatch *); + #endif /* _ANALYSIS_SCAN_MATCHES_BYTES_H */ diff --git a/src/analysis/scan/matches/pending.c b/src/analysis/scan/matches/pending.c index 9ed4de3..57c63d7 100644 --- a/src/analysis/scan/matches/pending.c +++ b/src/analysis/scan/matches/pending.c @@ -48,10 +48,6 @@ static int compare_match_area(const match_area_t *, const match_area_t *); - - - - /* ---------------------------------------------------------------------------------- */ /* MEMORISATION D'UNE ZONE BORNEE */ /* ---------------------------------------------------------------------------------- */ @@ -82,6 +78,12 @@ static int compare_match_area(const match_area_t *a, const match_area_t *b) if (result == 0) result = sort_unsigned_long_long(a->ttl, b->ttl); + if (result == 0) + result = sort_unsigned_long_long(a->has_mod_path, b->has_mod_path); + + if (result == 0) + result = sort_unsigned_long_long(a->mod_path_index, b->mod_path_index); + return result; } @@ -293,6 +295,51 @@ void add_pending_match(pending_matches_t *matches, phys_t start, phys_t length) area->ttl = 1; + area->has_mod_path = false; + +} + + +/****************************************************************************** +* * +* Paramètres : matches = suivi de correspondances à compléter. * +* start = point de départ d'une nouvelle correspondance. * +* length = taille de la zone couverte. * +* index = indice de construction pour le motif concerné. * +* * +* Description : Ajoute au suivi la définition d'une nouvelle correspondance. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void add_pending_match_with_path(pending_matches_t *matches, phys_t start, phys_t length, size_t index) +{ + match_area_t *area; /* Zone à initialiser */ + + if (matches->used == matches->allocated) + { + matches->allocated += PENDING_ALLOC_SIZE; + + matches->areas = realloc(matches->areas, matches->allocated * sizeof(match_area_t)); + + } + + area = &matches->areas[matches->used++]; + + area->start = start; + area->end = start + length; + + assert(matches->content_start <= area->start); + assert(area->end <= matches->content_end); + + area->ttl = 1; + + area->mod_path_index = index; + area->has_mod_path = true; + } diff --git a/src/analysis/scan/matches/pending.h b/src/analysis/scan/matches/pending.h index 6df01c9..f4ac7a2 100644 --- a/src/analysis/scan/matches/pending.h +++ b/src/analysis/scan/matches/pending.h @@ -41,6 +41,9 @@ typedef struct _match_area_t unsigned long ttl; /* Durée de vie pour analyse */ + size_t mod_path_index; /* Indice de construction */ + bool has_mod_path; /* Validité du champ précédent */ + } match_area_t; /* Suivi de correspondances */ @@ -86,6 +89,9 @@ match_area_t * const *get_all_pending_matches(const pending_matches_t *, size_t /* Ajoute au suivi la définition d'une nouvelle correspondance. */ void add_pending_match(pending_matches_t *, phys_t, phys_t); +/* Ajoute au suivi la définition d'une nouvelle correspondance. */ +void add_pending_match_with_path(pending_matches_t *, phys_t, phys_t, size_t); + /* Etend une zone couverte dans le suivi des correspondances. */ void extend_pending_match_beginning(pending_matches_t *, size_t, phys_t); diff --git a/src/analysis/scan/patterns/modifier-int.h b/src/analysis/scan/patterns/modifier-int.h index e1de2d8..c9b3a36 100644 --- a/src/analysis/scan/patterns/modifier-int.h +++ b/src/analysis/scan/patterns/modifier-int.h @@ -41,6 +41,9 @@ typedef bool (* can_token_modifier_handle_arg) (const GScanTokenModifier *, cons /* Transforme une séquence d'octets pour motif de recherche. */ typedef bool (* transform_scan_token_with_fc) (const GScanTokenModifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +typedef char * (* get_modifier_path) (const GScanTokenModifier *, size_t *); + /* Transformation d'une séquence d'octets en une ou plusieurs autres formes (instance) */ struct _GScanTokenModifier @@ -59,6 +62,7 @@ struct _GScanTokenModifierClass transform_scan_token_fc transform; /* Opération de transformation */ can_token_modifier_handle_arg can_handle; /* Support d'argument donné */ transform_scan_token_with_fc transform_with; /* Opération encadrée */ + get_modifier_path get_path; /* Rappel d'une combinaison */ }; diff --git a/src/analysis/scan/patterns/modifier.c b/src/analysis/scan/patterns/modifier.c index 65f671f..9efd404 100644 --- a/src/analysis/scan/patterns/modifier.c +++ b/src/analysis/scan/patterns/modifier.c @@ -249,3 +249,30 @@ bool g_scan_token_modifier_transform_with_arg(const GScanTokenModifier *modifier return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_scan_token_modifier_get_path(const GScanTokenModifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + GScanTokenModifierClass *class; /* Classe à activer */ + + class = G_SCAN_TOKEN_MODIFIER_GET_CLASS(modifier); + + result = class->get_path(modifier, index); + + return result; + +} diff --git a/src/analysis/scan/patterns/modifier.h b/src/analysis/scan/patterns/modifier.h index ce2589a..9030a72 100644 --- a/src/analysis/scan/patterns/modifier.h +++ b/src/analysis/scan/patterns/modifier.h @@ -65,6 +65,9 @@ bool g_scan_token_modifier_can_handle_arg(const GScanTokenModifier *, const modi /* Transforme une séquence d'octets pour motif de recherche. */ bool g_scan_token_modifier_transform_with_arg(const GScanTokenModifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +char *g_scan_token_modifier_get_path(const GScanTokenModifier *, size_t *); + #endif /* _ANALYSIS_SCAN_MODIFIER_H */ diff --git a/src/analysis/scan/patterns/modifiers/hex.c b/src/analysis/scan/patterns/modifiers/hex.c index 849d9ff..4a41c7d 100644 --- a/src/analysis/scan/patterns/modifiers/hex.c +++ b/src/analysis/scan/patterns/modifiers/hex.c @@ -58,6 +58,9 @@ static char *g_scan_hex_modifier_get_name(const GScanHexModifier *); /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_hex_modifier_transform(const GScanHexModifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_hex_modifier_get_path(const GScanHexModifier *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -96,6 +99,7 @@ static void g_scan_hex_modifier_class_init(GScanHexModifierClass *klass) modifier->get_name = (get_scan_modifier_name_fc)g_scan_hex_modifier_get_name; modifier->transform = (transform_scan_token_fc)g_scan_hex_modifier_transform; + modifier->get_path = (get_modifier_path)g_scan_hex_modifier_get_path; } @@ -259,3 +263,34 @@ static bool g_scan_hex_modifier_transform(const GScanHexModifier *modifier, cons return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_hex_modifier_get_path(const GScanHexModifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + + if (*index > 0) + { + result = NULL; + (*index)--; + } + + else + result = strdup("hex"); + + return result; + +} diff --git a/src/analysis/scan/patterns/modifiers/list.c b/src/analysis/scan/patterns/modifiers/list.c index 0390f47..040d2f8 100644 --- a/src/analysis/scan/patterns/modifiers/list.c +++ b/src/analysis/scan/patterns/modifiers/list.c @@ -58,6 +58,9 @@ static char *g_scan_modifier_list_get_name(const GScanModifierList *); /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_modifier_list_transform(const GScanModifierList *, const sized_binary_t *, size_t, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_modifier_list_get_path(const GScanModifierList *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -96,6 +99,7 @@ static void g_scan_modifier_list_class_init(GScanModifierListClass *klass) modifier->get_name = (get_scan_modifier_name_fc)g_scan_modifier_list_get_name; modifier->transform = (transform_scan_token_fc)g_scan_modifier_list_transform; + modifier->get_path = (get_modifier_path)g_scan_modifier_list_get_path; } @@ -404,3 +408,31 @@ static bool g_scan_modifier_list_transform(const GScanModifierList *modifier, co return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_modifier_list_get_path(const GScanModifierList *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + size_t i; /* Boucle de parcours #1 */ + + result = NULL; + + for (i = 0; i < modifier->count && result == NULL; i++) + result = g_scan_token_modifier_get_path(modifier->modifiers[i], index); + + return result; + +} diff --git a/src/analysis/scan/patterns/modifiers/plain.c b/src/analysis/scan/patterns/modifiers/plain.c index 9ed0d7b..ad09129 100644 --- a/src/analysis/scan/patterns/modifiers/plain.c +++ b/src/analysis/scan/patterns/modifiers/plain.c @@ -58,6 +58,9 @@ static char *g_scan_plain_modifier_get_name(const GScanPlainModifier *); /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_plain_modifier_transform(const GScanPlainModifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_plain_modifier_get_path(const GScanPlainModifier *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -96,6 +99,7 @@ static void g_scan_plain_modifier_class_init(GScanPlainModifierClass *klass) modifier->get_name = (get_scan_modifier_name_fc)g_scan_plain_modifier_get_name; modifier->transform = (transform_scan_token_fc)g_scan_plain_modifier_transform; + modifier->get_path = (get_modifier_path)g_scan_plain_modifier_get_path; } @@ -252,3 +256,34 @@ static bool g_scan_plain_modifier_transform(const GScanPlainModifier *modifier, return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_plain_modifier_get_path(const GScanPlainModifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + + if (*index > 0) + { + result = NULL; + (*index)--; + } + + else + result = strdup("plain"); + + return result; + +} diff --git a/src/analysis/scan/patterns/modifiers/rev.c b/src/analysis/scan/patterns/modifiers/rev.c index 9d74afe..ef4d5fa 100644 --- a/src/analysis/scan/patterns/modifiers/rev.c +++ b/src/analysis/scan/patterns/modifiers/rev.c @@ -58,6 +58,9 @@ static char *g_scan_reverse_modifier_get_name(const GScanReverseModifier *); /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_reverse_modifier_transform(const GScanReverseModifier *, const sized_binary_t *, size_t, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_reverse_modifier_get_path(const GScanReverseModifier *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -96,6 +99,7 @@ static void g_scan_reverse_modifier_class_init(GScanReverseModifierClass *klass) modifier->get_name = (get_scan_modifier_name_fc)g_scan_reverse_modifier_get_name; modifier->transform = (transform_scan_token_fc)g_scan_reverse_modifier_transform; + modifier->get_path = (get_modifier_path)g_scan_reverse_modifier_get_path; } @@ -254,3 +258,34 @@ static bool g_scan_reverse_modifier_transform(const GScanReverseModifier *modifi return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_reverse_modifier_get_path(const GScanReverseModifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + + if (*index > 0) + { + result = NULL; + (*index)--; + } + + else + result = strdup("rev"); + + return result; + +} diff --git a/src/analysis/scan/patterns/modifiers/xor.c b/src/analysis/scan/patterns/modifiers/xor.c index 53d75a5..d932c82 100644 --- a/src/analysis/scan/patterns/modifiers/xor.c +++ b/src/analysis/scan/patterns/modifiers/xor.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -66,6 +67,9 @@ static bool g_scan_xor_modifier_can_handle_arg(const GScanXorModifier *, const m /* Transforme une séquence d'octets pour motif de recherche. */ static bool g_scan_xor_modifier_transform_with_arg(const GScanXorModifier *, const sized_binary_t *, size_t, const modifier_arg_t *, sized_binary_t **, size_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +static char *g_scan_xor_modifier_get_path(const GScanXorModifier *, size_t *); + /* ---------------------------------------------------------------------------------- */ @@ -106,6 +110,7 @@ static void g_scan_xor_modifier_class_init(GScanXorModifierClass *klass) modifier->transform = (transform_scan_token_fc)g_scan_xor_modifier_transform; modifier->can_handle = (can_token_modifier_handle_arg)g_scan_xor_modifier_can_handle_arg; modifier->transform_with = (transform_scan_token_with_fc)g_scan_xor_modifier_transform_with_arg; + modifier->get_path = (get_modifier_path)g_scan_xor_modifier_get_path; } @@ -393,3 +398,41 @@ static bool g_scan_xor_modifier_transform_with_arg(const GScanXorModifier *modif return result; } + + +/****************************************************************************** +* * +* Paramètres : modifier = modificateur à consulter. * +* index = indice de la combinaison ciblée. [OUT] * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_scan_xor_modifier_get_path(const GScanXorModifier *modifier, size_t *index) +{ + char *result; /* Combinaison à retourner */ + int ret; /* Bilan intermédiaire */ + + if (*index > 255) + { + result = NULL; + (*index) -= 256; + } + + else + { + ret = asprintf(&result, "xor(0x%02hhx)", (unsigned char)*index); + + if (ret == -1) + result = strdup("xor(0x?)"); + + } + + return result; + +} diff --git a/src/analysis/scan/patterns/token.c b/src/analysis/scan/patterns/token.c index 8b3a843..030e4a1 100644 --- a/src/analysis/scan/patterns/token.c +++ b/src/analysis/scan/patterns/token.c @@ -30,6 +30,7 @@ #include "token-int.h" +#include "tokens/nodes/plain.h" #include "../../../common/cpp.h" #include "../../../core/logs.h" @@ -342,6 +343,33 @@ void g_bytes_token_check(const GBytesToken *token, GScanContext *context, GBinCo } +/****************************************************************************** +* * +* Paramètres : token = définition de la bribe à consulter. * +* index = indice de la combinaison de modificateurs ciblée. * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison gagnante. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_bytes_token_get_modifier_path(const GBytesToken *token, size_t index) +{ + char *result; /* Combinaison à retourner */ + + if (G_IS_SCAN_TOKEN_NODE_PLAIN(token->root)) + result = g_scan_token_node_plain_get_modifier_path(G_SCAN_TOKEN_NODE_PLAIN(token->root), index); + else + result = NULL; + + return result; + +} + + /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ diff --git a/src/analysis/scan/patterns/token.h b/src/analysis/scan/patterns/token.h index a71e367..2816bf8 100644 --- a/src/analysis/scan/patterns/token.h +++ b/src/analysis/scan/patterns/token.h @@ -64,6 +64,9 @@ bool g_bytes_token_enroll(GBytesToken *, GScanContext *, GEngineBackend *, size_ /* Transforme les correspondances locales en trouvailles. */ void g_bytes_token_check(const GBytesToken *, GScanContext *, GBinContent *, pending_matches_t *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +char *g_bytes_token_get_modifier_path(const GBytesToken *, size_t); + #endif /* _ANALYSIS_SCAN_PATTERNS_TOKEN_H */ diff --git a/src/analysis/scan/patterns/tokens/nodes/plain.c b/src/analysis/scan/patterns/tokens/nodes/plain.c index 5d89cf8..7309574 100644 --- a/src/analysis/scan/patterns/tokens/nodes/plain.c +++ b/src/analysis/scan/patterns/tokens/nodes/plain.c @@ -277,6 +277,35 @@ ScanPlainNodeFlags g_scan_token_node_plain_get_flags(const GScanTokenNodePlain * } +/****************************************************************************** +* * +* Paramètres : node = définition de la bribe à consulter. * +* index = indice de la combinaison de modificateurs ciblée. * +* * +* Description : Retrouve l'origine d'une correspondance à partir d'un indice.* +* * +* Retour : Version humainement lisible de la combinaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_scan_token_node_plain_get_modifier_path(const GScanTokenNodePlain *node, size_t index) +{ + char *result; /* Combinaison à retourner */ + + if (node->modifier == NULL) + result = strdup("plain"); + + else + result = g_scan_token_modifier_get_path(node->modifier, (size_t []){ index }); + + return result; + + +} + + /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ @@ -511,6 +540,7 @@ static bool check_scan_token_node_plain_content(const sized_binary_t *raw, const static void g_scan_token_node_plain_check_forward(const GScanTokenNodePlain *node, GScanContext *context, GBinContent *content, pending_matches_t *matches, node_search_offset_t *offset, bool not, bool *skip) { bool initialized; /* Initialisation du suivi ? */ + bool track_path; /* Conservation du chemin */ bool nocase; /* Pas d'intérêt pour la casse */ size_t ocount; /* Quantité de bornes présentes*/ size_t i; /* Boucle de parcours #1 */ @@ -531,6 +561,10 @@ static void g_scan_token_node_plain_check_forward(const GScanTokenNodePlain *nod initialized = are_pending_matches_initialized(matches); + track_path = (G_SCAN_TOKEN_NODE(node)->flags & STNF_MAIN); + + assert((track_path && !initialized) || !track_path); + nocase = (node->flags & SPNF_CASE_INSENSITIVE); get_node_search_offset_ranges(offset, &ocount); @@ -568,12 +602,18 @@ static void g_scan_token_node_plain_check_forward(const GScanTokenNodePlain *nod status = check_scan_token_node_plain_content(raw, atom, nocase, new_begin, content); if ((status && !not) || (!status && not)) + { /** * Il ne peut y avoir qu'une seule séquence d'octets à un même * emplacement, donc le couple (new_begin, len) enregistré est * unique. */ - add_pending_match(matches, new_begin, raw->len); + if (track_path) + add_pending_match_with_path(matches, new_begin, raw->len, i); + else + add_pending_match(matches, new_begin, raw->len); + + } } diff --git a/src/analysis/scan/patterns/tokens/nodes/plain.h b/src/analysis/scan/patterns/tokens/nodes/plain.h index c8f3920..abf71de 100644 --- a/src/analysis/scan/patterns/tokens/nodes/plain.h +++ b/src/analysis/scan/patterns/tokens/nodes/plain.h @@ -75,6 +75,9 @@ GScanTokenNode *g_scan_token_node_plain_new(const sized_binary_t *, GScanTokenMo /* Indique les propriétés particulières d'un noeud de texte. */ ScanPlainNodeFlags g_scan_token_node_plain_get_flags(const GScanTokenNodePlain *); +/* Retrouve l'origine d'une correspondance à partir d'un indice. */ +char *g_scan_token_node_plain_get_modifier_path(const GScanTokenNodePlain *, size_t); + #endif /* _ANALYSIS_SCAN_PATTERNS_TOKENS_NODES_PLAIN_H */ diff --git a/src/analysis/scan/rule.c b/src/analysis/scan/rule.c index 77a05b4..5826db9 100644 --- a/src/analysis/scan/rule.c +++ b/src/analysis/scan/rule.c @@ -623,6 +623,9 @@ void g_scan_rule_check(GScanRule *rule, GEngineBackend *backend, GScanContext *c match = g_scan_bytes_match_new(G_SEARCH_PATTERN(pattern), content, area->start, area->end - area->start); + if (area->has_mod_path) + g_scan_bytes_match_remember_modifier_path(G_SCAN_BYTES_MATCH(match), area->mod_path_index); + g_scan_context_register_full_match(context, match); g_object_unref(G_OBJECT(match)); diff --git a/tests/analysis/scan/functions.py b/tests/analysis/scan/functions.py index e936263..983b8da 100644 --- a/tests/analysis/scan/functions.py +++ b/tests/analysis/scan/functions.py @@ -33,6 +33,32 @@ rule test { self.check_rule_success(rule, cnt) + def testMaxCommon(self): + """Count the largest quantity of same items in a set.""" + + cnt = MemoryContent(b'') + + cases = [ + [ '1', 1 ], + [ '1, 2, 3', 1 ], + [ '1, 2, 1, 3, 1', 3 ], + [ '1, "a", 2, 3, "a"', 2 ], + ] + + for c, q in cases: + + rule = ''' +rule test { + + condition: + maxcommon(%s) == %u + +} +''' % (c, q) + + self.check_rule_success(rule, cnt) + + # Modules # ======= -- cgit v0.11.2-87-g4458