aboutsummaryrefslogtreecommitdiffstats
path: root/yara-compiler.py
blob: 8050b4ba445621713dc4c6ee0aa775167e4a577e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import argparse
import os
import re
from yara import *

class YACFormat(object):
    @staticmethod
    def write(filename, data):
        raise NotImplementedError

class YACFormatBinary(YACFormat):
    STR = "binary"
    EXT = "yac"

    @staticmethod
    def write(filename, data):
        with open(filename, "wb") as f:
            f.write(data)

class YACFormatCHeader(YACFormat):
    STR = "c_header"
    EXT = "h"
    MODE = "w"

    @staticmethod
    def write(filename, data):
        with open(filename, "w") as f:
            f.write("#ifndef FILE_{}_H\n#define FILE_{}_H\n\n#include <stdint.h>\n\n".format(os.path.splitext(os.path.basename(filename))[0], os.path.splitext(os.path.basename(filename))[0]))
            f.write("{} FILE_{}[{}] = {{\n\t".format("uint8_t", os.path.splitext(os.path.basename(filename))[0], len(data)))
            i = 0
            for d in data:
                i += 1
                f.write("{}{}".format(d, ",\n\t" if (i % 16 == 0) else ", "))
            f.write("\n};\n\n#endif\n")

DICT_STR_YACFORMAT = {
    YACFormatBinary.STR : YACFormatBinary,
    YACFormatCHeader.STR : YACFormatCHeader
}

def dir_path(string):
    logger.debug("{}: string = {}".format("dir_path", string))
    if os.path.isdir(string) or re.match(r"(^\/|^\.\/|^\.\.\/|^[^/])[^:*?\"<>|\r\n]*\.(yac|h)$", string):
        return string
    else:
        raise TypeError("no valid path")

def walk(args):
    logger = logging.getLogger(__name__)
    logger.info("Walking files ...")

    files = [os.path.abspath(os.path.join(dp, f)) for dp, dn, filenames in os.walk(args["input_directory"]) for f in filenames]
    logger.debug("Files: {}".format(files))
    logger.info("Number of files found: {}".format(len(files)))

    if args["output"].endswith(YACFormatBinary.EXT) or args["output"].endswith(YACFormatCHeader.EXT):
        yd = YaraDatabase()
        for file in files:
            if file.endswith(".json"):
                logger.info("Compiling file {}".format(file))
                yd.add_file(file)
        data = yd.compile(
            args["store_identifier_entry"],
            args["store_identifier_signature"],
            args["store_index_map_entries"],
            args["store_index_map_signatures"],
            args["store_index_map_string_blocks"],
            args["store_hash"])
        args["format"].write(args["output"], data)
    else:
        for file in files:
            if file.endswith(".json"):
                logger.info("Compiling file {}".format(file))
                yd = YaraDatabase()
                yd.add_file(file)
                data = yd.compile(
                    args["store_identifier_entry"],
                    args["store_identifier_signature"],
                    args["store_index_map_entries"],
                    args["store_index_map_signatures"],
                    args["store_index_map_string_blocks"],
                    args["store_hash"])
                args["format"].write(os.path.join(args["output"], os.path.splitext(os.path.basename(file))[0] + ".yac"), data)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Compile single or multiple yara files')
    parser.add_argument('-i', '--input-directory', nargs='?', default='.', type=dir_path, help='Input directory (default: %(default)s)')
    parser.add_argument('-o', '--output', nargs='?', default='.', type=dir_path, help='Output file or directory (default: %(default)s)')
    parser.add_argument('-f', '--input-file', nargs='?', default='.', type=dir_path, help='Input file (default: %(default)s)')
    parser.add_argument('--format', nargs='?', default=YACFormatBinary.STR, action='store', choices=[YACFormatBinary.STR, YACFormatCHeader.STR], help='Output file format (default: %(default)s)')
    parser.add_argument('-v', '--verbose', action="count", default=0, help="Verbosity level")
    parser.add_argument('--store-identifier-entry', action='store_true', help='Store identifier for entry elements (default: %(default)s)')
    parser.add_argument('--store-identifier-signature', action='store_true', help='Store identifier for signature elements (default: %(default)s)')
    parser.add_argument('--store-index-map-entries', action='store_true', help='Store index map for entries (default: %(default)s)')
    parser.add_argument('--store-index-map-signatures', action='store_true', help='Store identifier for signature elements (default: %(default)s)')
    parser.add_argument('--store-index-map-string-blocks', action='store_true', help='Store identifier for string blocks (default: %(default)s)')
    parser.add_argument('--store-hash', action='store_true', help='Store hash for database (default: %(default)s)')
    args = parser.parse_args()

    if args.verbose == 0:
        log_level = logging.WARNING
    elif args.verbose == 1:
        log_level = logging.INFO
    elif args.verbose >= 2:
        log_level = logging.DEBUG

    logging.basicConfig(stream=sys.stdout, level=log_level)
    logger = logging.getLogger(__name__)

    args = {
        "input_directory": args.input_directory,
        "output": args.output,
        "input_file": args.input_file,
        "format": DICT_STR_YACFORMAT[args.format],
        "verbosity": args.verbose,
        "store_identifier_entry": args.store_identifier_entry,
        "store_identifier_signature": args.store_identifier_signature,
        "store_index_map_entries": args.store_index_map_entries,
        "store_index_map_signatures": args.store_index_map_signatures,
        "store_index_map_string_blocks": args.store_index_map_string_blocks,
        "store_hash": args.store_hash
    }

    logger.debug("args = {}".format(args))

    walk(args)