From 5c31b8362235b7423dced633866a2d4507c63251 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Tue, 19 Sep 2023 01:26:47 +0200 Subject: Introduce a new tool to convert rules: yara2rost. --- .gitignore | 1 + configure.ac | 1 + tools/Makefile.am | 2 +- tools/d2c/grammar.y | 4 - tools/yara2rost/Makefile.am | 36 ++ tools/yara2rost/decl.h | 37 ++ tools/yara2rost/demo.yar | 27 + tools/yara2rost/enums.h | 47 ++ tools/yara2rost/grammar.y | 1332 +++++++++++++++++++++++++++++++++++++++++++ tools/yara2rost/tokens.l | 292 ++++++++++ tools/yara2rost/yara2rost.c | 295 ++++++++++ 11 files changed, 2069 insertions(+), 5 deletions(-) create mode 100644 tools/yara2rost/Makefile.am create mode 100644 tools/yara2rost/decl.h create mode 100644 tools/yara2rost/demo.yar create mode 100644 tools/yara2rost/enums.h create mode 100644 tools/yara2rost/grammar.y create mode 100644 tools/yara2rost/tokens.l create mode 100644 tools/yara2rost/yara2rost.c diff --git a/.gitignore b/.gitignore index dd160e3..430885b 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ src/chrysalide src/chrysalide-hub src/rost tools/d2c/d2c +tools/yara2rost/yara2rost # Misc plugins/python/androperms/androperms.db diff --git a/configure.ac b/configure.ac index a2c00f0..9de9abd 100644 --- a/configure.ac +++ b/configure.ac @@ -778,6 +778,7 @@ AC_CONFIG_FILES([Makefile tools/d2c/id/Makefile tools/d2c/pattern/Makefile tools/d2c/rules/Makefile + tools/yara2rost/Makefile system/Makefile system/desktop/Makefile system/pkgconfig/Makefile diff --git a/tools/Makefile.am b/tools/Makefile.am index ed53403..8b8f38b 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS = d2c +SUBDIRS = d2c yara2rost diff --git a/tools/d2c/grammar.y b/tools/d2c/grammar.y index 4444299..14959cf 100644 --- a/tools/d2c/grammar.y +++ b/tools/d2c/grammar.y @@ -1,7 +1,6 @@ %{ -#include ////// #include #include #include @@ -15,9 +14,6 @@ /* Affiche un message d'erreur suite à l'analyse en échec. */ static int yyerror(rented_coder *, char *, char *); -/* Affiche des indications sur l'utilisation du programme. */ -static void show_usage(const char *); - /* Prépare le traitement d'un contenu en l'affichant en mémoire. */ static void *map_input_data(const char *, size_t *); diff --git a/tools/yara2rost/Makefile.am b/tools/yara2rost/Makefile.am new file mode 100644 index 0000000..2830b03 --- /dev/null +++ b/tools/yara2rost/Makefile.am @@ -0,0 +1,36 @@ + +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 yara2rost_ -Wno-yacc #-Wcounterexamples + +AM_LFLAGS = -P yara2rost_ -o lex.yy.c --header-file=tokens.h \ + -Dyyget_lineno=yara2rost_get_lineno \ + -Dyy_scan_bytes=yara2rost__scan_bytes \ + -Dyy_delete_buffer=yara2rost__delete_buffer + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) + + +bin_PROGRAMS = yara2rost + +.NOTPARALLEL: $(bin_PROGRAMS) + +yara2rost_SOURCES = \ + decl.h \ + enums.h \ + tokens.l \ + grammar.y \ + yara2rost.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 ! +# On rajoute également de quoi générer les Makefiles. +EXTRA_DIST = tokens.h diff --git a/tools/yara2rost/decl.h b/tools/yara2rost/decl.h new file mode 100644 index 0000000..05d63d4 --- /dev/null +++ b/tools/yara2rost/decl.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * decl.h - déclarations de prototypes utiles + * + * 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 _TOOLS_YARA2ROST_DECL_H +#define _TOOLS_YARA2ROST_DECL_H + + +#include + + + +/* Parcourt des définitions de règles pour traduction. */ +bool process_rules_definitions(const char *, size_t); + + + +#endif /* _TOOLS_YARA2ROST_DECL_H */ diff --git a/tools/yara2rost/demo.yar b/tools/yara2rost/demo.yar new file mode 100644 index 0000000..081973f --- /dev/null +++ b/tools/yara2rost/demo.yar @@ -0,0 +1,27 @@ + +include "demobis.yar" + +import "modname" + + +private global rule Test : tag1 tag2 { + + meta: + desc_0 = "abc" + desc_1 = 123 + desc_2 = true + desc_3 = false + desc_z = "" + + strings: + $text = "value" + $text_b = "value" wide ascii fullword private xor(0x12) + $re = /hash: [0-9a-fA-F]{32}/ + $re_b = /hash: [0-9a-fA-F]{32}/ wide ascii nocase fullword private + $hex = { AA bb [2-4] 61 62 63 } + $hex_b = { AA bb [2-4] 61 62 63 } private + + condition: + filesize == 123 and entrypoint == 456 and for all of ($text*) : ( @ > @hex_b ) and any of them + +} diff --git a/tools/yara2rost/enums.h b/tools/yara2rost/enums.h new file mode 100644 index 0000000..19fe49c --- /dev/null +++ b/tools/yara2rost/enums.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * enums.h - Reprise des fanions de la syntaxe YARA + * + * 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 _TOOLS_YARA2ROST_ENUMS_H +#define _TOOLS_YARA2ROST_ENUMS_H + + +typedef enum _RuleFlags +{ + RULE_FLAGS_NONE = (0 << 0), + RULE_FLAGS_PRIVATE = (1 << 0), + RULE_FLAGS_GLOBAL = (1 << 1) + +} RuleFlags; + +typedef enum _StringExtraFlags +{ + STRING_FLAGS_NONE = (0 << 0), + STRING_FLAGS_NO_CASE = (1 << 0), + STRING_FLAGS_FULL_WORD = (1 << 1), + STRING_FLAGS_PRIVATE = (1 << 2) + +} StringExtraFlags; + + + +#endif /* _TOOLS_YARA2ROST_ENUMS_H */ diff --git a/tools/yara2rost/grammar.y b/tools/yara2rost/grammar.y new file mode 100644 index 0000000..0d756b1 --- /dev/null +++ b/tools/yara2rost/grammar.y @@ -0,0 +1,1332 @@ + +%{ + +#include +#include +#include +#include +#include +#include +#include + + +#include "decl.h" +#include "tokens.h" + + + +/* Affiche un message d'erreur suite à l'analyse en échec. */ +static int yyerror(yyscan_t, const char *); + +/* Initialise une amorce de copie. */ +static void init_dump(sz_str_t *, const sz_cst_str_t *); + +#define init_dump_with_fixed(d, s) \ + init_dump(d, (sz_cst_str_t []) { { .data = s, .len = sizeof(s) - 1 } }) + +/* Complète une chaîne de caractères avec une autre. */ +static void add_to_dump(sz_str_t *, const sz_cst_str_t *); + +#define add_fixed_to_dump(d, s) \ + add_to_dump(d, (sz_cst_str_t []) { { .data = s, .len = sizeof(s) - 1 } }) + +#define add_dyn_to_dump(d, s) \ + do \ + { \ + add_to_dump(d, (sz_cst_str_t *)s); \ + free((s)->data); \ + } \ + while (0) + +/* Imprime une bribe de définition formant une règle ROST. */ +void dump_string(const char *, size_t); + +#define dump_fixed_string(s) \ + dump_string(s, sizeof(s) - 1) + + +%} + + +%code requires { + +#include +#include + +#include "enums.h" + +#define YY_TYPEDEF_YY_SCANNER_T +typedef void *yyscan_t; + + +typedef struct _sz_str_t +{ + char *data; + size_t len; + +} sz_str_t; + +typedef struct _sz_cst_str_t +{ + char *data; + size_t len; + +} sz_cst_str_t; + +} + +%union { + + sz_str_t string; /* Chaîne de caractères #1 */ + sz_cst_str_t cstring; /* Chaîne de caractères #2 */ + + RuleFlags rule_flags; /* Fanions pour règle */ + StringExtraFlags string_flags; /* Fanions pour motif */ + +} + + +%expect 1 + +%define api.pure full +%define parse.error verbose + +%parse-param { yyscan_t yyscanner } +%lex-param { yyscan_t yyscanner } + + +%code provides { + +#define YY_DECL \ + int yara2rost_lex(YYSTYPE *yylval_param, yyscan_t yyscanner) + +YY_DECL; + +} + +%token COLON ":" +%token CURLY_BRACKET_O "{" +%token CURLY_BRACKET_C "}" +%token EQUAL "=" +%token PAREN_O "(" +%token PAREN_C ")" +%token DOT_DOT ".." +%token COMMA "," +%token BRACKET_O "[" +%token BRACKET_C "]" +%token PERCENT "%" +%token DOT "." + +%token ADD_OP "+" +%token SUB_OP "-" +%token MUL_OP "*" +%token DIV_OP "\\" +%token EOR_OP "^" +%token AND_OP "&" +%token OR_OP "|" +%token INV_OP "~" +%token SHIFT_LEFT_OP "<<" +%token SHIFT_RIGHT_OP ">>" + +%token LT "<" +%token GT ">" +%token LE "<=" +%token GE ">=" +%token EQ "==" +%token NEQ "!=" + +%token ALL "all" +%token AND "and" +%token ANY "any" +%token ASCII "ascii" +%token AT "at" +%token BASE64 "base64" +%token BASE64WIDE "base64wide" +%token CONDITION "condition" +%token CONTAINS "contains" +%token DEFINED "defined" +%token ENDSWITH "endswith" +%token ENTRYPOINT "entrypoint" +%token FILESIZE "filesize" +%token FOR "for" +%token FULLWORD "fullword" +%token GLOBAL "global" +%token ICONTAINS "icontains" +%token IENDSWITH "iendswith" +%token IEQUALS "iequals" +%token IMPORT "import" +%token IN "in" +%token INCLUDE "include" +%token ISTARTSWITH "istartswith" +%token MATCHES "matches" +%token META "meta" +%token NOCASE "nocase" +%token NONE "none" +%token NOT "not" +%token OF "of" +%token OR "or" +%token PRIVATE "private" +%token RULE "rule" +%token STARTSWITH "startswith" +%token STRINGS "strings" +%token THEM "them" +%token WIDE "wide" +%token XOR "xor" + +%token _FALSE "false" +%token _TRUE "true" + +%token STRING_IDENTIFIER_WITH_WILDCARD +%token STRING_IDENTIFIER +%token STRING_COUNT +%token STRING_OFFSET +%token STRING_LENGTH +%token INTEGER_FUNCTION +%token IDENTIFIER +%token NUMBER +%token DOUBLE +%token TEXT_STRING +%token REGEXP +%token HEX_STRING + +%type STRING_IDENTIFIER_WITH_WILDCARD +%type STRING_IDENTIFIER +%type STRING_COUNT +%type STRING_OFFSET +%type STRING_LENGTH +%type INTEGER_FUNCTION +%type IDENTIFIER +%type NUMBER +%type DOUBLE +%type TEXT_STRING +%type REGEXP +%type HEX_STRING + +%type rule_modifiers +%type rule_modifier + +%type string_modifiers +%type string_modifier +%type regexp_modifiers +%type regexp_modifier +%type hex_modifiers +%type hex_modifier + +%type boolean_expression +%type identifier +%type arguments +%type arguments_list +%type expression +%type for_iteration +%type for_variables +%type iterator +%type set +%type range +%type enumeration +%type string_iterator +%type string_set +%type string_enumeration +%type string_enumeration_item +%type rule_set +%type rule_enumeration +%type rule_enumeration_item +%type for_expression +%type for_quantifier +%type primary_expression +%type regexp + +%left OR +%left AND +%right NOT DEFINED +%left EQ NEQ CONTAINS ICONTAINS STARTSWITH ENDSWITH ISTARTSWITH IENDSWITH IEQUALS MATCHES +%left LT LE GT GE +%left OR_OP +%left EOR_OP +%left AND_OP +%left SHIFT_LEFT_OP SHIFT_RIGHT_OP +%left ADD_OP SUB_OP +%left MUL_OP DIV_OP PERCENT +%right INV_OP UNARY_MINUS + + +%% + + rules : /* empty */ + | rules include + | rules import + | rules rule + ; + + + include : "include" TEXT_STRING + { + dump_fixed_string("include "); + dump_string($2.data, $2.len); + dump_fixed_string("\n"); + } + ; + + import : "import" TEXT_STRING + { + dump_fixed_string("/* import "); + dump_string($2.data, $2.len); + dump_fixed_string(" */\n"); + } + ; + + + rule : rule_modifiers "rule" IDENTIFIER + { + if ($1 != RULE_FLAGS_NONE) + { + if ($1 & RULE_FLAGS_PRIVATE) + { + dump_fixed_string("private"); + dump_fixed_string(" "); + } + + if ($1 & RULE_FLAGS_GLOBAL) + { + dump_fixed_string("global"); + dump_fixed_string(" "); + } + + } + + dump_fixed_string("rule "); + dump_string($3.data, $3.len); + + } + tags "{" + { + dump_fixed_string(" {\n"); + } + meta strings condition "}" + { + dump_fixed_string("}\n"); + } + ; + + + rule_modifiers : /* empty */ + { + $$ = RULE_FLAGS_NONE; + } + | rule_modifiers rule_modifier + { + $$ = $1 | $2; + } + ; + + rule_modifier : "private" + { + $$ = RULE_FLAGS_PRIVATE; + } + | "global" + { + $$ = RULE_FLAGS_GLOBAL; + } + ; + + + tags : /* empty */ + | ":" + { + dump_fixed_string(" :"); + } + tag_list + ; + + tag_list : IDENTIFIER + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + } + | tag_list IDENTIFIER + { + dump_fixed_string(" "); + dump_string($2.data, $2.len); + } + ; + + +/** + * Section "meta:" + */ + + meta : /* empty */ + | "meta" ":" + { + dump_fixed_string("\n "); + dump_fixed_string("meta:\n"); + } + meta_declarations + ; + + meta_declarations : meta_declaration + { + dump_fixed_string("\n"); + } + | meta_declarations meta_declaration + { + dump_fixed_string("\n"); + } + ; + + meta_declaration : IDENTIFIER "=" TEXT_STRING + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = "); + dump_string($3.data, $3.len); + } + | IDENTIFIER "=" NUMBER + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = "); + dump_string($3.data, $3.len); + } + | IDENTIFIER "=" "-" NUMBER + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = -"); + dump_string($4.data, $4.len); + } + | IDENTIFIER "=" "true" + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = true"); + } + | IDENTIFIER "=" "false" + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = false"); + } + ; + + +/** + * Section "strings:" + */ + + strings : /* empty */ + | "strings" ":" + { + dump_fixed_string("\n "); + dump_fixed_string("bytes:\n"); + } + string_declarations + ; + + string_declarations : string_declaration + { + dump_fixed_string("\n"); + } + | string_declarations string_declaration + { + dump_fixed_string("\n"); + } + ; + + string_declaration : STRING_IDENTIFIER "=" + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = "); + } + TEXT_STRING + { + dump_string($4.data, $4.len); + } + string_modifiers + { + if ($6 & STRING_FLAGS_NO_CASE) + dump_fixed_string(" nocase"); + + if ($6 & STRING_FLAGS_FULL_WORD) + dump_fixed_string(" fullword"); + + if ($6 & STRING_FLAGS_PRIVATE) + dump_fixed_string(" private"); + + } + | STRING_IDENTIFIER "=" + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = "); + } + REGEXP + { + dump_fixed_string("/"); + dump_string($4.data, $4.len); + } + regexp_modifiers + { + if ($6 & STRING_FLAGS_NO_CASE) + dump_fixed_string(" nocase"); + + if ($6 & STRING_FLAGS_FULL_WORD) + dump_fixed_string(" fullword"); + + if ($6 & STRING_FLAGS_PRIVATE) + dump_fixed_string(" private"); + + } + | STRING_IDENTIFIER "=" + { + dump_fixed_string(" "); + dump_string($1.data, $1.len); + dump_fixed_string(" = "); + } + HEX_STRING + { + dump_string($4.data, $4.len); + } + hex_modifiers + { + if ($6 & STRING_FLAGS_NO_CASE) + dump_fixed_string(" nocase"); + + if ($6 & STRING_FLAGS_FULL_WORD) + dump_fixed_string(" fullword"); + + if ($6 & STRING_FLAGS_PRIVATE) + dump_fixed_string(" private"); + + } + ; + + + string_modifiers : /* empty */ + { + $$ = STRING_FLAGS_NONE; + } + | string_modifiers string_modifier + { + $$ = $1 | $2; + } + ; + + string_modifier : "wide" + { + dump_fixed_string(" wide"); + $$ = STRING_FLAGS_NONE; + } + | "ascii" + { + dump_fixed_string(" plain"); + $$ = STRING_FLAGS_NONE; + } + | "nocase" + { + $$ = STRING_FLAGS_NO_CASE; + } + | "fullword" + { + $$ = STRING_FLAGS_FULL_WORD; + } + | "private" + { + $$ = STRING_FLAGS_PRIVATE; + } + | "xor" + { + dump_fixed_string(" xor"); + $$ = STRING_FLAGS_NONE; + } + | "xor" "(" NUMBER ")" + { + dump_fixed_string(" xor("); + dump_string($3.data, $3.len); + dump_fixed_string(")"); + $$ = STRING_FLAGS_NONE; + } + | "xor" "(" NUMBER "-" NUMBER ")" + { + dump_fixed_string(" xor("); + dump_string($3.data, $3.len); + dump_fixed_string("-"); + dump_string($5.data, $5.len); + dump_fixed_string(")"); + $$ = STRING_FLAGS_NONE; + } + | "base64" + { + dump_fixed_string(" base64"); + $$ = STRING_FLAGS_NONE; + } + | "base64" "(" TEXT_STRING ")" + { + dump_fixed_string(" base64("); + dump_string($3.data, $3.len); + dump_fixed_string(")"); + $$ = STRING_FLAGS_NONE; + } + | "base64wide" + { + dump_fixed_string(" (base64 | wide)"); + $$ = STRING_FLAGS_NONE; + } + | "base64wide" "(" TEXT_STRING ")" + { + dump_fixed_string(" (base64("); + dump_string($3.data, $3.len); + dump_fixed_string(") | wide)"); + $$ = STRING_FLAGS_NONE; + } + ; + + regexp_modifiers : /* empty */ + { + $$ = STRING_FLAGS_NONE; + } + | regexp_modifiers regexp_modifier + { + $$ = $1 | $2; + } + ; + + regexp_modifier : "wide" + { + dump_fixed_string(" wide"); + $$ = STRING_FLAGS_NONE; + } + | "ascii" + { + dump_fixed_string(" plain"); + $$ = STRING_FLAGS_NONE; + } + | "nocase" + { + $$ = STRING_FLAGS_NO_CASE; + } + | "fullword" + { + $$ = STRING_FLAGS_FULL_WORD; + } + | "private" + { + $$ = STRING_FLAGS_PRIVATE; + } + ; + + hex_modifiers : /* empty */ + { + $$ = STRING_FLAGS_NONE; + } + | hex_modifiers hex_modifier + { + $$ = $1 | $2; + } + ; + + hex_modifier : "private" + { + $$ = STRING_FLAGS_PRIVATE; + } + ; + + +/** + * Section "condition:" + */ + + condition : "condition" ":" boolean_expression + { + dump_fixed_string("\n "); + dump_fixed_string("condition:\n"); + dump_fixed_string(" "); + dump_string($3.data, $3.len); + free($3.data); + dump_fixed_string("\n\n"); + } + ; + + boolean_expression : expression { $$ = $1; } + ; + + identifier : IDENTIFIER + { + init_dump(&$$, &$1); + } + | identifier "." IDENTIFIER + { + $$ = $1; + add_fixed_to_dump(&$$, "."); + add_to_dump(&$$, &$3); + } + | identifier "[" primary_expression "]" + { + $$ = $1; + add_fixed_to_dump(&$$, "["); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, "]"); + } + | identifier "(" arguments ")" + { + $$ = $1; + add_fixed_to_dump(&$$, "("); + if ($3.len > 0) + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, ")"); + } + ; + + + arguments : { $$.len = 0; /* empty */ } + | arguments_list { $$ = $1; } + ; + + + arguments_list : expression + { + $$ = $1; + } + | arguments_list "," expression + { + $$ = $1; + add_fixed_to_dump(&$$, ", "); + add_dyn_to_dump(&$$, &$3); + } + ; + + + expression : "true" + { + init_dump_with_fixed(&$$, "true"); + } + | "false" + { + init_dump_with_fixed(&$$, "false"); + } + | primary_expression "matches" regexp + { + $$ = $1; + add_fixed_to_dump(&$$, " matches "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "contains" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " contains "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "icontains" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " icontains "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "startswith" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " startswith "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "istartswith" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " istartswith "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "endswith" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " endswith "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "iendswith" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " iendswith "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "iequals" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " iequals "); + add_dyn_to_dump(&$$, &$3); + } + | STRING_IDENTIFIER + { + init_dump(&$$, &$1); + } + | STRING_IDENTIFIER "at" primary_expression + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, " at "); + add_dyn_to_dump(&$$, &$3); + } + | STRING_IDENTIFIER "in" range + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, " in "); + add_dyn_to_dump(&$$, &$3); + } + | "for" for_expression for_iteration ":" "(" boolean_expression ")" + { + init_dump_with_fixed(&$$, "for "); + add_dyn_to_dump(&$$, &$2); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, " : ("); + add_dyn_to_dump(&$$, &$6); + add_fixed_to_dump(&$$, ")"); + } + | for_expression "of" string_set + { + $$ = $1; + add_fixed_to_dump(&$$, " of "); + add_dyn_to_dump(&$$, &$3); + } + | for_expression "of" rule_set + { + $$ = $1; + add_fixed_to_dump(&$$, " of "); + add_dyn_to_dump(&$$, &$3); + } + + | primary_expression "%" "of" string_set + { + $$ = $1; + add_fixed_to_dump(&$$, "% of "); + add_dyn_to_dump(&$$, &$4); + } + | primary_expression "%" "of" rule_set + { + $$ = $1; + add_fixed_to_dump(&$$, "% of "); + add_dyn_to_dump(&$$, &$4); + } + + | for_expression "of" string_set "in" range + { + $$ = $1; + add_fixed_to_dump(&$$, " of "); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, " in "); + add_dyn_to_dump(&$$, &$5); + } + | for_expression "of" string_set "at" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " of "); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, " at "); + add_dyn_to_dump(&$$, &$5); + } + | "not" boolean_expression + { + init_dump_with_fixed(&$$, "not "); + add_dyn_to_dump(&$$, &$2); + } + | "defined" boolean_expression + { + init_dump_with_fixed(&$$, "defined "); + add_dyn_to_dump(&$$, &$2); + } + | boolean_expression "and" boolean_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " and "); + add_dyn_to_dump(&$$, &$3); + } + | boolean_expression "or" boolean_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " or "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "<" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " < "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression ">" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " > "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "<=" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " <= "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression ">=" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " >= "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "==" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " == "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "!=" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " != "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression + { + $$ = $1; + } + | "(" expression ")" + { + init_dump_with_fixed(&$$, "("); + add_dyn_to_dump(&$$, &$2); + add_fixed_to_dump(&$$, ")"); + } + ; + + + for_iteration : for_variables "in" iterator + { + $$ = $1; + add_fixed_to_dump(&$$, " in "); + add_dyn_to_dump(&$$, &$3); + } + | "of" string_iterator + { + init_dump_with_fixed(&$$, "of "); + add_dyn_to_dump(&$$, &$2); + } + ; + + for_variables : IDENTIFIER + { + init_dump(&$$, &$1); + } + | for_variables "," IDENTIFIER + { + $$ = $1; + add_fixed_to_dump(&$$, ", "); + add_to_dump(&$$, &$3); + } + ; + + + iterator : identifier { $$ = $1; } + | set { $$ = $1; } + ; + + + set : "(" enumeration ")" + { + init_dump_with_fixed(&$$, "("); + add_dyn_to_dump(&$$, &$2); + add_fixed_to_dump(&$$, ")"); + } + | range { $$ = $1; } + ; + + + range : "(" primary_expression ".." primary_expression ")" + { + init_dump_with_fixed(&$$, "("); + add_dyn_to_dump(&$$, &$2); + add_fixed_to_dump(&$$, " .. "); + add_dyn_to_dump(&$$, &$4); + add_fixed_to_dump(&$$, ")"); + } + ; + + + enumeration : primary_expression { $$ = $1; } + | enumeration "," primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, ", "); + add_dyn_to_dump(&$$, &$3); + } + ; + + + string_iterator : string_set { $$ = $1; } + ; + + string_set : "(" string_enumeration ")" + { + init_dump_with_fixed(&$$, "("); + add_dyn_to_dump(&$$, &$2); + add_fixed_to_dump(&$$, ")"); + } + | "them" + { + init_dump_with_fixed(&$$, "them"); + } + ; + + string_enumeration : string_enumeration_item + { + $$ = $1; + } + | string_enumeration "," string_enumeration_item + { + $$ = $1; + add_fixed_to_dump(&$$, ", "); + add_dyn_to_dump(&$$, &$3); + } + ; + +string_enumeration_item : STRING_IDENTIFIER + { + init_dump(&$$, &$1); + } + | STRING_IDENTIFIER_WITH_WILDCARD + { + init_dump(&$$, &$1); + } + ; + + + rule_set : "(" rule_enumeration ")" + { + init_dump_with_fixed(&$$, "("); + add_dyn_to_dump(&$$, &$2); + add_fixed_to_dump(&$$, ")"); + } + ; + + rule_enumeration : rule_enumeration_item + { + $$ = $1; + } + | rule_enumeration "," rule_enumeration_item + { + $$ = $1; + add_fixed_to_dump(&$$, ", "); + add_dyn_to_dump(&$$, &$3); + } + ; + + rule_enumeration_item : IDENTIFIER + { + init_dump(&$$, &$1); + } + | IDENTIFIER "*" + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, "*"); + } + ; + + + for_expression : primary_expression { $$ = $1; } + | for_quantifier { $$ = $1; } + ; + + for_quantifier : "all" + { + init_dump_with_fixed(&$$, "all"); + } + | "any" + { + init_dump_with_fixed(&$$, "any"); + } + | "none" + { + init_dump_with_fixed(&$$, "none"); + } + ; + + + primary_expression : "(" primary_expression ")" + { + init_dump_with_fixed(&$$, "("); + add_dyn_to_dump(&$$, &$2); + add_fixed_to_dump(&$$, ")"); + } + | "filesize" + { + init_dump_with_fixed(&$$, "datasize"); + } + | "entrypoint" + { + init_dump_with_fixed(&$$, "/* entrypoint */ 0"); + } + | INTEGER_FUNCTION "(" primary_expression ")" + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, "("); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, ")"); + } + | NUMBER + { + init_dump(&$$, &$1); + } + | DOUBLE + { + init_dump(&$$, &$1); + } + | TEXT_STRING + { + init_dump(&$$, &$1); + } + | STRING_COUNT "in" range + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, " in "); + add_dyn_to_dump(&$$, &$3); + } + | STRING_COUNT + { + init_dump(&$$, &$1); + } + | STRING_OFFSET "[" primary_expression "]" + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, "["); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, "]"); + } + | STRING_OFFSET + { + init_dump(&$$, &$1); + } + | STRING_LENGTH "[" primary_expression "]" + { + init_dump(&$$, &$1); + add_fixed_to_dump(&$$, "["); + add_dyn_to_dump(&$$, &$3); + add_fixed_to_dump(&$$, "]"); + } + | STRING_LENGTH + { + init_dump(&$$, &$1); + } + | identifier + { + $$ = $1; + } + | "-" primary_expression %prec UNARY_MINUS + { + init_dump_with_fixed(&$$, "-"); + add_dyn_to_dump(&$$, &$2); + } + | primary_expression "+" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " + "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "-" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " - "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "*" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " * "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "\\" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " \\ "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "%" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " % "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "^" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " ^ "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "&" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " & "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression "|" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " | "); + add_dyn_to_dump(&$$, &$3); + } + | "~" primary_expression + { + init_dump_with_fixed(&$$, "~"); + add_dyn_to_dump(&$$, &$2); + } + | primary_expression "<<" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " << "); + add_dyn_to_dump(&$$, &$3); + } + | primary_expression ">>" primary_expression + { + $$ = $1; + add_fixed_to_dump(&$$, " >> "); + add_dyn_to_dump(&$$, &$3); + } + | regexp + ; + + + regexp : REGEXP + { + init_dump_with_fixed(&$$, "/"); + add_to_dump(&$$, &$1); + } + ; + + +%% + + +/****************************************************************************** +* * +* Paramètres : yyscanner = décodeur impliqué dans le processus. * +* 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 char *msg) +{ + printf("YYERROR line %d: %s\n", yyget_lineno(yyscanner), msg); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : dst = chaîne de caractères à créer. * +* src = chaîne de caractères à ajouter. * +* * +* Description : Initialise une amorce de copie. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void init_dump(sz_str_t *dst, const sz_cst_str_t *src) +{ + dst->data = malloc((src->len + 1) * sizeof(char)); + dst->len = src->len; + + memcpy(dst->data, src->data, src->len); + + dst->data[dst->len] = '\0'; + +} + + +/****************************************************************************** +* * +* Paramètres : dst = chaîne de caractères à créer. * +* src = chaîne de caractères à ajouter. * +* * +* Description : Complète une chaîne de caractères avec une autre. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void add_to_dump(sz_str_t *dst, const sz_cst_str_t *src) +{ + dst->data = realloc(dst->data, (dst->len + src->len + 1) * sizeof(char)); + + memcpy(&dst->data[dst->len], src->data, src->len); + + dst->len += src->len; + + dst->data[dst->len] = '\0'; + +} + + +/****************************************************************************** +* * +* Paramètres : string = texte à copier sur la sortie standard. * +* length = longueur de ce texte. * +* * +* Description : Imprime une bribe de définition formant une règle ROST. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void dump_string(const char *string, size_t length) +{ + ssize_t ret; /* Bilan de l'appel */ + + ret = write(STDOUT_FILENO, string, length); + + if (ret != length) + perror("write"); + +} + + +/****************************************************************************** +* * +* Paramètres : text = définitions des règles à charger. * +* length = longueur de ces définitions. * +* * +* Description : Parcourt des définitions de règles pour traduction. * +* * +* Retour : Bilan à retourner. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool process_rules_definitions(const char *text, size_t length) +{ + bool result; /* Bilan à renvoyer */ + yyscan_t lexstate; /* Gestion d'analyse lexicale */ + YY_BUFFER_STATE state; /* Contexte d'analyse */ + int status; /* Bilan d'une analyse */ + + result = false; + + yara2rost_lex_init(&lexstate); + + state = yara2rost__scan_bytes(text, length, lexstate); + + status = yyparse(lexstate); + + result = (status == EXIT_SUCCESS); + + yy_delete_buffer(state, lexstate); + + yara2rost_lex_destroy(lexstate); + + return result; + +} diff --git a/tools/yara2rost/tokens.l b/tools/yara2rost/tokens.l new file mode 100644 index 0000000..34e61d0 --- /dev/null +++ b/tools/yara2rost/tokens.l @@ -0,0 +1,292 @@ + +%top { + +#include "grammar.h" + +} + + +%{ + +#include "decl.h" + +#include +#include +#include + + +#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 regexp +%x comment + + +str_not_escaped [^\"\\] +str_escaped \\a|\\b|\\t|\\n|\\v|\\f|\\r|\\e|\\\"|\\\\|\\x{hbyte} +str_mixed ({str_not_escaped}|{str_escaped}) + +hbyte [0-9a-fA-F]{2} + +digit [0-9] +letter [a-zA-Z] +hexdigit [a-fA-F0-9] +octdigit [0-7] + + +%% + + +":" { return COLON; } +"{" { return CURLY_BRACKET_O; } +"}" { return CURLY_BRACKET_C; } +"=" { return EQUAL; } +"(" { return PAREN_O; } +")" { return PAREN_C; } +".." { return DOT_DOT; } +"," { return COMMA; } +"[" { return BRACKET_O; } +"]" { return BRACKET_C; } +"%" { return PERCENT; } +"." { return DOT; } + +"+" { return ADD_OP; } +"-" { return SUB_OP; } +"*" { return MUL_OP; } +"\\" { return DIV_OP; } +"^" { return EOR_OP; } +"&" { return AND_OP; } +"|" { return OR_OP; } +"~" { return INV_OP; } +"<<" { return SHIFT_LEFT_OP; } +">>" { return SHIFT_RIGHT_OP; } + +"<" { return LT; } +">" { return GT; } +"<=" { return LE; } +">=" { return GE; } +"==" { return EQ; } +"!=" { return NEQ; } + +"all" { return ALL; } +"and" { return AND; } +"any" { return ANY; } +"ascii" { return ASCII; } +"at" { return AT; } +"base64" { return BASE64; } +"base64wide" { return BASE64WIDE; } +"condition" { return CONDITION; } +"contains" { return CONTAINS; } +"defined" { return DEFINED; } +"endswith" { return ENDSWITH; } +"entrypoint" { return ENTRYPOINT; } +"filesize" { return FILESIZE; } +"for" { return FOR; } +"fullword" { return FULLWORD; } +"global" { return GLOBAL; } +"icontains" { return ICONTAINS; } +"iendswith" { return IENDSWITH; } +"iequals" { return IEQUALS; } +"import" { return IMPORT; } +"in" { return IN; } +"include" { return INCLUDE; } +"istartswith" { return ISTARTSWITH; } +"matches" { return MATCHES; } +"meta" { return META; } +"nocase" { return NOCASE; } +"none" { return NONE; } +"not" { return NOT; } +"of" { return OF; } +"or" { return OR; } +"private" { return PRIVATE; } +"rule" { return RULE; } +"startswith" { return STARTSWITH; } +"strings" { return STRINGS; } +"them" { return THEM; } +"wide" { return WIDE; } +"xor" { return XOR; } + +"false" { return _FALSE; } +"true" { return _TRUE; } + + +%{ /* Commentaires */ %} + +"/*" { PUSH_STATE(comment); } +"*/" { POP_STATE; } +(.|\n) { } + +"//"[^\n]* { } + + +%{ /* Blocs de texte */ %} + +$({letter}|{digit}|_)*"*" { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return STRING_IDENTIFIER_WITH_WILDCARD; + +} + +$({letter}|{digit}|_)* { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return STRING_IDENTIFIER; + +} + +#({letter}|{digit}|_)* { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return STRING_COUNT; + +} + +@({letter}|{digit}|_)* { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return STRING_OFFSET; + +} + +!({letter}|{digit}|_)* { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return STRING_LENGTH; + +} + +u?int(8|16|32)(be)? { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return INTEGER_FUNCTION; + +} + +({letter}|_)({letter}|{digit}|_)* { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return IDENTIFIER; + +} + +{digit}+(MB|KB){0,1} { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return NUMBER; + +} + +{digit}+"."{digit}+ { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return DOUBLE; + +} + +0x{hexdigit}+ { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return NUMBER; + +} + +0o{octdigit}+ { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return NUMBER; + +} + +\"{str_mixed}*\" { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return TEXT_STRING; + +} + +"/" { + + PUSH_STATE(regexp); + +} + +(\\\/|\\.|[^/\n\\])+\/i?s? { + + POP_STATE; + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return REGEXP; + +} + +\{(({hexdigit}|[ \-|\~\?\[\]\(\)\n\r\t]|\/\*(\/|\**[^*/])*\*+\/)+|\/\/.*\n)+\} { + + yylval->cstring.data = yytext; + yylval->cstring.len = yyleng; + + return HEX_STRING; + +} + + +%{ /* Actions par défaut */ %} + +<*>[ \t\r]+ { } + +<*>[\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/tools/yara2rost/yara2rost.c b/tools/yara2rost/yara2rost.c new file mode 100644 index 0000000..3206309 --- /dev/null +++ b/tools/yara2rost/yara2rost.c @@ -0,0 +1,295 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * yara2rost.c - traduction de règles YARA en règles ROST + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "decl.h" + + + +/* Affiche des indications sur l'utilisation du programme. */ +static void show_usage(const char *); + +/* Récupère un contenu à traiter depuis l'entrée standard. */ +static void *get_input_data_from_stdin(size_t *); + +/* Récupère un contenu à traiter depuis un fichier externe. */ +static void *get_input_data_from_file(const char *, size_t *); + + + +/****************************************************************************** +* * +* Paramètres : argv0 = nombre du programme exécuté. * +* * +* Description : Affiche des indications sur l'utilisation du programme. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void show_usage(const char *argv0) +{ + printf("\n"); + + printf("Usage: %s [options] []\n", argv0); + + printf("\n"); + + printf("General options:\n"); + + printf("\n"); + + printf("\t-h | --help\t\tDisplay this messsage.\n"); + + printf("\n"); + + printf("If no YARA file is provided as argument, a rule definition is expected from the standard input.\n"); + + printf("\n"); + +} + + +/****************************************************************************** +* * +* Paramètres : length = taille de l'espace mémoire mis en place. [OUT] * +* * +* Description : Récupère un contenu à traiter depuis l'entrée standard. * +* * +* Retour : Adresse valide ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *get_input_data_from_stdin(size_t *length) +{ + char *result; /* Espace mémoire à retourner */ + ssize_t got; /* Quantité d'octets lus */ + + result = NULL; + *length = 0; + +#define ALLOC_SIZE 2048 + + while (true) + { + result = realloc(result, (*length + ALLOC_SIZE) * sizeof(char)); + + got = read(STDIN_FILENO, result + *length, ALLOC_SIZE); + + if (got == -1) + { + perror("read"); + goto exit_with_error; + } + + *length += got; + + if (got < ALLOC_SIZE) + break; + + } + + return result; + + exit_with_error: + + free(result); + + *length = 0; + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : filename = chemin du fichier à charger en mémoire. * +* length = taille de l'espace mémoire mis en place. [OUT] * +* * +* Description : Récupère un contenu à traiter depuis un fichier externe. * +* * +* Retour : Adresse valide ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *get_input_data_from_file(const char *filename, size_t *length) +{ + char *result; /* Espace mémoire à retourner */ + int fd; /* Descripteur du fichier */ + struct stat info; /* Informations sur le fichier */ + int ret; /* Bilan d'un appel */ + ssize_t got; /* Quantité d'octets lus */ + + result = NULL; + + fd = open(filename, O_RDONLY); + if (fd == -1) + { + perror("open"); + goto exit; + } + + ret = fstat(fd, &info); + if (ret == -1) + { + perror("fstat"); + goto exit_with_fd; + } + + *length = info.st_size; + + result = malloc(*length * sizeof(char)); + + got = read(fd, result, *length); + + if (got == -1 || got != *length) + { + perror("read"); + + free(result); + + result = NULL; + *length = 0; + + } + + exit_with_fd: + + close(fd); + + exit: + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : argc = nombre d'arguments dans la ligne de commande. * +* argv = arguments de la ligne de commande. * +* * +* Description : Point d'entrée du programme. * +* * +* Retour : EXIT_SUCCESS si le prgm s'est déroulé sans encombres. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int main(int argc, char **argv) +{ + int result; /* Bilan à retourner */ + bool need_help; /* Affichage de l'aide ? */ + int index; /* Indice d'argument à traiter */ + int ret; /* Bilan d'une lecture d'arg. */ + const char *source; /* Source de définitions */ + void *content; /* Contenu à traduire */ + size_t length; /* Taille de ce contenu */ + + static struct option long_options[] = { + + { "help", no_argument, NULL, 'h' }, + + { NULL, 0, NULL, 0 } + + }; + + /* Récupération des commandes */ + + need_help = false; + + while (true) + { + ret = getopt_long(argc, argv, "h", long_options, &index); + if (ret == -1) break; + + switch (ret) + { + case 'h': + need_help = true; + break; + + } + + } + + /* Vérifications supplémentaires */ + + if (need_help || (optind != argc && (optind + 1) != argc)) + { + show_usage(argv[0]); + result = (need_help ? EXIT_SUCCESS : EXIT_FAILURE); + goto exit; + } + + /* Execution attendue */ + + result = EXIT_FAILURE; + + if (optind == argc) + content = get_input_data_from_stdin(&length); + + else + { + source = argv[optind]; + + if (strcmp(source, "-") == 0 || strcmp(source, "/dev/stdin") == 0) + content = get_input_data_from_stdin(&length); + else + content = get_input_data_from_file(source, &length); + + } + + if (content != NULL) + { + if (process_rules_definitions(content, length)) + result = EXIT_SUCCESS; + + free(content); + + } + + exit: + + return result; + +} -- cgit v0.11.2-87-g4458