/* Program: CHECKUP.C Author : Kim Moser Date : 2/25/89 System : IBM PC / Borland Turbo-C 2.0 Descrip: Reads the hard drive's boot sector, C:\COMMAND.COM, C:\IBMBIO.COM, and C:\IBMDOS.COM. Then, either writes image of boot sector and information about three files mentioned to an image file or compares them with an already existing image file. Compiler: Model: SMALL Usage : CHECKUP -c [text] | -p | -w|-r[i] [drive] [-dfname] Option Meaning -c Write 'text' to unused CMOS RAM (defaults to CHR(0)'s if omitted) -p Print (display) text stored in unused CMOS RAM -w Write boot sector and information about 'drive:\COMMAND.COM', 'drive:\IBMBIO.COM', 'drive:\IBMDOS.COM', this program, and DOS version to file 'dfname' -r Compare boot sector and information about 'drive:\COMMAND.COM', 'drive:\IBMBIO.COM', 'drive:\IBMDOS.COM', this program, and DOS version with contents of file 'dfname' -i Ignore unused CMOS RAM Argument Meaning [drive] Letter of drive to analyze (boot sector, 'drive:\COMMAND.COM', 'drive:\IBMBIO.COM', and 'drive:\IBMDOS.COM' must be available on specified drive); defaults to current drive if not specified [-dfname] Name of data file (may include drive and/or path) to read/write; defaults to 'CHECKUP.DAT' if not specified. */ #include #include /* absread() */ #include /* O_BINARY */ #include /* setmode */ #include /* toupper() */ #include /* exit() */ #include /* getch() */ #include /* getdisk() */ typedef unsigned char boolean; #define FALSE ((boolean)0) #define TRUE ((boolean)1) char err[255]; /* Used for error messages */ char ddf[] = "CHECKUP.DAT"; /* Default data file name */ char CMD[] = "C:\\COMMAND.COM"; char BIO[] = "C:\\IBMBIO.COM"; char DOS[] = "C:\\IBMDOS.COM"; /* Default filenames [drive gets set during initialization] */ char *CHECKUP; /* Gets initialized to argv[0], which is assumed to point to fully qualified filename of this program */ char *DATAFILE = ddf; /* Name of data file */ unsigned int DISK; /* Drive to analyze [defaults to current drive] */ int IGNORECMOS; /* Whether to ignore CMOS RAM when comparing */ typedef unsigned int vinfo; /* DOS version */ unsigned int BOOTSIZE; /* Size of boot sector [gets initialized in main()] */ typedef char *bootinfo; #define CMOSSTART (0x34) #define CMOSEND (0x3F) /* Beginning and end of CMOS RAM that can be used to store user data [e.g. viruses that are activated by a .COM or .EXE program] */ typedef char cmosinfo[CMOSEND-CMOSSTART+1]; typedef struct finfo { unsigned long len; /* File length */ struct ftime td; /* File date and time */ int att; /* File attribute(s) */ long chk; /* File checksum */ }; typedef struct allinfo { /* All info except the boot record, since it's a pointer which must be dynamically allocated depending on the number of bytes in a sector */ vinfo ver; /* DOS version */ cmosinfo cmos; /* CMOS info */ struct finfo cmd; /* Info about COMMAND.COM */ struct finfo bio; /* Info about IBMBIO.COM */ struct finfo dos; /* Info about IBMDOS.COM */ struct finfo this; /* Info about CHECKUP.EXE */ }; unsigned int ALLSIZE = sizeof(struct allinfo); #define EXTENDED 256 int keyhit(void) { int ch; if ((ch=getch()) == NULL) /* Assumes that extended key code is next char returned, and that getch() will NOT wait for a key [i.e. key will be available immediately]. */ return (getch()+EXTENDED); else return ch; } unsigned char peekcmos(ofs) /* Returns byte at offset 'ofs' of CMOS RAM */ unsigned char ofs; { outportb((int)0x70,ofs); return inportb((int)0x71); } void pokecmos(ofs,b) unsigned char ofs; unsigned char b; /* Writes byte 'b' to offset 'ofs' of CMOS RAM */ { outportb((int)0x70,ofs); outportb((int)0x71,b); } void balk(s) /* Prints string 's', waits for user to hit ENTER, and exits program with a value of 1 */ char *s; { printf( "%s", s ); printf( "Hit to continue.\n" ); while (keyhit()!=13); exit(1); } void usage(void) /* Shows proper usage and exits with a value of 1 */ { printf( "\nUsage: CHECKUP -c [text] | -p | -w|-r[i] [drive] [-dfname]\n\n" ); printf( " OPTION MEANING\n" ); printf( " -c Write 'text' to unused CMOS RAM (defaults to CHR(0)'s if omitted)\n" ); printf( " -p Print (display) text stored in unused CMOS RAM\n\n" ); printf( " -w Write boot sector and information about 'drive:\\COMMAND.COM',\n" ); printf( " 'drive:\\IBMBIO.COM', 'drive:\\IBMDOS.COM', this program, and\n" ); printf( " DOS version to file 'dfname'\n" ); printf( " -r Compare boot sector and information about 'drive:\\COMMAND.COM',\n" ); printf( " 'drive:\\IBMBIO.COM', 'drive:\\IBMDOS.COM', this program, and\n" ); printf( " DOS version with contents of file 'dfname'\n" ); printf( " -i Ignore unused CMOS RAM (only valid for '-R' option)\n\n" ); printf( " ARGUMENT MEANING\n" ); printf( " [drive] Letter of drive to analyze (boot sector, 'drive:\\COMMAND.COM',\n" ); printf( " 'drive:\\IBMBIO.COM', and 'drive:\\IBMDOS.COM' must be available\n" ); printf( " on specified drive); defaults to current drive if not specified\n\n" ); printf( "[-dfname] Name of data file (may include drive and/or path) to read/write;\n" ); printf( " defaults to 'CHECKUP.DAT' if not specified\n" ); exit(1); } boolean rbs(b) /* Reads boot sector into bootinfo 'b'; returns TRUE if successful, else FALSE */ bootinfo b; { printf( "\nReading boot sector for drive %c:\n", DISK+'A' ); return (!absread(DISK, 1, 0, b)); } void readfile(fnam, dat) /* Reads file 'fnam' and determines attributes, checksum, length, and date; puts them in finfo structure pointed to by 'dat' */ char *fnam; struct finfo *dat; { FILE *fp; char ch; printf( "Reading info about file '%s'\n", fnam ); /* Get attrib */ if ( (dat->att=_chmod(fnam,0))==-1 ) { sprintf( err, "Unable to get attribute(s) for file '%s'.\n", fnam ); balk(err); } if ( (fp=fopen(fnam,"r")) == NULL) { sprintf( err, "Unable to open file '%s'.\n", fnam ); balk(err); } setmode(fileno(fp), O_BINARY); /* Get file length */ if ( (dat->len=filelength(fileno(fp)))==-1 ) { sprintf( err, "Unable to get file length for file '%s'.\n", fnam ); balk(err); } /* Get file date and time */ if ( getftime(fileno(fp), &(dat->td)) ) { sprintf( err, "Unable to get date and time for file '%s'.\n", fnam ); balk(err); } /* Compute checksum: */ /* Start at 0; all bytes <88 are subtracted, all bytes >88 are added */ dat->chk = 0L; while ( (ch=getc(fp))!= EOF ) { if (ch<88) dat->chk -= ((long) ch); else if (ch>88) dat->chk += ((long) ch); } fclose(fp); printf( " Attrib: %d Len: %lu Time: %u:%u:%u Date: %u/%u/%u Checksum: %ld\n", dat->att, dat->len, dat->td.ft_hour, dat->td.ft_min, dat->td.ft_tsec, dat->td.ft_month, dat->td.ft_day, dat->td.ft_year+1980, dat->chk ); } void readver(vp) /* Reads DOS version into vinfo record pointed to by 'vp' */ vinfo *vp; { printf( "Reading DOS version\n" ); *vp = _version; } void readcmos(c) /* Reads CMOS RAM from CMOSSTART to CMOSEND, putting bytes starting at 'cmosinfo' pointed to by 'c' */ cmosinfo c; { unsigned char i; printf( "Reading %u bytes from CMOS RAM at offset %u\n", CMOSEND-CMOSSTART+1, CMOSSTART ); for (i=CMOSSTART; i<=CMOSEND; i++) { *(c++) = peekcmos(i); } } void readall(all, boot) /* Reads relevant files, etc. into 'allinfo' struct pointed to by 'all' and boot info pointed to by 'boot' */ struct allinfo *all; bootinfo boot; { rbs( boot ); readfile( CHECKUP, &(all->this) ); readfile( CMD, &(all->cmd) ); readfile( BIO, &(all->bio) ); readfile( DOS, &(all->dos) ); readver( &(all->ver) ); readcmos( &(all->cmos) ); } void readinf(fnam, inf, boot) /* Reads info file named 'fnam' into struct allinfo pointed to by 'inf' and boot info pointed to by 'boot' */ char *fnam; struct allinfo *inf; bootinfo boot; { FILE *fp; unsigned int howmany; printf( "Reading data from file '%s'\n", fnam ); if ( (fp=fopen(fnam,"r"))==NULL ) { sprintf( err, "Unable to open info file '%s' for reading.\n", fnam ); balk(err); } setmode( fileno(fp), O_BINARY ); /* First we read the boot sector image, since it was the first thing written... */ if ( (howmany=read(fileno(fp), boot, BOOTSIZE)) != BOOTSIZE ) { fclose(fp); sprintf( err, "Only %u of %u boot sector bytes read from info file\n'%s'.\n", howmany, BOOTSIZE, fnam ); balk(err); } if ( (howmany=read(fileno(fp), inf, ALLSIZE)) != ALLSIZE ) { fclose(fp); sprintf( err, "Only %u of %u bytes read after boot sector from info file\n'%s'.\n", howmany, ALLSIZE, fnam ); balk(err); } fclose(fp); } void writeall(fnam, all, boot) /* Writes record pointed to by 'all' and boot info pointed to by 'boot' to info file named 'fnam' */ char *fnam; struct allinfo *all; bootinfo boot; { FILE *fp; printf( "\nWriting data to file '%s'\n", fnam ); if ( (fp=fopen(fnam,"w")) == NULL ) { sprintf( err, "Unable to open image file '%s' for writing.\n", fnam ); balk(err); } setmode(fileno(fp), O_BINARY); if (fwrite(boot, 1, BOOTSIZE, fp) != BOOTSIZE) { fclose(fp); sprintf( err, "Unable to write %u bytes of boot sector to image file\n'%s'\n", BOOTSIZE, fnam ); balk(err); } if (fwrite(all, 1, ALLSIZE, fp) != ALLSIZE) { fclose(fp); sprintf( err, "Unable to write %u bytes after boot sector to image file\n'%s'\n", ALLSIZE, fnam ); balk(err); } } void check(name, ofs, num, b1, b2) /* Compares 'num' bytes of 'b1' and 'b2', starting at offset 'ofs'; if any differences, writes error msg. */ char *name; unsigned int ofs, num; char *b1, *b2; { register unsigned int i; /* Start at appropriate offset: */ b1+=ofs; b2+=ofs; printf( "Comparing %u bytes at offset %u (info about %s)\n", num, ofs, name ); for (i=0; itd.ft_tsec != f2->td.ft_tsec) p = msgs[0]; else if (f1->td.ft_min != f2->td.ft_min) p = msgs[1]; else if (f1->td.ft_hour != f2->td.ft_hour) p = msgs[2]; else if (f1->td.ft_day != f2->td.ft_day) p = msgs[3]; else if (f1->td.ft_month != f2->td.ft_month) p = msgs[4]; else if (f1->td.ft_year != f2->td.ft_year) p = msgs[5]; else if (f1->len != f2->len) p = msgs[6]; else if (f1->att != f2->att) p = msgs[7]; else if (f1->chk != f2->chk) p = msgs[8]; if (p!=NULL) { sprintf( err, "'%s' FIELD OF FILE '%s' DOES NOT MATCH IMAGE!\nFILE '%s' MAY BE CORRUPT!\n\a", p, name, name ); balk(err); } } void doclear(s) /* Fills CMOS RAM from offset CMOSSTART to offset CMOSEND with string pointed to by 's' [or CHR(0) if s==NULL] */ char *s; { unsigned char i, ch; if (*s=='\0') printf( "\nClearing %u bytes of CMOS RAM at offset %u\n", CMOSEND-CMOSSTART+1, CMOSSTART ); else printf( "\nFilling %u bytes of CMOS RAM at offset %u with '%s'\n", CMOSEND-CMOSSTART+1, CMOSSTART, s ); for (i=CMOSSTART; i<=CMOSEND; i++) { if ( (ch=*s)!= '\0' ) s++; pokecmos(i,ch); } } void doprint(void) /* Display text stored in unused CMOS RAM */ { unsigned char i, ch; printf( "\nData stored in %u bytes of CMOS RAM at offset %u:\n", CMOSEND-CMOSSTART+1, CMOSSTART ); for(i=CMOSSTART; i<=CMOSEND; i++) { ch=peekcmos(i); printf( isprint(ch) ? "%c" : "[%u]", ch ); } printf( "\n\n" ); } void doread(a_actual, a_image, b_actual, b_image) struct allinfo *a_actual, *a_image; bootinfo b_actual, b_image; { readall( a_actual, b_actual ); readinf( DATAFILE, a_image, b_image ); /* Check boot sector: */ check( "BOOT SECTOR", (unsigned int) ((char*)(b_actual)-(char*)b_actual), BOOTSIZE, (char*)b_actual, (char*)b_image ); /* Check DOS version: */ check( "DOS VERSION", (unsigned int) ((char*)&(a_actual->ver)-(char*)a_actual), sizeof(a_actual->ver), (char*)a_actual, (char*)a_image ); if (IGNORECMOS) printf( "(Ignoring information about unused CMOS RAM)\n" ); else /* Check CMOS RAM: */ check( "CMOS RAM", (unsigned int) ((char*)&(a_actual->cmos)-(char*)a_actual), sizeof(a_actual->cmos), (char*)a_actual, (char*)a_image ); checkfile( CMD, &(a_actual->cmd), &(a_image->cmd) ); checkfile( BIO, &(a_actual->bio), &(a_image->bio) ); checkfile( DOS, &(a_actual->dos), &(a_image->dos) ); checkfile( CHECKUP, &(a_actual->this), &(a_image->this) ); printf( "\nData matches image file--no problem.\n" ); } void dowrite(a_actual, b_actual) struct allinfo *a_actual; bootinfo b_actual; { readall( a_actual, b_actual ); writeall( DATAFILE, a_actual, b_actual ); printf( "Data file successfully written.\n" ); } unsigned int getbps(d) /* Returns number of bytes per sector for drive 'd' [0=A, etc.] */ unsigned char d; { struct fatinfo f; getfat(d+1,&f); /* Add 1 because getfat() expects 1=A, etc */ return ((unsigned int) f.fi_bysec); } void main(argc, argv) int argc; char *argv[]; { int i; struct allinfo all1, all2; bootinfo boot1, boot2; /* Get allocated later on */ printf( "CHECKUP v1.1 (2/25/89) Copyright (c) Kim Moser All Rights Reserved\n" ); if (argc<2 || argc>4) usage(); /* Missing or extra command line params */ DISK = getdisk(); /* Because it's not legal in initialization */ CHECKUP = argv[0]; /* Parse argument(s): */ for (i=3; i<=argc; i++) { if (argv[i-1][0]=='-') /* User is specifying filename */ DATAFILE = argv[i-1]+1; /* +1 to point past '-' */ else { /* User is specifying drive */ if ( !isalpha(DISK=toupper(argv[i-1][0])) ) { /* Invalid drive specification [not 'A'..'Z'] */ usage(); } else /* Valid, so convert to 0..25: */ DISK -= 'A'; } } /* Set drive letter in filenames according to drive: */ CMD[0] = BIO[0] = DOS[0] = DISK+'A'; /* Determine size [in bytes] of a sector in specified drive: */ BOOTSIZE = getbps(DISK); /* Initialize buffers for boot sector images: */ if ( ((boot1 = (bootinfo) calloc(1,BOOTSIZE))==NULL) || ((boot2 = (bootinfo) calloc(1,BOOTSIZE))==NULL) ) { sprintf( err, "Unable to allocate %u bytes.\n", 2*BOOTSIZE ); balk(err); } /* Make sure that if they select the 'I' option, that it's used only in conjunction with the '-R' option: */ if ( ((IGNORECMOS=(toupper(argv[1][2])=='I'))!=0) && (toupper(argv[1][1])!='R') ) usage(); /* Parse options */ switch ( toupper(argv[1][1]) ) { case 'C': /* Clear unused CMOS RAM */ doclear(argv[2]); /* Should point to '\0' if omitted */ break; case 'P': /* Print unused CMOS RAM */ doprint(); break; case 'R': /* Compare info with info file */ doread(&all1, &all2, boot1, boot2); break; case 'W': /* Copy info to info file */ dowrite(&all1, boot1); break; default: /* Invalid command line switch */ usage(); } free(boot1); free(boot2); }