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



###
#
# 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

##
  # 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://docs.metasploit.com/docs/development/developing-modules/guides/how-to-write-a-check-method.html
  #
  ##
  class CheckCode < Struct.new(:code, :message, :reason, :details)
    # Do customization here because we need class constants and special
    # optional values and the block mode of Struct.new does not support that.
    #
    #
    # NOTE: This class relies on the array-like interface of Struct classes to
    # provide a backwards-compatible interface for the old CheckCode
    # representation of `['code', 'message']`. Any change to the order or
    # meaning of fields will need to be evaluated for how it affects the rest
    # of the codebase.

    class << self
      def Unknown(reason = nil, details: {})
        self.new('unknown', reason, details: details)
      end

      def Safe(reason = nil, details: {})
        self.new('safe', reason, details: details)
      end

      def Detected(reason = nil, details: {})
        self.new('detected', reason, details: details)
      end

      def Appears(reason = nil, details: {})
        self.new('appears', reason, details: details)
      end

      def Vulnerable(reason = nil, details: {})
        self.new('vulnerable', reason, details: details)
      end

      def Unsupported(reason = nil, details: {})
        self.new('unsupported', reason, details: details)
      end
    end

    # Deprecated, should use #===
    #
    # If you need to determine whether a CheckCode has the same code and
    # message as another one, {Struct#eql?} is the way to go.
    def ==(other)
      self === other
    end

    # Checks to see whether the other object is also a {CheckCode} and if so,
    # whether it shares the same code as this one.
    def ===(other)
      other.is_a?(self.class) && self.code == other.code
    end

    def initialize(code, reason, details: {})
      msg = case code
        when 'unknown';     'Cannot reliably check exploitability.'
        when 'safe';        'The target is not exploitable.'
        when 'detected';    'The service is running, but could not be validated.'
        when 'appears';     'The target appears to be vulnerable.'
        when 'vulnerable';  'The target is vulnerable.'
        when 'unsupported'; 'This module does not support check.'
        else
          ''
      end
      super(code, "#{msg} #{reason}".strip, reason, details)
    end

    #
    # 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     = self.Unknown()

    #
    # 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        = self.Safe()

    #
    # The target is running the service in question, but the check fails to determine whether
    # the target is vulnerable or not.
    #
    Detected    = self.Detected()

    #
    # 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     = self.Appears()

    #
    # 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  = self.Vulnerable()

    #
    # The module does not support the check method.
    #
    Unsupported = self.Unsupported()
  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


  #
  # Load all of the 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

  attr_accessor :needs_cleanup

  #
  # 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)

    if info.key? 'DefaultTarget'
      self.default_target = info['DefaultTarget']
    else
      self.default_target = 0
      # Add an auto-target to the exploit if it doesn't have one
      if info['Targets'] && info['Targets'].count > 1 && !has_auto_target?(info['Targets'])
        # 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}.merge(info['Targets'][self.default_target][1])]
          info['Targets'].unshift(auto)
        end
      end
    end

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

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

    # Initialize exploit datastore with target information
    import_target_defaults

    # 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 in seconds to wait for a session", 2 ])
        ], Msf::Exploit)
    end

    register_advanced_options(
      [
        # Allow all exploits to leverage context keyed encoding
        OptBool.new('EnableContextEncoding', [ false, "Use transient context when encoding payloads", false ]),
        OptPath.new('ContextInformationFile', [ false, "The information file that contains context information", nil ]),
        # Allow all exploits to disable their payload handlers
        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 exploit parameters. This method
  # is called during automated exploitation attempts and allows an
  # exploit to filter bad targets, obtain more information, and choose
  # better targets based on the available data. Returning anything that
  # evaluates to "false" will cause this specific exploit 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
    alert_user

    # 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

    # Defer starting bind handlers until after exploit completion
    return if handler_bind?

    # 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['DisableNops']     = payload_disable_nops(explicit_target)
    reqs['MaxNops']         = payload_max_nops(explicit_target)
    reqs['MinNops']         = payload_min_nops(explicit_target)
    reqs['Encoder']         = datastore['ENCODER'] || payload_encoder(explicit_target)
    reqs['Nop']             = datastore['NOP'] || payload_nop(explicit_target)
    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

  ##
  #
  # 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.include?(Stance::Aggressive)
  end

  #
  # Returns if the exploit has a passive stance. Aggressive exploits are always aggressive.
  #
  def passive?
    stance.include?(Stance::Passive) && !stance.include?(Stance::Aggressive)
  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 =
      begin
        Integer(datastore['TARGET'])
      rescue TypeError, ArgumentError
        datastore['TARGET']
      end

    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
    elsif target_idx.is_a?(String)
      target_idx = targets.index { |target| target.name == target_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 name [String] The payload name
  # @return [Boolean] True if the payload is compatible, False if it is not.
  #
  def is_payload_compatible?(name)
    p = framework.payloads[name]
    return false unless p

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

    begin
      pi = p.new
    rescue ::Exception, ::LoadError => e
      wlog("Module #{name} failed to initialize payload when checking exploit compatibility: #{e}", 'core', LEV_0)
      return false
    end

    # 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(excluded_platforms: [], excluded_archs: [])
    payloads = []

    c_platform, c_arch = normalize_platform_arch

    # The "All" platform name represents generic payloads
    results = Msf::Modules::Metadata::Cache.instance.find(
      'type'     => [['payload'], []],
      'platform' => [[*c_platform.names, 'All'], excluded_platforms],
      'arch'     => [c_arch, excluded_archs]
    )

    results.each do |res|
      if is_payload_compatible?(res.ref_name)
        payloads << [res.ref_name, framework.payloads[res.ref_name]]
      end
    end

    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 encoded instruction(s) required to adjust 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 && target.payload_stack_adjustment
      adj = target.payload_stack_adjustment
    else
      adj = payload_info['StackAdjustment']
    end

    return '' unless adj

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

    # Default to x86 if we can't find a list of architectures
    if arch && !arch.empty?
      arch = [arch].flatten.join(', ')
    else
      arch = 'x86'
    end

    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

  #
  # Whether NOP generation should be enabled or disabled
  #
  def payload_disable_nops(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_disable_nops)
      explicit_target.payload_disable_nops
    else
      payload_info['DisableNops']
    end
  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 that is associated with either the
  # current target or the exploit in general.
  #
  def payload_encoder(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_encoder)
      explicit_target.payload_encoder
    else
      payload_info['Encoder']
    end
  end

  #
  # Returns the payload NOP generator that is associated with either the
  # current target or the exploit in general.
  #
  def payload_nop(explicit_target = nil)
    explicit_target ||= target

    if (explicit_target and explicit_target.payload_nop)
      explicit_target.payload_nop
    else
      payload_info['Nop']
    end
  end

  #
  # Returns the payload encoder type that is associated with either the
  # current target or 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.
  #
  ##

  def rand_text_debug(length, char = 'A')
    char * (length.kind_of?(Range) ? length.first : length)
  end

  #
  # Generate random text characters avoiding the exploit's bad
  # characters.
  #
  def rand_text(length, bad=payload_badchars)
    if debugging?
      rand_text_debug(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?
      rand_text_debug(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?
      rand_text_debug(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?
      rand_text_debug(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?
      rand_text_debug(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?
      rand_text_debug(length, 'a')
    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?
      rand_text_debug(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?
      rand_text_debug(length, '0')
    else
      Rex::Text.rand_text_numeric(length, bad)
    end
  end

  #
  # Generate random hexadecimal characters avoiding the exploit's bad
  # characters.
  #
  def rand_text_hex(length, bad=payload_badchars)
    if debugging?
      rand_text_debug(length, '0')
    else
      Rex::Text.rand_text_hex(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 minimum "wait for session" delay is 3 seconds for all exploits, the
  # WfsDelay configuration option is added on top of this. The delay allows time
  # for the session handler to perform any session verification.
  #
  def wfs_delay
    (datastore['WfsDelay'] || 0).to_i + 3
  end

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

  #
  # If the payload uses a bind handler
  #
  def handler_bind?
    payload_instance && payload_instance.connection_type == 'bind'
  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?
    # Start bind handlers before checking session creation
    payload_instance.start_handler if handler_bind?

    (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::OptionValidateError
        self.fail_reason = Msf::Exploit::Failure::BadConfig
        ::Msf::Ui::Formatter::OptionValidateError.print_error(self, e)
        elog("Exploit failed (#{self.refname}): #{msg}", error: e)

      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}", error: e)

      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}", error: e)

      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}", error: e)

      when ::Interrupt
        self.fail_reason = Msf::Exploit::Failure::UserInterrupt
        self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
        elog("Exploit failed (#{self.refname}): #{msg}", error: e)
      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}", error: e)
    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

    return self.fail_reason
  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
