module Rex::Arch::X86

everything here is mostly stole from vlad's perl x86 stuff

Constants

EAX

Register number constants

EBP
EBX
ECX
EDI
EDX
ESI
ESP
REG_NAMES16
REG_NAMES32
REG_NAMES8L

Public Class Methods

add(val, reg, badchars = '', adjust = false, bits = 0) click to toggle source

This method generates the opcodes equivalent to subtracting with a negative value from a given register.

# File lib/rex/arch/x86.rb, line 342
def self.add(val, reg, badchars = '', adjust = false, bits = 0)
  sub(val, reg, badchars, true, adjust, bits)
end
adjust_reg(reg, adjustment) click to toggle source

This method adjusts the value of the ESP register by a given amount.

# File lib/rex/arch/x86.rb, line 370
def self.adjust_reg(reg, adjustment)
  if (adjustment > 0)
    sub(adjustment, reg, '', false, false, 32)
  else
    add(adjustment, reg, '', true, 32)
  end
end
call(addr) click to toggle source

This method returns the opcodes that compose a relative call instruction to the address specified.

# File lib/rex/arch/x86.rb, line 107
def self.call(addr)
  "\xe8" + pack_dword(rel_number(addr, -5))
end
clear(reg, badchars = '') click to toggle source

This method generates an instruction that clears the supplied register in a manner that attempts to avoid bad characters, if supplied.

# File lib/rex/arch/x86.rb, line 200
def self.clear(reg, badchars = '')
  _check_reg(reg)
  return set(reg, 0, badchars)
end
copy_to_stack(len) click to toggle source

Generates a buffer that will copy memory immediately following the stub that is generated to be copied to the stack

# File lib/rex/arch/x86.rb, line 77
def self.copy_to_stack(len)
  # four byte align
  len = (len + 3) & ~0x3

  stub =
    "\xeb\x0f"+                # jmp _end
    push_dword(len)+           # push n
    "\x59"+                    # pop ecx
    "\x5e"+                    # pop esi
    "\x29\xcc"+                # sub esp, ecx
    "\x89\xe7"+                # mov edi, esp
    "\xf3\xa4"+                # rep movsb
    "\xff\xe4"+                # jmp esp
    "\xe8\xec\xff\xff\xff"     # call _start

  stub
end
dword_adjust(dword, amount=0) click to toggle source

This method adds/subs a packed long integer

# File lib/rex/arch/x86.rb, line 53
def self.dword_adjust(dword, amount=0)
  pack_dword(dword.unpack('V')[0] + amount)
end
encode_effective(shift, dst) click to toggle source

This method generates the encoded effective value for a register.

# File lib/rex/arch/x86.rb, line 150
def self.encode_effective(shift, dst)
  return (0xc0 | (shift << 3) | dst)
end
encode_modrm(dst, src) click to toggle source

This method generates the mod r/m character for a source and destination register.

# File lib/rex/arch/x86.rb, line 158
def self.encode_modrm(dst, src)
  _check_reg(dst, src)
  return (0xc0 | src | dst << 3).chr
end
fpu_instructions() click to toggle source

This method returns an array of 'safe' FPU instructions

# File lib/rex/arch/x86.rb, line 398
def self.fpu_instructions
  fpus = []

  0xe8.upto(0xee) { |x| fpus << "\xd9" + x.chr }
  0xc0.upto(0xcf) { |x| fpus << "\xd9" + x.chr }
  0xc0.upto(0xdf) { |x| fpus << "\xda" + x.chr }
  0xc0.upto(0xdf) { |x| fpus << "\xdb" + x.chr }
  0xc0.upto(0xc7) { |x| fpus << "\xdd" + x.chr }

  fpus << "\xd9\xd0"
  fpus << "\xd9\xe1"
  fpus << "\xd9\xf6"
  fpus << "\xd9\xf7"
  fpus << "\xd9\xe5"

  # This FPU instruction seems to fail consistently on Linux
  #fpus << "\xdb\xe1"

  fpus
end
geteip_fpu(badchars, modified_registers = []) click to toggle source

This method returns an array containing a geteip stub, a register, and an offset This method will return nil if the getip generation fails

# File lib/rex/arch/x86.rb, line 423
def self.geteip_fpu(badchars, modified_registers = [])
  #
  # Default badchars to an empty string
  #
  badchars ||= ''

  #
  # Bail out early if D9 is restricted
  #
  return nil if badchars.index("\xd9")

  #
  # Create a list of FPU instructions
  #
  fpus = *self.fpu_instructions
  bads = []
  badchars.each_byte  do |c|
    fpus.each do |str|
      bads << str if (str.index(c.chr))
    end
  end
  bads.each { |str| fpus.delete(str) }
  return nil if fpus.length == 0

  #
  # Create a list of registers to use for fnstenv
  #
  dsts = []
  0.upto(7) do |c|
    dsts << c if (not badchars.index( (0x70+c).chr ))
  end

  if (dsts.include?(ESP) and badchars.index("\x24"))
    dsts.delete(ESP)
  end

  return nil if dsts.length == 0

  #
  # Grab a random FPU instruction
  #
  fpu = fpus[ rand(fpus.length) ]

  #
  # Grab a random register from dst
  #
  while(dsts.length > 0)
    buf = ''
    mod_registers = [ESP]
    dst = dsts[ rand(dsts.length) ]
    dsts.delete(dst)

    # If the register is not ESP, copy ESP
    if (dst != ESP)
      mod_registers.push(dst)
      if badchars.index( (0x70 + dst).chr )
        mod_registers.pop(dst)
        next
      end

      if !(badchars.index("\x89") or badchars.index( (0xE0+dst).chr ))
        buf << "\x89" + (0xE0 + dst).chr
      else
        if badchars.index("\x54")
          mod_registers.pop(dst)
          next
        end
        if badchars.index( (0x58+dst).chr )
          mod_registers.pop(dst)
          next
        end
        buf << "\x54" + (0x58 + dst).chr
      end
    end

    pad = 0
    while (pad < (128-12) and badchars.index( (256-12-pad).chr))
      pad += 4
    end

    # Give up on finding a value to use here
    if (pad == (128-12))
      return nil
    end

    out = buf + fpu + "\xd9" + (0x70 + dst).chr
    out << "\x24" if dst == ESP
    out << (256-12-pad).chr

    regs = [*(0..7)]
    while (regs.length > 0)
      reg = regs[ rand(regs.length) ]
      regs.delete(reg)
      next if reg == ESP
      next if badchars.index( (0x58 + reg).chr )
      mod_registers.push(reg)

      # Pop the value back out
      0.upto(pad / 4) { |c| out << (0x58 + reg).chr }

      # Fix the value to point to self
      gap = out.length - buf.length

      mod_registers.uniq!
      modified_registers.concat(mod_registers)
      return [out, REG_NAMES32[reg].upcase, gap]
    end
    mod_registers.pop(dst)
  end

  return nil
end
jmp(addr) click to toggle source

This method returns the opcodes that compose a jump instruction to the supplied relative offset.

# File lib/rex/arch/x86.rb, line 46
def self.jmp(addr)
  "\xe9" + pack_dword(rel_number(addr))
end
jmp_reg(str) click to toggle source

Jump tp a specific register

# File lib/rex/arch/x86.rb, line 30
def self.jmp_reg(str)
  reg = reg_number(str)
  _check_reg(reg)
  "\xFF" + [224 + reg].pack('C')
end
jmp_short(addr) click to toggle source

This method returns the opcodes that compose a short jump instruction to the supplied relative offset.

# File lib/rex/arch/x86.rb, line 99
def self.jmp_short(addr)
  "\xeb" + pack_lsb(rel_number(addr, -2))
end
loop(offset) click to toggle source

Generate a LOOP instruction (Decrement ECX and jump short if ECX == 0)

# File lib/rex/arch/x86.rb, line 39
def self.loop(offset)
  "\xE2" + pack_lsb(rel_number(offset, -2))
end
mov_byte(reg, val) click to toggle source

This method generates the opcodes that set the low byte of a given register to the supplied value.

# File lib/rex/arch/x86.rb, line 209
def self.mov_byte(reg, val)
  _check_reg(reg)
  # chr will raise RangeError if val not between 0 .. 255
  return (0xb0 | reg).chr + val.chr
end
mov_dword(reg, val) click to toggle source

This method generates the opcodes that set the a register to the supplied value.

# File lib/rex/arch/x86.rb, line 231
def self.mov_dword(reg, val)
  _check_reg(reg)
  return (0xb8 | reg).chr + pack_dword(val)
end
mov_word(reg, val) click to toggle source

This method generates the opcodes that set the low word of a given register to the supplied value.

# File lib/rex/arch/x86.rb, line 219
def self.mov_word(reg, val)
  _check_reg(reg)
  if val < 0 || val > 0xffff
    raise RangeError, "Can only take unsigned word values!", caller()
  end
  return "\x66" + (0xb8 | reg).chr + pack_word(val)
end
pack_dword(num) click to toggle source

This method wrappers packing an integer as a little-endian buffer.

# File lib/rex/arch/x86.rb, line 356
def self.pack_dword(num)
  [num].pack('V')
end
pack_lsb(num) click to toggle source

This method returns the least significant byte of a packed dword.

# File lib/rex/arch/x86.rb, line 363
def self.pack_lsb(num)
  pack_dword(num)[0,1]
end
pack_word(num) click to toggle source

This method wrappers packing a short integer as a little-endian buffer.

# File lib/rex/arch/x86.rb, line 349
def self.pack_word(num)
  [num].pack('v')
end
pop_dword(dst) click to toggle source

This method generates a pop dword instruction into a register.

# File lib/rex/arch/x86.rb, line 191
def self.pop_dword(dst)
  _check_reg(dst)
  return (0x58 | dst).chr
end
push_byte(byte) click to toggle source

This method generates a push byte instruction.

# File lib/rex/arch/x86.rb, line 166
def self.push_byte(byte)
  # push byte will sign extend...
  if byte < 128 && byte >= -128
    return "\x6a" + (byte & 0xff).chr
  end
  raise ::ArgumentError, "Can only take signed byte values!", caller()
end
push_dword(val) click to toggle source

This method generates a push dword instruction.

# File lib/rex/arch/x86.rb, line 184
def self.push_dword(val)
  return "\x68" + pack_dword(val)
end
push_word(val) click to toggle source

This method generates a push word instruction.

# File lib/rex/arch/x86.rb, line 177
def self.push_word(val)
  return "\x66\x68" + pack_word(val)
end
reg_name32(num) click to toggle source

This method returns the register named associated with a given register number.

# File lib/rex/arch/x86.rb, line 142
def self.reg_name32(num)
  _check_reg(num)
  return REG_NAMES32[num].dup
end
reg_number(str) click to toggle source

This method returns the number associated with a named register.

# File lib/rex/arch/x86.rb, line 134
def self.reg_number(str)
  return self.const_get(str.upcase)
end
register_names_to_ids(str) click to toggle source

Parse a list of registers as a space or command delimited string and return the internal register IDs as an array

# File lib/rex/arch/x86.rb, line 540
def self.register_names_to_ids(str)
  register_ids = []
  str.to_s.strip.split(/[,\s]/).
    map    {|reg| reg.to_s.strip.upcase }.
    select {|reg| reg.length > 0        }.
    uniq.each do |reg|
      next unless self.const_defined?(reg.intern)
      register_ids << self.const_get(reg.intern)
    end
  register_ids
end
rel_number(num, delta = 0) click to toggle source

This method returns a number offset to the supplied string.

# File lib/rex/arch/x86.rb, line 114
def self.rel_number(num, delta = 0)
  s = num.to_s

  case s[0, 2]
    when '$+'
      num = s[2 .. -1].to_i
    when '$-'
      num = -1 * s[2 .. -1].to_i
    when '0x'
      num = s.hex
    else
      delta = 0
  end

  return num + delta
end
searcher(tag) click to toggle source

This method returns the opcodes that compose a tag-based search routine

# File lib/rex/arch/x86.rb, line 60
def self.searcher(tag)
  "\xbe" + dword_adjust(tag,-1)+  # mov esi, Tag - 1
  "\x46" +                        # inc esi
  "\x47" +                        # inc edi (end_search:)
  "\x39\x37" +                    # cmp [edi],esi
  "\x75\xfb" +                    # jnz 0xa (end_search)
  "\x46" +                        # inc esi
  "\x4f" +                        # dec edi (start_search:)
  "\x39\x77\xfc" +                # cmp [edi-0x4],esi
  "\x75\xfa" +                    # jnz 0x10 (start_search)
  jmp_reg('edi')                  # jmp edi
end
set(dst, val, badchars = '') click to toggle source

This method is a general way of setting a register to a value. Depending on the value supplied, different sets of instructions may be used.

TODO: Make this moderatly intelligent so it chain instructions by itself

(ie. xor eax, eax + mov al, 4 + xchg ah, al)
# File lib/rex/arch/x86.rb, line 242
  def self.set(dst, val, badchars = '')
    _check_reg(dst)

    # If the value is 0 try xor/sub dst, dst (2 bytes)
    if val == 0
      opcodes = Rex::Text.remove_badchars("\x29\x2b\x31\x33", badchars)
      if !opcodes.empty?
        return opcodes[rand(opcodes.length)].chr + encode_modrm(dst, dst)
      end
# TODO: SHL/SHR
# TODO: AND
    end

    # try push BYTE val; pop dst (3 bytes)
    begin
      return _check_badchars(push_byte(val) + pop_dword(dst), badchars)
    rescue ::ArgumentError, ::RuntimeError, ::RangeError
    end

    # try clear dst, mov BYTE dst (4 bytes)
    begin
      unless val == 0 # clear tries to set(dst, 0, badchars), entering an infinite recursion
        return _check_badchars(clear(dst, badchars) + mov_byte(dst, val), badchars)
      end
    rescue ::ArgumentError, ::RuntimeError, ::RangeError
    end

    # try mov DWORD dst (5 bytes)
    begin
      return _check_badchars(mov_dword(dst, val), badchars)
    rescue ::ArgumentError, ::RuntimeError, ::RangeError
    end

    # try push DWORD, pop dst (6 bytes)
    begin
      return _check_badchars(push_dword(val) + pop_dword(dst), badchars)
    rescue ::ArgumentError, ::RuntimeError, ::RangeError
    end

    # try clear dst, mov WORD dst (6 bytes)
    begin
      unless val == 0 # clear tries to set(dst, 0, badchars), entering an infinite recursion
        return _check_badchars(clear(dst, badchars) + mov_word(dst, val), badchars)
      end
    rescue ::ArgumentError, ::RuntimeError, ::RangeError
    end

    raise RuntimeError, "No valid set instruction could be created!", caller()
  end
sub(val, reg, badchars = '', add = false, adjust = false, bits = 0) click to toggle source

Builds a subtraction instruction using the supplied operand and register.

# File lib/rex/arch/x86.rb, line 296
def self.sub(val, reg, badchars = '', add = false, adjust = false, bits = 0)
  opcodes = []
  shift   = (add == true) ? 0 : 5

  if (bits <= 8 and val >= -0x7f and val <= 0x7f)
    opcodes <<
      ((adjust) ? '' : clear(reg, badchars)) +
      "\x83" +
      [ encode_effective(shift, reg) ].pack('C') +
      [ val.to_i ].pack('C')
  end

  if (bits <= 16 and val >= -0xffff and val <= 0)
    opcodes <<
      ((adjust) ? '' : clear(reg, badchars)) +
      "\x66\x81" +
      [ encode_effective(shift, reg) ].pack('C') +
      [ val.to_i ].pack('v')
  end

  opcodes <<
    ((adjust) ? '' : clear(reg, badchars)) +
    "\x81" +
    [ encode_effective(shift, reg) ].pack('C') +
    [ val.to_i ].pack('V')

  # Search for a compatible opcode
  opcodes.each { |op|
    begin
      _check_badchars(op, badchars)
    rescue
      next
    end

    return op
  }

  if opcodes.empty?
    raise RuntimeError, "Could not find a usable opcode", caller()
  end
end