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