/* * Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 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. */ /* ** 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 #include "clamav.h" #include "others.h" #include "rebuildpe.h" #include "execs.h" #include "unsp.h" /* real_unpack(start_of_stuff, dest, malloc, free); */ 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; } dsize = cli_readint32(start_of_stuff + 9); ssize = cli_readint32(start_of_stuff + 5); if (ssize <= 13) { free(table); return 1; } tre = very_real_unpack(table, tablesz, tre, allocsz, firstbyte, src, ssize, dst, dsize); free(table); if (tre) return 1; section.raw = 0; section.rsz = dsize; section.vsz = dsize; section.rva = rva; return !cli_rebuildpe(dest, §ion, 1, base, ep, 0, 0, file); } 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 */ } uint32_t get_byte(struct UNSP *read_struct) { uint32_t ret; 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; } int getbit_from_table(uint16_t *intable, struct UNSP *read_struct) { 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; } read_struct->bitmap -= nval; read_struct->oldval -= nval; nval = *intable; nval -= (nval >> 5); /* word, unsigned */ *intable = nval; if (read_struct->bitmap < 0x1000000) { /* unsigned */ read_struct->oldval = (read_struct->oldval << 8) | get_byte(read_struct); read_struct->bitmap <<= 8; } return 1; } 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); } } return count & 0xff; } uint32_t get_100_bits_from_table(uint16_t *intable, struct UNSP *read_struct) { uint32_t count = 1; while (count < 0x100) count = (count * 2) | getbit_from_table(&intable[count], read_struct); return count & 0xff; } uint32_t get_n_bits_from_table(uint16_t *intable, uint32_t bits, struct UNSP *read_struct) { uint32_t count = 1; uint32_t bitcounter; /* if (bits) { always set! */ bitcounter = bits; while (bitcounter--) count = count * 2 + getbit_from_table(&intable[count], read_struct); /* } */ return count - (1 << (bits & 0xff)); } uint32_t get_n_bits_from_tablesize(uint16_t *intable, struct UNSP *read_struct, uint32_t backsize) { if (!getbit_from_table(intable, read_struct)) return get_n_bits_from_table(&intable[(backsize << 3) + 2], 3, read_struct); if (!getbit_from_table(&intable[1], read_struct)) return 8 + get_n_bits_from_table(&intable[(backsize << 3) + 0x82], 3, read_struct); return 0x10 + get_n_bits_from_table(&intable[0x102], 8, read_struct); } 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; if ((int32_t)back <= 0) /* signed */ return 0; for (i = 0; i < back; i++) { uint32_t bit = getbit_from_table(&intable[pos], read_struct); pos = (pos * 2) + bit; bb |= (bit << i); } 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); } } return retv; }