Module: OneGadget::ABI

Defined in:
lib/one_gadget/abi.rb

Overview

Defines the ABI of different architectures.

Constant Summary collapse

X86_32 =

Registers of i386.

%w[eax ebx ecx edx edi esi ebp esp] + 0.upto(7).map { |i| "xmm#{i}" }
X86_64 =

Registers of x86_64.

X86_32 +
%w[rax rbx rcx rdx rdi rsi rbp rsp] +
8.upto(15).map { |i| "r#{i}" } +
8.upto(15).map { |i| "xmm#{i}" }
AARCH64 =

Registers of AArch64.

%w[xzr wzr sp] + 0.upto(30).map { |i| ["x#{i}", "w#{i}"] }.flatten
ARM =

Registers of ARM (32-bit). objdump never prints the numbered name of a register that has a role name: +r10+-+r15+ always appear as +sl+/+fp+/+ip+/+sp+/+lr+/+pc+, so those are what this list holds.

%w[sl fp ip sp lr pc] + 0.upto(9).map { |i| "r#{i}" }
NARROW_VIEWS =

Names that address part of a wider register rather than storage of their own, mapped to the register they name part of. A write through either name is visible through the other, which is what Emulators::RegisterFile keeps them consistent about.

Examples:

writing edi is writing the low half of rdi

writing r8d is writing the low half of r8

{
  amd64: (%w[ax bx cx dx di si bp sp].map { |reg| ["e#{reg}", "r#{reg}"] } +
          8.upto(15).map { |i| ["r#{i}d", "r#{i}"] }).to_h,
  i386: {},
  aarch64: 0.upto(30).to_h { |i| ["w#{i}", "x#{i}"] }.merge('wzr' => 'xzr'),
  arm: {}
}.freeze
CALLER_SAVED =

Registers a call may destroy, per each ABI's calling convention: the return register, the argument registers and the scratch ones. A callee is free to leave anything here in an arbitrary state, so what it holds afterwards is not something the caller of a gadget can choose. Named by their full registers; the narrower views of each go with it (see NARROW_VIEWS).

{
  # SysV amd64: return rax, arguments rdi/rsi/rdx/rcx/r8/r9, scratch r10/r11.
  amd64: %w[rax rcx rdx rsi rdi r8 r9 r10 r11] + 0.upto(15).map { |i| "xmm#{i}" },
  # cdecl i386: return eax, scratch ecx/edx; arguments go on the stack.
  i386: %w[eax ecx edx] + 0.upto(7).map { |i| "xmm#{i}" },
  # AAPCS64: x0-x7 arguments and return, x9-x15 temporaries, x16/x17 the
  # intra-procedure-call scratch, x18 the platform register.
  aarch64: (0.upto(7).to_a + 9.upto(18).to_a).map { |i| "x#{i}" },
  # AAPCS: r0-r3 arguments and return, ip (r12) the intra-procedure scratch,
  # and lr, which the call itself overwrites with the return address.
  arm: %w[r0 r1 r2 r3 ip lr]
}.freeze
RETURN_REGISTER =

The register each ABI leaves an integer return value in.

{ amd64: 'rax', i386: 'eax', aarch64: 'x0', arm: 'r0' }.freeze

Class Method Summary collapse

Class Method Details

.aarch64Array<String>

Registers' name of aarch64.

Returns:

  • (Array<String>)

    List of registers.



74
75
76
# File 'lib/one_gadget/abi.rb', line 74

def aarch64
  AARCH64
end

.allArray<String>

Returns all names of registers.

Returns:

  • (Array<String>)

    List of registers.



86
87
88
# File 'lib/one_gadget/abi.rb', line 86

def all
  amd64 + aarch64 + arm
end

.amd64Array<String>

Registers' name of amd64.

Returns:

  • (Array<String>)

    List of registers.



62
63
64
# File 'lib/one_gadget/abi.rb', line 62

def amd64
  X86_64
end

.armArray<String>

Registers' name of arm (32-bit).

Returns:

  • (Array<String>)

    List of registers.



80
81
82
# File 'lib/one_gadget/abi.rb', line 80

def arm
  ARM
end

.i386Array<String>

Registers' name of i386.

Returns:

  • (Array<String>)

    List of registers.



68
69
70
# File 'lib/one_gadget/abi.rb', line 68

def i386
  X86_32
end

.stack_register?(reg) ⇒ Boolean

Checks if the register is a stack-related pointer.

Parameters:

  • reg (String)

    Register's name.

Returns:

  • (Boolean)

    true if reg is a stack or frame pointer (e.g. rsp, rbp, sp).



94
95
96
# File 'lib/one_gadget/abi.rb', line 94

def stack_register?(reg)
  %w[esp ebp rsp rbp sp x29].include?(reg)
end