
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
