class OneGadget::Emulators::AArch64

Emulator of aarch64.

Public Class Methods

new() click to toggle source

Instantiate a {AArch64} object.

Calls superclass method
# File lib/one_gadget/emulators/aarch64.rb, line 12
def initialize
  super(OneGadget::ABI.aarch64, 'sp')
  # Constant registers
  %w[xzr wzr].each { |r| @registers[r] = 0 }
  @pc = 'pc'
end

Private Class Methods

bits() click to toggle source

AArch64 is 64-bit.

# File lib/one_gadget/emulators/aarch64.rb, line 164
def bits
  64
end

Public Instance Methods

argument(idx) click to toggle source

Return the argument value of calling a function. @param [Integer] idx @return [Lambda, Integer]

# File lib/one_gadget/emulators/aarch64.rb, line 43
def argument(idx)
  registers["x#{idx}"]
end
instructions() click to toggle source

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

# File lib/one_gadget/emulators/aarch64.rb, line 28
def instructions
  [
    Instruction.new('add', 3..4),
    Instruction.new('adrp', 2),
    Instruction.new('bl', 1),
    Instruction.new('ldr', 2..3),
    Instruction.new('mov', 2),
    Instruction.new('stp', 3),
    Instruction.new('str', 2..3)
  ]
end
process!(cmd) click to toggle source

@see OneGadget::Emulators::X86#process!

# File lib/one_gadget/emulators/aarch64.rb, line 20
def process!(cmd)
  inst, args = parse(cmd.gsub(/#-?(0x)?[0-9a-f]+/) { |v| v[1..-1] })
  sym = "inst_#{inst.inst}".to_sym
  __send__(sym, *args) != :fail
end

Private Instance Methods

add_writable(lmda) click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 154
def add_writable(lmda)
  # XXX: Better way is check LOAD segment of the libc ELF.
  # XXX: Should also checks deref_count, but sometimes [[$base+xx]] is also writable..
  return if lmda.obj == libc_base.obj

  @constraints << [:writable, lmda]
end
inst_add(dst, src, op2, mode = 'sxtw') click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 49
def inst_add(dst, src, op2, mode = 'sxtw')
  check_register!(dst)

  src = arg_to_lambda(src)
  op2 = arg_to_lambda(op2)
  raise_unsupported('add', dst, src, op2) unless op2.is_a?(Integer) && mode == 'sxtw'

  registers[dst] = src + op2
end
inst_adrp(dst, imm) click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 59
def inst_adrp(dst, imm)
  check_register!(dst)

  registers[dst] = libc_base + imm.to_i(16)
end
inst_bl(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/aarch64.rb, line 68
def inst_bl(addr)
  # This is the last call
  return registers[pc] = addr if %w[execve execl].any? { |n| addr.include?(n) }

  # TODO: handle some registers would be fucked after call
  checker = {
    'sigprocmask' => {},
    '__sigaction' => { 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_ldr(dst, src, index = 0) click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 84
def inst_ldr(dst, src, index = 0)
  check_register!(dst)

  src_l = arg_to_lambda(src)
  registers[dst] = src_l
  raise_unsupported('ldr', dst, src, index) unless OneGadget::Helper.integer?(index)

  index = Integer(index)
  return unless src.end_with?('!') || index.nonzero?

  # Two cases:
  # 1. pre-index mode, +src+ is [reg, imm]!
  # 2. post-index mode, +src+ is [reg]
  lmda = OneGadget::Emulators::Lambda.parse(src)
  registers[lmda.obj] += lmda.immi + index
end
inst_mov(dst, src) click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 101
def inst_mov(dst, src)
  check_register!(dst)

  registers[dst] = arg_to_lambda(src)
end
inst_stp(reg1, reg2, dst) click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 107
def inst_stp(reg1, reg2, dst)
  raise_unsupported('stp', reg1, reg2, dst) unless reg64?(reg1) && reg64?(reg2)

  dst_l = arg_to_lambda(dst).ref!
  raise_unsupported('stp', reg1, reg2, dst) unless dst_l.obj == sp && dst_l.deref_count.zero?

  cur_top = dst_l.evaluate(eval_dict)
  stack[cur_top] = registers[reg1]
  stack[cur_top + size_t] = registers[reg2]

  registers[sp] += arg_to_lambda(dst).immi if dst.end_with?('!')
end
inst_str(src, dst, index = 0) click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 120
def inst_str(src, dst, index = 0)
  check_register!(src)
  raise_unsupported('str', src, dst, index) unless OneGadget::Helper.integer?(index)

  dst_l = arg_to_lambda(dst).ref!
  # Only stores on stack.
  if dst_l.obj == sp && dst_l.deref_count.zero?
    cur_top = dst_l.evaluate(eval_dict)
    stack[cur_top] = registers[src]
  else
    # Unlike the stack case, don't know where to save the value.
    # Simply add a constraint.
    add_writable(dst_l)
  end

  index = Integer(index)
  return unless dst.end_with?('!') || index.nonzero?

  # Two cases:
  # 1. pre-index mode, +dst+ is [reg, imm]!
  # 2. post-index mode, +dst+ is [reg]
  lmda = OneGadget::Emulators::Lambda.parse(dst)
  registers[lmda.obj] += lmda.immi + index
end
libc_base() click to toggle source
# File lib/one_gadget/emulators/aarch64.rb, line 145
def libc_base
  @libc_base ||= OneGadget::Emulators::Lambda.new('$base')
end
reg64?(reg) click to toggle source

Checks if reg is a 64-bit register.

# File lib/one_gadget/emulators/aarch64.rb, line 150
def reg64?(reg)
  register?(reg) && reg.start_with?('x')
end