class Rex::Poly::MachineX86

A subclass to represent a Rex poly machine on the x86 architecture.

Public Class Methods

new( badchars='', consume_base_pointer=nil, consume_stack_pointer=true ) click to toggle source
Calls superclass method Rex::Poly::Machine::new
# File lib/rex/poly/machine/x86.rb, line 11
def initialize( badchars='', consume_base_pointer=nil, consume_stack_pointer=true )
  super( badchars, Metasm::Ia32.new )

  @reg_available << Rex::Arch::X86::EAX
  @reg_available << Rex::Arch::X86::EBX
  @reg_available << Rex::Arch::X86::ECX
  @reg_available << Rex::Arch::X86::EDX
  @reg_available << Rex::Arch::X86::ESI
  @reg_available << Rex::Arch::X86::EDI
  @reg_available << Rex::Arch::X86::EBP
  @reg_available << Rex::Arch::X86::ESP

  # By default we consume the EBP register if badchars contains \x00. This helps speed
  # things up greatly as many instructions opperating on EBP introduce a NULL byte. For
  # example, a MOV instruction with EAX as the source operand is as follows:
  #     8B08    mov ecx, [eax]
  # but the same instruction with EBP as the source operand is as follows:
  #     8B4D00  mov ecx, [ebp] ; This is assembled as 'mov ecx, [ebp+0]'
  # we can see that EBP is encoded differently with an offset included. We can still
  # try to generate a solution with EBP included and \x00 in the badchars list but
  # it can take considerably longer.
  if( ( consume_base_pointer.nil? and not Rex::Text.badchar_index( "\x00", @badchars ).nil? ) or consume_base_pointer == true )
    create_variable( 'base_pointer', 'ebp' )
  end

  # By default we consume the ESP register to avoid munging the stack.
  if( consume_stack_pointer )
    create_variable( 'stack_pointer', 'esp' )
  end

  # discover all the safe FPU instruction we can use.
  @safe_fpu_instructions = ::Array.new
  Rex::Arch::X86.fpu_instructions.each do | fpu |
    if( is_valid?( fpu ) )
      @safe_fpu_instructions << fpu
    end
  end
end

Public Instance Methods

create_block_primitive( block_name, primitive_name, *args ) click to toggle source

Overload this method to intercept the 'set' primitive with the 'location' keyword and create the block with the '_set_variable_location'. We do this to keep a consistent style.

# File lib/rex/poly/machine/x86.rb, line 62
def create_block_primitive( block_name, primitive_name, *args )
  if( primitive_name == 'set' and args.length == 2 and args[1] == 'location' )
    _create_block_primitive( block_name, '_set_variable_location', args[0] )
  else
    super
  end
end
native_size() click to toggle source

The general purpose registers are 32bit

# File lib/rex/poly/machine/x86.rb, line 53
def native_size
  Rex::Poly::Machine::DWORD
end

Protected Instance Methods

_create_primitives() click to toggle source

Create the x86 primitives.

# File lib/rex/poly/machine/x86.rb, line 115
def _create_primitives

  #
  # Create the '_set_variable_location' primitive. The first param it the variable to place the current
  # blocks location value in.
  #
  _create_primitive( '_set_variable_location',
    ::Proc.new do | block, machine, variable |
      if( @safe_fpu_instructions.empty? )
        raise UnallowedPermutation
      end
      [
        "dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
        "mov #{machine.variable_value( 'temp' )}, esp",
        "fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
        "pop #{machine.variable_value( variable )}"
      ]
    end,
    ::Proc.new do | block, machine, variable |
      if( @safe_fpu_instructions.empty? )
        raise UnallowedPermutation
      end
      [
        "dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
        "mov #{machine.variable_value( 'temp' )}, esp",
        "fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
        "pop #{machine.variable_value( variable )}"
      ]
    end,
    ::Proc.new do | block, machine, variable |
      if( @safe_fpu_instructions.empty? )
        raise UnallowedPermutation
      end
      [
        "dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
        "push esp",
        "pop #{machine.variable_value( 'temp' )}",
        "fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
        "pop #{machine.variable_value( variable )}"
      ]
    end,
    ::Proc.new do | block, machine, variable |
      if( @safe_fpu_instructions.empty? )
        raise UnallowedPermutation
      end
      [
        "dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
        "fnstenv [ esp - 12 ]",
        "pop #{machine.variable_value( variable )}"
      ]
    end,
    ::Proc.new do | block, machine, variable |
      [
        "call $+5",
        "pop #{machine.variable_value( variable )}",
        "push #{machine.block_offset( block ) + 5}",
        "pop #{machine.variable_value( 'temp' )}",
        "sub #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
      ]
    end,
    ::Proc.new do | block, machine, variable |
      [
        "db 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0",
        "pop #{machine.variable_value( variable )}",
        "push #{machine.block_offset( block ) + 5}",
        "pop #{machine.variable_value( 'temp' )}",
        "sub #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
      ]
    end
  )

  #
  # Create the 'loop' primitive. The first param it the counter variable which holds the number of
  # times to perform the loop. The second param it the destination block to loop to.
  #
  _create_primitive( 'loop',
    ::Proc.new do | block, machine, counter, destination |
      if( machine.variable_value( counter ) != Rex::Arch::X86::REG_NAMES32[ Rex::Arch::X86::ECX ] )
        # we raise and UndefinedPermutation exception to indicate that untill a valid register (ECX) is
        # chosen we simply can't render this. This lets the machine know we can still try to use this
        # permutation and at a later stage the requirements (counter==ecx) may be satisfied.
        raise UndefinedPermutation
      end
      offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
      Rex::Arch::X86.loop( offset )
    end,
    ::Proc.new do | block, machine, counter, destination |
      offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
      [
        "dec #{machine.variable_value( counter )}",
        "test #{machine.variable_value( counter )}, #{machine.variable_value( counter )}",
        # JNZ destination
        "db 0x0F, 0x85 dd #{ "0x%08X" % [ offset & 0xFFFFFFFF ] }"
      ]
    end
  )

  #
  # Create the 'xor' primitive. The first param it the variable to xor with the second param value which
  # can be either a variable, literal or block offset.
  #
  _create_primitive( 'xor',
    ::Proc.new do | block, machine, variable, value |
      [
        "xor #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      # a ^ b == (a | b) & ~(a & b)
      [
        "mov #{machine.variable_value( 'temp' )}, #{machine.variable_value( variable )}",
        "or #{machine.variable_value( 'temp' )}, #{machine.resolve_value( value )}",
        "and #{machine.variable_value( variable )}, #{machine.resolve_value( value )}",
        "not #{machine.variable_value( variable )}",
        "and #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
      ]
    end
  )

  #
  # Create the 'goto' primitive. The first param is a destination block to jump to.
  #
  _create_primitive( 'goto',
    ::Proc.new do | block, machine, destination |
      offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
      if( ( offset > 0 and offset > 127 ) or ( offset < 0 and offset < -127 ) )
        raise UnallowedPermutation
      end
      [
        # short relative jump
        "db 0xEB db #{ "0x%02X" % [ offset & 0xFF ] }"
      ]
    end,
    ::Proc.new do | block, machine, destination |
      offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
      [
        # near relative jump
        "db 0xE9 dd #{ "0x%08X" % [ offset & 0xFFFFFFFF ] }"
      ]
    end
  )

  #
  # Create the 'add' primitive. The first param it the variable which will be added to the second
  # param, which may either be a literal number value, a variables assigned register or a block
  # name, in which case the block offset will be used.
  #
  _create_primitive( 'add',
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      [
        "lea #{machine.variable_value( variable )}, [ #{machine.variable_value( variable )} + #{machine.resolve_value( value )} ]"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      [
        "push #{machine.resolve_value( value )}",
        "add #{machine.variable_value( variable )}, [esp]",
        "pop #{machine.variable_value( 'temp' )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      [
        "add #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      [
        "sub #{machine.variable_value( variable )}, #{ "0x%08X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFFFFFF ] }"
      ]
    end
    # ::Proc.new do | block, machine, variable, value |
      # if( machine.variable_exist?( value ) )
      #   raise UnallowedPermutation
      # end
      # [
        # "push #{ "0x%08X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFFFFFF ] }",
        # "pop #{machine.variable_value( 'temp' )}",
        # "not #{machine.variable_value( 'temp' )}",
        # "add #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
      # ]
    # end,
    # ::Proc.new do | block, machine, variable, value |
      # if( machine.variable_exist?( value ) )
      #   raise UnallowedPermutation
      # end
      # [
        # "xor #{machine.variable_value( 'temp' )}, #{machine.variable_value( 'temp' )}",
        # "mov #{machine.variable_value( 'temp', 16 )}, #{ "0x%04X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFF ] }",
        # "not #{machine.variable_value( 'temp', 16 )}",
        # "add #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
      # ]
    # end,
  )

  #
  # Create the 'set' primitive. The first param it the variable which will be set. the second
  # param is the value to set the variable to (a variable, block or literal).
  #
  _create_primitive( 'set',
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      [
        "push #{ "0x%08X" % [ ~machine.resolve_value( value ) & 0xFFFFFFFF ] }",
        "pop #{machine.variable_value( variable )}",
        "not #{machine.variable_value( variable )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      if( machine.resolve_value( value, WORD ) > 0xFFFF )
        raise UndefinedPermutation
      end
      [
        "xor #{machine.variable_value( variable )}, #{machine.variable_value( variable )}",
        "mov #{machine.variable_value( variable, WORD )}, #{ "0x%04X" % [ ~machine.resolve_value( value, WORD ) & 0xFFFF ] }",
        "not #{machine.variable_value( variable, WORD )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      [
        "push #{machine.resolve_value( value )}",
        "pop #{machine.variable_value( variable )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      [
        "mov #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      if( machine.resolve_value( value, WORD ) > 0xFFFF )
        raise UndefinedPermutation
      end
      [
        "xor #{machine.variable_value( variable )}, #{machine.variable_value( variable )}",
        "mov #{machine.variable_value( variable, WORD )}, #{ "0x%04X" % [ machine.resolve_value( value, WORD ) & 0xFFFF ] }"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      dword = machine.make_safe_dword( machine.resolve_value( value ) )
      [
        "mov #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword ] }",
        "sub #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword - machine.resolve_value( value ) ] }"
      ]
    end,
    ::Proc.new do | block, machine, variable, value |
      if( machine.variable_exist?( value ) )
        raise UnallowedPermutation
      end
      dword = machine.make_safe_dword( machine.resolve_value( value ) )
      [
        "mov #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword - machine.resolve_value( value ) ] }",
        "add #{machine.variable_value( variable )}, #{ "0x%08X" % [ ~dword & 0xFFFFFFFF ] }",
        "not #{machine.variable_value( variable )}"
      ]
    end
  )

  #
  # Create the 'load' primitive. The first param it the variable which will be set. The second
  # param is the value (either a variable or literal) to load from. the third param is the size
  # of the load operation, either DWORD, WORD or BYTE.
  #
  _create_primitive( 'load',
    ::Proc.new do | block, machine, variable, value, size |
      result = nil
      if( size == Rex::Poly::Machine::DWORD )
        result = [ "mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]" ]
      elsif( size == Rex::Poly::Machine::WORD )
        result = [ "movzx #{machine.variable_value( variable )}, word [#{machine.resolve_value( value )}]" ]
      elsif( size == Rex::Poly::Machine::BYTE )
        result = [ "movzx #{machine.variable_value( variable )}, byte [#{machine.resolve_value( value )}]" ]
      else
        raise InvalidPermutation
      end
      result
    end,
    ::Proc.new do | block, machine, variable, value, size |
      result = nil
      if( size == Rex::Poly::Machine::DWORD )
        # we raise and UnallowedPermutation here as this permutation should only satisfy requests for
        # sizes of WORD or BYTE, any DWORD requests will be satisfied by the above permutation (otherwise
        # we would just be duplicating a 'mov dest, [src]' sequence which is the same as above.
        raise UnallowedPermutation
      elsif( size == Rex::Poly::Machine::WORD )
        result = [
          "mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]",
          "shl #{machine.variable_value( variable )}, 16",
          "shr #{machine.variable_value( variable )}, 16"
        ]
      elsif( size == Rex::Poly::Machine::BYTE )
        result = [
          "mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]",
          "shl #{machine.variable_value( variable )}, 24",
          "shr #{machine.variable_value( variable )}, 24"
        ]
      else
        raise InvalidPermutation
      end
      result
    end,
    ::Proc.new do | block, machine, variable, value, size |
      result = nil
      if( size == Rex::Poly::Machine::DWORD )
        result = [
          "push [#{machine.resolve_value( value )}]",
          "pop #{machine.variable_value( variable )}"
        ]
      elsif( size == Rex::Poly::Machine::WORD )
        result = [
          "push [#{machine.resolve_value( value )}]",
          "pop #{machine.variable_value( variable )}",
          "shl #{machine.variable_value( variable )}, 16",
          "shr #{machine.variable_value( variable )}, 16"
        ]
      elsif( size == Rex::Poly::Machine::BYTE )
        result = [
          "push [#{machine.resolve_value( value )}]",
          "pop #{machine.variable_value( variable )}",
          "shl #{machine.variable_value( variable )}, 24",
          "shr #{machine.variable_value( variable )}, 24"
        ]
      else
        raise InvalidPermutation
      end
      result
    end
  )

  #
  # Create the 'store' primitive.
  #
  _create_primitive( 'store',
    ::Proc.new do | block, machine, variable, value, size |
      result = nil
      if( size == Rex::Poly::Machine::DWORD )
        result = [ "mov [#{machine.variable_value( variable )}], #{machine.resolve_value( value )}" ]
      elsif( size == Rex::Poly::Machine::WORD )
        result = [ "mov word [#{machine.variable_value( variable )}], #{machine.resolve_value( value, WORD )}" ]
      elsif( size == Rex::Poly::Machine::BYTE )
        if( machine.resolve_value( value, BYTE ).nil? )
          # so long as we cant resolve the variable to an 8bit register value (AL,BL,CL,DL) we must raise
          # an UndefinedPermutation exception (this will happen when the variable has been assigned to ESI,
          # EDI, EBP or ESP which dont have a low byte representation)
          raise UndefinedPermutation
        end
        result = [ "mov byte [#{machine.variable_value( variable )}], #{machine.resolve_value( value, BYTE )}" ]
      else
        raise InvalidPermutation
      end
      result
    end,
    ::Proc.new do | block, machine, variable, value, size |
      result = nil
      if( size == Rex::Poly::Machine::DWORD )
        result = [
          "push #{machine.resolve_value( value )}",
          "pop [#{machine.variable_value( variable )}]"
        ]
      elsif( size == Rex::Poly::Machine::WORD )
        result = [
          "push #{machine.resolve_value( value, WORD )}",
          "pop word [#{machine.variable_value( variable )}]"
        ]
      else
        # we can never do this permutation for BYTE size (or any other size)
        raise UnallowedPermutation
      end
      result
    end
  )
end
_register_value( regnum, size=nil ) click to toggle source

Resolve a register number into a suitable register name.

# File lib/rex/poly/machine/x86.rb, line 92
def _register_value( regnum, size=nil )
  value = nil
  # we default to a native 32 bits if no size is specified.
  if( size.nil? )
    size = native_size()
  end

  if( size == Rex::Poly::Machine::DWORD )
    value = Rex::Arch::X86::REG_NAMES32[ regnum ]
  elsif( size == Rex::Poly::Machine::WORD )
    value = Rex::Arch::X86::REG_NAMES16[ regnum ]
  elsif( size == Rex::Poly::Machine::BYTE )
    # (will return nil for ESI,EDI,EBP,ESP)
    value = Rex::Arch::X86::REG_NAMES8L[ regnum ]
  else
    raise RuntimeError, "Register number '#{regnum}' (size #{size.to_i}) is unavailable."
  end
  return value
end