Appendix A. Source code



#!D:\dev\perl\bin\perl.exe 
# ComLog 1.0

#########################################################################################
# ComLog 1.0										#
# by Floydman  floydian_99@yahoo.com							#
# Copyright 2002 SecurIT Informatique Inc.  http://securit.iquebec.com			#
#											#
# This program captures the input/output of the Windows NT Command Promt (cmd.exe).	#
# It does so by prentending to be the real prompt and forwarding the commands to the 	#
# real (and renamed) cmd.exe.  I/O is stored in a random-generated log file in 		#
# \WINNT\Help\Tutor.
#########################################################################################

#########################################################################################
# LICENSE										#
# This software is Open Source.  This means that its source code is open, free and avai-#
# lable for anyone to look into, make modifications, correct bugs (let me know, please) #
# and use for personal and commercial use.  You can create your own binaries with 	#
# perl2exe (www.indigostar.com), or download the install package from  			#
# http://iquebec.ifrance.com/securit/download.html. 					#
#########################################################################################

#########################################################################################
# Main Program										#
# This is the main structure of ComLog.							#
# This programs is a loop that will be passed only once if arguments are passed with 	#
# ComLog, since it means it was not launched for interactive use.  Else, the loop will	#
# go on until the user types 'exit' or breaks otherwhise the program execution (CTRL-C).#
# The program then presents the prompt to the user, and gets the command typed at the	#
# keyboard.  Change of drives or directories are checked for, since ComLog have to	#
# provide these parameters to the real command prompt.  Everything is logged in a	#
# random generated filename to allow for multiple instances.  Output is sanitized to 	#
# hide ComLog's presence.								#
#########################################################################################


use Win32;

# Initialization of entrant parameters, current dir, command, exit and currentdrive
$input = join (' ',@ARGV);
$index = 0;
$currentdir[$index] = '.\\';
$command = '';
$exit = 1;
$currentdrive[$index] = getcurrentdrive ($currentdir[$index]);
$nothing = 1;	# nop
$winnt = "Windows NT Version 4.0";
$wintwok = "Microsoft Windows 2000";
$cleanlog = 0;  # Set to 0 to keep the local log files, set to 1 to delete them after the session is done

$history = randomfilename();

open (WINVER, "cm_.exe /C ver |") or die "Can't open pipe";
@header = ;
close (WINVER);

$header = join('', @header);

if ($header=~m/$winnt/) {sendoutput ("Microsoft(R) Windows NT(TM)\n(C) Copyright 1985-1996 Microsoft Corp.\n");}
if ($header=~m/$wintwok/) {sendoutput ("Microsoft Windows 2000 [Version 5.00.2195]\n(C) Copyright 1985-1999 Microsoft Corp.\n");}

# Get the path for the fake prompt
getprompt($currentdrive[$index], $currentdir[$index]);

# If no args are passed, we cancel the $exit marker and launch the prompt
if ($input eq '') {$input = ; $exit = 0;}

# Enters in an "interactive" session and will terminate on 'exit', the loop will run only once if $exit is set to 1
while ($input ne "exit\n")
{
chomp $input;
$cdcommand = 0; $baddir = 0; $baddrive = 0;

SWITCH: {
	# If the command starts with 'cd', then it checks if the next character is \
	# If it is, then the string replaces the $currentdir.  If it does not start with 
	# a \, then the string is appended at the end of the path in $currentdir
	# If nothing follows the cd command, then the command is put back in $input
	# since we modified the string for analysis with the shift command above
        $input=~m/^cd/i      && do {
					@input = split (//,$input);
					if ($input[0] eq "c" or $input[0] eq "C") {shift @input;}
					if ($input[0] eq "d" or $input[0] eq "D") {shift @input;}
					while ($input[0] eq " ") {shift @input;}

					if (@input)
					{
						if ($input[0] eq "\\")
						{ 
						  $input = join ('',@input);
						  $direxist = checkdir($currentdrive[$index], "\\", $input);
						  if ($direxist)
						  {
						  $currentdir[$index] = $input;
						  $cdcommand = 1;
						  $input = "cd ".$input;
						  }
						  else {$cdcommand = 1; $baddir = 1; $input="cd";}
						}
						else
						{
						  $input = join ('',@input);
						  $direxist = checkdir($currentdrive[$index],$currentdir[$index], $input);
						  if ($direxist)
						  {  
						    $currentdir[$index] = $currentdir[$index].$input.'\\';
						    $cdcommand = 1;
						    $input = "cd ".$input;
						  }
						  else 
						  { $cdcommand = 1; $baddir = 1; $input="cd"; }
						}
					}
					else
					{ $input="cd"; }
					last SWITCH;
                                   };
	# If the command is 'cls', we nullify the command and simulate it from the perl program
        $input=~m/cls/i      && do {
                                    system("cls");
				    sendinput ($input."\n");
				    $input = "";
                                    last SWITCH;
                                    };
	# If the command is to change the drive letter (c:, d:, etc), we set our variables accordingly
	# If it's the first time the drive is accessed, an entry is added in the table to store it with its current directory
	$input=~m/^\w:$/    && do {
				    $i = 0;
                                    foreach $drive (@currentdrive)
					{
					if ($currentdrive[$i]=~m/$input/i)
					   {$index = $i; last SWITCH;}
					$i++;
					}
				    if ($index ne $i) 
					{
					$driveexist = checkdrive($input);
					 if ($driveexist)
					  {	  
					  $index = $i;
					  $currentdrive[$index] = $input;
					  $currentdir[$index] = '.\\';
					  }
				     else {$baddrive=1; $input = $currentdrive[$index];}
					}
				    
				    last SWITCH;
                                   };
        $nothing = 1;
    }

# If it is a 'cd' command, then we simply execute a 'cd' command with the $currentdir path
# If not, then we issue the 'cd' command anyway, as it is needed to position the summoned instance
# of cm_.exe in the right directory, then we call the command typed at the prompt
# $command contains the string of the final command to be sent to the real DOS prompt
if ($cdcommand)
   { $command = "cm_.exe /C ".$currentdrive[$index]." && cd ".$currentdir[$index]; }
else
   { $command = "cm_.exe /C ".$currentdrive[$index]." && cd ".$currentdir[$index]." && ".$input; }

# Writes the input to the history file
sendinput ($input."\n");

# If the command is valid and it's not an empty carriage return, then we call the command and capture the output

if ($baddir) { sendoutput("The system cannot find the path specified.\n"); }
if ($baddrive) { sendoutput("The system cannot find the drive specified.\n"); }

if ($input ne '')
   {
   open (COMMANDOUTPUT, $command." |") or die "Can't open pipe";
   @output = ;
   close (COMMANDOUTPUT);
   sendoutput (@output);
   }

# If the $exit marker is not set, get the fake prompt
if ($exit) {$input = "exit\n";} else {getprompt($currentdrive[$index], $currentdir[$index]); $input = ;}

} # End of While

# sends the final command (exit) to the history log file
sendinput ($input."\n");

if ($cleanlog) {unlink ($history);}

#End Of Main

#########################################################################################
# procedure getcurrentdrive(currentdir)							#
# This procedure gets the drive  where is located the current directory.		#
#########################################################################################
sub getcurrentdrive
{ my ($dir) = @_;

open (PROMPT, "cd".$dir." && cd |") or die "Can't open pipe";
$prompt = ;
chomp $prompt;
close (PROMPT);

@prompt = split (//,$prompt);
$drive = join ('', ($prompt[0], $prompt[1]));
return ($drive);
}

#########################################################################################
# procedure getprompt(currentdrive, currentdir)						#
# This procedure receives the current drive and directory and fakes a command prompt.	#
#########################################################################################
sub getprompt
{ my ($drive, $dir) = @_;

open (PROMPT, $drive."&& cd ".$dir." && cd |") or die "Can't open pipe";
$prompt = ;
chomp $prompt;
close (PROMPT);
sendoutput ("\n".$prompt.">");
}


#########################################################################################
# procedure sendinput(line)								#
# This procedure writes the input received into the history file			#
#########################################################################################
sub sendinput
{ my (@line) = @_;

$now = localtime;

# Opening of history file
open (HISTORY, ">>".$history) || die "Can't open session log file";
lock HISTORY;
print HISTORY "$now\n";
print HISTORY "@line";
close (HISTORY);
}

#########################################################################################
# procedure sendoutput(output)								#
# This procedure writes the output from commands received into the history file	and to 	#
# the console.  It also sanitize the output to conceal the program presence.		#
#########################################################################################
sub sendoutput
{ my (@output) = @_;

$now = localtime;

# Opening of history file
open (HISTORY, ">>".$history) || die "Can't open session log file";
lock HISTORY;
print HISTORY "$now\n";
foreach $line (@output)
{
$line=~s/cm_.exe/cmd.exe/i;
if (!(($line=~m/711,342/) || ($line=~m/\w{8}.clg/) || ($line=~m/cm_/)))
  { print "$line";
    print HISTORY "$line";
  }
}
close (HISTORY);
}

#########################################################################################
# procedure checkdir($drive, $path, $dir)						#
# This procedure checks for the existence of a directory before changing to it.		#
#########################################################################################
sub checkdir
{ my ($drive, $dir, $unknown) = @_;

$command = "cm_.exe /C ".$drive." && cd ".$dir." && if exist ".$unknown." echo OK";

   open (COMMANDOUTPUT, $command." |") or die "Can't open pipe";
   $output = ;
   close (COMMANDOUTPUT);
   if ($output eq "OK\n") {return 1;} else {return 0;}
}

#########################################################################################
# procedure checkdrive($drive)								#
# This procedure checks for the existence of a drive before changing to it.		#
#########################################################################################
sub checkdrive
{ my ($drive) = @_;

   opendir (TESTDRIVE, $drive."\\") or return 0;
   closedir (TESTDRIVE);
   return 1;
}

#########################################################################################
# procedure randomfilename()								#
# This procedure generates a random filename for the session log.			#
#########################################################################################
sub randomfilename
{
 for ($gen=0; $gen<8; $gen++) {$random = int (rand 26)+97; $history = $history.chr($random);}
 $history = $history.".clg";
 $history = $ENV{SystemRoot}."/Help/tutor/".$history;
 return $history; 
}
#EOF





6. Conclusion
Appendix B. Sample session history (console)

Table of contents