Browse code

extract and scan PE files embedded into other executables or fake zip files generated by some worms

git-svn: trunk@2934

Tomasz Kojm authored on 2007/03/13 06:31:40
Showing 9 changed files
... ...
@@ -1,3 +1,8 @@
1
+Mon Mar 12 20:31:07 CET 2007 (tk)
2
+---------------------------------
3
+  * libclamav: extract and scan PE files embedded into other executables or
4
+	       fake zip files generated by some worms
5
+
1 6
 Mon Mar 12 19:55:31 CET 2007 (acab)
2 7
 -----------------------------------
3 8
   * libclamav/packlibs.h: Removed stale EXPERIMENTAL ifdef
... ...
@@ -21,6 +21,7 @@
21 21
 #define __EXECS_H
22 22
 
23 23
 #include "cltypes.h"
24
+#include <sys/types.h>
24 25
 
25 26
 struct cli_exe_section {
26 27
     uint32_t rva;
... ...
@@ -34,6 +35,7 @@ struct cli_exe_section {
34 34
 struct cli_exe_info {
35 35
     uint32_t ep;
36 36
     uint16_t nsections;
37
+    off_t offset;
37 38
     struct cli_exe_section *section;
38 39
 };
39 40
 
... ...
@@ -194,6 +194,8 @@ static const struct cli_smagic_s cli_smagic[] = {
194 194
     {"504b0304", "ZIP-SFX", CL_TYPE_ZIPSFX},
195 195
     {"4d534346", "CAB-SFX", CL_TYPE_CABSFX},
196 196
 
197
+    {"4d5a{180-300}50450000", "PE", CL_TYPE_MSEXE},
198
+
197 199
     {NULL,  NULL,   CL_TYPE_UNKNOWN_DATA}
198 200
 };
199 201
 
... ...
@@ -25,7 +25,7 @@
25 25
 
26 26
 #define MAGIC_BUFFER_SIZE 256
27 27
 #define CL_TYPENO 500
28
-#define SFX_MAX_TESTS 10
28
+#define MAX_EMBEDDED_OBJ 10
29 29
 
30 30
 typedef enum {
31 31
     CL_TYPE_UNKNOWN_TEXT = CL_TYPENO,
... ...
@@ -314,10 +314,18 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in
314 314
     if(!partsigs)
315 315
 	return CL_SUCCESS;
316 316
 
317
+    data->inioff = (off_t *) cli_malloc(partsigs * sizeof(off_t));
318
+    if(!data->inioff) {
319
+	cli_errmsg("cli_ac_init(): unable to cli_malloc(%u)\n", partsigs * sizeof(off_t));
320
+	return CL_EMEM;
321
+    }
322
+    memset(data->inioff, -1, partsigs * sizeof(off_t));
323
+
317 324
     data->partcnt = (unsigned int *) cli_calloc(partsigs, sizeof(unsigned int));
318 325
 
319 326
     if(!data->partcnt) {
320 327
 	cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(unsigned int));
328
+	free(data->inioff);
321 329
 	return CL_EMEM;
322 330
     }
323 331
 
... ...
@@ -325,6 +333,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in
325 325
 
326 326
     if(!data->offcnt) {
327 327
 	cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(uint8_t));
328
+	free(data->inioff);
328 329
 	free(data->partcnt);
329 330
 	return CL_EMEM;
330 331
     }
... ...
@@ -333,6 +342,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in
333 333
 
334 334
     if(!data->offidx) {
335 335
 	cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(uint8_t));
336
+	free(data->inioff);
336 337
 	free(data->partcnt);
337 338
 	free(data->offcnt);
338 339
 	return CL_EMEM;
... ...
@@ -342,6 +352,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in
342 342
 
343 343
     if(!data->maxshift) {
344 344
 	cli_errmsg("cli_ac_init(): unable to cli_malloc(%u)\n", partsigs * sizeof(int));
345
+	free(data->inioff);
345 346
 	free(data->partcnt);
346 347
 	free(data->offcnt);
347 348
 	free(data->offidx);
... ...
@@ -354,6 +365,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in
354 354
 
355 355
     if(!data->partoff) {
356 356
 	cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(unsigned int));
357
+	free(data->inioff);
357 358
 	free(data->partcnt);
358 359
 	free(data->offcnt);
359 360
 	free(data->offidx);
... ...
@@ -374,6 +386,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in
374 374
 		free(data->partoff[j]);
375 375
 
376 376
 	    free(data->partoff);
377
+	    free(data->inioff);
377 378
 	    free(data->partcnt);
378 379
 	    free(data->offcnt);
379 380
 	    free(data->offidx);
... ...
@@ -392,6 +405,7 @@ void cli_ac_freedata(struct cli_ac_data *data)
392 392
 
393 393
 
394 394
     if(data && data->partsigs) {
395
+	free(data->inioff);
395 396
 	free(data->partcnt);
396 397
 	free(data->offcnt);
397 398
 	free(data->offidx);
... ...
@@ -446,6 +460,9 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char
446 446
 
447 447
 		    if(pt->sigid) { /* it's a partial signature */
448 448
 
449
+			if(pt->partno == 1)
450
+			    mdata->inioff[pt->sigid - 1] = curroff;
451
+
449 452
 			if(mdata->partcnt[pt->sigid - 1] + 1 == pt->partno) {
450 453
 			    offnum = mdata->offcnt[pt->sigid - 1];
451 454
 			    if(offnum < AC_DEFAULT_TRACKLEN) {
... ...
@@ -491,10 +508,10 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char
491 491
 				if(++mdata->partcnt[pt->sigid - 1] + 1 == pt->parts) {
492 492
 				    if(pt->type) {
493 493
 					if(otfrec) {
494
-					    if(pt->type > type || pt->type >= CL_TYPE_SFX) {
495
-						cli_dbgmsg("Matched signature for file type %s\n", pt->virname);
494
+					    if(pt->type > type || pt->type >= CL_TYPE_SFX || pt->type == CL_TYPE_MSEXE) {
495
+						cli_dbgmsg("Matched signature for file type %s at %u\n", pt->virname, (unsigned int) mdata->inioff[pt->sigid - 1]);
496 496
 						type = pt->type;
497
-						if(ftoffset && (!*ftoffset || (*ftoffset)->cnt < SFX_MAX_TESTS) && ftype == CL_TYPE_MSEXE && type >= CL_TYPE_SFX) {
497
+						if(ftoffset && (!*ftoffset || (*ftoffset)->cnt < MAX_EMBEDDED_OBJ) && ((ftype == CL_TYPE_MSEXE && type >= CL_TYPE_SFX) || ((ftype == CL_TYPE_MSEXE || ftype == CL_TYPE_ZIP) && type == CL_TYPE_MSEXE)))  {
498 498
 						    if(!(tnode = cli_calloc(1, sizeof(struct cli_matched_type)))) {
499 499
 							cli_errmsg("cli_ac_scanbuff(): Can't allocate memory for new type node\n");
500 500
 							if(info.exeinfo.section)
... ...
@@ -503,7 +520,7 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char
503 503
 						    }
504 504
 
505 505
 						    tnode->type = type;
506
-						    tnode->offset = -1; /* we don't remember the offset of the first part */
506
+						    tnode->offset = mdata->inioff[pt->sigid - 1];
507 507
 
508 508
 						    if(*ftoffset)
509 509
 							tnode->cnt = (*ftoffset)->cnt + 1;
... ...
@@ -530,10 +547,10 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char
530 530
 		    } else { /* old type signature */
531 531
 			if(pt->type) {
532 532
 			    if(otfrec) {
533
-				if(pt->type > type || pt->type >= CL_TYPE_SFX) {
533
+				if(pt->type > type || pt->type >= CL_TYPE_SFX || pt->type == CL_TYPE_MSEXE) {
534 534
 				    cli_dbgmsg("Matched signature for file type %s at %u\n", pt->virname, curroff);
535 535
 				    type = pt->type;
536
-				    if(ftoffset && (!*ftoffset ||(*ftoffset)->cnt < SFX_MAX_TESTS) && ftype == CL_TYPE_MSEXE && type >= CL_TYPE_SFX) {
536
+				    if(ftoffset && (!*ftoffset || (*ftoffset)->cnt < MAX_EMBEDDED_OBJ) && ((ftype == CL_TYPE_MSEXE && type >= CL_TYPE_SFX) || ((ftype == CL_TYPE_MSEXE || ftype == CL_TYPE_ZIP) && type == CL_TYPE_MSEXE)))  {
537 537
 					if(!(tnode = cli_calloc(1, sizeof(struct cli_matched_type)))) {
538 538
 					    cli_errmsg("cli_ac_scanbuff(): Can't allocate memory for new type node\n");
539 539
 					    if(info.exeinfo.section)
... ...
@@ -20,6 +20,8 @@
20 20
 #ifndef __MATCHER_AC_H
21 21
 #define __MATCHER_AC_H
22 22
 
23
+#include <sys/types.h>
24
+
23 25
 #include "clamav.h"
24 26
 #include "matcher.h"
25 27
 #include "filetypes.h"
... ...
@@ -30,6 +32,7 @@
30 30
 
31 31
 struct cli_ac_data {
32 32
     unsigned int partsigs;
33
+    off_t *inioff;
33 34
     unsigned int *partcnt;
34 35
     unsigned int **partoff;
35 36
     uint8_t *offcnt;
... ...
@@ -272,7 +272,7 @@ int cli_validatesig(cli_file_t ftype, const char *offstr, off_t fileoff, struct
272 272
 	}
273 273
 
274 274
 	if(maxshift) {
275
-	    if((fileoff < offset) || (fileoff > offset + maxshift)) {
275
+	    if((fileoff < offset) || (fileoff > offset + (off_t) maxshift)) {
276 276
 		cli_dbgmsg("Signature offset: %lu, expected: [%lu..%lu] (%s)\n", fileoff, offset, offset + maxshift, virname);
277 277
 		return 0;
278 278
 	    }
... ...
@@ -2819,7 +2819,7 @@ int cli_peheader(int desc, struct cli_exe_info *peinfo)
2819 2819
 	return -1;
2820 2820
     }
2821 2821
 
2822
-    fsize = sb.st_size;
2822
+    fsize = sb.st_size - peinfo->offset;
2823 2823
 
2824 2824
     if(cli_readn(desc, &e_magic, sizeof(e_magic)) != sizeof(e_magic)) {
2825 2825
 	cli_dbgmsg("Can't read DOS signature\n");
... ...
@@ -2845,7 +2845,7 @@ int cli_peheader(int desc, struct cli_exe_info *peinfo)
2845 2845
 	return -1;
2846 2846
     }
2847 2847
 
2848
-    if(lseek(desc, e_lfanew, SEEK_SET) < 0) {
2848
+    if(lseek(desc, peinfo->offset + e_lfanew, SEEK_SET) < 0) {
2849 2849
 	/* probably not a PE file */
2850 2850
 	cli_dbgmsg("Can't lseek to e_lfanew\n");
2851 2851
 	return -1;
... ...
@@ -1680,17 +1680,84 @@ static int cli_scanmail(int desc, cli_ctx *ctx)
1680 1680
     return ret;
1681 1681
 }
1682 1682
 
1683
+static int cli_scanembpe(int desc, cli_ctx *ctx)
1684
+{
1685
+	int fd, bytes, ret = CL_CLEAN;
1686
+	unsigned long int size = 0;
1687
+	char buff[512];
1688
+	char *tmpname;
1689
+
1690
+
1691
+    tmpname = cli_gentemp(NULL);
1692
+    if(!tmpname)
1693
+	return CL_EMEM;
1694
+
1695
+    if((fd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
1696
+	cli_errmsg("cli_scanembpe: Can't create file %s\n", tmpname);
1697
+	free(tmpname);
1698
+	return CL_EIO;
1699
+    }
1700
+
1701
+    while((bytes = read(desc, buff, sizeof(buff))) > 0) {
1702
+	size += bytes;
1703
+
1704
+	if(ctx->limits && ctx->limits->maxfilesize && (size + sizeof(buff) > ctx->limits->maxfilesize)) {
1705
+	    cli_dbgmsg("cli_scanembpe: Size exceeded (stopped at %lu, max: %lu)\n", size, ctx->limits->maxfilesize);
1706
+	    /* BLOCKMAX should be ignored here */
1707
+	    break;
1708
+	}
1709
+
1710
+	if(cli_writen(fd, buff, bytes) != bytes) {
1711
+	    cli_dbgmsg("cli_scanembpe: Can't write to temporary file\n");
1712
+	    close(fd);
1713
+	    if(!cli_leavetemps_flag)
1714
+		unlink(tmpname);
1715
+	    free(tmpname);	
1716
+	    return CL_EIO;
1717
+	}
1718
+    }
1719
+
1720
+    if(fsync(fd) == -1) {
1721
+	cli_dbgmsg("cli_scanembpe: Can't synchronise descriptor %d\n", fd);
1722
+	close(fd);
1723
+	if(!cli_leavetemps_flag)
1724
+	    unlink(tmpname);
1725
+	free(tmpname);	
1726
+	return CL_EFSYNC;
1727
+    }
1728
+
1729
+    lseek(fd, 0, SEEK_SET);
1730
+    if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS) {
1731
+	cli_dbgmsg("cli_scanembpe: Infected with %s\n", *ctx->virname);
1732
+	close(fd);
1733
+	if(!cli_leavetemps_flag)
1734
+	    unlink(tmpname);
1735
+	free(tmpname);	
1736
+	return CL_VIRUS;
1737
+    }
1738
+
1739
+    close(fd);
1740
+    if(!cli_leavetemps_flag)
1741
+	unlink(tmpname);
1742
+    free(tmpname);
1743
+
1744
+    /* intentionally ignore possible errors from cli_magic_scandesc */
1745
+    return CL_CLEAN;
1746
+}
1747
+
1683 1748
 static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type)
1684 1749
 {
1685 1750
 	int ret = CL_CLEAN, nret = CL_CLEAN;
1686 1751
 	unsigned short ftrec;
1687 1752
 	struct cli_matched_type *ftoffset = NULL, *fpt;
1688 1753
 	uint32_t lastzip, lastrar;
1754
+	struct cli_exe_info peinfo;
1689 1755
 
1690 1756
 
1691 1757
     switch(type) {
1692 1758
 	case CL_TYPE_UNKNOWN_TEXT:
1693 1759
 	case CL_TYPE_MSEXE:
1760
+	case CL_TYPE_ZIP:
1694 1761
 	    ftrec = 1;
1695 1762
 	    break;
1696 1763
 	default:
... ...
@@ -1705,13 +1772,17 @@ static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type)
1705 1705
     ret = cli_scandesc(desc, ctx, ftrec, type, 0, &ftoffset);
1706 1706
 
1707 1707
     if(ret >= CL_TYPENO) {
1708
-	lseek(desc, 0, SEEK_SET);
1709 1708
 
1710
-	nret = cli_scandesc(desc, ctx, 0, ret, 1, NULL);
1711
-	if(nret == CL_VIRUS)
1712
-	    cli_dbgmsg("%s found in descriptor %d when scanning file type %u\n", *ctx->virname, desc, ret);
1709
+	if(ret < CL_TYPE_SFX && ret != CL_TYPE_MSEXE) {
1710
+	    lseek(desc, 0, SEEK_SET);
1711
+
1712
+	    nret = cli_scandesc(desc, ctx, 0, ret, 1, NULL);
1713
+	    if(nret == CL_VIRUS)
1714
+		cli_dbgmsg("%s found in descriptor %d when scanning file type %u\n", *ctx->virname, desc, ret);
1715
+	}
1713 1716
 
1714 1717
 	ret == CL_TYPE_MAIL ? ctx->mrec++ : ctx->arec++;
1718
+
1715 1719
 	if(nret != CL_VIRUS) switch(ret) {
1716 1720
 	    case CL_TYPE_HTML:
1717 1721
 		if(SCAN_HTML && type == CL_TYPE_UNKNOWN_TEXT && (DCONF_DOC & DOC_CONF_HTML))
... ...
@@ -1751,6 +1822,30 @@ static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type)
1751 1751
 		}
1752 1752
 		break;
1753 1753
 
1754
+	    case CL_TYPE_MSEXE:
1755
+		if(SCAN_PE && ctx->dconf->pe) {
1756
+		    fpt = ftoffset;
1757
+		    while(fpt) {
1758
+			if(fpt->type == CL_TYPE_MSEXE && fpt->offset) {
1759
+			    cli_dbgmsg("PE signature found at %u\n", (unsigned int) fpt->offset);
1760
+			    memset(&peinfo, 0, sizeof(struct cli_exe_info));
1761
+			    peinfo.offset = fpt->offset;
1762
+			    lseek(desc, fpt->offset, SEEK_SET);
1763
+			    if(cli_peheader(desc, &peinfo) == 0) {
1764
+				cli_dbgmsg("*** Detected embedded PE file ***\n");
1765
+				if(peinfo.section)
1766
+				    free(peinfo.section);
1767
+
1768
+				lseek(desc, fpt->offset, SEEK_SET);
1769
+				if((nret = cli_scanembpe(desc, ctx)) == CL_VIRUS)
1770
+				    break;
1771
+			    }
1772
+			}
1773
+			fpt = fpt->next;
1774
+		    }
1775
+		}
1776
+		break;
1777
+
1754 1778
 	    default:
1755 1779
 		break;
1756 1780
 	}