program cmd;
(*
= Version History
V0.0.1 : Initial
V0.0.2 : For DIR and TREE, will display textfiles if present in home directory
V0.0.3 : ?
= cmd.exe replacement
== Windows only! ==
= Purpose:
== To frustrate tech support scammers
= Documentation:
== see readme.txt file distributed with this application
= License:
== Copyright (C)2018 Gordon Bamber minesadorada AT charcodelvalle.com
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
*)
{$mode objfpc}{$H+}
uses {$IFDEF UNIX} {$IFDEF UseCThreads}
cthreads, {$ENDIF} {$ENDIF}
Classes,
SysUtils,
CustApp, { you can add units after this }
strutils,
registry;
type
{ TMyCmd }
TMyCmd = class(TCustomApplication)
private
fCurrDir: string;
fCurrDrive: string;
fCurrFiledate: TDateTime;
fCommand: string;
fUserInput: string;
fNumFiles: integer;
fTotalSize: int64;
fregistry: TRegistry;
ftreetextpresent: boolean;
fdirtextpresent: boolean;
// Get/Set TheCurrDir property
function GetTheCurrDir: string;
procedure SetTheCurrDir(AValue: string);
procedure WaitABit; //Blocking pause
procedure CDDotDot; // Deal with cd.. command
procedure ChangeDir(Avalue: string); // Deal with cd and mkdir commands
procedure WriteDirectoryListing; // Listing is semi-random each time
procedure WriteDirectoryListingFromFile; //display C_DIRTEXT
procedure WriteTreeListingFromFile; // display C_TREETEXT
function FetchNewFakeDirDate: string;
function FetchNewFakeFilesize: string;
procedure WriteFakeNetstat; // Entries are the same each time
procedure SetAutoRun(bCreateOrDelete: boolean);
// If set, then real cmd.exe will automatically run this cmd.exe
procedure DisplayReadme; // either cmd -h or type 'help' at prompt
protected
procedure DoRun; override; // Add new commands in this procedure
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure WriteHelp; virtual;
// Property tracks the fake current directory displayed at the prompt
property TheCurrDir: string read GetTheCurrDir write SetTheCurrDir;
end;
const
// Hardcoded
C_FULLPROMPT = 'Microsoft Windows [Version 10.0.17134.345]' +
LineEnding + '(c) 2018 Microsoft Corporation. All rights reserved.' +
LineEnding + LineEnding;
C_BADCOMMAND =
'''%s'' is not recognized as an internal or external command,%soperable program or batch file.'
+ LineEnding + LineEnding;
C_DIRDATEFORMAT = 'ddddd hh:nn';
C_REG_AUTORUN = '\Software\Microsoft\Command Processor'; //HKEY_CURRENT_USER
C_DIRTEXT = 'dirtext.txt';
C_TREETEXT = 'treetxt.txt';
//DEPRECATED: C_FullPrompt = 'Microsoft Windows [Version %d.%d.%d.%d]' + LineEnding +
// '(c) 2018 Microsoft Corporation. All rights reserved.' + LineEnding + LineEnding;
{ TMyCmd }
procedure TMyCmd.DisplayReadme;
// Displays readme.txt file in same folder as this app
var
F: TextFile;
s: string;
ct: integer;
begin
// Is readme.txt missing?
if not FileExists('readme.txt') then
begin
WriteLn('Help file ''readme.txt'' is missing');
exit;
end;
// OK. Now read and display;
try
System.Assign(F, 'readme.txt');
Reset(F);
ct := 0;
while not EOF(F) do
begin
Inc(ct);
if ct mod 15 = 0 then // Show 15 lines per screen
begin
WriteLn;
WriteLn('Press any key to continue');
Readln;
end
else
begin
// Read a line, then display a line
ReadLn(F, s);
WriteLn(s);
end;
end;
finally
Close(F);
end;
end;
procedure TMyCmd.SetAutoRun(bCreateOrDelete: boolean);
begin
fRegistry.RootKey := HKEY_CURRENT_USER;
if bCreateOrDelete = True then
begin
fregistry.OpenKey(C_REG_AUTORUN, True);
fRegistry.WriteString('Autorun', EXEname);
fregistry.CloseKey;
end
else
begin
fregistry.OpenKey(C_REG_AUTORUN, True);
fregistry.DeleteValue('Autorun');
fregistry.CloseKey;
end;
end;
procedure TMyCmd.WaitABit;
begin
Sleep(200);
end;
function TMyCmd.FetchNewFakeFilesize: string;
// 18 chars right aligned
var
fl: double;
begin
fl := Random * 1000000;
Result := Format('%.0n', [fl]);
Result := PadLeft(Result, 18);
Inc(fNumFiles);
Inc(fTotalSize, ROUND(fl));
end;
function TMyCmd.FetchNewFakeDirDate: string;
begin
fCurrFileDate := fCurrFileDate - Random * 20;
DateTimeToString(Result, C_DIRDATEFORMAT, fCurrFileDate, []);
end;
procedure TMyCmd.WriteDirectoryListingFromFile;
// Displays the file C_DIRTEXT
var
F: TextFile;
s: string;
ct: integer;
begin
// use cmd dir > dirtxt.txt to obtain a valid directory listing
try
System.Assign(F, C_DIRTEXT);
Reset(F);
ct := 0;
while not EOF(F) do
begin
Inc(ct);
if ct mod 15 = 0 then // Show 15 lines per screen
begin
WriteLn;
WriteLn('Press any key to continue');
Readln;
end
else
begin
// Read a line, then display a line
ReadLn(F, s);
WriteLn(s);
end;
end;
finally
Close(F);
end;
end;
procedure TMyCmd.WriteTreeListingFromFile;
// Displays the file C_TREETEXT
var
F: TextFile;
s: string;
cp: TSystemCodePage;
begin
// OK. Now read and display. Textfile MUST be encoded as UTF-8
// tree > treetxt.txt will output in the wrong codepage; notepad2 will convert it.
try
System.Assign(F, C_TREETEXT);
Reset(F);
cp := GetTextCodePage(F);
if cp <> CP_UTF8 then
SetTextCodePage(F, CP_UTF8);
while not EOF(F) do
begin
// Read a line (UTF-8), then display a line (ANSI)
ReadLn(F, s);
WriteLn(s);
// WriteLn(Utf8ToAnsi(s)); REM redundant as WriteLn set to UTF8
end;
finally
Close(F);
end;
end;
procedure TMyCmd.WriteDirectoryListing;
// Displays a fake directory listing
var
fOdds: single;
begin
fCurrFiledate := Now();
fOdds := 0.8;
fNumFiles := 0;
fTotalSize := 0;
WriteLn;
WriteLn(' Volume in drive ' + Upcase(fCurrDrive) + ' is WINDOWS');
WriteLn(' Volume Serial Number is 84A5-5539');
WriteLn;
WriteLn(' Directory of ' + fCurrDir);
WriteLn;
if (Length(fCurrDir) > 3) and (fdirtextpresent = False) then
begin
WriteLn(FetchNewFakeDirDate + '
.');
WriteLn(FetchNewFakeDirDate + ' ..');
end;
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Private');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Banking');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Logins');
if LeftStr(Upcase(fCurrDir), 8) = 'C:\USERS' then
begin
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Contacts');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Documents');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Downloads');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + ' Pictures');
end;
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + FetchNewFakeFilesize + ' readme.txt');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + FetchNewFakeFilesize + ' bank details.doc');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + FetchNewFakeFilesize + ' accounts.xls');
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + FetchNewFakeFilesize + ' passwords.doc');
if not ftreetextpresent then // display file summary
begin
if (Random > fOdds) then
WriteLn(FetchNewFakeDirDate + FetchNewFakeFilesize + ' ');
WriteLn(Format(' %d file(s) %d bytes', [fNumFiles, fTotalSize]));
WriteLn;
end;
end;
procedure TMyCmd.WriteFakeNetstat;
// Display fake list of connections
begin
WriteLn;
WriteLn('Active Connections');
WriteLn;
WriteLn(' Proto Local Address Foreign Address State');
WriteLn(' TCP 192.168.0.9:49682 ec2-18-211-19-105:https CLOSE_WAIT');
WriteLn(' TCP 192.168.0.9:49876 40.67.248.104:https ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53636 8.36.80.215:https TIME_WAIT');
WaitABit;
WaitABit;
WaitABit;
WaitABit;
WriteLn(' TCP 192.168.0.9:53957 54.239.21.139:https ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53958 s3-us-west-2-w:https ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53959 54.239.21.125:https ESTABLISHED');
WaitABit;
WaitABit;
WriteLn(' TCP 192.168.0.9:53960 54.239.31.63:https ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53961 s3-us-west-2-w:https ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53962 54.239.31.63:https ESTABLISHED');
WaitABit;
WriteLn(' TCP 192.168.0.9:53963 a104-83-194-139:https ESTABLISHED');
WaitABit;
WaitABit;
WaitABit;
WriteLn(' TCP 192.168.0.9:53964 a104-83-194-139:https ESTABLISHED');
WaitABit;
WaitABit;
WriteLn(' TCP 192.168.0.9:53965 a104-83-194-139:https ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53966 server-52-85-46-242:http ESTABLISHED');
WriteLn(' TCP 192.168.0.9:53967 a84-53-129-220:http TIME_WAIT');
WaitABit;
WriteLn(' TCP 192.168.0.9:53968 93.184.220.29:http ESTABLISHED');
WaitABit;
WaitABit;
WriteLn(' TCP 192.168.0.9:53969 104.18.25.243:http ESTABLISHED');
WriteLn;
end;
procedure TMyCmd.ChangeDir(Avalue: string);
// Updates the fake command prompt
var
s: string;
begin
s := GetTheCurrDir;
if Length(AValue) > 0 then
begin
SetTheCurrDir(s + '\' + AValue);
fCurrDrive := LeftStr(fCurrDir, 1);
end;
end;
procedure TMyCmd.CDDotDot;
// Deal with cd.. command by changing fake Current Directory
var
s: string;
begin
s := GetTheCurrDir;
if RPos('\', s) > 0 then
begin
SetTheCurrDir(LeftStr(fCurrDir, RPos('\', s)));
fCurrDrive := LeftStr(fCurrDir, 1);
end;
end;
procedure TMyCmd.SetTheCurrDir(AValue: string);
// set property TheCurrDir
begin
if fCurrDir <> AValue then
fCurrDir := AValue;
fCurrDrive := LeftStr(fCurrDir, 1);
end;
function TMyCmd.GetTheCurrDir: string;
// Get property TheCurrDir
begin
Result := ExcludeTrailingBackslash(fCurrDir);
end;
procedure TMyCmd.DoRun;
// Command parser loop
var
ErrorMsg, s: string;
ct: integer;
Parsed: boolean;
begin
// quick check parameters
ErrorMsg := CheckOptions('h', 'help');
if ErrorMsg <> '' then
begin
ShowException(Exception.Create(ErrorMsg));
Terminate;
Exit;
end;
// parse parameters
if HasOption('h', 'help') then
begin
WriteHelp;
Terminate;
Exit;
end;
// Deprecated:
// Write(Format(C_FULLPROMPT,[Win32Platform,Win32MajorVersion,Win32MinorVersion,Win32BuildNumber]) + TheCurrDir + '>');
Randomize; // For random datetimes, odds etc used in dir listings
// Show header info and command prompt
Write(C_FULLPROMPT + TheCurrDir + '>'); //hardcoded for windows 10
// Grab input
ReadLn(fUserInput);
Log(etInfo, 'Scammer typed ''%s''', [fUserInput]);
// Does nothing unless DoLog virtual procedure is overridden
fCommand := UpCase(fUserInput);
Parsed := False;
// Enter command loop
while fCommand <> 'EXIT' do
begin
// Parse various commands
// TODO: other commands
//Special commands
//SetAutoRun
if (fCommand = 'SETAUTORUN') and (Parsed = False) then
begin
Parsed := True;
SetAutoRun(True);
WriteLn('AutoRun set to ' + EXEName = ' successfully');
end;
//DelAutoRun
if (fCommand = 'DELAUTORUN') and (Parsed = False) then
begin
Parsed := True;
SetAutoRun(False);
WriteLn('AutoRun key deleted successfully');
end;
// Show help
if (fCommand = 'HELP') and (Parsed = False) then
begin
Parsed := True;
WriteHelp;
WriteLn;
end;
// format: Do a fake format of the drive
if (Pos('FORMAT', fCommand) > 0) and (Parsed = False) then
begin
Parsed := True;
WriteLn('This command will erase the contents of the specified disk.');
WriteLn('WARNING: This action cannot be undone. Are you sure? Y/N');
ReadLn(s);
if UpCase(s) = 'Y' then
begin
Write('Please wait. Formatting..');
for ct := 1 to 30 do
begin
WaitABit;
Write('.');
end;
Writeln('Format complete');
end
else
Writeln('Command canceled.');
WriteLn;
end;
// syskey: Pretend to encrypt the system database
if (Pos('SYSKEY', fCommand) > 0) and (Parsed = False) then
begin
Parsed := True;
WriteLn('WARNING: The syskey utility will encrypt your system database');
WriteLn('The operation cannot be undone. Type ''yes'' to continue');
ReadLn(s);
if UpCase(s) = 'YES' then
begin
WriteLn('Type in the new password:');
ReadLn(s);
Write('Please wait. Encrypting..');
for ct := 1 to 30 do
begin
WaitABit;
Write('.');
end;
Writeln('Syskey encryption complete. Restart the computer to complete the operation');
end
else
Writeln('Syskey command canceled.');
WriteLn;
end;
// netstat
if (Pos('NETSTAT', fCommand) > 0) and (Parsed = False) then
begin
Parsed := True;
WriteFakeNetstat;
WriteLn('Scan foreign addresses for hackers? Y/N');
ReadLn(s);
if UpCase(s) = 'Y' then
begin
Write('Please wait. Scanning connections..');
for ct := 1 to 10 do
begin
WaitABit;
Write('.');
end;
Writeln('Complete.');
WriteLn('Scan reports that all current connections are safe');
end
else
Writeln('WARNING: Scan was intentionally canceled - please run netstat command again.');
WriteLn;
end;
// del, delete deltree and erase
if ((Pos('DEL', fCommand) > 0) or (Pos('ERASE', fCommand) > 0)) and
(Parsed = False) then
begin
Parsed := True;
WriteLn('This command will delete files. Are you sure? Y/N');
ReadLn(s);
if UpCase(s) = 'Y' then
begin
Write('Please wait. Deleting files..');
for ct := 1 to 10 do
begin
WaitABit;
Write('.');
end;
Writeln('Complete');
end
else
Writeln('Command canceled.');
end;
// Go To drive Root
if (fCommand = 'CD\') and (Parsed = False) then
begin
Parsed := True;
SetTheCurrDir(fCurrDrive + ':\');
end;
// Change Drive
if ((Pos(':', fCommand) > 0) and (Parsed = False)) then
begin
Parsed := True;
SetTheCurrDir(LeftStr(fCommand, 2));
end;
// tree and dir
// Construct fake listing (random contents)
// Or display file dirtxt.txt
// Force a 'scan for viruses'
// Proclaim everything is tickety-boo
if ((Pos('DIR', fCommand) > 0) and (Parsed = False)) then
begin
Parsed := True;
if fdirtextpresent then
begin
WriteDirectoryListing;
WriteDirectoryListingFromFile;
end
else
WriteDirectoryListing;
WriteLn('Scan this folder for infections? Y/N');
ReadLn;
WriteLn('Please wait. Scanning for viruses and trojans');
for ct := 1 to 20 do
begin
WaitABit;
Write('.');
end;
WriteLn('System scanned');
WriteLn('Viruses detected: 0');
WriteLn('Trojans detected: 0');
WriteLn('Contents of ' + fCurrDir + ' are clean and not infected.' +
LineEnding + LineEnding);
end;
if ((Pos('TREE', fCommand) > 0) and (Parsed = False)) then
// Display file treetxt.txt if present
// otherwise produce a fake directory listing
begin
if ftreetextpresent then
WriteTreeListingFromFile
else
WriteDirectoryListing;
Parsed := True;
WriteLn('Scan this folder for infections? Y/N');
ReadLn;
WriteLn('Please wait. Scanning for viruses and trojans');
for ct := 1 to 20 do
begin
WaitABit;
Write('.');
end;
WriteLn('System scanned');
WriteLn('Viruses detected: 0');
WriteLn('Trojans detected: 0');
WriteLn('Contents of ' + fCurrDir + ' are clean and not infected.' +
LineEnding + LineEnding);
end;
if (fCommand = 'CD..') and (Parsed = False) then
begin
CDDotDot; // Change fake current directory to its fake parent
Parsed := True;
end;
// Change to another fake folder
if (Pos('CD', fCommand) > 0) and (Parsed = False) then
begin
Parsed := True;
ChangeDir(MidStr(fUserInput, 4, Length(fUserInput)));
end;
// Pretend to make a folder
if (Pos('MKDIR', fCommand) > 0) and (Parsed = False) then
begin
Parsed := True;
ChangeDir(MidStr(fUserInput, 7, Length(fUserInput)));
end;
// Unrecognised command fallback
if (Parsed = False) and (length(fUserInput) > 0) then
WriteLn(Format(C_BADCOMMAND, [fUserInput, LineEnding]));
// Show prompt
Write(TheCurrDir + '>');
// Fetch the next command
ReadLn(fUserInput);
fCommand := UpCase(fUserInput);
Parsed := False;
// Back to start of loop
// user types 'exit' to exit loop
end;
// stop program loop
Terminate;
end;
constructor TMyCmd.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException := True;
Title := 'C:\WINDOWS\system32\cmd.exe';
SetTheCurrDir(GetUserDir); // Set up fake Current Directory to a real one
fCurrDrive := LeftStr(TheCurrDir, 1);
fregistry := TRegistry.Create;
ftreetextpresent := FileExists(C_TREETEXT);
fdirtextpresent := FileExists(C_DIRTEXT);
end;
destructor TMyCmd.Destroy;
begin
fregistry.Free;
inherited Destroy;
end;
procedure TMyCmd.WriteHelp;
begin
{ add your help code here }
writeln('Usage: ', ExeName, ' -h');
DisplayReadme;
writeln('Press any key to continue');
readln;
end;
var
Application: TMyCmd;
{$R *.res}
begin
Application := TMyCmd.Create(nil);
Application.Title:='Command';
Application.Run;
Application.Free;
end.