www.pudn.com > antinimda.zip > VirusCleaner.cpp


// VirusCleaner.cpp: implementation of the CVirusCleaner class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "VirusCleaner.h" 
 
/* virus identities */ 
#include "../definitions/w32_nimda.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
CVirusCleaner::CVirusCleaner(LPCTSTR root) 
	: m_paused(false), m_scanned(0), m_skipped(0), m_errors(0), m_infected(0), m_deleted(0), m_cleaned(0), m_directories(0), m_velocity(0), m_bytesscanned(0), m_starttime(CTime::GetCurrentTime()), m_read_block_size(0xFFFF) 
{ 
	if (root) Root(root); 
 
	/* TODO: User might want to specify the log filename */ 
	m_logfile.Open("c:\\anti-nimba.log", CFile::modeCreate | CFile::modeWrite); 
} 
 
CVirusCleaner::~CVirusCleaner() 
{ 
	/* close log file if open */ 
	if (m_logfile.m_hFile) 
		m_logfile.Close(); 
} 
 
void CVirusCleaner::Root(LPCTSTR root) 
{ 
	ASSERT(root);  
	if (root) 
		if (root[strlen(root)-1]=='\\') 
			m_root=CString(root,strlen(root)-1); 
		else 
			m_root=root; 
} 
 
bool CVirusCleaner::Start() 
{  
	m_starttime=CTime::GetCurrentTime();  
	if (m_logfile.m_hFile) { 
		CString logentry; 
		logentry.Format("Scan of '%s' started on %s:\r\n", m_root, CTime::GetCurrentTime().Format("%c")); 
		m_logfile.Write((LPCTSTR)logentry,logentry.GetLength()); 
	} 
	return CServiceThread::Start();  
} 
 
double CVirusCleaner::Velocity() const 
{ 
	/* returns files scaned per second (scan speed) */ 
	return ( (double)m_scanned / Duration().GetTotalSeconds() ) * 60; 
} 
 
void CVirusCleaner::run() 
{ 
	/* TODO: I am adding the virus definitions manually here, a better method could be  
			implimented. */ 
	w32_nimda_a a;	m_killers.insert(m_killers.end(), &a); 
	w32_nimda_b b;	m_killers.insert(m_killers.end(), &b); 
	w32_nimda_c c;	m_killers.insert(m_killers.end(), &c); 
		 
	/* build signatures collection */ 
	m_killergraph = BuildVirusGraph(m_killers); 
 
	/* call scan to recurse from root directory */ 
	scandirectory(m_root); 
 
	m_stoptime=CTime::GetCurrentTime(); 
 
	if (m_logfile.m_hFile) { 
		m_logfile.Close(); 
		m_logfile.m_hFile=NULL; 
	} 
} 
 
CVirusCleaner::VirusGraph CVirusCleaner::BuildVirusGraph(const VirusKiller::Set& kset) 
{ 
	VirusGraph graph; 
 
	/* iterate through all killers adding virus killers with specified extensions to virus graph */ 
	int lbrk,brk; 
	CString extensions, ext; 
	for(VirusKiller::Set::const_iterator k=kset.begin(), _k=kset.end(); k!=_k; k++) { 
		/* get extensions string */ 
		extensions=(*k)->Extensions(); 
		lbrk=0; 
 
		while (lbrkExtensions().IsEmpty()) 
				/* add virus killer to virus graph if it is not associated with any particular extension */ 
				(*g).second.insert( (*g).second.end(), (*k) ); 
		} 
	} 
 
	return graph; 
} 
 
bool CVirusCleaner::scandirectory(LPCTSTR root) 
{ 
	WIN32_FIND_DATA find; 
	memset(&find,0,sizeof(WIN32_FIND_DATA)); 
	HANDLE hfind=FindFirstFile(CString(root)+"\\*.*",&find); 
	if (hfind) { 
		/* iterate through all files in the directory */ 
		do { 
			if (find.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) { 
				/* for directories recurse into them */ 
				if ((strcmp(find.cFileName,".")!=0) && (strcmp(find.cFileName,"..")!=0)) 
					scandirectory(CString(root)+'\\'+find.cFileName); 
			} 
			else { 
				/* for files call the virus cleaner */ 
				scanfile(CString(root)+'\\'+find.cFileName); 
			} 
 
			/* wait if scan thread is paused */ 
			while(IsPaused() && !StopPending()) 
				Sleep(10); 
 
		/* get next file */ 
		} while (FindNextFile(hfind,&find) && !StopPending()); 
 
		/* close the find operation */ 
		FindClose(hfind); 
	} 
 
	/* update scan statistics */ 
	m_directories++; 
 
	return hfind!=INVALID_HANDLE_VALUE; 
} 
 
bool CVirusCleaner::scanfile(LPCTSTR file) 
{ 
	FILE* fp; 
 
	try{ 
		/* dont scan this executable */ 
		char fname[1024]; 
		if (GetModuleFileName(NULL,fname,sizeof(fname)) && (stricmp(file,fname)==0)) 
			return true;		 
		 
		/* holds a lost of virus infections found in this file */ 
		VirusKiller::Set infections; 
 
		/* get the files extension */ 
		CString extension(file); 
		int brk=extension.ReverseFind('.'); 
		if (brk) 
			extension=extension.Mid(brk+1);		/* break off extension */ 
		else 
			extension="";						/* assume no extension */ 
		extension.MakeLower(); 
 
		/* get the virus killers associated with this extension */ 
		VirusKiller::Set& killers = ScanAll()? m_killers : m_killergraph[ extension ]; 
		int killers_count = killers.size(); 
 
		if (killers_count) { 
			/* open named file in read-only binary mode */ 
			if (fp=fopen(file,"rb")) { 
 
				/* build a vector of signature pointers */ 
				LPBYTE* signatures = new LPBYTE[ killers_count ]; 
 
				/* fill the signatures vector */ 
				int i=0; 
				for(VirusKiller::Set::const_iterator k=killers.begin(), _k=killers.end(); k!=_k; k++) 
					if ((*k)->AllFiles()) { 
						infections.insert( infections.begin(), (*k) ); 
						signatures[ i++ ] = NULL; 
					} 
					else 
						signatures[ i++ ] = (*k)->Signature(); 
 
				/* find the longest signature */ 
				int max_signature=0, j; 
				for(i=0; imax_signature) max_signature=j; 
 
				/* scan file for virus signature 
						Algorithm: Using double buffers (size of m_read_block_size) as a rolling buffer we will  
						replace the data with a next loaded buffer as each buffer is no longer required. 
				*/ 
				DWORD base=0;		/* base offset of first signature character into rolling buffer */ 
				DWORD offset;		/* offset from base of currently comparing character */ 
				DWORD buffer_size=2 * m_read_block_size;	/* size of our rolling buffer */ 
				DWORD end_base=buffer_size+1;	/* will signal when the scanner is at the end of  
											the file in the rolling buffer */ 
				size_t bytes_read;	/* number of bytes read with last read operation */ 
				 
				/* allocate our rolling buffer */ 
				LPBYTE buffer = (LPBYTE)malloc(buffer_size); 
 
				/* load the initial double buffers */ 
				bytes_read = fread(buffer, 1, m_read_block_size*2, fp); 
				m_bytesscanned+=bytes_read; 
 
				while (base!=end_base) { 
					/* search for signature match at this base address */ 
					for(i=0,offset=0; ibuffer_size) { 
						base=0; 
						if ((end_base>buffer_size) && (bytes_read=fread(&buffer[m_read_block_size], 1, m_read_block_size, fp))buffer_size) && (bytes_read=fread(buffer, 1, m_read_block_size, fp))Clean(file); 
 
	/* TODO: The result should be checked for each iteration so it's previous value is not 
		discarded as it is done now. Not a problem for cases of Nimda virus. 
	*/ 
 
	/* update scan statistics */ 
	if (result&VIRUS_ERR) m_errors++; 
 
	/* append log entry */ 
	if (m_logfile.m_hFile) { 
		CString action; 
		switch (result) { 
			case VIRUS_SKIPPED: action="skipped"; break; 
			case VIRUS_SCANNED: action="scanned"; break; 
			case VIRUS_INFECTED: action="infected"; break; 
			case VIRUS_CLEANED: action="cleaned"; break; 
			case VIRUS_DELETED: action="deleted"; break; 
		default: 
			action="error"; break; 
		} 
		 
		CString logentry; 
		logentry.Format("%s:  %s\r\n", action, file); 
		m_logfile.Write((LPCTSTR)logentry, logentry.GetLength()); 
	} 
 
	return result; 
}