-
Notifications
You must be signed in to change notification settings - Fork 24
/
CreateYaraSignature.py
181 lines (170 loc) · 8.28 KB
/
CreateYaraSignature.py
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
'''
********************************************************************************
Name: CreateYaraSignature.py
Author: case b <cbarnes@accuvant.com>
Version: 1
[Description]
This Python script for use in IDA allows the user to display information about
the code and data currently selected and optionally save the information to
disk.
The following configurable data items may be displayed and or saved:
- Header including length and VA range
- Bytes representing the selection
- Text of the disassembly selected
- Bytes organized by lines of disassembly
- Bytes representing the selection with all but the first byte per line
wildcarded if either of the operands represent memory addresses or relative
locations
- A basic YARA signature with the wildcarded byte string as the test signature
[Notes]
This script has only been tested in IDA 6.3 on MacOS X with PySide installed
analysing x86 code. YMMV.
PySide must be installed for save functionality to work.
Happy hunting. If you have any questions, comments, rants, etc. please send
them my way.
********************************************************************************
'''
import idaapi
class SignatureCreator( object):
'''Creates signature information for a selection'''
def __init__( self):
self.__ERRORS = {
'SUCCESS' : 0, # Everything's great
'BADADDR' : 1, # A selection wasn't made
'BADFILE' : 2, # Something bad happend whilst writing the signature file
}
self.__CONFIG = {
'SHOW_IN_OUTPUT_WINDOW' : True,
'PROMPT_TO_SAVE' : False,
'SHOW_HEADER' : True,
'SHOW_DISASSEMBLY_TEXT' : True,
'SHOW_RAW_BYTES' : True,
'SHOW_WILD_CARDED_BYTES' : True,
'SHOW_BYTES_PER_LINE' : True,
'SHOW_YARA_SIGNATURE' : True,
'OP_TYPES_TO_WILDCARD' : [o_mem,o_phrase, o_displ,o_far,o_near]
}
self.__SignatureHeader = []
self.__RawBytes = []
self.__DisassemblyText = []
self.__LinesOfBytes = []
self.__WildCardedBytes = []
self.__YaraSignature = []
self.__Signature = []
def __CheckBounds( self):
'''Check to see if a selection was made.'''
if self.__startAddress is BADADDR or self.__endAddress is BADADDR:
sys.stderr.write( "Please select the section you would like to create a signature on.")
return self.__ERRORS['BADADDR']
else:
return self.__ERRORS['SUCCESS']
def GetSignatureHeader( self):
'''Returns signature header information.'''
if not self.__SignatureHeader:
self.__SignatureHeader.append( "\n\n[SIGNATURE FOR {0}]\n".format( GetInputFilePath()))
self.__SignatureHeader.append( "\nLENGTH:\t{0:#x}\n".format( self.__endAddress-self.__startAddress))
self.__SignatureHeader.append( "RANGE:\t{0:#08x}-{1:#08x}\n".format( self.__startAddress, self.__endAddress))
return self.__SignatureHeader
def GetRawBytes( self):
'''Return bytes representing the selection.'''
if not self.__RawBytes:
self.__RawBytes.append( "\nBYTES:\n{0}\n".format( ''.join( [ "{0:02x} ".format( ord( byte)) \
for byte in GetManyBytes( self.__startAddress, self.__endAddress-self.__startAddress, 0)]).strip()))
return self.__RawBytes
def GetDisassemblyText( self):
'''Return lines of disassembly representing the selection.'''
if not self.__DisassemblyText:
self.__DisassemblyText = ["\nDISASSEMBLY:\n"]
currea = self.__startAddress
while currea < self.__endAddress:
nextea = NextNotTail( currea)
self.__DisassemblyText.append( "{0}\n".format( GetDisasm( currea)))
currea = nextea
return self.__DisassemblyText
def GetLinesOfBytes( self):
'''Return lines of bytes representing lines of disassembly of the selection.'''
if not self.__LinesOfBytes:
self.__LinesOfBytes = ["\nBYTES PER LINE:\n"]
currea = self.__startAddress
while currea < self.__endAddress:
nextea = NextNotTail( currea)
lineLength = ItemSize( currea)
self.__LinesOfBytes.append( "{0}\n".format( ''.join( [ "{0:02x} ".format( ord( byte)) \
for byte in GetManyBytes( currea, lineLength, 0)])))
currea = nextea
return self.__LinesOfBytes
def GetWildCardedBytes( self):
'''Get bytes of selection and wildcard any IDA 'tails' if either operand represents a memory location.'''
if not self.__WildCardedBytes:
self.__WildCardedBytes = ["\nWILD-CARDED MEMORY BYTES:\n"]
currea = self.__startAddress
while currea < self.__endAddress:
nextea = NextNotTail( currea)
lineLength = ItemSize( currea)
currFlags = GetFlags( currea)
if isCode( currFlags):
self.__WildCardedBytes.append( "{0:02x} ".format( Byte( currea)))
op1Type = GetOpType( currea, 0)
op2Type = GetOpType( currea, 1)
varTypes = self.__CONFIG['OP_TYPES_TO_WILDCARD']
if op1Type in varTypes or op2Type in varTypes:
self.__WildCardedBytes.append('?? ' * int(lineLength-1))
elif lineLength > 1:
self.__WildCardedBytes.append( ''.join( [ "{0:02x} ".format( ord( byte)) \
for byte in GetManyBytes( currea+1, lineLength-1, 0)]))
elif isData( currFlags):
self.__WildCardedBytes.append( format( ''.join( [ "{0:02x} ".format( ord( byte)) \
for byte in GetManyBytes( currea, lineLength, 0)])))
currea = nextea
self.__WildCardedBytes.append( '\n')
return self.__WildCardedBytes
def GetYaraSignature( self):
'''Create a dummy yara signature containing the wildcarded byte string'''
if not self.__YaraSignature:
self.__YaraSignature = ['\nYARA SIGNATURE:\n']
self.__YaraSignature.append( "rule Sig\n{\n\tstrings:\n\t\t$hex_string = { ")
self.__YaraSignature.append( "{0}}}\n\tcondition:\n\t\t$hex_string\n}}\n".format( ''.join( [ line \
for line in self.GetWildCardedBytes()[1:]]).strip()))
return self.__YaraSignature
def GetSignature( self):
'''Returns signature text per self.__CONFIG'''
if not self.__Signature:
if self.__CONFIG['SHOW_HEADER']:
self.__Signature.extend( self.GetSignatureHeader())
if self.__CONFIG['SHOW_DISASSEMBLY_TEXT']:
self.__Signature.extend( self.GetDisassemblyText())
if self.__CONFIG['SHOW_RAW_BYTES']:
self.__Signature.extend( self.GetRawBytes())
if self.__CONFIG['SHOW_BYTES_PER_LINE']:
self.__Signature.extend( self.GetLinesOfBytes())
if self.__CONFIG['SHOW_WILD_CARDED_BYTES']:
self.__Signature.extend( self.GetWildCardedBytes())
if self.__CONFIG['SHOW_YARA_SIGNATURE']:
self.__Signature.extend( self.GetYaraSignature())
return ''.join( [ line for line in self.__Signature])
def Run( self):
self.__startAddress = SelStart()
self.__endAddress = SelEnd()
boundCheck = self.__CheckBounds()
if boundCheck is not self.__ERRORS['SUCCESS']:
return boundCheck
signature = self.GetSignature()
if self.__CONFIG['SHOW_IN_OUTPUT_WINDOW']:
sys.stdout.write( signature)
if self.__CONFIG['PROMPT_TO_SAVE']:
from PySide import QtGui
saveFilePath = QtGui.QFileDialog.getSaveFileName( None,"Save YARA Signature To:")[0]
if saveFilePath:
try:
fp = open( saveFilePath, 'wb')
fp.write( signature)
fp.close()
except Exception, ex:
sys.stderr.write( str( ex))
return self.__ERRORS['BADFILE']
else:
sys.stderr.write('No file selected.\n')
return self.__ERRORS['SUCCESS']
if __name__ == "__main__":
script = SignatureCreator()
script.Run()