[Forensics-changes] [yara] 222/407: Use argparse library to parse command line arguments and some other improvements
Hilko Bengen
bengen at moszumanska.debian.org
Sat Jul 1 10:28:28 UTC 2017
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag v3.3.0
in repository yara.
commit f51c60806dca65deaff8812996e2f6510b7ba712
Author: Victor M. Alvarez <plusvic at gmail.com>
Date: Fri Nov 21 16:36:16 2014 +0100
Use argparse library to parse command line arguments and some other improvements
---
Makefile.am | 10 +-
argparse/.gitignore | 5 +
argparse/.travis.yml | 5 +
argparse/LICENSE | 21 ++
argparse/README.md | 89 ++++++
argparse/argparse.c | 366 +++++++++++++++++++++++
argparse/argparse.h | 158 ++++++++++
argparse/tap-functions | 445 ++++++++++++++++++++++++++++
argparse/test.sh | 46 +++
argparse/test_argparse.c | 57 ++++
yara.c | 749 ++++++++++++++++++-----------------------------
yarac.c | 251 ++++++++--------
12 files changed, 1610 insertions(+), 592 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 04dee0e..6c237a4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,18 +1,18 @@
-AM_CFLAGS=-O3 -Wall -I$(srcdir)/libyara/include
+AM_CFLAGS=-O3 -std=gnu99 -Wall -I$(srcdir)/libyara/include
# Build the library in the hand subdirectory first.
-SUBDIRS = libyara
-DIST_SUBDIRS = libyara
+SUBDIRS = libyara argparse
+DIST_SUBDIRS = libyara argparse
ACLOCAL_AMFLAGS=-I m4
bin_PROGRAMS = yara yarac
yara_SOURCES = threading.c threading.h yara.c
-yara_LDADD = libyara/.libs/libyara.a
+yara_LDADD = libyara/.libs/libyara.a argparse/libargparse.a
yarac_SOURCES = yarac.c
-yarac_LDADD = libyara/.libs/libyara.a
+yarac_LDADD = libyara/.libs/libyara.a argparse/libargparse.a
# man pages
man1_MANS = yara.man yarac.man
diff --git a/argparse/.gitignore b/argparse/.gitignore
new file mode 100755
index 0000000..fb716d2
--- /dev/null
+++ b/argparse/.gitignore
@@ -0,0 +1,5 @@
+tags
+test_argparse
+*.[ao]
+*.dylib
+*.so
diff --git a/argparse/.travis.yml b/argparse/.travis.yml
new file mode 100755
index 0000000..41bac87
--- /dev/null
+++ b/argparse/.travis.yml
@@ -0,0 +1,5 @@
+language: c
+compiler:
+ - gcc
+ - clang
+script: make test
diff --git a/argparse/LICENSE b/argparse/LICENSE
new file mode 100755
index 0000000..3c77749
--- /dev/null
+++ b/argparse/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2013 Yecheng Fu <cofyc.jackson at gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/argparse/README.md b/argparse/README.md
new file mode 100755
index 0000000..6d2ee87
--- /dev/null
+++ b/argparse/README.md
@@ -0,0 +1,89 @@
+NAME
+====
+
+argparse - A command line arguments parsing library.
+
+[](https://travis-ci.org/Cofyc/argparse)
+
+DESCRIPTION
+===========
+
+This module is inspired by parse-options.c (git) and python's argparse
+module.
+
+Arguments parsing is common task in cli program, but traditional `getopt`
+libraries are not easy to use. This library provides high-level arguments
+parsing solutions.
+
+The program defines what arguments it requires, and `argparse` will figure
+out how to parse those out of `argc` and `argv`, it also automatically
+generates help and usage messages and issues errors when users give the
+program invalid arguments.
+
+Features
+========
+
+ - handles both optional and positional arguments
+ - produces highly informative usage messages
+ - issures errors when given invalid arguments
+
+There are basically three types of options:
+
+ - boolean options
+ - options with mandatory argument
+ - options with optional argument
+
+There are basically two forms of options:
+
+ - short option consist of one dash (`-`) and one alphanumeric character.
+ - long option begin with two dashes (`--`) and some alphanumeric characters.
+
+Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.
+
+Options are case-sensitive.
+
+Options and non-option arguments can clearly be separated using the `--` option.
+
+Examples
+========
+
+```c
+#include "argparse.h"
+
+static const char *const usage[] = {
+ "test_argparse [options] [[--] args]",
+ NULL,
+};
+
+int
+main(int argc, const char **argv)
+{
+ int force = 0;
+ int num = 0;
+ const char *path = NULL;
+ struct argparse_option options[] = {
+ OPT_HELP(),
+ OPT_BOOLEAN('f', "force", &force, "force to do", NULL),
+ OPT_STRING('p', "path", &path, "path to read", NULL),
+ OPT_INTEGER('n', "num", &num, "selected num", NULL),
+ OPT_END(),
+ };
+ struct argparse argparse;
+ argparse_init(&argparse, options, usage, 0);
+ argc = argparse_parse(&argparse, argc, argv);
+ if (force != 0)
+ printf("force: %d\n", force);
+ if (path != NULL)
+ printf("path: %s\n", path);
+ if (num != 0)
+ printf("num: %d\n", num);
+ if (argc != 0) {
+ printf("argc: %d\n", argc);
+ int i;
+ for (i = 0; i < argc; i++) {
+ printf("argv[%d]: %s\n", i, *(argv + i));
+ }
+ }
+ return 0;
+}
+```
diff --git a/argparse/argparse.c b/argparse/argparse.c
new file mode 100755
index 0000000..260f103
--- /dev/null
+++ b/argparse/argparse.c
@@ -0,0 +1,366 @@
+#include "argparse.h"
+
+#define OPT_UNSET 1
+
+static const char *
+prefix_skip(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+int
+prefix_cmp(const char *str, const char *prefix)
+{
+ for (;; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+static void
+argparse_error(struct argparse *self, struct argparse_option *opt,
+ const char *reason)
+{
+ if (!strncmp(self->argv[0], "--", 2)) {
+ fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason);
+ exit(1);
+ } else {
+ fprintf(stderr, "error: option `%c` %s\n", opt->short_name, reason);
+ exit(1);
+ }
+}
+
+static int
+argparse_getvalue(struct argparse *self, struct argparse_option *opt,
+ int flags)
+{
+ const char *s = NULL;
+
+ if (opt->count == opt->max_count)
+ argparse_error(self, opt, "repeated too many times");
+
+ if (!opt->value)
+ goto skipped;
+
+ switch (opt->type) {
+ case ARGPARSE_OPT_BOOLEAN:
+ if (flags & OPT_UNSET) {
+ *(int *)opt->value = *(int *)opt->value - 1;
+ } else {
+ *(int *)opt->value = *(int *)opt->value + 1;
+ }
+ if (*(int *)opt->value < 0) {
+ *(int *)opt->value = 0;
+ }
+ break;
+ case ARGPARSE_OPT_BIT:
+ if (flags & OPT_UNSET) {
+ *(int *)opt->value &= ~opt->data;
+ } else {
+ *(int *)opt->value |= opt->data;
+ }
+ break;
+ case ARGPARSE_OPT_STRING:
+ if (self->optvalue) {
+ *(const char **)opt->value = self->optvalue;
+ self->optvalue = NULL;
+ } else if (self->argc > 1) {
+ self->argc--;
+ if (opt->max_count > 1) {
+ ((const char**)opt->value)[opt->count] = *++self->argv;
+ }
+ else {
+ *(const char **)opt->value = *++self->argv;
+ }
+ } else {
+ argparse_error(self, opt, "requires a value");
+ }
+ break;
+ case ARGPARSE_OPT_INTEGER:
+ if (self->optvalue) {
+ *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0);
+ self->optvalue = NULL;
+ } else if (self->argc > 1) {
+ self->argc--;
+ if (opt->max_count > 1) {
+ ((int*)opt->value)[opt->count] = strtol(*++self->argv, (char **)&s, 0);
+ }
+ else {
+ *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0);
+ }
+ } else {
+ argparse_error(self, opt, "requires a value");
+ }
+ if (s[0] != '\0')
+ argparse_error(self, opt, "expects a numerical value");
+ break;
+ default:
+ assert(0);
+ }
+
+ opt->count++;
+
+skipped:
+ if (opt->callback) {
+ return opt->callback(self, opt);
+ }
+
+ return 0;
+}
+
+static void
+argparse_options_check(struct argparse_option *options)
+{
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ switch (options->type) {
+ case ARGPARSE_OPT_END:
+ case ARGPARSE_OPT_BOOLEAN:
+ case ARGPARSE_OPT_BIT:
+ case ARGPARSE_OPT_INTEGER:
+ case ARGPARSE_OPT_STRING:
+ case ARGPARSE_OPT_GROUP:
+ continue;
+ default:
+ fprintf(stderr, "wrong option type: %d", options->type);
+ break;
+ }
+ }
+}
+
+static int
+argparse_short_opt(struct argparse *self, struct argparse_option *options)
+{
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ if (options->short_name == *self->optvalue) {
+ self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
+ return argparse_getvalue(self, options, 0);
+ }
+ }
+ return -2;
+}
+
+static int
+argparse_long_opt(struct argparse *self, struct argparse_option *options)
+{
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ const char *rest;
+ int opt_flags = 0;
+ if (!options->long_name)
+ continue;
+
+ rest = prefix_skip(self->argv[0] + 2, options->long_name);
+ if (!rest) {
+ // Negation allowed?
+ if (options->flags & OPT_NONEG) {
+ continue;
+ }
+ // Only boolean/bit allow negation.
+ if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) {
+ continue;
+ }
+
+ if (!prefix_cmp(self->argv[0] + 2, "no-")) {
+ rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name);
+ if (!rest)
+ continue;
+ opt_flags |= OPT_UNSET;
+ } else {
+ continue;
+ }
+ }
+ if (*rest) {
+ if (*rest != '=')
+ continue;
+ self->optvalue = rest + 1;
+ }
+ return argparse_getvalue(self, options, opt_flags);
+ }
+ return -2;
+}
+
+int
+argparse_init(struct argparse *self, struct argparse_option *options,
+ const char *const *usage, int flags)
+{
+ memset(self, 0, sizeof(*self));
+ self->options = options;
+ self->usage = usage;
+ self->flags = flags;
+
+ for (; options->type != ARGPARSE_OPT_END; options++)
+ options->count = 0;
+
+ return 0;
+}
+
+int
+argparse_parse(struct argparse *self, int argc, const char **argv)
+{
+ self->argc = argc - 1;
+ self->argv = argv + 1;
+ self->out = argv;
+
+ argparse_options_check(self->options);
+
+ for (; self->argc; self->argc--, self->argv++) {
+ const char *arg = self->argv[0];
+ if (arg[0] != '-' || !arg[1]) {
+ if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
+ goto end;
+ }
+ // if it's not option or is a single char '-', copy verbatimly
+ self->out[self->cpidx++] = self->argv[0];
+ continue;
+ }
+ // short option
+ if (arg[1] != '-') {
+ self->optvalue = arg + 1;
+ switch (argparse_short_opt(self, self->options)) {
+ case -1:
+ break;
+ case -2:
+ goto unknown;
+ }
+ while (self->optvalue) {
+ switch (argparse_short_opt(self, self->options)) {
+ case -1:
+ break;
+ case -2:
+ goto unknown;
+ }
+ }
+ continue;
+ }
+ // if '--' presents
+ if (!arg[2]) {
+ self->argc--;
+ self->argv++;
+ break;
+ }
+ // long option
+ switch (argparse_long_opt(self, self->options)) {
+ case -1:
+ break;
+ case -2:
+ goto unknown;
+ }
+ continue;
+
+unknown:
+ fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]);
+ argparse_usage(self);
+ exit(1);
+ }
+
+end:
+ memmove(self->out + self->cpidx, self->argv,
+ self->argc * sizeof(*self->out));
+ self->out[self->cpidx + self->argc] = NULL;
+
+ return self->cpidx + self->argc;
+}
+
+void
+argparse_usage(struct argparse *self)
+{
+ fprintf(stdout, "Usage: %s\n", *self->usage++);
+ while (*self->usage && **self->usage)
+ fprintf(stdout, " or: %s\n", *self->usage++);
+ fputc('\n', stdout);
+
+ struct argparse_option *options;
+
+ // figure out best width
+ size_t usage_opts_width = 0;
+ size_t len;
+ options = self->options;
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ len = 0;
+ if ((options)->short_name) {
+ len += 2;
+ }
+ if ((options)->short_name && (options)->long_name) {
+ len += 2; // separator ", "
+ }
+ if ((options)->long_name) {
+ len += strlen((options)->long_name) + 2;
+ }
+ if (options->type == ARGPARSE_OPT_INTEGER) {
+ len++; // equal sign "=" or space
+ if (options->type_help != NULL)
+ len += strlen(options->type_help);
+ else
+ len += strlen("<int>");
+ } else if (options->type == ARGPARSE_OPT_STRING) {
+ len++; // equal sign "=" or space
+ if (options->type_help != NULL)
+ len += strlen(options->type_help);
+ else
+ len += strlen("<str>");
+ }
+ len = ceil((float)len / 4) * 4;
+ if (usage_opts_width < len) {
+ usage_opts_width = len;
+ }
+ }
+ usage_opts_width += 4; // 4 spaces prefix
+
+ options = self->options;
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ size_t pos = 0;
+ int pad = 0;
+ if (options->type == ARGPARSE_OPT_GROUP) {
+ fputc('\n', stdout);
+ fprintf(stdout, "%s", options->help);
+ fputc('\n', stdout);
+ continue;
+ }
+ pos = fprintf(stdout, " ");
+ if (options->short_name) {
+ pos += fprintf(stdout, "-%c", options->short_name);
+ }
+ if (options->long_name && options->short_name) {
+ pos += fprintf(stdout, ", ");
+ }
+ if (options->long_name) {
+ pos += fprintf(stdout, "--%s", options->long_name);
+ }
+ if (options->type == ARGPARSE_OPT_INTEGER) {
+ if (options->long_name)
+ pos += fprintf(stdout, "=");
+ else
+ pos += fprintf(stdout, " ");
+ if (options->type_help != NULL)
+ pos += fprintf(stdout, "%s", options->type_help);
+ else
+ pos += fprintf(stdout, "<int>");
+ } else if (options->type == ARGPARSE_OPT_STRING) {
+ if (options->long_name)
+ pos += fprintf(stdout, "=");
+ else
+ pos += fprintf(stdout, " ");
+ if (options->type_help != NULL)
+ pos += fprintf(stdout, "%s", options->type_help);
+ else
+ pos += fprintf(stdout, "<str>");
+ }
+ if (pos <= usage_opts_width) {
+ pad = usage_opts_width - pos;
+ } else {
+ fputc('\n', stdout);
+ pad = usage_opts_width;
+ }
+ fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
+ }
+}
+
+int
+argparse_help_cb(struct argparse *self, const struct argparse_option *option)
+{
+ (void)option;
+ argparse_usage(self);
+ exit(0);
+ return 0;
+}
diff --git a/argparse/argparse.h b/argparse/argparse.h
new file mode 100755
index 0000000..b38a2a7
--- /dev/null
+++ b/argparse/argparse.h
@@ -0,0 +1,158 @@
+#ifndef ARGPARSE_H
+#define ARGPARSE_H
+/**
+ * Command-line arguments parsing library.
+ *
+ * This module is inspired by parse-options.c (git) and python's argparse
+ * module.
+ *
+ * Arguments parsing is common task in cli program, but traditional `getopt`
+ * libraries are not easy to use. This library provides high-level arguments
+ * parsing solutions.
+ *
+ * The program defines what arguments it requires, and `argparse` will figure
+ * out how to parse those out of `argc` and `argv`, it also automatically
+ * generates help and usage messages and issues errors when users give the
+ * program invalid arguments.
+ *
+ * Reserved namespaces:
+ * argparse
+ * OPT
+ * Author: Yecheng Fu <cofyc.jackson at gmail.com>
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct argparse;
+struct argparse_option;
+
+typedef int argparse_callback(struct argparse *self,
+ const struct argparse_option *option);
+
+enum argparse_flag {
+ ARGPARSE_STOP_AT_NON_OPTION = 1,
+};
+
+enum argparse_option_type {
+ /* special */
+ ARGPARSE_OPT_END,
+ ARGPARSE_OPT_GROUP,
+ /* options with no arguments */
+ ARGPARSE_OPT_BOOLEAN,
+ ARGPARSE_OPT_BIT,
+ /* options with arguments (optional or required) */
+ ARGPARSE_OPT_INTEGER,
+ ARGPARSE_OPT_STRING,
+ /* repetable options */
+ ARGPARSE_OPT_INTEGER_MULTI,
+ ARGPARSE_OPT_STRING_MULTI,
+};
+
+enum argparse_option_flags {
+ OPT_NONEG = 1, /* Negation disabled. */
+};
+
+/*
+ * Argparse option struct.
+ *
+ * `type`:
+ * holds the type of the option, you must have an ARGPARSE_OPT_END last in your
+ * array.
+ *
+ * `short_name`:
+ * the character to use as a short option name, '\0' if none.
+ *
+ * `long_name`:
+ * the long option name, without the leading dash, NULL if none.
+ *
+ * `value`:
+ * stores pointer to the value to be filled.
+ *
+ * `max_count`:
+ *
+ * maximum number of times self option can appear in the command line.
+ *
+ * `help`:
+ * the short help message associated to what the option does.
+ * Must never be NULL (except for ARGPARSE_OPT_END).
+ *
+ * `callback`:
+ * function is called when corresponding argument is parsed.
+ *
+ * `data`:
+ * associated data. Callbacks can use it like they want.
+ *
+ * `flags`:
+ * option flags.
+ *
+ *
+ *
+ */
+struct argparse_option {
+ enum argparse_option_type type;
+ const char short_name;
+ const char *long_name;
+ void *value;
+ int max_count;
+ const char *help;
+ const char *type_help;
+ argparse_callback *callback;
+ intptr_t data;
+ int flags;
+ int count;
+};
+
+/*
+ * argpparse
+ */
+struct argparse {
+ // user supplied
+ struct argparse_option *options;
+ const char *const *usage;
+ int flags;
+ // internal context
+ int argc;
+ const char **argv;
+ const char **out;
+ int cpidx;
+ const char *optvalue; // current option value
+};
+
+// builtin callbacks
+int argparse_help_cb(struct argparse *self,
+ const struct argparse_option *option);
+
+// builtin option macros
+#define OPT_BIT(short_name, long_name, value, ...) \
+ { ARGPARSE_OPT_BIT, short_name, long_name, value, 1, __VA_ARGS__ }
+
+#define OPT_BOOLEAN(short_name, long_name, value, ...) \
+ { ARGPARSE_OPT_BOOLEAN, short_name, long_name, value, 1, __VA_ARGS__ }
+
+#define OPT_INTEGER(short_name, long_name, value, ...) \
+ { ARGPARSE_OPT_INTEGER, short_name, long_name, value, 1, __VA_ARGS__ }
+
+#define OPT_STRING_MULTI(short_name, long_name, value, max_count, ...) \
+ { ARGPARSE_OPT_STRING, short_name, long_name, value, max_count, __VA_ARGS__ }
+
+#define OPT_STRING(short_name, long_name, value, ...) \
+ OPT_STRING_MULTI(short_name, long_name, value, 1, __VA_ARGS__)
+
+
+#define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, 0, h, NULL, NULL }
+#define OPT_END() { ARGPARSE_OPT_END }
+
+#define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, "show self help message and exit", NULL, argparse_help_cb)
+
+
+int argparse_init(struct argparse *self, struct argparse_option *options,
+ const char *const *usage, int flags);
+int argparse_parse(struct argparse *self, int argc, const char **argv);
+void argparse_usage(struct argparse *self);
+
+#endif
diff --git a/argparse/tap-functions b/argparse/tap-functions
new file mode 100755
index 0000000..84f700e
--- /dev/null
+++ b/argparse/tap-functions
@@ -0,0 +1,445 @@
+#!/bin/bash
+
+
+_version='1.02'
+
+_plan_set=0
+_no_plan=0
+_skip_all=0
+_test_died=0
+_expected_tests=0
+_executed_tests=0
+_failed_tests=0
+TODO=
+
+
+usage(){
+ cat <<'USAGE'
+tap-functions: A TAP-producing BASH library
+
+PLAN:
+ plan_no_plan
+ plan_skip_all [REASON]
+ plan_tests NB_TESTS
+
+TEST:
+ ok RESULT [NAME]
+ okx COMMAND
+ is RESULT EXPECTED [NAME]
+ isnt RESULT EXPECTED [NAME]
+ like RESULT PATTERN [NAME]
+ unlike RESULT PATTERN [NAME]
+ pass [NAME]
+ fail [NAME]
+
+SKIP:
+ skip [CONDITION] [REASON] [NB_TESTS=1]
+
+ skip $feature_not_present "feature not present" 2 || {
+ is $a "a"
+ is $b "b"
+ }
+
+TODO:
+ Specify TODO mode by setting $TODO:
+ TODO="not implemented yet"
+ ok $result "some not implemented test"
+ unset TODO
+
+OTHER:
+ diag MSG
+
+EXAMPLE:
+ #!/bin/bash
+
+ . tap-functions
+
+ plan_tests 7
+
+ me=$USER
+ is $USER $me "I am myself"
+ like $HOME $me "My home is mine"
+ like "`id`" $me "My id matches myself"
+
+ /bin/ls $HOME 1>&2
+ ok $? "/bin/ls $HOME"
+ # Same thing using okx shortcut
+ okx /bin/ls $HOME
+
+ [[ "`id -u`" != "0" ]]
+ i_am_not_root=$?
+ skip $i_am_not_root "Must be root" || {
+ okx ls /root
+ }
+
+ TODO="figure out how to become root..."
+ okx [ "$HOME" == "/root" ]
+ unset TODO
+USAGE
+ exit
+}
+
+opt=
+set_u=
+while getopts ":sx" opt ; do
+ case $_opt in
+ u) set_u=1 ;;
+ *) usage ;;
+ esac
+done
+shift $(( OPTIND - 1 ))
+# Don't allow uninitialized variables if requested
+[[ -n "$set_u" ]] && set -u
+unset opt set_u
+
+# Used to call _cleanup on shell exit
+trap _exit EXIT
+
+
+
+plan_no_plan(){
+ (( _plan_set != 0 )) && "You tried to plan twice!"
+
+ _plan_set=1
+ _no_plan=1
+
+ return 0
+}
+
+
+plan_skip_all(){
+ local reason=${1:-''}
+
+ (( _plan_set != 0 )) && _die "You tried to plan twice!"
+
+ _print_plan 0 "Skip $reason"
+
+ _skip_all=1
+ _plan_set=1
+ _exit 0
+
+ return 0
+}
+
+
+plan_tests(){
+ local tests=${1:?}
+
+ (( _plan_set != 0 )) && _die "You tried to plan twice!"
+ (( tests == 0 )) && _die "You said to run 0 tests! You've got to run something."
+
+ _print_plan $tests
+ _expected_tests=$tests
+ _plan_set=1
+
+ return $tests
+}
+
+
+_print_plan(){
+ local tests=${1:?}
+ local directive=${2:-''}
+
+ echo -n "1..$tests"
+ [[ -n "$directive" ]] && echo -n " # $directive"
+ echo
+}
+
+
+pass(){
+ local name=$1
+ ok 0 "$name"
+}
+
+
+fail(){
+ local name=$1
+ ok 1 "$name"
+}
+
+
+# This is the workhorse method that actually
+# prints the tests result.
+ok(){
+ local result=${1:?}
+ local name=${2:-''}
+
+ (( _plan_set == 0 )) && _die "You tried to run a test without a plan! Gotta have a plan."
+
+ _executed_tests=$(( $_executed_tests + 1 ))
+
+ if [[ -n "$name" ]] ; then
+ if _matches "$name" "^[0-9]+$" ; then
+ diag " You named your test '$name'. You shouldn't use numbers for your test names."
+ diag " Very confusing."
+ fi
+ fi
+
+ if (( result != 0 )) ; then
+ echo -n "not "
+ _failed_tests=$(( _failed_tests + 1 ))
+ fi
+ echo -n "ok $_executed_tests"
+
+ if [[ -n "$name" ]] ; then
+ local ename=${name//\#/\\#}
+ echo -n " - $ename"
+ fi
+
+ if [[ -n "$TODO" ]] ; then
+ echo -n " # TODO $TODO" ;
+ if (( result != 0 )) ; then
+ _failed_tests=$(( _failed_tests - 1 ))
+ fi
+ fi
+
+ echo
+ if (( result != 0 )) ; then
+ local file='tap-functions'
+ local func=
+ local line=
+
+ local i=0
+ local bt=$(caller $i)
+ while _matches "$bt" "tap-functions$" ; do
+ i=$(( $i + 1 ))
+ bt=$(caller $i)
+ done
+ local backtrace=
+ eval $(caller $i | (read line func file ; echo "backtrace=\"$file:$func() at line $line.\""))
+
+ local t=
+ [[ -n "$TODO" ]] && t="(TODO) "
+
+ if [[ -n "$name" ]] ; then
+ diag " Failed ${t}test '$name'"
+ diag " in $backtrace"
+ else
+ diag " Failed ${t}test in $backtrace"
+ fi
+ fi
+
+ return $result
+}
+
+
+okx(){
+ local command="$@"
+
+ local line=
+ diag "Output of '$command':"
+ $command | while read line ; do
+ diag "$line"
+ done
+ ok ${PIPESTATUS[0]} "$command"
+}
+
+
+_equals(){
+ local result=${1:?}
+ local expected=${2:?}
+
+ if [[ "$result" == "$expected" ]] ; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+# Thanks to Aaron Kangas for the patch to allow regexp matching
+# under bash < 3.
+ _bash_major_version=${BASH_VERSION%%.*}
+_matches(){
+ local result=${1:?}
+ local pattern=${2:?}
+
+ if [[ -z "$result" || -z "$pattern" ]] ; then
+ return 1
+ else
+ if (( _bash_major_version >= 3 )) ; then
+ eval '[[ "$result" =~ "$pattern" ]]'
+ else
+ echo "$result" | egrep -q "$pattern"
+ fi
+ fi
+}
+
+
+_is_diag(){
+ local result=${1:?}
+ local expected=${2:?}
+
+ diag " got: '$result'"
+ diag " expected: '$expected'"
+}
+
+
+is(){
+ local result=${1:?}
+ local expected=${2:?}
+ local name=${3:-''}
+
+ _equals "$result" "$expected"
+ (( $? == 0 ))
+ ok $? "$name"
+ local r=$?
+ (( r != 0 )) && _is_diag "$result" "$expected"
+ return $r
+}
+
+
+isnt(){
+ local result=${1:?}
+ local expected=${2:?}
+ local name=${3:-''}
+
+ _equals "$result" "$expected"
+ (( $? != 0 ))
+ ok $? "$name"
+ local r=$?
+ (( r != 0 )) && _is_diag "$result" "$expected"
+ return $r
+}
+
+
+like(){
+ local result=${1:?}
+ local pattern=${2:?}
+ local name=${3:-''}
+
+ _matches "$result" "$pattern"
+ (( $? == 0 ))
+ ok $? "$name"
+ local r=$?
+ (( r != 0 )) && diag " '$result' doesn't match '$pattern'"
+ return $r
+}
+
+
+unlike(){
+ local result=${1:?}
+ local pattern=${2:?}
+ local name=${3:-''}
+
+ _matches "$result" "$pattern"
+ (( $? != 0 ))
+ ok $? "$name"
+ local r=$?
+ (( r != 0 )) && diag " '$result' matches '$pattern'"
+ return $r
+}
+
+
+skip(){
+ local condition=${1:?}
+ local reason=${2:-''}
+ local n=${3:-1}
+
+ if (( condition == 0 )) ; then
+ local i=
+ for (( i=0 ; i<$n ; i++ )) ; do
+ _executed_tests=$(( _executed_tests + 1 ))
+ echo "ok $_executed_tests # skip: $reason"
+ done
+ return 0
+ else
+ return
+ fi
+}
+
+
+diag(){
+ local msg=${1:?}
+
+ if [[ -n "$msg" ]] ; then
+ echo "# $msg"
+ fi
+
+ return 1
+}
+
+
+_die(){
+ local reason=${1:-'<unspecified error>'}
+
+ echo "$reason" >&2
+ _test_died=1
+ _exit 255
+}
+
+
+BAIL_OUT(){
+ local reason=${1:-''}
+
+ echo "Bail out! $reason" >&2
+ _exit 255
+}
+
+
+_cleanup(){
+ local rc=0
+
+ if (( _plan_set == 0 )) ; then
+ diag "Looks like your test died before it could output anything."
+ return $rc
+ fi
+
+ if (( _test_died != 0 )) ; then
+ diag "Looks like your test died just after $_executed_tests."
+ return $rc
+ fi
+
+ if (( _skip_all == 0 && _no_plan != 0 )) ; then
+ _print_plan $_executed_tests
+ fi
+
+ local s=
+ if (( _no_plan == 0 && _expected_tests < _executed_tests )) ; then
+ s= ; (( _expected_tests > 1 )) && s=s
+ local extra=$(( _executed_tests - _expected_tests ))
+ diag "Looks like you planned $_expected_tests test$s but ran $extra extra."
+ rc=-1 ;
+ fi
+
+ if (( _no_plan == 0 && _expected_tests > _executed_tests )) ; then
+ s= ; (( _expected_tests > 1 )) && s=s
+ diag "Looks like you planned $_expected_tests test$s but only ran $_executed_tests."
+ fi
+
+ if (( _failed_tests > 0 )) ; then
+ s= ; (( _failed_tests > 1 )) && s=s
+ diag "Looks like you failed $_failed_tests test$s of $_executed_tests."
+ fi
+
+ return $rc
+}
+
+
+_exit_status(){
+ if (( _no_plan != 0 || _plan_set == 0 )) ; then
+ return $_failed_tests
+ fi
+
+ if (( _expected_tests < _executed_tests )) ; then
+ return $(( _executed_tests - _expected_tests ))
+ fi
+
+ return $(( _failed_tests + ( _expected_tests - _executed_tests )))
+}
+
+
+_exit(){
+ local rc=${1:-''}
+ if [[ -z "$rc" ]] ; then
+ _exit_status
+ rc=$?
+ fi
+
+ _cleanup
+ local alt_rc=$?
+ (( alt_rc != 0 )) && rc=$alt_rc
+ trap - EXIT
+ exit $rc
+}
+
diff --git a/argparse/test.sh b/argparse/test.sh
new file mode 100755
index 0000000..fdb430a
--- /dev/null
+++ b/argparse/test.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+. tap-functions
+plan_no_plan
+
+is "$(./test_argparse -f --path=/path/to/file a 2>&1)" 'force: 1
+path: /path/to/file
+argc: 1
+argv[0]: a'
+
+is "$(./test_argparse -f -f --force --no-force 2>&1)" 'force: 2'
+
+is "$(./test_argparse -n 2>&1)" 'error: option `n` requires a value'
+
+is "$(./test_argparse -n 2 2>&1)" 'num: 2'
+
+is "$(./test_argparse -n2 2>&1)" 'num: 2'
+
+is "$(./test_argparse -na 2>&1)" 'error: option `n` expects a numerical value'
+
+is "$(./test_argparse -f -- do -f -h 2>&1)" 'force: 1
+argc: 3
+argv[0]: do
+argv[1]: -f
+argv[2]: -h'
+
+is "$(./test_argparse -tf 2>&1)" 'force: 1
+test: 1'
+
+is "$(./test_argparse --read --write 2>&1)" 'perms: 3'
+
+is "$(./test_argparse -h)" 'Usage: test_argparse [options] [[--] args]
+ or: test_argparse [options]
+
+ -h, --help show this help message and exit
+
+OPTIONS
+ -f, --force force to do
+ -t, --test test only
+ -p, --path=<str> path to read
+ -n, --num=<int> selected num
+
+BITS
+ --read read perm
+ --write write perm
+ --exec exec perm'
diff --git a/argparse/test_argparse.c b/argparse/test_argparse.c
new file mode 100755
index 0000000..6d95d35
--- /dev/null
+++ b/argparse/test_argparse.c
@@ -0,0 +1,57 @@
+#include "argparse.h"
+
+static const char *const usage[] = {
+ "test_argparse [options] [[--] args]",
+ "test_argparse [options]",
+ NULL,
+};
+
+#define PERM_READ (1<<0)
+#define PERM_WRITE (1<<1)
+#define PERM_EXEC (1<<2)
+
+int
+main(int argc, const char **argv)
+{
+ int force = 0;
+ int test = 0;
+ int num = 0;
+ const char *path = NULL;
+ int perms = 0;
+ struct argparse_option options[] = {
+ OPT_HELP(),
+ OPT_GROUP("OPTIONS"),
+ OPT_BOOLEAN('f', "force", &force, "force to do"),
+ OPT_BOOLEAN('t', "test", &test, "test only"),
+ OPT_STRING('p', "path", &path, "path to read"),
+ OPT_INTEGER('n', "num", &num, "selected num"),
+ OPT_GROUP("BITS"),
+ OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG),
+ OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE),
+ OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC),
+ OPT_END(),
+ };
+
+ struct argparse argparse;
+ argparse_init(&argparse, options, usage, 0);
+ argc = argparse_parse(&argparse, argc, argv);
+ if (force != 0)
+ printf("force: %d\n", force);
+ if (test != 0)
+ printf("test: %d\n", test);
+ if (path != NULL)
+ printf("path: %s\n", path);
+ if (num != 0)
+ printf("num: %d\n", num);
+ if (argc != 0) {
+ printf("argc: %d\n", argc);
+ int i;
+ for (i = 0; i < argc; i++) {
+ printf("argv[%d]: %s\n", i, *(argv + i));
+ }
+ }
+ if (perms) {
+ printf("perms: %d\n", perms);
+ }
+ return 0;
+}
diff --git a/yara.c b/yara.c
index 4c3cba2..330cf8c 100644
--- a/yara.c
+++ b/yara.c
@@ -25,7 +25,6 @@ limitations under the License.
#else
#include <windows.h>
-#include "getopt.h"
#define PRIx64 "llx"
@@ -35,35 +34,14 @@ limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+
+#include <argparse/argparse.h>
#include <yara.h>
#include "threading.h"
#include "config.h"
-#define USAGE \
-"usage: yara [OPTION]... RULES_FILE FILE | PID\n"\
-"options:\n"\
-" -t <tag> only print rules tagged as <tag>.\n"\
-" -i <identifier> only print rules named <identifier>.\n"\
-" -n only print not satisfied rules (negate).\n"\
-" -g print tags.\n"\
-" -m print metadata.\n"\
-" -s print matching strings.\n"\
-" -p <number> use the specified <number> of threads to scan a directory.\n"\
-" -l <number> abort scanning after matching a number rules.\n"\
-" -a <seconds> abort scanning after a number of seconds has elapsed.\n"\
-" -d <identifier>=<value> define external variable.\n"\
-" -x <module>=<file> pass file's content as extra data to module.\n"\
-" -r recursively search directories.\n"\
-" -f fast matching mode.\n"\
-" -w disable warnings.\n"\
-" -v show version information.\n"
-
-#define EXTERNAL_TYPE_INTEGER 1
-#define EXTERNAL_TYPE_BOOLEAN 2
-#define EXTERNAL_TYPE_STRING 3
-
#define ERROR_COULD_NOT_CREATE_THREAD 100
#ifndef MAX_PATH
@@ -82,36 +60,6 @@ limitations under the License.
#define MAX_QUEUED_FILES 64
-typedef struct _TAG
-{
- char* identifier;
- struct _TAG* next;
-
-} TAG;
-
-
-typedef struct _IDENTIFIER
-{
- char* name;
- struct _IDENTIFIER* next;
-
-} IDENTIFIER;
-
-
-typedef struct _EXTERNAL
-{
- char type;
- char* name;
- union {
- char* string;
- int integer;
- int boolean;
- };
- struct _EXTERNAL* next;
-
-} EXTERNAL;
-
-
typedef struct _MODULE_DATA
{
const char* module_name;
@@ -128,13 +76,23 @@ typedef struct _QUEUED_FILE {
} QUEUED_FILE;
+#define MAX_ARGS_TAG 32
+#define MAX_ARGS_IDENTIFIER 32
+#define MAX_ARGS_EXT_VAR 32
+#define MAX_ARGS_MODULE_DATA 32
+
+char* tags[MAX_ARGS_TAG + 1];
+char* identifiers[MAX_ARGS_IDENTIFIER + 1];
+char* ext_vars[MAX_ARGS_EXT_VAR + 1];
+char* modules_data[MAX_ARGS_EXT_VAR + 1];
+
int recursive_search = FALSE;
int show_tags = FALSE;
int show_specified_tags = FALSE;
int show_specified_rules = FALSE;
int show_strings = FALSE;
-int show_warnings = TRUE;
int show_meta = FALSE;
+int ignore_warnings = FALSE;
int fast_scan = FALSE;
int negate = FALSE;
int count = 0;
@@ -143,10 +101,59 @@ int timeout = 0;
int threads = 8;
-TAG* specified_tags_list = NULL;
-IDENTIFIER* specified_rules_list = NULL;
-EXTERNAL* externals_list = NULL;
-MODULE_DATA* modules_data_list = NULL;
+static const char *const usage[] = {
+ "yara [OPTION]... RULES_FILE FILE | DIRECTORY | PID",
+ NULL,
+};
+
+
+struct argparse_option options[] =
+{
+ OPT_STRING_MULTI('t', "tag", &tags, MAX_ARGS_TAG,
+ "only print rules tagged as <tag>.", "<tag>"),
+
+ OPT_STRING_MULTI('i', "identifier", &identifiers, MAX_ARGS_IDENTIFIER,
+ "only print rules named <identifier>.", "<identifier>"),
+
+ OPT_BOOLEAN('n', "negate", &negate,
+ "only print not satisfied rules (negate)", NULL),
+
+ OPT_BOOLEAN('g', "print-tags", &show_tags,
+ "print tags"),
+
+ OPT_BOOLEAN('m', "print-meta", &show_meta,
+ "print metadata"),
+
+ OPT_BOOLEAN('s', "print-strings", &show_strings,
+ "print matching strings"),
+
+ OPT_INTEGER('p', "threads", &threads,
+ "use the specified number of threads to scan a directory", "<number>"),
+
+ OPT_INTEGER('l', "max-rules", &limit,
+ "abort scanning after matching a number rules", "<number>"),
+
+ OPT_STRING_MULTI('d', NULL, &ext_vars, MAX_ARGS_EXT_VAR,
+ "define external variable", "<identifier>=<value>"),
+
+ OPT_STRING_MULTI('x', NULL, &modules_data, MAX_ARGS_MODULE_DATA,
+ "pass file's content as extra data to module", "<module>=<file>"),
+
+ OPT_INTEGER('a', "timeout", &timeout,
+ "abort scanning after matching a number of rules", "<number>"),
+
+ OPT_BOOLEAN('r', "recursive", &recursive_search,
+ "recursively search directories"),
+
+ OPT_BOOLEAN('f', "fast-scan", &fast_scan,
+ "fast matching mode"),
+
+ OPT_BOOLEAN('w', "no-warnings", &ignore_warnings,
+ "disable warnings"),
+
+ OPT_HELP(),
+ OPT_END()
+};
// file_queue is size-limited queue stored as a circular array, files are
@@ -166,6 +173,8 @@ SEMAPHORE unused_slots;
MUTEX queue_mutex;
MUTEX output_mutex;
+MODULE_DATA* modules_data_list = NULL;
+
int file_queue_init()
{
@@ -263,20 +272,19 @@ void scan_dir(
YR_RULES* rules,
YR_CALLBACK_FUNC callback)
{
- WIN32_FIND_DATA FindFileData;
- HANDLE hFind;
-
- char full_path[MAX_PATH];
static char path_and_mask[MAX_PATH];
snprintf(path_and_mask, sizeof(path_and_mask), "%s\\*", dir);
- hFind = FindFirstFile(path_and_mask, &FindFileData);
+ WIN32_FIND_DATA FindFileData;
+ HANDLE hFind = FindFirstFile(path_and_mask, &FindFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
+ char full_path[MAX_PATH];
+
snprintf(full_path, sizeof(full_path), "%s\\%s",
dir, FindFileData.cFileName);
@@ -314,19 +322,17 @@ void scan_dir(
YR_RULES* rules,
YR_CALLBACK_FUNC callback)
{
- DIR *dp;
- struct dirent *de;
- struct stat st;
- char full_path[MAX_PATH];
-
- dp = opendir(dir);
+ DIR* dp = opendir(dir);
if (dp)
{
- de = readdir(dp);
+ struct dirent* de = readdir(dp);
while (de)
{
+ char full_path[MAX_PATH];
+ struct stat st;
+
snprintf(full_path, sizeof(full_path), "%s/%s", dir, de->d_name);
int err = lstat(full_path, &st);
@@ -359,12 +365,9 @@ void print_string(
uint8_t* data,
int length)
{
- int i;
- char* str;
+ char* str = (char*) (data);
- str = (char*) (data);
-
- for (i = 0; i < length; i++)
+ for (int i = 0; i < length; i++)
{
if (str[i] >= 32 && str[i] <= 126)
printf("%c", str[i]);
@@ -375,13 +378,12 @@ void print_string(
printf("\n");
}
+
void print_hex_string(
uint8_t* data,
int length)
{
- int i;
-
- for (i = 0; i < min(32, length); i++)
+ for (int i = 0; i < min(32, length); i++)
printf("%02X ", (uint8_t) data[i]);
if (length > 32)
@@ -434,7 +436,7 @@ void print_compiler_error(
}
else
{
- if (show_warnings)
+ if (!ignore_warnings)
fprintf(stderr, "%s(%d): warning: %s\n", file_name, line_number, message);
}
}
@@ -442,55 +444,47 @@ void print_compiler_error(
int handle_message(int message, YR_RULE* rule, void* data)
{
- TAG* tag;
- IDENTIFIER* identifier;
- YR_STRING* string;
- YR_MATCH* match;
- YR_META* meta;
-
- const char* tag_name;
-
- int is_matching;
+ const char* tag;
int show = TRUE;
- if (show_specified_tags)
+ if (tags[0] != NULL)
{
+ // The user specified one or more -t <tag> arguments, let's show this rule
+ // only if it's tagged with some of the specified tags.
+
show = FALSE;
- tag = specified_tags_list;
- while (tag != NULL)
+ for (int i = 0; !show && tags[i] != NULL; i++)
{
- yr_rule_tags_foreach(rule, tag_name)
+ yr_rule_tags_foreach(rule, tag)
{
- if (strcmp(tag_name, tag->identifier) == 0)
+ if (strcmp(tag, tags[i]) == 0)
{
show = TRUE;
break;
}
}
-
- tag = tag->next;
}
}
- if (show_specified_rules)
+ if (identifiers[0] != NULL)
{
+ // The user specified one or more -i <identifier> arguments, let's show
+ // this rule only if it's identifier is among of the provided ones.
+
show = FALSE;
- identifier = specified_rules_list;
- while (identifier != NULL)
+ for (int i = 0; !show && identifiers[i] != NULL; i++)
{
- if (strcmp(identifier->name, rule->identifier) == 0)
+ if (strcmp(identifiers[i], rule->identifier) == 0)
{
show = TRUE;
break;
}
-
- identifier = identifier->next;
}
}
- is_matching = (message == CALLBACK_MSG_RULE_MATCHING);
+ int is_matching = (message == CALLBACK_MSG_RULE_MATCHING);
show = show && ((!negate && is_matching) || (negate && !is_matching));
@@ -503,13 +497,13 @@ int handle_message(int message, YR_RULE* rule, void* data)
{
printf("[");
- yr_rule_tags_foreach(rule, tag_name)
+ yr_rule_tags_foreach(rule, tag)
{
// print a comma except for the first tag
- if (tag_name != rule->tags)
+ if (tag != rule->tags)
printf(",");
- printf("%s", tag_name);
+ printf("%s", tag);
}
printf("] ");
@@ -519,6 +513,8 @@ int handle_message(int message, YR_RULE* rule, void* data)
if (show_meta)
{
+ YR_META* meta;
+
printf("[");
yr_rule_metas_foreach(rule, meta)
@@ -543,8 +539,12 @@ int handle_message(int message, YR_RULE* rule, void* data)
if (show_strings)
{
+ YR_STRING* string;
+
yr_rule_strings_foreach(rule, string)
{
+ YR_MATCH* match;
+
yr_string_matches_foreach(string, match)
{
printf("0x%" PRIx64 ":%s: ",
@@ -613,14 +613,11 @@ void* scanning_thread(void* param)
#endif
{
YR_RULES* rules = (YR_RULES*) param;
- char* file_path;
- int result;
-
- file_path = file_queue_get();
+ char* file_path = file_queue_get();
while (file_path != NULL)
{
- result = yr_rules_scan_file(
+ int result = yr_rules_scan_file(
rules,
file_path,
fast_scan ? SCAN_FLAGS_FAST_MODE : 0,
@@ -631,7 +628,7 @@ void* scanning_thread(void* param)
if (result != ERROR_SUCCESS)
{
mutex_lock(&output_mutex);
- fprintf(stderr, "Error scanning %s: ", file_path);
+ fprintf(stderr, "error scanning %s: ", file_path);
print_scanner_error(result);
mutex_unlock(&output_mutex);
}
@@ -646,61 +643,6 @@ void* scanning_thread(void* param)
}
-void cleanup()
-{
- IDENTIFIER* identifier;
- IDENTIFIER* next_identifier;
- TAG* tag;
- TAG* next_tag;
- EXTERNAL* external;
- EXTERNAL* next_external;
- MODULE_DATA* module_data;
- MODULE_DATA* next_module_data;
-
- tag = specified_tags_list;
-
- while(tag != NULL)
- {
- next_tag = tag->next;
- free(tag);
- tag = next_tag;
- }
-
- external = externals_list;
-
- while(external != NULL)
- {
- next_external = external->next;
- free(external);
- external = next_external;
- }
-
- identifier = specified_rules_list;
-
- while(identifier != NULL)
- {
- next_identifier = identifier->next;
- free(identifier);
- identifier = next_identifier;
- }
-
- module_data = modules_data_list;
-
- while(module_data != NULL)
- {
- next_module_data = module_data->next;
-
- yr_filemap_unmap(&module_data->mapped_file);
-
- free((void*) module_data->module_name);
- free((void*) module_data);
-
- module_data = next_module_data;
- }
-
-}
-
-
int is_numeric(
const char *str)
{
@@ -714,399 +656,271 @@ int is_numeric(
}
-int process_cmd_line(
- int argc,
- char const* argv[])
+int define_external_variables(
+ YR_RULES* rules,
+ YR_COMPILER* compiler)
{
- char* equal_sign;
- char* value;
- int c;
-
- TAG* tag;
- IDENTIFIER* identifier;
- EXTERNAL* external;
- MODULE_DATA* module_data;
-
- opterr = 0;
-
- while ((c = getopt (argc, (char**) argv, "wrnsvgma:l:t:i:d:x:p:f")) != -1)
+ for (int i = 0; ext_vars[i] != NULL; i++)
{
- switch (c)
- {
- case 'v':
- printf("%s\n", PACKAGE_STRING);
- return 0;
+ char* equal_sign = strchr(ext_vars[i], '=');
- case 'r':
- recursive_search = TRUE;
- break;
-
- case 'g':
- show_tags = TRUE;
- break;
-
- case 'm':
- show_meta = TRUE;
- break;
-
- case 's':
- show_strings = TRUE;
- break;
-
- case 'w':
- show_warnings = FALSE;
- break;
-
- case 'f':
- fast_scan = TRUE;
- break;
-
- case 'n':
- negate = TRUE;
- break;
-
- case 't':
- show_specified_tags = TRUE;
- tag = (TAG*) malloc(sizeof(TAG));
-
- if (tag != NULL)
- {
- tag->identifier = optarg;
- tag->next = specified_tags_list;
- specified_tags_list = tag;
- }
- else
- {
- fprintf(stderr, "Not enough memory.\n");
- return 0;
- }
- break;
+ if (!equal_sign)
+ {
+ fprintf(stderr, "error: wrong syntax for `-d` option.\n");
+ return FALSE;
+ }
- case 'i':
- show_specified_rules = TRUE;
- identifier = (IDENTIFIER*) malloc(sizeof(IDENTIFIER));
+ // Replace the equal sign with null character to split the external
+ // variable definition (i.e: myvar=somevalue) in two strings: identifier
+ // and value.
- if (identifier != NULL)
- {
- identifier->name = optarg;
- identifier->next = specified_rules_list;
- specified_rules_list = identifier;
- }
- else
- {
- fprintf(stderr, "Not enough memory.\n");
- return 0;
- }
- break;
+ *equal_sign = '\0';
- case 'd':
- equal_sign = strchr(optarg, '=');
- external = (EXTERNAL*) malloc(sizeof(EXTERNAL));
+ char* identifier = ext_vars[i];
+ char* value = equal_sign + 1;
- if (external != NULL)
- {
- external->name = optarg;
- external->next = externals_list;
- externals_list = external;
- }
- else
- {
- fprintf(stderr, "Not enough memory.\n");
- return 0;
- }
-
- if (equal_sign != NULL)
- {
- *equal_sign = '\0';
- value = equal_sign + 1;
-
- if (is_numeric(value))
- {
- external->type = EXTERNAL_TYPE_INTEGER;
- external->integer = atoi(value);
- }
- else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0)
- {
- external->type = EXTERNAL_TYPE_BOOLEAN;
- external->boolean = strcmp(value, "true") == 0;
- }
- else
- {
- external->type = EXTERNAL_TYPE_STRING;
- external->string = value;
- }
- }
- break;
+ if (is_numeric(value))
+ {
+ if (rules != NULL)
+ yr_rules_define_integer_variable(
+ rules,
+ identifier,
+ atoi(value));
+
+ if (compiler != NULL)
+ yr_compiler_define_integer_variable(
+ compiler,
+ identifier,
+ atoi(value));
+ }
+ else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0)
+ {
+ if (rules != NULL)
+ yr_rules_define_boolean_variable(
+ rules,
+ identifier,
+ strcmp(value, "true") == 0);
+
+ if (compiler != NULL)
+ yr_compiler_define_boolean_variable(
+ compiler,
+ identifier,
+ strcmp(value, "true") == 0);
+ }
+ else
+ {
+ if (rules != NULL)
+ yr_rules_define_string_variable(
+ rules,
+ identifier,
+ value);
+
+ if (compiler != NULL)
+ yr_compiler_define_string_variable(
+ compiler,
+ identifier,
+ value);
+ }
+ }
- case 'x':
+ return TRUE;
+}
- equal_sign = strchr(optarg, '=');
- if (equal_sign == NULL)
- {
- fprintf(stderr, "Wrong syntax for -x modifier.\n");
- return 0;
- }
+int load_modules_data()
+{
+ for (int i = 0; modules_data[i] != NULL; i++)
+ {
+ char* equal_sign = strchr(modules_data[i], '=');
- module_data = (MODULE_DATA*) malloc(sizeof(MODULE_DATA));
+ if (!equal_sign)
+ {
+ fprintf(stderr, "error: wrong syntax for `-x` option.\n");
+ return FALSE;
+ }
- if (module_data != NULL)
- module_data->module_name = strdup(optarg);
+ *equal_sign = '\0';
- if (module_data == NULL || module_data->module_name == NULL)
- {
- if (module_data != NULL)
- free(module_data);
+ MODULE_DATA* module_data = (MODULE_DATA*) malloc(sizeof(MODULE_DATA));
- fprintf(stderr, "Not enough memory.\n");
- return 0;
- }
+ if (module_data != NULL)
+ {
+ module_data->module_name = modules_data[i];
- *equal_sign = '\0';
- value = equal_sign + 1;
+ int result = yr_filemap_map(equal_sign + 1, &module_data->mapped_file);
- if (yr_filemap_map(value, &module_data->mapped_file) != ERROR_SUCCESS)
- {
- free(module_data);
- fprintf(stderr, "Could not open file \"%s\".\n", value);
- return 0;
- }
+ if (result != ERROR_SUCCESS)
+ {
+ free(module_data);
+ fprintf(stderr, "error: could not open file \"%s\".\n", equal_sign + 1);
+ return FALSE;
+ }
- module_data->next = modules_data_list;
- modules_data_list = module_data;
+ module_data->next = modules_data_list;
+ modules_data_list = module_data;
+ }
+ }
- break;
+ return TRUE;
+}
- case 'l':
- limit = atoi(optarg);
- break;
- case 'a':
- timeout = atoi(optarg);
- break;
+void unload_modules_data()
+{
+ MODULE_DATA* module_data = modules_data_list;
- case 'p':
- threads = atoi(optarg);
- break;
+ while(module_data != NULL)
+ {
+ MODULE_DATA* next_module_data = module_data->next;
- case '?':
- if (optopt == 't')
- {
- fprintf(stderr, "Option -%c requires an argument.\n", optopt);
- }
- else if (isprint(optopt))
- {
- fprintf(stderr, "Unknown option `-%c'.\n", optopt);
- }
- else
- {
- fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
- }
- return 0;
+ yr_filemap_unmap(&module_data->mapped_file);
+ free(module_data);
- default:
- abort();
- }
+ module_data = next_module_data;
}
- return 1;
-
+ modules_data_list = NULL;
}
-void show_help()
-{
- printf(USAGE);
- printf("\nReport bugs to: <%s>\n", PACKAGE_BUGREPORT);
-}
-
+#define exit_with_code(code) { result = code; goto _exit; }
int main(
int argc,
- char const* argv[])
+ const char** argv)
{
- YR_COMPILER* compiler;
- YR_RULES* rules;
- FILE* rule_file;
- EXTERNAL* external;
+ YR_COMPILER* compiler = NULL;
+ YR_RULES* rules = NULL;
- int pid;
- int i;
- int errors;
+ struct argparse argparse;
int result;
- THREAD thread[MAX_THREADS];
+ argparse_init(&argparse, options, usage, 0);
- if (!process_cmd_line(argc, argv))
- return EXIT_FAILURE;
+ argc = argparse_parse(&argparse, argc, argv);
- if (argc == 1 || optind == argc)
+ if (argc != 2)
{
- show_help();
- cleanup();
+ // After parsing the command-line options we expect two additional
+ // arguments, the rules file and the target file, directory or pid to
+ // be scanned.
+
+ fprintf(stderr, "error: wrong number of arguments\n");
+ argparse_usage(&argparse);
return EXIT_FAILURE;
}
+ if (!load_modules_data())
+ exit_with_code(EXIT_FAILURE);
+
result = yr_initialize();
if (result != ERROR_SUCCESS)
{
- fprintf(stderr, "initialization error: %d\n", result);
- cleanup();
- return EXIT_FAILURE;
+ fprintf(stderr, "error: initialization error (%d)\n", result);
+ exit_with_code(EXIT_FAILURE);
}
- result = yr_rules_load(argv[optind], &rules);
+ // Try to load the rules file as a binary file containing
+ // compiled rules first
+
+ result = yr_rules_load(argv[0], &rules);
+
+ // Accepted result are ERROR_SUCCESS or ERROR_INVALID_FILE
+ // if we are passing the rules in source form, if result is
+ // different from those exit with error.
if (result != ERROR_SUCCESS &&
result != ERROR_INVALID_FILE)
{
print_scanner_error(result);
- yr_finalize();
- cleanup();
- return EXIT_FAILURE;
+ exit_with_code(EXIT_FAILURE);
}
if (result == ERROR_SUCCESS)
{
- external = externals_list;
-
- while (external != NULL)
- {
- switch (external->type)
- {
- case EXTERNAL_TYPE_INTEGER:
- yr_rules_define_integer_variable(
- rules,
- external->name,
- external->integer);
- break;
-
- case EXTERNAL_TYPE_BOOLEAN:
- yr_rules_define_boolean_variable(
- rules,
- external->name,
- external->boolean);
- break;
-
- case EXTERNAL_TYPE_STRING:
- yr_rules_define_string_variable(
- rules,
- external->name,
- external->string);
- break;
- }
- external = external->next;
- }
+ if (!define_external_variables(rules, NULL))
+ exit_with_code(EXIT_FAILURE);
}
else
{
- if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
- {
- yr_finalize();
- cleanup();
- return EXIT_FAILURE;
- }
-
- external = externals_list;
+ // Rules file didn't contain compiled rules, let's handle it
+ // as a text file containing rules in source form.
- while (external != NULL)
- {
- switch (external->type)
- {
- case EXTERNAL_TYPE_INTEGER:
- yr_compiler_define_integer_variable(
- compiler,
- external->name,
- external->integer);
- break;
-
- case EXTERNAL_TYPE_BOOLEAN:
- yr_compiler_define_boolean_variable(
- compiler,
- external->name,
- external->boolean);
- break;
+ if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
+ exit_with_code(EXIT_FAILURE);
- case EXTERNAL_TYPE_STRING:
- yr_compiler_define_string_variable(
- compiler,
- external->name,
- external->string);
- break;
- }
- external = external->next;
- }
+ if (!define_external_variables(NULL, compiler))
+ exit_with_code(EXIT_FAILURE);
yr_compiler_set_callback(compiler, print_compiler_error);
- rule_file = fopen(argv[optind], "r");
+ FILE* rule_file = fopen(argv[0], "r");
if (rule_file == NULL)
{
- fprintf(stderr, "could not open file: %s\n", argv[optind]);
- yr_compiler_destroy(compiler);
- yr_finalize();
- cleanup();
- return EXIT_FAILURE;
+ fprintf(stderr, "error: could not open file: %s\n", argv[0]);
+ exit_with_code(EXIT_FAILURE);
}
- errors = yr_compiler_add_file(compiler, rule_file, NULL, argv[optind]);
+ int errors = yr_compiler_add_file(compiler, rule_file, NULL, argv[0]);
fclose(rule_file);
if (errors > 0)
- {
- yr_compiler_destroy(compiler);
- yr_finalize();
- cleanup();
- return EXIT_FAILURE;
- }
+ exit_with_code(EXIT_FAILURE);
result = yr_compiler_get_rules(compiler, &rules);
yr_compiler_destroy(compiler);
+ compiler = NULL;
+
if (result != ERROR_SUCCESS)
- {
- yr_finalize();
- cleanup();
- return EXIT_FAILURE;
- }
+ exit_with_code(EXIT_FAILURE);
}
mutex_init(&output_mutex);
- if (is_numeric(argv[argc - 1]))
+ if (is_numeric(argv[1]))
{
- pid = atoi(argv[argc - 1]);
+ int pid = atoi(argv[1]);
+
result = yr_rules_scan_proc(
rules,
pid,
fast_scan ? SCAN_FLAGS_FAST_MODE : 0,
callback,
- (void*) argv[argc - 1],
+ (void*) argv[1],
timeout);
if (result != ERROR_SUCCESS)
+ {
print_scanner_error(result);
+ exit_with_code(EXIT_FAILURE);
+ }
}
- else if (is_directory(argv[argc - 1]))
+ else if (is_directory(argv[1]))
{
if (file_queue_init() != 0)
+ {
print_scanner_error(ERROR_INTERNAL_FATAL_ERROR);
+ exit_with_code(EXIT_FAILURE);
+ }
+
+ THREAD thread[MAX_THREADS];
- for (i = 0; i < threads; i++)
+ for (int i = 0; i < threads; i++)
{
if (create_thread(&thread[i], scanning_thread, (void*) rules) != 0)
{
print_scanner_error(ERROR_COULD_NOT_CREATE_THREAD);
- return EXIT_FAILURE;
+ exit_with_code(EXIT_FAILURE);
}
}
scan_dir(
- argv[argc - 1],
+ argv[1],
recursive_search,
rules,
callback);
@@ -1114,7 +928,7 @@ int main(
file_queue_finish();
// Wait for scan threads to finish
- for (i = 0; i < threads; i++)
+ for (int i = 0; i < threads; i++)
thread_join(&thread[i]);
file_queue_destroy();
@@ -1123,16 +937,17 @@ int main(
{
result = yr_rules_scan_file(
rules,
- argv[argc - 1],
+ argv[1],
fast_scan ? SCAN_FLAGS_FAST_MODE : 0,
callback,
- (void*) argv[argc - 1],
+ (void*) argv[1],
timeout);
if (result != ERROR_SUCCESS)
{
- fprintf(stderr, "Error scanning %s: ", argv[argc - 1]);
+ fprintf(stderr, "error scanning %s: ", argv[1]);
print_scanner_error(result);
+ exit_with_code(EXIT_FAILURE);
}
}
@@ -1140,11 +955,19 @@ int main(
yr_rules_print_profiling_info(rules);
#endif
- yr_rules_destroy(rules);
- yr_finalize();
+ result = EXIT_SUCCESS;
- mutex_destroy(&output_mutex);
- cleanup();
+_exit:
- return EXIT_SUCCESS;
+ unload_modules_data();
+
+ if (compiler != NULL)
+ yr_compiler_destroy(compiler);
+
+ if (rules != NULL)
+ yr_rules_destroy(rules);
+
+ yr_finalize();
+
+ return result;
}
diff --git a/yarac.c b/yarac.c
index f45361d..f4f1e51 100644
--- a/yarac.c
+++ b/yarac.c
@@ -24,7 +24,6 @@ limitations under the License.
#else
#include <windows.h>
-#include "getopt.h"
#endif
@@ -32,6 +31,7 @@ limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <argparse/argparse.h>
#include <yara.h>
#include "config.h"
@@ -40,19 +40,30 @@ limitations under the License.
#define MAX_PATH 255
#endif
+#define MAX_ARGS_EXT_VAR 32
-int show_warnings = TRUE;
+char* ext_vars[MAX_ARGS_EXT_VAR + 1];
+int ignore_warnings = FALSE;
-void show_help()
+
+static const char *const usage[] = {
+ "yarac [option]... [namespace:]rules_file... output_file",
+ NULL,
+};
+
+
+struct argparse_option options[] =
{
- printf("usage: yarac [OPTION]... [RULE_FILE]... OUTPUT_FILE\n");
- printf("options:\n");
- printf(" -d <identifier>=<value> define external variable.\n");
- printf(" -w disable warnings.\n");
- printf(" -v show version information.\n");
- printf("\nReport bugs to: <%s>\n", PACKAGE_BUGREPORT);
-}
+ OPT_STRING_MULTI('d', NULL, &ext_vars, MAX_ARGS_EXT_VAR,
+ "define external variable"),
+
+ OPT_BOOLEAN('w', "no-warnings", &ignore_warnings,
+ "disable warnings", NULL),
+
+ OPT_HELP(),
+ OPT_END()
+};
int is_numeric(
@@ -68,81 +79,6 @@ int is_numeric(
}
-int process_cmd_line(
- YR_COMPILER* compiler,
- int argc,
- char const* argv[])
-{
- char* equal_sign;
- char* value;
- char c;
- opterr = 0;
-
- while ((c = getopt (argc, (char**) argv, "wvd:")) != -1)
- {
- switch (c)
- {
- case 'v':
- printf("%s\n", PACKAGE_STRING);
- return 0;
-
- case 'w':
- show_warnings = FALSE;
- break;
-
- case 'd':
- equal_sign = strchr(optarg, '=');
-
- if (equal_sign != NULL)
- {
- *equal_sign = '\0';
- value = equal_sign + 1;
-
- if (is_numeric(value))
- {
- yr_compiler_define_integer_variable(
- compiler,
- optarg,
- atol(value));
- }
- else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0)
- {
- yr_compiler_define_boolean_variable(
- compiler,
- optarg,
- strcmp(value, "true") == 0);
- }
- else
- {
- yr_compiler_define_string_variable(
- compiler,
- optarg,
- value);
- }
- }
- break;
-
- case '?':
-
- if (isprint(optopt))
- {
- fprintf(stderr, "Unknown option `-%c'.\n", optopt);
- }
- else
- {
- fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
- }
- return 0;
-
- default:
- abort();
- }
- }
-
- return 1;
-}
-
-
void report_error(
int error_level,
const char* file_name,
@@ -155,70 +91,130 @@ void report_error(
}
else
{
- if (show_warnings)
+ if (!ignore_warnings)
fprintf(stderr, "%s(%d): warning: %s\n", file_name, line_number, message);
}
}
+int define_external_variables(
+ YR_COMPILER* compiler)
+{
+ for (int i = 0; ext_vars[i] != NULL; i++)
+ {
+ char* equal_sign = strchr(ext_vars[i], '=');
+
+ if (!equal_sign)
+ {
+ fprintf(stderr, "error: wrong syntax for `-d` option.\n");
+ return FALSE;
+ }
+
+ // Replace the equal sign with null character to split the external
+ // variable definition (i.e: myvar=somevalue) in two strings: identifier
+ // and value.
+
+ *equal_sign = '\0';
+
+ char* identifier = ext_vars[i];
+ char* value = equal_sign + 1;
+
+ if (is_numeric(value))
+ {
+ yr_compiler_define_integer_variable(
+ compiler,
+ identifier,
+ atoi(value));
+ }
+ else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0)
+ {
+ yr_compiler_define_boolean_variable(
+ compiler,
+ identifier,
+ strcmp(value, "true") == 0);
+ }
+ else
+ {
+ yr_compiler_define_string_variable(
+ compiler,
+ identifier,
+ value);
+ }
+ }
+
+ return TRUE;
+}
+
+
+#define exit_with_code(code) { result = code; goto _exit; }
+
+
int main(
int argc,
- char const* argv[])
+ const char** argv)
{
- int i, result, errors;
+ YR_COMPILER* compiler = NULL;
+ YR_RULES* rules = NULL;
- YR_COMPILER* compiler;
- YR_RULES* rules;
- FILE* rule_file;
+ struct argparse argparse;
+ int result;
- result = yr_initialize();
+ argparse_init(&argparse, options, usage, 0);
- if (result != ERROR_SUCCESS)
- return EXIT_FAILURE;
+ argc = argparse_parse(&argparse, argc, argv);
- if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
+ if (argc < 2)
{
- yr_finalize();
- return EXIT_FAILURE;
+ fprintf(stderr, "error: wrong number of arguments\n");
+ exit_with_code(EXIT_FAILURE);
}
- if (!process_cmd_line(compiler, argc, argv))
- {
- yr_compiler_destroy(compiler);
- yr_finalize();
- return EXIT_FAILURE;
- }
+ result = yr_initialize();
- if (argc == 1 || optind == argc)
- {
- show_help();
- yr_compiler_destroy(compiler);
- yr_finalize();
- return EXIT_FAILURE;
- }
+ if (result != ERROR_SUCCESS)
+ exit_with_code(EXIT_FAILURE);
+
+ if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
+ exit_with_code(EXIT_FAILURE);
+
+ if (!define_external_variables(compiler))
+ exit_with_code(EXIT_FAILURE);
yr_compiler_set_callback(compiler, report_error);
- for (i = optind; i < argc - 1; i++)
+ for (int i = 0; i < argc - 1; i++)
{
- rule_file = fopen(argv[i], "r");
+ const char* ns;
+ const char* file_name;
+ char* colon = strchr(argv[i], ':');
+
+ if (colon)
+ {
+ file_name = colon + 1;
+ *colon = '\0';
+ ns = argv[i];
+ }
+ else
+ {
+ file_name = argv[i];
+ ns = NULL;
+ }
+
+ FILE* rule_file = fopen(file_name, "r");
if (rule_file != NULL)
{
- errors = yr_compiler_add_file(compiler, rule_file, NULL, argv[i]);
+ int errors = yr_compiler_add_file(
+ compiler, rule_file, ns, file_name);
fclose(rule_file);
if (errors) // errors during compilation
- {
- yr_compiler_destroy(compiler);
- yr_finalize();
- return EXIT_FAILURE;
- }
+ exit_with_code(EXIT_FAILURE);
}
else
{
- fprintf(stderr, "could not open file: %s\n", argv[i]);
+ fprintf(stderr, "error: could not open file: %s\n", file_name);
}
}
@@ -227,7 +223,7 @@ int main(
if (result != ERROR_SUCCESS)
{
fprintf(stderr, "error: %d\n", result);
- return EXIT_FAILURE;
+ exit_with_code(EXIT_FAILURE);
}
result = yr_rules_save(rules, argv[argc - 1]);
@@ -235,14 +231,21 @@ int main(
if (result != ERROR_SUCCESS)
{
fprintf(stderr, "error: %d\n", result);
- return EXIT_FAILURE;
+ exit_with_code(EXIT_FAILURE);
}
- yr_rules_destroy(rules);
- yr_compiler_destroy(compiler);
+ result = EXIT_SUCCESS;
+
+_exit:
+
+ if (compiler != NULL)
+ yr_compiler_destroy(compiler);
+
+ if (rules != NULL)
+ yr_rules_destroy(rules);
yr_finalize();
- return EXIT_SUCCESS;
+ return result;
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/forensics/yara.git
More information about the forensics-changes
mailing list