libclamav/wwunpack.c
50593e02
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
2023340a
  *
  *  Authors: Alberto Wu
50593e02
  *
  *  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
 
60d8d2c3
 #include "clamav.h"
50593e02
 #include "others.h"
 #include "execs.h"
5cd3f734
 #include "wwunpack.h"
50593e02
 
 #if HAVE_STRING_H
 #include <string.h>
 #endif
 
288057e9
 #define RESEED                                  \
     if (CLI_ISCONTAINED(compd, szd, ccur, 4)) { \
         bt = cli_readint32(ccur);               \
         ccur += 4;                              \
     } else {                                    \
         cli_dbgmsg("WWPack: Out of bits\n");    \
         error = 1;                              \
     }                                           \
     bc = 32;
50593e02
 
288057e9
 #define BIT          \
     bits = bt >> 31; \
     bt <<= 1;        \
     if (!--bc) {     \
         RESEED;      \
     }
50593e02
 
288057e9
 #define BITS(N)                                     \
     bits = bt >> (32 - (N));                        \
     if (bc >= (N)) {                                \
         bc -= (N);                                  \
         bt <<= (N);                                 \
         if (!bc) {                                  \
             RESEED;                                 \
         }                                           \
     } else {                                        \
         if (CLI_ISCONTAINED(compd, szd, ccur, 4)) { \
             bt = cli_readint32(ccur);               \
             ccur += 4;                              \
             bc += 32 - (N);                         \
             bits |= bt >> (bc);                     \
             bt <<= (32 - bc);                       \
         } else {                                    \
             cli_dbgmsg("WWPack: Out of bits\n");    \
             error = 1;                              \
         }                                           \
     }
50593e02
 
6c03dc5d
 cl_error_t wwunpack(uint8_t *exe, uint32_t exesz, uint8_t *wwsect, struct cli_exe_section *sects, uint16_t scount, uint32_t pe, int desc)
288057e9
 {
     uint8_t *structs = wwsect + 0x2a1, *compd, *ccur, *unpd, *ucur, bc;
     uint32_t src, srcend, szd, bt, bits;
6c03dc5d
     cl_error_t error = 0;
     uint16_t i;
50593e02
 
288057e9
     cli_dbgmsg("in wwunpack\n");
     while (1) {
         if (!CLI_ISCONTAINED(wwsect, sects[scount].rsz, structs, 17)) {
             cli_dbgmsg("WWPack: Array of structs out of section\n");
             break;
         }
         src = sects[scount].rva - cli_readint32(structs); /* src delta / dst delta - not used / dwords / end of src */
         structs += 8;
         szd = cli_readint32(structs) * 4;
         structs += 4;
         srcend = cli_readint32(structs);
         structs += 4;
50593e02
 
288057e9
         unpd = ucur = exe + src + srcend + 4 - szd;
         if (!szd || !CLI_ISCONTAINED(exe, exesz, unpd, szd)) {
             cli_dbgmsg("WWPack: Compressed data out of file\n");
             break;
         }
         cli_dbgmsg("WWP: src: %x, szd: %x, srcend: %x - %x\n", src, szd, srcend, srcend + 4 - szd);
         if (!(compd = cli_malloc(szd))) {
             cli_dbgmsg("WWPack: Unable to allocate memory for compd\n");
             break;
         }
         memcpy(compd, unpd, szd);
         memset(unpd, -1, szd); /*FIXME*/
         ccur = compd;
50593e02
 
288057e9
         RESEED;
6c03dc5d
         while (CL_SUCCESS == error) {
288057e9
             uint32_t backbytes, backsize;
             uint8_t saved;
50593e02
 
288057e9
             BIT;
             if (!bits) { /* BYTE copy */
                 if (ccur - compd >= szd || !CLI_ISCONTAINED(exe, exesz, ucur, 1))
                     error = 1;
                 else
                     *ucur++ = *ccur++;
                 continue;
             }
50593e02
 
288057e9
             BITS(2);
             if (bits == 3) { /* WORD backcopy */
                 uint8_t shifted, subbed = 31;
                 BITS(2);
                 shifted = bits + 5;
                 if (bits >= 2) {
                     shifted++;
                     subbed += 0x80;
                 }
                 backbytes = (1 << shifted) - subbed; /* 1h, 21h, 61h, 161h */
                 BITS(shifted);                       /* 5, 6, 8, 9 */
                 if (error || bits == 0x1ff) break;
                 backbytes += bits;
                 if (!CLI_ISCONTAINED(exe, exesz, ucur, 2) || !CLI_ISCONTAINED(exe, exesz, ucur - backbytes, 2)) {
                     error = 1;
                 } else {
                     ucur[0] = *(ucur - backbytes);
                     ucur[1] = *(ucur - backbytes + 1);
                     ucur += 2;
                 }
                 continue;
             }
50593e02
 
288057e9
             /* BLOCK backcopy */
             saved = bits; /* cmp al, 1 / pushf */
50593e02
 
288057e9
             BITS(3);
             if (bits < 6) {
                 backbytes = bits;
                 switch (bits) {
                     case 4: /* 10,11 */
                         backbytes++;
                     case 3: /* 8,9 */
                         BIT;
                         backbytes += bits;
                     case 0:
                     case 1:
                     case 2: /* 5,6,7 */
                         backbytes += 5;
                         break;
                     case 5: /* 12 */
                         backbytes = 12;
                         break;
                 }
                 BITS(backbytes);
                 bits += (1 << backbytes) - 31;
             } else if (bits == 6) {
                 BITS(0x0e);
                 bits += 0x1fe1;
             } else {
                 BITS(0x0f);
                 bits += 0x5fe1;
             }
50593e02
 
288057e9
             backbytes = bits;
50593e02
 
288057e9
             /* popf / jb */
             if (!saved) {
                 BIT;
                 if (!bits) {
                     BIT;
                     bits += 5;
                 } else {
                     BITS(3);
                     if (bits) {
                         bits += 6;
                     } else {
                         BITS(4);
                         if (bits) {
                             bits += 13;
                         } else {
                             uint8_t cnt      = 4;
                             uint16_t shifted = 0x0d;
50593e02
 
288057e9
                             do {
                                 if (cnt == 7) {
                                     cnt     = 0x0e;
                                     shifted = 0;
                                     break;
                                 }
                                 shifted = ((shifted + 2) << 1) - 1;
                                 BIT;
                                 cnt++;
                             } while (!bits);
                             BITS(cnt);
                             bits += shifted;
                         }
                     }
                 }
                 backsize = bits;
             } else {
                 backsize = saved + 2;
             }
 
             if (!CLI_ISCONTAINED(exe, exesz, ucur, backsize) || !CLI_ISCONTAINED(exe, exesz, ucur - backbytes, backsize))
                 error = 1;
             else
                 while (backsize--) {
                     *ucur = *(ucur - backbytes);
                     ucur++;
                 }
         }
         free(compd);
         if (error) {
             cli_dbgmsg("WWPack: decompression error\n");
             break;
         }
         if (error || !*structs++) break;
50593e02
     }
 
6c03dc5d
     if (CL_SUCCESS == error) {
288057e9
         if (pe + 6 > exesz || pe + 7 > exesz || pe + 0x28 > exesz ||
             pe + 0x50 > exesz || pe + 0x14 > exesz)
             return CL_EFORMAT;
6c03dc5d
 
288057e9
         exe[pe + 6] = (uint8_t)scount;
         exe[pe + 7] = (uint8_t)(scount >> 8);
         if (!CLI_ISCONTAINED(wwsect, sects[scount].rsz, wwsect + 0x295, 4))
             cli_dbgmsg("WWPack: unpack memory address out of bounds.\n");
         else
             cli_writeint32(&exe[pe + 0x28], cli_readint32(wwsect + 0x295) + sects[scount].rva + 0x299);
         cli_writeint32(&exe[pe + 0x50], cli_readint32(&exe[pe + 0x50]) - sects[scount].vsz);
50593e02
 
288057e9
         structs = &exe[(0xffff & cli_readint32(&exe[pe + 0x14])) + pe + 0x18];
         for (i = 0; i < scount; i++) {
             if (!CLI_ISCONTAINED(exe, exesz, structs, 0x28)) {
                 cli_dbgmsg("WWPack: structs pointer out of bounds\n");
                 return CL_EFORMAT;
             }
71990820
 
288057e9
             cli_writeint32(structs + 8, sects[i].vsz);
             cli_writeint32(structs + 12, sects[i].rva);
             cli_writeint32(structs + 16, sects[i].vsz);
             cli_writeint32(structs + 20, sects[i].rva);
             structs += 0x28;
         }
         if (!CLI_ISCONTAINED(exe, exesz, structs, 0x28)) {
             cli_dbgmsg("WWPack: structs pointer out of bounds\n");
             return CL_EFORMAT;
         }
71990820
 
288057e9
         memset(structs, 0, 0x28);
6c03dc5d
         if (cli_writen(desc, exe, exesz) != (size_t)exesz) {
             error = CL_EWRITE;
         }
288057e9
     }
     return error;
50593e02
 }