1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """Routines for manipulating yara rule definitions."""
20
21 import string
22 import sys
23 import yaml
24 import pyparsing
25
26
27 _RULE = pyparsing.Keyword("rule")
28 _KEYWORD = (pyparsing.Literal("wide") |
29 pyparsing.Literal("fullword") |
30 pyparsing.Literal("ascii") |
31 pyparsing.Literal("nocase"))
32
33 _IDENTIFIER = pyparsing.Word(pyparsing.alphanums + '_' + "$")
34 _REGEX = (pyparsing.QuotedString("/", escChar="\\", unquoteResults=False) +
35 pyparsing.Optional(pyparsing.Word("sig")))
36 _LEFT_CURLY = pyparsing.Literal("{")
37 _RIGHT_CURLY = pyparsing.Literal("}")
38 _COLON = pyparsing.Literal(':')
39 _EQUALS = pyparsing.Literal("=")
40
41
43 """Builds a (pyparsing) parser for the content inside delimiters.
44
45 Args:
46 opener_and_closer: a string containing two elements: opener and closer
47
48 Returns:
49 A (pyparsing) parser for the content inside delimiters.
50 """
51 opener = pyparsing.Literal(opener_and_closer[0])
52 closer = pyparsing.Literal(opener_and_closer[1])
53 char_removal_mapping = dict.fromkeys(map(ord, opener_and_closer))
54 other_chars = unicode(string.printable).translate(char_removal_mapping)
55 word_without_delimiters = pyparsing.Word(other_chars).setName(
56 "other_chars")
57 anything = pyparsing.Forward()
58 delimited_block = opener + anything + closer
59
60 anything << pyparsing.ZeroOrMore(
61 word_without_delimiters.setName("word_without_delimiters")
62 | delimited_block.setName("delimited_block")
63 )
64
65
66 return pyparsing.Combine(anything)
67
69 opener = opener_and_closer[0]
70 closer = opener_and_closer[1]
71 anything = anything_beetween(opener_and_closer)
72 return opener + anything + closer
73
76
85
87 return pyparsing.Group(
88 _IDENTIFIER.setResultsName("lhs") + _EQUALS +
89 pyparsing.Combine(
90 (anything_in_curly() |
91 pyparsing.QuotedString("'", escChar="\\", unquoteResults=False) |
92 pyparsing.QuotedString("\"", escChar="\\", unquoteResults=False) |
93 _REGEX) +
94 pyparsing.ZeroOrMore(_KEYWORD),
95 adjacent=False,
96 joinString=" ",
97 ).setResultsName("rhs")
98 )
99
101 return pyparsing.Group(
102 pyparsing.Literal("strings") +
103 _COLON +
104 pyparsing.OneOrMore(statement()).setResultsName("statements")
105 ).setResultsName("strings")
106
112
117
124
126 return pyparsing.OneOrMore(rule())
127
129 condition = parsed_rule["condition"]["statement"]
130
131 result = dict(name=parsed_rule["name"],
132 meta={},
133 strings=[],
134 condition=condition)
135
136 for x in parsed_rule.get("meta", {}).get("statements", []):
137 result["meta"][x["lhs"]] = x["rhs"]
138
139 for x in parsed_rule.get("strings", {}).get("statements", []):
140 result["strings"].append((x["lhs"], x["rhs"]))
141
142 return result
143
145 """Parse a yara rules file into a python AST."""
146
147 yara_rules = pyparsing.cppStyleComment.suppress().transformString(
148 yara_rules)
149
150 result = []
151 for rules, _, _ in rule().parseWithTabs().scanString(yara_rules):
152 try:
153 result.append(rule_to_ast(rules))
154 except Exception:
155 pass
156
157 return result
158
160 result = []
161 for rule_ast in parsed_rules:
162 result.append("rule %s {" % rule_ast["name"])
163 metadata = rule_ast.get("meta")
164 if metadata:
165 result.append(" meta:")
166 for k, v in metadata.iteritems():
167 result.append(" %s = %s" % (k, v))
168
169 if rule_ast.get("strings"):
170 result.append(" strings:")
171 for k, v in sorted(rule_ast["strings"]):
172 result.append(" %s = %s" % (k, v))
173
174 result.append(" condition: %s" % rule_ast["condition"])
175 result.append(" }")
176 return "\n".join(result)
177
178
179 if __name__ == "__main__":
180 action = sys.argv[1]
181 filename = sys.argv[2]
182 if action == "parse":
183 data = open(filename).read()
184 print yaml.safe_dump(
185 parse_yara_to_ast(data),
186 default_flow_style=False)
187 elif action == "encode":
188 data = open(filename).read()
189 print ast_to_yara(yaml.safe_load(data))
190 else:
191 raise RuntimeError("Unknown action %s" % action)
192