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
|
#!/usr/bin/env python
# encoding: utf-8
#
# Tested on Linux (Ubuntu), Windows XP/7, and Mac OS X
#
'''
untitled.py
Created by Matthew Richard on 2010-03-12.
Ported to py3 by Julien (jvoisin) Voisin on feb2014
Copyright (c) 2010. All rights reserved.
'''
import os
import re
import argparse
import collections
def main():
parser = argparse.ArgumentParser(description='PEiD to yara rules converter')
parser.add_argument('-n', '--no-ep', dest='no_ep', action='store_true',
default=False, help='no entrypoint restriction')
parser.add_argument('files', metavar='files', type=str, nargs='+',
help='scanned filenames')
parser.add_argument('-o', '--output-file', action='store', dest='outfile',
help='output filename')
opts = parser.parse_args()
if opts.outfile is None:
parser.error('You must specify an output filename!\n')
elif opts.files is None:
parser.error('You must supply at least one filename!\n')
else:
for fin in opts.files:
if not os.path.isfile(fin):
parser.error('%s does not exist' % fin)
# yara rule template from which rules will be created
yara_rule = '''
rule %s
{
strings:
%s
condition:
%s
}
'''
rules = collections.defaultdict(lambda: set(), {})
# read the PEiD signature files
data = ' '.join([open(f, 'r').read() for f in opts.files])
# every signature takes the form of
# [signature_name]
# signature = hex signature
# ep_only = (true|false)
signature = re.compile(r'''
\[\d*
([^(?:\->)\n]{1,128}) # This is the rule name
(?:\->)?[^\]]*\]\s*\n # We don't care about content after a "->"
signature\ =\ (?:\?.\ )* # Signature pattern can't start with ??
((?:[0-9A-Fa-f?]{2}\ )* # Only match hex pairs
[0-9A-Fa-f?]{2})\s*\n # Get the terminal pair
ep_only\ =\ (true|false)\s*\n
''', re.MULTILINE | re.VERBOSE)
# rule name has the same constraints as a C variable name
rules_cpt = 0
double_cpt = 0
name_filter = re.compile(r'(\W)')
for match in signature.finditer(data):
name = name_filter.sub('_', match.group(1)).rstrip('_')
if (match.group(2), match.group(3)) in rules[name]:
double_cpt +=1
rules[name].add((match.group(2), match.group(3)))
rules_cpt += 1
print('[+] Found %d signatures (%d duplicates) in PEiD input file' %
(rules_cpt, double_cpt))
output = ''
for rule in list(rules.keys()):
detects = ''
conds = '\t'
counter = 0
for (detect, use_ep) in rules[rule]:
# create each new rule using a unique numeric value
# to allow for multiple criteria and no collisions
detects += '\t$a%d = { %s }\n' % (counter, detect)
if counter > 0:
conds += ' or '
# if the rule specifies it should be at EP we add
# the yara specifier 'at entrypoint'
conds += '$a%d' % counter
if use_ep == 'true' and opts.no_ep is False:
conds += ' at entrypoint'
counter += 1
# add the rule to the output
output += yara_rule % (rule, detects, conds)
# could be written to an output file
with open(opts.outfile, 'w') as fout:
fout.write(output)
print('[+] Wrote %d rules to %s' % (len(rules), opts.outfile))
if __name__ == '__main__':
main()
|