Class: Rex::Poly::Machine

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/poly/machine/machine.rb

Overview

A machine capable of creating a small blob of code in a metamorphic kind of way. Note: this is designed to perform an exhaustive search for a solution and can be slow. If you need a speedier option, the origional Rex::Polly::Block stuff is a better choice.

Direct Known Subclasses

MachineX86

Defined Under Namespace

Classes: Block, InvalidPermutation, Permutation, Primitive, Solution, SymbolicPermutation, UnallowedPermutation, UndefinedPermutation

Constant Summary collapse

QWORD =
8
DWORD =
4
WORD =
2
BYTE =
1

Instance Method Summary collapse

Constructor Details

#initialize(badchars, cpu) ⇒ Machine

Create a new machine instance.


351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/rex/poly/machine/machine.rb', line 351

def initialize( badchars, cpu )
  @badchars      = badchars
  @cpu           = cpu

  @reg_available = ::Array.new
  @reg_consumed  = ::Array.new
  @variables     = ::Hash.new
  @blocks        = ::Hash.new
  @primitives    = ::Hash.new
  @solution      = Solution.new

  _create_primitives

  @blocks['begin'] = Block.new( 'begin' )
  @blocks['begin'] << SymbolicPermutation.new( 'begin', self )

  _create_variable( 'temp' )
end

Instance Method Details

#assemble(asm) ⇒ Object

Use METASM to assemble a line of asm using this machines current cpu.


380
381
382
# File 'lib/rex/poly/machine/machine.rb', line 380

def assemble( asm )
  return Metasm::Shellcode.assemble( @cpu, asm ).encode_string
end

#block_exist?(name) ⇒ Boolean

Does a given block exist?

Returns:

  • (Boolean)

558
559
560
# File 'lib/rex/poly/machine/machine.rb', line 558

def block_exist?( name )
  return @blocks.include?( name )
end

#block_next(target_block) ⇒ Object

Get the block next to the target block.


601
602
603
604
605
606
607
608
# File 'lib/rex/poly/machine/machine.rb', line 601

def block_next( target_block )
  @blocks.each_key do | current_block |
    if( block_previous( current_block ) == target_block )
      return current_block
    end
  end
  return nil
end

#block_offset(name) ⇒ Object

Get the offset for a blocks active permutation. This is easy for backward references as they will already have been rendered and their sizes known. For forward references we can't know in advance but the correct value can be known later once the final solution is available and a final pass to generate the raw buffer is made.


541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'lib/rex/poly/machine/machine.rb', line 541

def block_offset( name )
  if( name == 'end' )
    return @solution.offset
  elsif( @blocks[name] )
    @blocks[name].each do | permutation |
      if( permutation.active )
        return permutation.offset
      end
    end
  end
  # If we are forward referencing a block it will be at least the current solutions offset +1
  return @solution.offset + 1
end

#block_previous(target_block) ⇒ Object

Get the block previous to the target block.


587
588
589
590
591
592
593
594
595
596
# File 'lib/rex/poly/machine/machine.rb', line 587

def block_previous( target_block )
  previous_block = nil
  @blocks.each_key do | current_block |
    if( current_block == target_block )
      return previous_block
    end
    previous_block = current_block
  end
  return nil
end

#create_block(name, *permutation_sources) ⇒ Object

Create a block by name and add in its list of permutations.

XXX: this doesnt support the fuzzy order of block dependencies ala the origional rex::poly


504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
# File 'lib/rex/poly/machine/machine.rb', line 504

def create_block( name, *permutation_sources )
  # Sanity check we aren't trying to create one of the reserved symbolic blocks.
  if( name == 'begin' or name == 'end' )
    raise RuntimeError, "Unable to add block, '#{name}' is a reserved block name."
  end
  # If this is the first time this block is being created, create the block object to hold the permutation list
  if( not @blocks[name] )
    @blocks[name] = Block.new( name )
  end
  # Now create a new permutation object for every one supplied.
  permutation_sources.each do | source |
    @blocks[name] << Permutation.new( name, '', self, source )
  end
  return name
end

#create_block_primitive(block_name, primitive_name, *args) ⇒ Object

Create a block which is based on a primitive defined by this machine.


523
524
525
526
527
528
529
530
531
532
533
# File 'lib/rex/poly/machine/machine.rb', line 523

def create_block_primitive( block_name, primitive_name, *args )
  # Santiy check this primitive is actually available and is not an internal primitive (begins with an _).
  if( not @primitives[primitive_name] or primitive_name[0] == "_" )
    raise RuntimeError, "Unable to add block, Primitive '#{primitive_name}' is not available."
  end
  # Sanity check we aren't trying to create one of the reserved symbolic blocks.
  if( block_name == 'begin' or block_name == 'end' )
    raise RuntimeError, "Unable to add block, '#{block_name}' is a reserved block name."
  end
  return _create_block_primitive( block_name, primitive_name, *args )
end

#create_variable(name, reg = nil) ⇒ Object

Create a variable by name which will be assigned a register during generation. We can optionally assign a static register value to a variable if needed.


426
427
428
429
430
431
432
# File 'lib/rex/poly/machine/machine.rb', line 426

def create_variable( name, reg=nil )
  # Sanity check we aren't trying to create one of the reserved variables.
  if( name == 'temp' )
    raise RuntimeError, "Unable to create variable, '#{name}' is a reserved variable name."
  end
  return _create_variable( name, reg )
end

#generateObject

Try to generate a solution.


613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
# File 'lib/rex/poly/machine/machine.rb', line 613

def generate

  if( @blocks.has_key?( 'end' ) )
    @blocks.delete( 'end' )
  end

  @blocks['end'] = Block.new( 'end' )
  @blocks['end'] << SymbolicPermutation.new( 'end', self, 1 )

  # Mix up the permutation orders for each block and create the tree structure.
  previous = ::Array.new
  @blocks.each_value do | block |
    # Shuffle the order of the blocks permutations.
    block.shuffle
    # create the tree by adding the current blocks permutations as children of the previous block.
    current = ::Array.new
    block.each do | permutation |
      permutation.remove_children
      previous.each do | prev |
        prev.add_child( permutation )
      end
      current << permutation
    end
    previous = current
  end

  # Shuffle the order of the available registers
  @reg_available = @reg_available.shuffle

  # We must try every permutation of the register orders, so if we fail to
  # generate a solution we rotate the available registers to try again with
  # a different order. This ensures we perform and exhaustive search.
  0.upto( @reg_available.length - 1 ) do

    @solution.reset

    # Start from the root node in the solution space and generate a
    # solution by traversing the solution space's tree structure.
    if( @blocks['begin'].solve )
      # Return the solutions buffer (perform a last pass to fixup all offsets)...
      return @solution.buffer
    end

    @reg_available.push( @reg_available.shift )
  end

  # :(
  nil
end

#is_valid?(data) ⇒ Boolean

Check if a data blob is valid against the badchar list (or perform any other validation here)

Returns:

  • (Boolean)

387
388
389
390
391
392
# File 'lib/rex/poly/machine/machine.rb', line 387

def is_valid?( data )
  if( data.nil? )
    return false
  end
  return Rex::Text.badchar_index( data, @badchars ).nil?
end

#make_safe_byte(number = nil) ⇒ Object

Generate a 8 bit number whoes bytes are valid in this machine.


418
419
420
# File 'lib/rex/poly/machine/machine.rb', line 418

def make_safe_byte( number=nil )
  return _make_safe_number( BYTE, number ) & 0xFF
end

#make_safe_dword(number = nil) ⇒ Object

Generate a 32 bit number whoes bytes are valid in this machine.


404
405
406
# File 'lib/rex/poly/machine/machine.rb', line 404

def make_safe_dword( number=nil )
  return _make_safe_number( DWORD, number ) & 0xFFFFFFFF
end

#make_safe_qword(number = nil) ⇒ Object

Generate a 64 bit number whoes bytes are valid in this machine.


397
398
399
# File 'lib/rex/poly/machine/machine.rb', line 397

def make_safe_qword( number=nil )
  return _make_safe_number( QWORD, number ) & 0xFFFFFFFFFFFFFFFF
end

#make_safe_word(number = nil) ⇒ Object

Generate a 16 bit number whoes bytes are valid in this machine.


411
412
413
# File 'lib/rex/poly/machine/machine.rb', line 411

def make_safe_word( number=nil )
  return _make_safe_number( WORD, number ) & 0xFFFF
end

#native_sizeObject

Overloaded by a subclass to return the maximum native general register size supported.


373
374
375
# File 'lib/rex/poly/machine/machine.rb', line 373

def native_size
  nil
end

#release_temp_variableObject

If the temp variable was assigned we release it.


437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/rex/poly/machine/machine.rb', line 437

def release_temp_variable
  if( @variables['temp'] )
    regnum = @variables['temp']
    # Sanity check the temp variable was actually assigned (it may not have been if the last permutation didnot use it)
    if( regnum )
      # place the assigned register back in the available list for consumption later.
      @reg_available.push( @reg_consumed.delete( regnum ) )
      # unasign the temp vars register
      @variables['temp'] = nil
      return true
    end
  end
  return false
end

#resolve_value(value, size = nil) ⇒ Object

Resolve a given value into either a number literal, a block offset or a variables assigned register.


575
576
577
578
579
580
581
582
# File 'lib/rex/poly/machine/machine.rb', line 575

def resolve_value( value, size=nil )
  if( block_exist?( value ) )
    return block_offset( value )
  elsif( variable_exist?( value ) )
    return variable_value( value, size )
  end
  return value.to_i
end

#solution_is_valid?Boolean

Check this solution is still currently valid (as offsets change it may not be).

Returns:

  • (Boolean)

479
480
481
# File 'lib/rex/poly/machine/machine.rb', line 479

def solution_is_valid?
  return self.is_valid?( @solution.buffer )
end

#solution_popObject

Backtrack one step in the solution and restore the register/variable state.


494
495
496
497
498
# File 'lib/rex/poly/machine/machine.rb', line 494

def solution_pop
  permutation, @reg_available, @reg_consumed, @variables = @solution.pop

  @reg_available.push( @reg_available.shift )
end

#solution_push(permutation) ⇒ Object

As the solution advances we save state for each permutation step in the solution. This lets use rewind at a later stage if the solving algorithm wishes to perform some backtracking.


487
488
489
# File 'lib/rex/poly/machine/machine.rb', line 487

def solution_push( permutation )
  @solution.push( permutation, @reg_available, @reg_consumed, @variables  )
end

#variable_exist?(name) ⇒ Boolean

Does a given block exist?

Returns:

  • (Boolean)

565
566
567
# File 'lib/rex/poly/machine/machine.rb', line 565

def variable_exist?( name )
  return @variables.include?( name )
end

#variable_value(name, size = nil) ⇒ Object

Resolve a variable name into its currently assigned register value.


455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/rex/poly/machine/machine.rb', line 455

def variable_value( name, size=nil )
  # Sanity check we this variable has been created
  if( not @variables.has_key?( name ) )
    raise RuntimeError, "Unknown register '#{name}'."
  end
  # Pull out its current register value if it has been assigned one
  regnum = @variables[ name ]
  if( not regnum )
    regnum = @reg_available.pop
    if( not regnum )
      raise RuntimeError, "Unable to assign variable '#{name}' a register value, none available."
    end
    # and add it to the consumed list so we can track it later
    @reg_consumed << regnum
    # and now assign the variable the register
    @variables[ name ] = regnum
  end
  # resolve the register number int a string representation (e.g. 0 in x86 is EAX if size is 32)
  return _register_value( regnum, size )
end