libclamav/autoit.c
ed93f138
 /*
2023340a
  *  Copyright (C) 2007-2008 Sourcefire, Inc.
  *
  *  Authors: Alberto Wu
ed93f138
  *
  *  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 <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdio.h>
dbeb3de2
 
 #if HAVE_STRING_H
ed93f138
 #include <string.h>
dbeb3de2
 #endif
ed93f138
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
 #include "others.h"
 #include "scanners.h"
5cd3f734
 #include "autoit.h"
ac867aad
 #include "fmap.h"
ed93f138
 
 
 /* FIXME: use unicode detection and normalization from edwin */
e66912ad
 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<j; i+=2)
       cnt+=(src[i]!=0 && src[i+1]==0);
 
f1b4700d
     if (cnt*4 < j)
e66912ad
       return len;
ed93f138
   }
 
e66912ad
   j=len;
   len>>=1;
   for (i=0; i<j; i+=2)
     *dest++ = src[i];
 
   return len;
ed93f138
 }
 
f1b4700d
 
ed93f138
 /*********************
    MT realted stuff 
 *********************/
 
 struct MT {
   uint32_t *next;
b5231f5f
   uint32_t items;
   uint32_t mt[624];
ed93f138
 };
 
 static uint8_t MT_getnext(struct MT *MT) {
   uint32_t r;
 
   if (!--MT->items) {
     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) {
8a06eed1
     cli_dbgmsg("autoit: getbits() - not enough bits available\n");
ed93f138
     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 
 *********************/
 
 
ac867aad
 static int ea05(cli_ctx *ctx, uint8_t *base, char *tmpd) {
ed93f138
   uint8_t b[300], comp;
   uint32_t s, m4sum=0;
b80ae277
   int i, ret;
ed93f138
   unsigned int files=0;
   char tempfile[1024];
   struct UNP UNP;
ac867aad
   fmap_t *map = *ctx->fmap;
ed93f138
 
ac867aad
   if(!fmap_need_ptr_once(map, base, 16))
ed93f138
     return CL_CLEAN;
 
   for (i=0; i<16; i++)
ac867aad
     m4sum += *base++;
ed93f138
 
d91ab809
   while((ret=cli_checklimits("autoit", ctx, 0, 0, 0))==CL_CLEAN) {
ac867aad
     if(!fmap_need_ptr_once(map, base, 8))
ed93f138
       return CL_CLEAN;
 
     /*     MT_decrypt(buf,4,0x16fa);  waste of time */
ac867aad
     if((uint32_t)cli_readint32(base) != 0xceb06dff) {
ed93f138
       cli_dbgmsg("autoit: no FILE magic found, extraction complete\n");
       return CL_CLEAN;
     }
 
ac867aad
     s = cli_readint32(base+4) ^ 0x29bc;
4861d4b6
     if ((int32_t)s<0)
       return CL_CLEAN; /* the original code wouldn't seek back here */
ac867aad
     base += 8;
4861d4b6
     if(cli_debug_flag && s<sizeof(b)) {
ac867aad
       if (!fmap_need_ptr_once(map, base, s))
ed93f138
 	return CL_CLEAN;
ac867aad
       memcpy(b, base, s);
       MT_decrypt(b,s,s+0xa25e);
       b[s]='\0';
       cli_dbgmsg("autoit: magic string '%s'\n", b);
ed93f138
     }
ac867aad
     base += s;
ed93f138
 
ac867aad
     if (!fmap_need_ptr_once(map, base, 4))
ed93f138
       return CL_CLEAN;
ac867aad
     s = cli_readint32(base) ^ 0x29ac;
ed93f138
     if ((int32_t)s<0)
       return CL_CLEAN; /* the original code wouldn't seek back here */
ac867aad
     base += 4;
051910c9
     if (cli_debug_flag && s<sizeof(b)) {
ac867aad
       if (!fmap_need_ptr_once(map, base, s))
ed93f138
 	return CL_CLEAN;
ac867aad
       memcpy(b, base, s);
       MT_decrypt(b,s,s+0xf25e);
       b[s]='\0';
       cli_dbgmsg("autoit: original filename '%s'\n", b);
ed93f138
     }
ac867aad
     base += s;
ed93f138
 
ac867aad
     if (!fmap_need_ptr_once(map, base, 13))
ed93f138
       return CL_CLEAN;
ac867aad
     comp = *base;
     UNP.csize = cli_readint32(base+1) ^ 0x45aa;
3c0912c8
     if ((int32_t)UNP.csize<0) {
       cli_dbgmsg("autoit: bad file size - giving up\n");
       return CL_CLEAN;
     }
2b304af9
 
     if(!UNP.csize) {
       cli_dbgmsg("autoit: skipping empty file\n");
6c6638b0
       base += 13 + 16;
2b304af9
       continue;
     }
ed93f138
     cli_dbgmsg("autoit: compressed size: %x\n", UNP.csize);
ac867aad
     cli_dbgmsg("autoit: advertised uncompressed size %x\n", cli_readint32(base+5) ^ 0x45aa);
     cli_dbgmsg("autoit: ref chksum: %x\n", cli_readint32(base+9) ^ 0xc3d2);
 
     base += 13 + 16;
ed93f138
 
d91ab809
     if(cli_checklimits("autoit", ctx, UNP.csize, 0, 0)!=CL_CLEAN) {
ac867aad
       base += UNP.csize;
ed93f138
       continue;
     }
 
ac867aad
     if (!(UNP.inputbuf = cli_malloc(UNP.csize)))
ed93f138
       return CL_EMEM;
ac867aad
     if (!fmap_need_ptr_once(map, base, UNP.csize)) {
ed93f138
       cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n");
ac867aad
       free(UNP.inputbuf);
ed93f138
       return CL_CLEAN;
     }
ac867aad
     memcpy(UNP.inputbuf, base, UNP.csize);
     base += UNP.csize;
     MT_decrypt(UNP.inputbuf,UNP.csize,0x22af+m4sum);
ed93f138
 
     if (comp == 1) {
       cli_dbgmsg("autoit: file is compressed\n");
ac867aad
       if (cli_readint32(UNP.inputbuf)!=0x35304145) {
ed93f138
 	cli_dbgmsg("autoit: bad magic or unsupported version\n");
ac867aad
 	free(UNP.inputbuf);
ed93f138
 	continue;
       }
 
ac867aad
       if(!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf+4))))
2b304af9
 	UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */
d91ab809
       if(cli_checklimits("autoit", ctx, UNP.usize, 0, 0)!=CL_CLEAN) {
ac867aad
 	free(UNP.inputbuf);
ed93f138
 	continue;
       }
 
       if (!(UNP.outputbuf = cli_malloc(UNP.usize))) {
ac867aad
 	free(UNP.inputbuf);
ed93f138
 	return CL_EMEM;
       }
       cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize);
 
       UNP.cur_output = 0;
       UNP.cur_input = 8;
       UNP.bitmap.full = 0;
       UNP.bits_avail = 0;
       UNP.error = 0;
3c0912c8
 
ed93f138
       while (!UNP.error && UNP.cur_output < UNP.usize) {
 	if (getbits(&UNP, 1)) {
 	  uint32_t bb, bs, addme=0;
 	  bb = getbits(&UNP, 15);
3c0912c8
 
ed93f138
 	  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++;
 	}
       }
 
ac867aad
       free(UNP.inputbuf);
8a06eed1
       /* Sometimes the autoit exe is in turn packed/lamed with a runtime compressor and similar shit.
        * However, since the autoit script doesn't compress a second time very well, chances are we're
        * still able to match the headers and unpack something (see sample 0811129)
        * I'd rather unpack something (although possibly highly corrupted) than nothing at all
        *
        * - Fortuna audaces iuvat -
        */
812a6fe0
       if (UNP.error) {
 	cli_dbgmsg("autoit: decompression error after %u bytes  - partial file may exist\n", UNP.cur_output);
 	UNP.usize = UNP.cur_output;
       }
ed93f138
     } else {
       cli_dbgmsg("autoit: file is not compressed\n");
ac867aad
       UNP.outputbuf = UNP.inputbuf;
ed93f138
       UNP.usize = UNP.csize;
     }
 
812a6fe0
     if (UNP.usize<4) {
       cli_dbgmsg("autoit: file is too short\n");
       free(UNP.outputbuf);
       continue;
     }
 
ed93f138
     files++;
 
c75c99bf
     /* FIXME: REGRESSION NEEDED! */
     /* UNP.usize = u2a(UNP.outputbuf, UNP.usize); */
ed93f138
 
58481352
     snprintf(tempfile, 1023, "%s"PATHSEP"autoit.%.3u", tmpd, files);
ed93f138
     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(UNP.outputbuf);
871177cd
       return CL_ECREAT;
ed93f138
     }
     if(cli_writen(i, UNP.outputbuf, UNP.usize) != (int32_t)UNP.usize) {
       cli_dbgmsg("autoit: cannot write %d bytes\n", UNP.usize);
       close(i);
       free(UNP.outputbuf);
871177cd
       return CL_EWRITE;
ed93f138
     }
     free(UNP.outputbuf);
33068e09
     if(ctx->engine->keeptmp)
ed93f138
       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);
33068e09
       if(!ctx->engine->keeptmp)
871177cd
         if(cli_unlink(tempfile)) return CL_EUNLINK;
ed93f138
       return CL_VIRUS;
     }
     close(i);
33068e09
     if(!ctx->engine->keeptmp) 
871177cd
       if (cli_unlink(tempfile)) return CL_EUNLINK;
ed93f138
   }
b80ae277
   return ret;
ed93f138
 }
 
 
 /*********************
   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 
 *********************/
 
ac867aad
 static int ea06(cli_ctx *ctx, uint8_t *base, char *tmpd) {
dc0bf212
   uint8_t b[600], comp, script, *buf;
ed93f138
   uint32_t s;
b80ae277
   int i, ret;
ed93f138
   unsigned int files=0;
   char tempfile[1024];
   const char prefixes[] = { '\0', '\0', '@', '$', '\0', '.', '"', '#' };
   const char *opers[] = { ",", "=", ">", "<", "<>", ">=", "<=", "(", ")", "+", "-", "/", "*", "&", "[", "]", "==", "^", "+=", "-=", "/=", "*=", "&=" };
   struct UNP UNP;
ac867aad
   fmap_t *map = *ctx->fmap;
 
ed93f138
 
   /* 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; */
ac867aad
   base += 16;   /* for now we just skip the garbage */
ed93f138
 
d91ab809
   while((ret=cli_checklimits("cli_autoit", ctx, 0, 0, 0))==CL_CLEAN) {
ac867aad
     if(!fmap_need_ptr_once(map, base, 8))
ed93f138
       return CL_CLEAN;
dbeb3de2
     /*     LAME_decrypt(buf, 4, 0x18ee); waste of time */
ac867aad
     if(cli_readint32(base) != 0x52ca436b) {
ed93f138
       cli_dbgmsg("autoit: no FILE magic found, giving up\n");
       return CL_CLEAN;
     }
 
     script = 0;
dbeb3de2
 
ac867aad
     s = cli_readint32(base+4) ^ 0xadbc;
dbeb3de2
     if ((int32_t)(s*2)<0)
       return CL_CLEAN; /* the original code wouldn't seek back here */
ac867aad
     base += 8;
     if(s < sizeof(b) / 2) {
       if(!fmap_need_ptr_once(map, base, s*2))
ed93f138
 	return CL_CLEAN;
ac867aad
       memcpy(b, base, s*2);
       LAME_decrypt(b,s*2,s+0xb33f);
       u2a(b,s*2);
       cli_dbgmsg("autoit: magic string '%s'\n", b);
       if (s==19 && !memcmp(">>>AUTOIT SCRIPT<<<", b, 19))
ed93f138
 	script = 1;
     } else {
       cli_dbgmsg("autoit: magic string too long to print\n");
     }
ac867aad
     base += s*2;
ed93f138
 
ac867aad
     if (!fmap_need_ptr_once(map, base, 4))
ed93f138
       return CL_CLEAN;
ac867aad
     s = cli_readint32(base) ^ 0xf820;
dbeb3de2
     if ((int32_t)(s*2)<0)
       return CL_CLEAN; /* the original code wouldn't seek back here */
ac867aad
     base += 4;
     if(cli_debug_flag && s<sizeof(b) / 2) {
       if(!fmap_need_ptr_once(map, base, s*2))
ed93f138
 	return CL_CLEAN;
ac867aad
       memcpy(b, base, s*2);
       LAME_decrypt(b,s*2,s+0xf479);
       b[s*2]='\0'; b[s*2+1]='\0';
       u2a(b,s*2);
       cli_dbgmsg("autoit: original filename '%s'\n", b);
ed93f138
     }
ac867aad
     base += s*2;
ed93f138
 
ac867aad
     if(!fmap_need_ptr_once(map, base, 13))
ed93f138
       return CL_CLEAN;
ac867aad
     comp = *base;
     UNP.csize = cli_readint32(base+1) ^ 0x87bc;
dbeb3de2
     if ((int32_t)UNP.csize<0) {
       cli_dbgmsg("autoit: bad file size - giving up\n");
       return CL_CLEAN;
     }
2b304af9
 
     if(!UNP.csize) {
       cli_dbgmsg("autoit: skipping empty file\n");
6c6638b0
       base += 13 + 16;
2b304af9
       continue;
     }
ed93f138
     cli_dbgmsg("autoit: compressed size: %x\n", UNP.csize);
ac867aad
     cli_dbgmsg("autoit: advertised uncompressed size %x\n", cli_readint32(base+5) ^ 0x87bc);
     cli_dbgmsg("autoit: ref chksum: %x\n", cli_readint32(base+9) ^ 0xa685);
 
     base += 13 + 16;
ed93f138
 
d91ab809
     if(cli_checklimits("autoit", ctx, UNP.csize, 0, 0)!=CL_CLEAN) {
ac867aad
       base += UNP.csize;
ed93f138
       continue;
     }
 
     files++;
ac867aad
     if (!(UNP.inputbuf = cli_malloc(UNP.csize)))
ed93f138
       return CL_EMEM;
ac867aad
     if (!fmap_need_ptr_once(map, base, UNP.csize)) {
ed93f138
       cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n");
ac867aad
       free(UNP.inputbuf);
ed93f138
       return CL_CLEAN;
     }
ac867aad
     memcpy(UNP.inputbuf, base, UNP.csize);
     base += UNP.csize;
     LAME_decrypt(UNP.inputbuf,UNP.csize,0x2477 /* + m4sum (broken by design) */ );
ed93f138
 
     if (comp == 1) {
       cli_dbgmsg("autoit: file is compressed\n");
ac867aad
       if (cli_readint32(UNP.inputbuf)!=0x36304145) {
ed93f138
 	cli_dbgmsg("autoit: bad magic or unsupported version\n");
ac867aad
 	free(UNP.inputbuf);
ed93f138
 	continue;
       }
 
ac867aad
       if(!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf+4))))
2b304af9
 	UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */
d91ab809
       if(cli_checklimits("autoit", ctx, UNP.usize, 0, 0)!=CL_CLEAN) {
ac867aad
 	free(UNP.inputbuf);
ed93f138
 	continue;
       }
       if (!(UNP.outputbuf = cli_malloc(UNP.usize))) {
ac867aad
 	free(UNP.inputbuf);
ed93f138
 	return CL_EMEM;
       }
       cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize);
 
       UNP.cur_output = 0;
       UNP.cur_input = 8;
       UNP.bitmap.full = 0;
       UNP.bits_avail = 0;
79e179b3
       UNP.error = 0;
 
ed93f138
       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++;
 	}
       }
 
ac867aad
       free(UNP.inputbuf);
812a6fe0
       if (UNP.error) {
 	cli_dbgmsg("autoit: decompression error after %u bytes - partial file may exist\n", UNP.cur_output);
 	UNP.usize = UNP.cur_output;
       }
ed93f138
     } else {
       cli_dbgmsg("autoit: file is not compressed\n");
ac867aad
       UNP.outputbuf = UNP.inputbuf;
ed93f138
       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);
79e179b3
       UNP.error = 0;
ed93f138
       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: /* <INT> */
 	  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;
 	  }
8a06eed1
 	  snprintf((char *)&buf[UNP.cur_output], 12, "0x%08x ", cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]));
 	  UNP.cur_output += 11;
ed93f138
 	  UNP.cur_input += 4;
 	  break;
 
f99a37a2
 	case 0x10: /* <INT64> */
 	  {
 	    uint64_t val;
 	    if (UNP.usize < 8 || UNP.cur_input >= UNP.usize-8) {
8a06eed1
 	      UNP.error = 1;
f99a37a2
 	      cli_dbgmsg("autoit: not enough space for an int64\n");
8a06eed1
 	      break;
 	    }
f99a37a2
 	    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]);
e78b5186
 	    snprintf((char *)&buf[UNP.cur_output], 20, "0x%016lx ", (unsigned long int) val);
f99a37a2
 	    UNP.cur_output += 19;
 	    UNP.cur_input += 8;
 	    break;
8a06eed1
 	  }
 
ed93f138
 	case 0x20: /* <DOUBLE> */
 	  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<dchars; i+=2) {
 		UNP.outputbuf[UNP.cur_input+i] ^= (uint8_t)chars;
 		UNP.outputbuf[UNP.cur_input+i+1] ^= (uint8_t)(chars>>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 ;
     }
 
58481352
     snprintf(tempfile, 1023, "%s"PATHSEP"autoit.%.3u", tmpd, files);
ed93f138
     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);
871177cd
       return CL_ECREAT;
ed93f138
     }
     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);
871177cd
       return CL_EWRITE;
ed93f138
     }
     free(buf);
33068e09
     if(ctx->engine->keeptmp)
ed93f138
       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);
79e179b3
     if(cli_magic_scandesc(i, ctx) == CL_VIRUS) {
ed93f138
       close(i);
33068e09
       if(!ctx->engine->keeptmp) 
871177cd
         if (cli_unlink(tempfile)) return CL_EUNLINK;
ed93f138
       return CL_VIRUS;
     }
     close(i);
33068e09
     if(!ctx->engine->keeptmp)
871177cd
       if (cli_unlink(tempfile)) return CL_EUNLINK;
ed93f138
   }
b80ae277
   return ret;
ed93f138
 }
 
 #endif /* FPU_WORDS_BIGENDIAN */
 
 /*********************
    autoit3 wrapper 
 *********************/
 
ac867aad
 int cli_scanautoit(cli_ctx *ctx, off_t offset) {
   uint8_t *version;
e5fc1926
   int r;
ed93f138
   char *tmpd;
ac867aad
   fmap_t *map = *ctx->fmap;
ed93f138
 
   cli_dbgmsg("in scanautoit()\n");
 
ac867aad
   if(!(version = fmap_need_off_once(map, offset, sizeof(*version))))
     return CL_EREAD;
 
33068e09
   if (!(tmpd = cli_gentemp(ctx->engine->tmpdir)))    
ed93f138
     return CL_ETMPDIR;
   if (mkdir(tmpd, 0700)) {
     cli_dbgmsg("autoit: Can't create temporary directory %s\n", tmpd);
     free(tmpd);
     return CL_ETMPDIR;
   }
33068e09
   if (ctx->engine->keeptmp)
ed93f138
     cli_dbgmsg("autoit: Extracting files to %s\n", tmpd);
 
ac867aad
   switch(*version) {
ed93f138
   case 0x35:
ac867aad
     r = ea05(ctx, version + 1, tmpd);
ed93f138
     break;
   case 0x36:
 #ifdef FPU_WORDS_BIGENDIAN
ac867aad
     r = ea06(ctx, version + 1, tmpd);
ed93f138
 #else
     cli_dbgmsg("autoit: EA06 support not available\n");
e5fc1926
     r = CL_CLEAN;
ed93f138
 #endif
e5fc1926
     break;
ed93f138
   default:
     /* NOT REACHED */
     cli_dbgmsg("autoit: unknown method\n");
e5fc1926
     r = CL_CLEAN;
ed93f138
   }
 
33068e09
   if (!ctx->engine->keeptmp)
ed93f138
     cli_rmdirs(tmpd);
 
   free(tmpd);
   return r;
 }