|
35 | 35 | import idaapi |
36 | 36 |
|
37 | 37 | class SignatureCreator( object): |
38 | | - '''Creates signature information for a selection''' |
39 | | - def __init__( self): |
40 | | - self.__ERRORS = { |
41 | | - 'SUCCESS' : 0, # Everything's great |
42 | | - 'BADADDR' : 1, # A selection wasn't made |
43 | | - 'BADFILE' : 2, # Something bad happend whilst writing the signature file |
44 | | - } |
| 38 | + '''Creates signature information for a selection''' |
| 39 | + def __init__( self): |
| 40 | + self.__ERRORS = { |
| 41 | + 'SUCCESS' : 0, # Everything's great |
| 42 | + 'BADADDR' : 1, # A selection wasn't made |
| 43 | + 'BADFILE' : 2, # Something bad happend whilst writing the signature file |
| 44 | + } |
| 45 | + |
| 46 | + self.__CONFIG = { |
| 47 | + 'SHOW_IN_OUTPUT_WINDOW' : True, |
| 48 | + 'PROMPT_TO_SAVE' : False, |
| 49 | + 'SHOW_HEADER' : True, |
| 50 | + 'SHOW_DISASSEMBLY_TEXT' : True, |
| 51 | + 'SHOW_RAW_BYTES' : True, |
| 52 | + 'SHOW_WILD_CARDED_BYTES' : True, |
| 53 | + 'SHOW_BYTES_PER_LINE' : True, |
| 54 | + 'SHOW_YARA_SIGNATURE' : True, |
| 55 | + 'OP_TYPES_TO_WILDCARD' : [o_mem,o_phrase, o_displ,o_far,o_near] |
| 56 | + } |
| 57 | + self.__SignatureHeader = [] |
| 58 | + self.__RawBytes = [] |
| 59 | + self.__DisassemblyText = [] |
| 60 | + self.__LinesOfBytes = [] |
| 61 | + self.__WildCardedBytes = [] |
| 62 | + self.__YaraSignature = [] |
| 63 | + self.__Signature = [] |
| 64 | + def __CheckBounds( self): |
| 65 | + '''Check to see if a selection was made.''' |
| 66 | + if self.__startAddress is BADADDR or self.__endAddress is BADADDR: |
| 67 | + sys.stderr.write( "Please select the section you would like to create a signature on.") |
| 68 | + return self.__ERRORS['BADADDR'] |
| 69 | + else: |
| 70 | + return self.__ERRORS['SUCCESS'] |
| 71 | + def GetSignatureHeader( self): |
| 72 | + '''Returns signature header information.''' |
| 73 | + if not self.__SignatureHeader: |
| 74 | + self.__SignatureHeader.append( "\n\n[SIGNATURE FOR {0}]\n".format( GetInputFilePath())) |
| 75 | + self.__SignatureHeader.append( "\nLENGTH:\t{0:#x}\n".format( self.__endAddress-self.__startAddress)) |
| 76 | + self.__SignatureHeader.append( "RANGE:\t{0:#08x}-{1:#08x}\n".format( self.__startAddress, self.__endAddress)) |
| 77 | + return self.__SignatureHeader |
| 78 | + def GetRawBytes( self): |
| 79 | + '''Return bytes representing the selection.''' |
| 80 | + if not self.__RawBytes: |
| 81 | + self.__RawBytes.append( "\nBYTES:\n{0}\n".format( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
| 82 | + for byte in GetManyBytes( self.__startAddress, self.__endAddress-self.__startAddress, 0)]).strip())) |
| 83 | + return self.__RawBytes |
| 84 | + def GetDisassemblyText( self): |
| 85 | + '''Return lines of disassembly representing the selection.''' |
| 86 | + if not self.__DisassemblyText: |
| 87 | + self.__DisassemblyText = ["\nDISASSEMBLY:\n"] |
| 88 | + currea = self.__startAddress |
| 89 | + while currea < self.__endAddress: |
| 90 | + nextea = NextNotTail( currea) |
| 91 | + self.__DisassemblyText.append( "{0}\n".format( GetDisasm( currea))) |
| 92 | + currea = nextea |
| 93 | + return self.__DisassemblyText |
| 94 | + def GetLinesOfBytes( self): |
| 95 | + '''Return lines of bytes representing lines of disassembly of the selection.''' |
| 96 | + if not self.__LinesOfBytes: |
| 97 | + self.__LinesOfBytes = ["\nBYTES PER LINE:\n"] |
| 98 | + currea = self.__startAddress |
| 99 | + while currea < self.__endAddress: |
| 100 | + nextea = NextNotTail( currea) |
| 101 | + lineLength = ItemSize( currea) |
| 102 | + self.__LinesOfBytes.append( "{0}\n".format( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
| 103 | + for byte in GetManyBytes( currea, lineLength, 0)]))) |
| 104 | + currea = nextea |
| 105 | + return self.__LinesOfBytes |
| 106 | + def GetWildCardedBytes( self): |
| 107 | + '''Get bytes of selection and wildcard any IDA 'tails' if either operand represents a memory location.''' |
| 108 | + if not self.__WildCardedBytes: |
| 109 | + self.__WildCardedBytes = ["\nWILD-CARDED MEMORY BYTES:\n"] |
| 110 | + currea = self.__startAddress |
| 111 | + while currea < self.__endAddress: |
| 112 | + nextea = NextNotTail( currea) |
| 113 | + lineLength = ItemSize( currea) |
| 114 | + currFlags = GetFlags( currea) |
| 115 | + if isCode( currFlags): |
| 116 | + self.__WildCardedBytes.append( "{0:02x} ".format( Byte( currea))) |
| 117 | + op1Type = GetOpType( currea, 0) |
| 118 | + op2Type = GetOpType( currea, 1) |
| 119 | + varTypes = self.__CONFIG['OP_TYPES_TO_WILDCARD'] |
| 120 | + if op1Type in varTypes or op2Type in varTypes: |
| 121 | + self.__WildCardedBytes.append('?? ' * int(lineLength-1)) |
| 122 | + elif lineLength > 1: |
| 123 | + self.__WildCardedBytes.append( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
| 124 | + for byte in GetManyBytes( currea+1, lineLength-1, 0)])) |
| 125 | + elif isData( currFlags): |
| 126 | + self.__WildCardedBytes.append( format( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
| 127 | + for byte in GetManyBytes( currea, lineLength, 0)]))) |
| 128 | + currea = nextea |
| 129 | + self.__WildCardedBytes.append( '\n') |
| 130 | + return self.__WildCardedBytes |
| 131 | + def GetYaraSignature( self): |
| 132 | + '''Create a dummy yara signature containing the wildcarded byte string''' |
| 133 | + if not self.__YaraSignature: |
| 134 | + self.__YaraSignature = ['\nYARA SIGNATURE:\n'] |
| 135 | + self.__YaraSignature.append( "rule Sig\n{\n\tstrings:\n\t\t$hex_string = { ") |
| 136 | + self.__YaraSignature.append( "{0}}}\n\tcondition:\n\t\t$hex_string\n}}\n".format( ''.join( [ line \ |
| 137 | + for line in self.GetWildCardedBytes()[1:]]).strip())) |
| 138 | + return self.__YaraSignature |
| 139 | + def GetSignature( self): |
| 140 | + '''Returns signature text per self.__CONFIG''' |
| 141 | + if not self.__Signature: |
| 142 | + if self.__CONFIG['SHOW_HEADER']: |
| 143 | + self.__Signature.extend( self.GetSignatureHeader()) |
| 144 | + if self.__CONFIG['SHOW_DISASSEMBLY_TEXT']: |
| 145 | + self.__Signature.extend( self.GetDisassemblyText()) |
| 146 | + if self.__CONFIG['SHOW_RAW_BYTES']: |
| 147 | + self.__Signature.extend( self.GetRawBytes()) |
| 148 | + if self.__CONFIG['SHOW_BYTES_PER_LINE']: |
| 149 | + self.__Signature.extend( self.GetLinesOfBytes()) |
| 150 | + if self.__CONFIG['SHOW_WILD_CARDED_BYTES']: |
| 151 | + self.__Signature.extend( self.GetWildCardedBytes()) |
| 152 | + if self.__CONFIG['SHOW_YARA_SIGNATURE']: |
| 153 | + self.__Signature.extend( self.GetYaraSignature()) |
| 154 | + return ''.join( [ line for line in self.__Signature]) |
| 155 | + def Run( self): |
| 156 | + self.__startAddress = SelStart() |
| 157 | + self.__endAddress = SelEnd() |
| 158 | + boundCheck = self.__CheckBounds() |
| 159 | + if boundCheck is not self.__ERRORS['SUCCESS']: |
| 160 | + return boundCheck |
| 161 | + signature = self.GetSignature() |
| 162 | + if self.__CONFIG['SHOW_IN_OUTPUT_WINDOW']: |
| 163 | + sys.stdout.write( signature) |
| 164 | + if self.__CONFIG['PROMPT_TO_SAVE']: |
| 165 | + from PySide import QtGui |
| 166 | + saveFilePath = QtGui.QFileDialog.getSaveFileName( None,"Save YARA Signature To:")[0] |
| 167 | + if saveFilePath: |
| 168 | + try: |
| 169 | + fp = open( saveFilePath, 'wb') |
| 170 | + fp.write( signature) |
| 171 | + fp.close() |
| 172 | + except Exception, ex: |
| 173 | + sys.stderr.write( str( ex)) |
| 174 | + return self.__ERRORS['BADFILE'] |
| 175 | + else: |
| 176 | + sys.stderr.write('No file selected.\n') |
| 177 | + return self.__ERRORS['SUCCESS'] |
45 | 178 |
|
46 | | - self.__CONFIG = { |
47 | | - 'SHOW_IN_OUTPUT_WINDOW' : True, |
48 | | - 'PROMPT_TO_SAVE' : False, |
49 | | - 'SHOW_HEADER' : True, |
50 | | - 'SHOW_DISASSEMBLY_TEXT' : True, |
51 | | - 'SHOW_RAW_BYTES' : True, |
52 | | - 'SHOW_WILD_CARDED_BYTES' : True, |
53 | | - 'SHOW_BYTES_PER_LINE' : True, |
54 | | - 'SHOW_YARA_SIGNATURE' : True, |
55 | | - 'OP_TYPES_TO_WILDCARD' : [o_mem,o_phrase, o_displ,o_far,o_near] |
56 | | - } |
57 | | - self.__SignatureHeader = [] |
58 | | - self.__RawBytes = [] |
59 | | - self.__DisassemblyText = [] |
60 | | - self.__LinesOfBytes = [] |
61 | | - self.__WildCardedBytes = [] |
62 | | - self.__YaraSignature = [] |
63 | | - self.__Signature = [] |
64 | | - def __CheckBounds( self): |
65 | | - '''Check to see if a selection was made.''' |
66 | | - if self.__startAddress is BADADDR or self.__endAddress is BADADDR: |
67 | | - sys.stderr.write( "Please select the section you would like to create a signature on.") |
68 | | - return self.__ERRORS['BADADDR'] |
69 | | - else: |
70 | | - return self.__ERRORS['SUCCESS'] |
71 | | - def GetSignatureHeader( self): |
72 | | - '''Returns signature header information.''' |
73 | | - if not self.__SignatureHeader: |
74 | | - self.__SignatureHeader.append( "\n\n[SIGNATURE FOR {0}]\n".format( GetInputFilePath())) |
75 | | - self.__SignatureHeader.append( "\nLENGTH:\t{0:#x}\n".format( self.__endAddress-self.__startAddress)) |
76 | | - self.__SignatureHeader.append( "RANGE:\t{0:#08x}-{1:#08x}\n".format( self.__startAddress, self.__endAddress)) |
77 | | - return self.__SignatureHeader |
78 | | - def GetRawBytes( self): |
79 | | - '''Return bytes representing the selection.''' |
80 | | - if not self.__RawBytes: |
81 | | - self.__RawBytes.append( "\nBYTES:\n{0}\n".format( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
82 | | - for byte in GetManyBytes( self.__startAddress, self.__endAddress-self.__startAddress, 0)]).strip())) |
83 | | - return self.__RawBytes |
84 | | - def GetDisassemblyText( self): |
85 | | - '''Return lines of disassembly representing the selection.''' |
86 | | - if not self.__DisassemblyText: |
87 | | - self.__DisassemblyText = ["\nDISASSEMBLY:\n"] |
88 | | - currea = self.__startAddress |
89 | | - while currea < self.__endAddress: |
90 | | - nextea = NextNotTail( currea) |
91 | | - self.__DisassemblyText.append( "{0}\n".format( GetDisasm( currea))) |
92 | | - currea = nextea |
93 | | - return self.__DisassemblyText |
94 | | - def GetLinesOfBytes( self): |
95 | | - '''Return lines of bytes representing lines of disassembly of the selection.''' |
96 | | - if not self.__LinesOfBytes: |
97 | | - self.__LinesOfBytes = ["\nBYTES PER LINE:\n"] |
98 | | - currea = self.__startAddress |
99 | | - while currea < self.__endAddress: |
100 | | - nextea = NextNotTail( currea) |
101 | | - lineLength = ItemSize( currea) |
102 | | - self.__LinesOfBytes.append( "{0}\n".format( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
103 | | - for byte in GetManyBytes( currea, lineLength, 0)]))) |
104 | | - currea = nextea |
105 | | - return self.__LinesOfBytes |
106 | | - def GetWildCardedBytes( self): |
107 | | - '''Get bytes of selection and wildcard any IDA 'tails' if either operand represents a memory location.''' |
108 | | - if not self.__WildCardedBytes: |
109 | | - self.__WildCardedBytes = ["\nWILD-CARDED MEMORY BYTES:\n"] |
110 | | - currea = self.__startAddress |
111 | | - while currea < self.__endAddress: |
112 | | - nextea = NextNotTail( currea) |
113 | | - lineLength = ItemSize( currea) |
114 | | - currFlags = GetFlags( currea) |
115 | | - if isCode( currFlags): |
116 | | - self.__WildCardedBytes.append( "{0:02x} ".format( Byte( currea))) |
117 | | - op1Type = GetOpType( currea, 0) |
118 | | - op2Type = GetOpType( currea, 1) |
119 | | - varTypes = self.__CONFIG['OP_TYPES_TO_WILDCARD'] |
120 | | - if op1Type in varTypes or op2Type in varTypes: |
121 | | - self.__WildCardedBytes.append('?? ' * int(lineLength-1)) |
122 | | - elif lineLength > 1: |
123 | | - self.__WildCardedBytes.append( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
124 | | - for byte in GetManyBytes( currea+1, lineLength-1, 0)])) |
125 | | - elif isData( currFlags): |
126 | | - self.__WildCardedBytes.append( format( ''.join( [ "{0:02x} ".format( ord( byte)) \ |
127 | | - for byte in GetManyBytes( currea, lineLength, 0)]))) |
128 | | - currea = nextea |
129 | | - self.__WildCardedBytes.append( '\n') |
130 | | - return self.__WildCardedBytes |
131 | | - def GetYaraSignature( self): |
132 | | - '''Create a dummy yara signature containing the wildcarded byte string''' |
133 | | - if not self.__YaraSignature: |
134 | | - self.__YaraSignature = ['\nYARA SIGNATURE:\n'] |
135 | | - self.__YaraSignature.append( "rule Sig\n{\n\tstrings:\n\t\t$hex_string = { ") |
136 | | - self.__YaraSignature.append( "{0}}}\n\tcondition:\n\t\t$hex_string\n}}\n".format( ''.join( [ line \ |
137 | | - for line in self.GetWildCardedBytes()[1:]]).strip())) |
138 | | - return self.__YaraSignature |
139 | | - def GetSignature( self): |
140 | | - '''Returns signature text per self.__CONFIG''' |
141 | | - if not self.__Signature: |
142 | | - if self.__CONFIG['SHOW_HEADER']: |
143 | | - self.__Signature.extend( self.GetSignatureHeader()) |
144 | | - if self.__CONFIG['SHOW_DISASSEMBLY_TEXT']: |
145 | | - self.__Signature.extend( self.GetDisassemblyText()) |
146 | | - if self.__CONFIG['SHOW_RAW_BYTES']: |
147 | | - self.__Signature.extend( self.GetRawBytes()) |
148 | | - if self.__CONFIG['SHOW_BYTES_PER_LINE']: |
149 | | - self.__Signature.extend( self.GetLinesOfBytes()) |
150 | | - if self.__CONFIG['SHOW_WILD_CARDED_BYTES']: |
151 | | - self.__Signature.extend( self.GetWildCardedBytes()) |
152 | | - if self.__CONFIG['SHOW_YARA_SIGNATURE']: |
153 | | - self.__Signature.extend( self.GetYaraSignature()) |
154 | | - return ''.join( [ line for line in self.__Signature]) |
155 | | - def Run( self): |
156 | | - self.__startAddress = SelStart() |
157 | | - self.__endAddress = SelEnd() |
158 | | - boundCheck = self.__CheckBounds() |
159 | | - if boundCheck is not self.__ERRORS['SUCCESS']: |
160 | | - return boundCheck |
161 | | - signature = self.GetSignature() |
162 | | - if self.__CONFIG['SHOW_IN_OUTPUT_WINDOW']: |
163 | | - sys.stdout.write( signature) |
164 | | - if self.__CONFIG['PROMPT_TO_SAVE']: |
165 | | - from PySide import QtGui |
166 | | - saveFilePath = QtGui.QFileDialog.getSaveFileName( None,"Save YARA Signature To:")[0] |
167 | | - if saveFilePath: |
168 | | - try: |
169 | | - fp = open( saveFilePath, 'wb') |
170 | | - fp.write( signature) |
171 | | - fp.close() |
172 | | - except Exception, ex: |
173 | | - sys.stderr.write( str( ex)) |
174 | | - return self.__ERRORS['BADFILE'] |
175 | | - else: |
176 | | - sys.stderr.write('No file selected.\n') |
177 | | - return self.__ERRORS['SUCCESS'] |
178 | | - |
179 | 179 | if __name__ == "__main__": |
180 | | - script = SignatureCreator() |
181 | | - script.Run() |
| 180 | + script = SignatureCreator() |
| 181 | + script.Run() |
0 commit comments