Skip to content

Commit 4085fa7

Browse files
committed
Merge branch 'stephenfewer-master'
2 parents 3334257 + 4546d14 commit 4085fa7

File tree

8 files changed

+1868
-4
lines changed

8 files changed

+1868
-4
lines changed

lib/rex/arch/x86.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,27 @@ module X86
2222
ESI = DH = SI = 6
2323
EDI = BH = DI = 7
2424

25-
REG_NAMES32 = [ 'eax', 'ecx', 'edx', 'ebx',
26-
'esp', 'ebp', 'esi', 'edi' ] # :nodoc:
27-
25+
REG_NAMES32 = [ 'eax', 'ecx', 'edx', 'ebx', 'esp', 'ebp', 'esi', 'edi' ]
26+
27+
REG_NAMES16 = [ 'ax', 'cx', 'dx', 'bx', 'sp', 'bp', 'si', 'di' ]
28+
29+
REG_NAMES8L = [ 'al', 'cl', 'dl', 'bl', nil, nil, nil, nil ]
30+
2831
# Jump tp a specific register
2932
def self.jmp_reg(str)
3033
reg = reg_number(str)
3134
_check_reg(reg)
3235
"\xFF" + [224 + reg].pack('C')
3336
end
34-
37+
38+
#
39+
# Generate a LOOP instruction (Decrement ECX and jump short if ECX == 0)
40+
#
41+
def self.loop(offset)
42+
"\xE2" + pack_lsb(rel_number(offset, -2))
43+
end
44+
45+
#
3546
# This method returns the opcodes that compose a jump instruction to the
3647
# supplied relative offset.
3748
def self.jmp(addr)

lib/rex/encoder/bloxor/bloxor.rb

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
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

lib/rex/poly.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Poly
44

55
require 'rex/poly/register'
66
require 'rex/poly/block'
7+
require 'rex/poly/machine'
78

89
###
910
#

lib/rex/poly/machine.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
module Rex
3+
4+
module Poly
5+
6+
require 'metasm'
7+
require 'rex/poly/machine/machine'
8+
require 'rex/poly/machine/x86'
9+
10+
end
11+
12+
end

0 commit comments

Comments
 (0)