# -*- coding: binary -*-
module Msf

###
#
# This modules provides a target-aware brute-forcing wrapper.  It implements
# the exploit method and calls exploit_brute with target supplied information.
# If the selected target is not a bruteforce target, then single_exploit is
# called.
#
###
module Exploit::Brute

	#
	# Initializes an instance of an exploit module that supports brute force
	# targets.
	#
	def initialize(info = {})
		super

		#
		# Register BruteWait and BruteStep as two advanced options for this
		# exploit even though not all targets may be brute force targets.
		#
		register_advanced_options(
			[
				OptInt.new('BruteWait', [ false, "Delay between brute force attempts"     ]),
				OptInt.new('BruteStep', [ false, "Step size between brute force attempts" ])
			], Msf::Exploit::Brute)
	end

	#
	# Entry point for initiating an exploit.  This module wrappers the exploit
	# method and determines whether or not the selected target supports brute
	# force.  If it does, it does some special things and wraps the brute
	# forcing logic.
	#
	def exploit
		# Is the selected target a brute force target?
		if (target.bruteforce?)
			# The step direction is automatically calculated
			direction = {}

			bf = target.bruteforce

			# Get the start and stop address hashes
			start = bf.start_addresses ? bf.start_addresses.dup : {}
			stop  = bf.stop_addresses ? bf.stop_addresses.dup : {}
			step  = bf.step_size
			delay = bf.delay

			# Enumerate each start address and try to figure out the direction
			start.each_pair { |name, addr|
				# If there's a stop address, figure out if it's above or below
				# the start address
				if (stop[name])
					if (stop[name] < addr)
						direction[name] = -1
					else
						direction[name] = 1
					end
				# If there's no stop address, infer the direction based on
				# the default
				else
					direction[name] = bf.default_direction
				end
			}

			# Import start/stop address overrides from the datastore
			import_from_datastore(start, 'Start')
			import_from_datastore(stop,  'Stop')

			# User-defined brute wait?
			if self.datastore['BruteWait'] and self.datastore['BruteWait'] > 0
				delay = self.datastore['BruteWait'].to_i
			end

			# User-defined brute step?
			if self.datastore['BruteStep'] and self.datastore['BruteStep'] > 0
				step = self.datastore['BruteStep'].to_i
			end

			# Sane defaults
			delay = 1 if delay.nil? or delay == 0

			# Okay, we've got all this crap out of the way, let's actually brute
			# force
			stopped = []
			curr    = start.dup

			# Automatically determine the step size based off the nop sled length
			if step == 0
				step = payload.nop_sled_size

				if step == 0
					raise OptionValidateError.new(['BruteStep']), "The step size for this exploit is invalid"
				end
			end

			# Keep going until we run out of options
			while (curr.length != stopped.length)

				# Stop brute forcing once a session is found
				break if session_created?

				# Fire off an exploit attempt with the supplied addresses
				brute_exploit(curr)

				# Give it time before we try again
				brute_wait(delay)

				# Scan each current key, increasing it or decreasing it by the
				# step size according to its direction
				curr.each_key { |k|

					# Has movement been stopped on this address?  If so, skip it.
					next if (stopped.include?(k))

					# Calculate the next address before we move it to see if
					# we're going to go over
					next_addr = step * direction[k]

					# If this item has hit a stop address, add it to the stopped
					# hash and move it no further
					if (stop[k])
						if ((direction[k] == 1 and curr[k] + next_addr >= stop[k]) or
								(direction[k] == -1 and curr[k] + next_addr < stop[k]))
							stopped << k
							next
						end
					end

					# If it's not time to stop, move it
					curr[k] += next_addr
				}
			end
		else
			single_exploit
		end
	end

	#
	# This routine is called once per brute force iteration.  The addresses
	# parameter is a hash of addresses that are incremented each iteration and
	# are derived from the target's bruteforce information or the module's
	# datastore in case they are being overriden.
	#
	def brute_exploit(addrs)
	end

	#
	# Call if the target is not a brute force target.
	#
	def single_exploit
	end

	#
	# Waits for the provide delay.
	#
	def brute_wait(delay)
		sleep(delay)
	end

protected

	#
	# Imports information into the supplied hash from the datastore.
	# This is a way of allowing the user to override values for a
	# specific brute force target by name without them actually
	# being conveyed in the options list.  This is a bit of a change
	# from 2.x, but 2.x didn't have per-target brute force
	# addresses, which I think is more valuable.
	#
	def import_from_datastore(hash, prefix = '')
		hash.each_key { |k|
			if (self.datastore[prefix + k])
				hash[k] = self.datastore[prefix + k]
			end
		}
	end

end

end
