VirusCleaner.cpp
上传用户:leon2013
上传日期:2007-01-10
资源大小:186k
文件大小:11k
源码类别:

杀毒

开发平台:

Visual C++

  1. // VirusCleaner.cpp: implementation of the CVirusCleaner class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "VirusCleaner.h"
  6. /* virus identities */
  7. #include "../definitions/w32_nimda.h"
  8. #ifdef _DEBUG
  9. #undef THIS_FILE
  10. static char THIS_FILE[]=__FILE__;
  11. #define new DEBUG_NEW
  12. #endif
  13. //////////////////////////////////////////////////////////////////////
  14. // Construction/Destruction
  15. //////////////////////////////////////////////////////////////////////
  16. CVirusCleaner::CVirusCleaner(LPCTSTR root)
  17. : 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)
  18. {
  19. if (root) Root(root);
  20. /* TODO: User might want to specify the log filename */
  21. m_logfile.Open("c:\anti-nimba.log", CFile::modeCreate | CFile::modeWrite);
  22. }
  23. CVirusCleaner::~CVirusCleaner()
  24. {
  25. /* close log file if open */
  26. if (m_logfile.m_hFile)
  27. m_logfile.Close();
  28. }
  29. void CVirusCleaner::Root(LPCTSTR root)
  30. {
  31. ASSERT(root); 
  32. if (root)
  33. if (root[strlen(root)-1]=='\')
  34. m_root=CString(root,strlen(root)-1);
  35. else
  36. m_root=root;
  37. }
  38. bool CVirusCleaner::Start()
  39. m_starttime=CTime::GetCurrentTime(); 
  40. if (m_logfile.m_hFile) {
  41. CString logentry;
  42. logentry.Format("Scan of '%s' started on %s:rn", m_root, CTime::GetCurrentTime().Format("%c"));
  43. m_logfile.Write((LPCTSTR)logentry,logentry.GetLength());
  44. }
  45. return CServiceThread::Start(); 
  46. }
  47. double CVirusCleaner::Velocity() const
  48. {
  49. /* returns files scaned per second (scan speed) */
  50. return ( (double)m_scanned / Duration().GetTotalSeconds() ) * 60;
  51. }
  52. void CVirusCleaner::run()
  53. {
  54. /* TODO: I am adding the virus definitions manually here, a better method could be 
  55. implimented. */
  56. w32_nimda_a a; m_killers.insert(m_killers.end(), &a);
  57. w32_nimda_b b; m_killers.insert(m_killers.end(), &b);
  58. w32_nimda_c c; m_killers.insert(m_killers.end(), &c);
  59. /* build signatures collection */
  60. m_killergraph = BuildVirusGraph(m_killers);
  61. /* call scan to recurse from root directory */
  62. scandirectory(m_root);
  63. m_stoptime=CTime::GetCurrentTime();
  64. if (m_logfile.m_hFile) {
  65. m_logfile.Close();
  66. m_logfile.m_hFile=NULL;
  67. }
  68. }
  69. CVirusCleaner::VirusGraph CVirusCleaner::BuildVirusGraph(const VirusKiller::Set& kset)
  70. {
  71. VirusGraph graph;
  72. /* iterate through all killers adding virus killers with specified extensions to virus graph */
  73. int lbrk,brk;
  74. CString extensions, ext;
  75. for(VirusKiller::Set::const_iterator k=kset.begin(), _k=kset.end(); k!=_k; k++) {
  76. /* get extensions string */
  77. extensions=(*k)->Extensions();
  78. lbrk=0;
  79. while (lbrk<extensions.GetLength()) {
  80. /* break string into extensions */
  81. if ((brk=extensions.Find(';',lbrk))==-1) brk=extensions.GetLength();
  82. /* add virus killer to graph under specified extension */
  83. ext=extensions.Mid(lbrk,brk-lbrk);
  84. ext.MakeLower();
  85. graph[ ext ].insert( graph[ ext ].end(), (*k) );
  86. /* next extension */
  87. lbrk=brk+1;
  88. }
  89. }
  90. /* add all virus killers without extensions to all known extensions */
  91. for(k=kset.begin(), _k=kset.end(); k!=_k; k++)  {
  92. for(VirusGraph::iterator g=graph.begin(), _g=graph.end(); g!=_g; g++) {
  93. if ((*k)->Extensions().IsEmpty())
  94. /* add virus killer to virus graph if it is not associated with any particular extension */
  95. (*g).second.insert( (*g).second.end(), (*k) );
  96. }
  97. }
  98. return graph;
  99. }
  100. bool CVirusCleaner::scandirectory(LPCTSTR root)
  101. {
  102. WIN32_FIND_DATA find;
  103. memset(&find,0,sizeof(WIN32_FIND_DATA));
  104. HANDLE hfind=FindFirstFile(CString(root)+"\*.*",&find);
  105. if (hfind) {
  106. /* iterate through all files in the directory */
  107. do {
  108. if (find.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) {
  109. /* for directories recurse into them */
  110. if ((strcmp(find.cFileName,".")!=0) && (strcmp(find.cFileName,"..")!=0))
  111. scandirectory(CString(root)+'\'+find.cFileName);
  112. }
  113. else {
  114. /* for files call the virus cleaner */
  115. scanfile(CString(root)+'\'+find.cFileName);
  116. }
  117. /* wait if scan thread is paused */
  118. while(IsPaused() && !StopPending())
  119. Sleep(10);
  120. /* get next file */
  121. } while (FindNextFile(hfind,&find) && !StopPending());
  122. /* close the find operation */
  123. FindClose(hfind);
  124. }
  125. /* update scan statistics */
  126. m_directories++;
  127. return hfind!=INVALID_HANDLE_VALUE;
  128. }
  129. bool CVirusCleaner::scanfile(LPCTSTR file)
  130. {
  131. FILE* fp;
  132. try{
  133. /* dont scan this executable */
  134. char fname[1024];
  135. if (GetModuleFileName(NULL,fname,sizeof(fname)) && (stricmp(file,fname)==0))
  136. return true;
  137. /* holds a lost of virus infections found in this file */
  138. VirusKiller::Set infections;
  139. /* get the files extension */
  140. CString extension(file);
  141. int brk=extension.ReverseFind('.');
  142. if (brk)
  143. extension=extension.Mid(brk+1); /* break off extension */
  144. else
  145. extension=""; /* assume no extension */
  146. extension.MakeLower();
  147. /* get the virus killers associated with this extension */
  148. VirusKiller::Set& killers = ScanAll()? m_killers : m_killergraph[ extension ];
  149. int killers_count = killers.size();
  150. if (killers_count) {
  151. /* open named file in read-only binary mode */
  152. if (fp=fopen(file,"rb")) {
  153. /* build a vector of signature pointers */
  154. LPBYTE* signatures = new LPBYTE[ killers_count ];
  155. /* fill the signatures vector */
  156. int i=0;
  157. for(VirusKiller::Set::const_iterator k=killers.begin(), _k=killers.end(); k!=_k; k++)
  158. if ((*k)->AllFiles()) {
  159. infections.insert( infections.begin(), (*k) );
  160. signatures[ i++ ] = NULL;
  161. }
  162. else
  163. signatures[ i++ ] = (*k)->Signature();
  164. /* find the longest signature */
  165. int max_signature=0, j;
  166. for(i=0; i<killers_count; i++)
  167. if (signatures[i] && (j=strlen((LPCTSTR)signatures[i]))>max_signature) max_signature=j;
  168. /* scan file for virus signature
  169. Algorithm: Using double buffers (size of m_read_block_size) as a rolling buffer we will 
  170. replace the data with a next loaded buffer as each buffer is no longer required.
  171. */
  172. DWORD base=0; /* base offset of first signature character into rolling buffer */
  173. DWORD offset; /* offset from base of currently comparing character */
  174. DWORD buffer_size=2 * m_read_block_size; /* size of our rolling buffer */
  175. DWORD end_base=buffer_size+1; /* will signal when the scanner is at the end of 
  176. the file in the rolling buffer */
  177. size_t bytes_read; /* number of bytes read with last read operation */
  178. /* allocate our rolling buffer */
  179. LPBYTE buffer = (LPBYTE)malloc(buffer_size);
  180. /* load the initial double buffers */
  181. bytes_read = fread(buffer, 1, m_read_block_size*2, fp);
  182. m_bytesscanned+=bytes_read;
  183. while (base!=end_base) {
  184. /* search for signature match at this base address */
  185. for(i=0,offset=0; i<killers_count; i++,offset=0)
  186. if (signatures[i]) {
  187. /* continue comparing signature character to file character 
  188. until not equal or encountered null in signature */
  189. while ( (signatures[i][offset]) && (signatures[i][offset] == *(buffer + (base+offset)%buffer_size)) )
  190. offset++;
  191. /* if we have reached the end of signature string we have found a match */
  192. if (!signatures[i][offset]) {
  193. int j=0;
  194. /* find the virus killer associated with this sequence */
  195. for(VirusKiller::Set::iterator k=killers.begin(), _k=killers.end(); k!=_k; k++, j++)
  196. if (j==i) {
  197. /* add virus to list of infections */
  198. infections.insert( infections.end(), (*k) );
  199. /* TO DO: we can ignore further occurances of virus sequence */
  200. signatures[i]=NULL;
  201. break;
  202. }
  203. }
  204. }
  205. /* increase base address of character we are scanning in the rolling buffer */
  206. base++;
  207. /* load back buffer if scanned */
  208. if (base>buffer_size) {
  209. base=0;
  210. if ((end_base>buffer_size) && (bytes_read=fread(&buffer[m_read_block_size], 1, m_read_block_size, fp))<m_read_block_size) {
  211. end_base=m_read_block_size+bytes_read;
  212. /* update scan statistics */
  213. m_bytesscanned+=bytes_read;
  214. }
  215. /* load front buffer if scanned */
  216. else if (base==m_read_block_size) {
  217. if ((end_base>buffer_size) && (bytes_read=fread(buffer, 1, m_read_block_size, fp))<m_read_block_size) {
  218. end_base=bytes_read;
  219.  /* update scan statistics */
  220. m_bytesscanned+=bytes_read;
  221. }
  222. }
  223. }
  224. /* free our rolling buffer */
  225. free(buffer);
  226. /* free the signature vector */
  227. delete[] signatures;
  228. /* close the file */
  229. fclose(fp);
  230. /* update scan statistics */
  231. m_lastfile=file;
  232. m_scanned++;
  233. /* check to see if any sequences where found */
  234. if (infections.size()) {
  235. /* clean file of infected viruses */
  236. VirusKiller::SCANRESULT result;
  237. result=cleanfile(file, infections);
  238. /* update scan statistics */
  239. if (result&(VIRUS_CLEANED|VIRUS_DELETED|VIRUS_SKIPPED)) { result|=VIRUS_INFECTED; m_infected++; }
  240. if (result&VIRUS_CLEANED) m_cleaned++;
  241. if (result&VIRUS_DELETED) m_deleted++;
  242. if (result&VIRUS_SKIPPED) m_skipped++;
  243. /* notify children */
  244. OnInfected(file, result);
  245. }
  246. return true;
  247. }
  248. else {
  249. /* error opening file */
  250. m_errors++;
  251. return false;
  252. }
  253. }
  254. else  {
  255. /* no associated virus killers : skip file */
  256. m_skipped++;
  257. return true;
  258. }
  259. } catch(...) {
  260. m_errors++;
  261. return false;
  262. }
  263. }
  264. VirusKiller::SCANRESULT CVirusCleaner::cleanfile(LPCTSTR file, VirusKiller::Set& infections)
  265. {
  266. /* call the virus killers clean method */
  267. VirusKiller::SCANRESULT result;
  268. for(VirusKiller::Set::iterator k=infections.begin(), _k=infections.end(); k!=_k; k++)
  269. result = (*k)->Clean(file);
  270. /* TODO: The result should be checked for each iteration so it's previous value is not
  271. discarded as it is done now. Not a problem for cases of Nimda virus.
  272. */
  273. /* update scan statistics */
  274. if (result&VIRUS_ERR) m_errors++;
  275. /* append log entry */
  276. if (m_logfile.m_hFile) {
  277. CString action;
  278. switch (result) {
  279. case VIRUS_SKIPPED: action="skipped"; break;
  280. case VIRUS_SCANNED: action="scanned"; break;
  281. case VIRUS_INFECTED: action="infected"; break;
  282. case VIRUS_CLEANED: action="cleaned"; break;
  283. case VIRUS_DELETED: action="deleted"; break;
  284. default:
  285. action="error"; break;
  286. }
  287. CString logentry;
  288. logentry.Format("%s:  %srn", action, file);
  289. m_logfile.Write((LPCTSTR)logentry, logentry.GetLength());
  290. }
  291. return result;
  292. }