libclamav/aspack.c
2f73b977
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
2023340a
  *
  *  Authors: Luciano Giuseppe 'Pnluck', Alberto Wu
2f73b977
  *
  *  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.
  */
 
 #include <string.h>
b2e7c931
 
60d8d2c3
 #include "clamav.h"
2f73b977
 #include "execs.h"
 #include "others.h"
 #include "rebuildpe.h"
c98bc23f
 #include "aspack.h"
2f73b977
 
288057e9
 #define ASPACK_BLOCKS_OFFSET_212 0x57c
 #define ASPACK_BLOCKS_OFFSET_OTHER 0x5d8
 #define ASPACK_BLOCKS_OFFSET_242 0x5e4
2f73b977
 
288057e9
 #define ASPACK_STR_INIT_MLT_OFFSET_212 0x70e
 #define ASPACK_STR_INIT_MLT_OFFSET_OTHER 0x76a
 #define ASPACK_STR_INIT_MLT_OFFSET_242 0x776
86e29b67
 
288057e9
 #define ASPACK_COMP_BLOCK_OFFSET_212 0x6d6
 #define ASPACK_COMP_BLOCK_OFFSET_OTHER 0x732
 #define ASPACK_COMP_BLOCK_OFFSET_242 0x73e
86e29b67
 
288057e9
 #define ASPACK_WRKBUF_OFFSET_212 0x148
 #define ASPACK_WRKBUF_OFFSET_OTHER 0x13a
 #define ASPACK_WRKBUF_OFFSET_242 0x148
86e29b67
 
4b4c98e0
 #define ASPACK_OEP_OFFSET_212 0x39b
 #define ASPACK_OEP_OFFSET_OTHER 0x401
 #define ASPACK_OEP_OFFSET_242 0x40d
 
2f73b977
 struct DICT_HELPER {
288057e9
     uint32_t *starts;
     uint8_t *ends;
     uint32_t size;
2f73b977
 };
 
 struct ASPK {
288057e9
     uint32_t bitpos;
     uint32_t hash;
     uint32_t init_array[58];
     struct DICT_HELPER dict_helper[4];
     uint8_t *input;
     uint8_t *iend;
     uint8_t *decrypt_dict;
     uint32_t decarray3[4][24];
     uint32_t decarray4[4][24];
     int dict_ok;
     uint8_t array2[758];
     uint8_t array1[19];
2f73b977
 };
 
288057e9
 static inline int readstream(struct ASPK *stream)
 {
     while (stream->bitpos >= 8) {
         if (stream->input >= stream->iend) return 0;
         stream->hash = (stream->hash << 8) | *stream->input;
         stream->input++;
         stream->bitpos -= 8;
     }
     return 1;
2f73b977
 }
 
288057e9
 static uint32_t getdec(struct ASPK *stream, uint8_t which, int *err)
 {
     uint32_t ret;
     uint8_t pos;
     uint32_t *d3 = stream->decarray3[which];
     uint32_t *d4 = stream->decarray4[which];
2f73b977
 
288057e9
     *err = 1;
2f73b977
 
288057e9
     if (!readstream(stream)) return 0;
2f73b977
 
288057e9
     ret = (stream->hash >> (8 - stream->bitpos)) & 0xfffe00;
2f73b977
 
288057e9
     if (ret < d3[8]) {
         if ((ret >> 16) >= 0x100) return 0;
         if (!(pos = stream->dict_helper[which].ends[ret >> 16]) || pos >= 24) return 0; /* 0<pos<24 */
2f73b977
     } else {
288057e9
         if (ret < d3[10]) {
             if (ret < d3[9])
                 pos = 9;
             else
                 pos = 10;
         } else {
             if (ret < d3[11])
                 pos = 11;
             else {
                 if (ret < d3[12])
                     pos = 12;
                 else {
                     if (ret < d3[13])
                         pos = 13;
                     else {
                         if (ret < d3[14])
                             pos = 14;
                         else
                             pos = 15;
                     }
                 }
             }
         }
2f73b977
     }
 
288057e9
     stream->bitpos += pos;
     ret = ((ret - d3[pos - 1]) >> (24 - pos)) + d4[pos];
2f73b977
 
288057e9
     if (ret >= stream->dict_helper[which].size) return 0;
     ret = stream->dict_helper[which].starts[ret];
2f73b977
 
288057e9
     *err = 0;
     return ret;
2f73b977
 }
 
288057e9
 static uint8_t build_decrypt_array(struct ASPK *stream, uint8_t *array, uint8_t which)
 {
     uint32_t sum = 0, counter = 23, i, endoff = 0, bus[18], dict[18];
 
     uint32_t *d3 = stream->decarray3[which];
     uint32_t *d4 = stream->decarray4[which];
2f73b977
 
288057e9
     memset(bus, 0, sizeof(bus));
     memset(dict, 0, sizeof(dict));
2f73b977
 
288057e9
     for (i = 0; i < stream->dict_helper[which].size; i++) {
         /* within bounds - see comments in build_decrypt_dictionaries */
         if (array[i] > 17) return 0;
         bus[array[i]]++;
     }
2f73b977
 
288057e9
     d3[0] = 0;
     d4[0] = 0;
2f73b977
 
288057e9
     i = 0;
     while (counter >= 9) { /* 0<=i<=14 */
         sum += (bus[i + 1] << counter);
         if (sum > 0x1000000) return 0;
2f73b977
 
288057e9
         d3[i + 1] = sum;
         d4[i + 1] = dict[i + 1] = bus[i] + d4[i];
2f73b977
 
288057e9
         if (counter >= 0x10) {
             uint32_t old = endoff;
             endoff       = d3[i + 1] >> 0x10;
             if (endoff - old) {
                 if (!CLI_ISCONTAINED(stream->dict_helper[which].ends, 0x100, stream->dict_helper[which].ends + old, endoff - old)) return 0;
                 memset((stream->dict_helper[which].ends + old), i + 1, endoff - old);
             }
         }
2f73b977
 
288057e9
         i++;
         counter--;
2f73b977
     }
 
288057e9
     if (sum != 0x1000000) return 0;
2f73b977
 
288057e9
     i = 0;
     for (i = 0; i < stream->dict_helper[which].size; i++) {
         if (array[i]) { /* within bounds - see above */
             if (array[i] > 17) return 0;
             if (dict[array[i]] >= stream->dict_helper[which].size) return 0;
             stream->dict_helper[which].starts[dict[array[i]]] = i;
             dict[array[i]]++;
         }
2f73b977
     }
 
288057e9
     return 1;
2f73b977
 }
 
288057e9
 static uint8_t getbits(struct ASPK *stream, uint32_t num, int *err)
 {
     uint8_t retvalue;
2f73b977
 
288057e9
     if (!readstream(stream)) {
         *err = 1;
         return 0;
     }
2f73b977
 
288057e9
     *err     = 0;
     retvalue = ((stream->hash >> (8 - stream->bitpos)) & 0xffffff) >> (24 - num);
     stream->bitpos += num;
2f73b977
 
288057e9
     return retvalue;
2f73b977
 }
 
288057e9
 static int build_decrypt_dictionaries(struct ASPK *stream)
 {
     unsigned int counter;
     uint32_t ret;
     int oob;
2f73b977
 
288057e9
     if (!getbits(stream, 1, &oob)) memset(stream->decrypt_dict, 0, 0x2f5);
2f73b977
     if (oob) return 0;
 
288057e9
     for (counter = 0; counter < 19; counter++) {
         stream->array1[counter] = getbits(stream, 4, &oob);
         if (oob) return 0;
2f73b977
     }
288057e9
 
     if (!build_decrypt_array(stream, stream->array1, 3)) return 0; /* array1[19] - [3].size=19 */
 
     counter = 0;
     while (counter < 757) {
         ret = getdec(stream, 3, &oob);
         if (oob) return 0;
         if (ret >= 16) {
             if (ret != 16) {
                 if (ret == 17)
                     ret = 3 + getbits(stream, 3, &oob);
                 else
                     ret = 11 + getbits(stream, 7, &oob);
                 if (oob) return 0;
                 while (ret) {
                     if (counter >= 757) break;
                     stream->array2[1 + counter] = 0;
                     counter++;
                     ret--;
                 }
             } else {
                 ret = 3 + getbits(stream, 2, &oob);
                 if (oob) return 0;
                 while (ret) {
                     if (counter >= 757) break;
                     stream->array2[1 + counter] = stream->array2[counter];
                     counter++;
                     ret--;
                 }
             }
         } else {
             stream->array2[1 + counter] = (stream->decrypt_dict[counter] + ret) & 0xF;
             counter++;
         }
2f73b977
     }
 
288057e9
     if (!build_decrypt_array(stream, &stream->array2[1], 0) /* array2[758-1=757] - [0].size=721 */ || !build_decrypt_array(stream, &stream->array2[722], 1) /* array2[758-722=36] - [1].size=28 */ || !build_decrypt_array(stream, &stream->array2[750], 2) /* array2[758-750=8] - [2].size=8 */) return 0;
2f73b977
 
288057e9
     stream->dict_ok = 0;
     for (counter = 0; counter < 8; counter++) {
         if (stream->array2[750 + counter] != 3) {
             stream->dict_ok = 1;
             break;
         }
     }
2f73b977
 
288057e9
     memcpy(stream->decrypt_dict, &stream->array2[1], 757);
2f73b977
 
288057e9
     return 1;
 }
2f73b977
 
288057e9
 static int decrypt(struct ASPK *stream, uint8_t *stuff, uint32_t size, uint8_t *output)
 {
     /* ep+6d6 -> ep+748  = 0x72*/
     uint32_t gen, backsize, backbytes, useold, counter = 0;
     uint32_t hist[4] = {0, 0, 0, 0};
     int oob;
 
     cli_dbgmsg("Aspack: decrypt size:%x\n", size);
     while (counter < size) {
         gen = getdec(stream, 0, &oob);
         if (oob) return 0;
         if (gen < 256) { /* implied within bounds */
             output[counter] = (uint8_t)gen;
             counter++;
             continue;
         }
         if (gen >= 720) {
             if (!build_decrypt_dictionaries(stream)) return 0;
             continue;
         }
         if ((backbytes = (gen - 256) >> 3) >= 58) return 0; /* checks init_array + stuff */
         backsize = ((gen - 256) & 7) + 2;
         if ((backsize - 2) == 7) {
             uint8_t hlp;
             gen = getdec(stream, 1, &oob);
             if (oob || gen >= 0x56) return 0;
             hlp = stuff[gen + 0x1c];
             if (!readstream(stream)) return 0;
             backsize += stuff[gen] + (((stream->hash >> (8 - stream->bitpos)) & 0xffffff) >> (0x18 - hlp));
             stream->bitpos += hlp;
         }
2f73b977
 
288057e9
         useold = stream->init_array[backbytes];
         gen    = stuff[backbytes + 0x38];
 
         if (!stream->dict_ok || gen < 3) {
             if (!readstream(stream)) return 0;
             useold += ((stream->hash >> (8 - stream->bitpos)) & 0xffffff) >> (24 - gen);
             stream->bitpos += gen;
         } else {
             gen -= 3;
             if (!readstream(stream)) return 0;
             useold += ((((stream->hash >> (8 - stream->bitpos)) & 0xffffff) >> (24 - gen)) * 8);
             stream->bitpos += gen;
             useold += getdec(stream, 2, &oob);
             if (oob) return 0;
         }
2f73b977
 
288057e9
         if (useold < 3) {
             backbytes = hist[useold];
             if (useold != 0) {
                 hist[useold] = hist[0];
                 hist[0]      = backbytes;
             }
         } else {
             hist[2] = hist[1];
             hist[1] = hist[0];
             hist[0] = backbytes = useold - 3;
         }
2f73b977
 
288057e9
         backbytes++;
2f73b977
 
288057e9
         if (!backbytes || backbytes > counter || backsize > size - counter) return 0;
         while (backsize--) {
             output[counter] = output[counter - backbytes];
             counter++;
         }
2f73b977
     }
 
288057e9
     return 1;
2f73b977
 }
 
288057e9
 static int decomp_block(struct ASPK *stream, uint32_t size, uint8_t *stuff, uint8_t *output)
 {
     memset(stream->decarray3, 0, sizeof(stream->decarray3));
     memset(stream->decarray4, 0, sizeof(stream->decarray4));
     memset(stream->decrypt_dict, 0, 757);
     stream->bitpos = 0x20;
     if (!build_decrypt_dictionaries(stream)) return 0;
     return decrypt(stream, stuff, size, output);
2f73b977
 }
 
288057e9
 #define INIT_DICT_HELPER(n, sz)                                    \
     stream.dict_helper[n].starts = (uint32_t *)wrkbuf;             \
     stream.dict_helper[n].ends   = &wrkbuf[sz * sizeof(uint32_t)]; \
     stream.dict_helper[n].size   = sz;                             \
     wrkbuf                       = &wrkbuf[sz * sizeof(uint32_t) + 0x100];
2f73b977
 
86e29b67
 int unaspack(uint8_t *image, unsigned int size, struct cli_exe_section *sections, uint16_t sectcount, uint32_t ep, uint32_t base, int f, aspack_version_t version)
 {
288057e9
     struct ASPK stream;
     uint32_t i = 0, j = 0;
     uint8_t *blocks    = NULL, *wrkbuf;
     uint32_t block_rva = 1, block_size;
     struct cli_exe_section *outsects;
 
4b4c98e0
     uint32_t blocks_offset, stream_init_multiplier_offset, comp_block_offset, wrkbuf_offset, oep_offset;
288057e9
 
     switch (version) {
         case ASPACK_VER_212:
             cli_dbgmsg("Aspack: Attempting to unpack Aspack 2.12.\n");
             blocks_offset                 = ASPACK_BLOCKS_OFFSET_212;
             stream_init_multiplier_offset = ASPACK_STR_INIT_MLT_OFFSET_212;
             comp_block_offset             = ASPACK_COMP_BLOCK_OFFSET_212;
             wrkbuf_offset                 = ASPACK_WRKBUF_OFFSET_212;
4b4c98e0
             oep_offset                    = ASPACK_OEP_OFFSET_212;
288057e9
             break;
         case ASPACK_VER_OTHER:
             cli_dbgmsg("Aspack: Attempting to unpack Aspack >2.12, <2.42.\n");
             blocks_offset                 = ASPACK_BLOCKS_OFFSET_OTHER;
             stream_init_multiplier_offset = ASPACK_STR_INIT_MLT_OFFSET_OTHER;
             comp_block_offset             = ASPACK_COMP_BLOCK_OFFSET_OTHER;
             wrkbuf_offset                 = ASPACK_WRKBUF_OFFSET_OTHER;
4b4c98e0
             oep_offset                    = ASPACK_OEP_OFFSET_OTHER;
288057e9
             break;
         case ASPACK_VER_242:
             cli_dbgmsg("Aspack: Attempting to unpack Aspack 2.42.\n");
             blocks_offset                 = ASPACK_BLOCKS_OFFSET_242;
             stream_init_multiplier_offset = ASPACK_STR_INIT_MLT_OFFSET_242;
             comp_block_offset             = ASPACK_COMP_BLOCK_OFFSET_242;
             wrkbuf_offset                 = ASPACK_WRKBUF_OFFSET_242;
4b4c98e0
             oep_offset                    = ASPACK_OEP_OFFSET_242;
288057e9
             break;
         default:
             cli_dbgmsg("Aspack: Unexpected/Unknown version number.\n");
             return 0;
     }
 
     blocks = image + ep + blocks_offset;
 
     if (!(wrkbuf = cli_calloc(0x1800, sizeof(uint8_t)))) {
         cli_dbgmsg("Aspack: Unable to allocate dictionary\n");
         return 0;
86e29b67
     }
2f73b977
 
288057e9
     INIT_DICT_HELPER(0, 721); /* dictionary -> dictionary + b44 */
     INIT_DICT_HELPER(1, 28);  /* dictionary + c44 -> dictionary + cb4 */
     INIT_DICT_HELPER(2, 8);   /* dictionary + db4 -> dictionary + dd4 */
     INIT_DICT_HELPER(3, 19);  /* dictionary + ed4 -> dictionary + f20 */
     stream.decrypt_dict = wrkbuf;
2f73b977
 
288057e9
     stream.hash = 0x10000;
 
     for (i = 0; i < 58; i++) {
         stream.init_array[i] = j;
091a447a
         if (ep + i + stream_init_multiplier_offset < size) {
             j += (1 << image[ep + i + stream_init_multiplier_offset]);
         }
2f73b977
     }
288057e9
 
     memset(stream.array1, 0, sizeof(stream.array1));
     memset(stream.array2, 0, sizeof(stream.array2));
 
     i = 0;
     while (CLI_ISCONTAINED(image, size, blocks, 8) && (block_rva = cli_readint32(blocks)) && (block_size = cli_readint32(blocks + 4)) && CLI_ISCONTAINED(image, size, image + block_rva, block_size)) {
 
         cli_dbgmsg("Aspack: unpacking block rva:%x - sz:%x\n", block_rva, block_size);
         wrkbuf = (uint8_t *)cli_calloc(block_size + 0x10e, sizeof(uint8_t));
 
         if (!wrkbuf) {
             cli_dbgmsg("Aspack: Null work buff\n");
             break;
         }
         stream.input = wrkbuf;
         stream.iend  = &wrkbuf[block_size + 0x10e];
 
         memcpy(wrkbuf, image + block_rva, block_size);
 
         if (!decomp_block(&stream, block_size, &image[ep + comp_block_offset], image + block_rva)) {
             cli_dbgmsg("Aspack: decomp_block failed\n");
             free(wrkbuf);
             break;
         } else
             cli_dbgmsg("Aspack: decomp block succeed\n");
 
         free(wrkbuf);
 
         if (i == 0 && block_size > 7) { /* first sect j/c unrolling */
             while (i < block_size - 6) {
                 uint8_t curbyte = image[block_rva + i];
                 if (curbyte == 0xe8 || curbyte == 0xe9) {
                     wrkbuf = &image[block_rva + i + 1];
                     if (*wrkbuf == image[ep + wrkbuf_offset]) {
                         uint32_t target = cli_readint32(wrkbuf) & 0xffffff00;
                         CLI_ROL(target, 0x18);
                         cli_writeint32(wrkbuf, target - i);
                         i += 4;
                     }
                 }
                 i++;
             }
         }
         if (version == ASPACK_VER_212) {
             blocks += 8;
         } else {
             blocks += 12;
             block_size = cli_readint32(blocks + 4);
             while (!((block_size + 0x10e) & 0xffffffff)) {
                 blocks += 12;
                 block_size = cli_readint32(blocks + 4);
             }
86e29b67
         }
     }
288057e9
 
     cli_dbgmsg("Aspack: leaving loop all uncompressed\n");
 
     free(stream.dict_helper[0].starts);
     if (block_rva) {
         cli_dbgmsg("Aspack: unpacking failure\n");
         return 0;
     }
 
     if (sectcount > 2 && ep == sections[sectcount - 2].rva && !sections[sectcount - 1].rsz) {
         sectcount -= 2;
     }
     if (!(outsects = cli_malloc(sizeof(struct cli_exe_section) * sectcount))) {
         cli_dbgmsg("Aspack: OOM - rebuild failed\n");
         cli_writen(f, image, size);
         return 1; /* No whatsoheader - won't infloop in pe.c */
     }
     memcpy(outsects, sections, sizeof(struct cli_exe_section) * sectcount);
     for (i = 0; i < sectcount; i++) {
         outsects[i].raw = outsects[i].rva;
         outsects[i].rsz = outsects[i].vsz;
     }
4b4c98e0
 
     if (!cli_rebuildpe((char *)image, outsects, sectcount, base, cli_readint32(image + ep + oep_offset), 0, 0, f)) {
288057e9
         cli_dbgmsg("Aspack: rebuild failed\n");
         cli_writen(f, image, size);
86e29b67
     } else {
288057e9
         cli_dbgmsg("Aspack: successfully rebuilt\n");
2f73b977
     }
288057e9
     free(outsects);
     return 1;
2f73b977
 }