# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/module'

module Msf

###
#
# This module exposes an interface that is used when wanting to receive
# notifications about events pertaining to exploitation.
#
###
module ExploitEvent

  #
  # This method is called when an exploit succeeds.
  #
  def on_exploit_success(exploit, session)
  end

end

###
#
# The exploit class acts as the base class for all exploit modules.  It
# provides a common interface for interacting with exploits at the most basic
# level.
#
###
class Exploit < Msf::Module

  require 'msf/core/post'
  require 'msf/core/exploit/local'

  ##
  # Exceptions
  ##

  # Indicate that the exploit should abort because it has completed
  class Complete < RuntimeError
  end

  # Indicate that the exploit should abort because it has failed
  class Failed < RuntimeError
  end


  ##
  #
  # Default compatibility settings for exploit modules.
  #
  ##
  module CompatDefaults
    #
    # Default compatibility specifications for payloads
    #
    Payload =
      {
        # Support reverse, bind, and noconn connection types
        # for all exploits unless expressly disabled.
        'ConnectionType' => 'reverse bind noconn none tunnel',
      }
  end

  ##
  #
  # The various check codes that can be returned from the ``check'' routine.
  # Please read the following wiki to learn how these codes are used:
  # https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-check()-method
  #
  ##
  module CheckCode

    #
    # Can't tell if the target is exploitable or not. This is recommended if the module fails to
    # retrieve enough information from the target machine, such as due to a timeout.
    #
    Unknown     = [ 'unknown', "Cannot reliably check exploitability."]

    #
    # The target is safe and is therefore not exploitable. This is recommended after the check
    # fails to trigger the vulnerability, or even detect the service.
    #
    Safe        = [ 'safe', "The target is not exploitable." ]

    #
    # The target is running the service in question, but the check fails to determine whether
    # the target is vulnerable or not.
    #
    Detected    = [ 'detected', "The target service is running, but could not be validated." ]

    #
    # The target appears to be vulnerable. This is recommended if the vulnerability is determined
    # based on passive reconnaissance. For example: version, banner grabbing, or having the resource
    # that's known to be vulnerable.
    #
    Appears     = [ 'appears', "The target appears to be vulnerable." ]

    #
    # The target is vulnerable. Only used if the check is able to actually take advantage of the
    # bug, and obtain hard evidence. For example: executing a command on the target machine, and
    # retrieve the output.
    #
    Vulnerable  = [ 'vulnerable', "The target is vulnerable." ]

    #
    # The module does not support the check method.
    #
    Unsupported = [ 'unsupported', "This module does not support check." ]
  end

  #
  # The various basic types of exploits
  #
  module Type

    #
    # Indicates that the exploit is a remote exploit.
    #
    Remote  = "remote"

    #
    # Indicates that the exploit is a local exploit.
    #
    Local   = "local"

    #
    # Indicates that the exploit can work anywhere it damn pleases.
    #
    Omni    = "omnipresent"
  end

  #
  # The types of stances an exploit can take, such as passive or aggressive.
  # Stances indicate whether or not the exploit triggers the exploit without
  # waiting for one or more conditions to be met (aggressive) or whether it
  # must wait for certain conditions to be satisfied before the exploit can
  # be initiated (passive)
  #
  module Stance

    #
    # Used to indicate that an exploit takes an aggressive stance.  This
    # means that the exploit proactively triggers a vulnerability.
    #
    Aggressive = "aggressive"

    #
    # Used to indicate that an exploit takes a passive stance.  This means
    # that the exploit waits for interaction from a client or other entity
    # before being able to trigger the vulnerability.
    #
    Passive    = "passive"
  end

  ###
  #
  # The remote exploit class is a specialization of the exploit module class
  # that is geared toward exploits that are performed against targets other than
  # the local machine.  This typically implies exploiting other machines via a
  # network connection, though it is not limited to this scope.
  #
  ###
  class Remote < Exploit
    require 'msf/core/exploit/auto_target'
    include Msf::Exploit::AutoTarget

    #
    # Initializes the socket array.
    #
    def initialize(info)
      super

      self.sockets = Array.new
    end

    #
    # Returns the fact that this exploit is a remote exploit.
    #
    def exploit_type
      Exploit::Type::Remote
    end

    #
    # Adds a socket to the list of sockets opened by this exploit.
    #
    def add_socket(sock)
      self.sockets << sock
    end

    #
    # Removes a socket from the list of sockets.
    #
    def remove_socket(sock)
      self.sockets.delete(sock)
    end

    #
    # This method is called once a new session has been created on behalf of
    # this exploit instance and all socket connections created by this
    # exploit should be closed.
    #
    def abort_sockets
      sockets.delete_if { |sock|

        begin
          sock.close
        rescue ::Exception
        end
        true
      }
    end

  protected

    #
    # The list of sockets established by this exploit.
    #
    attr_accessor :sockets

  end

  #
  # Load all of the exploit mixins
  #
  require 'msf/core/exploit/mixins'

  #
  # Returns an array of all of the exploit mixins.  Lame algorithm right now.
  # We search the Msf::Exploit namespace for all modules that do not have any
  # constants in them.  In the future we can replace this with a better
  # algorithm.  It's just important that it returns an array of all of the
  # mixin modules.
  #
  # @return [Array]
  def self.mixins
    mixins  = []
    wl      = [ Msf::Exploit ]
    visited = {}

    until wl.length == 0
      wl.delete_if { |mod|
        mod.constants.each { |const|
          child = mod.const_get(const)

          next if child.to_s !~ /^Msf::Exploit/

          next if visited[child]

          next if child.kind_of?(::Module) == false

          visited[child] = true

          if child.constants.length > 0
            wl << child
          else
            mixins << child
          end
        }

        true
      }
    end

    return mixins
  end

  #
  # Creates an instance of the exploit module.  Mad skillz.
  #
  def initialize(info = {})

    # Ghetto compat mirroring for payload compatibilities.  This mirrors
    #
    # Payload => Compat => xyz
    #
    # to
    #
    # Compat => Payload => xyz
    if (info['Payload'] and info['Payload']['Compat'])
      info['Compat'] = Hash.new if (info['Compat'] == nil)
      info['Compat']['Payload'] = Hash.new if (info['Compat']['Payload'] == nil)
      info['Compat']['Payload'].update(info['Payload']['Compat'])
    end

    # Call the parent constructor after making any necessary modifications
    # to the information hash.
    super(info)

    # Skip this whole routine if there are no targets
    unless info['Targets'].nil?
      # Add an Automatic Target to the Exploit if it doesn't have one
      unless has_auto_target?(info['Targets'])
        # Don't add the automatic target unless there's already more than one target to pick from
        if info['Targets'].count > 1
          # Finally, only add the target if there is a remote host option
          if self.respond_to?(:rhost) && self.respond_to?(:auto_targeted_index)
            auto = ["Automatic", { 'AutoGenerated' => true}]
            info['Targets'].unshift(auto)
          end
        end
      end
    end

    self.targets = Rex::Transformer.transform(info['Targets'], Array,
        [ Target ], 'Targets')
    self.default_target = info['DefaultTarget'] || 0
    self.payload_info = info['Payload'] || {}
    self.successful = false
    self.session_count = 0
    self.active_timeout = 120
    self.fail_reason = Failure::None

    if (info['Payload'] and info['Payload']['ActiveTimeout'])
      self.active_timeout = info['Payload']['ActiveTimeout'].to_i
    end

    # All exploits can increase the delay when waiting for a session.
    # However, this only applies to aggressive exploits.
    if aggressive?
      register_advanced_options(
        [
          OptInt.new('WfsDelay', [ false, "Additional delay when waiting for a session", 0 ])
        ], Msf::Exploit)
    end

    # Allow all exploits to leverage context keyed encoding
    register_advanced_options(
      [
        OptBool.new('EnableContextEncoding', [ false, "Use transient context when encoding payloads", false ]),
        OptPath.new('ContextInformationFile', [ false, "The information file that contains context information", nil ])
      ], Msf::Exploit)

    # Allow all exploits to disable their payload handlers
    register_advanced_options(
      [
        OptBool.new('DisablePayloadHandler', [ false, "Disable the handler code for the selected payload", false ])
      ], Msf::Exploit)
  end

  def has_auto_target?(targets=[])
    target_names = targets.collect { |target| target.first}
    target_names.each do |target|
      return true if target =~ /Automatic/
    end
    return false
  end

  ##
  #
  # Core exploit interface
  #
  # These are the methods that exploits will override to perform various
  # tasks, such as checking a target to see if it's vulnerable, automatically
  # selecting a target, or performing an exploit.
  #
  ##

  #
  # Kicks off the actual exploit.  Prior to this call, the framework will
  # have validated the data store using the options associated with this
  # exploit module.  It will also pre-generate the desired payload, though
  # exploits can re-generate the payload if necessary.
  #
  # This method is designed to be overriden by exploit modules.
  #
  def exploit
  end

  #
  # Performs last-minute sanity checking of auxiliary parameters. This method
  # is called during automated exploitation attempts and allows an
  # auxiliary module to filter bad attempts, obtain more information, and choose
  # better parameters based on the available data. Returning anything that
  # evaluates to "false" will cause this specific auxiliary attempt to
  # be skipped. This method can and will change datastore values and
  # may interact with the backend database.
  #
  def autofilter
    true
  end

  #
  # Provides a list of ports that can be used for matching this module
  # against target systems.
  #
  def autofilter_ports
    @autofilter_ports || []
  end

  #
  # Provides a list of services that can be used for matching this module
  # against target systems.
  #
  def autofilter_services
    @autofilter_services || []
  end

  #
  # Adds a port into the list of ports
  #
  def register_autofilter_ports(ports=[])
    @autofilter_ports ||= []
    @autofilter_ports << ports
    @autofilter_ports.flatten!
    @autofilter_ports.uniq!
  end

  def register_autofilter_services(services=[])
    @autofilter_services ||= []
    @autofilter_services << services
    @autofilter_services.flatten!
    @autofilter_services.uniq!
  end

  #
  # Prepares the module for exploitation, initializes any state, and starts
  # the payload handler.
  #
  def setup
    # Reset the session counts to zero.
    reset_session_counts

    return if not payload_instance
    return if not handler_enabled?

    # Configure the payload handler
    payload_instance.exploit_config = {
      'active_timeout' => self.active_timeout
    }

    # Set up the payload handlers
    payload_instance.setup_handler

    # Start the payload handler
    payload_instance.start_handler

  end

  #
  # Performs any cleanup that may be necessary, such as disconnecting
  # connections and any other such fun things.  If a payload is active then
  # its handler cleanup routines are called as well.
  #
  def cleanup
    if (payload_instance and handler_enabled?)
      payload_instance.cleanup_handler
    end
    self.abort_sockets if self.respond_to?(:abort_sockets)
  end

  #
  # Generates the encoded version of the supplied payload using the payload
  # requirements specific to this exploit.  The encoded instance is returned
  # to the caller.  This method is exposed in the manner that it is such
  # that passive exploits and re-generate an encoded payload on the fly
  # rather than having to use the pre-generated one.
  #
  # The return value is an EncodedPayload instance.
  #
  def generate_payload(pinst = nil)
    # Set the encoded payload to the result of the encoding process
    self.payload = generate_single_payload(pinst)

    # Save the payload instance
    self.payload_instance = (pinst) ? pinst : self.payload_instance

    return self.payload
  end

  #
  # Allows arbitrary shellcode to be encoded from within an exploit
  #
  def encode_shellcode_stub(code, badchars=payload_badchars)
    platform = self.platform
    if(self.payload_instance)
      self.payload_instance.platform
    end
    compatible_encoders.each do |name, mod|
      begin
        enc = framework.encoders.create(name)
        raw = enc.encode(code, badchars, nil, platform)
        return raw if raw
      rescue ::Exception
      end
    end
    nil
  end

  #
  # Allows the payload handler to spawn a new monitor
  #
  def add_handler(opts={})
    return if not payload_instance
    return if not handler_enabled?
    payload_instance.add_handler(opts)
  end

  #
  # This method generates a non-cached payload which is typically useful for
  # passive exploits that will have more than one client.
  #
  def generate_single_payload(pinst = nil, platform = nil, arch = nil, explicit_target = nil)
    explicit_target ||= target

    if (explicit_target == nil)
      raise MissingTargetError, "No target has been specified.",
        caller
    end

    # If a payload instance was supplied, use it, otherwise
    # use the active payload instance
    real_payload = (pinst) ? pinst : self.payload_instance

    if (real_payload == nil)
      raise MissingPayloadError, "No payload has been selected.",
        caller
    end

    # If this is a generic payload, then we should specify the platform
    # and architecture so that it knows how to pass things on.
    if real_payload.kind_of?(Msf::Payload::Generic)
      # Convert the architecture specified into an array.
      if arch and arch.kind_of?(String)
        arch = [ arch ]
      end

      # Define the explicit platform and architecture information only if
      # it's been specified.
      if platform
        real_payload.explicit_platform = Msf::Module::PlatformList.transform(platform)
      end

      if arch
        real_payload.explicit_arch = arch
      end

      # Force it to reset so that it will find updated information.
      real_payload.reset
    end

    # Duplicate the exploit payload requirements
    reqs = self.payload_info.dup

    # Pass save register requirements to the NOP generator
    reqs['Space']           = payload_space(explicit_target)
    reqs['SaveRegisters']   = nop_save_registers(explicit_target)
    reqs['Prepend']         = payload_prepend(explicit_target)
    reqs['PrependEncoder']  = payload_prepend_encoder(explicit_target)
    reqs['BadChars']        = payload_badchars(explicit_target)
    reqs['Append']          = payload_append(explicit_target)
    reqs['AppendEncoder']   = payload_append_encoder(explicit_target)
    reqs['MaxNops']         = payload_max_nops(explicit_target)
    reqs['MinNops']         = payload_min_nops(explicit_target)
    reqs['Encoder']         = datastore['ENCODER']
    reqs['Nop']             = datastore['NOP']
    reqs['EncoderType']     = payload_encoder_type(explicit_target)
    reqs['EncoderOptions']  = payload_encoder_options(explicit_target)
    reqs['ExtendedOptions'] = payload_extended_options(explicit_target)
    reqs['Exploit']         = self

    # Pass along the encoder don't fall through flag
    reqs['EncoderDontFallThrough'] = datastore['EncoderDontFallThrough']

    # Incorporate any context encoding requirements that are needed
    define_context_encoding_reqs(reqs)

    # Call the encode begin routine.
    encode_begin(real_payload, reqs)

    # Generate the encoded payload.
    encoded = EncodedPayload.create(real_payload, reqs)

    # Call the encode end routine which is expected to return the actual
    # encoded payload instance.
    return encode_end(real_payload, reqs, encoded)
  end

  #
  # Re-generates an encoded payload, typically called after something in the
  # datastore has changed.  An optional platform and architecture can be
  # supplied as well.
  #
  def regenerate_payload(platform = nil, arch = nil, explicit_target = nil)
    generate_single_payload(nil, platform, arch, explicit_target)
  end

  #
  # Called prior to encoding a payload.
  #
  def encode_begin(real_payload, reqs)
  end

  #
  # Called after an encoded payload has been generated.  This gives exploits
  # or mixins a chance to alter the encoded payload.
  #
  def encode_end(real_payload, reqs, encoded)
    encoded
  end

  ##
  #
  # Feature detection
  #
  # These methods check to see if there is a derived implementation of
  # various methods as a way of inferring whether or not a given exploit
  # supports the feature.
  #
  ##

  #
  # Returns true if the exploit module supports the check method.
  #
  def supports_check?
    derived_implementor?(Msf::Exploit, 'check')
  end

  #
  # Returns true if the exploit module supports the exploit method.
  #
  def supports_exploit?
    derived_implementor?(Msf::Exploit, 'exploit')
  end

  #
  # Returns a hash of the capabilities this exploit module has support for,
  # such as whether or not it supports check and exploit.
  #
  def capabilities
    {
      'check'       => supports_check?,
      'exploit'     => supports_exploit?
    }
  end

  ##
  #
  # Getters/Setters
  #
  # Querying and set interfaces for some of the exploit's attributes.
  #
  ##

  #
  # Returns MODULE_EXPLOIT to indicate that this is an exploit module.
  #
  def self.type
    Msf::MODULE_EXPLOIT
  end

  #
  # Returns MODULE_EXPLOIT to indicate that this is an exploit module.
  #
  def type
    Msf::MODULE_EXPLOIT
  end

  #
  # If we don't know the exploit type, then I guess it's omnipresent!
  #
  def exploit_type
    Type::Omni
  end

  #
  # Generally, all exploits take an aggressive stance.
  #
  def stance
    module_info['Stance'] || Stance::Aggressive
  end

  #
  # Returns true if the exploit has an aggressive stance.
  #
  def aggressive?
    (stance == Stance::Aggressive)
  end

  #
  # Returns if the exploit has a passive stance.
  #
  def passive?
    (stance == Stance::Passive)
  end

  #
  # Returns the active target for this exploit.  If not target has been
  # defined, nil is returned.  If no target was defined but there is a
  # default target, that one will be automatically used.
  #
  def target
    if self.respond_to?(:auto_targeted_index)
      if auto_target?
        auto_idx = auto_targeted_index
        if auto_idx.present?
          datastore['TARGET'] = auto_idx
        else
          # If our inserted Automatic Target was selected but we failed to
          # find a suitable target, we just grab the original first target.
          datastore['TARGET'] = 1
        end
      end
    end

    target_idx = target_index
    return (target_idx) ? targets[target_idx.to_i] : nil
  end

  #
  # The target index that has been selected.
  #
  def target_index
    target_idx = datastore['TARGET']

    default_idx = default_target || 0
    # Use the default target if one was not supplied.
    if (target_idx == nil and default_idx and default_idx >= 0)
      target_idx = default_idx
    end
    return (target_idx) ? target_idx.to_i : nil
  end

  #
  # Returns the target's platform, or the one assigned to the module itself.
  #
  def target_platform
    (target and target.platform) ? target.platform : platform
  end

  #
  # Returns the target's architecture, or the one assigned to the module
  # itself.
  #
  def target_arch
    (target and target.arch) ? target.arch : (arch == []) ? nil : arch
  end

  def normalize_platform_arch
    c_platform = (target && target.platform) ? target.platform : platform
    c_arch     = (target && target.arch)     ? target.arch     : (arch == []) ? nil : arch
    c_arch   ||= [ ARCH_X86 ]
    return c_platform, c_arch
  end

  #
  # Returns whether the requested payload is compatible with the module.
  #
  # @param [String] payload_name The payload name
  # @return [TrueClass] Payload is compatible.
  # @return [FalseClass] Payload is not compatible.
  #
  def is_payload_compatible?(name)
    p = framework.payloads[name]

    # Skip over payloads that are too big
    return false if payload_space && p.cached_size && p.cached_size > payload_space

    pi = p.new

    # Are we compatible in terms of conventions and connections and
    # what not?
    return false if !compatible?(pi)

    # If the payload is privileged but the exploit does not give
    # privileged access, then fail it.
    return false if !self.privileged && pi.privileged

    return true
  end

  #
  # Returns a list of compatible payloads based on platform, architecture,
  # and size requirements.
  #
  def compatible_payloads
    payloads = []

    c_platform, c_arch = normalize_platform_arch

    framework.payloads.each_module(
      'Arch' => c_arch, 'Platform' => c_platform) { |name, mod|
      payloads << [ name, mod ] if is_payload_compatible?(name)
    }

    return payloads;
  end

  #
  # Returns a list of compatible encoders based on architecture
  #
  def compatible_encoders
    encoders = []

    c_platform, c_arch = normalize_platform_arch

    framework.encoders.each_module_ranked(
      'Arch' => c_arch, 'Platform' => c_platform) { |name, mod|
      encoders << [ name, mod ]
    }

    return encoders;
  end

  #
  # This method returns the number of bytes that should be adjusted to the
  # stack pointer prior to executing any code.  The number of bytes to adjust
  # is indicated to the routine through the payload 'StackAdjustment'
  # attribute or through a target's payload 'StackAdjustment' attribute.
  #
  def stack_adjustment
    if (target and target.payload_stack_adjustment)
      adj = target.payload_stack_adjustment
    else
      adj = payload_info['StackAdjustment']
    end

    # Get the architecture for the current target or use the one specific to
    # this exploit
    arch = (target and target.arch) ? target.arch : self.arch

    # Default to x86 if we can't find a list of architectures
    if (arch and arch.empty? == false)
      arch = arch.join(", ")
    else
      arch = 'x86'
    end

    (adj != nil) ? Rex::Arch::adjust_stack_pointer(arch, adj) || '' : ''
  end

  #
  # Return any text that should be prepended to the payload.  The payload
  # module is passed so that the exploit can take a guess at architecture
  # and platform if it's a multi exploit.  This automatically takes into
  # account any require stack adjustments.
  #
  def payload_prepend(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_prepend)
      p = explicit_target.payload_prepend
    else
      p = payload_info['Prepend'] || ''
    end

    stack_adjustment + p
  end

  #
  # Return any text that should be appended to the payload.  The payload
  # module is passed so that the exploit can take a guess at architecture
  # and platform if it's a multi exploit.
  #
  def payload_append(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_append)
      explicit_target.payload_append
    else
      payload_info['Append'] || ''
    end
  end

  #
  # Return any text that should be prepended to the encoder of the payload.
  # The payload module is passed so that the exploit can take a guess
  # at architecture and platform if it's a multi exploit.
  #
  def payload_prepend_encoder(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_prepend_encoder)
      p = explicit_target.payload_prepend_encoder
    else
      p = payload_info['PrependEncoder'] || ''
    end

    p
  end

  #
  # Return any text that should be appended to the encoder of the payload.
  # The payload module is passed so that the exploit can take a guess
  # at architecture and platform if it's a multi exploit.
  #
  def payload_append_encoder(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_append_encoder)
      p = explicit_target.payload_append_encoder
    else
      p = payload_info['AppendEncoder'] || ''
    end

    p
  end

  #
  # Maximum number of nops to use as a hint to the framework.
  # Nil signifies that the framework should decide.
  #
  def payload_max_nops(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_max_nops)
      explicit_target.payload_max_nops
    else
      payload_info['MaxNops'] || nil
    end
  end

  #
  # Minimum number of nops to use as a hint to the framework.
  # Nil signifies that the framework should decide.
  #
  def payload_min_nops(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_min_nops)
      explicit_target.payload_min_nops
    else
      payload_info['MinNops'] || nil
    end
  end

  #
  # Returns the maximum amount of room the exploit has for a payload.
  #
  def payload_space(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_space)
      explicit_target.payload_space
    elsif (payload_info['Space'])
      payload_info['Space'].to_i
    else
      nil
    end
  end

  #
  # Returns the bad characters that cannot be in any payload used by this
  # exploit.
  #
  def payload_badchars(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_badchars)
      explicit_target.payload_badchars
    else
      payload_info['BadChars']
    end
  end

  #
  # Returns the payload encoder type that is associated with either the
  # current target of the exploit in general.
  #
  def payload_encoder_type(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_encoder_type)
      explicit_target.payload_encoder_type
    else
      payload_info['EncoderType']
    end
  end

  #
  # Returns the payload encoder option hash that is used to initialize the
  # datastore of the encoder that is selected when generating an encoded
  # payload.
  #
  def payload_encoder_options(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_encoder_options)
      explicit_target.payload_encoder_options
    else
      payload_info['EncoderOptions']
    end
  end

  #
  # Returns the payload extended options hash which is used to provide
  # a location to store extended information that may be useful to
  # a particular type of payload or mixin.
  #
  def payload_extended_options(explicit_target = nil)
    explicit_target ||= target

    if explicit_target and explicit_target.payload_extended_options
      explicit_target.payload_extended_options
    else
      payload_info['ExtendedOptions']
    end
  end

  ##
  #
  # NOP requirements
  #
  # Hints to the nop generator on how it should perform if it's used.
  #
  ##

  #
  # Returns the list of registers that the NOP generator should save,
  # if any.  It will use the current target's save registers in precedence
  # over those defined globally for the exploit module.
  #
  # If there are no save registers, nil is returned.
  #
  def nop_save_registers(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.save_registers)
      return explicit_target.save_registers
    else
      return module_info['SaveRegisters']
    end
  end

  #
  # Returns the first compatible NOP generator for this exploit's payload
  # instance.
  #
  def nop_generator
    return nil if (!payload_instance)

    payload_instance.compatible_nops.each { |nopname, nopmod|
      return nopmod.new
    }
  end


  #
  # Generates a NOP sled using the #make_nops method.
  # The difference between this and #make_nops is this method is much faster, good for exploit
  # developers that actually want huge chunks of NOPs. The downside of using this is the NOP sled
  # is less randomized.
  #
  # @param count [String] Number of NOPs to return.
  # @return [String] NOPs
  #
  def make_fast_nops(count)
    max_nop_chunk_size = 100

    if count < max_nop_chunk_size
      return make_nops(count)
    end

    nops = make_nops(max_nop_chunk_size)
    nops += nops while nops.length < count

    nops[0, count]
  end


  #
  # Generates a nop sled of a supplied length and returns it to the caller.
  #
  def make_nops(count)
    # If we're debugging, then make_nops will return a safe sled.  We
    # currently assume x86.
    if debugging?
      return "\x90" * count
    end

    nop_sled = nil

    # If there is no payload instance then we can't succeed.
    return nil if (!payload_instance)

    payload_instance.compatible_nops.each { |nopname, nopmod|
      # Create an instance of the nop module
      nop = nopmod.new

      # The list of save registers
      save_regs = nop_save_registers || []

      if (save_regs.empty? == true)
        save_regs = nil
      end

      begin
        nop.copy_ui(self)

        nop_sled = nop.generate_sled(count,
          'BadChars'      => payload_badchars || '',
          'SaveRegisters' => save_regs)

        if nop_sled && nop_sled.length == count
          break
        else
          wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit", 'core', LEV_0)
        end
      rescue
        wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit: #{$!}",
          'core', LEV_0)
      end
    }

    nop_sled
  end

  ##
  #
  # Utility methods for generating random text that implicitly uses the
  # exploit's bad character set.
  #
  ##

  #
  # Generate random text characters avoiding the exploit's bad
  # characters.
  #
  def rand_text(length, bad=payload_badchars)
    if debugging?
      "A" * length
    else
      Rex::Text.rand_text(length, bad)
    end
  end

  #
  # Generate random english-like avoiding the exploit's bad
  # characters.
  #
  def rand_text_english(length, bad=payload_badchars)
    if debugging?
      "A" * length
    else
      Rex::Text.rand_text_english(length, bad)
    end
  end

  #
  # Generate random high ascii characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_highascii(length, bad=payload_badchars)
    if debugging?
      "A" * length
    else
      Rex::Text.rand_text_highascii(length, bad)
    end
  end

  #
  # Generate random alpha characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_alpha(length, bad=payload_badchars)
    if debugging?
      "A" * length
    else
      Rex::Text.rand_text_alpha(length, bad)
    end
  end

  #
  # Generate random alpha upper characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_alpha_upper(length, bad=payload_badchars)
    if debugging?
      "A" * length
    else
      Rex::Text.rand_text_alpha_upper(length, bad)
    end
  end

  #
  # Generate random alpha lower characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_alpha_lower(length, bad=payload_badchars)
    if debugging?
      "a" * length
    else
      Rex::Text.rand_text_alpha_lower(length, bad)
    end
  end

  #
  # Generate random alphanumeric characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_alphanumeric(length, bad=payload_badchars)
    if debugging?
      "A" * length
    else
      Rex::Text.rand_text_alphanumeric(length, bad)
    end
  end

  #
  # Generate random numeric characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_numeric(length, bad=payload_badchars)
    if debugging?
      "0" * length
    else
      Rex::Text.rand_text_numeric(length, bad)
    end
  end

  #
  # Generate a random character avoiding the exploit's bad
  # characters.
  #
  def rand_char(bad=payload_badchars)
    if debugging?
      "A"
    else
      Rex::Text.rand_char(bad)
    end
  end

  #
  # Generate a non-repeating static random string
  #
  def pattern_create(length, sets = nil)
    Rex::Text.pattern_create(length, sets)
  end

  #
  # The default "wait for session" delay is zero for all exploits.
  #
  def wfs_delay
    (datastore['WfsDelay'] || 0).to_i
  end

  #
  # Allow the user to disable the payload handler
  #
  def handler_enabled?
    not datastore['DisablePayloadHandler']
  end

  ##
  #
  # Handler interaction
  #
  ##

  #
  # Passes the connection to the associated payload handler to see if the
  # exploit succeeded and a connection has been established.  The return
  # value can be one of the Handler::constants.
  #
  def handler(*args)
    if payload_instance && handler_enabled?
      payload_instance.handler(*args)
    end
  end

  def interrupt_handler
    if payload_instance && handler_enabled? && payload_instance.respond_to?(:interrupt_wait_for_session)
      payload_instance.interrupt_wait_for_session()
    end
  end

  ##
  #
  # Session tracking
  #
  ##

  #
  # This is called by the payload when a new session is created
  #
  def on_new_session(session)
    self.session_count += 1
    self.successful = true
  end

  #
  # A boolean for whether a session has been created yet
  #
  def session_created?
    (self.session_count > 0) ? true : false
  end

  #
  # Reset the session counter to zero (which occurs during set up of the
  # exploit prior to calling exploit).
  #
  def reset_session_counts
    self.session_count = 0
  end


  ##
  # Failure tracking
  ##

  # Raises a Msf::Exploit::Failed exception. It overrides the fail_with method
  # in lib/msf/core/module.rb
  #
  # @param reason [String] A constant from Msf::Module::Failure.
  #                        If the reason does not come from there, then it will default to
  #                        Msf::Module::Failure::Unknown.
  # @param msg [String] (Optional) A message about the failure.
  # @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception.
  # @return [void]
  # @see Msf::Module::Failure
  # @see Msf::Module#fail_with
  # @example
  #   fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload')
  def fail_with(reason,msg=nil)
    # The reason being registered here will be used later on, so it's important we don't actually
    # provide a made-up one.
    allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)}
    if allowed_values.include?(reason)
      self.fail_reason = reason
    else
      self.fail_reason = Msf::Module::Failure::Unknown
    end

    self.fail_detail = msg
    raise Msf::Exploit::Failed, (msg || "No failure message given")
  end

  def setup_fail_detail_from_exception e
    # Build a user-friendly error message
    msg = "#{e}"
    unless e.class == Msf::Exploit::Failed
      msg = "#{e.class} #{e}"
    end

    self.error = e

    # Record the detailed reason
    self.fail_detail ||= e.to_s
    msg
  end

  #
  # Handle the exception
  #
  def handle_exception e
    msg = setup_fail_detail_from_exception e

    case e
      when Msf::Exploit::Complete
        # Nothing to show in this case
        return

      when Msf::Exploit::Failed
        self.print_error("Exploit aborted due to failure: #{self.fail_reason}: #{msg}")

        # The caller should have already set self.fail_reason
        if self.fail_reason == Msf::Exploit::Failure::None
          self.fail_reason = Msf::Exploit::Failure::Unknown
        end

      when Rex::ConnectionError
        self.fail_reason = Msf::Exploit::Failure::Unreachable
        self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
        elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
        dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)

      when Rex::BindFailed
        self.fail_reason = Msf::Exploit::Failure::BadConfig
        self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
        elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
        dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)

      when Timeout::Error
        self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
        self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
        elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
        dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
      else

        # Compare as a string since not all error classes may be loaded
        case msg
          when /access.denied|Login Failed/i # Covers SMB as well as some generic errors
            self.fail_reason = Msf::Exploit::Failure::NoAccess
          when /connection reset/i
            self.fail_reason = Msf::Exploit::Failure::Disconnected
          when /connection timed out|SSL_connect|unreachable|connection was refused/i
            self.fail_reason = Msf::Exploit::Failure::Unreachable
          when /unable.*target/i
            self.fail_reason = Msf::Exploit::Failure::NoTarget
          when /execution expired/i
            self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
          when /(doesn.t|not).*vulnerable|may.*patched/i
            self.fail_reason = Msf::Exploit::Failure::NotVulnerable
        end

        # The caller should have already set self.fail_reason
        if self.fail_reason == Msf::Exploit::Failure::None
          self.fail_reason = Msf::Exploit::Failure::Unknown
        end

        if self.fail_reason == Msf::Exploit::Failure::Unknown
          self.print_error("Exploit failed: #{msg}")
        else
          self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
        end

        elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
        dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
    end

    # Record the error to various places
    self.framework.events.on_module_error(self, msg)

    # Report the failure (and attempt) in the database
    self.report_failure

    # Interrupt any session waiters in the handler
    self.interrupt_handler
  end

  def report_failure
    return unless framework.db and framework.db.active

    info = {
      :timestamp   => Time.now.utc,
      :workspace   => framework.db.find_workspace(self.workspace),
      :module      => self.fullname,
      :fail_reason => self.fail_reason,
      :fail_detail => self.fail_detail,
      :target_name => self.target.name,
      :username    => self.owner,
      :refs        => self.references,
      :run_id      => self.datastore['RUN_ID']
    }

    if self.datastore['RHOST'] and self.options['RHOST']
      info[:host] = self.datastore['RHOST']
    end

    if self.datastore['RPORT'] and self.options['RPORT']
      info[:port] = self.datastore['RPORT']
      if self.class.ancestors.include?(Msf::Exploit::Remote::Tcp)
        info[:proto] = 'tcp'
      end
    end

    framework.db.report_exploit_failure(info)
  end

  ##
  #
  # Aliases
  #
  # These allow access to methods inside this class, even if exploits use mixins that
  # override them.
  #
  ##

  # Give exploits the ability to use the original +regenerate_payload+ so
  # they can avoid needing additional arguments added by overridden versions.
  # Used specifically by things that include +TcpServer+ (or a descendant)
  # but which are active exploits.
  alias :exploit_regenerate_payload :regenerate_payload


  ##
  #
  # Attributes
  #
  ##

  #
  # The reason why the exploit was not successful (one of Msf::Module::Failure)
  #
  attr_accessor  :fail_reason

  #
  # Detailed exception string indicating why the exploit was not successful
  #
  attr_accessor  :fail_detail

  #
  # The list of targets.
  #
  attr_reader   :targets
  #
  # The default target.
  #
  attr_reader   :default_target
  #
  # The payload requirement hash.
  #
  attr_reader   :payload_info
  #
  # The active payload instance.
  #
  attr_accessor :payload_instance
  #
  # The encoded payload instance.  An instance of an
  # EncodedPayload object.
  #
  attr_accessor :payload

  #
  # The number of active sessions created by this instance
  #
  attr_reader  :session_count

  #
  # The boolean indicating whether the exploit succeeded
  #
  attr_reader  :successful


protected

  #
  # Writable copy of the list of targets.
  #
  attr_writer :targets
  #
  # Writable copy of the default target.
  #
  attr_writer :default_target
  #
  # Writable copy of the payload requirement hash.
  #
  attr_writer :payload_info
  #
  # Number of sessions created by this exploit instance.
  #
  attr_writer :session_count
  #
  # Maximum number of seconds for active handlers
  #
  attr_accessor :active_timeout

  #
  # Boolean indicating whether the exploit succeeded
  #
  attr_writer :successful


  #
  # Overrides the base class method and serves to initialize default
  # compatibilities for exploits
  #
  def init_compat
    super

    #
    # Merge in payload compatible defaults
    #
    p = module_info['Compat']['Payload']

    CompatDefaults::Payload.each_pair { |k,v|
      (p[k]) ? p[k] << " #{v}" : p[k] = v
    }

    #
    # Set the default save registers if none have been explicitly
    # specified.
    #
    if (module_info['SaveRegisters'] == nil)
      module_info['SaveRegisters'] = [ 'esp', 'ebp' ]
    end
  end

  #
  # Gets the memory map file and other context information that is
  # required when wanting to support context keyed encoding
  #
  def define_context_encoding_reqs(reqs)
    return unless datastore['EnableContextEncoding']

    # At present, we don't support any automatic methods of obtaining
    # context information.  In the future, we might support obtaining
    # temporal information remotely.

    # Pass along the information specified in our exploit datastore as
    # encoder options
    reqs['EncoderOptions'] = {} if reqs['EncoderOptions'].nil?
    reqs['EncoderOptions']['EnableContextEncoding']  = datastore['EnableContextEncoding']
    reqs['EncoderOptions']['ContextInformationFile'] = datastore['ContextInformationFile']
  end

end

end
