#!/usr/bin/perl
###############

##
#         Name: msfconsole
#       Author: H D Moore <hdm [at] metasploit.com>
#       Author: spoonm <ninjatools [at] hush.com>
#  Description: Console shell interface to the Metasploit Exploit Framework
#      Version: $Revision: 1.91 $
#      License:
#
#      This file is part of the Metasploit Exploit Framework
#      and is subject to the same licenses and copyrights as
#      the rest of this package.
#
##

require 5.6.0;

use strict;
use FindBin qw{$RealBin};
use lib "$RealBin/lib";
use vars qw($VERSION);
use IO::Socket;
use POSIX;
use Getopt::Std;

use Pex::PsuedoShell;
use Msf::TextUI;
use Pex;

no utf8;
no locale;

Msf::UI::ActiveStateSucks();
Msf::UI::BrokenUTF8();

my $ui = Msf::TextUI->new($RealBin);
my $FRAMEVERSION = $ui->Version;
my $VERSION = '$Revision: 1.91 $';

$SIG{'CHLD'} = sub { while (waitpid(-1, WNOHANG) == 0) { } };

my $exploitsIndex;
my $payloadsIndex;
my $encodersIndex;
my $nopsIndex;

my $encoders;
my $nops;
my $exploits;
my $payloads;

my %opts;
getopts('hvqs:', \%opts);

Usage() if($opts{'h'});
Version() if($opts{'v'});

my $state = {'Mode' => 'Main'};

# For many reasons (modules setup stuff on new), internal variables
# keeping state between exploit attempts, etc, we want new instances
# of everything, so we will call load before anything that uses a
# module, and we will let the Load functions do the module instantiation
# this is also good if they add/remove a module the directories 
Load();

my $console = Pex::PsuedoShell->new('Metasploit Console', 'msf > ', 0);
$console->tabCompletion(\&xTabCompletion);

# virtual command table
my %virtualCmds = ();

# I know the sub thing is ugly, but I did it to make clear what functions
# are calling Load(), because this could bring up some very hard to find bugs
# and cause some really bizarre issues depending on all sorts of factors
# **** Make sure to call Load if the function uses any module (exploit, payload, etc) *****

# global command tables
my %globalCmds =
(
    'version'   => [\&gVersion,              "Show console version"],
    'help'      => [\&gHelp,                 "Show the main console help"],
    '?'         => [\&gHelp,                 "Show the main console help"],
    'quit'      => [\&gExit,                 "Exit the console"],
    'exit'      => [\&gExit,                 "Exit the console"],
    'use'       => [sub{gUse(@_);},  "Select an exploit by name"],
    'info'      => [sub{gInfo(@_);}, "Display detailed exploit or payload information"],
    'cd'        => [\&gChdir,                "Change working directory"],
    'save'      => [\&gSave,                 "Save configuration to disk"],
    'setg'      => [\&gSet,                  "Set a global environment variable"],
    'unsetg'    => [\&gUnset,                "Remove a global environment variable"],
    'reload'    => [sub{Load();},            "Reload exploits and payloads"],
);


# main mode commands
my %mainCmds =
(
    'show'      => [sub{Load(); mShow(@_);},   "Show available exploits and payloads"],
);

# exploit mode commands
my %exploitCmds = 
(
    'set'       => [\&eSet,                         "Set a temporary environment variable"],
    'unset'     => [\&eUnset,                       "Remove a temporary environment variable"],
    'back'      => [\&eBack,                        "Drop back to the main menu"],
    'show'      => [sub{ eShow(@_);},        "Show options, advanced, payloads, or targets"],
    'check'     => [sub{eCheck(@_);},       "Perform vulnerability check"],
    'exploit'   => [sub {eExploit(@_);},     "Launch the actual exploit"],
    'rexploit'  => [sub{Load(); eExploit(@_);},     "Reload and exploit, for us tester types"],
);


$state->{'LocalAddress'} = Pex::Utils::SourceIP();
xAddAddressCache($state->{'LocalAddress'});

if (! defined($opts{'q'})) {
    Msf::TextUI::PrintAsciiLogo();

    # print initialization message
    printf("\n+ -- --=[ msfconsole v%s [%d exploits - %d payloads]\n\n", 
           $FRAMEVERSION, 
           scalar(keys(%{$exploits})),
           scalar(keys(%{$payloads}))
    );
}

ProcessCmd('use', shift(@ARGV)) if(@ARGV);
ProcessScript($opts{'s'}) if(exists($opts{'s'}));

while (1)
{
    SetupCmds();
    last if(!ProcessCmd($console->readCommand()));
}
print "\n";

sub SetupCmds {
  %virtualCmds = %globalCmds;
  
  my $stateMode = $state->{'Mode'};
    
  if ($stateMode eq 'Main')    { foreach (keys(%mainCmds)){ $virtualCmds{$_} = $mainCmds{$_} } }
  if ($stateMode eq 'Exploit') { foreach (keys(%exploitCmds)){ $virtualCmds{$_} = $exploitCmds{$_} } }
}

sub ProcessCmd {
  my $cmd = shift;
  my @args = @_;

  SetupCmds();

  return(0) if(!defined($cmd));

  if(exists($virtualCmds{$cmd})) {
    $virtualCmds{$cmd}->[0]($cmd, @args);
  }
  else {
    gUnknown($cmd, @args);
  }
  return(1);
}

sub ProcessScript {
  my $file = shift;
  open(INFILE, "<$file") or die("Cannot open script: $file: $!\n");
  local $/;
  my $data = <INFILE>;
  close(INFILE);
  foreach my $line (split("\n", $data)) {
    ProcessCmd(Pex::PsuedoShell->parseCommands($line));
  }
}

sub Load {

    $exploitsIndex = $ui->LoadExploits;
    $payloadsIndex = $ui->LoadPayloads;
    $encodersIndex = $ui->LoadEncoders;
    $nopsIndex     = $ui->LoadNops;
    $exploits = { };
    $payloads = { };
    $encoders = { };
    $nops     = { };
    
    foreach my $key (sort(keys(%{$exploitsIndex}))) {
        $exploits->{$exploitsIndex->{$key}->SelfEndName} = $exploitsIndex->{$key};
    }
    
    foreach my $key (sort(keys(%{$encodersIndex}))) {
        $encoders->{$encodersIndex->{$key}->SelfEndName} = $encodersIndex->{$key};
    } 
       
    foreach my $key (sort(keys(%{$nopsIndex}))) {
        $nops->{$nopsIndex->{$key}->SelfEndName} = $nopsIndex->{$key};
    }    
    
    foreach my $key (keys(%{$payloadsIndex})) {
        $payloads->{$payloadsIndex->{$key}->SelfEndName} = $payloadsIndex->{$key};
    }


    # Important, reload the exploit object in state
    if($state->{'Mode'} eq 'Exploit') {
      my $exploit = $exploits->{$state->{'Exploit'}->{'Name'}};

      # check to make sure the module reloaded, it could have had errors..
      if(!$exploit) {
        xMsg('reload', 'Error reloading current exploit, moving back to Main.');
        eBack();
      }
      # ok, call gUse to reload tab completetion, etc
      else {
        gUse('use', $state->{'Exploit'}->{'Name'});
      }
    }
}

sub gSave {
    my $cmd = shift;
    $ui->SaveTempEnv($state->{'Exploit'}->{'Name'}) if($state->{'Mode'} eq 'Exploit');
    $ui->SaveConfig;
    print "Saved configuration to: " . $ui->ConfigFile . "\n";
}

sub gVersion { print "msfconsole version $VERSION\n" }

sub gSet {
    my $cmd = shift;
    if(@_ == 1) {
        print "$_[0]: " . $ui->GetGlobalEnv($_[0]) . "\n";
    }
    elsif(@_ == 2) {
        print "$_[0] -> $_[1]\n";
        $ui->SetGlobalEnv($_[0], $_[1]);
    }
    else {
        foreach (sort(keys(%{$ui->GetGlobalEnv}))) {
            print "$_: " . $ui->GetGlobalEnv($_) . "\n";
        }
    }
}

sub gUnset
{
    my ($cmd, $key) = @_;

    if(!defined($key))
    {
        my $ok = xAskYN('Clear env? [yes/no]: ');
		return if $ok ne 'yes';
        $ui->UnsetGlobalEnv;
    }
    $ui->UnsetGlobalEnv($key);
}

sub eSet {
    my $cmd = shift;
    if(@_ == 1) {
        print "$_[0]: " . $ui->GetTempEnv($_[0]) . "\n";
    }
    elsif(@_ == 2) {
        print "$_[0] -> $_[1]\n";
        $ui->SetTempEnv($_[0], $_[1]);
    }
    else {
        foreach (sort(keys(%{$ui->GetTempEnv}))) {
            print "$_: " . $ui->GetTempEnv($_) . "\n";
        }
    }
    
    my $prompt = 'msf '.$state->{'Exploit'}->{'Name'};
    if ($ui->GetEnv('PAYLOAD')) {
        $prompt .= '('.$ui->GetEnv('PAYLOAD').')';
    }
    $prompt .= ' > ';
    $console->_prompt($prompt);
}

sub eUnset
{
    my ($cmd, $key) = @_;

    if(!defined($key))
    {
        my $ok = xAskYN('Clear temporary env? [yes/no]: ');
		return if $ok ne 'yes';
        $ui->UnsetTempEnv;
    }
    $ui->UnsetTempEnv($key);
    
    my $prompt = 'msf '.$state->{'Exploit'}->{'Name'};
    if ($ui->GetEnv('PAYLOAD')) {
        $prompt .= '('.$ui->GetEnv('PAYLOAD').')';
    }
    $prompt .= ' > ';
    $console->_prompt($prompt);
}


sub gExit
{
    POSIX::_exit(0) if($ui->GetEnv('AlternateExit') == 1);
    if($ui->GetEnv('AlternateExit') == 2) {
      exec('true');
    }
    exit(0);
}

sub gUnknown
{
    my ($cmd, @args) = @_;
    
    if (! xCheckSystemCommand($cmd)) 
    {
        xMsg($cmd, "command not found");
    } else {
        system("$cmd ". join(" ", @args));
    }
}

sub gHelp
{
    my ($cmd, @args) = @_;
    
    my $col = Msf::ColPrint->new(8, 6);
    print "\nMetasploit Framework " . $state->{'Mode'}  . " Console Help\n";
    print   "======================================\n\n";
    foreach my $cmd (sort(keys(%virtualCmds)))
    {
        $col->AddRow($cmd, $virtualCmds{$cmd}->[1]);
    }
    print $col->GetOutput . "\n";
}

sub gUse 
{
    my ($cmd, @args) = @_;
    if (! exists($exploits->{$args[0]}))
    {
        xMsg("use", "please specify a valid exploit name");
        return;
    }
    
    my $exploit = $exploits->{$args[0]};
    
    # switch to exploit mode
    $state->{'Mode'} = 'Exploit';
    
    # wipe out any previous exploit state
    delete($state->{'Exploit'});
    
    $state->{'Exploit'}->{'Exploit'} = $exploit;
    $state->{'Exploit'}->{'Name'} = $args[0];
    $state->{'Exploit'}->{'Payloads'} = xValidPayloads($exploit);

    $ui->LoadTempEnv($args[0]);

    if(defined($exploit->UseMessage)) {
      print $exploit->UseMessage . "\n";
    }
    
    my $prompt = 'msf '.$state->{'Exploit'}->{'Name'};
    if ($ui->GetEnv('PAYLOAD'))
    {
        $prompt .= '('.$ui->GetEnv('PAYLOAD').')';
    }
    $prompt .= ' > ';
    $console->_prompt($prompt);
}

sub eBack
{
    $ui->SaveTempEnv($state->{'Exploit'}->{'Name'});
    $ui->UnsetTempEnv;
    $state->{'Mode'} = 'Main';
    $console->_prompt("msf > ");
}

sub gChdir
{
    my ($cmd, @args) = @_;
    
    if (! $args[0])
    {
        chdir($ENV{'HOME'});
        return;
    }
    
    if (chdir($args[0]))
    {
        xMsg("chdir", "changed to directory $args[0]");
    } else {
        xMsg("chdir", "failed to change directory $!");
    }
}

sub gInfo {
  my $cmd = shift;
  my @args = @_;
  my $module;
  my $type;

  # Support old info exploit/payload syntax
  if(@args == 2) {
    $module = $args[1];
    $type = $args[0];
  }
  elsif(@args == 1) {
    $module = $args[0];
  }
    
  if(!defined($module) || ($type ? $type !~ /^(encoder|exploit|payload|nop)$/ : 0)) {
    xMsg("info", "usage: info [type] <module name>");
    return;
  }

  if($exploits->{$module} && ($type ? $type eq 'exploit' : 1)) {
    print "\n" . $ui->DumpExploitSummary($exploits->{$module});
  }
  elsif($payloads->{$module} && ($type ? $type eq 'payload' : 1)) {
    print "\n" . $ui->DumpPayloadSummary($payloads->{$module});
  }

  # Kinda a hack, we rebuild the keys for exploits and payloads, but
  # not for nops or encoders...
  elsif($encoders->{$module} && ($type ? $type eq 'encoder' : 1)) {
    print "\n" . $ui->DumpEncoderSummary($encoders->{$module});
  }
  elsif($nops->{$module} && ($type ? $type eq 'nop' : 1)) {
    print "\n" . $ui->DumpNopSummary($nops->{$module});
  }
  
  else {
    xMsg("info", "invalid module name");
  }
}

sub mShow 
{
    my ($cmd, @args) = @_;
    my $c = $state->{'CONF'};
    
    if (lc($args[0]) eq "exploits")
    {
        print "\nMetasploit Framework Loaded Exploits\n";
        print   "====================================\n\n";
        
        print $ui->DumpExploits(2, $exploits) . "\n";
        return;
    }
    
    if (lc($args[0]) eq "payloads")
    {
        print "\nMetasploit Framework Loaded Payloads\n";
        print   "====================================\n\n";
        
        print $ui->DumpPayloads(2, $payloads) . "\n";
        return;
    }

    if (lc($args[0]) eq "encoders")
    {
        print "\nMetasploit Framework Loaded Encoders\n";
        print   "====================================\n\n";
        
        print $ui->DumpEncoders(2, $encoders) . "\n";
        return;
    }

    if (lc($args[0]) eq "nops")
    {
        print "\nMetasploit Framework Loaded Nop Engines\n";
        print   "=======================================\n\n";
        
        print $ui->DumpNops(2, $nops) . "\n";
        return;
    }

    if (lc($args[0]) eq "config")
    {
        print "\nMetasploit Framework Configuration\n";
        print   "====================================\n\n";
        
        foreach my $v (sort(keys(%{$c}))) {
            print "  $v" . (" " x (30-length($v))) . $c->{$v} ."\n";
        }
        print "\n";
        return;
    }

    xMsg("show", "requires an option: 'exploits', 'payloads', 'encoders', or 'nops'");
}


sub eShow 
{
    my ($cmd, @args) = @_;
    if (lc($args[0]) eq 'options')  { eOptions();  return }
    if (lc($args[0]) eq 'advanced') { eAdvanced(); return }
    if (lc($args[0]) eq 'targets')  { eTargets();  return }
    if (lc($args[0]) eq 'payloads') { ePayloads(); return }

    xMsg("show", "specify 'targets', 'payloads', 'options', or 'advanced'");

}

sub ePayloads {
  SaveTemp();
  FillTemp();
  $ui->Payloads;
  RestoreTemp();
}
sub eOptions {
  SaveTemp();
  FillTemp();
  $ui->Options;
  RestoreTemp();
}
sub eAdvanced {
  SaveTemp();
  FillTemp();
  $ui->AdvancedOptions;
  RestoreTemp();
}
sub eTargets  {
  SaveTemp();
  FillTemp();
  $ui->Targets;
  RestoreTemp();
}

sub eCheck {
  xUpdateAddrCache();
  SaveTemp();
  FillTemp();
  $ui->Check;
  RestoreTemp();
}

sub eExploit {
  # if reload failed, and bumped us out to main...
  return if($state->{'Mode'} ne 'Exploit');
  xUpdateAddrCache();
  SaveTemp();
  FillTemp();
  $ui->Exploit;
  RestoreTemp();
}


sub xMsg
{
    my ($loc, $msg) = @_;
    print STDERR "msfconsole: $loc: $msg\n";
}

sub xValidPayloads
{
    my $exploit = shift;
    if($exploit->Payload) {
      $state->{'Exploit'}->{'Payloads'} = $ui->MatchPayloads($exploit, $payloads);
      return $state->{'Exploit'}->{'Payloads'};
    }
    return;
}

sub xCheckSystemCommand
{
    my $cmd = shift;
    return(1) if -e $cmd;
    foreach my $d (split(/:/, $ENV{'PATH'})) { 
    
        if ($^O eq 'cygwin') {
            return(1) if -e "$d/$cmd";
        } else {
            return(1) if -x "$d/$cmd";
        }
    }
    return(0);
}

sub xGetAddressCache
{
    my $cache = $state->{'CacheAddress'};
    return keys(%{$cache});
}

sub xAddAddressCache
{
    my $addr = shift;
    $state->{'CacheAddress'}->{$addr}++
}

sub xUpdateAddrCache 
{
    my $x = $state->{'Exploit'}->{'Exploit'};
    my $p = $ui->GetEnv('PAYLOAD');
    my %options = ();

    
    # create a list of all exploit options of type ADDR
    foreach (keys(%{$x->UserOpts})) {
        next if $x->UserOpts->{$_}->[1] ne 'ADDR';
        $options{$_}++;
    }
    foreach (keys(%{$x->Advanced})) {
        next if $x->Advanced->{$_}->[1] ne 'ADDR';
        $options{$_}++;
    }  

    # create a list of all payload options of type ADDR
    if ($x->Payload && $p && exists($payloads->{$p}))
    {
        $p = $payloads->{$p};
	$p->_Load;
        foreach (keys(%{$p->UserOpts})) {
            next if $p->UserOpts->{$_}->[1] ne 'ADDR';
            $options{$_}++;
        }        
        foreach (keys(%{$p->Advanced})) {
            next if $p->Advanced->{$_}->[1] ne 'ADDR';
            $options{$_}++;
        }
    }
    
    # scan environments and add to the cache
    foreach (keys(%options)) {
        my $value = $ui->GetTempEnv($_) || $ui->GetEnv($_) || undef;
        next if ! $value;
        $state->{'CacheAddress'}->{$value}++;
    }
}

sub xAskYN {
	my $quest	= shift;
	my $cinp	= IO::Handle->new_from_fd(0, '<');
	my $cout	= IO::Handle->new_from_fd(1, '>');
	my $iblock	= $cinp->blocking;
	
	$cinp->blocking(1);

	my $res = 'run';
	while ($res && $res ne 'yes' && $res ne 'no') {
		$cout->printflush($quest);
		if ( defined($res = $cinp->getline)) {
			chomp($res);
			$res = lc($res);
		} 
		else { $cout->printflush("\nError reading from input: $!\n"); }
	}
	$cinp->blocking($iblock);
	return $res;
}


#
# TAB COMPLETION ROUTINES
#

sub xCreateSetList
{
    if ($state->{"Mode"} eq "Main") { return keys(%{$ui->GetGlobalEnv}) } 
    
    if ($state->{"Mode"} eq "Exploit") 
    {
        my %options = ();
        
        my $x = $state->{'Exploit'}->{'Exploit'};
        my $p = $ui->GetEnv('PAYLOAD');
        
        foreach (keys(%{$x->UserOpts})) { $options{$_}++ }
        foreach (keys(%{$x->Advanced})) { $options{$_}++ }
        
        if ($x->Payload && $p && exists($payloads->{$p}))
        {
            $p = $payloads->{$p};
	    $p->_Load;
            foreach (keys(%{$p->UserOpts})) { $options{$_}++ }
            foreach (keys(%{$p->Advanced})) { $options{$_}++ }
        }
         
        if ($x->Payload) { $options{"PAYLOAD"}++ }
        if ($x->TargetsList) { $options{"TARGET"}++  }

        foreach my $e (keys(%{ $ui->GetEnv })) {
            $options{$e}++;
        }
        
        foreach my $e (keys(%{ $ui->GetTempEnv })) {
            $options{$e}++;
        }   

        return(keys(%options));
    }
}

sub xCreateSetValueList
{
    if ($state->{"Mode"} eq "Main") { return ' ' } 
    
    if ($state->{"Mode"} eq "Exploit") 
    {
        my %results = ();
        
        my $n = $state->{'SetName'};
        my $x = $state->{'Exploit'}->{'Exploit'};
        my $v = $state->{'Exploit'}->{'Payloads'};
        my $p = $ui->GetEnv('PAYLOAD');

        if ($x->Payload && $p && exists($payloads->{$p})) { $p = $payloads->{$p} }
        
        if (uc($n) eq "PAYLOAD") { return keys(%{$v}) }
        
        if (uc($n) eq "TARGET")
        {
            my $tidx = 0;
            foreach ($x->TargetsList) { $results{$tidx}++ ; $tidx++;}
            return keys(%results);
        }
        
        my ($req, $type, $desc, $dflt);
        
        if (exists($x->UserOpts->{$n})) {
            ($req, $type, $desc, $dflt) = @{$x->UserOpts->{$n}};  
        }
        
        if (exists($x->Advanced->{$n})) {
            ($req, $type) = (0, 'DATA');
            ($dflt, $desc) = @{$x->Advanced->{$n}};
        }
                
        if ($p && exists($p->UserOpts->{$n})) {
            ($req, $type, $desc, $dflt) = @{$p->UserOpts->{$n}};  
        }
                
        if ($p && exists($p->Advanced->{$n})) {
            ($req, $type) = (0, 'DATA');
            ($dflt, $desc) = @{$p->Advanced->{$n}};
        }
        
        if ($dflt) { return ($dflt) }
        
        if ($type eq "ADDR") { return xGetAddressCache() }
        if ($type eq "BOOL") { return ("TRUE", "FALSE") }
        if ($type eq "FILE") { return undef }
        if ($type eq "PATH") { return undef }
        return ' ';
    }
}

sub xTabCompletion
{
    my ($text, $line, $start, $end) = @_;
    my ($cmd, @args) = split(/\s+/, $line);
    
    # default to match of space
    $state->{'TabVals'} = [' '];
    
    # this handles command matching
    if ($start == 0) { $state->{'TabVals'} = [sort(keys(%virtualCmds))] }
    
    if (lc($cmd) eq "use") { $state->{'TabVals'} = [sort(keys(%{$exploits}))] }
    
    if (lc($cmd) eq "show")
    {
        if ($state->{'Mode'} eq 'Main') { $state->{'TabVals'} = ['exploits', 'payloads', 'encoders', 'nops'] }
        if ($state->{'Mode'} eq 'Exploit') { $state->{'TabVals'} = ['advanced', 'options', 'targets', 'payloads'] }
    }
    
    if (lc($cmd) eq "info")
    {
        # display variables if no args are specified
        if (! $args[0] || (! $args[1] && ($args[0] && $text))) { 
            
            my %allmods;
            for (keys(%{$exploits}), keys(%{$payloads}), keys(%{$encoders}), keys(%{$nops})) {
                $allmods{$_}++;
            }
            
            $state->{'TabVals'} = ['exploit', 'payload', 'encoder', 'nop', sort(keys(%allmods))] 
        }
        if (! $args[1] || (! $args[2] && ($args[1] && $text)))
        {
            if ($args[0] eq "exploit") { $state->{'TabVals'} = [sort(keys(%{$exploits}))] }
            if ($args[0] eq "payload") { $state->{'TabVals'} = [sort(keys(%{$payloads}))] }
            if ($args[0] eq "encoder") { $state->{'TabVals'} = [sort(keys(%{$encoders}))] }
            if ($args[0] eq "nop"    ) { $state->{'TabVals'} = [sort(keys(%{$nops}))] }            
        }
    }
        
    if (
        ($state->{'Mode'} eq "Main"    && lc($cmd) =~ /^(un|)setg$/) ||
        ($state->{'Mode'} eq "Exploit" && lc($cmd) =~ /^(un|)set$/)
       )
    {
        # display variables if no args are specified
        if (! $args[0] || (! $args[1] && ($args[0] && $text)))
        {
            $state->{'TabVals'} = [sort(xCreateSetList())];
        } elsif (! $args[1] || (! $args[2] && ($args[1] && $text)))
        {
            $state->{'SetName'} = $args[0];
            $state->{'TabVals'} = [sort(xCreateSetValueList())];
        }
    }
    
    # revert to file completion for non-commands
    if (! scalar(@{$state->{'TabVals'}}) && ! exists($virtualCmds{$cmd})) {
        $state->{'TabVals'} = []; 
    }
    
    

    my @matches = $console->_term->completion_matches($text, \&xTabCompletionMatcher);
    return(@matches); 
}


# This is a localized closure for matching speed, this routine has been borrowed
# from http://lists.n0i.net/pipermail/perl/2003-October/000015.html
{
    my $list_index;
    my @name;
    
    sub xTabCompletionMatcher
    {
        my ($text, $mstate) = @_;
        $text = quotemeta($text);

        # If this is a new word to complete, initialize now.  This
        # includes saving the length of TEXT for efficiency, and
        # initializing the index variable to 0.

        unless ($mstate) {
	        $list_index = 0;
            @name = @{$state->{'TabVals'}};
            return undef if (scalar(@name) == 0);
        }

        # Return the next name which partially matches from the
        # command list.
        while ($list_index <= $#name) {
	        $list_index++;
	        return $name[$list_index - 1]
                if ($name[$list_index - 1] =~ /^$text/);
        }

        # If no names matched, then return NULL.
        return undef;
    }
}

sub SaveTemp {
  $ui->SaveTempEnv('_Save');
}

sub RestoreTemp {
  $ui->LoadTempEnv('_Save');
  $ui->DeleteTempEnv('_Save');
}

sub FillTemp {
  $ui->SetTempEnv('_ExploitsIndex', $exploitsIndex);
  $ui->SetTempEnv('_PayloadsIndex', $payloadsIndex);
  $ui->SetTempEnv('_Encoders', $encodersIndex);
  $ui->SetTempEnv('_Nops', $nopsIndex);

  $ui->SetTempEnv('_Exploits', $exploits);
  $ui->SetTempEnv('_Payloads', $payloads);

  my $exploit = $state->{'Exploit'}->{'Exploit'};
  $ui->SetTempEnv('_Exploit', $exploit);
  
  $ui->SetTempEnv('_UI', $ui);

  # XXX added by spoon, maybe this should be looked at...
  # applying the AutoOpts settings to the local temp env we have setup,
  # specifically this fixes things like the EXITFUNC display issues
  # this will later get called again in Exploit::Prepare, maybe we should
  # look into just having it done once....
  $exploit->ApplyAutoOpts;

  my $payloadName = $ui->GetEnv('PAYLOAD');
  $ui->SetTempEnv('_PayloadName', $payloadName);
  my $validPayloads = $ui->MatchPayloads($exploit, $payloads) if($exploit->Payload);
  my $payload = $validPayloads->{$payloadName};
  # make sure the OO stuff is good for whoever might use this...
  $payload->_Load if($payload);
  $ui->SetTempEnv('_Payload', $payload);
  $ui->SetTempEnv('_ValidPayloads', $validPayloads);
}

sub Usage {
    print STDERR qq{
  Usage: $0 <options> <exploit>
Options:
         -h             You're looking at me baby
         -v             List version information
         -s   <file>    Process file of console commands
         -q             No splash screen on startup

};
    exit(0);
}

sub Version {
    my $ver = Pex::Utils::Rev2Ver($VERSION);
    print STDERR qq{
   Framework Version:  $FRAMEVERSION
  Msfconsole Version:  $ver

};

  exit(0);
}
