2005-12-17 06:46:23 +00:00
|
|
|
#!/usr/bin/env ruby
|
2005-06-09 04:24:45 +00:00
|
|
|
|
|
|
|
module Rex
|
|
|
|
module Arch
|
|
|
|
|
2005-06-09 17:35:39 +00:00
|
|
|
#
|
|
|
|
# everything here is mostly stole from vlad's perl x86 stuff
|
|
|
|
#
|
|
|
|
|
2005-06-09 04:24:45 +00:00
|
|
|
module X86
|
|
|
|
|
|
|
|
#
|
|
|
|
# Register number constants
|
|
|
|
#
|
|
|
|
EAX = AL = AX = ES = 0
|
|
|
|
ECX = CL = CX = CS = 1
|
|
|
|
EDX = DL = DX = SS = 2
|
|
|
|
EBX = BL = BX = DS = 3
|
|
|
|
ESP = AH = SP = FS = 4
|
|
|
|
EBP = CH = BP = GS = 5
|
|
|
|
ESI = DH = SI = 6
|
|
|
|
EDI = BH = DI = 7
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
REG_NAMES32 = [ 'eax', 'ecx', 'edx', 'ebx',
|
|
|
|
'esp', 'ebp', 'esi', 'edi' ] # :nodoc:
|
2005-09-07 17:23:30 +00:00
|
|
|
|
2005-11-25 20:02:21 +00:00
|
|
|
#
|
|
|
|
# This method adds/subs a packed long integer
|
|
|
|
#
|
|
|
|
def self.dword_adjust(dword, amount=0)
|
|
|
|
[dword.unpack('V')[0] + amount].pack('V')
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# This method returns the opcodes that compose a tag-based search routine
|
|
|
|
#
|
|
|
|
def self.searcher(tag)
|
|
|
|
"\xbe" + dword_adjust(tag,-1)+ # mov esi, Tag - 1
|
|
|
|
"\x46" + # inc esi
|
|
|
|
"\x47" + # inc edi (end_search:)
|
|
|
|
"\x39\x37" + # cmp [edi],esi
|
|
|
|
"\x75\xfb" + # jnz 0xa (end_search)
|
|
|
|
"\x46" + # inc esi
|
|
|
|
"\x4f" + # dec edi (start_search:)
|
|
|
|
"\x39\x77\xfc" + # cmp [edi-0x4],esi
|
|
|
|
"\x75\xfa" + # jnz 0x10 (start_search)
|
|
|
|
"\xff\xe7" # jmp edi
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method returns the opcodes that compose a short jump instruction to
|
|
|
|
# the supplied relative offset.
|
|
|
|
#
|
2005-07-17 06:01:11 +00:00
|
|
|
def self.jmp_short(addr)
|
2005-07-18 03:58:29 +00:00
|
|
|
"\xeb" + pack_lsb(rel_number(addr, -2))
|
2005-07-17 06:01:11 +00:00
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method returns the opcodes that compose a relative call instruction
|
|
|
|
# to the address specified.
|
|
|
|
#
|
2005-07-17 06:01:11 +00:00
|
|
|
def self.call(addr)
|
|
|
|
"\xe8" + pack_dword(rel_number(addr, -5))
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method returns a number offset to the supplied string.
|
|
|
|
#
|
2005-07-17 06:01:11 +00:00
|
|
|
def self.rel_number(num, delta = 0)
|
|
|
|
s = num.to_s
|
|
|
|
|
|
|
|
case s[0, 2]
|
|
|
|
when '$+'
|
|
|
|
num = s[2 .. -1].to_i
|
|
|
|
when '$-'
|
|
|
|
num = -1 * s[2 .. -1].to_i
|
|
|
|
when '0x'
|
|
|
|
num = s.hex
|
|
|
|
else
|
|
|
|
delta = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
return num + delta
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method returns the number associated with a named register.
|
|
|
|
#
|
2005-06-09 04:24:45 +00:00
|
|
|
def self.reg_number(str)
|
|
|
|
return self.const_get(str.upcase)
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method returns the register named associated with a given register
|
|
|
|
# number.
|
|
|
|
#
|
2005-09-07 17:23:30 +00:00
|
|
|
def self.reg_name32(num)
|
|
|
|
_check_reg(num)
|
|
|
|
return REG_NAMES32[num].dup
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates the encoded effective value for a register.
|
|
|
|
#
|
2005-07-10 20:49:13 +00:00
|
|
|
def self.encode_effective(shift, dst)
|
|
|
|
return (0xc0 | (shift << 3) | dst)
|
|
|
|
end
|
2005-06-09 17:35:39 +00:00
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
2005-12-30 20:26:41 +00:00
|
|
|
# This method generates the mod r/m character for a source and destination
|
2005-11-02 23:03:02 +00:00
|
|
|
# register.
|
|
|
|
#
|
2005-06-09 17:35:39 +00:00
|
|
|
def self.encode_modrm(dst, src)
|
|
|
|
_check_reg(dst, src)
|
|
|
|
return (0xc0 | src | dst << 3).chr
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates a push byte instruction.
|
|
|
|
#
|
2005-06-09 04:24:45 +00:00
|
|
|
def self.push_byte(byte)
|
|
|
|
# push byte will sign extend...
|
|
|
|
if byte < 128 && byte >= -128
|
|
|
|
return "\x6a" + (byte & 0xff).chr
|
|
|
|
end
|
2005-10-01 05:55:15 +00:00
|
|
|
raise ::ArgumentError, "Can only take signed byte values!", caller()
|
2005-06-09 17:35:39 +00:00
|
|
|
end
|
2005-11-02 23:03:02 +00:00
|
|
|
|
2006-01-14 17:45:42 +00:00
|
|
|
#
|
|
|
|
# This method generates a push dword instruction.
|
|
|
|
#
|
|
|
|
def self.push_dword(val)
|
|
|
|
return "\x68" + [ val ].pack('V')
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates a pop dword instruction into a register.
|
|
|
|
#
|
2005-06-09 17:35:39 +00:00
|
|
|
def self.pop_dword(dst)
|
|
|
|
_check_reg(dst)
|
|
|
|
return (0x58 | dst).chr
|
2005-06-09 04:24:45 +00:00
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates an instruction that clears the supplied register in
|
|
|
|
# a manner that attempts to avoid bad characters, if supplied.
|
|
|
|
#
|
2005-06-09 17:35:39 +00:00
|
|
|
def self.clear(reg, badchars = '')
|
|
|
|
_check_reg(reg)
|
2006-01-09 02:07:56 +00:00
|
|
|
return set(reg, 0, badchars)
|
2005-06-09 04:24:45 +00:00
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates the opcodes that set the low byte of a given
|
|
|
|
# register to the supplied value.
|
|
|
|
#
|
2005-06-09 17:35:39 +00:00
|
|
|
def self.mov_byte(reg, val)
|
|
|
|
_check_reg(reg)
|
|
|
|
# chr will raise RangeError if val not between 0 .. 255
|
|
|
|
return (0xb0 | reg).chr + val.chr
|
2005-06-09 04:24:45 +00:00
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates the opcodes that set the low word of a given
|
|
|
|
# register to the supplied value.
|
|
|
|
#
|
2005-06-09 17:44:43 +00:00
|
|
|
def self.mov_word(reg, val)
|
|
|
|
_check_reg(reg)
|
|
|
|
if val < 0 || val > 0xffff
|
|
|
|
raise RangeError, "Can only take unsigned word values!", caller()
|
|
|
|
end
|
|
|
|
return "\x66" + (0xb8 | reg).chr + [ val ].pack('v')
|
|
|
|
end
|
|
|
|
|
2005-12-30 20:26:41 +00:00
|
|
|
#
|
|
|
|
# This method generates the opcodes that set the a register to the
|
|
|
|
# supplied value.
|
|
|
|
#
|
|
|
|
def self.mov_dword(reg, val)
|
|
|
|
_check_reg(reg)
|
|
|
|
return (0xb8 | reg).chr + [ val ].pack('V')
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method is a general way of setting a register to a value. Depending
|
|
|
|
# on the value supplied, different sets of instructions may be used.
|
|
|
|
#
|
2006-01-14 17:45:42 +00:00
|
|
|
# TODO: Make this moderatly intelligent so it chain instructions by itself
|
2006-01-27 05:33:08 +00:00
|
|
|
# (ie. xor eax, eax + mov al, 4 + xchg ah, al)
|
2005-06-09 17:35:39 +00:00
|
|
|
def self.set(dst, val, badchars = '')
|
|
|
|
_check_reg(dst)
|
|
|
|
|
2006-01-09 02:07:56 +00:00
|
|
|
# If the value is 0 try xor/sub dst, dst (2 bytes)
|
|
|
|
if(val == 0)
|
|
|
|
opcodes = Rex::Text.remove_badchars("\x29\x2b\x31\x33", badchars)
|
|
|
|
if !opcodes.empty?
|
|
|
|
return opcodes[rand(opcodes.length)].chr + encode_modrm(dst, dst)
|
|
|
|
end
|
2006-01-14 17:45:42 +00:00
|
|
|
# TODO: SHL/SHR
|
|
|
|
# TODO: AND
|
2006-01-09 02:07:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# try push BYTE val; pop dst (3 bytes)
|
2005-06-09 17:35:39 +00:00
|
|
|
begin
|
|
|
|
return _check_badchars(push_byte(val) + pop_dword(dst), badchars)
|
2005-11-01 00:48:40 +00:00
|
|
|
rescue ::ArgumentError, RuntimeError, RangeError
|
2005-06-09 17:35:39 +00:00
|
|
|
end
|
|
|
|
|
2006-01-09 02:07:56 +00:00
|
|
|
# try clear dst, mov BYTE dst (4 bytes)
|
2005-06-09 17:35:39 +00:00
|
|
|
begin
|
2006-01-14 17:45:42 +00:00
|
|
|
break if val == 0
|
2005-06-09 17:35:39 +00:00
|
|
|
return _check_badchars(clear(dst, badchars) + mov_byte(dst, val), badchars)
|
2005-11-01 00:48:40 +00:00
|
|
|
rescue ::ArgumentError, RuntimeError, RangeError
|
2005-06-09 17:35:39 +00:00
|
|
|
end
|
|
|
|
|
2006-01-14 17:45:42 +00:00
|
|
|
# try mov DWORD dst (5 bytes)
|
2005-06-09 17:44:43 +00:00
|
|
|
begin
|
2006-01-14 17:45:42 +00:00
|
|
|
return _check_badchars(mov_dword(dst, val), badchars)
|
2005-11-01 00:48:40 +00:00
|
|
|
rescue ::ArgumentError, RuntimeError, RangeError
|
2005-06-09 17:44:43 +00:00
|
|
|
end
|
|
|
|
|
2006-01-14 17:45:42 +00:00
|
|
|
# try push DWORD, pop dst (6 bytes)
|
2005-12-30 20:26:41 +00:00
|
|
|
begin
|
2006-01-14 17:45:42 +00:00
|
|
|
return _check_badchars(push_dword(val) + pop_dword(dst), badchars)
|
|
|
|
rescue ::ArgumentError, RuntimeError, RangeError
|
|
|
|
end
|
|
|
|
|
|
|
|
# try clear dst, mov WORD dst (6 bytes)
|
|
|
|
begin
|
|
|
|
break if val == 0
|
|
|
|
return _check_badchars(clear(dst, badchars) + mov_word(dst, val), badchars)
|
2005-12-30 20:26:41 +00:00
|
|
|
rescue ::ArgumentError, RuntimeError, RangeError
|
|
|
|
end
|
|
|
|
|
2005-06-09 17:35:39 +00:00
|
|
|
raise RuntimeError, "No valid set instruction could be created!", caller()
|
|
|
|
end
|
|
|
|
|
2005-07-10 20:49:13 +00:00
|
|
|
#
|
|
|
|
# Builds a subtraction instruction using the supplied operand
|
|
|
|
# and register.
|
|
|
|
#
|
2005-11-11 01:22:03 +00:00
|
|
|
def self.sub(val, reg, badchars = '', add = false, adjust = false, bits = 0)
|
2005-07-10 20:49:13 +00:00
|
|
|
opcodes = []
|
2005-10-01 05:55:15 +00:00
|
|
|
shift = (add == true) ? 0 : 5
|
2005-07-10 20:49:13 +00:00
|
|
|
|
2005-11-11 01:22:03 +00:00
|
|
|
if (bits <= 8 and val >= -0x7f and val <= 0x7f)
|
2005-07-10 20:49:13 +00:00
|
|
|
opcodes <<
|
2005-10-01 05:55:15 +00:00
|
|
|
((adjust) ? '' : clear(reg, badchars)) +
|
2005-07-10 20:49:13 +00:00
|
|
|
"\x83" +
|
2005-10-01 05:55:15 +00:00
|
|
|
[ encode_effective(shift, reg) ].pack('C') +
|
2005-07-10 20:49:13 +00:00
|
|
|
[ val.to_i ].pack('C')
|
|
|
|
end
|
|
|
|
|
2005-11-11 01:22:03 +00:00
|
|
|
if (bits <= 16 and val >= -0xffff and val <= 0)
|
2005-07-10 20:49:13 +00:00
|
|
|
opcodes <<
|
2005-10-01 05:55:15 +00:00
|
|
|
((adjust) ? '' : clear(reg, badchars)) +
|
2005-07-10 20:49:13 +00:00
|
|
|
"\x66\x81" +
|
2005-10-01 05:55:15 +00:00
|
|
|
[ encode_effective(shift, reg) ].pack('C') +
|
2005-07-10 20:49:13 +00:00
|
|
|
[ val.to_i ].pack('v')
|
|
|
|
end
|
|
|
|
|
|
|
|
opcodes <<
|
2005-10-01 05:55:15 +00:00
|
|
|
((adjust) ? '' : clear(reg, badchars)) +
|
2005-07-10 20:49:13 +00:00
|
|
|
"\x81" +
|
2005-10-01 05:55:15 +00:00
|
|
|
[ encode_effective(shift, reg) ].pack('C') +
|
2005-07-10 20:49:13 +00:00
|
|
|
[ val.to_i ].pack('V')
|
|
|
|
|
|
|
|
# Search for a compatible opcode
|
|
|
|
opcodes.each { |op|
|
|
|
|
begin
|
|
|
|
_check_badchars(op, badchars)
|
|
|
|
rescue
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
return op
|
|
|
|
}
|
|
|
|
|
|
|
|
if opcodes.empty?
|
|
|
|
raise RuntimeError, "Could not find a usable opcode", caller()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method generates the opcodes equivalent to subtracting with a
|
|
|
|
# negative value from a given register.
|
|
|
|
#
|
2005-11-11 01:22:03 +00:00
|
|
|
def self.add(val, reg, badchars = '', adjust = false, bits = 0)
|
|
|
|
sub(val, reg, badchars, true, adjust, bits)
|
2005-10-01 05:55:15 +00:00
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method wrappers packing an integer as a little-endian buffer.
|
|
|
|
#
|
2005-07-17 06:01:11 +00:00
|
|
|
def self.pack_dword(num)
|
|
|
|
[num].pack('V')
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method returns the least significant byte of a packed dword.
|
|
|
|
#
|
2005-07-17 06:01:11 +00:00
|
|
|
def self.pack_lsb(num)
|
2005-07-18 01:47:03 +00:00
|
|
|
pack_dword(num)[0,1]
|
2005-07-17 06:01:11 +00:00
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
#
|
|
|
|
# This method adjusts the value of the ESP register by a given amount.
|
|
|
|
#
|
2005-11-11 01:22:03 +00:00
|
|
|
def self.adjust_reg(reg, adjustment)
|
2005-10-01 05:55:15 +00:00
|
|
|
if (adjustment > 0)
|
2005-11-11 01:22:03 +00:00
|
|
|
sub(adjustment, reg, '', false, false, 32)
|
2005-10-01 05:55:15 +00:00
|
|
|
else
|
2005-11-11 01:22:03 +00:00
|
|
|
add(adjustment, reg, '', true, 32)
|
2005-10-01 05:55:15 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
def self._check_reg(*regs) # :nodoc:
|
2005-06-09 17:35:39 +00:00
|
|
|
regs.each { |reg|
|
|
|
|
if reg > 7 || reg < 0
|
|
|
|
raise ArgumentError, "Invalid register #{reg}", caller()
|
|
|
|
end
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2005-11-02 23:03:02 +00:00
|
|
|
def self._check_badchars(data, badchars) # :nodoc:
|
2005-11-09 04:18:08 +00:00
|
|
|
idx = Rex::Text.badchar_index(data, badchars)
|
2005-06-09 17:35:39 +00:00
|
|
|
if idx
|
|
|
|
raise RuntimeError, "Bad character at #{idx}", caller()
|
|
|
|
end
|
2005-06-09 04:24:45 +00:00
|
|
|
return data
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end end
|