diff --git a/ChangeLog b/ChangeLog index 4bb52692f..81c1e6f6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Mon Mar 12 20:31:07 CET 2007 (tk) +--------------------------------- + * libclamav: extract and scan PE files embedded into other executables or + fake zip files generated by some worms + Mon Mar 12 19:55:31 CET 2007 (acab) ----------------------------------- * libclamav/packlibs.h: Removed stale EXPERIMENTAL ifdef diff --git a/libclamav/execs.h b/libclamav/execs.h index 4780b8601..c554eec2b 100644 --- a/libclamav/execs.h +++ b/libclamav/execs.h @@ -21,6 +21,7 @@ #define __EXECS_H #include "cltypes.h" +#include struct cli_exe_section { uint32_t rva; @@ -34,6 +35,7 @@ struct cli_exe_section { struct cli_exe_info { uint32_t ep; uint16_t nsections; + off_t offset; struct cli_exe_section *section; }; diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c index 4e0002419..686045d02 100644 --- a/libclamav/filetypes.c +++ b/libclamav/filetypes.c @@ -194,6 +194,8 @@ static const struct cli_smagic_s cli_smagic[] = { {"504b0304", "ZIP-SFX", CL_TYPE_ZIPSFX}, {"4d534346", "CAB-SFX", CL_TYPE_CABSFX}, + {"4d5a{180-300}50450000", "PE", CL_TYPE_MSEXE}, + {NULL, NULL, CL_TYPE_UNKNOWN_DATA} }; diff --git a/libclamav/filetypes.h b/libclamav/filetypes.h index decb913a0..83363920f 100644 --- a/libclamav/filetypes.h +++ b/libclamav/filetypes.h @@ -25,7 +25,7 @@ #define MAGIC_BUFFER_SIZE 256 #define CL_TYPENO 500 -#define SFX_MAX_TESTS 10 +#define MAX_EMBEDDED_OBJ 10 typedef enum { CL_TYPE_UNKNOWN_TEXT = CL_TYPENO, diff --git a/libclamav/matcher-ac.c b/libclamav/matcher-ac.c index 76752c4c0..6d496caa0 100644 --- a/libclamav/matcher-ac.c +++ b/libclamav/matcher-ac.c @@ -314,10 +314,18 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in if(!partsigs) return CL_SUCCESS; + data->inioff = (off_t *) cli_malloc(partsigs * sizeof(off_t)); + if(!data->inioff) { + cli_errmsg("cli_ac_init(): unable to cli_malloc(%u)\n", partsigs * sizeof(off_t)); + return CL_EMEM; + } + memset(data->inioff, -1, partsigs * sizeof(off_t)); + data->partcnt = (unsigned int *) cli_calloc(partsigs, sizeof(unsigned int)); if(!data->partcnt) { cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(unsigned int)); + free(data->inioff); return CL_EMEM; } @@ -325,6 +333,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in if(!data->offcnt) { cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(uint8_t)); + free(data->inioff); free(data->partcnt); return CL_EMEM; } @@ -333,6 +342,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in if(!data->offidx) { cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(uint8_t)); + free(data->inioff); free(data->partcnt); free(data->offcnt); return CL_EMEM; @@ -342,6 +352,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in if(!data->maxshift) { cli_errmsg("cli_ac_init(): unable to cli_malloc(%u)\n", partsigs * sizeof(int)); + free(data->inioff); free(data->partcnt); free(data->offcnt); free(data->offidx); @@ -354,6 +365,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in if(!data->partoff) { cli_errmsg("cli_ac_init(): unable to cli_calloc(%u, %u)\n", partsigs, sizeof(unsigned int)); + free(data->inioff); free(data->partcnt); free(data->offcnt); free(data->offidx); @@ -374,6 +386,7 @@ int cli_ac_initdata(struct cli_ac_data *data, unsigned int partsigs, unsigned in free(data->partoff[j]); free(data->partoff); + free(data->inioff); free(data->partcnt); free(data->offcnt); free(data->offidx); @@ -392,6 +405,7 @@ void cli_ac_freedata(struct cli_ac_data *data) if(data && data->partsigs) { + free(data->inioff); free(data->partcnt); free(data->offcnt); free(data->offidx); @@ -446,6 +460,9 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char if(pt->sigid) { /* it's a partial signature */ + if(pt->partno == 1) + mdata->inioff[pt->sigid - 1] = curroff; + if(mdata->partcnt[pt->sigid - 1] + 1 == pt->partno) { offnum = mdata->offcnt[pt->sigid - 1]; if(offnum < AC_DEFAULT_TRACKLEN) { @@ -491,10 +508,10 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char if(++mdata->partcnt[pt->sigid - 1] + 1 == pt->parts) { if(pt->type) { if(otfrec) { - if(pt->type > type || pt->type >= CL_TYPE_SFX) { - cli_dbgmsg("Matched signature for file type %s\n", pt->virname); + if(pt->type > type || pt->type >= CL_TYPE_SFX || pt->type == CL_TYPE_MSEXE) { + cli_dbgmsg("Matched signature for file type %s at %u\n", pt->virname, (unsigned int) mdata->inioff[pt->sigid - 1]); type = pt->type; - if(ftoffset && (!*ftoffset || (*ftoffset)->cnt < SFX_MAX_TESTS) && ftype == CL_TYPE_MSEXE && type >= CL_TYPE_SFX) { + 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))) { if(!(tnode = cli_calloc(1, sizeof(struct cli_matched_type)))) { cli_errmsg("cli_ac_scanbuff(): Can't allocate memory for new type node\n"); if(info.exeinfo.section) @@ -503,7 +520,7 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char } tnode->type = type; - tnode->offset = -1; /* we don't remember the offset of the first part */ + tnode->offset = mdata->inioff[pt->sigid - 1]; if(*ftoffset) tnode->cnt = (*ftoffset)->cnt + 1; @@ -530,10 +547,10 @@ int cli_ac_scanbuff(const unsigned char *buffer, unsigned int length, const char } else { /* old type signature */ if(pt->type) { if(otfrec) { - if(pt->type > type || pt->type >= CL_TYPE_SFX) { + if(pt->type > type || pt->type >= CL_TYPE_SFX || pt->type == CL_TYPE_MSEXE) { cli_dbgmsg("Matched signature for file type %s at %u\n", pt->virname, curroff); type = pt->type; - if(ftoffset && (!*ftoffset ||(*ftoffset)->cnt < SFX_MAX_TESTS) && ftype == CL_TYPE_MSEXE && type >= CL_TYPE_SFX) { + 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))) { if(!(tnode = cli_calloc(1, sizeof(struct cli_matched_type)))) { cli_errmsg("cli_ac_scanbuff(): Can't allocate memory for new type node\n"); if(info.exeinfo.section) diff --git a/libclamav/matcher-ac.h b/libclamav/matcher-ac.h index 862c6a822..c79bf3e61 100644 --- a/libclamav/matcher-ac.h +++ b/libclamav/matcher-ac.h @@ -20,6 +20,8 @@ #ifndef __MATCHER_AC_H #define __MATCHER_AC_H +#include + #include "clamav.h" #include "matcher.h" #include "filetypes.h" @@ -30,6 +32,7 @@ struct cli_ac_data { unsigned int partsigs; + off_t *inioff; unsigned int *partcnt; unsigned int **partoff; uint8_t *offcnt; diff --git a/libclamav/matcher.c b/libclamav/matcher.c index dd553869a..a7fed8557 100644 --- a/libclamav/matcher.c +++ b/libclamav/matcher.c @@ -272,7 +272,7 @@ int cli_validatesig(cli_file_t ftype, const char *offstr, off_t fileoff, struct } if(maxshift) { - if((fileoff < offset) || (fileoff > offset + maxshift)) { + if((fileoff < offset) || (fileoff > offset + (off_t) maxshift)) { cli_dbgmsg("Signature offset: %lu, expected: [%lu..%lu] (%s)\n", fileoff, offset, offset + maxshift, virname); return 0; } diff --git a/libclamav/pe.c b/libclamav/pe.c index b61cab21e..11fa9d77a 100644 --- a/libclamav/pe.c +++ b/libclamav/pe.c @@ -2819,7 +2819,7 @@ int cli_peheader(int desc, struct cli_exe_info *peinfo) return -1; } - fsize = sb.st_size; + fsize = sb.st_size - peinfo->offset; if(cli_readn(desc, &e_magic, sizeof(e_magic)) != sizeof(e_magic)) { cli_dbgmsg("Can't read DOS signature\n"); @@ -2845,7 +2845,7 @@ int cli_peheader(int desc, struct cli_exe_info *peinfo) return -1; } - if(lseek(desc, e_lfanew, SEEK_SET) < 0) { + if(lseek(desc, peinfo->offset + e_lfanew, SEEK_SET) < 0) { /* probably not a PE file */ cli_dbgmsg("Can't lseek to e_lfanew\n"); return -1; diff --git a/libclamav/scanners.c b/libclamav/scanners.c index 7e2cc4f45..a18d4ecc4 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -1680,17 +1680,84 @@ static int cli_scanmail(int desc, cli_ctx *ctx) return ret; } +static int cli_scanembpe(int desc, cli_ctx *ctx) +{ + int fd, bytes, ret = CL_CLEAN; + unsigned long int size = 0; + char buff[512]; + char *tmpname; + + + tmpname = cli_gentemp(NULL); + if(!tmpname) + return CL_EMEM; + + if((fd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { + cli_errmsg("cli_scanembpe: Can't create file %s\n", tmpname); + free(tmpname); + return CL_EIO; + } + + while((bytes = read(desc, buff, sizeof(buff))) > 0) { + size += bytes; + + if(ctx->limits && ctx->limits->maxfilesize && (size + sizeof(buff) > ctx->limits->maxfilesize)) { + cli_dbgmsg("cli_scanembpe: Size exceeded (stopped at %lu, max: %lu)\n", size, ctx->limits->maxfilesize); + /* BLOCKMAX should be ignored here */ + break; + } + + if(cli_writen(fd, buff, bytes) != bytes) { + cli_dbgmsg("cli_scanembpe: Can't write to temporary file\n"); + close(fd); + if(!cli_leavetemps_flag) + unlink(tmpname); + free(tmpname); + return CL_EIO; + } + } + + if(fsync(fd) == -1) { + cli_dbgmsg("cli_scanembpe: Can't synchronise descriptor %d\n", fd); + close(fd); + if(!cli_leavetemps_flag) + unlink(tmpname); + free(tmpname); + return CL_EFSYNC; + } + + lseek(fd, 0, SEEK_SET); + if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS) { + cli_dbgmsg("cli_scanembpe: Infected with %s\n", *ctx->virname); + close(fd); + if(!cli_leavetemps_flag) + unlink(tmpname); + free(tmpname); + return CL_VIRUS; + } + + close(fd); + if(!cli_leavetemps_flag) + unlink(tmpname); + free(tmpname); + + /* intentionally ignore possible errors from cli_magic_scandesc */ + return CL_CLEAN; +} + static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type) { int ret = CL_CLEAN, nret = CL_CLEAN; unsigned short ftrec; struct cli_matched_type *ftoffset = NULL, *fpt; uint32_t lastzip, lastrar; + struct cli_exe_info peinfo; switch(type) { case CL_TYPE_UNKNOWN_TEXT: case CL_TYPE_MSEXE: + case CL_TYPE_ZIP: ftrec = 1; break; default: @@ -1705,13 +1772,17 @@ static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type) ret = cli_scandesc(desc, ctx, ftrec, type, 0, &ftoffset); if(ret >= CL_TYPENO) { - lseek(desc, 0, SEEK_SET); - nret = cli_scandesc(desc, ctx, 0, ret, 1, NULL); - if(nret == CL_VIRUS) - cli_dbgmsg("%s found in descriptor %d when scanning file type %u\n", *ctx->virname, desc, ret); + if(ret < CL_TYPE_SFX && ret != CL_TYPE_MSEXE) { + lseek(desc, 0, SEEK_SET); + + nret = cli_scandesc(desc, ctx, 0, ret, 1, NULL); + if(nret == CL_VIRUS) + cli_dbgmsg("%s found in descriptor %d when scanning file type %u\n", *ctx->virname, desc, ret); + } ret == CL_TYPE_MAIL ? ctx->mrec++ : ctx->arec++; + if(nret != CL_VIRUS) switch(ret) { case CL_TYPE_HTML: 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) } break; + case CL_TYPE_MSEXE: + if(SCAN_PE && ctx->dconf->pe) { + fpt = ftoffset; + while(fpt) { + if(fpt->type == CL_TYPE_MSEXE && fpt->offset) { + cli_dbgmsg("PE signature found at %u\n", (unsigned int) fpt->offset); + memset(&peinfo, 0, sizeof(struct cli_exe_info)); + peinfo.offset = fpt->offset; + lseek(desc, fpt->offset, SEEK_SET); + if(cli_peheader(desc, &peinfo) == 0) { + cli_dbgmsg("*** Detected embedded PE file ***\n"); + if(peinfo.section) + free(peinfo.section); + + lseek(desc, fpt->offset, SEEK_SET); + if((nret = cli_scanembpe(desc, ctx)) == CL_VIRUS) + break; + } + } + fpt = fpt->next; + } + } + break; + default: break; }