/*
 *	VIRCHECK.C
 *	
 *	This is an attempt at virus detection.  The routine VirCheck should
 *	be called sometime during the boot process, and takes one argument, a
 *	handle to the application instance.  The ComplainAndQuit() call
 *	should be replaced with code appropriate to your application.  It is
 *	recommended that this bring up a dialog with an error message, and
 *	give the user the option of continuing (defaults to terminating).  If
 *	the user chooses to terminate (or if the option is not given),
 *	ComplainAndQuit() should clean up anything that has been done so far,
 *	and exit.
 *	
 *	WARNING!! Do not change WHashGood at all!!
 *	
 *	WARNING!! WHashGood must be a near procedure, compiled native.
 *	
 *	WARNING!! This file must not be compiled using *any* compiler
 *				optimizations!
 */

/*
 *	NOTE: this code was oh so slightly modified to return an error code
 *	instead of making some callback in case of trouble. Should this file
 *	ever be updated with a new and improved algorithm, be sure to twiddle
 *	it appropriately!
 *	
 *	NOTE: this code has been passed thru the GNU indent utility to make
 *	it more readable. Parms used:
 *	
 *	indent -bad -bap -sob -bl -bli0 -nce -nps -psl -TEC -i4 -lp -ip0 \
 *			vircheck.c out.c
 *	
 *	Unfortunately, the tool thinks TABs are 8 characters big. So, after
 *	indenting, change all single TABs to double TABs and replace all
 *	groups of 4 spaces with a single TAB.
 */

#include <slingsho.h>
#include <ec.h>
#include <demilayr.h>


unsigned near	WHashGood(void);
unsigned		WHash(unsigned, BYTE[], int);
EC				EcVirCheck(HANDLE);

/*
 *	EXE header format definitions.  Lifted from linker.
 */
#define EMAGIC		0x5A4D				/* Old magic number */
#define ERES2WDS	0x000A				/* No. of reserved words in e_res2 */

struct exe_hdr							/* DOS 1, 2, 3 .EXE header */
{
	unsigned short	e_magic;			/* Magic number */
	unsigned short	e_cblp;				/* Bytes on last page of file */
	unsigned short	e_cp;				/* Pages in file */
	unsigned short	e_crlc;				/* Relocations */
	unsigned short	e_cparhdr;			/* Size of header in paragraphs */
	unsigned short	e_minalloc;			/* Minimum extra paragraphs needed */
	unsigned short	e_maxalloc;			/* Maximum extra paragraphs needed */
	unsigned short	e_ss;				/* Initial (relative) SS value */
	unsigned short	e_sp;				/* Initial SP value */
	unsigned short	e_csum;				/* Checksum */
	unsigned short	e_ip;				/* Initial IP value */
	unsigned short	e_cs;				/* Initial (relative) CS value */
	unsigned short	e_lfarlc;			/* File address of relocation table */
	unsigned short	e_ovno;				/* Overlay number */
	unsigned long	e_sym_tab;			/* offset of symbol table file */
	unsigned short	e_flags;			/* old exe header flags	 */
	unsigned short	e_res;				/* Reserved words */
	unsigned short	e_oemid;			/* OEM identifier (for e_oeminfo) */
	unsigned short	e_oeminfo;			/* OEM information; e_oemid specific */
	unsigned short	e_res2[ERES2WDS];	/* Reserved words */
	long			e_lfanew;			/* File address of new exe header */
};

/*
 *	NEW EXE format definitions. Lifted from linker
 */

#define NEMAGIC		0x454E				/* New magic number */
#define NERESBYTES	8					/* Eight bytes reserved (now) */
#define NECRC		8					/* Offset into new header of NE_CRC */

struct new_exe							/* New .EXE header */
{
	unsigned short	ne_magic;			/* Magic number NE_MAGIC */
	unsigned char	ne_ver;				/* Version number */
	unsigned char	ne_rev;				/* Revision number */
	unsigned short	ne_enttab;			/* Offset of Entry Table */
	unsigned short	ne_cbenttab;		/* Number of bytes in Entry Table */
	long			ne_crc;				/* Checksum of whole file */
	unsigned short	ne_flags;			/* Flag word */
	unsigned short	ne_autodata;		/* Automatic data segment number */
	unsigned short	ne_heap;			/* Initial heap allocation */
	unsigned short	ne_stack;			/* Initial stack allocation */
	long			ne_csip;			/* Initial CS:IP setting */
	long			ne_sssp;			/* Initial SS:SP setting */
	unsigned short	ne_cseg;			/* Count of file segments */
	unsigned short	ne_cmod;			/* Entries in Module Reference Table */
	unsigned short	ne_cbnrestab;		/* Size of non-resident name table */
	unsigned short	ne_segtab;			/* Offset of Segment Table */
	unsigned short	ne_rsrctab;			/* Offset of Resource Table */
	unsigned short	ne_restab;			/* Offset of resident name table */
	unsigned short	ne_modtab;			/* Offset of Module Reference Table */
	unsigned short	ne_imptab;			/* Offset of Imported Names Table */
	long			ne_nrestab;			/* Offset of Non-resident Names Table */
	unsigned short	ne_cmovent;			/* Count of movable entries */
	unsigned short	ne_align;			/* Segment alignment shift count */
	unsigned short	ne_cres;			/* Count of resource entries */
	unsigned char	ne_exetyp;			/* Target operating system */
	unsigned char	ne_flagsothers;		/* Other .EXE flags */
	char			ne_res[NERESBYTES];	/* Pad structure to 64 bytes */
};


/*
 *	WHashGood()
 *	
 *	This returns the correct hash value.
 *	
 *	WARNING!! This routine must not be altered in ANY way.  It gets
 *	patched and/or rewritten by VIRPATCH!!
 */
#pragma check_stack(off)
unsigned near 
WHashGood()
{
	return (0x1234);
}
#pragma check_stack()

/*
 *	WHash(wHash, rgb, cb)
 *	
 *	Update hash value to account for cb new bytes pointed to by rgb. Old
 *	hash value is wHash; returns new hash value.
 *	
 *	We do the hash on a word basis; the hash function is a simple rotate
 *	and add.
 */
unsigned 
WHash(wHash, rgb, cb)
unsigned		wHash;
BYTE			rgb[];
int				cb;
{
	while (cb > 1)
	{
		wHash = (wHash << 3) + (wHash >> 13) + *((short *) rgb)++;
		cb -= 2;
	}
	if (cb != 0)
		wHash = (wHash << 3) + (wHash >> 13) + *rgb;
	return (wHash);
}


/*
 *	EcVirCheck(hinst)
 *	
 *	This is the main virus detection routine.  It should be called during
 *	boot, with a handle to the application instance. The detection method
 *	used is to hash the EXE headers; this hash value will change if the
 *	number or type of segments change, or if their length changes.
 */
EC 
EcVirCheck(hinst)
HANDLE			hinst;
{
	int				fh;
	unsigned		wHash;
	unsigned		cb, cbT;
	long			lPos;
	char			sz[256];
	BYTE			rgb[512];
#define pehdr ((struct exe_hdr *)rgb)
#define pnex ((struct new_exe *)rgb)

/*
 *	First we have to get a handle to the executable file. Unfortunately,
 *	although Windows already has this file open, there's no way to use
 *	its handle.  Instead we have to reopen the file.
 */
	GetModuleFileName(hinst, (char far *) sz, 256);
	if ((fh = OpenFile((LPSTR) sz, (LPOFSTRUCT) rgb, OF_READ)) == -1)
	{
/*
 *		We can't open the file.  This should never happen; if it does, it
 *		means we're in a weird state, and probably did something wrong in
 *		this code.  We'll just say everything is OK, and continue the
 *		boot.
 */
		return ecNone;
	}
	/* Read old header */
	if (_lread(fh, (LPSTR) rgb, sizeof(struct exe_hdr)) != sizeof(struct exe_hdr) ||
		pehdr->e_magic != EMAGIC)
		goto Corrupted;
	/* Hash old header */
	wHash = WHash(0, rgb, sizeof(struct exe_hdr));
	lPos = pehdr->e_lfanew;
	/* Read new header (and some more) */
	if (lPos == 0 || _llseek(fh, lPos, 0) != lPos ||
		_lread(fh, (LPSTR) rgb, 512) != 512 || pnex->ne_magic != NEMAGIC)
		goto Corrupted;
/*
 *	Figure out size of total header; nonresident table is last part of
 *	header.
 */
	cb = (unsigned) (pnex->ne_nrestab - lPos) + pnex->ne_cbnrestab;
	/* Do hash on buffer basis */
	while (cb > 512)
	{
		/* Hash this buffer */
		wHash = WHash(wHash, rgb, 512);
		cb -= 512;
		cbT = (cb > 512 ? 512 : cb);
		/* and read in next */
		if (_lread(fh, (LPSTR) rgb, cbT) != cbT)
			goto Corrupted;
	}
	/* Update hash for final partial buffer, and compare with good value. */
	if (WHash(wHash, rgb, cb) != WHashGood())
	{
Corrupted:
/*
 *		We've got an error reading the file or, more likely, a hash
 *		mismatch.  Close the file, give an error, and quit.
 */
		_lclose(fh);
/*
 *		CHANGE THE FOLLOWING LINE TO CODE APPROPRIATE TO YOUR
 *		APPLICATION!!  This should be replaced with code giving an error
 *		message (such as "Application file is corrupted").  It is
 *		recommended that this bring up a dialog with an error message,
 *		and give the user the option of continuing (defaults to
 *		terminating).  If the user chooses to terminate (or if the option
 *		is not given), ComplainAndQuit() should clean up anything that
 *		has been done so far, and exit.
 *	
 *		NOTE: line has already been made suitable
 */
		return ecInfected;
	}
	/* Everything's OK. Just close the file, and continue. */
	_lclose(fh);
#undef pehdr
#undef pnex
	return ecNone;
}
