rex-encoder 0.1.0
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/README.md +32 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/encoder.rb +15 -0
- data/lib/rex/encoder/alpha2.rb +31 -0
- data/lib/rex/encoder/alpha2/alpha_mixed.rb +129 -0
- data/lib/rex/encoder/alpha2/alpha_upper.rb +138 -0
- data/lib/rex/encoder/alpha2/generic.rb +90 -0
- data/lib/rex/encoder/alpha2/unicode_mixed.rb +116 -0
- data/lib/rex/encoder/alpha2/unicode_upper.rb +123 -0
- data/lib/rex/encoder/bloxor/bloxor.rb +327 -0
- data/lib/rex/encoder/ndr.rb +90 -0
- data/lib/rex/encoder/nonalpha.rb +61 -0
- data/lib/rex/encoder/nonupper.rb +64 -0
- data/lib/rex/encoder/version.rb +5 -0
- data/lib/rex/encoder/xdr.rb +108 -0
- data/lib/rex/encoder/xor.rb +69 -0
- data/lib/rex/encoder/xor/dword.rb +13 -0
- data/lib/rex/encoder/xor/dword_additive.rb +13 -0
- data/lib/rex/encoding/xor.rb +20 -0
- data/lib/rex/encoding/xor/byte.rb +15 -0
- data/lib/rex/encoding/xor/dword.rb +21 -0
- data/lib/rex/encoding/xor/dword_additive.rb +92 -0
- data/lib/rex/encoding/xor/exceptions.rb +17 -0
- data/lib/rex/encoding/xor/generic.rb +146 -0
- data/lib/rex/encoding/xor/qword.rb +15 -0
- data/lib/rex/encoding/xor/word.rb +21 -0
- data/lib/rex/poly.rb +134 -0
- data/lib/rex/poly/block.rb +480 -0
- data/lib/rex/poly/machine.rb +13 -0
- data/lib/rex/poly/machine/machine.rb +830 -0
- data/lib/rex/poly/machine/x86.rb +509 -0
- data/lib/rex/poly/register.rb +101 -0
- data/lib/rex/poly/register/x86.rb +41 -0
- data/rex-encoder.gemspec +31 -0
- metadata +248 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,830 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
|
5
|
+
module Poly
|
6
|
+
|
7
|
+
#
|
8
|
+
# A machine capable of creating a small blob of code in a metamorphic kind of way.
|
9
|
+
# Note: this is designed to perform an exhaustive search for a solution and can be
|
10
|
+
# slow. If you need a speedier option, the origional Rex::Polly::Block stuff is a
|
11
|
+
# better choice.
|
12
|
+
#
|
13
|
+
class Machine
|
14
|
+
|
15
|
+
QWORD = 8
|
16
|
+
DWORD = 4
|
17
|
+
WORD = 2
|
18
|
+
BYTE = 1
|
19
|
+
|
20
|
+
#
|
21
|
+
# A Permutation!
|
22
|
+
#
|
23
|
+
class Permutation
|
24
|
+
|
25
|
+
attr_accessor :active, :offset
|
26
|
+
|
27
|
+
attr_reader :name, :primitive, :length, :args
|
28
|
+
|
29
|
+
#
|
30
|
+
# Create a new permutation object.
|
31
|
+
#
|
32
|
+
def initialize( name, primitive, machine, source, args=nil )
|
33
|
+
@name = name
|
34
|
+
@primitive = primitive
|
35
|
+
@machine = machine
|
36
|
+
@source = source
|
37
|
+
@args = args
|
38
|
+
@active = false
|
39
|
+
@valid = true
|
40
|
+
@length = 0
|
41
|
+
@offset = 0
|
42
|
+
@children = ::Array.new
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Add in a child permutation to this one. Used to build the permutation tree.
|
47
|
+
#
|
48
|
+
def add_child( child )
|
49
|
+
@children << child
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Does this permutation have children?
|
54
|
+
#
|
55
|
+
def has_children?
|
56
|
+
not @children.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Remove any existing children. Called by the machines generate function
|
61
|
+
# to build a fresh tree in case generate was previously called.
|
62
|
+
#
|
63
|
+
def remove_children
|
64
|
+
@children.clear
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Actully render this permutation into a raw buffer.
|
69
|
+
#
|
70
|
+
def render
|
71
|
+
raw = ''
|
72
|
+
# Zero the length as we will be rendering the raw buffer and the length may change.
|
73
|
+
@length = 0
|
74
|
+
# If this permutation source is a Primitive/Procedure we can call it, otherwise we have a string
|
75
|
+
if( @source.kind_of?( Primitive ) or @source.kind_of?( ::Proc ) )
|
76
|
+
if( @source.kind_of?( Primitive ) )
|
77
|
+
raw = @source.call( @name, @machine, *@args )
|
78
|
+
elsif( @source.kind_of?( ::Proc ) )
|
79
|
+
raw = @source.call
|
80
|
+
end
|
81
|
+
# If the primitive/procedure returned an array, it is an array of assembly strings which we can assemble.
|
82
|
+
if( raw.kind_of?( ::Array ) )
|
83
|
+
lines = raw
|
84
|
+
raw = ''
|
85
|
+
# itterate over each line of assembly
|
86
|
+
lines.each do | asm |
|
87
|
+
# parse the asm and substitute in any offset values specified...
|
88
|
+
offsets = asm.scan( /:([\S]+)_offset/ )
|
89
|
+
offsets.each do | name, |
|
90
|
+
asm = asm.gsub( ":#{name}_offset", @machine.block_offset( name ).to_s )
|
91
|
+
end
|
92
|
+
# and substitute in and register values for any variables specified...
|
93
|
+
regs = asm.scan( /:([\S]+)_reg([\d]+)/ )
|
94
|
+
regs.each do | name, size |
|
95
|
+
asm = asm.gsub( ":#{name}_reg#{size}", @machine.variable_value( name, size.to_i ) )
|
96
|
+
end
|
97
|
+
# assemble it into a raw blob
|
98
|
+
blob = @machine.assemble( asm )
|
99
|
+
#if( not @machine.is_valid?( blob ) )
|
100
|
+
# p "#{name}(#{primitive}):#{asm} is invalid"
|
101
|
+
#end
|
102
|
+
raw << blob
|
103
|
+
end
|
104
|
+
end
|
105
|
+
else
|
106
|
+
# the source must just be a static string
|
107
|
+
raw = @source
|
108
|
+
end
|
109
|
+
# Update the length to reflect the new raw buffer
|
110
|
+
@length = raw.to_s.length
|
111
|
+
# As the temp variable is only assigned for the duration of a single permutation we
|
112
|
+
# can now release it if it was used in this permutation.
|
113
|
+
@machine.release_temp_variable
|
114
|
+
return raw.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Test if this permutation raw buffer is valid in this machine (e.g. against the badchar list).
|
119
|
+
#
|
120
|
+
def is_valid?
|
121
|
+
result = false
|
122
|
+
if( @valid )
|
123
|
+
begin
|
124
|
+
result = @machine.is_valid?( self.render )
|
125
|
+
rescue UnallowedPermutation
|
126
|
+
# This permutation is unallowed and can never be rendered so just mark it as
|
127
|
+
# not valid to skip it during future attempts.
|
128
|
+
@valid = false
|
129
|
+
rescue UndefinedPermutation
|
130
|
+
# allow an undefined permutation to fail validation but keep it marked
|
131
|
+
# as valid as it may be defined and passed validation later.
|
132
|
+
ensure
|
133
|
+
# Should a temporary variable have been assigned we can release it here.
|
134
|
+
@machine.release_temp_variable
|
135
|
+
end
|
136
|
+
end
|
137
|
+
return result
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Try to find a solution within the solution space by performing a depth first search
|
142
|
+
# into the permutation tree and backtracking when needed.
|
143
|
+
#
|
144
|
+
def solve
|
145
|
+
# Check to see if this permutation can make part of a valid solution
|
146
|
+
if( self.is_valid? )
|
147
|
+
# record this permutation as part of the final solution (the current machines register state is also saved here)
|
148
|
+
@machine.solution_push( self )
|
149
|
+
# If we have no children we are at the end of the tree and have a potential full solution.
|
150
|
+
if( not self.has_children? )
|
151
|
+
# We have a solution but doing a final pass to update offsets may introduce bad chars
|
152
|
+
# so we test for this and keep searching if this isnt a real solution after all.
|
153
|
+
if( not @machine.solution_is_valid? )
|
154
|
+
# remove this permutation and keep searching
|
155
|
+
@machine.solution_pop
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
# Return true to unwind the recursive call as we have got a final solution.
|
159
|
+
return true
|
160
|
+
end
|
161
|
+
# Itterate over the children of this permutation (the perutations of the proceeding block).
|
162
|
+
@children.each do | child |
|
163
|
+
# Traverse into this child to keep trying to generate a solution...
|
164
|
+
if( child.solve )
|
165
|
+
# Keep returning true to unwind as we are done.
|
166
|
+
return true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
# If we get here this permutation, origionally thought to be good for a solution, is not after all,
|
170
|
+
# so remove it from the machines final solution, restoring the register state aswell.
|
171
|
+
@machine.solution_pop
|
172
|
+
end
|
173
|
+
# No children can be made form part of the solution, return failure for this path in the tree.
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# A symbolic permutation to mark locations like the begining and end of a group of blocks.
|
181
|
+
# Used to calculate usefull offsets.
|
182
|
+
#
|
183
|
+
class SymbolicPermutation < Permutation
|
184
|
+
def initialize( name, machine, initial_offset=0 )
|
185
|
+
super( name, '', machine, '' )
|
186
|
+
# fudge the initial symbolic offset with a default (it gets patched correctly later),
|
187
|
+
# helps with the end symbolic block to not be 0 (as its a forward reference it really
|
188
|
+
# slows things down if we leave it 0)
|
189
|
+
@offset = initial_offset
|
190
|
+
# A symbolic block is allways active!
|
191
|
+
@active = true
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# We block all attempts to set the active state of this permutation so as
|
196
|
+
# it is always true. This lets us always address the offset.
|
197
|
+
#
|
198
|
+
def active=( value )
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
#
|
203
|
+
# A primitive is a machine defined permutation which accepts some arguments when it is called.
|
204
|
+
#
|
205
|
+
class Primitive
|
206
|
+
|
207
|
+
#
|
208
|
+
# Initialize this primitive with its target source procedure and the machine it belongs to.
|
209
|
+
#
|
210
|
+
def initialize( source )
|
211
|
+
@source = source
|
212
|
+
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# Call the primitives source procedure, passing in the arguments.
|
216
|
+
#
|
217
|
+
def call( name, machine, *args )
|
218
|
+
return @source.call( name, machine, *args )
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
#
|
224
|
+
#
|
225
|
+
#
|
226
|
+
class Block
|
227
|
+
|
228
|
+
#attr_accessor :next, :previous
|
229
|
+
attr_reader :name
|
230
|
+
|
231
|
+
def initialize( name )
|
232
|
+
@name = name
|
233
|
+
@next = nil
|
234
|
+
@previous = nil
|
235
|
+
@permutations = ::Array.new
|
236
|
+
end
|
237
|
+
|
238
|
+
def shuffle
|
239
|
+
@permutations = @permutations.shuffle
|
240
|
+
end
|
241
|
+
|
242
|
+
def solve
|
243
|
+
@permutations.first.solve
|
244
|
+
end
|
245
|
+
|
246
|
+
def << ( permutation )
|
247
|
+
@permutations << permutation
|
248
|
+
end
|
249
|
+
|
250
|
+
def each
|
251
|
+
@permutations.each do | permutation |
|
252
|
+
yield permutation
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# A class to hold a solution for a Rex::Poly::Machine problem.
|
260
|
+
#
|
261
|
+
class Solution
|
262
|
+
|
263
|
+
attr_reader :offset
|
264
|
+
|
265
|
+
def initialize
|
266
|
+
@permutations = ::Array.new
|
267
|
+
@reg_state = ::Array.new
|
268
|
+
@offset = 0
|
269
|
+
end
|
270
|
+
|
271
|
+
#
|
272
|
+
# Reset this solution to an empty state.
|
273
|
+
#
|
274
|
+
def reset
|
275
|
+
@offset = 0
|
276
|
+
@permutations.each do | permutation |
|
277
|
+
permutation.active = false
|
278
|
+
permutation.offset = 0
|
279
|
+
end
|
280
|
+
@permutations.clear
|
281
|
+
@reg_state.clear
|
282
|
+
end
|
283
|
+
|
284
|
+
#
|
285
|
+
# Push a new permutation onto this solutions permutations list and save the associated register/variables state
|
286
|
+
#
|
287
|
+
def push( permutation, reg_available, reg_consumed, variables )
|
288
|
+
permutation.active = true
|
289
|
+
permutation.offset = @offset
|
290
|
+
@offset += permutation.length
|
291
|
+
@permutations.push( permutation )
|
292
|
+
@reg_state.push( [ [].concat(reg_available), [].concat(reg_consumed), {}.merge(variables) ] )
|
293
|
+
end
|
294
|
+
|
295
|
+
#
|
296
|
+
# Pop off the last permutaion and register/variables state from this solution.
|
297
|
+
#
|
298
|
+
def pop
|
299
|
+
reg_available, reg_consumed, variables = @reg_state.pop
|
300
|
+
permutation = @permutations.pop
|
301
|
+
permutation.active = false
|
302
|
+
permutation.offset = 0
|
303
|
+
@offset -= permutation.length
|
304
|
+
return permutation, reg_available, reg_consumed, variables
|
305
|
+
end
|
306
|
+
|
307
|
+
#
|
308
|
+
# Render the final buffer.
|
309
|
+
#
|
310
|
+
def buffer
|
311
|
+
previous_offset = nil
|
312
|
+
count = 0
|
313
|
+
# perform an N-pass fixup for offsets...
|
314
|
+
while( true ) do
|
315
|
+
# If we cant get the offsets fixed within a fixed ammount of tries we return
|
316
|
+
# nil to indicate failure and keep searching for a solution that will work.
|
317
|
+
if( count > 64 )
|
318
|
+
return nil
|
319
|
+
end
|
320
|
+
# Reset the solution offset so as to update it for this pass
|
321
|
+
@offset = 0
|
322
|
+
# perform a single pass to ensure we are using the correct offset values
|
323
|
+
@permutations.each do | permutation |
|
324
|
+
permutation.offset = @offset
|
325
|
+
# Note: calling render() can throw both UndefinedPermutation and UnallowedPermutation exceptions,
|
326
|
+
# however as we assume we only ever return the buffer once a final solution has been generated
|
327
|
+
# we should never have either of those exceptions thrown.
|
328
|
+
permutation.render
|
329
|
+
@offset += permutation.length
|
330
|
+
end
|
331
|
+
# If we have generated two consecutive passes which are the same length we can stop fixing up the offsets.
|
332
|
+
if( not previous_offset.nil? and @offset == previous_offset )
|
333
|
+
break
|
334
|
+
end
|
335
|
+
count +=1
|
336
|
+
previous_offset = @offset
|
337
|
+
end
|
338
|
+
# now a final pass to render the solution into the raw buffer
|
339
|
+
raw = ''
|
340
|
+
@permutations.each do | permutation |
|
341
|
+
#$stderr.puts "#{permutation.name} - #{ "0x%08X (%d)" % [ permutation.offset, permutation.length] } "
|
342
|
+
raw << permutation.render
|
343
|
+
end
|
344
|
+
return raw
|
345
|
+
end
|
346
|
+
|
347
|
+
end
|
348
|
+
|
349
|
+
#
|
350
|
+
# Create a new machine instance.
|
351
|
+
#
|
352
|
+
def initialize( badchars, cpu )
|
353
|
+
@badchars = badchars
|
354
|
+
@cpu = cpu
|
355
|
+
|
356
|
+
@reg_available = ::Array.new
|
357
|
+
@reg_consumed = ::Array.new
|
358
|
+
@variables = ::Hash.new
|
359
|
+
@blocks = ::Hash.new
|
360
|
+
@primitives = ::Hash.new
|
361
|
+
@solution = Solution.new
|
362
|
+
|
363
|
+
_create_primitives
|
364
|
+
|
365
|
+
@blocks['begin'] = Block.new( 'begin' )
|
366
|
+
@blocks['begin'] << SymbolicPermutation.new( 'begin', self )
|
367
|
+
|
368
|
+
_create_variable( 'temp' )
|
369
|
+
end
|
370
|
+
|
371
|
+
#
|
372
|
+
# Overloaded by a subclass to return the maximum native general register size supported.
|
373
|
+
#
|
374
|
+
def native_size
|
375
|
+
nil
|
376
|
+
end
|
377
|
+
|
378
|
+
#
|
379
|
+
# Use METASM to assemble a line of asm using this machines current cpu.
|
380
|
+
#
|
381
|
+
def assemble( asm )
|
382
|
+
return Metasm::Shellcode.assemble( @cpu, asm ).encode_string
|
383
|
+
end
|
384
|
+
|
385
|
+
#
|
386
|
+
# Check if a data blob is valid against the badchar list (or perform any other validation here)
|
387
|
+
#
|
388
|
+
def is_valid?( data )
|
389
|
+
if( data.nil? )
|
390
|
+
return false
|
391
|
+
end
|
392
|
+
return Rex::Text.badchar_index( data, @badchars ).nil?
|
393
|
+
end
|
394
|
+
|
395
|
+
#
|
396
|
+
# Generate a 64 bit number whoes bytes are valid in this machine.
|
397
|
+
#
|
398
|
+
def make_safe_qword( number=nil )
|
399
|
+
return _make_safe_number( QWORD, number ) & 0xFFFFFFFFFFFFFFFF
|
400
|
+
end
|
401
|
+
|
402
|
+
#
|
403
|
+
# Generate a 32 bit number whoes bytes are valid in this machine.
|
404
|
+
#
|
405
|
+
def make_safe_dword( number=nil )
|
406
|
+
return _make_safe_number( DWORD, number ) & 0xFFFFFFFF
|
407
|
+
end
|
408
|
+
|
409
|
+
#
|
410
|
+
# Generate a 16 bit number whoes bytes are valid in this machine.
|
411
|
+
#
|
412
|
+
def make_safe_word( number=nil )
|
413
|
+
return _make_safe_number( WORD, number ) & 0xFFFF
|
414
|
+
end
|
415
|
+
|
416
|
+
#
|
417
|
+
# Generate a 8 bit number whoes bytes are valid in this machine.
|
418
|
+
#
|
419
|
+
def make_safe_byte( number=nil )
|
420
|
+
return _make_safe_number( BYTE, number ) & 0xFF
|
421
|
+
end
|
422
|
+
|
423
|
+
#
|
424
|
+
# Create a variable by name which will be assigned a register during generation. We can
|
425
|
+
# optionally assign a static register value to a variable if needed.
|
426
|
+
#
|
427
|
+
def create_variable( name, reg=nil )
|
428
|
+
# Sanity check we aren't trying to create one of the reserved variables.
|
429
|
+
if( name == 'temp' )
|
430
|
+
raise RuntimeError, "Unable to create variable, '#{name}' is a reserved variable name."
|
431
|
+
end
|
432
|
+
return _create_variable( name, reg )
|
433
|
+
end
|
434
|
+
|
435
|
+
#
|
436
|
+
# If the temp variable was assigned we release it.
|
437
|
+
#
|
438
|
+
def release_temp_variable
|
439
|
+
if( @variables['temp'] )
|
440
|
+
regnum = @variables['temp']
|
441
|
+
# Sanity check the temp variable was actually assigned (it may not have been if the last permutation didnot use it)
|
442
|
+
if( regnum )
|
443
|
+
# place the assigned register back in the available list for consumption later.
|
444
|
+
@reg_available.push( @reg_consumed.delete( regnum ) )
|
445
|
+
# unasign the temp vars register
|
446
|
+
@variables['temp'] = nil
|
447
|
+
return true
|
448
|
+
end
|
449
|
+
end
|
450
|
+
return false
|
451
|
+
end
|
452
|
+
|
453
|
+
#
|
454
|
+
# Resolve a variable name into its currently assigned register value.
|
455
|
+
#
|
456
|
+
def variable_value( name, size=nil )
|
457
|
+
# Sanity check we this variable has been created
|
458
|
+
if( not @variables.has_key?( name ) )
|
459
|
+
raise RuntimeError, "Unknown register '#{name}'."
|
460
|
+
end
|
461
|
+
# Pull out its current register value if it has been assigned one
|
462
|
+
regnum = @variables[ name ]
|
463
|
+
if( not regnum )
|
464
|
+
regnum = @reg_available.pop
|
465
|
+
if( not regnum )
|
466
|
+
raise RuntimeError, "Unable to assign variable '#{name}' a register value, none available."
|
467
|
+
end
|
468
|
+
# and add it to the consumed list so we can track it later
|
469
|
+
@reg_consumed << regnum
|
470
|
+
# and now assign the variable the register
|
471
|
+
@variables[ name ] = regnum
|
472
|
+
end
|
473
|
+
# resolve the register number int a string representation (e.g. 0 in x86 is EAX if size is 32)
|
474
|
+
return _register_value( regnum, size )
|
475
|
+
end
|
476
|
+
|
477
|
+
#
|
478
|
+
# Check this solution is still currently valid (as offsets change it may not be).
|
479
|
+
#
|
480
|
+
def solution_is_valid?
|
481
|
+
return self.is_valid?( @solution.buffer )
|
482
|
+
end
|
483
|
+
|
484
|
+
#
|
485
|
+
# As the solution advances we save state for each permutation step in the solution. This lets
|
486
|
+
# use rewind at a later stage if the solving algorithm wishes to perform some backtracking.
|
487
|
+
#
|
488
|
+
def solution_push( permutation )
|
489
|
+
@solution.push( permutation, @reg_available, @reg_consumed, @variables )
|
490
|
+
end
|
491
|
+
|
492
|
+
#
|
493
|
+
# Backtrack one step in the solution and restore the register/variable state.
|
494
|
+
#
|
495
|
+
def solution_pop
|
496
|
+
permutation, @reg_available, @reg_consumed, @variables = @solution.pop
|
497
|
+
|
498
|
+
@reg_available.push( @reg_available.shift )
|
499
|
+
end
|
500
|
+
|
501
|
+
#
|
502
|
+
# Create a block by name and add in its list of permutations.
|
503
|
+
#
|
504
|
+
# XXX: this doesnt support the fuzzy order of block dependencies ala the origional rex::poly
|
505
|
+
def create_block( name, *permutation_sources )
|
506
|
+
# Sanity check we aren't trying to create one of the reserved symbolic blocks.
|
507
|
+
if( name == 'begin' or name == 'end' )
|
508
|
+
raise RuntimeError, "Unable to add block, '#{name}' is a reserved block name."
|
509
|
+
end
|
510
|
+
# If this is the first time this block is being created, create the block object to hold the permutation list
|
511
|
+
if( not @blocks[name] )
|
512
|
+
@blocks[name] = Block.new( name )
|
513
|
+
end
|
514
|
+
# Now create a new permutation object for every one supplied.
|
515
|
+
permutation_sources.each do | source |
|
516
|
+
@blocks[name] << Permutation.new( name, '', self, source )
|
517
|
+
end
|
518
|
+
return name
|
519
|
+
end
|
520
|
+
|
521
|
+
#
|
522
|
+
# Create a block which is based on a primitive defined by this machine.
|
523
|
+
#
|
524
|
+
def create_block_primitive( block_name, primitive_name, *args )
|
525
|
+
# Santiy check this primitive is actually available and is not an internal primitive (begins with an _).
|
526
|
+
if( not @primitives[primitive_name] or primitive_name[0] == "_" )
|
527
|
+
raise RuntimeError, "Unable to add block, Primitive '#{primitive_name}' is not available."
|
528
|
+
end
|
529
|
+
# Sanity check we aren't trying to create one of the reserved symbolic blocks.
|
530
|
+
if( block_name == 'begin' or block_name == 'end' )
|
531
|
+
raise RuntimeError, "Unable to add block, '#{block_name}' is a reserved block name."
|
532
|
+
end
|
533
|
+
return _create_block_primitive( block_name, primitive_name, *args )
|
534
|
+
end
|
535
|
+
|
536
|
+
#
|
537
|
+
# Get the offset for a blocks active permutation. This is easy for backward references as
|
538
|
+
# they will already have been rendered and their sizes known. For forward references we
|
539
|
+
# can't know in advance but the correct value can be known later once the final solution is
|
540
|
+
# available and a final pass to generate the raw buffer is made.
|
541
|
+
#
|
542
|
+
def block_offset( name )
|
543
|
+
if( name == 'end' )
|
544
|
+
return @solution.offset
|
545
|
+
elsif( @blocks[name] )
|
546
|
+
@blocks[name].each do | permutation |
|
547
|
+
if( permutation.active )
|
548
|
+
return permutation.offset
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
# If we are forward referencing a block it will be at least the current solutions offset +1
|
553
|
+
return @solution.offset + 1
|
554
|
+
end
|
555
|
+
|
556
|
+
#
|
557
|
+
# Does a given block exist?
|
558
|
+
#
|
559
|
+
def block_exist?( name )
|
560
|
+
return @blocks.include?( name )
|
561
|
+
end
|
562
|
+
|
563
|
+
#
|
564
|
+
# Does a given block exist?
|
565
|
+
#
|
566
|
+
def variable_exist?( name )
|
567
|
+
return @variables.include?( name )
|
568
|
+
end
|
569
|
+
|
570
|
+
# XXX: ambiguity between variable names and block name may introduce confusion!!! make them be unique.
|
571
|
+
|
572
|
+
#
|
573
|
+
# Resolve a given value into either a number literal, a block offset or
|
574
|
+
# a variables assigned register.
|
575
|
+
#
|
576
|
+
def resolve_value( value, size=nil )
|
577
|
+
if( block_exist?( value ) )
|
578
|
+
return block_offset( value )
|
579
|
+
elsif( variable_exist?( value ) )
|
580
|
+
return variable_value( value, size )
|
581
|
+
end
|
582
|
+
return value.to_i
|
583
|
+
end
|
584
|
+
|
585
|
+
#
|
586
|
+
# Get the block previous to the target block.
|
587
|
+
#
|
588
|
+
def block_previous( target_block )
|
589
|
+
previous_block = nil
|
590
|
+
@blocks.each_key do | current_block |
|
591
|
+
if( current_block == target_block )
|
592
|
+
return previous_block
|
593
|
+
end
|
594
|
+
previous_block = current_block
|
595
|
+
end
|
596
|
+
return nil
|
597
|
+
end
|
598
|
+
|
599
|
+
#
|
600
|
+
# Get the block next to the target block.
|
601
|
+
#
|
602
|
+
def block_next( target_block )
|
603
|
+
@blocks.each_key do | current_block |
|
604
|
+
if( block_previous( current_block ) == target_block )
|
605
|
+
return current_block
|
606
|
+
end
|
607
|
+
end
|
608
|
+
return nil
|
609
|
+
end
|
610
|
+
|
611
|
+
#
|
612
|
+
# Try to generate a solution.
|
613
|
+
#
|
614
|
+
def generate
|
615
|
+
|
616
|
+
if( @blocks.has_key?( 'end' ) )
|
617
|
+
@blocks.delete( 'end' )
|
618
|
+
end
|
619
|
+
|
620
|
+
@blocks['end'] = Block.new( 'end' )
|
621
|
+
@blocks['end'] << SymbolicPermutation.new( 'end', self, 1 )
|
622
|
+
|
623
|
+
# Mix up the permutation orders for each block and create the tree structure.
|
624
|
+
previous = ::Array.new
|
625
|
+
@blocks.each_value do | block |
|
626
|
+
# Shuffle the order of the blocks permutations.
|
627
|
+
block.shuffle
|
628
|
+
# create the tree by adding the current blocks permutations as children of the previous block.
|
629
|
+
current = ::Array.new
|
630
|
+
block.each do | permutation |
|
631
|
+
permutation.remove_children
|
632
|
+
previous.each do | prev |
|
633
|
+
prev.add_child( permutation )
|
634
|
+
end
|
635
|
+
current << permutation
|
636
|
+
end
|
637
|
+
previous = current
|
638
|
+
end
|
639
|
+
|
640
|
+
# Shuffle the order of the available registers
|
641
|
+
@reg_available = @reg_available.shuffle
|
642
|
+
|
643
|
+
# We must try every permutation of the register orders, so if we fail to
|
644
|
+
# generate a solution we rotate the available registers to try again with
|
645
|
+
# a different order. This ensures we perform and exhaustive search.
|
646
|
+
0.upto( @reg_available.length - 1 ) do
|
647
|
+
|
648
|
+
@solution.reset
|
649
|
+
|
650
|
+
# Start from the root node in the solution space and generate a
|
651
|
+
# solution by traversing the solution space's tree structure.
|
652
|
+
if( @blocks['begin'].solve )
|
653
|
+
# Return the solutions buffer (perform a last pass to fixup all offsets)...
|
654
|
+
return @solution.buffer
|
655
|
+
end
|
656
|
+
|
657
|
+
@reg_available.push( @reg_available.shift )
|
658
|
+
end
|
659
|
+
|
660
|
+
# :(
|
661
|
+
nil
|
662
|
+
end
|
663
|
+
|
664
|
+
#
|
665
|
+
# An UndefinedPermutation exception is raised when a permutation can't render yet
|
666
|
+
# as the conditions required are not yet satisfied.
|
667
|
+
#
|
668
|
+
class UndefinedPermutation < RuntimeError
|
669
|
+
def initialize( msg=nil )
|
670
|
+
super
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
#
|
675
|
+
# An UnallowedPermutation exception is raised when a permutation can't ever render
|
676
|
+
# as the conditions supplied are impossible to satisfy.
|
677
|
+
#
|
678
|
+
class UnallowedPermutation < RuntimeError
|
679
|
+
def initialize( msg=nil )
|
680
|
+
super
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
#
|
685
|
+
# An InvalidPermutation exception is raised when a permutation receives a invalid
|
686
|
+
# argument and cannot continue to render. This is a fatal exception.
|
687
|
+
#
|
688
|
+
class InvalidPermutation < RuntimeError
|
689
|
+
def initialize( msg=nil )
|
690
|
+
super
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
protected
|
695
|
+
|
696
|
+
#
|
697
|
+
# Overloaded by a subclass to resolve a register number into a suitable register
|
698
|
+
# name for the target architecture. E.g on x64 the register number 0 with size 64
|
699
|
+
# would resolve to RCX. Size is nil by default to indicate we want the default
|
700
|
+
# machine size, e.g. 32bit DWORD on x86 or 64bit QWORD on x64.
|
701
|
+
#
|
702
|
+
def _register_value( regnum, size=nil )
|
703
|
+
nil
|
704
|
+
end
|
705
|
+
|
706
|
+
#
|
707
|
+
# Perform the actual variable creation.
|
708
|
+
#
|
709
|
+
def _create_variable( name, reg=nil )
|
710
|
+
regnum = nil
|
711
|
+
# Sanity check this variable has not already been created.
|
712
|
+
if( @variables[name] )
|
713
|
+
raise RuntimeError, "Variable '#{name}' is already created."
|
714
|
+
end
|
715
|
+
# If a fixed register is being assigned to this variable then resolve it
|
716
|
+
if( reg )
|
717
|
+
# Resolve the register name into a register number
|
718
|
+
@reg_available.each do | num |
|
719
|
+
if( _register_value( num ) == reg.downcase )
|
720
|
+
regnum = num
|
721
|
+
break
|
722
|
+
end
|
723
|
+
end
|
724
|
+
# If an invalid register name was given or the chosen register is not available we must fail.
|
725
|
+
if( not regnum )
|
726
|
+
raise RuntimeError, "Register '#{reg}' is unknown or unavailable."
|
727
|
+
end
|
728
|
+
# Sanity check another variable isnt assigned this register
|
729
|
+
if( @variables.has_value?( regnum ) )
|
730
|
+
raise RuntimeError, "Register number '#{regnum}' is already consumed by variable '#{@variables[name]}'."
|
731
|
+
end
|
732
|
+
# Finally we consume the register chosen so we dont select it again later.
|
733
|
+
@reg_consumed << @reg_available.delete( regnum )
|
734
|
+
end
|
735
|
+
# Create the variable and assign it a register number (or nil if not yet assigned)
|
736
|
+
@variables[name] = regnum
|
737
|
+
return name
|
738
|
+
end
|
739
|
+
|
740
|
+
#
|
741
|
+
# Create a block which is based on a primitive defined by this machine.
|
742
|
+
#
|
743
|
+
def _create_block_primitive( block_name, primitive_name, *args )
|
744
|
+
# If this is the first time this block is being created, create the array to hold the permutation list
|
745
|
+
if( not @blocks[block_name] )
|
746
|
+
@blocks[block_name] = Block.new( block_name )
|
747
|
+
end
|
748
|
+
# Now create a new permutation object for every one supplied.
|
749
|
+
@primitives[primitive_name].each do | source |
|
750
|
+
@blocks[block_name] << Permutation.new( block_name, primitive_name, self, source, args )
|
751
|
+
end
|
752
|
+
return block_name
|
753
|
+
end
|
754
|
+
|
755
|
+
#
|
756
|
+
# Overloaded by a subclass to create any primitives available in this machine.
|
757
|
+
#
|
758
|
+
def _create_primitives
|
759
|
+
nil
|
760
|
+
end
|
761
|
+
|
762
|
+
#
|
763
|
+
# Rex::Poly::Machine::Primitive
|
764
|
+
#
|
765
|
+
def _create_primitive( name, *permutations )
|
766
|
+
# If this is the first time this primitive is being created, create the array to hold the permutation list
|
767
|
+
if( not @primitives[name] )
|
768
|
+
@primitives[name] = ::Array.new
|
769
|
+
end
|
770
|
+
# Add in the permutation object (Rex::Poly::Machine::Primitive) for every one supplied.
|
771
|
+
permutations.each do | permutation |
|
772
|
+
@primitives[name] << Primitive.new( permutation )
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
#
|
777
|
+
# Helper function to generate a number whoes byte representation is valid in this
|
778
|
+
# machine (does not contain any badchars for example). Optionally we can supply a
|
779
|
+
# number and the resulting addition/subtraction of this number against the newly
|
780
|
+
# generated value is also tested for validity. This helps in the assembly primitives
|
781
|
+
# which can use these values.
|
782
|
+
#
|
783
|
+
def _make_safe_number( bytes, number=nil )
|
784
|
+
format = ''
|
785
|
+
if( bytes == BYTE )
|
786
|
+
format = 'C'
|
787
|
+
elsif( bytes == WORD )
|
788
|
+
format = 'v'
|
789
|
+
elsif( bytes == DWORD )
|
790
|
+
format = 'V'
|
791
|
+
elsif( bytes == QWORD )
|
792
|
+
format = 'Q'
|
793
|
+
else
|
794
|
+
raise RuntimeError, "Invalid size '#{bytes}' used in _make_safe_number."
|
795
|
+
end
|
796
|
+
|
797
|
+
goodchars = (0..255).to_a
|
798
|
+
|
799
|
+
@badchars.unpack( 'C*' ).each do | b |
|
800
|
+
goodchars.delete( b.chr )
|
801
|
+
end
|
802
|
+
|
803
|
+
while( true ) do
|
804
|
+
value = 0
|
805
|
+
|
806
|
+
0.upto( bytes-1 ) do | i |
|
807
|
+
value |= ( (goodchars[ rand(goodchars.length) ] << i*8) & (0xFF << i*8) )
|
808
|
+
end
|
809
|
+
|
810
|
+
if( not is_valid?( [ value ].pack(format) ) or not is_valid?( [ ~value ].pack(format) ) )
|
811
|
+
redo
|
812
|
+
end
|
813
|
+
|
814
|
+
if( not number.nil? )
|
815
|
+
if( not is_valid?( [ value + number ].pack(format) ) or not is_valid?( [ value - number ].pack(format) ) )
|
816
|
+
redo
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
break
|
821
|
+
end
|
822
|
+
|
823
|
+
return value
|
824
|
+
end
|
825
|
+
|
826
|
+
end
|
827
|
+
|
828
|
+
end
|
829
|
+
|
830
|
+
end
|