summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/analysis/scan/context-int.h2
-rw-r--r--src/analysis/scan/context.c48
-rw-r--r--src/analysis/scan/context.h6
-rw-r--r--src/analysis/scan/grammar.y34
-rw-r--r--src/analysis/scan/pattern.c10
-rw-r--r--src/analysis/scan/rule-int.h6
-rw-r--r--src/analysis/scan/rule.c83
-rw-r--r--src/analysis/scan/rule.h15
-rw-r--r--src/analysis/scan/scanner.c58
-rw-r--r--src/analysis/scan/tokens.l3
-rw-r--r--tests/analysis/scan/common.py2
-rw-r--r--tests/analysis/scan/grammar.py66
12 files changed, 301 insertions, 32 deletions
diff --git a/src/analysis/scan/context-int.h b/src/analysis/scan/context-int.h
index 8a5fbaf..654ecca 100644
--- a/src/analysis/scan/context-int.h
+++ b/src/analysis/scan/context-int.h
@@ -83,6 +83,8 @@ struct _GScanContext
full_match_tracker_t **full_trackers; /* Correspondances confirmées */
size_t full_count; /* Quantité de correspondances */
+ bool global; /* Validation globale */
+
rule_condition_t *conditions; /* Ensemble de règles suivies */
size_t cond_count; /* Quantité de ces conditions */
diff --git a/src/analysis/scan/context.c b/src/analysis/scan/context.c
index c016f7e..8a9b600 100644
--- a/src/analysis/scan/context.c
+++ b/src/analysis/scan/context.c
@@ -246,6 +246,8 @@ static void g_scan_context_init(GScanContext *context)
context->full_trackers = NULL;
context->full_count = 0;
+ context->global = true;
+
context->conditions = NULL;
context->cond_count = 0;
@@ -751,6 +753,49 @@ bool g_scan_context_has_rule_for_name(const GScanContext *context, const char *n
/******************************************************************************
* *
* Paramètres : context = mémoire de résultats d'analyse à consulter. *
+* *
+* Description : Indique le bilan des règles globales. *
+* *
+* Retour : Bilan global des analyses menées. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_scan_context_has_global_match(const GScanContext *context)
+{
+ bool result; /* Bilan global à retourner */
+
+ result = context->global;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : context = mémoire de résultats d'analyse à actualiser. *
+* global = bilan global des analyses menées. *
+* *
+* Description : Définit le bilan des règles globales. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_scan_context_set_global_match(GScanContext *context, bool global)
+{
+ context->global = global;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : context = mémoire de résultats d'analyse à consulter. *
* name = désignation de la règle ciblée. *
* *
* Description : Indique si une correspondance globale a pu être établie. *
@@ -773,6 +818,9 @@ bool g_scan_context_has_match_for_rule(GScanContext *context, const char *name)
result = false;
+ if (!context->global)
+ goto exit;
+
/* Recherche de la règle visée */
cond = NULL;
diff --git a/src/analysis/scan/context.h b/src/analysis/scan/context.h
index de8a213..7fb3dd4 100644
--- a/src/analysis/scan/context.h
+++ b/src/analysis/scan/context.h
@@ -101,6 +101,12 @@ bool g_scan_context_set_rule_condition(GScanContext *, const char *, GScanExpres
/* Indique si un nom donné correspond à une règle. */
bool g_scan_context_has_rule_for_name(const GScanContext *, const char *);
+/* Indique le bilan des règles globales. */
+bool g_scan_context_has_global_match(const GScanContext *);
+
+/* Définit le bilan des règles globales. */
+void g_scan_context_set_global_match(GScanContext *, bool);
+
/* Indique si une correspondance globale a pu être établie. */
bool g_scan_context_has_match_for_rule(GScanContext *, const char *);
diff --git a/src/analysis/scan/grammar.y b/src/analysis/scan/grammar.y
index 25bb536..e1f0e9e 100644
--- a/src/analysis/scan/grammar.y
+++ b/src/analysis/scan/grammar.y
@@ -73,11 +73,9 @@ typedef void *yyscan_t;
sized_string_t *tmp_masks; /* Masques associés */
} masked;
-
+ ScanRuleFlags rule_flags; /* Fanions pour règle */
GScanRule *rule; /* Nouvelle règle à intégrer */
-
-
GScanTokenNode *node; /* Bribe de motif à intégrer */
GSearchPattern *pattern; /* Nouveau motif à considérer */
@@ -140,6 +138,7 @@ YY_DECL;
%token NOCASE "nocase"
%token FULLWORD "fullword"
%token PRIVATE "private"
+%token GLOBAL "global"
%token HEX_BYTES
@@ -234,6 +233,8 @@ YY_DECL;
%type <unsigned_integer> UNSIGNED_INTEGER
%type <sized_cstring> STRING
+%type <rule_flags> rule_flags
+%type <rule_flags> rule_flag
%type <rule> rule
%type <sized_cstring> PLAIN_TEXT
@@ -345,14 +346,35 @@ YY_DECL;
* Définition de règle.
*/
- rule : RAW_RULE RULE_NAME
+ rule : rule_flags RAW_RULE RULE_NAME
{
- *built_rule = g_scan_rule_new($2.data);
+ *built_rule = g_scan_rule_new($1, $3.data);
$<rule>$ = *built_rule;
}
BRACE_IN meta bytes condition BRACE_OUT
{
- $$ = $<rule>3;
+ $$ = $<rule>4;
+ }
+ ;
+
+
+ rule_flags : /* empty */
+ {
+ $$ = SRF_NONE;
+ }
+ | rule_flags rule_flag
+ {
+ $$ = $1 | $2;
+ }
+ ;
+
+ rule_flag : "private"
+ {
+ $$ = SRF_PRIVATE;
+ }
+ | "global"
+ {
+ $$ = SRF_GLOBAL;
}
;
diff --git a/src/analysis/scan/pattern.c b/src/analysis/scan/pattern.c
index 797f4ac..fe3babc 100644
--- a/src/analysis/scan/pattern.c
+++ b/src/analysis/scan/pattern.c
@@ -295,7 +295,7 @@ char *g_search_pattern_convert_as_text(const GSearchPattern *pattern, GScanConte
* padding = éventuel bourrage initial à placer ou NULL. *
* level = profondeur actuelle. *
* fd = canal d'écriture. *
-* trailing = impose une virgule finale ? *
+* tail = décline la pose d'une virgule finale ? *
* *
* Description : Affiche un motif de recherche au format JSON. *
* *
@@ -305,7 +305,7 @@ char *g_search_pattern_convert_as_text(const GSearchPattern *pattern, GScanConte
* *
******************************************************************************/
-void g_search_pattern_output_to_json(const GSearchPattern *pattern, GScanContext *context, const sized_string_t *padding, unsigned int level, int fd, bool trailing)
+void g_search_pattern_output_to_json(const GSearchPattern *pattern, GScanContext *context, const sized_string_t *padding, unsigned int level, int fd, bool tail)
{
unsigned int i; /* Boucle de parcours */
GSearchPatternClass *class; /* Classe à activer */
@@ -339,10 +339,10 @@ void g_search_pattern_output_to_json(const GSearchPattern *pattern, GScanContext
for (i = 0; i < level; i++)
write(fd, padding->data, padding->len);
- if (trailing)
- write(fd, "},\n", 3);
- else
+ if (tail)
write(fd, "}\n", 2);
+ else
+ write(fd, "},\n", 3);
}
diff --git a/src/analysis/scan/rule-int.h b/src/analysis/scan/rule-int.h
index b43cba9..1ca2b7f 100644
--- a/src/analysis/scan/rule-int.h
+++ b/src/analysis/scan/rule-int.h
@@ -37,6 +37,8 @@ struct _GScanRule
{
GObject parent; /* A laisser en premier */
+ ScanRuleFlags flags; /* Propriétés de la règle */
+
char *name; /* Désignation de la règle */
fnv64_t name_hash; /* Empreinte de la désignation */
@@ -56,5 +58,9 @@ struct _GScanRuleClass
};
+/* Met en place une règle de détection statique avec motifs. */
+bool g_scan_rule_create(GScanRule *, ScanRuleFlags, const char *);
+
+
#endif /* _ANALYSIS_SCAN_RULE_INT_H */
diff --git a/src/analysis/scan/rule.c b/src/analysis/scan/rule.c
index a7d7765..68222dd 100644
--- a/src/analysis/scan/rule.c
+++ b/src/analysis/scan/rule.c
@@ -97,6 +97,8 @@ static void g_scan_rule_class_init(GScanRuleClass *klass)
static void g_scan_rule_init(GScanRule *rule)
{
+ rule->flags = SRF_NONE;
+
rule->name = NULL;
rule->name_hash = 0;
@@ -159,7 +161,8 @@ static void g_scan_rule_finalize(GScanRule *rule)
/******************************************************************************
* *
-* Paramètres : name = désignation à associer à la future règle. *
+* Paramètres : flags = propriétés particulières à conférer à la règle. *
+* name = désignation à associer à la future règle. *
* *
* Description : Crée une règle de détection statique à l'aide de motifs. *
* *
@@ -169,12 +172,14 @@ static void g_scan_rule_finalize(GScanRule *rule)
* *
******************************************************************************/
-GScanRule *g_scan_rule_new(const char *name)
+GScanRule *g_scan_rule_new(ScanRuleFlags flags, const char *name)
{
GScanRule *result; /* Structure à retourner */
result = g_object_new(G_TYPE_SCAN_RULE, NULL);
+ result->flags = flags;
+
result->name = strdup(name);
result->name_hash = fnv_64a_hash(name);
@@ -185,7 +190,60 @@ GScanRule *g_scan_rule_new(const char *name)
/******************************************************************************
* *
-* Paramètres : rule = règle de détection à compléter. *
+* Paramètres : rule = règle de détection à initialiser pleinement. *
+* flags = propriétés particulières à conférer à la règle. *
+* name = désignation à associer à la future règle. *
+* *
+* Description : Met en place une règle de détection statique avec motifs. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_scan_rule_create(GScanRule *rule, ScanRuleFlags flags, const char *name)
+{
+ GScanRule *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_SCAN_RULE, NULL);
+
+ result->flags = flags;
+
+ result->name = strdup(name);
+ result->name_hash = fnv_64a_hash(name);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : rule = règle de détection à consulter. *
+* *
+* Description : Indique les particularités liées à une règle de détection. *
+* *
+* Retour : Propriétés particulières attachées à la règle. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+ScanRuleFlags g_scan_rule_get_flags(const GScanRule *rule)
+{
+ ScanRuleFlags result; /* Fanions à retourner */
+
+ result = rule->flags;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : rule = règle de détection à consulter. *
* hash = empreinte précalculée associée au nom. [OUT] *
* *
* Description : Indique le nom associé à une règle de détection. *
@@ -202,7 +260,8 @@ const char *g_scan_rule_get_name(const GScanRule *rule, fnv64_t *hash)
result = rule->name;
- *hash = rule->name_hash;
+ if (hash != NULL)
+ *hash = rule->name_hash;
return result;
@@ -635,7 +694,7 @@ char *g_scan_rule_convert_as_text(const GScanRule *rule, GScanContext *context)
* padding = éventuel bourrage initial à placer ou NULL. *
* level = profondeur actuelle. *
* fd = canal d'écriture. *
-* trailing = impose une virgule finale ? *
+* tail = décline la pose d'une virgule finale ? *
* *
* Description : Affiche une règle au format JSON. *
* *
@@ -645,10 +704,10 @@ char *g_scan_rule_convert_as_text(const GScanRule *rule, GScanContext *context)
* *
******************************************************************************/
-void g_scan_rule_output_to_json(const GScanRule *rule, GScanContext *context, const sized_string_t *padding, unsigned int level, int fd, bool trailing)
+void g_scan_rule_output_to_json(const GScanRule *rule, GScanContext *context, const sized_string_t *padding, unsigned int level, int fd, bool tail)
{
size_t i; /* Boucle de parcours */
- bool sub_trailing; /* Virgule finale */
+ bool sub_tail; /* Saut de la virgule finale ? */
/* Introduction */
@@ -677,9 +736,9 @@ void g_scan_rule_output_to_json(const GScanRule *rule, GScanContext *context, co
for (i = 0; i < rule->bytes_used; i++)
{
- sub_trailing = ((i + 1) < rule->bytes_used);
+ sub_tail = ((i + 1) == rule->bytes_used);
- g_search_pattern_output_to_json(rule->bytes_locals[i], context, padding, level + 2, fd, sub_trailing);
+ g_search_pattern_output_to_json(rule->bytes_locals[i], context, padding, level + 2, fd, sub_tail);
}
@@ -707,10 +766,10 @@ void g_scan_rule_output_to_json(const GScanRule *rule, GScanContext *context, co
for (i = 0; i < level; i++)
write(fd, padding->data, padding->len);
- if (trailing)
- write(fd, "},\n", 3);
- else
+ if (tail)
write(fd, "}\n", 2);
+ else
+ write(fd, "},\n", 3);
}
diff --git a/src/analysis/scan/rule.h b/src/analysis/scan/rule.h
index 6ab5105..3e6fe9d 100644
--- a/src/analysis/scan/rule.h
+++ b/src/analysis/scan/rule.h
@@ -53,11 +53,24 @@ typedef struct _GScanRule GScanRule;
typedef struct _GScanRuleClass GScanRuleClass;
+/* Particularités de règle à faire valoir */
+typedef enum _ScanRuleFlags
+{
+ SRF_NONE = (0 << 0), /* Absence de particularité */
+ SRF_PRIVATE = (1 << 0), /* Règle silencieuse */
+ SRF_GLOBAL = (1 << 1) /* Règle de base implicite */
+
+} ScanRuleFlags;
+
+
/* Indique le type défini pour une règle de détection par motifs. */
GType g_scan_rule_get_type(void);
/* Crée une règle de détection statique à l'aide de motifs. */
-GScanRule *g_scan_rule_new(const char *);
+GScanRule *g_scan_rule_new(ScanRuleFlags, const char *);
+
+/* Indique les particularités liées à une règle de détection. */
+ScanRuleFlags g_scan_rule_get_flags(const GScanRule *);
/* Indique le nom associé à une règle de détection. */
const char *g_scan_rule_get_name(const GScanRule *, fnv64_t *);
diff --git a/src/analysis/scan/scanner.c b/src/analysis/scan/scanner.c
index 41fa0de..7b553e6 100644
--- a/src/analysis/scan/scanner.c
+++ b/src/analysis/scan/scanner.c
@@ -364,7 +364,7 @@ bool g_content_scanner_include_resource(GContentScanner *scanner, const char *pa
if (!result)
{
- inc_name = g_scan_rule_get_name(included->rules[i], (fnv64_t []) { 0 });
+ inc_name = g_scan_rule_get_name(included->rules[i], NULL);
log_variadic_message(LMT_ERROR, "Can not import from '%s': rule '%s' already exists!",
path, inc_name);
@@ -459,7 +459,7 @@ bool g_content_scanner_add_rule(GContentScanner *scanner, GScanRule *rule)
if (!result)
{
- inc_name = g_scan_rule_get_name(rule, (fnv64_t []) { 0 });
+ inc_name = g_scan_rule_get_name(rule, NULL);
log_variadic_message(LMT_ERROR, "Can not add rule: '%s' already exists!", inc_name);
@@ -489,6 +489,11 @@ GScanContext *g_content_scanner_analyze(GContentScanner *scanner, GScanOptions *
GScanContext *result; /* Bilan global à retourner */
bool status; /* Bilan d'opération locale */
size_t i; /* Boucle de parcours */
+ bool global; /* Bilan des règles globales */
+ GScanRule *rule; /* Règle à consulter */
+ const char *name; /* Désignation de la règle */
+
+ /* Préparations... */
result = g_scan_context_new(options);
@@ -510,6 +515,8 @@ GScanContext *g_content_scanner_analyze(GContentScanner *scanner, GScanOptions *
}
+ /* Phase d'analyse */
+
g_scan_context_set_content(result, content);
g_engine_backend_run_scan(scanner->data_backend, result);
@@ -519,6 +526,25 @@ GScanContext *g_content_scanner_analyze(GContentScanner *scanner, GScanOptions *
for (i = 0; i < scanner->rule_count; i++)
g_scan_rule_check(scanner->rules[i], scanner->data_backend, result);
+ /* Etablissement d'un bilan global */
+
+ global = true;
+
+ for (i = 0; i < scanner->rule_count; i++)
+ {
+ rule = scanner->rules[i];
+
+ if ((g_scan_rule_get_flags(rule) & SRF_GLOBAL) == 0)
+ continue;
+
+ name = g_scan_rule_get_name(rule, NULL);
+
+ global = g_scan_context_has_match_for_rule(result, name);
+
+ }
+
+ g_scan_context_set_global_match(result, global);
+
exit:
return result;
@@ -645,29 +671,45 @@ char *g_content_scanner_convert_to_text(const GContentScanner *scanner, GScanCon
void g_content_scanner_output_to_json(const GContentScanner *scanner, GScanContext *context, const sized_string_t *padding, unsigned int level, int fd)
{
- size_t i; /* Boucle de parcours */
- bool trailing; /* Virgule finale */
+ size_t k; /* Boucle de parcours #1 */
+ size_t i; /* Boucle de parcours #2 */
+ size_t last_displayed; /* Dernier indice affiché */
+ bool tail; /* Saut de la virgule finale ? */
/* Introduction */
- for (i = 0; i < level; i++)
+ for (k = 0; k < level; k++)
write(fd, padding->data, padding->len);
write(fd, "[\n", 2);
/* Sous-traitance aux règles */
+ for (i = scanner->rule_count; i > 0; i--)
+ if ((g_scan_rule_get_flags(scanner->rules[i - 1]) & SRF_PRIVATE) == 0)
+ break;
+
+ if (i == 0)
+ goto nothing_to_display;
+ else
+ last_displayed = i - 1;
+
for (i = 0; i < scanner->rule_count; i++)
{
- trailing = ((i + 1) < scanner->rule_count);
+ if ((g_scan_rule_get_flags(scanner->rules[i]) & SRF_PRIVATE) == SRF_PRIVATE)
+ continue;
- g_scan_rule_output_to_json(scanner->rules[i], context, padding, level + 1, fd, trailing);
+ tail = (i == last_displayed);
+
+ g_scan_rule_output_to_json(scanner->rules[i], context, padding, level + 1, fd, tail);
}
/* Conclusion */
- for (i = 0; i < level; i++)
+ nothing_to_display:
+
+ for (k = 0; k < level; k++)
write(fd, padding->data, padding->len);
write(fd, "]\n", 2);
diff --git a/src/analysis/scan/tokens.l b/src/analysis/scan/tokens.l
index 241c973..1a17344 100644
--- a/src/analysis/scan/tokens.l
+++ b/src/analysis/scan/tokens.l
@@ -403,6 +403,9 @@ bytes_fuzzy_id [\*A-Za-z_][\*A-Za-z0-9_]*
%{ /* Définition locale d'une règle */ %}
+ "global" { return GLOBAL; }
+ "private" { return PRIVATE; }
+
"rule" {
PUSH_STATE(rule_intro);
return RAW_RULE;
diff --git a/tests/analysis/scan/common.py b/tests/analysis/scan/common.py
index 3b52e38..507b7e2 100644
--- a/tests/analysis/scan/common.py
+++ b/tests/analysis/scan/common.py
@@ -33,6 +33,8 @@ class RostTestClass(ChrysalideTestCase):
else:
self.assertFalse(ctx.has_match_for_rule('test'))
+ return scanner, ctx
+
def check_rule_success(self, rule, content = None):
"""Check for scan success."""
diff --git a/tests/analysis/scan/grammar.py b/tests/analysis/scan/grammar.py
index 8b18f81..13a255b 100644
--- a/tests/analysis/scan/grammar.py
+++ b/tests/analysis/scan/grammar.py
@@ -1,4 +1,6 @@
+import json
+
from common import RostTestClass
@@ -181,7 +183,71 @@ rule test {
self.check_rule_success(rule)
+ def testPrivateRules(self):
+ """Ensure private rules remain silent."""
+
+ for private in [ True, False ]:
+ for state in [ True, False ]:
+
+ rule = '''
+%srule silent {
+
+ condition:
+ %s
+
+}
+
+rule test {
+
+ condition:
+ silent
+
+}
+''' % ('private ' if private else '', 'true' if state else 'false')
+
+ scanner, ctx = self._validate_rule_result(rule, self._empty_content, state)
+
+ data = scanner.convert_to_json(ctx)
+ jdata = json.loads(data)
+
+ # Exemple :
+ #
+ # [{'bytes_patterns': [], 'matched': True, 'name': 'test'},
+ # {'bytes_patterns': [], 'matched': True, 'name': 'silent'}]
+
+ found = len([ j['name'] for j in jdata if j['name'] == 'silent' ]) > 0
+
+ self.assertTrue(private ^ found)
+
+
+ def testGlobalRules(self):
+ """Take global rules into account."""
+
+ for glob_state in [ True, False ]:
+ for state in [ True, False ]:
+
+ rule = '''
+%srule silent {
+
+ condition:
+ %s
+
+}
+
+rule test {
+
+ condition:
+ true
+
+}
+''' % ('global ' if glob_state else '', 'true' if state else 'false')
+
+ expected = not(glob_state) or state
+ if expected:
+ self.check_rule_success(rule)
+ else:
+ self.check_rule_failure(rule)