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.



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

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.



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

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

#block_exist?(name) ⇒ Boolean

Does a given block exist?

Returns:

  • (Boolean)


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

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

#block_next(target_block) ⇒ Object

Get the block next to the target block.



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

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.



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

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.



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

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



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

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.



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

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.



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

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.



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
662
# File 'lib/rex/poly/machine/machine.rb', line 614

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)


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

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.



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

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.



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

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.



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

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.



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

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.



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

def native_size
  nil
end

#release_temp_variableObject

If the temp variable was assigned we release it.



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

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.



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

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)


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

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.



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

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.



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

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

#variable_exist?(name) ⇒ Boolean

Does a given block exist?

Returns:

  • (Boolean)


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

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.



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

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