/* * Copyright (C) 2007-2008 Sourcefire, Inc. * * Authors: Alberto Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #if HAVE_CONFIG_H #include "clamav-config.h" #endif #include #include #include #include #if HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifndef O_BINARY #define O_BINARY 0 #endif #include "others.h" #include "scanners.h" #include "autoit.h" /* FIXME: use unicode detection and normalization from edwin */ static unsigned int u2a(uint8_t *dest, unsigned int len) { uint8_t *src = dest; unsigned int i,j; if (len<2) return len; if (len>4 && src[0]==0xff && src[1]==0xfe && src[2]) { len-=2; src+=2; } else { unsigned int cnt=0; j = (len > 20) ? 20 : (len&~1); for (i=0; i>=1; for (i=0; iitems) { uint32_t *mt = MT->mt; unsigned int i; MT->items = 624; MT->next = mt; for (i=0; i<227; i++) mt[i] = ((((mt[i] ^ mt[i+1])&0x7ffffffe)^mt[i])>>1)^((0-(mt[i+1]&1))&0x9908b0df)^mt[i+397]; for (; i<623; i++) mt[i] = ((((mt[i] ^ mt[i+1])&0x7ffffffe)^mt[i])>>1)^((0-(mt[i+1]&1))&0x9908b0df)^mt[i-227]; mt[623] = ((((mt[623] ^ mt[0])&0x7ffffffe)^mt[623])>>1)^((0-(mt[0]&1))&0x9908b0df)^mt[i-227]; } r = *(MT->next++); r ^= (r >> 11); r ^= ((r & 0xff3a58ad) << 7); r ^= ((r & 0xffffdf8c) << 15); r ^= (r >> 18); return (uint8_t)(r >> 1); } static void MT_decrypt(uint8_t *buf, unsigned int size, uint32_t seed) { struct MT MT; unsigned int i; uint32_t *mt = MT.mt; *mt=seed; for(i=1; i<624; i++) mt[i] = i+0x6c078965*((mt[i-1]>>30)^mt[i-1]); MT.items = 1; while(size--) *buf++ ^= MT_getnext(&MT); } /********************* inflate stuff *********************/ struct UNP { uint8_t *outputbuf; uint8_t *inputbuf; uint32_t cur_output; uint32_t cur_input; uint32_t usize; uint32_t csize; uint32_t bits_avail; union { uint32_t full; struct { #if WORDS_BIGENDIAN != 0 uint16_t h; /* BE */ uint16_t l; #else uint16_t l; /* LE */ uint16_t h; #endif } half; } bitmap; uint32_t error; }; static uint32_t getbits(struct UNP *UNP, uint32_t size) { UNP->bitmap.half.h = 0; if (size > UNP->bits_avail && ((size - UNP->bits_avail - 1)/16+1)*2 > UNP->csize - UNP->cur_input) { cli_dbgmsg("autoit: getbits() - not enough bits available\n"); UNP->error = 1; return 0; /* won't infloop nor spam */ } while (size) { if (!UNP->bits_avail) { UNP->bitmap.half.l |= UNP->inputbuf[UNP->cur_input++]<<8; UNP->bitmap.half.l |= UNP->inputbuf[UNP->cur_input++]; UNP->bits_avail = 16; } UNP->bitmap.full<<=1; UNP->bits_avail--; size--; } return (uint32_t)UNP->bitmap.half.h; } /********************* autoit3 EA05 handler *********************/ static int ea05(int desc, cli_ctx *ctx, char *tmpd) { uint8_t b[300], comp; uint8_t *buf = b; uint32_t s, m4sum=0; int i, ret; unsigned int files=0; char tempfile[1024]; struct UNP UNP; if (cli_readn(desc, buf, 16)!=16) return CL_CLEAN; for (i=0; i<16; i++) m4sum += buf[i]; while((ret=cli_checklimits("autoit", ctx, 0, 0, 0))==CL_CLEAN) { buf = b; if (cli_readn(desc, buf, 8)!=8) return CL_CLEAN; /* MT_decrypt(buf,4,0x16fa); waste of time */ if((uint32_t)cli_readint32((char *)buf) != 0xceb06dff) { cli_dbgmsg("autoit: no FILE magic found, extraction complete\n"); return CL_CLEAN; } s = cli_readint32((char *)buf+4) ^ 0x29bc; if ((int32_t)s<0) return CL_CLEAN; /* the original code wouldn't seek back here */ if(cli_debug_flag && sengine->keeptmp) cli_dbgmsg("autoit: file extracted to %s\n", tempfile); else cli_dbgmsg("autoit: file successfully extracted\n"); lseek(i, 0, SEEK_SET); if(cli_magic_scandesc(i, ctx) == CL_VIRUS) { close(i); if(!ctx->engine->keeptmp) if(cli_unlink(tempfile)) return CL_EUNLINK; return CL_VIRUS; } close(i); if(!ctx->engine->keeptmp) if (cli_unlink(tempfile)) return CL_EUNLINK; } return ret; } /********************* LAME realted stuff *********************/ #ifdef FPU_WORDS_BIGENDIAN #define ROFL(a,b) (( a << (b % (sizeof(a)<<3) )) | (a >> ( (sizeof(a)<<3) - (b % (sizeof(a)<<3 )) ) )) struct LAME { uint32_t c0; uint32_t c1; uint32_t grp1[17]; }; static double LAME_fpusht(struct LAME *l) { union { double as_double; struct { #if FPU_WORDS_BIGENDIAN == 0 uint32_t lo; uint32_t hi; #else uint32_t hi; uint32_t lo; #endif } as_uint; } ret; uint32_t rolled = ROFL(l->grp1[l->c0],9) + ROFL(l->grp1[l->c1],13); l->grp1[l->c0] = rolled; if (!l->c0--) l->c0 = 16; if (!l->c1--) l->c1 = 16; /* if (l->grp1[l->c0] == l->grp2[0]) { */ /* if (!memcmp(l->grp1, (uint32_t *)l + 0x24 - l->c0, 0x44)) */ /* return 0.0; */ /* } */ ret.as_uint.lo = rolled << 0x14; ret.as_uint.hi = 0x3ff00000 | (rolled >> 0xc); return ret.as_double - 1.0; } static void LAME_srand(struct LAME *l, uint32_t seed) { unsigned int i; for (i=0; i<17; i++) { seed *= 0x53A9B4FB; /*1403630843*/ seed = 1 - seed; l->grp1[i] = seed; } l->c0 = 0; l->c1 = 10; for (i = 0; i < 9; i++) LAME_fpusht(l); } static uint8_t LAME_getnext(struct LAME *l) { double x; uint8_t ret; LAME_fpusht(l); x = LAME_fpusht(l) * 256.0; if ((int32_t)x < 256) ret = (uint8_t)x; else ret=0xff; return ret; } static void LAME_decrypt (uint8_t *cypher, uint32_t size, uint16_t seed) { struct LAME lame; /* mt_srand_timewrap(struct srand_struc bufDC); */ LAME_srand(&lame, (uint32_t)seed); while(size--) *cypher++^=LAME_getnext(&lame); } /********************* autoit3 EA06 handler *********************/ static int ea06(int desc, cli_ctx *ctx, char *tmpd) { uint8_t b[600], comp, script; uint8_t *buf; uint32_t s; int i, ret; unsigned int files=0; char tempfile[1024]; const char prefixes[] = { '\0', '\0', '@', '$', '\0', '.', '"', '#' }; const char *opers[] = { ",", "=", ">", "<", "<>", ">=", "<=", "(", ")", "+", "-", "/", "*", "&", "[", "]", "==", "^", "+=", "-=", "/=", "*=", "&=" }; struct UNP UNP; /* Useless due to a bug in CRC calculation - LMAO!!1 */ /* if (cli_readn(desc, buf, 24)!=24) */ /* return CL_CLEAN; */ /* LAME_decrypt(buf, 0x10, 0x99f2); */ /* buf+=0x10; */ lseek(desc, 16, SEEK_CUR); /* for now we just skip the garbage */ while((ret=cli_checklimits("cli_autoit", ctx, 0, 0, 0))==CL_CLEAN) { buf = b; if (cli_readn(desc, buf, 8)!=8) return CL_CLEAN; /* LAME_decrypt(buf, 4, 0x18ee); waste of time */ if(cli_readint32((char *)buf) != 0x52ca436b) { cli_dbgmsg("autoit: no FILE magic found, giving up\n"); return CL_CLEAN; } script = 0; s = cli_readint32((char *)buf+4) ^ 0xadbc; if ((int32_t)(s*2)<0) return CL_CLEAN; /* the original code wouldn't seek back here */ if(s<300) { if (cli_readn(desc, buf, s*2)!=(int)s*2) return CL_CLEAN; LAME_decrypt(buf,s*2,s+0xb33f); u2a(buf,s*2); cli_dbgmsg("autoit: magic string '%s'\n", buf); if (s==19 && !memcmp(">>>AUTOIT SCRIPT<<<", buf, 19)) script = 1; } else { cli_dbgmsg("autoit: magic string too long to print\n"); lseek(desc, s*2, SEEK_CUR); } if (cli_readn(desc, buf, 4)!=4) return CL_CLEAN; s = cli_readint32((char *)buf) ^ 0xf820; if ((int32_t)(s*2)<0) return CL_CLEAN; /* the original code wouldn't seek back here */ if(cli_debug_flag && s<300) { if (cli_readn(desc, buf, s*2)!=(int)s*2) return CL_CLEAN; LAME_decrypt(buf,s*2,s+0xf479); buf[s*2]='\0'; buf[s*2+1]='\0'; u2a(buf,s*2); cli_dbgmsg("autoit: original filename '%s'\n", buf); } else { lseek(desc, s*2, SEEK_CUR); } if (cli_readn(desc, buf, 13)!=13) return CL_CLEAN; comp = *buf; UNP.csize = cli_readint32((char *)buf+1) ^ 0x87bc; if ((int32_t)UNP.csize<0) { cli_dbgmsg("autoit: bad file size - giving up\n"); return CL_CLEAN; } lseek(desc, 16, SEEK_CUR); if(!UNP.csize) { cli_dbgmsg("autoit: skipping empty file\n"); continue; } cli_dbgmsg("autoit: compressed size: %x\n", UNP.csize); cli_dbgmsg("autoit: advertised uncompressed size %x\n", cli_readint32((char *)buf+5) ^ 0x87bc); cli_dbgmsg("autoit: ref chksum: %x\n", cli_readint32((char *)buf+9) ^ 0xa685); if(cli_checklimits("autoit", ctx, UNP.csize, 0, 0)!=CL_CLEAN) { lseek(desc, UNP.csize, SEEK_CUR); continue; } files++; if (!(buf = cli_malloc(UNP.csize))) return CL_EMEM; if (cli_readn(desc, buf, UNP.csize)!=(int)UNP.csize) { cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n"); free(buf); return CL_CLEAN; } LAME_decrypt(buf,UNP.csize,0x2477 /* + m4sum (broken by design) */ ); if (comp == 1) { cli_dbgmsg("autoit: file is compressed\n"); if (cli_readint32((char *)buf)!=0x36304145) { cli_dbgmsg("autoit: bad magic or unsupported version\n"); free(buf); continue; } if(!(UNP.usize = be32_to_host(*(uint32_t *)(buf+4)))) UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */ if(cli_checklimits("autoit", ctx, UNP.usize, 0, 0)!=CL_CLEAN) { free(buf); continue; } if (!(UNP.outputbuf = cli_malloc(UNP.usize))) { free(buf); return CL_EMEM; } cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize); UNP.inputbuf = buf; UNP.cur_output = 0; UNP.cur_input = 8; UNP.bitmap.full = 0; UNP.bits_avail = 0; UNP.error = 0; while (!UNP.error && UNP.cur_output < UNP.usize) { if (!getbits(&UNP, 1)) { uint32_t bb, bs, addme=0; bb = getbits(&UNP, 15); if ((bs = getbits(&UNP, 2))==3) { addme = 3; if((bs = getbits(&UNP, 3))==7) { addme = 10; if((bs = getbits(&UNP, 5))==31) { addme = 41; if((bs = getbits(&UNP, 8))==255) { addme = 296; while((bs = getbits(&UNP, 8))==255) { addme+=255; } } } } } bs += 3+addme; if(!CLI_ISCONTAINED(UNP.outputbuf, UNP.usize, &UNP.outputbuf[UNP.cur_output], bs) || !CLI_ISCONTAINED(UNP.outputbuf, UNP.usize, &UNP.outputbuf[UNP.cur_output-bb], bs)) { UNP.error = 1; break; } while(bs--) { UNP.outputbuf[UNP.cur_output]=UNP.outputbuf[UNP.cur_output-bb]; UNP.cur_output++; } } else { UNP.outputbuf[UNP.cur_output] = (uint8_t)getbits(&UNP, 8); UNP.cur_output++; } } free(buf); if (UNP.error) cli_dbgmsg("autoit: decompression error - partial file may exist\n"); } else { cli_dbgmsg("autoit: file is not compressed\n"); UNP.outputbuf = buf; UNP.usize = UNP.csize; } if (UNP.usize<4) { cli_dbgmsg("autoit: file is too short\n"); free(UNP.outputbuf); continue; } if (script) { UNP.csize = UNP.usize; if (!(buf = cli_malloc(UNP.csize))) { free(UNP.outputbuf); return CL_EMEM; } UNP.cur_output = 0; UNP.cur_input = 4; UNP.bits_avail = cli_readint32((char *)UNP.outputbuf); UNP.error = 0; cli_dbgmsg("autoit: script has got %u lines\n", UNP.bits_avail); while (!UNP.error && UNP.bits_avail && UNP.cur_input < UNP.usize) { uint8_t op; switch((op = UNP.outputbuf[UNP.cur_input++])) { case 5: /* */ if (UNP.cur_input >= UNP.usize-4) { UNP.error = 1; cli_dbgmsg("autoit: not enough space for an int\n"); break; } if (UNP.cur_output+12 >= UNP.csize) { uint8_t *newout; UNP.csize += 512; if (!(newout = cli_realloc(buf, UNP.csize))) { UNP.error = 1; break; } buf = newout; } snprintf((char *)&buf[UNP.cur_output], 12, "0x%08x ", cli_readint32((char *)&UNP.outputbuf[UNP.cur_input])); UNP.cur_output += 11; UNP.cur_input += 4; break; case 0x10: /* */ { uint64_t val; if (UNP.usize < 8 || UNP.cur_input >= UNP.usize-8) { UNP.error = 1; cli_dbgmsg("autoit: not enough space for an int64\n"); break; } if (UNP.cur_output+20 >= UNP.csize) { uint8_t *newout; UNP.csize += 512; if (!(newout = cli_realloc(buf, UNP.csize))) { UNP.error = 1; break; } buf = newout; } val = (uint64_t)cli_readint32((char *)&UNP.outputbuf[UNP.cur_input+4]); val <<=32; val += (uint64_t)cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]); snprintf((char *)&buf[UNP.cur_output], 20, "0x%016lx ", val); UNP.cur_output += 19; UNP.cur_input += 8; break; } case 0x20: /* */ if (UNP.usize < 8 || UNP.cur_input >= UNP.usize-8) { UNP.error = 1; cli_dbgmsg("autoit: not enough space for a double\n"); break; } if (UNP.cur_output+40 >= UNP.csize) { uint8_t *newout; UNP.csize += 512; if (!(newout = cli_realloc(buf, UNP.csize))) { UNP.error = 1; break; } buf = newout; } #if FPU_WORDS_BIGENDIAN == 0 snprintf((char *)&buf[UNP.cur_output], 39, "%g ", *(double *)&UNP.outputbuf[UNP.cur_input]); #else do { double x; uint8_t *j = (uint8_t *)&x; unsigned int i; for(i=0; i<8; i++) j[7-i]=UNP.outputbuf[UNP.cur_input+i]; snprintf((char *)&buf[UNP.cur_output], 39, "%g ", x); /* FIXME: check */ } while(0); #endif buf[UNP.cur_output+38]=' '; buf[UNP.cur_output+39]='\0'; UNP.cur_output += strlen((char *)&buf[UNP.cur_output]); UNP.cur_input += 8; break; case 0x30: /* COSTRUCT */ case 0x31: /* COMMAND */ case 0x32: /* MACRO */ case 0x33: /* VAR */ case 0x34: /* FUNC */ case 0x35: /* OBJECT */ case 0x36: /* STRING */ case 0x37: /* DIRECTIVE */ { uint32_t chars, dchars, i; if (UNP.cur_input >= UNP.usize-4) { UNP.error = 1; cli_dbgmsg("autoit: not enough space for size\n"); break; } chars = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]); dchars = chars*2; UNP.cur_input+=4; if (UNP.usize < dchars || UNP.cur_input >= UNP.usize-dchars) { UNP.error = 1; cli_dbgmsg("autoit: size too big - needed %d, total %d, avail %d\n", dchars, UNP.usize, UNP.usize - UNP.cur_input); break; } if (UNP.cur_output+chars+3 >= UNP.csize) { uint8_t *newout; UNP.csize += chars + 512; if (!(newout = cli_realloc(buf, UNP.csize))) { UNP.error = 1; break; } buf = newout; } if(prefixes[op-0x30]) buf[UNP.cur_output++] = prefixes[op-0x30]; if (chars) { for (i = 0; i>8); } u2a(&UNP.outputbuf[UNP.cur_input], dchars); memcpy(&buf[UNP.cur_output], &UNP.outputbuf[UNP.cur_input], chars); UNP.cur_output += chars; UNP.cur_input += dchars; } if (op==0x36) buf[UNP.cur_output++] = '"'; if (op!=0x34) buf[UNP.cur_output++] = ' '; } break; case 0x40: /* , */ case 0x41: /* = */ case 0x42: /* > */ case 0x43: /* < */ case 0x44: /* <> */ case 0x45: /* >= */ case 0x46: /* <= */ case 0x47: /* ( */ case 0x48: /* ) */ case 0x49: /* + */ case 0x4a: /* - */ case 0x4b: /* / */ case 0x4c: /* * */ case 0x4d: /* & */ case 0x4e: /* [ */ case 0x4f: /* ] */ case 0x50: /* == */ case 0x51: /* ^ */ case 0x52: /* += */ case 0x53: /* -= */ case 0x54: /* /= */ case 0x55: /* *= */ case 0x56: /* &= */ if (UNP.cur_output+4 >= UNP.csize) { uint8_t *newout; UNP.csize += 512; if (!(newout = cli_realloc(buf, UNP.csize))) { UNP.error = 1; break; } buf = newout; } UNP.cur_output += snprintf((char *)&buf[UNP.cur_output], 4, "%s ", opers[op-0x40]); break; case 0x7f: UNP.bits_avail--; if (UNP.cur_output+1 >= UNP.csize) { uint8_t *newout; UNP.csize += 512; if (!(newout = cli_realloc(buf, UNP.csize))) { UNP.error = 1; break; } buf = newout; } buf[UNP.cur_output++]='\n'; break; default: cli_dbgmsg("autoit: found unknown op (%x)\n", op); UNP.error = 1; } } if (UNP.error) cli_dbgmsg("autoit: decompilation aborted - partial script may exist\n"); free(UNP.outputbuf); } else { buf = UNP.outputbuf; UNP.cur_output = UNP.usize ; } snprintf(tempfile, 1023, "%s/autoit.%.3u", tmpd, files); tempfile[1023]='\0'; if((i = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { cli_dbgmsg("autoit: Can't create file %s\n", tempfile); free(buf); return CL_ECREAT; } if(cli_writen(i, buf, UNP.cur_output) != (int32_t)UNP.cur_output) { cli_dbgmsg("autoit: cannot write %d bytes\n", UNP.usize); close(i); free(buf); return CL_EWRITE; } free(buf); if(ctx->engine->keeptmp) cli_dbgmsg("autoit: %s extracted to %s\n", (script)?"script":"file", tempfile); else cli_dbgmsg("autoit: %s successfully extracted\n", (script)?"script":"file"); lseek(i, 0, SEEK_SET); if(cli_magic_scandesc(i, ctx) == CL_VIRUS) { close(i); if(!ctx->engine->keeptmp) if (cli_unlink(tempfile)) return CL_EUNLINK; return CL_VIRUS; } close(i); if(!ctx->engine->keeptmp) if (cli_unlink(tempfile)) return CL_EUNLINK; } return ret; } #endif /* FPU_WORDS_BIGENDIAN */ /********************* autoit3 wrapper *********************/ int cli_scanautoit(int desc, cli_ctx *ctx, off_t offset) { uint8_t version; int r; char *tmpd; lseek(desc, offset, SEEK_SET); if (cli_readn(desc, &version, 1)!=1) return CL_EREAD; cli_dbgmsg("in scanautoit()\n"); if (!(tmpd = cli_gentemp(ctx->engine->tmpdir))) return CL_ETMPDIR; if (mkdir(tmpd, 0700)) { cli_dbgmsg("autoit: Can't create temporary directory %s\n", tmpd); free(tmpd); return CL_ETMPDIR; } if (ctx->engine->keeptmp) cli_dbgmsg("autoit: Extracting files to %s\n", tmpd); switch(version) { case 0x35: r = ea05(desc, ctx, tmpd); break; case 0x36: #ifdef FPU_WORDS_BIGENDIAN r = ea06(desc, ctx, tmpd); #else cli_dbgmsg("autoit: EA06 support not available\n"); r = CL_CLEAN; #endif break; default: /* NOT REACHED */ cli_dbgmsg("autoit: unknown method\n"); r = CL_CLEAN; } if (!ctx->engine->keeptmp) cli_rmdirs(tmpd); free(tmpd); return r; }