
##
# This file is part of the Metasploit Framework and may be redistributed
# according to the licenses defined in the Authors field below. In the
# case of an unknown or missing license, this file defaults to the same
# license as the core Framework (dual GPLv2 and Artistic). The latest
# version of the Framework can always be obtained from metasploit.com.
##

package Msf::Encoder::PexAlphaNum;
use strict;
use base 'Msf::Encoder';
use Pex::Encoder;

my $advanced = {
};

my $info = {
  'Name'    => 'Pex Alphanumeric Encoder',
  'Version' => '$Revision: 1.19 $',
  'Authors' => [ 'Berend-Jan Wever <skylined [at] edup.tudelft.nl>', ],
  'Arch'    => [ 'x86' ],
  'OS'      => [ ],
  'Description'  =>  "Skylined's alphanumeric encoder ported to perl",
  'Refs'    => [ ],
  'Keys'    => [ 'alphanum' ],
};

sub new {
  my $class = shift; 
  return($class->SUPER::new({'Info' => $info, 'Advanced' => $advanced}, @_));
}

#
# This code is a port of Skylined's awesome alpha encoder
#
sub EncodePayload {
  my $self = shift;
  my $rawshell = shift;
  my $badChars = shift;
  
  my $type = $self->GetVar('GETPCTYPE');
  if (! $type && $self->GetVar('_Payload') && grep {/win32/} @{ $self->GetVar('_Payload')->OS})
  {
    $type = 'win32';
  }

  # Begin hd-written foo

    my $prepend = "";
    
    if (! $type)
    {
        # the prepend chunks leave ecx=end of code
        $type    = 'ecx';
        
        # use a somewhat sane small prepend first
        $prepend = "\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff";
        
        # if it doesnt work, use this behemoth with minimized chars
        if (Pex::Text::CharsInBuffer($prepend, $badChars))
        {
            # unique chars: 59 EB E8 A4 FF
            $prepend = 
            "\xeb\x59\x59\x59\x59\xeb\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59".
            "\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59".
            "\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59".
            "\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59".
            "\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59".
            "\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\x59\xe8\xa4\xff\xff\xff";
        }
    }

    # the decoder in all its glory (hardcoded for 9 byte baseaddr)
    my $decoder = 'VTX630VX4A0B6HH0B30BCVX2BDBH4A2AD0ADTBDQB0ADAVX4Z8BDJOM';
    my $encoded;
    
    my $allowed = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXY';
    
    # first check to see if the encoder/alphabet is allowed 
    if ( Pex::Text::CharsInBuffer($allowed.$decoder.'Z', $badChars) )
    {
        $self->PrintDebugLine(3, 'Encoder failed: restricted character in decoder or alphabet');
        return;
    }
    
    # the different places where our baseaddr is stored by getpc
    my %baseaddr;
    $baseaddr{'eax'}    = 'PZJJJJJRY';
    $baseaddr{'ebx'}    = 'SZJJJJJRY';
    $baseaddr{'ecx'}    = 'OIIIIIIQZ';
    $baseaddr{'edx'}    = 'OJJJJJJRY';
    $baseaddr{'esp'}    = 'TZJJJJJRY';
    $baseaddr{'ebp'}    = 'UZJJJJJRY';
    $baseaddr{'esi'}    = 'VZJJJJJRY';
    $baseaddr{'edi'}    = 'WZJJJJJRY';
    $baseaddr{'win32'}  = $baseaddr{'ecx'};

    if (! exists($baseaddr{$type}))
    {
        $self->PrintDebugLine(3, "Encoder failed: invalid type specified ($type)");
        return;
    }

    my $win32getpc = 'VTX630VXH49HHHPhYAAQhZYYYYAAQQDDDd36FFFFTXVj0PPTUPPa301089';
    
    if ($type eq 'win32' && ! Pex::Text::CharsInBuffer($baseaddr{'win32'}.$win32getpc, $badChars))
    {
        $encoded = $win32getpc . $baseaddr{'win32'} . $decoder;
    } 
    else 
    {
        $encoded = $baseaddr{$type} . $decoder;
    }

    my @alphanum = split(//, $allowed);
    my (@lonibs, @hinibs);

    foreach my $x (0 .. 255)  {
    foreach my $y (@alphanum) {
        $lonibs[$x] = (($x & 0x0f) ^ 0x41) + 1;

        if (($x & 0xf0) >> 4 == (ord($y) & 0x0f))
        {
            push @{$hinibs[$x]}, $y;
        }
    } }


    foreach (split(//, $rawshell))
    {
        my $nibL = chr($lonibs[ord($_)]);
        my $nibH = @{$hinibs[ord($_)]}[ rand @{$hinibs[ord($_)]} ];
        $encoded .= $nibL . $nibH;
    }
    $encoded .= "Z";
    return($prepend.$encoded);
}

1;
