class OneGadget::Emulators::X86

Super class for amd64 and i386 processor.

Public Class Methods

new(registers, sp, pc) click to toggle source

Constructor for a x86 processor.

Calls superclass method OneGadget::Emulators::Processor::new
# File lib/one_gadget/emulators/x86.rb, line 13
def initialize(registers, sp, pc)
  super(registers, sp)
  @pc = pc
end

Public Instance Methods

instructions() click to toggle source

Supported instruction set. @return [Array<Instruction>] The supported instructions.

# File lib/one_gadget/emulators/x86.rb, line 35
def instructions
  [
    Instruction.new('add', 2),
    Instruction.new('call', 1),
    Instruction.new('jmp', 1),
    Instruction.new('lea', 2),
    Instruction.new('mov', 2),
    Instruction.new('nop', -1),
    Instruction.new('push', 1),
    Instruction.new('sub', 2),
    Instruction.new('xor', 2),
    Instruction.new('movq', 2),
    Instruction.new('movaps', 2),
    Instruction.new('movhps', 2),
    Instruction.new('punpcklqdq', 2)
  ]
end
process!(cmd) click to toggle source

Process one command. Will raise exceptions when encounter unhandled instruction. @param [String] cmd

One line from result of objdump.

@return [Boolean]

If successfully processed.
# File lib/one_gadget/emulators/x86.rb, line 24
def process!(cmd)
  inst, args = parse(cmd)
  # return registers[pc] = args[0] if inst.inst == 'call'
  return true if inst.inst == 'jmp' # believe the fetcher has handled jmp.

  sym = "inst_#{inst.inst}".to_sym
  __send__(sym, *args) != :fail
end

Private Instance Methods

add_writable(dst) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 201
def add_writable(dst)
  lmda = arg_to_lambda(dst).ref!
  # pc-relative addresses should be writable
  return if lmda.obj == pc

  @constraints << [:writable, lmda]
end
check_xmm_sp(dst, src) { || ... } click to toggle source

check whether (dst, src) is in form (xmm*, [sp+*])

# File lib/one_gadget/emulators/x86.rb, line 112
def check_xmm_sp(dst, src)
  return yield unless xmm_reg?(dst) && src.include?(sp)

  dst_lm = arg_to_lambda(dst)
  src_lm = arg_to_lambda(src)
  return yield if src_lm.deref_count != 1

  src_lm.ref!
  [dst_lm, src_lm]
end
inst_add(dst, src) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 163
def inst_add(dst, src)
  check_register!(dst)

  src = arg_to_lambda(src)
  registers[dst] += src
end
inst_call(addr) click to toggle source

Handle some valid calls. For example, sigprocmask will always be a valid call because it just invokes syscall.

# File lib/one_gadget/emulators/x86.rb, line 183
def inst_call(addr)
  # This is the last call
  return registers[pc] = addr if %w[execve execl posix_spawn].any? { |n| addr.include?(n) }

  # TODO: handle some registers would be fucked after call
  checker = {
    'sigprocmask' => {},
    '__close' => {},
    'unsetenv' => { 0 => :global_var? },
    '__sigaction' => { 1 => :global_var?, 2 => :zero? }
  }
  func = checker.keys.find { |n| addr.include?(n) }
  return if func && checker[func].all? { |idx, sym| check_argument(idx, sym) }

  # unhandled case or checker's condition fails
  :fail
end
inst_lea(dst, src) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 138
def inst_lea(dst, src)
  check_register!(dst)

  registers[dst] = arg_to_lambda(src).ref!
end
inst_mov(dst, src) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 55
def inst_mov(dst, src)
  src = arg_to_lambda(src)
  if register?(dst)
    registers[dst] = src
  else
    # Just ignore strange case...
    # TODO(david942j): #120
    return add_writable(dst) unless dst.include?(sp)

    dst = arg_to_lambda(dst)
    return if dst.deref_count != 1 # should not happen

    dst.ref!
    stack[dst.evaluate(eval_dict)] = src
  end
end
inst_movaps(dst, src) click to toggle source

This instruction moves 128bits.

# File lib/one_gadget/emulators/x86.rb, line 73
def inst_movaps(dst, src)
  # XXX: here we only support `movaps [sp+*], xmm*`
  src, dst = check_xmm_sp(src, dst) { raise_unsupported('movaps', dst, src) }
  off = dst.evaluate(eval_dict)
  @constraints << [:raw, "#{sp} & 0xf == #{0x10 - off & 0xf}"]
  (128 / self.class.bits).times do |i|
    stack[off + i * size_t] = src[i]
  end
end
inst_movhps(dst, src) click to toggle source

Move src to dst

# File lib/one_gadget/emulators/x86.rb, line 102
def inst_movhps(dst, src)
  # XXX: here we only support `movhps xmm*, [sp+*]`
  dst, src = check_xmm_sp(dst, src) { raise_unsupported('movhps', dst, src) }
  off = src.evaluate(eval_dict)
  (64 / self.class.bits).times do |i|
    dst[i + 64 / self.class.bits] = stack[off + i * size_t]
  end
end
inst_movq(dst, src) click to toggle source

Move src to dst Supported forms:

movq xmm*, [sp+*]
movq xmm*, reg64
# File lib/one_gadget/emulators/x86.rb, line 87
def inst_movq(dst, src)
  if self.class.bits == 64 && xmm_reg?(dst) && src.start_with?('r') && register?(src)
    dst = arg_to_lambda(dst)
    src = arg_to_lambda(src)
    dst[0] = src
    return
  end
  dst, src = check_xmm_sp(dst, src) { raise_unsupported('movq', dst, src) }
  off = src.evaluate(eval_dict)
  (64 / self.class.bits).times do |i|
    dst[i] = stack[off + i * size_t]
  end
end
inst_nop(*) click to toggle source

yap, nop

# File lib/one_gadget/emulators/x86.rb, line 178
def inst_nop(*); end
inst_punpcklqdq(dst, src) click to toggle source

dst = src

# File lib/one_gadget/emulators/x86.rb, line 128
def inst_punpcklqdq(dst, src)
  raise_unsupported('punpcklqdq', dst, src) unless xmm_reg?(dst) && xmm_reg?(src)

  dst = arg_to_lambda(dst)
  src = arg_to_lambda(src)
  (64 / self.class.bits).times do |i|
    dst[i + 64 / self.class.bits] = src[i]
  end
end
inst_push(val) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 144
def inst_push(val)
  val = arg_to_lambda(val)
  registers[sp] -= size_t
  cur_top = registers[sp].evaluate(eval_dict)
  raise Error::InstructionArgumentError, "Corrupted stack pointer: #{cur_top}" unless cur_top.is_a?(Integer)

  stack[cur_top] = val
end
inst_sub(dst, src) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 170
def inst_sub(dst, src)
  src = arg_to_lambda(src)
  raise Error::UnsupportedInstructionArgumentError, "Unhandled -= of type #{src.class}" unless src.is_a?(Integer)

  registers[dst] -= src
end
inst_xor(dst, src) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 153
def inst_xor(dst, src)
  check_register!(dst)

  # only supports dst == src
  raise Error::UnsupportedInstructionArgumentError, 'xor operator only supports dst = src' unless dst == src

  dst[0] = 'r' if self.class.bits == 64 && dst.start_with?('e')
  registers[dst] = 0
end
to_lambda(reg) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 209
def to_lambda(reg)
  return super unless reg =~ /^xmm\d+$/

  Array.new(128 / self.class.bits) do |i|
    cast = "(u#{self.class.bits})"
    OneGadget::Emulators::Lambda.new(i.zero? ? "#{cast}#{reg}" : "#{cast}(#{reg} >> #{self.class.bits * i})")
  end
end
xmm_reg?(reg) click to toggle source
# File lib/one_gadget/emulators/x86.rb, line 123
def xmm_reg?(reg)
  reg.start_with?('xmm') && register?(reg)
end