/* * 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. */ #if HAVE_CONFIG_H #include "clamav-config.h" #endif #include "clamav.h" #include "others.h" #include "execs.h" #include "wwunpack.h" #if HAVE_STRING_H #include #endif #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; #define BIT \ bits = bt >> 31; \ bt <<= 1; \ if (!--bc) { \ RESEED; \ } #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; \ } \ } 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) { uint8_t *structs = wwsect + 0x2a1, *compd, *ccur, *unpd, *ucur, bc; uint32_t src, srcend, szd, bt, bits; cl_error_t error = 0; uint16_t i; 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; 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; RESEED; while (CL_SUCCESS == error) { uint32_t backbytes, backsize; uint8_t saved; BIT; if (!bits) { /* BYTE copy */ if (ccur - compd >= szd || !CLI_ISCONTAINED(exe, exesz, ucur, 1)) error = 1; else *ucur++ = *ccur++; continue; } 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; } /* BLOCK backcopy */ saved = bits; /* cmp al, 1 / pushf */ 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; } backbytes = bits; /* 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; 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; } if (CL_SUCCESS == error) { if (pe + 6 > exesz || pe + 7 > exesz || pe + 0x28 > exesz || pe + 0x50 > exesz || pe + 0x14 > exesz) return CL_EFORMAT; 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); 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; } 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; } memset(structs, 0, 0x28); if (cli_writen(desc, exe, exesz) != (size_t)exesz) { error = CL_EWRITE; } } return error; }