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;
}