
/* VirusX - by Steve Tibbett

	The complete Virus Elimination System!

		Please - if you find a new virus, Send me a copy!
		(And warn me it's on the disk!).  I want to keep
		this program current.  (Feel free to put something
		neat (but not copyrighted) on the disk also!
*/

/* - Ok, it's not the cleanest code in the world - it was written in two
     days, for a specific purpose, and it works!                         */

struct Port *diskport; 		/* disk's port.*/
struct IOStdReq *diskreq;	/* disk's IOStdReq */

int DisksChecked, DisksInstalled, SCAFound;	/* for title bar info */

char titlebuffer[80];


/* Who needs includes with Aztec?  Everything's precompiled! */

/* Amount of boot code we've got (approx):  */
#define BSIZE 40

int ChangeCount[4];		/* TD_CHANGECOUNT for all 4 drives */

int LastSum;			/* Used in the checksumming */

int CheckDrives;		/* Boolean, from the command line */

int error;			/* sort of a temporary variable sort of */

unsigned char diskbuffer[3*512];/* Everything ends up in here. 
				   I suppose I should have AllocMem'ed this,
				   but it's SO easy this way */

/* Warning messages.  These messages get modified before being displayed
   (Unless you DO have a DF9:) */
char TEXTPTR[] = "Danger:  The disk in DF9: is";
char NBCTEXT[] = "Danger:  The disk in DF9: has"; 


/* This is a byte by byte copy of working boot block code.  Check it
   out if you like.  This is what gets written back to the disk when you
   ask VirusX to fix a disk. */

unsigned char bootblock[] = { 'D', 'O', 'S', 0, 0xc0, 0x20, 0x0f, 0x19, 
0, 0, 3, 0x70, 0x43, 0xfa, 0, 0x18, 
0x4e, 0xae, 0xff, 0xa0, 0x4a, 0x80, 0x67, 0x0a, 0x20, 0x40, 0x20, 0x68, 
0x00, 0x16, 0x70, 0x00, 0x4e, 0x75, 0x70, 0xff, 0x60, 0xfa, 0x64, 
0x6f, 0x73, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,0,0,0,0,0};

/* for IntuiText's */
char def_font[] ="topaz.font";

struct TextAttr TxtAt_Plain = {	(UBYTE *)def_font, 8,
	FS_NORMAL, FPF_ROMFONT};

/***  Non SCA warning requester IntuiText's ***/
struct IntuiText Body2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *)"Nonstandard Boot Code!", NULL };
struct IntuiText Body1 = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)NBCTEXT, &Body2 };
struct IntuiText Pos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Remove it", NULL };
struct IntuiText Neg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "Ignore it", NULL };

/***** SCA Danger Requester IntuiText's ******/
struct IntuiText SCABody2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "infected with an SCA Virus!!", NULL };
struct IntuiText SCABody = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)TEXTPTR, &SCABody2 };
struct IntuiText SCAPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Remove it", NULL };
struct IntuiText SCANeg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "Ignore it", NULL };

/***** Write Protect Error Requester IntuiText's ******/
struct IntuiText ERRBody2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "Write Protected.", NULL };
struct IntuiText ERRBody = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)"DISK ERROR:  Disk is", &ERRBody2 };
struct IntuiText ERRPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Retry", NULL };
struct IntuiText ERRNeg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "Cancel", NULL };

/***** Rewrite block?  Really? ******/
struct IntuiText REWBody3 = {
   0, 1,     JAM2,          20,28,        &TxtAt_Plain,(UBYTE *) "boot sectors?", NULL };
struct IntuiText REWBody2 = {
   0, 1,     JAM2,          20,18,        &TxtAt_Plain,(UBYTE *) "rewrite that disk's boot", &REWBody3};
struct IntuiText REWBody = {
   0, 1,        JAM2,          20, 8, &TxtAt_Plain,      (UBYTE *)"Are you sure you want to", &REWBody2 };
struct IntuiText REWPos = {
   0, 1,     JAM2,           7,3,        &TxtAt_Plain, "Yes", NULL };
struct IntuiText REWNeg = {
   0, 1,    JAM2,           7,3,        &TxtAt_Plain, "No!", NULL };

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Window *LittleWindow;
struct IntuiMessage *Message;
struct RastPort *RP;

int Keepgoing;		/* a boolean flag.  it's false when we want out. */
int Class, Code;	/* for storing IntuiNewspaper things. */
int x, y, i;		/* left over from my using Basic */

/*** The Newwindow Structure. ***/

char TITLETEXT[] = "VirusX 1.0 by Steve Tibbett";
struct NewWindow NewLittleWindow = {
	128,	/* Left, Top, Width, Height */	
	0,		
	300,		
	10,		
	0,	/* Frontpen, Backpen */	
	1,		
DISKINSERTED | CLOSEWINDOW | VANILLAKEY | MOUSEBUTTONS,	/* IDCMP Flagz */
WINDOWDRAG | WINDOWDEPTH | RMBTRAP | WINDOWCLOSE | NOCAREREFRESH, /* Windo Flagz */	
	NULL,		
	NULL,		
	TITLETEXT,	/* My name.  Dont touch it!! */
	NULL,		
	NULL,		
	0,		
	0,		
	0,		
	0,		
	WBENCHSCREEN,	
	};
/*********************Da Beginnin*************************/
main(argc, argv)
int argc;
char *argv[];
{
int OldPri;


/* Come on, folks, is intuition never NOT going to be available???? */
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);


/* If checkdrives is true, later on it means we want to check all the drives
   with disks in them for viruses. */

CheckDrives = FALSE;

if (argc == 0)	/* If argc == 0, we're a Workbench deryvatuv */
	{
	CheckDrives = TRUE;	/* If we're workbench, we want to check drives */
	}
	else
	{
	for (i = 0; i < argc; i++)
		{
		if (argv[i][0] == '-')	/* if we find a -C, we want CheckDrives */
			{
			if (argv[i][1] == 'c' | argv[i][1] == 'C') CheckDrives = TRUE;
			};
		if (argv[i][0] == '?')	/* HELP ME! */
			{
			puts("Usage: VirusX [-C]\n");
			puts("      -c checks all drives for Virus upon execution\n");
			exit(FALSE);
			};
		};
	};

/* Save OldPri so we can restore it later on.  We're so nice. */
OldPri = SetTaskPri(FindTask(0), 19); /* and stick us up high. */
diskport = CreatePort(0,0);
diskreq = CreateStdIO(diskport);

SetUp();

DoLittle();	/* The main loop.  Do Little.  Ya. */

/* restore priorities to saneness, and take off, eh? */
SetTaskPri(FindTask(0), OldPri);

DeletePort(diskport);
DeleteStdIO(diskreq);

exit(FALSE);
}			

/*********************/
DoLittle()
{
register int Code;
register int Class;	/* gee */
register int KG2;	/* KeepGoing 2.  Another booleean. */

LittleWindow = OpenWindow(&NewLittleWindow);
if (LittleWindow == NULL) exit(400L);	/* No memory to open little window! */

KG2 = TRUE;
RP = LittleWindow->RPort;	/* easier than typing Move(LittleWindow->RP... all
				   the time */

if (CheckDrives == TRUE) 	/* We'll fake it:  Set changecount to 1000, to*/
	{			/*make people think it's been changed */
	for (x = 0; x < 4; x++) ChangeCount[x] = 1000;
	CheckBlock();		/* CheckBlock() checks boot block for virus. */
	CheckDrives = FALSE;	/* so we don't do it again */
	};

SetAPen(RP, 1);
SetBPen(RP, 0);
SetDrMd(RP, JAM2);	/* ya. JAM 2!  Love that name! */

while (KG2 == TRUE)
	{
	sprintf(titlebuffer, "VirusX: Disks Checked: %d   Disks Installed: %d   Viruses Found: %d", DisksChecked, DisksInstalled, SCAFound);
	SetWindowTitles(LittleWindow, -1, titlebuffer);
	Message = GetMsg(LittleWindow->UserPort);
	while (Message == NULL)
			{
			/* Lets be nice to other tasks!  Ya! */
			Wait(1<<LittleWindow->UserPort->mp_SigBit);
			Message = GetMsg(LittleWindow->UserPort);
			};
	Class = Message->Class;
	Code = Message->Code;
	if (Message != NULL) ReplyMsg(Message);
	
	if (Class == CLOSEWINDOW) 
		{
		CloseWindow(LittleWindow);
		exit(FALSE);
		};

	if (Class == DISKINSERTED) CheckBlock();
	};

/* KG2 = false, we fell thru to here.  Ow. */

CloseWindow(LittleWindow);
return;
}

/**********************/
puts(str)		/* outputs a string, but is REAL small. */
char *str[];
{
	Write(Output(), str, strlen(str));
}

/********************/
/* Opens trackdisk, finds out who's out there, and sets Changecount up accordioningly. */
/********************/

SetUp()
{

for (x = 0; x < 4; x++)	/* go thru all 4 possible drives */
	{
	ChangeCount[x] = 0;
	error = OpenDevice("trackdisk.device",x,diskreq,0);
	if (error > 0) continue;	/* no drive here */
	diskreq->io_Command = TD_CHANGENUM;	
	error = DoIO(diskreq);		
	ChangeCount[x] = diskreq->io_Actual;	/* save changecount for later */
	CloseDevice(diskreq);
	};

}

/********************************/
/* This routine returns which drive changed disks lately */
/********************************/
WhoChanged()
{
int RetVal;	/* The value we'll return */

RetVal = -1;	/* return -1 if all else fails */
for (x = 0; x < 4; x++)
	{
	if (ChangeCount[x] == 0) continue;	/* no drive here */
	error = OpenDevice("trackdisk.device",x,diskreq,0);
	if (error > 0) continue;	/* no drive here */
	
	diskreq->io_Command = TD_CHANGESTATE;
	DoIO(diskreq);
	if (diskreq->io_Actual != 0) 
		{
		continue;
		};

	diskreq->io_Command = TD_CHANGENUM;
	DoIO(diskreq);
	if (diskreq->io_Actual != ChangeCount[x]) 
		{
		RetVal = x;
		ChangeCount[x] = diskreq->io_Actual;
		CloseDevice(diskreq);
		goto Out;
		};
	CloseDevice(diskreq);
	};
Out:;
return(RetVal);
}
/********************************/

CheckBlock()
{
int Sum, Bootable, Virus;   /* Virus is a flag, Bootable is a flag, Sum the cksum. */
int a, Unit;
int SCA;	/* don't get scared, it's just a flag. */

Unit = WhoChanged();

CheckAgainYouFool:;	/* oh oh, a label.  Could be a goto later on! */

DisksChecked++;

SCA = FALSE;

/* Unit # to open is returned by "WhoChanged()" up above. */
if (Unit == -1) return;
error = OpenDevice("trackdisk.device",Unit,diskreq,0);
if (error > 0) return;

/* I've heard stories that pulling a read request to block zero with a
   length of 1024 will cause the virus to write itself back.  Not taking
   any chances. */

diskreq->io_Command = CMD_READ;
diskreq->io_Data = diskbuffer;
diskreq->io_Length = 3*512;
diskreq->io_Offset = 0;
DoIO(diskreq);

diskreq->io_Length = 0;
diskreq->io_Command = TD_MOTOR;
DoIO(diskreq);				/* turn off motor */

if (diskreq->io_Error > 19) return;	/* disk error, lemme out */
CloseDevice(diskreq);

Sum = 0;
for (a=0; a<1024; a=a+4)
	{
	LastSum = Sum;
	Sum = Sum + diskbuffer[a+3];
	Sum = Sum + (diskbuffer[a+2] * 256);
	Sum = Sum + (diskbuffer[a+1] * 65536);
	Sum = Sum + (diskbuffer[a] * (65536 * 256));
	if (LastSum > Sum) Sum++;	/* took me a while to figger this out */
	}

if (Sum != 0) return;	/* if it's not bootable, we DONT want it! */

/* check specifically for SCA virus */
if (diskbuffer[8] == 'C')
	if (diskbuffer[9] == 'H')
		if (diskbuffer[10] == 'W')
				{
				SCA = TRUE;	/* CHW is part of SCA virus */
				SCAFound++;
				};

/* compare boot block with real boot block.  If it's not, notify God. */
Virus = FALSE;
for (x = 0; x < 39; x++) /* nuum of lements in bootblock */
	{
	if (diskbuffer[8+x] != bootblock[8+x])
		{	
		Virus = TRUE;
		};
	};

/* Oh no, a Virus! */
if (Virus == TRUE) 
	{
	/* Oh no, it's the SCA Virus! */
	if (SCA != TRUE)
		{
		Delay(1);
		NBCTEXT[23] = '0'+Unit; /* change DF9: to real drive in text */
		error = AutoRequest(LittleWindow, &Body1, &Pos, &Neg, 0, 0, 320, 75);
		if (error == TRUE) DoInstall(Unit); /* user wants it neutered. */
		Delay(1);
		}
	  else
		{
		/* Phew, just a nonstandard boot block, probably */
		Delay(1);
		TEXTPTR[23] = '0'+Unit;
		error = AutoRequest(LittleWindow, &SCABody, &SCAPos, &SCANeg, 0, 0, 320, 75);
		if (error == TRUE) DoInstall(Unit);	/* user wants it fixed. */
		Delay(1);
		};
	};


Unit = WhoChanged();	/* more than one disk was changed. check again */
if (Unit != -1) goto CheckAgainYouFool;
}

/********************************/
/* This is where the boot code gets changed */
/********************************/
DoInstall(un)
int un;	/* unit to write to */
{
register int x;
register int Sum;
int err, a;

/* Rewrite disk?  Really?  */
error = AutoRequest(LittleWindow, &REWBody, &REWPos, &REWNeg, 0, 0, 320, 75);
if (error != TRUE) return;	/* user changed his brain. */

DisksInstalled++;

for (x = 0; x < 1024; x++)
	diskbuffer[x] = 0;	/* clear diskbuffer to zero.  clean. */

for (x = 0; x < 50; x++)
	{
	diskbuffer[x] = bootblock[x];	/* copy boot code into buffer */
	};

/* Write it ! */

error = OpenDevice("trackdisk.device", un,diskreq,0);
if (error > 0) return;

diskreq->io_Command = CMD_READ;
diskreq->io_Data = diskbuffer;
diskreq->io_Length = 512;
diskreq->io_Offset = 44*512;
DoIO(diskreq);

trygain:		/* GOTO!  A GOTO!  OH NOOOO! */

diskreq->io_Command = TD_PROTSTATUS;
DoIO(diskreq);		/* check if disk is write protected */

if (diskreq->io_Actual != 0)
	{
	error = AutoRequest(LittleWindow, &ERRBody, &ERRPos, &ERRNeg, 0, 0, 320, 75);
	if (error == TRUE) /* error is true or false, depending on user */
		{
		goto trygain;
		};
	CloseDevice(diskreq);
	return;	/* unrecoverable write protect error!!!!!!!!! */
	};

diskreq->io_Length = 1024; /* here we go! */
diskreq->io_Data = &diskbuffer[0];  
diskreq->io_Command = CMD_WRITE;
diskreq->io_Offset = 0L;
DoIO(diskreq);

diskreq->io_Command = CMD_UPDATE;	/* flush buffer to disk */
DoIO(diskreq);
error = diskreq->io_Error;

diskreq->io_Length = 0;
diskreq->io_Command = ETD_MOTOR;
DoIO(diskreq);				/* turn off motor */

CloseDevice(diskreq);

if (error > 19) 
	{
	SetWindowTitles(LittleWindow, "Error, Nothing Done.", -1);
	} 
    else
	{
	SetWindowTitles(LittleWindow, "Disk Healed.", -1);
	};

Delay(100);
SetWindowTitles(LittleWindow, TITLETEXT, -1);

}
/************************/

