assemblyline-v4-service 4.4.0.24__py3-none-any.whl → 4.4.0.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of assemblyline-v4-service might be problematic. Click here for more details.
- assemblyline_v4_service/VERSION +1 -1
- assemblyline_v4_service/common/api.py +3 -2
- assemblyline_v4_service/common/base.py +3 -4
- assemblyline_v4_service/common/helper.py +1 -2
- assemblyline_v4_service/common/{extractor/ocr.py → ocr.py} +0 -1
- assemblyline_v4_service/common/ontology_helper.py +7 -8
- assemblyline_v4_service/common/request.py +4 -5
- assemblyline_v4_service/common/result.py +3 -3
- assemblyline_v4_service/common/task.py +3 -3
- assemblyline_v4_service/common/utils.py +2 -2
- assemblyline_v4_service/updater/helper.py +4 -0
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/METADATA +1 -1
- assemblyline_v4_service-4.4.0.26.dist-info/RECORD +28 -0
- assemblyline_v4_service/common/balbuzard/__init__.py +0 -0
- assemblyline_v4_service/common/balbuzard/balbuzard.py +0 -656
- assemblyline_v4_service/common/balbuzard/bbcrack.py +0 -830
- assemblyline_v4_service/common/balbuzard/patterns.py +0 -650
- assemblyline_v4_service/common/dynamic_service_helper.py +0 -3631
- assemblyline_v4_service/common/extractor/__init__.py +0 -1
- assemblyline_v4_service/common/extractor/base64.py +0 -86
- assemblyline_v4_service/common/extractor/pe_file.py +0 -51
- assemblyline_v4_service/common/icap.py +0 -149
- assemblyline_v4_service/common/keytool_parse.py +0 -66
- assemblyline_v4_service/common/pestudio/__init__.py +0 -0
- assemblyline_v4_service/common/pestudio/xml/__init__.py +0 -0
- assemblyline_v4_service/common/pestudio/xml/features.xml +0 -5607
- assemblyline_v4_service/common/pestudio/xml/functions.xml +0 -5824
- assemblyline_v4_service/common/pestudio/xml/languages.xml +0 -375
- assemblyline_v4_service/common/pestudio/xml/resources.xml +0 -511
- assemblyline_v4_service/common/pestudio/xml/signatures.xml +0 -29105
- assemblyline_v4_service/common/pestudio/xml/strings.xml +0 -2379
- assemblyline_v4_service/common/safelist_helper.py +0 -73
- assemblyline_v4_service/common/section_reducer.py +0 -43
- assemblyline_v4_service/common/tag_helper.py +0 -117
- assemblyline_v4_service/common/tag_reducer.py +0 -242
- assemblyline_v4_service/testing/__init__.py +0 -0
- assemblyline_v4_service/testing/helper.py +0 -463
- assemblyline_v4_service/testing/regenerate_results.py +0 -37
- assemblyline_v4_service-4.4.0.24.dist-info/RECORD +0 -53
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/LICENCE.md +0 -0
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/WHEEL +0 -0
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/top_level.txt +0 -0
|
@@ -1,830 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env python2
|
|
2
|
-
"""
|
|
3
|
-
2016-10-21:
|
|
4
|
-
Modified version of bbcrack application for AL, original code found here:
|
|
5
|
-
https://github.com/decalage2/balbuzard
|
|
6
|
-
"""
|
|
7
|
-
"""
|
|
8
|
-
bbcrack - v0.14 2014-05-22 Philippe Lagadec
|
|
9
|
-
|
|
10
|
-
bbcrack is a tool to crack malware obfuscation such as XOR, ROL, ADD (and
|
|
11
|
-
many combinations), by bruteforcing all possible keys and and checking for
|
|
12
|
-
specific patterns (IP addresses, domain names, URLs, known file headers and
|
|
13
|
-
strings, etc) using the balbuzard engine.
|
|
14
|
-
It is part of the Balbuzard package.
|
|
15
|
-
|
|
16
|
-
For more info and updates: http://www.decalage.info/balbuzard
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
# LICENSE:
|
|
20
|
-
#
|
|
21
|
-
# bbcrack is copyright (c) 2013-2014, Philippe Lagadec (http://www.decalage.info)
|
|
22
|
-
# All rights reserved.
|
|
23
|
-
#
|
|
24
|
-
# Redistribution and use in source and binary forms, with or without modification,
|
|
25
|
-
# are permitted provided that the following conditions are met:
|
|
26
|
-
#
|
|
27
|
-
# * Redistributions of source code must retain the above copyright notice, this
|
|
28
|
-
# list of conditions and the following disclaimer.
|
|
29
|
-
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
30
|
-
# this list of conditions and the following disclaimer in the documentation
|
|
31
|
-
# and/or other materials provided with the distribution.
|
|
32
|
-
#
|
|
33
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
34
|
-
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
35
|
-
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
36
|
-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
37
|
-
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
38
|
-
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
39
|
-
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
40
|
-
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
41
|
-
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
42
|
-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
__version__ = '0.13'
|
|
46
|
-
|
|
47
|
-
# --- IMPORTS ------------------------------------------------------------------
|
|
48
|
-
|
|
49
|
-
from assemblyline_v4_service.common.balbuzard.balbuzard import Balbuzard
|
|
50
|
-
from assemblyline_v4_service.common.balbuzard.patterns import PatternMatch
|
|
51
|
-
|
|
52
|
-
#--- CLASSES ------------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
class Transform_string (object):
|
|
55
|
-
"""
|
|
56
|
-
Generic class to define a transform that acts on a string globally.
|
|
57
|
-
"""
|
|
58
|
-
# generic name and id for the class:
|
|
59
|
-
gen_name = 'Generic String Transform'
|
|
60
|
-
gen_id = 'string'
|
|
61
|
-
|
|
62
|
-
def __init__(self, params=None):
|
|
63
|
-
"""
|
|
64
|
-
constructor for the Transform object.
|
|
65
|
-
This method needs to be overloaded for every specific Transform.
|
|
66
|
-
It should set name and shortname according to the provided parameters.
|
|
67
|
-
(for example shortname="xor_17" for a XOR transform with params=17)
|
|
68
|
-
params: single value or tuple of values, parameters for the transformation
|
|
69
|
-
"""
|
|
70
|
-
self.name = 'Undefined String Transform'
|
|
71
|
-
self.shortname = 'undefined_string'
|
|
72
|
-
self.params = params
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def transform_string (self, data):
|
|
76
|
-
"""
|
|
77
|
-
Method to be overloaded, only for a transform that acts on a string
|
|
78
|
-
globally.
|
|
79
|
-
This method should apply the transform to the data string, using params
|
|
80
|
-
as parameters, and return the transformed data as a string.
|
|
81
|
-
(the resulting string does not need to have the same length as data)
|
|
82
|
-
"""
|
|
83
|
-
raise NotImplementedError
|
|
84
|
-
|
|
85
|
-
@staticmethod
|
|
86
|
-
def iter_params ():
|
|
87
|
-
"""
|
|
88
|
-
Method to be overloaded.
|
|
89
|
-
This static method should iterate over all possible parameters for the
|
|
90
|
-
transform function, yielding each set of parameters as a single value
|
|
91
|
-
or a tuple of values.
|
|
92
|
-
(for example for a XOR transform, it should yield 1 to 255)
|
|
93
|
-
This method should be used on the Transform class in order to
|
|
94
|
-
instantiate a Transform object with each set of parameters.
|
|
95
|
-
"""
|
|
96
|
-
raise NotImplementedError
|
|
97
|
-
|
|
98
|
-
## def iter_transform (self, data):
|
|
99
|
-
## """
|
|
100
|
-
## Runs the transform on data for all possible values of the parameters,
|
|
101
|
-
## and yields the transformed data for each possibility.
|
|
102
|
-
## """
|
|
103
|
-
## for params in self.iter_params():
|
|
104
|
-
## #print self.name
|
|
105
|
-
## yield self.transform_string(data, params)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class Transform_char (Transform_string):
|
|
109
|
-
"""
|
|
110
|
-
Generic class to define a transform that acts on each character of a string
|
|
111
|
-
separately.
|
|
112
|
-
"""
|
|
113
|
-
# generic name for the class:
|
|
114
|
-
gen_name = 'Generic Character Transform'
|
|
115
|
-
gen_id = 'char'
|
|
116
|
-
|
|
117
|
-
def __init__(self, params=None):
|
|
118
|
-
"""
|
|
119
|
-
constructor for the Transform object.
|
|
120
|
-
This method needs to be overloaded for every specific Transform.
|
|
121
|
-
It should set name and shortname according to the provided parameters.
|
|
122
|
-
(for example shortname="xor_17" for a XOR transform with params=17)
|
|
123
|
-
params: single value or tuple of values, parameters for the transformation
|
|
124
|
-
"""
|
|
125
|
-
self.name = 'Undefined Character Transform'
|
|
126
|
-
self.shortname = 'undefined_char'
|
|
127
|
-
self.params = params
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def transform_string (self, data):
|
|
131
|
-
"""
|
|
132
|
-
This method applies the transform to the data string, using params
|
|
133
|
-
as parameters, and return the transformed data as a string.
|
|
134
|
-
Here, each character is transformed separately by calling transform_char.
|
|
135
|
-
A translation table is used to speed up the processing.
|
|
136
|
-
(the resulting string should have the same length as data)
|
|
137
|
-
"""
|
|
138
|
-
# for optimal speed, we build a translation table:
|
|
139
|
-
self.trans_table = bytearray()
|
|
140
|
-
for i in range(256):
|
|
141
|
-
self.trans_table.append(self.transform_int(i))
|
|
142
|
-
return data.translate(self.trans_table)
|
|
143
|
-
|
|
144
|
-
def transform_char (self, char):
|
|
145
|
-
"""
|
|
146
|
-
Method that can be overloaded, only for a transform that acts on a character.
|
|
147
|
-
This method should apply the transform to the provided char, using params
|
|
148
|
-
as parameters, and return the transformed data as a character.
|
|
149
|
-
NOTE: it is usually simpler to overload transform_int and leave this one
|
|
150
|
-
untouched.
|
|
151
|
-
(here character = string of length 1)
|
|
152
|
-
"""
|
|
153
|
-
# by default, call transform_int using ord(char), and convert it back
|
|
154
|
-
# to a single character:
|
|
155
|
-
return chr(self.transform_int(ord(char)))
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def transform_int (self, i):
|
|
159
|
-
"""
|
|
160
|
-
Method to be overloaded, only for a transform that acts on a character.
|
|
161
|
-
This method should apply the transform to the provided integer which is
|
|
162
|
-
the ASCII code of a character (i.e. ord(c)), using params
|
|
163
|
-
as parameters, and return the transformed data as an integer.
|
|
164
|
-
(here character = string of length 1)
|
|
165
|
-
"""
|
|
166
|
-
raise NotImplementedError
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
#--- TRANSFORMS ---------------------------------------------------------------
|
|
170
|
-
|
|
171
|
-
class Transform_identity (Transform_string):
|
|
172
|
-
"""
|
|
173
|
-
Transform that does not change data.
|
|
174
|
-
"""
|
|
175
|
-
# generic name for the class:
|
|
176
|
-
gen_name = 'Identity Transformation, no change to data. Parameters: none.'
|
|
177
|
-
gen_id = 'identity'
|
|
178
|
-
|
|
179
|
-
def __init__(self, params=None):
|
|
180
|
-
self.name = self.gen_name
|
|
181
|
-
self.shortname = self.gen_id
|
|
182
|
-
self.params = None
|
|
183
|
-
|
|
184
|
-
def transform_string (self, data):
|
|
185
|
-
return data
|
|
186
|
-
|
|
187
|
-
@staticmethod
|
|
188
|
-
def iter_params ():
|
|
189
|
-
yield None
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
#------------------------------------------------------------------------------
|
|
193
|
-
class Transform_XOR (Transform_char):
|
|
194
|
-
"""
|
|
195
|
-
XOR Transform
|
|
196
|
-
"""
|
|
197
|
-
# generic name for the class:
|
|
198
|
-
gen_name = 'XOR with 8 bits static key A. Parameters: A (1-FF).'
|
|
199
|
-
gen_id = 'xor'
|
|
200
|
-
|
|
201
|
-
def __init__(self, params):
|
|
202
|
-
assert isinstance(params, int)
|
|
203
|
-
assert params>0 and params<256
|
|
204
|
-
self.params = params
|
|
205
|
-
self.name = "XOR %02X" % params
|
|
206
|
-
self.shortname = "xor%02X" % params
|
|
207
|
-
|
|
208
|
-
def transform_int (self, i):
|
|
209
|
-
# here params is an integer
|
|
210
|
-
return i ^ self.params
|
|
211
|
-
|
|
212
|
-
@staticmethod
|
|
213
|
-
def iter_params ():
|
|
214
|
-
# the XOR key can be 1 to 255 (0 would be identity)
|
|
215
|
-
for key in range(1,256):
|
|
216
|
-
yield key
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
#------------------------------------------------------------------------------
|
|
220
|
-
class Transform_XOR_INC (Transform_string):
|
|
221
|
-
"""
|
|
222
|
-
XOR Transform, with incrementing key
|
|
223
|
-
"""
|
|
224
|
-
# generic name for the class:
|
|
225
|
-
gen_name = 'XOR with 8 bits key A incrementing after each character. Parameters: A (0-FF).'
|
|
226
|
-
gen_id = 'xor_inc'
|
|
227
|
-
|
|
228
|
-
def __init__(self, params):
|
|
229
|
-
assert isinstance(params, int)
|
|
230
|
-
assert params>=0 and params<256
|
|
231
|
-
self.params = params
|
|
232
|
-
self.name = "XOR %02X INC" % params
|
|
233
|
-
self.shortname = "xor%02X_inc" % params
|
|
234
|
-
|
|
235
|
-
def transform_string (self, data):
|
|
236
|
-
# here params is an integer
|
|
237
|
-
#TODO: use a list comprehension + join to get better performance
|
|
238
|
-
# this loop is more readable, but likely to be much slower
|
|
239
|
-
out = ''
|
|
240
|
-
for i in range(len(data)):
|
|
241
|
-
xor_key = (self.params + i) & 0xFF
|
|
242
|
-
out += chr(ord(data[i]) ^ xor_key)
|
|
243
|
-
return out
|
|
244
|
-
|
|
245
|
-
@staticmethod
|
|
246
|
-
def iter_params ():
|
|
247
|
-
# the XOR key can be 0 to 255 (0 is not identity here)
|
|
248
|
-
for xor_key in range(0,256):
|
|
249
|
-
yield xor_key
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
#------------------------------------------------------------------------------
|
|
253
|
-
class Transform_XOR_DEC (Transform_string):
|
|
254
|
-
"""
|
|
255
|
-
XOR Transform, with decrementing key
|
|
256
|
-
"""
|
|
257
|
-
# generic name for the class:
|
|
258
|
-
gen_name = 'XOR with 8 bits key A decrementing after each character. Parameters: A (0-FF).'
|
|
259
|
-
gen_id = 'xor_dec'
|
|
260
|
-
|
|
261
|
-
def __init__(self, params):
|
|
262
|
-
assert isinstance(params, int)
|
|
263
|
-
assert params>=0 and params<256
|
|
264
|
-
self.params = params
|
|
265
|
-
self.name = "XOR %02X DEC" % params
|
|
266
|
-
self.shortname = "xor%02X_dec" % params
|
|
267
|
-
|
|
268
|
-
def transform_string (self, data):
|
|
269
|
-
# here params is an integer
|
|
270
|
-
#TODO: use a list comprehension + join to get better performance
|
|
271
|
-
# this loop is more readable, but likely to be much slower
|
|
272
|
-
out = ''
|
|
273
|
-
for i in range(len(data)):
|
|
274
|
-
xor_key = (self.params + 0xFF - i) & 0xFF
|
|
275
|
-
out += chr(ord(data[i]) ^ xor_key)
|
|
276
|
-
return out
|
|
277
|
-
|
|
278
|
-
@staticmethod
|
|
279
|
-
def iter_params ():
|
|
280
|
-
# the XOR key can be 0 to 255 (0 is not identity here)
|
|
281
|
-
for xor_key in range(0,256):
|
|
282
|
-
yield xor_key
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
#------------------------------------------------------------------------------
|
|
286
|
-
class Transform_XOR_INC_ROL (Transform_string):
|
|
287
|
-
"""
|
|
288
|
-
XOR Transform, with incrementing key, then ROL N bits
|
|
289
|
-
"""
|
|
290
|
-
# generic name for the class:
|
|
291
|
-
gen_name = 'XOR with 8 bits key A incrementing after each character, then rotate B bits left. Parameters: A (0-FF), B (1-7).'
|
|
292
|
-
gen_id = 'xor_inc_rol'
|
|
293
|
-
|
|
294
|
-
def __init__(self, params):
|
|
295
|
-
self.params = params
|
|
296
|
-
self.name = "XOR %02X INC then ROL %d" % params
|
|
297
|
-
self.shortname = "xor%02X_inc_rol%d" % params
|
|
298
|
-
|
|
299
|
-
def transform_char (self, char):
|
|
300
|
-
# here params is a tuple
|
|
301
|
-
xor_key, rol_bits = self.params
|
|
302
|
-
return chr(rol(ord(char) ^ xor_key, rol_bits))
|
|
303
|
-
|
|
304
|
-
def transform_string (self, data):
|
|
305
|
-
# here params is a tuple
|
|
306
|
-
#TODO: use a list comprehension + join to get better performance
|
|
307
|
-
# this loop is more readable, but likely to be much slower
|
|
308
|
-
xor_key_init, rol_bits = self.params
|
|
309
|
-
out = ''
|
|
310
|
-
for i in range(len(data)):
|
|
311
|
-
xor_key = (xor_key_init + i) & 0xFF
|
|
312
|
-
out += chr(rol(ord(data[i]) ^ xor_key, rol_bits))
|
|
313
|
-
return out
|
|
314
|
-
|
|
315
|
-
@staticmethod
|
|
316
|
-
def iter_params ():
|
|
317
|
-
"return (XOR key, ROL bits)"
|
|
318
|
-
# the XOR key can be 0 to 255 (0 is not identity here)
|
|
319
|
-
for xor_key in range(0,256):
|
|
320
|
-
# the ROL bits can be 1 to 7:
|
|
321
|
-
for rol_bits in range(1,8):
|
|
322
|
-
yield (xor_key, rol_bits)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
#------------------------------------------------------------------------------
|
|
326
|
-
class Transform_SUB_INC (Transform_string):
|
|
327
|
-
"""
|
|
328
|
-
SUB Transform, with incrementing key
|
|
329
|
-
"""
|
|
330
|
-
# generic name for the class:
|
|
331
|
-
gen_name = 'SUB with 8 bits key A incrementing after each character. Parameters: A (0-FF).'
|
|
332
|
-
gen_id = 'sub_inc'
|
|
333
|
-
|
|
334
|
-
def __init__(self, params):
|
|
335
|
-
assert isinstance(params, int)
|
|
336
|
-
assert params>=0 and params<256
|
|
337
|
-
self.params = params
|
|
338
|
-
self.name = "SUB %02X INC" % params
|
|
339
|
-
self.shortname = "sub%02X_inc" % params
|
|
340
|
-
|
|
341
|
-
def transform_string (self, data):
|
|
342
|
-
# here params is an integer
|
|
343
|
-
#TODO: use a list comprehension + join to get better performance
|
|
344
|
-
# this loop is more readable, but likely to be much slower
|
|
345
|
-
out = bytearray()
|
|
346
|
-
for i in range(len(data)):
|
|
347
|
-
key = (self.params + i) & 0xFF
|
|
348
|
-
out.append((ord(data[i]) - key) & 0xFF)
|
|
349
|
-
return out
|
|
350
|
-
|
|
351
|
-
@staticmethod
|
|
352
|
-
def iter_params ():
|
|
353
|
-
# the SUB key can be 0 to 255 (0 is not identity here)
|
|
354
|
-
for key in range(0,256):
|
|
355
|
-
yield key
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
def rol(byte, count):
|
|
359
|
-
byte = (byte << count | byte >> (8-count)) & 0xFF
|
|
360
|
-
return byte
|
|
361
|
-
|
|
362
|
-
###safety checks
|
|
363
|
-
##assert rol(1, 1) == 2
|
|
364
|
-
##assert rol(128, 1) == 1
|
|
365
|
-
##assert rol(1, 7) == 128
|
|
366
|
-
##assert rol(1, 8) == 1
|
|
367
|
-
|
|
368
|
-
#------------------------------------------------------------------------------
|
|
369
|
-
class Transform_XOR_Chained (Transform_string):
|
|
370
|
-
"""
|
|
371
|
-
XOR Transform, chained with previous character.
|
|
372
|
-
xor_chained(c[i], key) = c[i] xor c[i-1] xor key
|
|
373
|
-
"""
|
|
374
|
-
# generic name for the class:
|
|
375
|
-
gen_name = 'XOR with 8 bits key A chained with previous character. Parameters: A (1-FF).'
|
|
376
|
-
gen_id = 'xor_chained'
|
|
377
|
-
|
|
378
|
-
def __init__(self, params):
|
|
379
|
-
assert isinstance(params, int)
|
|
380
|
-
assert params>=0 and params<256
|
|
381
|
-
self.params = params
|
|
382
|
-
self.name = "XOR %02X Chained" % params
|
|
383
|
-
self.shortname = "xor%02X_chained" % params
|
|
384
|
-
|
|
385
|
-
def transform_string (self, data):
|
|
386
|
-
# here params is an integer
|
|
387
|
-
#TODO: it would be much faster to do the xor_chained once, then all
|
|
388
|
-
# xor transforms using translate() only
|
|
389
|
-
#TODO: use a list comprehension + join to get better performance
|
|
390
|
-
# this loop is more readable, but likely to be much slower
|
|
391
|
-
if len(data) == 0: return b''
|
|
392
|
-
xor_key = self.params
|
|
393
|
-
# 1st char is just xored with key:
|
|
394
|
-
out = bytearray((ord(data[0]) ^ xor_key,))
|
|
395
|
-
for i in range(1, len(data)):
|
|
396
|
-
out.append(ord(data[i]) ^ xor_key ^ ord(data[i-1]))
|
|
397
|
-
return out
|
|
398
|
-
|
|
399
|
-
@staticmethod
|
|
400
|
-
def iter_params ():
|
|
401
|
-
# the XOR key can be 0 to 255 (0 is not identity here)
|
|
402
|
-
for xor_key in range(0,256):
|
|
403
|
-
yield xor_key
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
#------------------------------------------------------------------------------
|
|
407
|
-
class Transform_XOR_RChained (Transform_string):
|
|
408
|
-
"""
|
|
409
|
-
XOR Transform, chained with next character. (chained on the right)
|
|
410
|
-
xor_rchained(c[i], key) = c[i] xor c[i+1] xor key
|
|
411
|
-
"""
|
|
412
|
-
# generic name for the class:
|
|
413
|
-
gen_name = 'XOR with 8 bits key A chained with next character (Reverse order from end to start). Parameters: A (1-FF).'
|
|
414
|
-
gen_id = 'xor_rchained'
|
|
415
|
-
|
|
416
|
-
def __init__(self, params):
|
|
417
|
-
assert isinstance(params, int)
|
|
418
|
-
assert params>=0 and params<256
|
|
419
|
-
self.params = params
|
|
420
|
-
self.name = "XOR %02X RChained" % params
|
|
421
|
-
self.shortname = "xor%02X_rchained" % params
|
|
422
|
-
|
|
423
|
-
def transform_string (self, data):
|
|
424
|
-
# here params is an integer
|
|
425
|
-
#TODO: it would be much faster to do the xor_rchained once, then all
|
|
426
|
-
# xor transforms using translate() only
|
|
427
|
-
#TODO: use a list comprehension + join to get better performance
|
|
428
|
-
# this loop is more readable, but likely to be much slower
|
|
429
|
-
if len(data) == 0: return b''
|
|
430
|
-
out = bytearray()
|
|
431
|
-
xor_key = self.params
|
|
432
|
-
# all chars except last one are xored with key and next char:
|
|
433
|
-
for i in range(len(data)-1):
|
|
434
|
-
out.append(ord(data[i]) ^ xor_key ^ ord(data[i+1]))
|
|
435
|
-
# last char is just xored with key:
|
|
436
|
-
out.append(ord(data[len(data)-1]) ^ xor_key)
|
|
437
|
-
return out
|
|
438
|
-
|
|
439
|
-
@staticmethod
|
|
440
|
-
def iter_params ():
|
|
441
|
-
# the XOR key can be 0 to 255 (0 is not identity here)
|
|
442
|
-
for xor_key in range(0,256):
|
|
443
|
-
yield xor_key
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
#------------------------------------------------------------------------------
|
|
447
|
-
class Transform_XOR_RChainedAll (Transform_string):
|
|
448
|
-
"""
|
|
449
|
-
XOR Transform, chained from the right with all following characters.
|
|
450
|
-
(as found in Taidoor malware)
|
|
451
|
-
NOTE: this only works well in harvest mode, when testing all 256
|
|
452
|
-
possibilities, because the key is position-dependent.
|
|
453
|
-
xor_rchained_all(c[i], key) = c[i] xor key xor c[i+1] xor c[i+2]... xor c[N]
|
|
454
|
-
"""
|
|
455
|
-
# generic name for the class:
|
|
456
|
-
gen_name = 'XOR Transform, chained from the right with all following characters. Only works well with bbharvest.'
|
|
457
|
-
gen_id = 'xor_rchained_all'
|
|
458
|
-
|
|
459
|
-
def __init__(self, params):
|
|
460
|
-
assert isinstance(params, int)
|
|
461
|
-
assert params>=0 and params<256
|
|
462
|
-
self.params = params
|
|
463
|
-
self.name = "XOR %02X RChained All" % params
|
|
464
|
-
self.shortname = "xor%02X_rchained_all" % params
|
|
465
|
-
|
|
466
|
-
def transform_string (self, data):
|
|
467
|
-
# here params is an integer
|
|
468
|
-
#TODO: it would be much faster to do the xor_rchained once, then all
|
|
469
|
-
# xor transforms using translate() only
|
|
470
|
-
#TODO: use a list comprehension + join to get better performance
|
|
471
|
-
# this loop is more readable, but likely to be much slower
|
|
472
|
-
if len(data) == 0: return b''
|
|
473
|
-
xor_key = self.params
|
|
474
|
-
# transform data string to list of integers:
|
|
475
|
-
l = map(ord, data)
|
|
476
|
-
# loop from last char to 2nd one:
|
|
477
|
-
for i in range(len(data)-1, 1, -1):
|
|
478
|
-
l[i-1] = l[i-1] ^ xor_key ^ l[i]
|
|
479
|
-
# last char is only xored with key:
|
|
480
|
-
l[len(data)-1] = l[len(data)-1] ^ xor_key
|
|
481
|
-
# convert back to list of chars:
|
|
482
|
-
#l = map(chr, l)
|
|
483
|
-
#out = ''.join(l)
|
|
484
|
-
out = bytearray(l)
|
|
485
|
-
return out
|
|
486
|
-
|
|
487
|
-
@staticmethod
|
|
488
|
-
def iter_params ():
|
|
489
|
-
# the XOR key can be 0 to 255 (0 is not identity here)
|
|
490
|
-
for xor_key in range(0,256):
|
|
491
|
-
yield xor_key
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
#------------------------------------------------------------------------------
|
|
495
|
-
class Transform_ROL (Transform_char):
|
|
496
|
-
"""
|
|
497
|
-
ROL Transform
|
|
498
|
-
"""
|
|
499
|
-
# generic name for the class:
|
|
500
|
-
gen_name = 'ROL - rotate A bits left. Parameters: A (1-7).'
|
|
501
|
-
gen_id = 'rol'
|
|
502
|
-
|
|
503
|
-
def __init__(self, params):
|
|
504
|
-
self.params = params
|
|
505
|
-
self.name = "ROL %d" % params
|
|
506
|
-
self.shortname = "rol%d" % params
|
|
507
|
-
|
|
508
|
-
def transform_int (self, i):
|
|
509
|
-
# here params is an int
|
|
510
|
-
rol_bits = self.params
|
|
511
|
-
return rol(i, rol_bits)
|
|
512
|
-
|
|
513
|
-
@staticmethod
|
|
514
|
-
def iter_params ():
|
|
515
|
-
"return (ROL bits)"
|
|
516
|
-
# the ROL bits can be 1 to 7:
|
|
517
|
-
for rol_bits in range(1,8):
|
|
518
|
-
yield rol_bits
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
#------------------------------------------------------------------------------
|
|
522
|
-
class Transform_XOR_ROL (Transform_char):
|
|
523
|
-
"""
|
|
524
|
-
XOR+ROL Transform - first XOR, then ROL
|
|
525
|
-
"""
|
|
526
|
-
# generic name for the class:
|
|
527
|
-
gen_name = 'XOR with static 8 bits key A, then rotate B bits left. Parameters: A (1-FF), B (1-7).'
|
|
528
|
-
gen_id = 'xor_rol'
|
|
529
|
-
|
|
530
|
-
def __init__(self, params):
|
|
531
|
-
self.params = params
|
|
532
|
-
self.name = "XOR %02X then ROL %d" % params
|
|
533
|
-
self.shortname = "xor%02X_rol%d" % params
|
|
534
|
-
|
|
535
|
-
def transform_int (self, i):
|
|
536
|
-
# here params is a tuple
|
|
537
|
-
xor_key, rol_bits = self.params
|
|
538
|
-
return rol(i ^ xor_key, rol_bits)
|
|
539
|
-
|
|
540
|
-
@staticmethod
|
|
541
|
-
def iter_params ():
|
|
542
|
-
"return (XOR key, ROL bits)"
|
|
543
|
-
# the XOR key can be 1 to 255 (0 would be like ROL)
|
|
544
|
-
for xor_key in range(1,256):
|
|
545
|
-
# the ROL bits can be 1 to 7:
|
|
546
|
-
for rol_bits in range(1,8):
|
|
547
|
-
yield (xor_key, rol_bits)
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
#------------------------------------------------------------------------------
|
|
551
|
-
class Transform_ADD (Transform_char):
|
|
552
|
-
"""
|
|
553
|
-
ADD Transform
|
|
554
|
-
"""
|
|
555
|
-
# generic name for the class:
|
|
556
|
-
gen_name = 'ADD with 8 bits static key A. Parameters: A (1-FF).'
|
|
557
|
-
gen_id = 'add'
|
|
558
|
-
|
|
559
|
-
def __init__(self, params):
|
|
560
|
-
self.params = params
|
|
561
|
-
self.name = "ADD %02X" % params
|
|
562
|
-
self.shortname = "add%02X" % params
|
|
563
|
-
|
|
564
|
-
def transform_int (self, i):
|
|
565
|
-
# here params is an integer
|
|
566
|
-
add_key = self.params
|
|
567
|
-
return (i + add_key) & 0xFF
|
|
568
|
-
|
|
569
|
-
@staticmethod
|
|
570
|
-
def iter_params ():
|
|
571
|
-
"return ADD key"
|
|
572
|
-
# the ADD key can be 1 to 255 (0 would be identity):
|
|
573
|
-
for add_key in range(1,256):
|
|
574
|
-
yield add_key
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
#------------------------------------------------------------------------------
|
|
578
|
-
class Transform_ADD_ROL (Transform_char):
|
|
579
|
-
"""
|
|
580
|
-
ADD+ROL Transform - first ADD, then ROL
|
|
581
|
-
"""
|
|
582
|
-
# generic name for the class:
|
|
583
|
-
gen_name = 'ADD with static 8 bits key A, then rotate B bits left. Parameters: A (1-FF), B (1-7).'
|
|
584
|
-
gen_id = 'add_rol'
|
|
585
|
-
|
|
586
|
-
def __init__(self, params):
|
|
587
|
-
self.params = params
|
|
588
|
-
self.name = "ADD %02X then ROL %d" % params
|
|
589
|
-
self.shortname = "add%02X_rol%d" % params
|
|
590
|
-
|
|
591
|
-
def transform_int (self, i):
|
|
592
|
-
# here params is a tuple
|
|
593
|
-
add_key, rol_bits = self.params
|
|
594
|
-
return rol((i + add_key) & 0xFF, rol_bits)
|
|
595
|
-
|
|
596
|
-
@staticmethod
|
|
597
|
-
def iter_params ():
|
|
598
|
-
"return (ADD key, ROL bits)"
|
|
599
|
-
# the ADD key can be 1 to 255 (0 would be like ROL)
|
|
600
|
-
for add_key in range(1,256):
|
|
601
|
-
# the ROL bits can be 1 to 7:
|
|
602
|
-
for rol_bits in range(1,8):
|
|
603
|
-
yield (add_key, rol_bits)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
#------------------------------------------------------------------------------
|
|
607
|
-
class Transform_ROL_ADD (Transform_char):
|
|
608
|
-
"""
|
|
609
|
-
ROL+ADD Transform - first ROL, then ADD
|
|
610
|
-
"""
|
|
611
|
-
# generic name for the class:
|
|
612
|
-
gen_name = 'rotate A bits left, then ADD with static 8 bits key B. Parameters: A (1-7), B (1-FF).'
|
|
613
|
-
gen_id = 'rol_add'
|
|
614
|
-
|
|
615
|
-
def __init__(self, params):
|
|
616
|
-
self.params = params
|
|
617
|
-
self.name = "ROL %d then ADD %02X" % params
|
|
618
|
-
self.shortname = "rol%d_add%02X" % params
|
|
619
|
-
|
|
620
|
-
def transform_int (self, i):
|
|
621
|
-
# here params is a tuple
|
|
622
|
-
rol_bits, add_key = self.params
|
|
623
|
-
return (rol(i, rol_bits) + add_key) & 0xFF
|
|
624
|
-
|
|
625
|
-
@staticmethod
|
|
626
|
-
def iter_params ():
|
|
627
|
-
"return (ROL bits, ADD key)"
|
|
628
|
-
# the ROL bits can be 1 to 7:
|
|
629
|
-
for rol_bits in range(1,8):
|
|
630
|
-
# the ADD key can be 1 to 255 (0 would be identity)
|
|
631
|
-
for add_key in range(1,256):
|
|
632
|
-
yield (rol_bits, add_key)
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
#------------------------------------------------------------------------------
|
|
636
|
-
class Transform_XOR_ADD (Transform_char):
|
|
637
|
-
"""
|
|
638
|
-
XOR+ADD Transform - first XOR, then ADD
|
|
639
|
-
"""
|
|
640
|
-
# generic name for the class:
|
|
641
|
-
gen_name = 'XOR with 8 bits static key A, then ADD with 8 bits static key B. Parameters: A (1-FF), B (1-FF).'
|
|
642
|
-
gen_id = 'xor_add'
|
|
643
|
-
|
|
644
|
-
def __init__(self, params):
|
|
645
|
-
self.params = params
|
|
646
|
-
self.name = "XOR %02X then ADD %02X" % params
|
|
647
|
-
self.shortname = "xor%02X_add%02X" % params
|
|
648
|
-
|
|
649
|
-
def transform_int (self, i):
|
|
650
|
-
# here params is a tuple
|
|
651
|
-
xor_key, add_key = self.params
|
|
652
|
-
return ((i ^ xor_key) + add_key) & 0xFF
|
|
653
|
-
|
|
654
|
-
@staticmethod
|
|
655
|
-
def iter_params ():
|
|
656
|
-
"return (XOR key1, ADD key2)"
|
|
657
|
-
# the XOR key can be 1 to 255 (0 would be identity)
|
|
658
|
-
for xor_key in range(1,256):
|
|
659
|
-
# the ADD key can be 1 to 255 (0 would be identity):
|
|
660
|
-
for add_key in range(1,256):
|
|
661
|
-
yield (xor_key, add_key)
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
#------------------------------------------------------------------------------
|
|
665
|
-
class Transform_ADD_XOR (Transform_char):
|
|
666
|
-
"""
|
|
667
|
-
ADD+XOR Transform - first ADD, then XOR
|
|
668
|
-
"""
|
|
669
|
-
# generic name for the class:
|
|
670
|
-
gen_name = 'ADD with 8 bits static key A, then XOR with 8 bits static key B. Parameters: A (1-FF), B (1-FF).'
|
|
671
|
-
gen_id = 'add_xor'
|
|
672
|
-
|
|
673
|
-
def __init__(self, params):
|
|
674
|
-
self.params = params
|
|
675
|
-
self.name = "ADD %02X then XOR %02X" % params
|
|
676
|
-
self.shortname = "add%02X_xor%02X" % params
|
|
677
|
-
|
|
678
|
-
def transform_int (self, i):
|
|
679
|
-
# here params is a tuple
|
|
680
|
-
add_key, xor_key = self.params
|
|
681
|
-
return ((i + add_key) & 0xFF) ^ xor_key
|
|
682
|
-
|
|
683
|
-
@staticmethod
|
|
684
|
-
def iter_params ():
|
|
685
|
-
"return (ADD key1, XOR key2)"
|
|
686
|
-
# the ADD key can be 1 to 255 (0 would be identity):
|
|
687
|
-
for add_key in range(1,256):
|
|
688
|
-
# the XOR key can be 1 to 255 (0 would be identity)
|
|
689
|
-
for xor_key in range(1,256):
|
|
690
|
-
yield (add_key, xor_key)
|
|
691
|
-
|
|
692
|
-
#--- CUSTOM XOR BRUTE FORCE ---------------------------------------------------
|
|
693
|
-
|
|
694
|
-
def xor_simple(a, b):
|
|
695
|
-
out = ""
|
|
696
|
-
for i, c in enumerate(a):
|
|
697
|
-
out += chr(c ^ ord(b[i % len(b)]))
|
|
698
|
-
return out
|
|
699
|
-
|
|
700
|
-
def deobfuscate_simple(d, r, m):
|
|
701
|
-
"Take mask and will create a key to unmask suspected data, then check if the xor'd data matches a regex pattern"
|
|
702
|
-
import regex as re
|
|
703
|
-
max_mask = m.lower()
|
|
704
|
-
for i in range(1, len(max_mask)+1):
|
|
705
|
-
t_mask = max_mask[:i]
|
|
706
|
-
r_mask = xor_simple(d[:i], t_mask)
|
|
707
|
-
de_enc = xor_simple(d, r_mask).encode()
|
|
708
|
-
if re.match(r, de_enc):
|
|
709
|
-
return de_enc.strip(), r_mask
|
|
710
|
-
return None, None
|
|
711
|
-
|
|
712
|
-
#--- TRANSFORM GROUPS ---------------------------------------------------------
|
|
713
|
-
|
|
714
|
-
# Transforms level 1
|
|
715
|
-
transform_classes1 = [
|
|
716
|
-
#Transform_identity,
|
|
717
|
-
Transform_XOR,
|
|
718
|
-
Transform_ADD,
|
|
719
|
-
Transform_ROL,
|
|
720
|
-
]
|
|
721
|
-
|
|
722
|
-
# Transforms level 2
|
|
723
|
-
transform_classes2 = [
|
|
724
|
-
Transform_XOR_ROL,
|
|
725
|
-
Transform_ADD_ROL,
|
|
726
|
-
Transform_ROL_ADD,
|
|
727
|
-
]
|
|
728
|
-
|
|
729
|
-
# Transforms level 3
|
|
730
|
-
transform_classes3 = [
|
|
731
|
-
Transform_XOR_ADD,
|
|
732
|
-
Transform_ADD_XOR,
|
|
733
|
-
Transform_XOR_INC,
|
|
734
|
-
Transform_XOR_DEC,
|
|
735
|
-
Transform_SUB_INC,
|
|
736
|
-
Transform_XOR_Chained,
|
|
737
|
-
Transform_XOR_RChained,
|
|
738
|
-
Transform_XOR_INC_ROL,
|
|
739
|
-
Transform_XOR_RChainedAll,
|
|
740
|
-
]
|
|
741
|
-
|
|
742
|
-
# all transforms
|
|
743
|
-
transform_classes_all = transform_classes1 + transform_classes2 + transform_classes3
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
#--- PATTERNS -----------------------------------------------------------------
|
|
747
|
-
|
|
748
|
-
#see balbuzard.patterns.py
|
|
749
|
-
|
|
750
|
-
# === MAIN =====================================================================
|
|
751
|
-
"""
|
|
752
|
-
2016-10-20: Main Module modified for FrankenStrings AL service
|
|
753
|
-
"""
|
|
754
|
-
|
|
755
|
-
def read_file(filename):
|
|
756
|
-
"""
|
|
757
|
-
Open a file, read and return its data as a string.
|
|
758
|
-
"""
|
|
759
|
-
f = open(filename, 'rb')
|
|
760
|
-
raw_data = f.read()
|
|
761
|
-
f.close()
|
|
762
|
-
return raw_data
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
def bbcrack(file_data, level=1):
|
|
766
|
-
|
|
767
|
-
raw_data = file_data
|
|
768
|
-
if level == 1 or level == 'small_string':
|
|
769
|
-
transform_classes = transform_classes1
|
|
770
|
-
elif level == 2:
|
|
771
|
-
transform_classes = transform_classes1 + transform_classes2
|
|
772
|
-
else:
|
|
773
|
-
transform_classes = transform_classes_all
|
|
774
|
-
|
|
775
|
-
results = []
|
|
776
|
-
bbc = PatternMatch()
|
|
777
|
-
bbcrack_patterns = bbc.bbcr(level=level)
|
|
778
|
-
|
|
779
|
-
if level == 'small_string':
|
|
780
|
-
|
|
781
|
-
bbz = Balbuzard(bbcrack_patterns)
|
|
782
|
-
|
|
783
|
-
# Round 1
|
|
784
|
-
for Transform_class in transform_classes:
|
|
785
|
-
for params in Transform_class.iter_params():
|
|
786
|
-
transform = Transform_class(params)
|
|
787
|
-
data = transform.transform_string(raw_data)
|
|
788
|
-
for pattern, matches in bbz.scan(data):
|
|
789
|
-
for index, match in matches:
|
|
790
|
-
regex = pattern.name.split("_", 1)[1]
|
|
791
|
-
smatch = match
|
|
792
|
-
if transform.shortname == "xor20":
|
|
793
|
-
# for basic alpha characters, will essentially convert lower and uppercase.
|
|
794
|
-
continue
|
|
795
|
-
results.append((transform.shortname, regex, smatch))
|
|
796
|
-
return results
|
|
797
|
-
|
|
798
|
-
for pattern in bbz.list_patterns():
|
|
799
|
-
pmask = pattern.name.split("_", 1)[0]
|
|
800
|
-
sxor, smask = deobfuscate_simple(raw_data, pattern.pat, pmask)
|
|
801
|
-
if sxor:
|
|
802
|
-
results.append((smask, pattern.name.split("_", 1)[1], sxor))
|
|
803
|
-
return results
|
|
804
|
-
|
|
805
|
-
# Run bbcrack patterns against transforms
|
|
806
|
-
bbz = Balbuzard(bbcrack_patterns)
|
|
807
|
-
|
|
808
|
-
for Transform_class in transform_classes:
|
|
809
|
-
for params in Transform_class.iter_params():
|
|
810
|
-
transform = Transform_class(params)
|
|
811
|
-
if transform.shortname == "xor20":
|
|
812
|
-
# for basic alpha characters, will essentially convert lower and uppercase.
|
|
813
|
-
continue
|
|
814
|
-
data = transform.transform_string(raw_data)
|
|
815
|
-
score = 0
|
|
816
|
-
for pattern, matches in bbz.scan(data):
|
|
817
|
-
for index, match in matches:
|
|
818
|
-
regex = pattern.name
|
|
819
|
-
smatch = match
|
|
820
|
-
if regex == 'EXE_HEAD':
|
|
821
|
-
if b'This program cannot be run' not in data:
|
|
822
|
-
continue
|
|
823
|
-
score = 100000
|
|
824
|
-
results.append((transform.shortname, regex, index, score, data))
|
|
825
|
-
continue
|
|
826
|
-
score += len(match) * pattern.weight
|
|
827
|
-
results.append((transform.shortname, regex, index, score, smatch[0:50]))
|
|
828
|
-
|
|
829
|
-
return results
|
|
830
|
-
# This was coded while listening to The Walkmen "Heaven". --Philippe Lagadec
|