#!/usr/bin/perl -w

#Proxy MultiProtocolo
#sha0proxy.pl  v1.0 coded by sha0@badchecksum.net

#TODO: capturar SIGINT
#      ncurses para modificar los bytes directamente
#      udp
#      formato shellcode
#      logear
#      fuzz
#      quit


#perl -MCPAN -e shell
#cpan>install threads
#...
#cpan>install IO::Socket
#...
#cpan>install IO::Select
#...

use IO::Socket;
use IO::Select;
#use Net::UDP;
my %color=(
	red=>"\x1b[31;01m",
	green=>"\x1b[32;02m",
	yellow=>"\x1b[33;01m",
	blue=>"\x1b[34;01m",
	magenta=>"\x1b[35;01m",
	cyan=>"\x1b[36;01m",
	white=>"\x1b[37;00m"
);


die "$0 <lport> <rhost> <rport> <mode>\nmodes: view trap fuzz\ntrap sample: where>3A1 what>AAA\\x00 (enter for change nothing)\ntrap also can be used inline with extra params: <packet number> <where> <what>\nor: <replacements file>\nfuzz: not tested!\n" if (@ARGV!=4 && @ARGV !=5 && @ARGV!=7);

die "Valid modes are:  view, trap & fuzz\n" if ($ARGV[3] ne 'view' && $ARGV[3] ne 'trap' && $ARGV[3] ne 'fuzz');



#my $lport=(int(rand(500))+10000);
my $lport=$ARGV[0];
my $rport=$ARGV[2];
my $rhost=$ARGV[1];
my $buff;
my $vulnerable=0;
my $mode=$ARGV[3];
my @sended;

my $packet_num=1;
my @inline_num;
my @inline_where;
my @inline_what;

if (@ARGV == 7) {
	push @inline_num, $ARGV[4];
	push @inline_where, $ARGV[5];
	push @inline_what, $ARGV[6];
}

if (@ARGV == 5) {
	$file = $ARGV[4];
	print "loading $file\n";
	open F,"<$file";
	while (<F>) {
		chomp;
		next if (/^\#/);	
		if (/([0-9]{1,5})[ \t]([0-9a-f]{1,5})[ \t](.*)/i) {
			push @inline_num, $1;
			push @inline_where, $2;
			push @inline_what, $3;
		}
	}
}

sub doFuzz {
	print "START FUZZING!!!";
}

my $out;
my $in=IO::Socket::INET->new (
	LocalAddr=>'0.0.0.0',
	LocalPort=>$lport,
	Proto=>'tcp',
	Listen=>1,
	Reuse=>100
) or die "cannot open port $!\n";

print "listening $lport port\n";


#print	"\x1b[?25l"; #no cursor

while (my $welcome=$in->accept()) {
	$out=IO::Socket::INET->new (
		PeerAddr=>$rhost,
		PeerPort=>$rport,
		Timeout=>20
	) or die "cannot connect $!\n";

	print "connected to $rhost:$rport\n";


	#proxy
	if (!fork()) {
		$out->blocking(1);
		$welcome->blocking(1);
		$out->autoflush(1);
		$welcome->autoflush(1);

		$s=IO::Select->new($out, $welcome);
	proxy:
    		while(1) {
      			my @ready = $s->can_read;
			foreach my $ready (@ready) {
        			if($ready == $welcome) {
          				my $data;
	          			$welcome->recv($data, 8192);
					last proxy if (! length($data));
					last proxy if(!$out || !$out->connected);
					&muestra($data,1);
					push @sended, $data;
					$data=&changeData($data,1);
					$packet_num++;
	          			eval { $out->send($data); };
	          			last proxy if $@;
	        		} elsif ($ready == $out) {
	          			my $data;
				        $out->recv($data, 8192);
					last proxy if(!length($data));
				        last proxy if(!$welcome || !$welcome->connected);
					&muestra($data,0);
					$data=&changeData($data,0);
					$packet_num++;
				        eval { $welcome->send($data); };
				        last proxy if $@;
	        		}
			}#foreach
			
			if (!$welcome || !$out) {
				close $out;
				close $welcome;
				return;
			}
      		}#while 1
    	} #fork

}

sub changeData {
	my $data = $_[0];
	my $alserver = $_[1];
	my $changes=0;

	my $what='';
	my $where=0;

	for ($i=0; $i<= $#inline_num; $i++) {
		if ($inline_num[$i] == $packet_num) {
			$where = hex($inline_where[$i]);
			$what = $inline_what[$i];
			$data = &reemplaza($data,$alserver,$where,$what);
			$changes=1;
		}
	}

	if ($mode eq 'trap') {
		print "where>";	
		$where = <stdin>;
		chomp $where;
		$where = hex($where);
		print "what>";
		$what = <stdin>;
		chomp $what;
		$data = &reemplaza($data,$alserver,$where,$what);
		$changes = 1 if ($what);
	}
	
	&muestra($data,$alserver) if ($changes);
	return $data;
}

sub reemplaza () {
	my $d=0;
	my $cleanwtf;
	my ($data,$alserver,$where,$what) = @_;

	@databytes = split('',$data);
	@wtf = split('',$what);
	$cleanwtf='';
	for ($i = 0; $i < length($what); $i++) {
		#If user enter byte mode
		if ($wtf[$i] eq '\\' && $wtf[$i+1] eq 'x') {
			#print "byte".chr(hex($wtf[$i+2].$wtf[$i+3]))."\n";
			$cleanwtf.=chr(hex($wtf[$i+2].$wtf[$i+3]));
			$i+=3;
		} else {
			$cleanwtf.=$wtf[$i];
		}
	}

	@wtf = split('',$cleanwtf);
	
	for ($i = 0; $i < length($cleanwtf); $i++) {
		$databytes[$i+$where] = $wtf[$i];
	}
	$data = join('',@databytes);


	return $data;
}


sub muestra {
	my $data = $_[0];
	my @bytes = split(//,$data);
	my $b;
	my $alserver = $_[1];
	my $count=0;
	my $str="";
	my $lin=1;
	print $color{white};
	$banner=">"x33 if ($alserver);
	$banner="<"x33 if (!$alserver);
	printf "%d%s\n",$packet_num,$banner;
	print "\n   |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |";
	print "\n---+------------------------------------------------+---\n";

	print "000|";
	foreach $b (@bytes) {
		print $color{green}  if (($b ge 'a' && $b le 'z') ||($b ge 'A' && $b le 'Z') || $b eq "\x20"); 
		print $color{blue}   if ($b ge '0' && $b le '9');
		print $color{red}    if ($b eq "\x00");
		print $color{cyan}   if ($b eq "\x0a" || $b eq "\x0d");
		printf "%.2x ",ord($b);
		print $color{white};

		$b = "." if ($b lt "\x20" || $b gt "\x7e");
	
		$count++;
		$str.=$b;
		if ($count==16) {
			#$str=~s/[^a-z^A-Z^0-9^#^@^:^]/\./ig;
			printf "%s\n%.3x|",$str,$count*$lin;
			$lin++;
			$str="";
			$count = 0;
		}
	}
	$str=~s/[^a-z^A-Z^0-9^#^@]/\./ig;
	for ($b=$count;$b<16;$b++){
		print "   ";
	}
	print $str."\n";
	
	#print "\n"."-"x33;
	#$data=~s/[^a-z^A-Z^0-9]/\./ig;
	#print "\n$data\n";
}


