From 6f160d1b1e1bfdd06414d5b6c49a0c9d9fc8a830 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Tue, 12 Sep 2023 23:49:01 +0200 Subject: Complete the output of scan results (text or JSON). --- plugins/pychrysalide/analysis/scan/scanner.c | 125 ++++++++++++++++++++++++++ src/analysis/scan/matches/bytes.c | 55 +++++++++++- src/analysis/scan/pattern.c | 128 +++++++++++++++++++++++++-- src/analysis/scan/pattern.h | 4 +- src/analysis/scan/rule.c | 128 +++++++++++++++++++++++++-- src/analysis/scan/rule.h | 4 +- src/analysis/scan/scanner.c | 128 +++++++++++++++++++++++++-- src/analysis/scan/scanner.h | 4 +- 8 files changed, 550 insertions(+), 26 deletions(-) diff --git a/plugins/pychrysalide/analysis/scan/scanner.c b/plugins/pychrysalide/analysis/scan/scanner.c index befbc80..e2d5a18 100644 --- a/plugins/pychrysalide/analysis/scan/scanner.c +++ b/plugins/pychrysalide/analysis/scan/scanner.c @@ -25,6 +25,7 @@ #include "scanner.h" +#include #include @@ -34,6 +35,7 @@ #include +#include "context.h" #include "options.h" #include "../content.h" #include "../../access.h" @@ -49,6 +51,12 @@ static int py_content_scanner_init(PyObject *, PyObject *, PyObject *); /* Lance une analyse d'un contenu binaire. */ static PyObject *py_content_scanner_analyze(PyObject *, PyObject *); +/* Convertit un gestionnaire de recherches en JSON. */ +static PyObject *py_content_scanner_convert_to_json(PyObject *, PyObject *); + +/* Indique le chemin d'un éventuel fichier de source. */ +static PyObject *py_content_scanner_get_filename(PyObject *, void *); + /****************************************************************************** @@ -188,6 +196,121 @@ static PyObject *py_content_scanner_analyze(PyObject *self, PyObject *args) /****************************************************************************** * * +* Paramètres : self = classe représentant un format. * +* args = arguments fournis à l'appel. * +* * +* Description : Convertit un gestionnaire de recherches en texte. * +* * +* Retour : Données textuelles ou None en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_content_scanner_convert_to_text(PyObject *self, PyObject *args) +{ + PyObject *result; /* Contexte de suivi à renvoyer*/ + GScanContext *context; /* Contexte d'analyse */ + int ret; /* Bilan de lecture des args. */ + GContentScanner *scanner; /* Encadrement de recherche */ + char *out; /* Données en sortie */ + +#define CONTENT_SCANNER_CONVERT_TO_TEXT_METHOD PYTHON_METHOD_DEF \ +( \ + convert_to_text, "$self, context, /", \ + METH_VARARGS, py_content_scanner, \ + "Output a scan results as text.\n" \ + "\n" \ + "The *context* argument is a pychrysalide.analysis.scan.ScanContext" \ + " instance provided by a previous call to *self.analyze()*. This" \ + " context stores all the scan results.\n" \ + "\n" \ + "The method returns a string value, or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_scan_context, &context); + if (!ret) return NULL; + + scanner = G_CONTENT_SCANNER(pygobject_get(self)); + + out = g_content_scanner_convert_to_text(scanner, context); + + if (out != NULL) + { + result = PyUnicode_FromString(out); + free(out); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant un format. * +* args = arguments fournis à l'appel. * +* * +* Description : Convertit un gestionnaire de recherches en JSON. * +* * +* Retour : Données textuelles au format JSON ou None en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_content_scanner_convert_to_json(PyObject *self, PyObject *args) +{ + PyObject *result; /* Contexte de suivi à renvoyer*/ + GScanContext *context; /* Contexte d'analyse */ + int ret; /* Bilan de lecture des args. */ + GContentScanner *scanner; /* Encadrement de recherche */ + char *out; /* Données en sortie */ + +#define CONTENT_SCANNER_CONVERT_TO_JSON_METHOD PYTHON_METHOD_DEF \ +( \ + convert_to_json, "$self, context, /", \ + METH_VARARGS, py_content_scanner, \ + "Output a scan results as JSON data.\n" \ + "\n" \ + "The *context* argument is a pychrysalide.analysis.scan.ScanContext" \ + " instance provided by a previous call to *self.analyze()*. This" \ + " context stores all the scan results.\n" \ + "\n" \ + "The method returns JSON data as a string value, or *None* in case" \ + " of failure." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_scan_context, &context); + if (!ret) return NULL; + + scanner = G_CONTENT_SCANNER(pygobject_get(self)); + + out = g_content_scanner_convert_to_json(scanner, context); + + if (out != NULL) + { + result = PyUnicode_FromString(out); + free(out); + } + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -247,6 +370,8 @@ PyTypeObject *get_python_content_scanner_type(void) { static PyMethodDef py_content_scanner_methods[] = { CONTENT_SCANNER_ANALYZE_METHOD, + CONTENT_SCANNER_CONVERT_TO_TEXT_METHOD, + CONTENT_SCANNER_CONVERT_TO_JSON_METHOD, { NULL } }; diff --git a/src/analysis/scan/matches/bytes.c b/src/analysis/scan/matches/bytes.c index 043170e..ccd73a1 100644 --- a/src/analysis/scan/matches/bytes.c +++ b/src/analysis/scan/matches/bytes.c @@ -449,7 +449,7 @@ static void g_scan_bytes_match_output_to_json(const GScanBytesMatch *match, cons write(fd, ",\n", 2); - /* Affichage du contenu */ + /* Affichage du contenu brut */ for (i = 0; i < level; i++) write(fd, padding->data, padding->len); @@ -462,12 +462,61 @@ static void g_scan_bytes_match_output_to_json(const GScanBytesMatch *match, cons for (k = 0; k < match->len; k++) { - if (isprint(data[k])) + if (data[k] == '\\') + write(fd, "\\\\", 2); + + else if (isprint(data[k])) write(fd, &data[k], 1); else { - write(fd, "\\x", 2); + write(fd, "\\u", 2); + + /** + * Cf. https://datatracker.ietf.org/doc/html/rfc8259#section-7 + */ + ret = snprintf(value, ULLONG_MAXLEN, "%04hhx", data[k]); + + if (ret > 0) + { + assert(ret == 4); + write(fd, value, ret); + } + + else + { + log_simple_message(LMT_EXT_ERROR, "Error while converting data!"); + write(fd, "??", 2); + } + + } + + } + + write(fd, "\",\n", 3); + + /* Affichage du contenu en version humainement lisible */ + + for (i = 0; i < level; i++) + write(fd, padding->data, padding->len); + + write(fd, "\"content_str\": \"", 16); + + init_vmpa(&pos, match->start, VMPA_NO_VIRTUAL); + + data = g_binary_content_get_raw_access(match->content, &pos, match->len); + + for (k = 0; k < match->len; k++) + { + if (data[k] == '\\') + write(fd, "\\\\", 2); + + else if (isprint(data[k])) + write(fd, &data[k], 1); + + else + { + write(fd, "\\\\x", 3); ret = snprintf(value, ULLONG_MAXLEN, "%02hhx", data[k]); diff --git a/src/analysis/scan/pattern.c b/src/analysis/scan/pattern.c index 5b966d2..797f4ac 100644 --- a/src/analysis/scan/pattern.c +++ b/src/analysis/scan/pattern.c @@ -25,10 +25,15 @@ #include +#include #include +#include +#include +#include #include "pattern-int.h" +#include "../../core/logs.h" @@ -216,14 +221,69 @@ void g_search_pattern_output_to_text(const GSearchPattern *pattern, GScanContext * * * Description : Convertit un motif de recherche en texte. * * * -* Retour : - * +* Retour : Données textuelles ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -void g_search_pattern_convert_as_text(const GSearchPattern *pattern, GScanContext *context) +char *g_search_pattern_convert_as_text(const GSearchPattern *pattern, GScanContext *context) { + char *result; /* Données à retourner */ + char *name; /* Nom "unique" pour le canal */ + int ret; /* Bilan de création de nom */ + int fd; /* Canal d'écriture */ + struct stat info; /* Infos. incluant une taille */ + ssize_t got; /* Données effectivement relues*/ + + static unsigned long long counter = 0; + + result = NULL; + + ret = asprintf(&name, "rost-pattern2text-%llu", counter++); + if (ret == -1) goto exit; + + fd = memfd_create(name, MFD_CLOEXEC); + if (fd == -1) + { + LOG_ERROR_N("memfd_create"); + goto exit_with_name; + } + + g_search_pattern_output_to_text(pattern, context, fd); + + ret = fstat(fd, &info); + if (ret != 0) + { + LOG_ERROR_N("fstat"); + goto exit_with_name_and_fd; + } + + result = malloc((info.st_size + 1) * sizeof(char)); + + lseek(fd, SEEK_SET, 0); + + got = read(fd, result, info.st_size); + if (got != info.st_size) + { + LOG_ERROR_N("read"); + free(result); + goto exit_with_name_and_fd; + } + + result[info.st_size] = '\0'; + + exit_with_name_and_fd: + + close(fd); + + exit_with_name: + + free(name); + + exit: + + return result; } @@ -294,14 +354,72 @@ void g_search_pattern_output_to_json(const GSearchPattern *pattern, GScanContext * * * Description : Convertit un motif de recherche en JSON. * * * -* Retour : - * +* Retour : Données textuelles au format JSON ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -void g_search_pattern_convert_as_json(const GSearchPattern *pattern, GScanContext *context) +char *g_search_pattern_convert_as_json(const GSearchPattern *pattern, GScanContext *context) { - /* TODO */ + char *result; /* Données à retourner */ + char *name; /* Nom "unique" pour le canal */ + int ret; /* Bilan de création de nom */ + int fd; /* Canal d'écriture */ + sized_string_t padding; /* Bourrage pour le JSON */ + struct stat info; /* Infos. incluant une taille */ + ssize_t got; /* Données effectivement relues*/ + + static unsigned long long counter = 0; + + result = NULL; + + ret = asprintf(&name, "rost-pattern2json-%llu", counter++); + if (ret == -1) goto exit; + + fd = memfd_create(name, MFD_CLOEXEC); + if (fd == -1) + { + LOG_ERROR_N("memfd_create"); + goto exit_with_name; + } + + padding.data = " "; + padding.len = 3; + + g_search_pattern_output_to_json(pattern, context, &padding, 0, fd, false); + + ret = fstat(fd, &info); + if (ret != 0) + { + LOG_ERROR_N("fstat"); + goto exit_with_name_and_fd; + } + + result = malloc((info.st_size + 1) * sizeof(char)); + + lseek(fd, SEEK_SET, 0); + + got = read(fd, result, info.st_size); + if (got != info.st_size) + { + LOG_ERROR_N("read"); + free(result); + goto exit_with_name_and_fd; + } + + result[info.st_size] = '\0'; + + exit_with_name_and_fd: + + close(fd); + + exit_with_name: + + free(name); + + exit: + + return result; } diff --git a/src/analysis/scan/pattern.h b/src/analysis/scan/pattern.h index 0e4327d..72f87e4 100644 --- a/src/analysis/scan/pattern.h +++ b/src/analysis/scan/pattern.h @@ -66,13 +66,13 @@ void g_search_pattern_set_name(GSearchPattern *, const char *, size_t); void g_search_pattern_output_to_text(const GSearchPattern *, GScanContext *, int); /* Convertit un motif de recherche en texte. */ -void g_search_pattern_convert_as_text(const GSearchPattern *, GScanContext *); +char *g_search_pattern_convert_as_text(const GSearchPattern *, GScanContext *); /* Affiche un motif de recherche au format JSON. */ void g_search_pattern_output_to_json(const GSearchPattern *, GScanContext *, const sized_string_t *, unsigned int, int, bool); /* Convertit un motif de recherche en JSON. */ -void g_search_pattern_convert_as_json(const GSearchPattern *, GScanContext *); +char *g_search_pattern_convert_as_json(const GSearchPattern *, GScanContext *); diff --git a/src/analysis/scan/rule.c b/src/analysis/scan/rule.c index 4ef1e3c..a1fcfcb 100644 --- a/src/analysis/scan/rule.c +++ b/src/analysis/scan/rule.c @@ -26,7 +26,11 @@ #include #include +#include #include +#include +#include +#include #include "rule-int.h" @@ -561,15 +565,69 @@ void g_scan_rule_output_to_text(const GScanRule *rule, GScanContext *context, bo * * * Description : Convertit une règle en texte. * * * -* Retour : - * +* Retour : Données textuelles ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -void g_scan_rule_convert_as_text(const GScanRule *rule, GScanContext *context) +char *g_scan_rule_convert_as_text(const GScanRule *rule, GScanContext *context) { - /* TODO */ + char *result; /* Données à retourner */ + char *name; /* Nom "unique" pour le canal */ + int ret; /* Bilan de création de nom */ + int fd; /* Canal d'écriture */ + struct stat info; /* Infos. incluant une taille */ + ssize_t got; /* Données effectivement relues*/ + + static unsigned long long counter = 0; + + result = NULL; + + ret = asprintf(&name, "rost-rule2text-%llu", counter++); + if (ret == -1) goto exit; + + fd = memfd_create(name, MFD_CLOEXEC); + if (fd == -1) + { + LOG_ERROR_N("memfd_create"); + goto exit_with_name; + } + + g_scan_rule_output_to_text(rule, context, true, fd); + + ret = fstat(fd, &info); + if (ret != 0) + { + LOG_ERROR_N("fstat"); + goto exit_with_name_and_fd; + } + + result = malloc((info.st_size + 1) * sizeof(char)); + + lseek(fd, SEEK_SET, 0); + + got = read(fd, result, info.st_size); + if (got != info.st_size) + { + LOG_ERROR_N("read"); + free(result); + goto exit_with_name_and_fd; + } + + result[info.st_size] = '\0'; + + exit_with_name_and_fd: + + close(fd); + + exit_with_name: + + free(name); + + exit: + + return result; } @@ -668,14 +726,72 @@ void g_scan_rule_output_to_json(const GScanRule *rule, GScanContext *context, co * * * Description : Convertit une règle en JSON. * * * -* Retour : - * +* Retour : Données textuelles au format JSON ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -void g_scan_rule_convert_as_json(const GScanRule *rule, GScanContext *context) +char *g_scan_rule_convert_as_json(const GScanRule *rule, GScanContext *context) { - /* TODO */ + char *result; /* Données à retourner */ + char *name; /* Nom "unique" pour le canal */ + int ret; /* Bilan de création de nom */ + int fd; /* Canal d'écriture */ + sized_string_t padding; /* Bourrage pour le JSON */ + struct stat info; /* Infos. incluant une taille */ + ssize_t got; /* Données effectivement relues*/ + + static unsigned long long counter = 0; + + result = NULL; + + ret = asprintf(&name, "rost-rule2json-%llu", counter++); + if (ret == -1) goto exit; + + fd = memfd_create(name, MFD_CLOEXEC); + if (fd == -1) + { + LOG_ERROR_N("memfd_create"); + goto exit_with_name; + } + + padding.data = " "; + padding.len = 3; + + g_scan_rule_output_to_json(rule, context, &padding, 0, fd, false); + + ret = fstat(fd, &info); + if (ret != 0) + { + LOG_ERROR_N("fstat"); + goto exit_with_name_and_fd; + } + + result = malloc((info.st_size + 1) * sizeof(char)); + + lseek(fd, SEEK_SET, 0); + + got = read(fd, result, info.st_size); + if (got != info.st_size) + { + LOG_ERROR_N("read"); + free(result); + goto exit_with_name_and_fd; + } + + result[info.st_size] = '\0'; + + exit_with_name_and_fd: + + close(fd); + + exit_with_name: + + free(name); + + exit: + + return result; } diff --git a/src/analysis/scan/rule.h b/src/analysis/scan/rule.h index 7ade51b..6ab5105 100644 --- a/src/analysis/scan/rule.h +++ b/src/analysis/scan/rule.h @@ -84,13 +84,13 @@ void g_scan_rule_check(GScanRule *, GEngineBackend *, GScanContext *); void g_scan_rule_output_to_text(const GScanRule *, GScanContext *, bool, int); /* Convertit une règle en texte. */ -void g_scan_rule_convert_as_text(const GScanRule *, GScanContext *); +char *g_scan_rule_convert_as_text(const GScanRule *, GScanContext *); /* Affiche une règle au format JSON. */ void g_scan_rule_output_to_json(const GScanRule *, GScanContext *, const sized_string_t *, unsigned int, int, bool); /* Convertit une règle en JSON. */ -void g_scan_rule_convert_as_json(const GScanRule *, GScanContext *); +char *g_scan_rule_convert_as_json(const GScanRule *, GScanContext *); diff --git a/src/analysis/scan/scanner.c b/src/analysis/scan/scanner.c index 29f47eb..41fa0de 100644 --- a/src/analysis/scan/scanner.c +++ b/src/analysis/scan/scanner.c @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include "decl.h" @@ -556,15 +560,69 @@ void g_content_scanner_output_to_text(const GContentScanner *scanner, GScanConte * * * Description : Convertit un gestionnaire de recherches en texte. * * * -* Retour : - * +* Retour : Données textuelles ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -void g_content_scanner_convert_as_text(const GContentScanner *scanner, GScanContext *context) +char *g_content_scanner_convert_to_text(const GContentScanner *scanner, GScanContext *context) { - /* TODO */ + char *result; /* Données à retourner */ + char *name; /* Nom "unique" pour le canal */ + int ret; /* Bilan de création de nom */ + int fd; /* Canal d'écriture */ + struct stat info; /* Infos. incluant une taille */ + ssize_t got; /* Données effectivement relues*/ + + static unsigned long long counter = 0; + + result = NULL; + + ret = asprintf(&name, "rost-scanner2text-%llu", counter++); + if (ret == -1) goto exit; + + fd = memfd_create(name, MFD_CLOEXEC); + if (fd == -1) + { + LOG_ERROR_N("memfd_create"); + goto exit_with_name; + } + + g_content_scanner_output_to_text(scanner, context, true, fd); + + ret = fstat(fd, &info); + if (ret != 0) + { + LOG_ERROR_N("fstat"); + goto exit_with_name_and_fd; + } + + result = malloc((info.st_size + 1) * sizeof(char)); + + lseek(fd, SEEK_SET, 0); + + got = read(fd, result, info.st_size); + if (got != info.st_size) + { + LOG_ERROR_N("read"); + free(result); + goto exit_with_name_and_fd; + } + + result[info.st_size] = '\0'; + + exit_with_name_and_fd: + + close(fd); + + exit_with_name: + + free(name); + + exit: + + return result; } @@ -624,14 +682,72 @@ void g_content_scanner_output_to_json(const GContentScanner *scanner, GScanConte * * * Description : Convertit un gestionnaire de recherches en JSON. * * * -* Retour : - * +* Retour : Données textuelles au format JSON ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ -void g_content_scanner_convert_as_json(const GContentScanner *scanner, GScanContext *context) +char *g_content_scanner_convert_to_json(const GContentScanner *scanner, GScanContext *context) { - /* TODO */ + char *result; /* Données à retourner */ + char *name; /* Nom "unique" pour le canal */ + int ret; /* Bilan de création de nom */ + int fd; /* Canal d'écriture */ + sized_string_t padding; /* Bourrage pour le JSON */ + struct stat info; /* Infos. incluant une taille */ + ssize_t got; /* Données effectivement relues*/ + + static unsigned long long counter = 0; + + result = NULL; + + ret = asprintf(&name, "rost-scanner2json-%llu", counter++); + if (ret == -1) goto exit; + + fd = memfd_create(name, MFD_CLOEXEC); + if (fd == -1) + { + LOG_ERROR_N("memfd_create"); + goto exit_with_name; + } + + padding.data = " "; + padding.len = 3; + + g_content_scanner_output_to_json(scanner, context, &padding, 0, fd); + + ret = fstat(fd, &info); + if (ret != 0) + { + LOG_ERROR_N("fstat"); + goto exit_with_name_and_fd; + } + + result = malloc((info.st_size + 1) * sizeof(char)); + + lseek(fd, SEEK_SET, 0); + + got = read(fd, result, info.st_size); + if (got != info.st_size) + { + LOG_ERROR_N("read"); + free(result); + goto exit_with_name_and_fd; + } + + result[info.st_size] = '\0'; + + exit_with_name_and_fd: + + close(fd); + + exit_with_name: + + free(name); + + exit: + + return result; } diff --git a/src/analysis/scan/scanner.h b/src/analysis/scan/scanner.h index d2b5dc1..e03ecda 100644 --- a/src/analysis/scan/scanner.h +++ b/src/analysis/scan/scanner.h @@ -76,13 +76,13 @@ GScanContext *g_content_scanner_analyze(GContentScanner *, GScanOptions *, GBinC void g_content_scanner_output_to_text(const GContentScanner *, GScanContext *, bool, int); /* Convertit un gestionnaire de recherches en texte. */ -void g_content_scanner_convert_as_text(const GContentScanner *, GScanContext *); +char *g_content_scanner_convert_to_text(const GContentScanner *, GScanContext *); /* Affiche un gestionnaire de recherches au format JSON. */ void g_content_scanner_output_to_json(const GContentScanner *, GScanContext *, const sized_string_t *, unsigned int, int); /* Convertit un gestionnaire de recherches en JSON. */ -void g_content_scanner_convert_as_json(const GContentScanner *, GScanContext *); +char *g_content_scanner_convert_to_json(const GContentScanner *, GScanContext *); -- cgit v0.11.2-87-g4458