Permalink
Browse files

open up the bloxor encoder.

  • Loading branch information...
1 parent d0478eb commit 8e6e1bc16408ac51db418c8976bf8defed40d420 @stephenfewer stephenfewer committed Jan 10, 2013
View
@@ -22,16 +22,27 @@ module X86
ESI = DH = SI = 6
EDI = BH = DI = 7
- REG_NAMES32 = [ 'eax', 'ecx', 'edx', 'ebx',
- 'esp', 'ebp', 'esi', 'edi' ] # :nodoc:
-
+ REG_NAMES32 = [ 'eax', 'ecx', 'edx', 'ebx', 'esp', 'ebp', 'esi', 'edi' ]
+
+ REG_NAMES16 = [ 'ax', 'cx', 'dx', 'bx', 'sp', 'bp', 'si', 'di' ]
+
+ REG_NAMES8L = [ 'al', 'cl', 'dl', 'bl', nil, nil, nil, nil ]
+
# Jump tp a specific register
def self.jmp_reg(str)
reg = reg_number(str)
_check_reg(reg)
"\xFF" + [224 + reg].pack('C')
end
-
+
+ #
+ # Generate a LOOP instruction (Decrement ECX and jump short if ECX == 0)
+ #
+ def self.loop(offset)
+ "\xE2" + pack_lsb(rel_number(offset, -2))
+ end
+
+ #
# This method returns the opcodes that compose a jump instruction to the
# supplied relative offset.
def self.jmp(addr)
@@ -0,0 +1,326 @@
+
+require 'rex/poly/machine'
+
+module Rex
+
+module Encoder
+
+ class BloXor < Msf::Encoder
+
+ def initialize( *args )
+ super
+ @machine = nil
+ @blocks_out = []
+ @block_size = 0
+ end
+
+ #
+ #
+ #
+ def decoder_stub( state )
+
+ if( not state.decoder_stub )
+ @blocks_out = []
+ @block_size = 0
+
+ # XXX: It would be ideal to use a random block size but unless we know the maximum size our final encoded
+ # blob can be we should instead start with the smallest block size and go up to avoid generating
+ # anything too big (if we knew the max size we could try something smaller if we generated a blob too big)
+ #block_sizes = (1..state.buf.length).to_a.shuffle
+ #block_sizes.each do | len |
+
+ 1.upto( state.buf.length ) do | len |
+
+ # For now we ignore all odd sizes to help with performance (The rex poly machine
+ # doesnt have many load/store primitives that can handle byte sizes efficiently)
+ if( len % 2 != 0 )
+ next
+ end
+
+ blocks, size = compute_encoded( state, len )
+ if( blocks and size )
+
+ # We sanity check that the newly generated block ammount and the block size
+ # are not in the badchar list when converted into a hex form. Helps speed
+ # things up a great deal when generating a decoder stub later as these
+ # values may be used throughout.
+
+ if( not number_is_valid?( state, blocks.length - 1 ) or not number_is_valid?( state, ~( blocks.length - 1 ) ) )
+ next
+ end
+
+ if( not number_is_valid?( state, size ) or not number_is_valid?( state, ~size ) )
+ next
+ end
+
+ @blocks_out = blocks
+ @block_size = size
+
+ break
+ end
+ end
+
+ raise RuntimeError, "Unable to generate seed block." if( @blocks_out.empty? )
+
+ state.decoder_stub = compute_decoder( state )
+ end
+
+ state.decoder_stub
+ end
+
+ #
+ #
+ #
+ def encode_block( state, data )
+
+ buffer = ''
+
+ @blocks_out.each do | block |
+ buffer << block.pack( 'C*' )
+ end
+
+ buffer
+ end
+
+ protected
+
+ #
+ # Is a number in its byte form valid against the badchars?
+ #
+ def number_is_valid?( state, number )
+ size = 'C'
+ if( number > 0xFFFF )
+ size = 'V'
+ elsif( number > 0xFF )
+ size = 'v'
+ end
+ return Rex::Text.badchar_index( [ number ].pack( size ), state.badchars ).nil?
+ end
+
+ #
+ # Calculate Shannon's entropy.
+ #
+ def entropy( data )
+ entropy = 0.to_f
+ (0..255).each do | byte |
+ freq = data.to_s.count( byte.chr ).to_f / data.to_s.length
+ if( freq > 0 )
+ entropy -= freq * Math.log2( freq )
+ end
+ end
+ return entropy / 8
+ end
+
+ #
+ # Compute the encoded blocks (and associated seed)
+ #
+ def compute_encoded( state, len )
+
+ blocks_in = ::Array.new
+
+ input = '' << state.buf
+
+ block_padding = ( input.length % len ) > 0 ? len - ( input.length % len ) : 0
+
+ if( block_padding > 0 )
+ 0.upto( block_padding-1 ) do
+ input << [ rand( 255 ) ].pack( 'C' )
+ end
+ end
+
+ while( input.length > 0 )
+ blocks_in << input[0..len-1].unpack( 'C*' )
+ input = input[len..input.length]
+ end
+
+ seed = compute_seed( blocks_in, len, block_padding, state.badchars.unpack( 'C*' ) )
+
+ if( not seed )
+ return [ nil, nil ]
+ end
+
+ blocks_out = [ seed ]
+
+ blocks_in.each do | block |
+ blocks_out << compute_block( blocks_out.last, block )
+ end
+
+ return [ blocks_out, len ]
+ end
+
+ #
+ # Generate the decoder stub which is functionally equivalent to the following:
+ #
+ # source = &end;
+ # dest = source + BLOCK_SIZE;
+ # counter = BLOCK_COUNT * ( BLOCK_SIZE / chunk_size );
+ # do
+ # {
+ # encoded = *(CHUNK_SIZE *)dest;
+ # dest += chunk_size;
+ # decoded = *(CHUNK_SIZE *)source;
+ # *(CHUNK_SIZE *)source = decoded ^ encoded;
+ # source += chunk_size;
+ # } while( --counter );
+ #
+ # end:
+ #
+ def compute_decoder( state )
+
+ @machine.create_variable( 'source' )
+ @machine.create_variable( 'dest' )
+ @machine.create_variable( 'counter' )
+ @machine.create_variable( 'encoded' )
+ @machine.create_variable( 'decoded' )
+
+ chunk_size = Rex::Poly::Machine::BYTE
+ if( @machine.native_size() == Rex::Poly::Machine::QWORD )
+ if( @block_size % Rex::Poly::Machine::QWORD == 0 )
+ chunk_size = Rex::Poly::Machine::QWORD
+ elsif( @block_size % Rex::Poly::Machine::DWORD == 0 )
+ chunk_size = Rex::Poly::Machine::DWORD
+ elsif( @block_size % Rex::Poly::Machine::WORD == 0 )
+ chunk_size = Rex::Poly::Machine::WORD
+ end
+ elsif( @machine.native_size() == Rex::Poly::Machine::DWORD )
+ if( @block_size % Rex::Poly::Machine::DWORD == 0 )
+ chunk_size = Rex::Poly::Machine::DWORD
+ elsif( @block_size % Rex::Poly::Machine::WORD == 0 )
+ chunk_size = Rex::Poly::Machine::WORD
+ end
+ elsif( @machine.native_size() == Rex::Poly::Machine::WORD )
+ if( @block_size % Rex::Poly::Machine::WORD == 0 )
+ chunk_size = Rex::Poly::Machine::WORD
+ end
+ end
+
+ # Block 1 - Set the source variable to the address of the start block
+ @machine.create_block_primitive( 'block1', 'set', 'source', 'location' )
+
+ # Block 2 - Set the source variable to the address of the 1st encoded block
+ @machine.create_block_primitive( 'block2', 'add', 'source', 'end' )
+
+ # Block 3 - Set the destingation variable to the value of the source variable
+ @machine.create_block_primitive( 'block3', 'set', 'dest', 'source' )
+
+ # Block 4 - Set the destingation variable to the address of the 2nd encoded block
+ @machine.create_block_primitive( 'block4', 'add', 'dest', @block_size )
+
+ # Block 5 - Sets the loop counter to the number of blocks to process
+ @machine.create_block_primitive( 'block5', 'set', 'counter', ( ( @block_size / chunk_size ) * (@blocks_out.length - 1) ) )
+
+ # Block 6 - Set the encoded variable to the byte pointed to by the dest variable
+ @machine.create_block_primitive( 'block6', 'load', 'encoded', 'dest', chunk_size )
+
+ # Block 7 - Increment the destination variable by one
+ @machine.create_block_primitive( 'block7', 'add', 'dest', chunk_size )
+
+ # Block 8 - Set the decoded variable to the byte pointed to by the source variable
+ @machine.create_block_primitive( 'block8', 'load', 'decoded', 'source', chunk_size )
+
+ # Block 9 - Xor the decoded variable with the encoded variable
+ @machine.create_block_primitive( 'block9', 'xor', 'decoded', 'encoded' )
+
+ # Block 10 - store the newly decoded byte
+ @machine.create_block_primitive( 'block10', 'store', 'source', 'decoded', chunk_size )
+
+ # Block 11 - Increment the source variable by one
+ @machine.create_block_primitive( 'block11', 'add', 'source', chunk_size )
+
+ # Block 12 - Jump back up to the outer_loop block while the counter variable > 0
+ @machine.create_block_primitive( 'block12', 'loop', 'counter', 'block6' )
+
+ # Try to generate the decoder stub...
+ decoder = @machine.generate
+
+ if( not decoder )
+ raise RuntimeError, "Unable to generate decoder stub."
+ end
+
+ decoder
+ end
+
+ #
+ # Compute the seed block which will successfully decode all proceeding encoded
+ # blocks while ensuring the encoded blocks do not contain any badchars.
+ #
+ def compute_seed( blocks_in, block_size, block_padding, badchars )
+ seed = []
+ redo_bytes = []
+
+ 0.upto( block_size-1 ) do | index |
+
+ seed_bytes = (0..255).sort_by do
+ rand()
+ end
+
+ seed_bytes.each do | seed_byte |
+
+ next if( badchars.include?( seed_byte ) )
+
+ success = true
+
+ previous_byte = seed_byte
+
+ if( redo_bytes.length < 256 )
+ redo_bytes = (0..255).sort_by do
+ rand()
+ end
+ end
+
+ blocks_in.each do | block |
+
+ decoded_byte = block[ index ]
+
+ encoded_byte = previous_byte ^ decoded_byte
+
+ if( badchars.include?( encoded_byte ) )
+ # the padding bytes we added earlier can be changed if they are causing us to fail.
+ if( block == blocks_in.last and index >= (block_size-block_padding) )
+ if( redo_bytes.empty? )
+ success = false
+ break
+ end
+ block[ index ] = redo_bytes.shift
+ redo
+ end
+
+ success = false
+ break
+ end
+
+ previous_byte = encoded_byte
+ end
+
+ if( success )
+ seed << seed_byte
+ break
+ end
+ end
+
+ end
+
+ if( seed.length == block_size )
+ return seed
+ end
+
+ return nil
+ end
+
+ #
+ # Compute the next encoded block by xoring the previous
+ # encoded block with the next decoded block.
+ #
+ def compute_block( encoded, decoded )
+ block = []
+ 0.upto( encoded.length-1 ) do | index |
+ block << ( encoded[ index ] ^ decoded[ index ] )
+ end
+ return block
+ end
+
+ end
+
+end
+
+end
View
@@ -4,6 +4,7 @@ module Poly
require 'rex/poly/register'
require 'rex/poly/block'
+require 'rex/poly/machine'
###
#
View
@@ -0,0 +1,12 @@
+
+module Rex
+
+ module Poly
+
+ require 'metasm'
+ require 'rex/poly/machine/machine'
+ require 'rex/poly/machine/x86'
+
+ end
+
+end
Oops, something went wrong.

0 comments on commit 8e6e1bc

Please sign in to comment.