Module: Pwnlib::Shellcraft::Generators::Amd64::Common
- Extended by:
- Helper
- Defined in:
- lib/pwnlib/shellcraft/generators/amd64/common/common.rb,
lib/pwnlib/shellcraft/generators/amd64/common/mov.rb,
lib/pwnlib/shellcraft/generators/amd64/common/nop.rb,
lib/pwnlib/shellcraft/generators/amd64/common/ret.rb,
lib/pwnlib/shellcraft/generators/amd64/common/popad.rb,
lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb,
lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb,
lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb,
lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb,
lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb
Overview
For non os-related methods.
Instance Method Summary collapse
- #infloop ⇒ Object
-
#memcpy(dst, src, n) ⇒ Object
Like
memcpy
in glibc. -
#mov(dst, src, stack_allowed: true) ⇒ Object
Move
src
intodst
without newlines and null bytes. -
#nop ⇒ Object
A no-op instruction.
-
#popad ⇒ Object
Pop all of the registers onto the stack which i386
popad
does. -
#pushstr(str, append_null: true) ⇒ Object
Push a string to stack.
- #pushstr_array(reg, array) ⇒ Object
-
#ret(return_value = nil) ⇒ Object
Instruction return.
- #setregs(reg_context, stack_allowed: true) ⇒ Object
Methods included from Helper
Instance Method Details
#infloop ⇒ Object
15 16 17 18 19 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb', line 15 def infloop(*args) context.local(arch: :amd64) do cat X86::Common.infloop(*args) end end |
#memcpy(dst, src, n) ⇒ Object
Like memcpy
in glibc.
Copy n
bytes from src
to dst
.
25 26 27 28 29 30 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb', line 25 def memcpy(dst, src, n) cat "/* memcpy(#{pretty(dst)}, #{pretty(src)}, #{pretty(n)}) */" cat 'cld' cat Common.setregs({ rdi: dst, rsi: src, rcx: n }) cat 'rep movsb' end |
#mov(dst, src, stack_allowed: true) ⇒ Object
Move src
into dst
without newlines and null bytes.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/mov.rb', line 38 def mov(dst, src, stack_allowed: true) raise ArgumentError, "#{dst} is not a register" unless register?(dst) dst = get_register(dst) if register?(src) src = get_register(src) if dst.size < src.size && !dst.bigger.include?(src.name) raise ArgumentError, "cannot mov #{dst}, #{src}: dst is smaller than src" end # Downgrade our register choice if possible. # Opcodes for operating on 32-bit registers are always (?) shorter. dst = get_register(dst.native32) if dst.size == 64 && src.size <= 32 else context.local(arch: 'amd64') { src = evaluate(src) } raise ArgumentError, format('cannot mov %s, %d: dst is smaller than src', dst, src) unless dst.fits(src) orig_dst = dst dst = get_register(dst.native32) if dst.size == 64 && bits_required(src) <= 32 # Calculate the packed version. srcp = pack(src & ((1 << dst.size) - 1), bits: dst.size) # Calculate the unsigned and signed versions. srcu = unpack(srcp, bits: dst.size, signed: false) # N.B.: We may have downsized the register for e.g. mov('rax', 0xffffffff) # In this case, srcp is now a 4-byte packed value, which will expand to "-1", which isn't correct. srcs = orig_dst.size == dst.size ? unpack(srcp, bits: dst.size, signed: true) : src end if register?(src) if src == dst || dst.bigger.include?(src.name) cat "/* moving #{src} into #{dst}, but this is a no-op */" elsif dst.size > src.size cat "movzx #{dst}, #{src}" else cat "mov #{dst}, #{src}" end elsif src.is_a?(Numeric) # Constant or immi xor = ->(reg) { "xor #{reg.xor}, #{reg.xor}" } if src.zero? # Special case for zeroes. # XORing the 32-bit register clears the high 32 bits as well. cat "xor #{dst}, #{dst} /* #{src} */" elsif stack_allowed && [32, 64].include?(dst.size) && src == 10 cat "push 9 /* mov #{dst}, '\\n' */" cat "pop #{dst.native64}" cat "inc #{dst}" elsif stack_allowed && [32, 64].include?(dst.size) && (-2**7 <= srcs && srcs < 2**7) && okay(srcp[0]) # It's smaller to PUSH and POP small sign-extended values than to directly move them into various # registers. # # 6aff58 push -1; pop rax # 48c7c0ffffffff mov rax, -1 cat "push #{pretty(src)}" cat "pop #{dst.native64}" elsif okay(srcp) # Easy case. This implies that the register size and value are the same. cat "mov #{dst}, #{pretty(src)}" elsif srcu < 2**8 && okay(srcp[0]) && dst.sizes.include?(8) # Move 8-bit value into register. cat xor[dst] cat "mov #{dst.sizes[8]}, #{pretty(src)}" elsif srcu == srcu & 0xff00 && okay(srcp[1]) && dst.ff00 # Target value is a 16-bit value with no data in the low 8 bits, we can use the 'AH' style register. cat xor[dst] cat "mov #{dst.ff00}, #{pretty(src)} >> 8" elsif srcu < 2**16 && okay(srcp[0, 2]) # Target value is a 16-bit value, use a 16-bit mov. cat xor[dst] cat "mov #{dst.sizes[16]}, #{pretty(src)}" else # All else has failed. Use some XOR magic to move things around. a, b = xor_pair(srcp, avoid: "\x00\n") a = hex(unpack(a, bits: dst.size)) b = hex(unpack(b, bits: dst.size)) if dst.size != 64 # There's no XOR REG, IMM64 but we can take the easy route for smaller registers. cat "mov #{dst}, #{a}" cat "xor #{dst}, #{b} /* #{hex(src)} == #{a} ^ #{b} */" elsif stack_allowed # However, we can PUSH IMM64 and then perform the XOR that way at the top of the stack. cat "mov #{dst}, #{a}" cat "push #{dst}" cat "mov #{dst}, #{b}" cat "xor [rsp], #{dst} /* #{hex(src)} == #{a} ^ #{b} */" cat "pop #{dst}" else raise ArgumentError, "Cannot put #{pretty(src)} into '#{dst}' without using stack." end end end end |
#nop ⇒ Object
A no-op instruction.
11 12 13 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/nop.rb', line 11 def nop cat 'nop' end |
#popad ⇒ Object
Pop all of the registers onto the stack which i386 popad
does.
12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/popad.rb', line 12 def popad cat <<-EOS pop rdi pop rsi pop rbp pop rbx /* add rsp, 8 */ pop rbx pop rdx pop rcx pop rax EOS end |
#pushstr(str, append_null: true) ⇒ Object
Push a string to stack.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb', line 27 def pushstr(str, append_null: true) # This will not affect callee's +str+. str += "\x00" if append_null && !str.end_with?("\x00") return if str.empty? padding = str[-1].ord >= 128 ? "\xff" : "\x00" cat "/* push #{str.inspect} */" group(8, str, underfull_action: :fill, fill_value: padding).reverse_each do |word| sign = u64(word, endian: 'little', signed: true) sign32 = u32(word[0, 4], bits: 32, endian: 'little', signed: true) if [0, 0xa].include?(sign) # simple forbidden byte case cat "push #{pretty(sign + 1)}" cat 'dec byte ptr [rsp]' elsif sign >= -0x80 && sign <= 0x7f && okay(word[0]) # simple byte case cat "push #{pretty(sign)}" elsif sign >= -0x80000000 && sign <= 0x7fffffff && okay(word[0, 4]) # simple 32bit without forbidden byte cat "push #{pretty(sign)}" elsif okay(word) cat "mov rax, #{pretty(sign)}" cat 'push rax' elsif sign32.positive? && word[4, 4] == "\x00" * 4 # The high 4 byte of word are all zeros, so we can use +xor dword ptr [rsp]+. a = u32(xor_pair(word[0, 4]).first, endian: 'little', signed: true) cat "push #{pretty(a)} ^ #{pretty(sign)}" cat "xor dword ptr [rsp], #{pretty(a)}" else a = u64(xor_pair(word).first, endian: 'little', signed: false) cat "mov rax, #{pretty(a)}" cat 'push rax' cat "mov rax, #{pretty(a ^ sign)} /* #{pretty(a)} ^ #{pretty(sign)} */" cat 'xor [rsp], rax' end end end |
#pushstr_array(reg, array) ⇒ Object
15 16 17 18 19 |
# File 'lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb', line 15 def pushstr_array(*args) context.local(arch: :amd64) do cat X86::Common.pushstr_array(*args) end end |