libclamav/unsp.c
81030038
 /*
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
81030038
  *
  *  This program is free software; you can redistribute it and/or modify
632be7ba
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
81030038
  *
  *  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.
  */
 
 /*
 ** unsp.c
 **
 ** 11/10/2k6 - Merge started.
 **
 */
 
 /*
 ** Plays around with NsPack compressed executables
 **
 ** This piece of code is dedicated to Damian Put
 ** who I made a successful and wealthy man.
 **
 ** Damian, you owe me a pint!
 */
 
 /*
 ** TODO:
 **
 ** - Investigate the "unused" code in NsPack
 ** - Fetch all the nspacked samples from the zoo and run extensive testing
 ** - Add bound checks
 ** - Test against the zoo again
 ** - Perform regression testing against the full zoo 
 ** - check nested
 ** - look at the 64bit version (one of these days)
 **
 */
 
 /* 
 
    FIXME: clean this rubbish
 
 
 init_and_check_dll_loadflags();
 
 nsp1:004359FE                 add     edi, [ebp-28Dh]
 nsp1:00435A04                 mov     ebx, edi
 nsp1:00435A06                 cmp     dword ptr [edi], 0
 nsp1:00435A09                 jnz     short loc_435A15
 nsp1:00435A0B                 add     edi, 4
 nsp1:00435A0E                 mov     ecx, 0
 nsp1:00435A13                 jmp     short loc_435A2B
 nsp1:00435A15 ; ---------------------------------------------------------------------------
 nsp1:00435A15
 nsp1:00435A15 loc_435A15:                             ; CODE XREF: start+349EEj
 nsp1:00435A15                 mov     ecx, 1
 nsp1:00435A1A                 add     edi, [ebx]
 nsp1:00435A1C                 add     ebx, 4
 nsp1:00435A1F
 nsp1:00435A1F loc_435A1F:                             ; CODE XREF: start+34A3Dj
 nsp1:00435A1F                 cmp     dword ptr [ebx], 0
 nsp1:00435A22                 jz      short loc_435A5A
 nsp1:00435A24                 add     [ebx], edx
 nsp1:00435A26                 mov     esi, [ebx]
 nsp1:00435A28                 add     edi, [ebx+4]
 nsp1:00435A2B
 nsp1:00435A2B loc_435A2B:                             ; CODE XREF: start+349F8j
 nsp1:00435A2B                 push    edi
 nsp1:00435A2C                 push    ecx
 nsp1:00435A2D                 push    edx
 nsp1:00435A2E                 push    ebx
 nsp1:00435A2F                 push    dword ptr [ebp-1D1h] ; VirtualFree
 nsp1:00435A35                 push    dword ptr [ebp-1D5h] ; alloc
 nsp1:00435A3B                 mov     edx, esi
 nsp1:00435A3D                 mov     ecx, edi
 nsp1:00435A3F                 mov     eax, offset get_byte
 nsp1:00435A44                 int     3               ; Trap to Debugger
 nsp1:00435A45                 add     eax, 5A9h
 nsp1:00435A4A                 call    eax ; real_unpack ; edx=401000
 nsp1:00435A4A                                         ; ecx=436282
 nsp1:00435A4C                 pop     ebx
 nsp1:00435A4D                 pop     edx
 nsp1:00435A4E                 pop     ecx
 nsp1:00435A4F                 pop     edi
 nsp1:00435A50                 cmp     ecx, 0
 nsp1:00435A53                 jz      short loc_435A5A
 nsp1:00435A55                 add     ebx, 8
 nsp1:00435A58                 jmp     short loc_435A1F
 nsp1:00435A5A ; ---------------------------------------------------------------------------
 nsp1:00435A5A
 nsp1:00435A5A loc_435A5A:                             ; CODE XREF: start+34A07j
 nsp1:00435A5A                                         ; start+34A38j
 nsp1:00435A5A                 push    8000h
 
 */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdlib.h>
 
 #include "clamav.h"
 #include "others.h"
 #include "rebuildpe.h"
57866af1
 #include "execs.h"
81030038
 #include "unsp.h"
 
 /* real_unpack(start_of_stuff, dest, malloc, free); */
288057e9
 uint32_t unspack(const char *start_of_stuff, char *dest, cli_ctx *ctx, uint32_t rva, uint32_t base, uint32_t ep, int file)
 {
     uint8_t c = *start_of_stuff;
     uint32_t i, firstbyte, tre, allocsz, tablesz, dsize, ssize;
     uint16_t *table;
     char *dst       = dest;
     const char *src = start_of_stuff + 0xd;
     struct cli_exe_section section;
 
     if (c >= 0xe1) return 1;
 
     if (c >= 0x2d) {
         firstbyte = i = c / 0x2d;
         do {
             c += 0xd3;
         } while (--i);
     } else
         firstbyte = 0;
 
     if (c >= 9) {
         allocsz = i = c / 9;
         do {
             c += 0xf7;
         } while (--i);
     } else
         allocsz = 0;
 
     tre     = c;
     i       = allocsz;
     c       = (tre + i) & 0xff;
     tablesz = ((0x300 << c) + 0x736) * sizeof(uint16_t);
 
     if (cli_checklimits("nspack", ctx, tablesz, 0, 0) != CL_CLEAN)
         return 1; /* Should be ~15KB, if it's so big it's prolly just not nspacked */
 
     cli_dbgmsg("unsp: table size = %d\n", tablesz);
     if (!(table = cli_malloc(tablesz))) {
         cli_dbgmsg("unspack: Unable to allocate memory for table\n");
         return 1;
     }
81030038
 
288057e9
     dsize = cli_readint32(start_of_stuff + 9);
     ssize = cli_readint32(start_of_stuff + 5);
     if (ssize <= 13) {
         free(table);
         return 1;
     }
81030038
 
288057e9
     tre = very_real_unpack(table, tablesz, tre, allocsz, firstbyte, src, ssize, dst, dsize);
     free(table);
     if (tre) return 1;
81030038
 
288057e9
     section.raw = 0;
     section.rsz = dsize;
     section.vsz = dsize;
     section.rva = rva;
     return !cli_rebuildpe(dest, &section, 1, base, ep, 0, 0, file);
 }
81030038
 
288057e9
 uint32_t very_real_unpack(uint16_t *table, uint32_t tablesz, uint32_t tre, uint32_t allocsz, uint32_t firstbyte, const char *src, uint32_t ssize, char *dst, uint32_t dsize)
 {
     struct UNSP read_struct;
     uint32_t i = (0x300 << ((allocsz + tre) & 0xff)) + 0x736;
 
     uint32_t previous_bit         = 0;
     uint32_t unpacked_so_far      = 0;
     uint32_t backbytes            = 1;
     uint32_t oldbackbytes         = 1;
     uint32_t old_oldbackbytes     = 1;
     uint32_t old_old_oldbackbytes = 1;
 
     uint32_t damian = 0;
     uint32_t put    = (1 << (allocsz & 0xff)) - 1;
 
     uint32_t bielle = 0;
 
     firstbyte = (1 << (firstbyte & 0xff)) - 1;
 
     if (tablesz < i * sizeof(uint16_t)) return 2;
 
     /* init table */
     while (i) table[--i] = 0x400;
 
     /* table noinit */
 
     /* get_five - inlined */
     read_struct.error    = 0;
     read_struct.oldval   = 0;
     read_struct.src_curr = src;
     read_struct.bitmap   = 0xffffffff;
     read_struct.src_end  = src + ssize - 13;
     read_struct.table    = (char *)table;
     read_struct.tablesz  = tablesz;
 
     for (i = 0; i < 5; i++) read_struct.oldval = (read_struct.oldval << 8) | get_byte(&read_struct);
     if (read_struct.error) return 1;
     /* if (!dsize) return 0; - checked in pe.c */
 
     /* very_unpacking_loop */
 
     while (1) {
         uint32_t backsize = firstbyte & unpacked_so_far;
         uint32_t tpos;
         uint32_t temp = damian;
 
         if (read_struct.error) return 1; /* checked once per mainloop, keeps the code readable and it's still safe */
 
         if (!getbit_from_table(&table[(damian << 4) + backsize], &read_struct)) { /* no_mainbit */
 
             uint32_t shft = 8 - (tre & 0xff);
             shft &= 0xff;
             tpos = (bielle >> shft) + ((put & unpacked_so_far) << (tre & 0xff));
             tpos *= 3;
             tpos <<= 8;
 
             if ((int32_t)damian >= 4) {       /* signed */
                 if ((int32_t)damian >= 0xa) { /* signed */
                     damian -= 6;
                 } else {
                     damian -= 3;
                 }
             } else {
                 damian = 0;
             }
 
             /* 44847E */
             if (previous_bit) {
                 if (!CLI_ISCONTAINED(dst, dsize, &dst[unpacked_so_far - backbytes], 1)) return 1;
                 ssize        = (ssize & 0xffffff00) | (uint8_t)dst[unpacked_so_far - backbytes]; /* FIXME! ssize is not static */
                 bielle       = get_100_bits_from_tablesize(&table[tpos + 0x736], &read_struct, ssize);
                 previous_bit = 0;
             } else {
                 bielle = get_100_bits_from_table(&table[tpos + 0x736], &read_struct);
             }
 
             /* unpack_one_byte - duplicated */
             if (!CLI_ISCONTAINED(dst, dsize, &dst[unpacked_so_far], 1)) return 1;
             dst[unpacked_so_far] = bielle;
             unpacked_so_far++;
             if (unpacked_so_far >= dsize) return 0;
             continue;
 
         } else { /* got_mainbit */
 
             bielle = previous_bit = 1;
 
             if (getbit_from_table(&table[damian + 0xc0], &read_struct)) {
                 if (!getbit_from_table(&table[damian + 0xcc], &read_struct)) {
                     tpos = damian + 0xf;
                     tpos <<= 4;
                     tpos += backsize;
                     if (!getbit_from_table(&table[tpos], &read_struct)) {
                         if (!unpacked_so_far) return bielle; /* FIXME: WTF?! */
 
                         damian = 2 * ((int32_t)damian >= 7) + 9; /* signed */
                         if (!CLI_ISCONTAINED(dst, dsize, &dst[unpacked_so_far - backbytes], 1)) return 1;
                         bielle = (uint8_t)dst[unpacked_so_far - backbytes];
                         /* unpack_one_byte - real */
                         dst[unpacked_so_far] = bielle;
                         unpacked_so_far++;
                         if (unpacked_so_far >= dsize) return 0;
                         continue;
 
                     } else { /* gotbit_tre */
                         backsize = get_n_bits_from_tablesize(&table[0x534], &read_struct, backsize);
                         damian   = ((int32_t)damian >= 7); /* signed */
                         damian   = ((damian - 1) & 0xfffffffd) + 0xb;
                         /* jmp checkloop_and_backcopy (uses edx) */
                     }    /* gotbit_uno ends */
                 } else { /* gotbit_due */
                     if (!getbit_from_table(&table[damian + 0xd8], &read_struct)) {
                         tpos = oldbackbytes;
                     } else {
                         if (!getbit_from_table(&table[damian + 0xe4], &read_struct)) {
                             tpos = old_oldbackbytes;
                         } else {
                             /* set_old_old_oldback */
                             tpos                 = old_old_oldbackbytes;
                             old_old_oldbackbytes = old_oldbackbytes;
                         }
                         /* set_old_oldback */
                         old_oldbackbytes = oldbackbytes;
                     }
                     /* set_oldback */
                     oldbackbytes = backbytes;
                     backbytes    = tpos;
 
                     backsize = get_n_bits_from_tablesize(&table[0x534], &read_struct, backsize);
                     damian   = ((int32_t)damian >= 7); /* signed */
                     damian   = ((damian - 1) & 0xfffffffd) + 0xb;
                     /* jmp checkloop_and_backcopy (uses edx) */
                 }    /* gotbit_due ends */
             } else { /* gotbit_uno */
 
                 old_old_oldbackbytes = old_oldbackbytes;
                 old_oldbackbytes     = oldbackbytes;
                 oldbackbytes         = backbytes;
 
                 damian = ((int32_t)damian >= 7); /* signed */
                 damian = ((damian - 1) & 0xfffffffd) + 0xa;
 
                 backsize = get_n_bits_from_tablesize(&table[0x332], &read_struct, backsize);
 
                 tpos = ((int32_t)backsize >= 4) ? 3 : backsize; /* signed */
                 tpos <<= 6;
                 tpos = get_n_bits_from_table(&table[0x1b0 + tpos], 6, &read_struct);
 
                 if (tpos >= 4) { /* signed */
 
                     uint32_t s = tpos;
                     s >>= 1;
                     s--;
 
                     temp = (tpos & bielle) | 2;
                     temp <<= (s & 0xff);
 
                     if ((int32_t)tpos < 0xe) {
                         temp += get_bb(&table[(temp - tpos) + 0x2af], s, &read_struct);
                     } else {
                         s += 0xfffffffc;
                         tpos = get_bitmap(&read_struct, s);
                         tpos <<= 4;
                         temp += tpos;
                         temp += get_bb(&table[0x322], 4, &read_struct);
                     }
                 } else {
                     /* gotbit_uno_out1 */
                     backbytes = temp = tpos;
                 }
                 /* gotbit_uno_out2 */
                 backbytes = temp + 1;
                 /* jmp checkloop_and_backcopy (uses edx) */
             } /* gotbit_uno ends */
 
             /* checkloop_and_backcopy */
             if (!backbytes) return 0;                       /* very_real_unpack_end */
             if (backbytes > unpacked_so_far) return bielle; /* FIXME: WTF?! */
 
             backsize += 2;
 
             if (!CLI_ISCONTAINED(dst, dsize, &dst[unpacked_so_far], backsize) ||
                 !CLI_ISCONTAINED(dst, dsize, &dst[unpacked_so_far - backbytes], backsize)) {
                 cli_dbgmsg("%p %x %p %x\n", dst, dsize, &dst[unpacked_so_far], backsize);
                 return 1;
             }
 
             do {
                 dst[unpacked_so_far] = dst[unpacked_so_far - backbytes];
                 unpacked_so_far++;
             } while (--backsize && unpacked_so_far < dsize);
             bielle = (uint8_t)dst[unpacked_so_far - 1];
 
             if (unpacked_so_far >= dsize) return 0;
 
         } /* got_mainbit ends */
 
     } /* while true ends */
 }
81030038
 
288057e9
 uint32_t get_byte(struct UNSP *read_struct)
 {
81030038
 
288057e9
     uint32_t ret;
81030038
 
288057e9
     if (read_struct->src_curr >= read_struct->src_end) {
         read_struct->error = 1;
         return 0xff;
     }
     ret = *(read_struct->src_curr);
     read_struct->src_curr++;
     return ret & 0xff;
81030038
 }
 
288057e9
 int getbit_from_table(uint16_t *intable, struct UNSP *read_struct)
 {
81030038
 
288057e9
     uint32_t nval;
     if (!CLI_ISCONTAINED((char *)read_struct->table, read_struct->tablesz, (char *)intable, sizeof(uint16_t))) {
         read_struct->error = 1;
         return 0xff;
     }
     nval = *intable * (read_struct->bitmap >> 0xb);
 
     if (read_struct->oldval < nval) { /* unsigned */
         uint32_t sval;
         read_struct->bitmap = nval;
         nval                = *intable;
         sval                = 0x800 - nval;
         sval                = CLI_SRS((int32_t)sval, 5); /* signed */
         sval += nval;
         *intable = sval;
         if (read_struct->bitmap < 0x1000000) { /* unsigned */
             read_struct->oldval = (read_struct->oldval << 8) | get_byte(read_struct);
             read_struct->bitmap <<= 8;
         }
         return 0;
81030038
     }
 
288057e9
     read_struct->bitmap -= nval;
     read_struct->oldval -= nval;
81030038
 
288057e9
     nval = *intable;
     nval -= (nval >> 5); /* word, unsigned */
     *intable = nval;
81030038
 
288057e9
     if (read_struct->bitmap < 0x1000000) { /* unsigned */
         read_struct->oldval = (read_struct->oldval << 8) | get_byte(read_struct);
         read_struct->bitmap <<= 8;
     }
81030038
 
288057e9
     return 1;
81030038
 }
 
288057e9
 uint32_t get_100_bits_from_tablesize(uint16_t *intable, struct UNSP *read_struct, uint32_t ssize)
 {
 
     uint32_t count = 1;
 
     while (count < 0x100) {
         uint32_t lpos, tpos;
         lpos  = ssize & 0xff;
         ssize = (ssize & 0xffffff00) | ((lpos << 1) & 0xff);
         lpos >>= 7;
         tpos = lpos + 1;
         tpos <<= 8;
         tpos += count;
         tpos  = getbit_from_table(&intable[tpos], read_struct);
         count = (count * 2) | tpos;
         if (lpos != tpos) {
             /* second loop */
             while (count < 0x100)
                 count = (count * 2) | getbit_from_table(&intable[count], read_struct);
         }
81030038
     }
288057e9
     return count & 0xff;
81030038
 }
 
288057e9
 uint32_t get_100_bits_from_table(uint16_t *intable, struct UNSP *read_struct)
 {
     uint32_t count = 1;
81030038
 
288057e9
     while (count < 0x100)
         count = (count * 2) | getbit_from_table(&intable[count], read_struct);
     return count & 0xff;
81030038
 }
 
288057e9
 uint32_t get_n_bits_from_table(uint16_t *intable, uint32_t bits, struct UNSP *read_struct)
 {
     uint32_t count = 1;
     uint32_t bitcounter;
81030038
 
288057e9
     /*  if (bits) { always set! */
     bitcounter = bits;
     while (bitcounter--)
         count = count * 2 + getbit_from_table(&intable[count], read_struct);
     /*  } */
81030038
 
288057e9
     return count - (1 << (bits & 0xff));
81030038
 }
 
288057e9
 uint32_t get_n_bits_from_tablesize(uint16_t *intable, struct UNSP *read_struct, uint32_t backsize)
 {
81030038
 
288057e9
     if (!getbit_from_table(intable, read_struct))
         return get_n_bits_from_table(&intable[(backsize << 3) + 2], 3, read_struct);
81030038
 
288057e9
     if (!getbit_from_table(&intable[1], read_struct))
         return 8 + get_n_bits_from_table(&intable[(backsize << 3) + 0x82], 3, read_struct);
81030038
 
288057e9
     return 0x10 + get_n_bits_from_table(&intable[0x102], 8, read_struct);
81030038
 }
 
288057e9
 uint32_t get_bb(uint16_t *intable, uint32_t back, struct UNSP *read_struct)
 {
     uint32_t pos = 1;
     uint32_t bb  = 0;
     uint32_t i;
81030038
 
288057e9
     if ((int32_t)back <= 0) /* signed */
         return 0;
81030038
 
288057e9
     for (i = 0; i < back; i++) {
         uint32_t bit = getbit_from_table(&intable[pos], read_struct);
         pos          = (pos * 2) + bit;
         bb |= (bit << i);
81030038
     }
288057e9
     return bb;
 }
 
 uint32_t get_bitmap(struct UNSP *read_struct, uint32_t bits)
 {
     uint32_t retv = 0;
 
     if ((int32_t)bits <= 0) return 0; /* signed */
 
     while (bits--) {
         read_struct->bitmap >>= 1; /* unsigned */
         retv <<= 1;
         if (read_struct->oldval >= read_struct->bitmap) { /* unsigned */
             read_struct->oldval -= read_struct->bitmap;
             retv |= 1;
         }
         if (read_struct->bitmap < 0x1000000) {
             read_struct->bitmap <<= 8;
             read_struct->oldval = (read_struct->oldval << 8) | get_byte(read_struct);
         }
81030038
     }
288057e9
     return retv;
81030038
 }