git-svn: trunk@2934
Tomasz Kojm authored on 2007/03/13 06:31:40... | ... |
@@ -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 |
|
... | ... |
@@ -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 |
} |