510 lines
21 KiB
Ruby
510 lines
21 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
module Rex
|
|
|
|
module Poly
|
|
|
|
#
|
|
# A subclass to represent a Rex poly machine on the x86 architecture.
|
|
#
|
|
class MachineX86 < Rex::Poly::Machine
|
|
|
|
def initialize( badchars='', consume_base_pointer=nil, consume_stack_pointer=true )
|
|
super( badchars, Metasm::Ia32.new )
|
|
|
|
@reg_available << Rex::Arch::X86::EAX
|
|
@reg_available << Rex::Arch::X86::EBX
|
|
@reg_available << Rex::Arch::X86::ECX
|
|
@reg_available << Rex::Arch::X86::EDX
|
|
@reg_available << Rex::Arch::X86::ESI
|
|
@reg_available << Rex::Arch::X86::EDI
|
|
@reg_available << Rex::Arch::X86::EBP
|
|
@reg_available << Rex::Arch::X86::ESP
|
|
|
|
# By default we consume the EBP register if badchars contains \x00. This helps speed
|
|
# things up greatly as many instructions opperating on EBP introduce a NULL byte. For
|
|
# example, a MOV instruction with EAX as the source operand is as follows:
|
|
# 8B08 mov ecx, [eax]
|
|
# but the same instruction with EBP as the source operand is as follows:
|
|
# 8B4D00 mov ecx, [ebp] ; This is assembled as 'mov ecx, [ebp+0]'
|
|
# we can see that EBP is encoded differently with an offset included. We can still
|
|
# try to generate a solution with EBP included and \x00 in the badchars list but
|
|
# it can take considerably longer.
|
|
if( ( consume_base_pointer.nil? and not Rex::Text.badchar_index( "\x00", @badchars ).nil? ) or consume_base_pointer == true )
|
|
create_variable( 'base_pointer', 'ebp' )
|
|
end
|
|
|
|
# By default we consume the ESP register to avoid munging the stack.
|
|
if( consume_stack_pointer )
|
|
create_variable( 'stack_pointer', 'esp' )
|
|
end
|
|
|
|
# discover all the safe FPU instruction we can use.
|
|
@safe_fpu_instructions = ::Array.new
|
|
Rex::Arch::X86.fpu_instructions.each do | fpu |
|
|
if( is_valid?( fpu ) )
|
|
@safe_fpu_instructions << fpu
|
|
end
|
|
end
|
|
end
|
|
|
|
#
|
|
# The general purpose registers are 32bit
|
|
#
|
|
def native_size
|
|
Rex::Poly::Machine::DWORD
|
|
end
|
|
|
|
#
|
|
# Overload this method to intercept the 'set' primitive with the 'location' keyword
|
|
# and create the block with the '_set_variable_location'. We do this to keep a
|
|
# consistent style.
|
|
#
|
|
def create_block_primitive( block_name, primitive_name, *args )
|
|
if( primitive_name == 'set' and args.length == 2 and args[1] == 'location' )
|
|
_create_block_primitive( block_name, '_set_variable_location', args[0] )
|
|
else
|
|
super
|
|
end
|
|
end
|
|
|
|
#
|
|
# XXX: If we have a loop primitive, it is a decent speed bump to force the associated variable
|
|
# of the first loop primitive to be assigned as ECX (for the x86 LOOP instruction), this is not
|
|
# neccasary but can speed generation up significantly.
|
|
#
|
|
#def generate
|
|
# @blocks.each_value do | block |
|
|
# if( block.first.primitive == 'loop' )
|
|
# @variables.delete( block.first.args.first )
|
|
# create_variable( block.first.args.first, 'ecx' )
|
|
# break
|
|
# end
|
|
# end
|
|
# # ...go go go
|
|
# super
|
|
#end
|
|
|
|
protected
|
|
|
|
#
|
|
# Resolve a register number into a suitable register name.
|
|
#
|
|
def _register_value( regnum, size=nil )
|
|
value = nil
|
|
# we default to a native 32 bits if no size is specified.
|
|
if( size.nil? )
|
|
size = native_size()
|
|
end
|
|
|
|
if( size == Rex::Poly::Machine::DWORD )
|
|
value = Rex::Arch::X86::REG_NAMES32[ regnum ]
|
|
elsif( size == Rex::Poly::Machine::WORD )
|
|
value = Rex::Arch::X86::REG_NAMES16[ regnum ]
|
|
elsif( size == Rex::Poly::Machine::BYTE )
|
|
# (will return nil for ESI,EDI,EBP,ESP)
|
|
value = Rex::Arch::X86::REG_NAMES8L[ regnum ]
|
|
else
|
|
raise RuntimeError, "Register number '#{regnum}' (size #{size.to_i}) is unavailable."
|
|
end
|
|
return value
|
|
end
|
|
|
|
#
|
|
# Create the x86 primitives.
|
|
#
|
|
def _create_primitives
|
|
|
|
#
|
|
# Create the '_set_variable_location' primitive. The first param it the variable to place the current
|
|
# blocks location value in.
|
|
#
|
|
_create_primitive( '_set_variable_location',
|
|
::Proc.new do | block, machine, variable |
|
|
if( @safe_fpu_instructions.empty? )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
|
|
"mov #{machine.variable_value( 'temp' )}, esp",
|
|
"fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
|
|
"pop #{machine.variable_value( variable )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable |
|
|
if( @safe_fpu_instructions.empty? )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
|
|
"mov #{machine.variable_value( 'temp' )}, esp",
|
|
"fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
|
|
"pop #{machine.variable_value( variable )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable |
|
|
if( @safe_fpu_instructions.empty? )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
|
|
"push esp",
|
|
"pop #{machine.variable_value( 'temp' )}",
|
|
"fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
|
|
"pop #{machine.variable_value( variable )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable |
|
|
if( @safe_fpu_instructions.empty? )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
|
|
"fnstenv [ esp - 12 ]",
|
|
"pop #{machine.variable_value( variable )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable |
|
|
[
|
|
"call $+5",
|
|
"pop #{machine.variable_value( variable )}",
|
|
"push #{machine.block_offset( block ) + 5}",
|
|
"pop #{machine.variable_value( 'temp' )}",
|
|
"sub #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable |
|
|
[
|
|
"db 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0",
|
|
"pop #{machine.variable_value( variable )}",
|
|
"push #{machine.block_offset( block ) + 5}",
|
|
"pop #{machine.variable_value( 'temp' )}",
|
|
"sub #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
|
|
]
|
|
end
|
|
)
|
|
|
|
#
|
|
# Create the 'loop' primitive. The first param it the counter variable which holds the number of
|
|
# times to perform the loop. The second param it the destination block to loop to.
|
|
#
|
|
_create_primitive( 'loop',
|
|
::Proc.new do | block, machine, counter, destination |
|
|
if( machine.variable_value( counter ) != Rex::Arch::X86::REG_NAMES32[ Rex::Arch::X86::ECX ] )
|
|
# we raise and UndefinedPermutation exception to indicate that untill a valid register (ECX) is
|
|
# chosen we simply can't render this. This lets the machine know we can still try to use this
|
|
# permutation and at a later stage the requirements (counter==ecx) may be satisfied.
|
|
raise UndefinedPermutation
|
|
end
|
|
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
|
|
Rex::Arch::X86.loop( offset )
|
|
end,
|
|
::Proc.new do | block, machine, counter, destination |
|
|
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
|
|
[
|
|
"dec #{machine.variable_value( counter )}",
|
|
"test #{machine.variable_value( counter )}, #{machine.variable_value( counter )}",
|
|
# JNZ destination
|
|
"db 0x0F, 0x85 dd #{ "0x%08X" % [ offset & 0xFFFFFFFF ] }"
|
|
]
|
|
end
|
|
)
|
|
|
|
#
|
|
# Create the 'xor' primitive. The first param it the variable to xor with the second param value which
|
|
# can be either a variable, literal or block offset.
|
|
#
|
|
_create_primitive( 'xor',
|
|
::Proc.new do | block, machine, variable, value |
|
|
[
|
|
"xor #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
# a ^ b == (a | b) & ~(a & b)
|
|
[
|
|
"mov #{machine.variable_value( 'temp' )}, #{machine.variable_value( variable )}",
|
|
"or #{machine.variable_value( 'temp' )}, #{machine.resolve_value( value )}",
|
|
"and #{machine.variable_value( variable )}, #{machine.resolve_value( value )}",
|
|
"not #{machine.variable_value( variable )}",
|
|
"and #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
|
|
]
|
|
end
|
|
)
|
|
|
|
#
|
|
# Create the 'goto' primitive. The first param is a destination block to jump to.
|
|
#
|
|
_create_primitive( 'goto',
|
|
::Proc.new do | block, machine, destination |
|
|
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
|
|
if( ( offset > 0 and offset > 127 ) or ( offset < 0 and offset < -127 ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
# short relative jump
|
|
"db 0xEB db #{ "0x%02X" % [ offset & 0xFF ] }"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, destination |
|
|
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
|
|
[
|
|
# near relative jump
|
|
"db 0xE9 dd #{ "0x%08X" % [ offset & 0xFFFFFFFF ] }"
|
|
]
|
|
end
|
|
)
|
|
|
|
#
|
|
# Create the 'add' primitive. The first param it the variable which will be added to the second
|
|
# param, which may either be a literal number value, a variables assigned register or a block
|
|
# name, in which case the block offset will be used.
|
|
#
|
|
_create_primitive( 'add',
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"lea #{machine.variable_value( variable )}, [ #{machine.variable_value( variable )} + #{machine.resolve_value( value )} ]"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
[
|
|
"push #{machine.resolve_value( value )}",
|
|
"add #{machine.variable_value( variable )}, [esp]",
|
|
"pop #{machine.variable_value( 'temp' )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
[
|
|
"add #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"sub #{machine.variable_value( variable )}, #{ "0x%08X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFFFFFF ] }"
|
|
]
|
|
end
|
|
# ::Proc.new do | block, machine, variable, value |
|
|
# if( machine.variable_exist?( value ) )
|
|
# raise UnallowedPermutation
|
|
# end
|
|
# [
|
|
# "push #{ "0x%08X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFFFFFF ] }",
|
|
# "pop #{machine.variable_value( 'temp' )}",
|
|
# "not #{machine.variable_value( 'temp' )}",
|
|
# "add #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
|
|
# ]
|
|
# end,
|
|
# ::Proc.new do | block, machine, variable, value |
|
|
# if( machine.variable_exist?( value ) )
|
|
# raise UnallowedPermutation
|
|
# end
|
|
# [
|
|
# "xor #{machine.variable_value( 'temp' )}, #{machine.variable_value( 'temp' )}",
|
|
# "mov #{machine.variable_value( 'temp', 16 )}, #{ "0x%04X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFF ] }",
|
|
# "not #{machine.variable_value( 'temp', 16 )}",
|
|
# "add #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
|
|
# ]
|
|
# end,
|
|
)
|
|
|
|
#
|
|
# Create the 'set' primitive. The first param it the variable which will be set. the second
|
|
# param is the value to set the variable to (a variable, block or literal).
|
|
#
|
|
_create_primitive( 'set',
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
[
|
|
"push #{ "0x%08X" % [ ~machine.resolve_value( value ) & 0xFFFFFFFF ] }",
|
|
"pop #{machine.variable_value( variable )}",
|
|
"not #{machine.variable_value( variable )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
if( machine.resolve_value( value, WORD ) > 0xFFFF )
|
|
raise UndefinedPermutation
|
|
end
|
|
[
|
|
"xor #{machine.variable_value( variable )}, #{machine.variable_value( variable )}",
|
|
"mov #{machine.variable_value( variable, WORD )}, #{ "0x%04X" % [ ~machine.resolve_value( value, WORD ) & 0xFFFF ] }",
|
|
"not #{machine.variable_value( variable, WORD )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
[
|
|
"push #{machine.resolve_value( value )}",
|
|
"pop #{machine.variable_value( variable )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
[
|
|
"mov #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
if( machine.resolve_value( value, WORD ) > 0xFFFF )
|
|
raise UndefinedPermutation
|
|
end
|
|
[
|
|
"xor #{machine.variable_value( variable )}, #{machine.variable_value( variable )}",
|
|
"mov #{machine.variable_value( variable, WORD )}, #{ "0x%04X" % [ machine.resolve_value( value, WORD ) & 0xFFFF ] }"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
dword = machine.make_safe_dword( machine.resolve_value( value ) )
|
|
[
|
|
"mov #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword ] }",
|
|
"sub #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword - machine.resolve_value( value ) ] }"
|
|
]
|
|
end,
|
|
::Proc.new do | block, machine, variable, value |
|
|
if( machine.variable_exist?( value ) )
|
|
raise UnallowedPermutation
|
|
end
|
|
dword = machine.make_safe_dword( machine.resolve_value( value ) )
|
|
[
|
|
"mov #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword - machine.resolve_value( value ) ] }",
|
|
"add #{machine.variable_value( variable )}, #{ "0x%08X" % [ ~dword & 0xFFFFFFFF ] }",
|
|
"not #{machine.variable_value( variable )}"
|
|
]
|
|
end
|
|
)
|
|
|
|
#
|
|
# Create the 'load' primitive. The first param it the variable which will be set. The second
|
|
# param is the value (either a variable or literal) to load from. the third param is the size
|
|
# of the load operation, either DWORD, WORD or BYTE.
|
|
#
|
|
_create_primitive( 'load',
|
|
::Proc.new do | block, machine, variable, value, size |
|
|
result = nil
|
|
if( size == Rex::Poly::Machine::DWORD )
|
|
result = [ "mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]" ]
|
|
elsif( size == Rex::Poly::Machine::WORD )
|
|
result = [ "movzx #{machine.variable_value( variable )}, word [#{machine.resolve_value( value )}]" ]
|
|
elsif( size == Rex::Poly::Machine::BYTE )
|
|
result = [ "movzx #{machine.variable_value( variable )}, byte [#{machine.resolve_value( value )}]" ]
|
|
else
|
|
raise InvalidPermutation
|
|
end
|
|
result
|
|
end,
|
|
::Proc.new do | block, machine, variable, value, size |
|
|
result = nil
|
|
if( size == Rex::Poly::Machine::DWORD )
|
|
# we raise and UnallowedPermutation here as this permutation should only satisfy requests for
|
|
# sizes of WORD or BYTE, any DWORD requests will be satisfied by the above permutation (otherwise
|
|
# we would just be duplicating a 'mov dest, [src]' sequence which is the same as above.
|
|
raise UnallowedPermutation
|
|
elsif( size == Rex::Poly::Machine::WORD )
|
|
result = [
|
|
"mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]",
|
|
"shl #{machine.variable_value( variable )}, 16",
|
|
"shr #{machine.variable_value( variable )}, 16"
|
|
]
|
|
elsif( size == Rex::Poly::Machine::BYTE )
|
|
result = [
|
|
"mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]",
|
|
"shl #{machine.variable_value( variable )}, 24",
|
|
"shr #{machine.variable_value( variable )}, 24"
|
|
]
|
|
else
|
|
raise InvalidPermutation
|
|
end
|
|
result
|
|
end,
|
|
::Proc.new do | block, machine, variable, value, size |
|
|
result = nil
|
|
if( size == Rex::Poly::Machine::DWORD )
|
|
result = [
|
|
"push [#{machine.resolve_value( value )}]",
|
|
"pop #{machine.variable_value( variable )}"
|
|
]
|
|
elsif( size == Rex::Poly::Machine::WORD )
|
|
result = [
|
|
"push [#{machine.resolve_value( value )}]",
|
|
"pop #{machine.variable_value( variable )}",
|
|
"shl #{machine.variable_value( variable )}, 16",
|
|
"shr #{machine.variable_value( variable )}, 16"
|
|
]
|
|
elsif( size == Rex::Poly::Machine::BYTE )
|
|
result = [
|
|
"push [#{machine.resolve_value( value )}]",
|
|
"pop #{machine.variable_value( variable )}",
|
|
"shl #{machine.variable_value( variable )}, 24",
|
|
"shr #{machine.variable_value( variable )}, 24"
|
|
]
|
|
else
|
|
raise InvalidPermutation
|
|
end
|
|
result
|
|
end
|
|
)
|
|
|
|
#
|
|
# Create the 'store' primitive.
|
|
#
|
|
_create_primitive( 'store',
|
|
::Proc.new do | block, machine, variable, value, size |
|
|
result = nil
|
|
if( size == Rex::Poly::Machine::DWORD )
|
|
result = [ "mov [#{machine.variable_value( variable )}], #{machine.resolve_value( value )}" ]
|
|
elsif( size == Rex::Poly::Machine::WORD )
|
|
result = [ "mov word [#{machine.variable_value( variable )}], #{machine.resolve_value( value, WORD )}" ]
|
|
elsif( size == Rex::Poly::Machine::BYTE )
|
|
if( machine.resolve_value( value, BYTE ).nil? )
|
|
# so long as we cant resolve the variable to an 8bit register value (AL,BL,CL,DL) we must raise
|
|
# an UndefinedPermutation exception (this will happen when the variable has been assigned to ESI,
|
|
# EDI, EBP or ESP which dont have a low byte representation)
|
|
raise UndefinedPermutation
|
|
end
|
|
result = [ "mov byte [#{machine.variable_value( variable )}], #{machine.resolve_value( value, BYTE )}" ]
|
|
else
|
|
raise InvalidPermutation
|
|
end
|
|
result
|
|
end,
|
|
::Proc.new do | block, machine, variable, value, size |
|
|
result = nil
|
|
if( size == Rex::Poly::Machine::DWORD )
|
|
result = [
|
|
"push #{machine.resolve_value( value )}",
|
|
"pop [#{machine.variable_value( variable )}]"
|
|
]
|
|
elsif( size == Rex::Poly::Machine::WORD )
|
|
result = [
|
|
"push #{machine.resolve_value( value, WORD )}",
|
|
"pop word [#{machine.variable_value( variable )}]"
|
|
]
|
|
else
|
|
# we can never do this permutation for BYTE size (or any other size)
|
|
raise UnallowedPermutation
|
|
end
|
|
result
|
|
end
|
|
)
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|