1
+
2
+ require 'rex/poly/machine'
3
+
4
+ module Rex
5
+
6
+ module Encoder
7
+
8
+ class BloXor < Msf ::Encoder
9
+
10
+ def initialize ( *args )
11
+ super
12
+ @machine = nil
13
+ @blocks_out = [ ]
14
+ @block_size = 0
15
+ end
16
+
17
+ #
18
+ #
19
+ #
20
+ def decoder_stub ( state )
21
+
22
+ if ( not state . decoder_stub )
23
+ @blocks_out = [ ]
24
+ @block_size = 0
25
+
26
+ # XXX: It would be ideal to use a random block size but unless we know the maximum size our final encoded
27
+ # blob can be we should instead start with the smallest block size and go up to avoid generating
28
+ # anything too big (if we knew the max size we could try something smaller if we generated a blob too big)
29
+ #block_sizes = (1..state.buf.length).to_a.shuffle
30
+ #block_sizes.each do | len |
31
+
32
+ 1 . upto ( state . buf . length ) do | len |
33
+
34
+ # For now we ignore all odd sizes to help with performance (The rex poly machine
35
+ # doesnt have many load/store primitives that can handle byte sizes efficiently)
36
+ if ( len % 2 != 0 )
37
+ next
38
+ end
39
+
40
+ blocks , size = compute_encoded ( state , len )
41
+ if ( blocks and size )
42
+
43
+ # We sanity check that the newly generated block ammount and the block size
44
+ # are not in the badchar list when converted into a hex form. Helps speed
45
+ # things up a great deal when generating a decoder stub later as these
46
+ # values may be used throughout.
47
+
48
+ if ( not number_is_valid? ( state , blocks . length - 1 ) or not number_is_valid? ( state , ~( blocks . length - 1 ) ) )
49
+ next
50
+ end
51
+
52
+ if ( not number_is_valid? ( state , size ) or not number_is_valid? ( state , ~size ) )
53
+ next
54
+ end
55
+
56
+ @blocks_out = blocks
57
+ @block_size = size
58
+
59
+ break
60
+ end
61
+ end
62
+
63
+ raise RuntimeError , "Unable to generate seed block." if ( @blocks_out . empty? )
64
+
65
+ state . decoder_stub = compute_decoder ( state )
66
+ end
67
+
68
+ state . decoder_stub
69
+ end
70
+
71
+ #
72
+ #
73
+ #
74
+ def encode_block ( state , data )
75
+
76
+ buffer = ''
77
+
78
+ @blocks_out . each do | block |
79
+ buffer << block . pack ( 'C*' )
80
+ end
81
+
82
+ buffer
83
+ end
84
+
85
+ protected
86
+
87
+ #
88
+ # Is a number in its byte form valid against the badchars?
89
+ #
90
+ def number_is_valid? ( state , number )
91
+ size = 'C'
92
+ if ( number > 0xFFFF )
93
+ size = 'V'
94
+ elsif ( number > 0xFF )
95
+ size = 'v'
96
+ end
97
+ return Rex ::Text . badchar_index ( [ number ] . pack ( size ) , state . badchars ) . nil?
98
+ end
99
+
100
+ #
101
+ # Calculate Shannon's entropy.
102
+ #
103
+ def entropy ( data )
104
+ entropy = 0 . to_f
105
+ ( 0 ..255 ) . each do | byte |
106
+ freq = data . to_s . count ( byte . chr ) . to_f / data . to_s . length
107
+ if ( freq > 0 )
108
+ entropy -= freq * Math . log2 ( freq )
109
+ end
110
+ end
111
+ return entropy / 8
112
+ end
113
+
114
+ #
115
+ # Compute the encoded blocks (and associated seed)
116
+ #
117
+ def compute_encoded ( state , len )
118
+
119
+ blocks_in = ::Array . new
120
+
121
+ input = '' << state . buf
122
+
123
+ block_padding = ( input . length % len ) > 0 ? len - ( input . length % len ) : 0
124
+
125
+ if ( block_padding > 0 )
126
+ 0 . upto ( block_padding -1 ) do
127
+ input << [ rand ( 255 ) ] . pack ( 'C' )
128
+ end
129
+ end
130
+
131
+ while ( input . length > 0 )
132
+ blocks_in << input [ 0 ..len -1 ] . unpack ( 'C*' )
133
+ input = input [ len ..input . length ]
134
+ end
135
+
136
+ seed = compute_seed ( blocks_in , len , block_padding , state . badchars . unpack ( 'C*' ) )
137
+
138
+ if ( not seed )
139
+ return [ nil , nil ]
140
+ end
141
+
142
+ blocks_out = [ seed ]
143
+
144
+ blocks_in . each do | block |
145
+ blocks_out << compute_block ( blocks_out . last , block )
146
+ end
147
+
148
+ return [ blocks_out , len ]
149
+ end
150
+
151
+ #
152
+ # Generate the decoder stub which is functionally equivalent to the following:
153
+ #
154
+ # source = &end;
155
+ # dest = source + BLOCK_SIZE;
156
+ # counter = BLOCK_COUNT * ( BLOCK_SIZE / chunk_size );
157
+ # do
158
+ # {
159
+ # encoded = *(CHUNK_SIZE *)dest;
160
+ # dest += chunk_size;
161
+ # decoded = *(CHUNK_SIZE *)source;
162
+ # *(CHUNK_SIZE *)source = decoded ^ encoded;
163
+ # source += chunk_size;
164
+ # } while( --counter );
165
+ #
166
+ # end:
167
+ #
168
+ def compute_decoder ( state )
169
+
170
+ @machine . create_variable ( 'source' )
171
+ @machine . create_variable ( 'dest' )
172
+ @machine . create_variable ( 'counter' )
173
+ @machine . create_variable ( 'encoded' )
174
+ @machine . create_variable ( 'decoded' )
175
+
176
+ chunk_size = Rex ::Poly ::Machine ::BYTE
177
+ if ( @machine . native_size ( ) == Rex ::Poly ::Machine ::QWORD )
178
+ if ( @block_size % Rex ::Poly ::Machine ::QWORD == 0 )
179
+ chunk_size = Rex ::Poly ::Machine ::QWORD
180
+ elsif ( @block_size % Rex ::Poly ::Machine ::DWORD == 0 )
181
+ chunk_size = Rex ::Poly ::Machine ::DWORD
182
+ elsif ( @block_size % Rex ::Poly ::Machine ::WORD == 0 )
183
+ chunk_size = Rex ::Poly ::Machine ::WORD
184
+ end
185
+ elsif ( @machine . native_size ( ) == Rex ::Poly ::Machine ::DWORD )
186
+ if ( @block_size % Rex ::Poly ::Machine ::DWORD == 0 )
187
+ chunk_size = Rex ::Poly ::Machine ::DWORD
188
+ elsif ( @block_size % Rex ::Poly ::Machine ::WORD == 0 )
189
+ chunk_size = Rex ::Poly ::Machine ::WORD
190
+ end
191
+ elsif ( @machine . native_size ( ) == Rex ::Poly ::Machine ::WORD )
192
+ if ( @block_size % Rex ::Poly ::Machine ::WORD == 0 )
193
+ chunk_size = Rex ::Poly ::Machine ::WORD
194
+ end
195
+ end
196
+
197
+ # Block 1 - Set the source variable to the address of the start block
198
+ @machine . create_block_primitive ( 'block1' , 'set' , 'source' , 'location' )
199
+
200
+ # Block 2 - Set the source variable to the address of the 1st encoded block
201
+ @machine . create_block_primitive ( 'block2' , 'add' , 'source' , 'end' )
202
+
203
+ # Block 3 - Set the destingation variable to the value of the source variable
204
+ @machine . create_block_primitive ( 'block3' , 'set' , 'dest' , 'source' )
205
+
206
+ # Block 4 - Set the destingation variable to the address of the 2nd encoded block
207
+ @machine . create_block_primitive ( 'block4' , 'add' , 'dest' , @block_size )
208
+
209
+ # Block 5 - Sets the loop counter to the number of blocks to process
210
+ @machine . create_block_primitive ( 'block5' , 'set' , 'counter' , ( ( @block_size / chunk_size ) * ( @blocks_out . length - 1 ) ) )
211
+
212
+ # Block 6 - Set the encoded variable to the byte pointed to by the dest variable
213
+ @machine . create_block_primitive ( 'block6' , 'load' , 'encoded' , 'dest' , chunk_size )
214
+
215
+ # Block 7 - Increment the destination variable by one
216
+ @machine . create_block_primitive ( 'block7' , 'add' , 'dest' , chunk_size )
217
+
218
+ # Block 8 - Set the decoded variable to the byte pointed to by the source variable
219
+ @machine . create_block_primitive ( 'block8' , 'load' , 'decoded' , 'source' , chunk_size )
220
+
221
+ # Block 9 - Xor the decoded variable with the encoded variable
222
+ @machine . create_block_primitive ( 'block9' , 'xor' , 'decoded' , 'encoded' )
223
+
224
+ # Block 10 - store the newly decoded byte
225
+ @machine . create_block_primitive ( 'block10' , 'store' , 'source' , 'decoded' , chunk_size )
226
+
227
+ # Block 11 - Increment the source variable by one
228
+ @machine . create_block_primitive ( 'block11' , 'add' , 'source' , chunk_size )
229
+
230
+ # Block 12 - Jump back up to the outer_loop block while the counter variable > 0
231
+ @machine . create_block_primitive ( 'block12' , 'loop' , 'counter' , 'block6' )
232
+
233
+ # Try to generate the decoder stub...
234
+ decoder = @machine . generate
235
+
236
+ if ( not decoder )
237
+ raise RuntimeError , "Unable to generate decoder stub."
238
+ end
239
+
240
+ decoder
241
+ end
242
+
243
+ #
244
+ # Compute the seed block which will successfully decode all proceeding encoded
245
+ # blocks while ensuring the encoded blocks do not contain any badchars.
246
+ #
247
+ def compute_seed ( blocks_in , block_size , block_padding , badchars )
248
+ seed = [ ]
249
+ redo_bytes = [ ]
250
+
251
+ 0 . upto ( block_size -1 ) do | index |
252
+
253
+ seed_bytes = ( 0 ..255 ) . sort_by do
254
+ rand ( )
255
+ end
256
+
257
+ seed_bytes . each do | seed_byte |
258
+
259
+ next if ( badchars . include? ( seed_byte ) )
260
+
261
+ success = true
262
+
263
+ previous_byte = seed_byte
264
+
265
+ if ( redo_bytes . length < 256 )
266
+ redo_bytes = ( 0 ..255 ) . sort_by do
267
+ rand ( )
268
+ end
269
+ end
270
+
271
+ blocks_in . each do | block |
272
+
273
+ decoded_byte = block [ index ]
274
+
275
+ encoded_byte = previous_byte ^ decoded_byte
276
+
277
+ if ( badchars . include? ( encoded_byte ) )
278
+ # the padding bytes we added earlier can be changed if they are causing us to fail.
279
+ if ( block == blocks_in . last and index >= ( block_size -block_padding ) )
280
+ if ( redo_bytes . empty? )
281
+ success = false
282
+ break
283
+ end
284
+ block [ index ] = redo_bytes . shift
285
+ redo
286
+ end
287
+
288
+ success = false
289
+ break
290
+ end
291
+
292
+ previous_byte = encoded_byte
293
+ end
294
+
295
+ if ( success )
296
+ seed << seed_byte
297
+ break
298
+ end
299
+ end
300
+
301
+ end
302
+
303
+ if ( seed . length == block_size )
304
+ return seed
305
+ end
306
+
307
+ return nil
308
+ end
309
+
310
+ #
311
+ # Compute the next encoded block by xoring the previous
312
+ # encoded block with the next decoded block.
313
+ #
314
+ def compute_block ( encoded , decoded )
315
+ block = [ ]
316
+ 0 . upto ( encoded . length -1 ) do | index |
317
+ block << ( encoded [ index ] ^ decoded [ index ] )
318
+ end
319
+ return block
320
+ end
321
+
322
+ end
323
+
324
+ end
325
+
326
+ end
0 commit comments