lib/pwnlib/shellcraft/generators/i386/common/mov.rb
# encoding: ASCII-8BIT
# frozen_string_literal: true
require 'pwnlib/shellcraft/generators/i386/common/common'
module Pwnlib
module Shellcraft
module Generators
module I386
module Common
# Move +src+ into +dst+ without newlines and null bytes.
#
# See {Amd64::Common#mov} for parameters' details.
def mov(dst, src, stack_allowed: true)
raise ArgumentError, "#{dst} is not a register" unless register?(dst)
dst = get_register(dst)
raise ArgumentError, "cannot use #{dst} on i386" if dst.size > 32 || dst.is64bit
if register?(src)
src = get_register(src)
raise ArgumentError, "cannot use #{src} on i386" if src.size > 32 || src.is64bit
if dst.size < src.size && !dst.bigger.include?(src.name)
raise ArgumentError, "cannot mov #{dst}, #{src}: dst is smaller than src"
end
else
context.local(arch: 'i386') { src = evaluate(src) }
raise ArgumentError, format('cannot mov %s, %d: dst is smaller than src', dst, src) unless dst.fits(src)
# 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)
srcs = unpack(srcp, bits: dst.size, signed: true)
srcp_neg = p32(-src)
srcp_not = p32(src ^ 0xffffffff)
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
cat "xor #{dst}, #{dst} /* #{src} */"
elsif stack_allowed && dst.size == 32 && src == 10
cat "push 9 /* mov #{dst}, '\\n' */"
cat "pop #{dst}"
cat "inc #{dst}"
elsif stack_allowed && dst.size == 32 && (-2**7 <= srcs && srcs < 2**7) && okay(srcp[0])
cat "push #{pretty(src)}"
cat "pop #{dst}"
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 reg.
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)}"
elsif okay(srcp_neg)
cat "mov #{dst}, -#{pretty(src)}"
cat "neg #{dst}"
elsif okay(srcp_not)
cat "mov #{dst}, (-1) ^ #{pretty(src)}"
cat "not #{dst}"
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))
cat "mov #{dst}, #{a}"
cat "xor #{dst}, #{b} /* #{hex(src)} == #{a} ^ #{b} */"
end
end
end
end
end
end
end
end