libclamav/pe.c
6307ca15
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
2023340a
  *
1d7f6b27
  *  Authors: Alberto Wu, Tomasz Kojm, Andrew Williams
5eb34fac
  *
6c03dc5d
  *  Acknowledgements: The header structures were based upon a PE format
6289eda8
  *                    analysis by B. Luevelsmeyer.
6c03dc5d
  *
6307ca15
  *  This program is free software; you can redistribute it and/or modify
2023340a
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
6307ca15
  *
  *  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
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
6307ca15
  */
aedd942c
 /*
   Portions of Code (i.e. pe_ordinal) Copyright (c) 2014. The YARA Authors. All Rights Reserved.
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
 
   http://www.apache.org/licenses/LICENSE-2.0
 
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
6307ca15
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
cd94be7a
 
 /*
42d26ac9
 #define _XOPEN_SOURCE 500
cd94be7a
 */
 
6307ca15
 #include <stdio.h>
cd94be7a
 #include <stdlib.h>
7a1e6921
 #include <stdint.h>
cd94be7a
 
f377e052
 #if HAVE_STRING_H
6307ca15
 #include <string.h>
f377e052
 #endif
cd94be7a
 
6307ca15
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
3fcb62ca
 #ifdef HAVE_UNISTD_H
6307ca15
 #include <unistd.h>
3fcb62ca
 #endif
6307ca15
 #include <time.h>
95e31dc7
 #include <stdarg.h>
6307ca15
 
 #include "clamav.h"
 #include "others.h"
85dd8460
 #include "pe.h"
 #include "petite.h"
5f1a932b
 #include "fsg.h"
c2dfe70e
 #include "spin.h"
822930fc
 #include "upx.h"
 #include "yc.h"
2f73b977
 #include "aspack.h"
60cd49c9
 #include "wwunpack.h"
81030038
 #include "unsp.h"
85dd8460
 #include "scanners.h"
41fd7c2f
 #include "str.h"
01302683
 #include "execs.h"
4aa4a05c
 #include "mew.h"
e8042398
 #include "upack.h"
0a3d4094
 #include "matcher.h"
3faa9783
 #include "matcher-hash.h"
7f67d9e3
 #include "disasm.h"
42d26ac9
 #include "special.h"
56e5821b
 #include "ishield.h"
0393aa56
 #include "asn1.h"
6307ca15
 
172c4dd2
 #include "json_api.h"
 
bc93eda0
 #define DCONF ctx->dconf->pe
 
288057e9
 #define PE_IMAGE_DOS_SIGNATURE 0x5a4d     /* MZ */
 #define PE_IMAGE_DOS_SIGNATURE_OLD 0x4d5a /* ZM */
 #define PE_IMAGE_NT_SIGNATURE 0x00004550
 #define PE32_SIGNATURE 0x010b
 #define PE32P_SIGNATURE 0x020b
afe940da
 #define OPT_HDR_SIZE_DIFF (sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32))
a9b1e8d2
 
ce389c9c
 #define UPX_NRV2B "\x11\xdb\x11\xc9\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9\x11\xc9\x75\x20\x41\x01\xdb"
 #define UPX_NRV2D "\x83\xf0\xff\x74\x78\xd1\xf8\x89\xc5\xeb\x0b\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9"
 #define UPX_NRV2E "\xeb\x52\x31\xc9\x83\xe8\x03\x72\x11\xc1\xe0\x08\x8a\x06\x46\x83\xf0\xff\x74\x75\xd1\xf8\x89\xc5"
288057e9
 #define UPX_LZMA1_FIRST "\x56\x83\xc3\x04\x53\x50\xc7\x03"
1c6bead7
 #define UPX_LZMA1_SECOND "\x90\x90\x90\x55\x57\x56\x53\x83"
 #define UPX_LZMA0 "\x56\x83\xc3\x04\x53\x50\xc7\x03\x03\x00\x00\x00\x90\x90\x90\x55\x57\x56\x53\x83"
f2b223fe
 #define UPX_LZMA2 "\x56\x83\xc3\x04\x53\x50\xc7\x03\x03\x00\x02\x00\x90\x90\x90\x90\x90\x55\x57\x56"
342e27a5
 
48641f8b
 #define PE_MAXNAMESIZE 256
288057e9
 #define PE_MAXIMPORTS 1024
0f53ea60
 // TODO On Vista and above, up to 65535 sections are allowed.  Make sure
 // that using this lower limit from XP is acceptable in all cases
288057e9
 #define PE_MAXSECTIONS 96
48641f8b
 
634c8594
 #define EC64(x) ((uint64_t)cli_readint64(&(x))) /* Convert little endian to host */
 #define EC32(x) ((uint32_t)cli_readint32(&(x)))
09f20cb6
 #define EC16(x) ((uint16_t)cli_readint16(&(x)))
7cd9337a
 /* lower and upper boundary alignment (size vs offset) */
288057e9
 #define PEALIGN(o, a) (((a)) ? (((o) / (a)) * (a)) : (o))
 #define PESALIGN(o, a) (((a)) ? (((o) / (a) + ((o) % (a) != 0)) * (a)) : (o))
a9082ea2
 
afe940da
 // TODO Replace all of these with static inline functions
288057e9
 #define CLI_UNPSIZELIMITS(NAME, CHK)                           \
     if (cli_checklimits(NAME, ctx, (CHK), 0, 0) != CL_CLEAN) { \
afe940da
         cli_exe_info_destroy(peinfo);                          \
288057e9
         return CL_CLEAN;                                       \
     }
95e31dc7
 
288057e9
 #define CLI_UNPTEMP(NAME, FREEME)                                                       \
     if (!(tempfile = cli_gentemp(ctx->engine->tmpdir))) {                               \
afe940da
         cli_exe_info_destroy(peinfo);                                                   \
288057e9
         cli_multifree FREEME;                                                           \
         return CL_EMEM;                                                                 \
     }                                                                                   \
     if ((ndesc = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU)) < 0) { \
         cli_dbgmsg(NAME ": Can't create file %s\n", tempfile);                          \
         free(tempfile);                                                                 \
afe940da
         cli_exe_info_destroy(peinfo);                                                   \
288057e9
         cli_multifree FREEME;                                                           \
         return CL_ECREAT;                                                               \
     }
95e31dc7
 
288057e9
 #define CLI_TMPUNLK()               \
     if (!ctx->engine->keeptmp) {    \
         if (cli_unlink(tempfile)) { \
             free(tempfile);         \
             return CL_EUNLINK;      \
         }                           \
     }
95e31dc7
 
16b28d07
 #ifdef HAVE__INTERNAL__SHA_COLLECT
288057e9
 #define SHA_OFF                \
     do {                       \
         ctx->sha_collect = -1; \
     } while (0)
 #define SHA_RESET                       \
     do {                                \
         ctx->sha_collect = sha_collect; \
     } while (0)
16b28d07
 #else
288057e9
 #define SHA_OFF \
     do {        \
     } while (0)
 #define SHA_RESET \
     do {          \
     } while (0)
16b28d07
 #endif
 
288057e9
 #define FSGCASE(NAME, FREESEC)                            \
     case 0: /* Unpacked and NOT rebuilt */                \
         cli_dbgmsg(NAME ": Successfully decompressed\n"); \
         close(ndesc);                                     \
         if (cli_unlink(tempfile)) {                       \
afe940da
             cli_exe_info_destroy(peinfo);                 \
288057e9
             free(tempfile);                               \
             FREESEC;                                      \
             return CL_EUNLINK;                            \
         }                                                 \
         free(tempfile);                                   \
         FREESEC;                                          \
         found       = 0;                                  \
         upx_success = 1;                                  \
         break; /* FSG ONLY! - scan raw data after upx block */
 
afe940da
 #define SPINCASE()                                         \
     case 2:                                                \
         free(spinned);                                     \
         close(ndesc);                                      \
         if (cli_unlink(tempfile)) {                        \
             cli_exe_info_destroy(peinfo);                  \
             free(tempfile);                                \
             return CL_EUNLINK;                             \
         }                                                  \
         cli_dbgmsg("cli_scanpe: PESpin: Size exceeded\n"); \
         free(tempfile);                                    \
288057e9
         break;
 
 #define CLI_UNPRESULTS_(NAME, FSGSTUFF, EXPR, GOOD, FREEME)                                   \
     switch (EXPR) {                                                                           \
         case GOOD: /* Unpacked and rebuilt */                                                 \
             if (ctx->engine->keeptmp)                                                         \
                 cli_dbgmsg(NAME ": Unpacked and rebuilt executable saved in %s\n", tempfile); \
             else                                                                              \
                 cli_dbgmsg(NAME ": Unpacked and rebuilt executable\n");                       \
             cli_multifree FREEME;                                                             \
afe940da
             cli_exe_info_destroy(peinfo);                                                     \
288057e9
             lseek(ndesc, 0, SEEK_SET);                                                        \
             cli_dbgmsg("***** Scanning rebuilt PE file *****\n");                             \
             SHA_OFF;                                                                          \
             if (cli_magic_scandesc(ndesc, tempfile, ctx) == CL_VIRUS) {                       \
                 close(ndesc);                                                                 \
afe940da
                 SHA_RESET;                                                                    \
288057e9
                 CLI_TMPUNLK();                                                                \
                 free(tempfile);                                                               \
                 return CL_VIRUS;                                                              \
             }                                                                                 \
             SHA_RESET;                                                                        \
             close(ndesc);                                                                     \
             CLI_TMPUNLK();                                                                    \
             free(tempfile);                                                                   \
             return CL_CLEAN;                                                                  \
                                                                                               \
             FSGSTUFF;                                                                         \
                                                                                               \
         default:                                                                              \
             cli_dbgmsg(NAME ": Unpacking failed\n");                                          \
             close(ndesc);                                                                     \
             if (cli_unlink(tempfile)) {                                                       \
afe940da
                 cli_exe_info_destroy(peinfo);                                                 \
288057e9
                 free(tempfile);                                                               \
                 cli_multifree FREEME;                                                         \
                 return CL_EUNLINK;                                                            \
             }                                                                                 \
             cli_multifree FREEME;                                                             \
             free(tempfile);                                                                   \
95e31dc7
     }
 
288057e9
 #define CLI_UNPRESULTS(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, (void)0, EXPR, GOOD, FREEME)
afe940da
 // TODO The second argument to FSGCASE below should match what gets freed as
 // indicated by FREEME, otherwise a memory leak can occur (as currently used,
 // it looks like dest can get leaked by these macros).
288057e9
 #define CLI_UNPRESULTSFSG1(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, FSGCASE(NAME, free(sections)), EXPR, GOOD, FREEME)
 #define CLI_UNPRESULTSFSG2(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, FSGCASE(NAME, (void)0), EXPR, GOOD, FREEME)
a9082ea2
 
048a88e6
 #define DETECT_BROKEN_PE (SCAN_HEURISTIC_BROKEN && !ctx->corrupted_input)
c6b9d863
 
8cb85148
 extern const unsigned int hashlen[];
 
be62f8ce
 struct offset_list {
     uint32_t offset;
     struct offset_list *next;
 };
 
8955ca5b
 struct pe_image_import_descriptor {
     union {
         uint32_t Characteristics;
         uint32_t OriginalFirstThunk;
     } u;
     uint32_t TimeDateStamp;
     uint32_t ForwarderChain;
     uint32_t Name;
     uint32_t FirstThunk;
 };
 
288057e9
 #define PE_IMAGEDIR_ORDINAL_FLAG32 0x80000000
 #define PE_IMAGEDIR_ORDINAL_FLAG64 0x8000000000000000L
8955ca5b
 
 struct pe_image_thunk32 {
     union {
         uint32_t ForwarderString;
         uint32_t Function;
         uint32_t Ordinal;
         uint32_t AddressOfData;
     } u;
 };
 
 struct pe_image_thunk64 {
     union {
         uint64_t ForwarderString;
         uint64_t Function;
         uint64_t Ordinal;
         uint64_t AddressOfData;
     } u;
 };
 
 struct pe_image_import_by_name {
     uint16_t Hint;
     uint8_t Name[1];
 };
 
288057e9
 static void cli_multifree(void *f, ...)
 {
95e31dc7
     void *ff;
     va_list ap;
     free(f);
     va_start(ap, f);
288057e9
     while ((ff = va_arg(ap, void *))) free(ff);
95e31dc7
     va_end(ap);
 }
 
85310158
 struct vinfo_list {
     uint32_t rvas[16];
     unsigned int count;
 };
 
288057e9
 static int versioninfo_cb(void *opaque, uint32_t type, uint32_t name, uint32_t lang, uint32_t rva)
 {
85310158
     struct vinfo_list *vlist = (struct vinfo_list *)opaque;
 
1e7afd20
     cli_dbgmsg("versioninfo_cb: type: %x, name: %x, lang: %x, rva: %x\n", type, name, lang, rva);
85310158
     vlist->rvas[vlist->count] = rva;
288057e9
     if (++vlist->count == sizeof(vlist->rvas) / sizeof(vlist->rvas[0]))
017f3490
         return 1;
f018e8b1
     return 0;
 }
 
afe940da
 /* Given an RVA (relative to the ImageBase), return the file offset of the
  * corresponding data */
c80f26a2
 uint32_t cli_rawaddr(uint32_t rva, const struct cli_exe_section *shp, uint16_t nos, unsigned int *err, size_t fsize, uint32_t hdr_size)
ac75a532
 {
95e31dc7
     int i, found = 0;
     uint32_t ret;
ac75a532
 
288057e9
     if (rva < hdr_size) { /* Out of section EP - mapped to imagebase+rva */
017f3490
         if (rva >= fsize) {
288057e9
             *err = 1;
017f3490
             return 0;
         }
 
288057e9
         *err = 0;
017f3490
         return rva;
57866af1
     }
ac75a532
 
288057e9
     for (i = nos - 1; i >= 0; i--) {
afe940da
         if (shp[i].rsz && shp[i].rva <= rva && shp[i].rsz > (rva - shp[i].rva)) {
017f3490
             found = 1;
             break;
         }
ac75a532
     }
 
288057e9
     if (!found) {
017f3490
         *err = 1;
         return 0;
ac75a532
     }
 
afe940da
     ret  = (rva - shp[i].rva) + shp[i].raw;
33f89aa5
     *err = 0;
57866af1
     return ret;
ac75a532
 }
 
6c03dc5d
 /*
afe940da
    void findres(uint32_t by_type, uint32_t by_name, fmap_t *map, struct cli_exe_info *peinfo, int (*cb)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *opaque)
235464bb
    callback based res lookup
 
    by_type: lookup type
    by_name: lookup name or (unsigned)-1 to look for any name
    res_rva: base resource rva (i.e. dirs[2].VirtualAddress)
afe940da
    map, peinfo: same as in scanpe
235464bb
    cb: the callback function executed on each successful match
    opaque: an opaque pointer passed to the callback
 
    the callback proto is
    int pe_res_cballback (void *opaque, uint32_t type, uint32_t name, uint32_t lang, uint32_t rva);
    the callback shall return 0 to continue the lookup or 1 to abort
 */
afe940da
 void findres(uint32_t by_type, uint32_t by_name, fmap_t *map, struct cli_exe_info *peinfo, int (*cb)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *opaque)
288057e9
 {
235464bb
     unsigned int err = 0;
     uint32_t type, type_offs, name, name_offs, lang, lang_offs;
288057e9
     const uint8_t *resdir, *type_entry, *name_entry, *lang_entry;
235464bb
     uint16_t type_cnt, name_cnt, lang_cnt;
afe940da
     uint32_t res_rva;
 
     if (NULL == peinfo || peinfo->ndatadirs < 3) {
         return;
     }
235464bb
 
8650c790
     if (0 != peinfo->offset) {
         cli_dbgmsg("findres: Assumption Violated: Looking for version info when peinfo->offset != 0\n");
     }
 
afe940da
     res_rva = EC32(peinfo->dirs[2].VirtualAddress);
 
     if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva, peinfo->sections, peinfo->nsections, &err, map->len, peinfo->hdr_size), 16)) || err)
89f3be11
         return;
235464bb
 
288057e9
     type_cnt   = (uint16_t)cli_readint16(resdir + 12);
     type_entry = resdir + 16;
     if (!(by_type >> 31)) {
89f3be11
         type_entry += type_cnt * 8;
288057e9
         type_cnt = (uint16_t)cli_readint16(resdir + 14);
235464bb
     }
 
288057e9
     while (type_cnt--) {
         if (!fmap_need_ptr_once(map, type_entry, 8))
89f3be11
             return;
288057e9
         type      = cli_readint32(type_entry);
         type_offs = cli_readint32(type_entry + 4);
         if (type == by_type && (type_offs >> 31)) {
89f3be11
             type_offs &= 0x7fffffff;
afe940da
             if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva + type_offs, peinfo->sections, peinfo->nsections, &err, map->len, peinfo->hdr_size), 16)) || err)
89f3be11
                 return;
 
288057e9
             name_cnt   = (uint16_t)cli_readint16(resdir + 12);
             name_entry = resdir + 16;
             if (by_name == 0xffffffff)
                 name_cnt += (uint16_t)cli_readint16(resdir + 14);
             else if (!(by_name >> 31)) {
89f3be11
                 name_entry += name_cnt * 8;
288057e9
                 name_cnt = (uint16_t)cli_readint16(resdir + 14);
89f3be11
             }
288057e9
             while (name_cnt--) {
                 if (!fmap_need_ptr_once(map, name_entry, 8))
89f3be11
                     return;
288057e9
                 name      = cli_readint32(name_entry);
                 name_offs = cli_readint32(name_entry + 4);
                 if ((by_name == 0xffffffff || name == by_name) && (name_offs >> 31)) {
89f3be11
                     name_offs &= 0x7fffffff;
afe940da
                     if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva + name_offs, peinfo->sections, peinfo->nsections, &err, map->len, peinfo->hdr_size), 16)) || err)
89f3be11
                         return;
 
288057e9
                     lang_cnt   = (uint16_t)cli_readint16(resdir + 12) + (uint16_t)cli_readint16(resdir + 14);
                     lang_entry = resdir + 16;
                     while (lang_cnt--) {
                         if (!fmap_need_ptr_once(map, lang_entry, 8))
89f3be11
                             return;
288057e9
                         lang      = cli_readint32(lang_entry);
                         lang_offs = cli_readint32(lang_entry + 4);
                         if (!(lang_offs >> 31)) {
                             if (cb(opaque, type, name, lang, res_rva + lang_offs))
89f3be11
                                 return;
                         }
                         lang_entry += 8;
                     }
                 }
                 name_entry += 8;
             }
             return; /* FIXME: unless we want to find ALL types */
         }
         type_entry += 8;
235464bb
     }
 }
 
afe940da
 static void cli_parseres_special(uint32_t base, uint32_t rva, fmap_t *map, struct cli_exe_info *peinfo, size_t fsize, unsigned int level, uint32_t type, unsigned int *maxres, struct swizz_stats *stats)
288057e9
 {
42d26ac9
     unsigned int err = 0, i;
f304dc68
     const uint8_t *resdir;
     const uint8_t *entry, *oentry;
42d26ac9
     uint16_t named, unnamed;
afe940da
     uint32_t rawaddr = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
42d26ac9
     uint32_t entries;
 
288057e9
     if (level > 2 || !*maxres) return;
     *maxres -= 1;
     if (err || !(resdir = fmap_need_off_once(map, rawaddr, 16)))
         return;
     named   = (uint16_t)cli_readint16(resdir + 12);
     unnamed = (uint16_t)cli_readint16(resdir + 14);
42d26ac9
 
288057e9
     entries = /*named+*/ unnamed;
42d26ac9
     if (!entries)
288057e9
         return;
     rawaddr += named * 8; /* skip named */
42d26ac9
     /* this is just used in a heuristic detection, so don't give error on failure */
288057e9
     if (!(entry = fmap_need_off(map, rawaddr + 16, entries * 8))) {
         cli_dbgmsg("cli_parseres_special: failed to read resource directory at:%lu\n", (unsigned long)rawaddr + 16);
         return;
42d26ac9
     }
a5241d27
     oentry = entry;
42d26ac9
     /*for (i=0; i<named; i++) {
89f3be11
         uint32_t id, offs;
         id = cli_readint32(entry);
         offs = cli_readint32(entry+4);
         if(offs>>31)
afe940da
             cli_parseres( base, base + (offs&0x7fffffff), srcfd, peinfo, fsize, level+1, type, maxres, stats);
89f3be11
         entry+=8;
42d26ac9
     }*/
288057e9
     for (i = 0; i < unnamed; i++, entry += 8) {
89f3be11
         uint32_t id, offs;
         if (stats->errors >= SWIZZ_MAXERRORS) {
             cli_dbgmsg("cli_parseres_special: resources broken, ignoring\n");
             return;
         }
288057e9
         id = cli_readint32(entry) & 0x7fffffff;
         if (level == 0) {
             type = 0;
             switch (id) {
                 case 4:  /* menu */
                 case 5:  /* dialog */
                 case 6:  /* string */
                 case 11: /* msgtable */
                     type = id;
                     break;
                 case 16:
                     type = id;
                     /* 14: version */
                     stats->has_version = 1;
                     break;
                 case 24: /* manifest */
                     stats->has_manifest = 1;
                     break;
                     /* otherwise keep it 0, we don't want it */
             }
89f3be11
         }
         if (!type) {
288057e9
             /* if we are not interested in this type, skip */
             continue;
89f3be11
         }
288057e9
         offs = cli_readint32(entry + 4);
         if (offs >> 31)
afe940da
             cli_parseres_special(base, base + (offs & 0x7fffffff), map, peinfo, fsize, level + 1, type, maxres, stats);
89f3be11
         else {
288057e9
             offs    = cli_readint32(entry + 4);
afe940da
             rawaddr = cli_rawaddr(base + offs, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
288057e9
             if (!err && (resdir = fmap_need_off_once(map, rawaddr, 16))) {
                 uint32_t isz = cli_readint32(resdir + 4);
                 const uint8_t *str;
afe940da
                 rawaddr = cli_rawaddr(cli_readint32(resdir), peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
288057e9
                 if (err || !isz || isz >= fsize || rawaddr + isz >= fsize) {
                     cli_dbgmsg("cli_parseres_special: invalid resource table entry: %lu + %lu\n",
                                (unsigned long)rawaddr,
                                (unsigned long)isz);
                     stats->errors++;
                     continue;
                 }
                 if ((id & 0xff) != 0x09) /* english res only */
                     continue;
                 if ((str = fmap_need_off_once(map, rawaddr, isz)))
                     cli_detect_swizz_str(str, isz, stats, type);
             }
89f3be11
         }
42d26ac9
     }
288057e9
     fmap_unneed_ptr(map, oentry, entries * 8);
42d26ac9
 }
 
288057e9
 static unsigned int cli_hashsect(fmap_t *map, struct cli_exe_section *s, unsigned char **digest, int *foundhash, int *foundwild)
e37613ad
 {
     const void *hashme;
 
     if (s->rsz > CLI_MAX_ALLOCATION) {
         cli_dbgmsg("cli_hashsect: skipping hash calculation for too big section\n");
         return 0;
     }
 
288057e9
     if (!s->rsz) return 0;
     if (!(hashme = fmap_need_off_once(map, s->raw, s->rsz))) {
e37613ad
         cli_dbgmsg("cli_hashsect: unable to read section data\n");
         return 0;
     }
 
288057e9
     if (foundhash[CLI_HASH_MD5] || foundwild[CLI_HASH_MD5])
b2e7c931
         cl_hash_data("md5", hashme, s->rsz, digest[CLI_HASH_MD5], NULL);
288057e9
     if (foundhash[CLI_HASH_SHA1] || foundwild[CLI_HASH_SHA1])
b2e7c931
         cl_sha1(hashme, s->rsz, digest[CLI_HASH_SHA1], NULL);
288057e9
     if (foundhash[CLI_HASH_SHA256] || foundwild[CLI_HASH_SHA256])
b2e7c931
         cl_sha256(hashme, s->rsz, digest[CLI_HASH_SHA256], NULL);
e37613ad
 
     return 1;
 }
 
 /* check hash section sigs */
288057e9
 static int scan_pe_mdb(cli_ctx *ctx, struct cli_exe_section *exe_section)
e37613ad
 {
288057e9
     struct cli_matcher *mdb_sect = ctx->engine->hm_mdb;
     unsigned char *hashset[CLI_HASH_AVAIL_TYPES];
     const char *virname = NULL;
e37613ad
     int foundsize[CLI_HASH_AVAIL_TYPES];
     int foundwild[CLI_HASH_AVAIL_TYPES];
     enum CLI_HASH_TYPE type;
288057e9
     int ret            = CL_CLEAN;
     unsigned char *md5 = NULL;
 
e37613ad
     /* pick hashtypes to generate */
288057e9
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
e37613ad
         foundsize[type] = cli_hm_have_size(mdb_sect, type, exe_section->rsz);
         foundwild[type] = cli_hm_have_wild(mdb_sect, type);
288057e9
         if (foundsize[type] || foundwild[type]) {
8cb85148
             hashset[type] = cli_malloc(hashlen[type]);
288057e9
             if (!hashset[type]) {
afe940da
                 cli_errmsg("scan_pe_mdb: cli_malloc failed!\n");
288057e9
                 for (; type > 0;)
e37613ad
                     free(hashset[--type]);
                 return CL_EMEM;
             }
288057e9
         } else {
e37613ad
             hashset[type] = NULL;
         }
     }
 
     /* Generate hashes */
     cli_hashsect(*ctx->fmap, exe_section, hashset, foundsize, foundwild);
 
8cb85148
     /* Print hash */
     if (cli_debug_flag) {
         md5 = hashset[CLI_HASH_MD5];
3ca11170
         if (md5) {
cbf5017a
             cli_dbgmsg("MDB hashset: %u:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
288057e9
                        exe_section->rsz, md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], md5[7],
                        md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], md5[14], md5[15]);
3ca11170
         } else if (cli_always_gen_section_hash) {
             const void *hashme = fmap_need_off_once(*ctx->fmap, exe_section->raw, exe_section->rsz);
             if (!(hashme)) {
                 cli_errmsg("scan_pe_mdb: unable to read section data\n");
                 ret = CL_EREAD;
                 goto end;
             }
 
             md5 = cli_malloc(16);
             if (!(md5)) {
                 cli_errmsg("scan_pe_mdb: cli_malloc failed!\n");
                 ret = CL_EMEM;
                 goto end;
             }
 
b2e7c931
             cl_hash_data("md5", hashme, exe_section->rsz, md5, NULL);
3ca11170
 
             cli_dbgmsg("MDB: %u:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
288057e9
                        exe_section->rsz, md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], md5[7],
                        md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], md5[14], md5[15]);
3ca11170
 
             free(md5);
 
         } else {
fea6f9a7
             cli_dbgmsg("MDB: %u:notgenerated\n", exe_section->rsz);
3ca11170
         }
8cb85148
     }
 
e37613ad
     /* Do scans */
288057e9
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
         if (foundsize[type] && cli_hm_scan(hashset[type], exe_section->rsz, &virname, mdb_sect, type) == CL_VIRUS) {
cbf5017a
             ret = cli_append_virus(ctx, virname);
             if (ret != CL_CLEAN) {
                 if (ret != CL_VIRUS)
                     break;
048a88e6
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
e37613ad
             }
288057e9
         }
         if (foundwild[type] && cli_hm_scan_wild(hashset[type], &virname, mdb_sect, type) == CL_VIRUS) {
cbf5017a
             ret = cli_append_virus(ctx, virname);
             if (ret != CL_CLEAN) {
                 if (ret != CL_VIRUS)
                     break;
048a88e6
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
e37613ad
             }
288057e9
         }
e37613ad
     }
 
3ca11170
 end:
288057e9
     for (type = CLI_HASH_AVAIL_TYPES; type > 0;)
efdb83da
         free(hashset[--type]);
e37613ad
     return ret;
 }
 
8955ca5b
 /* imptbl scanning */
634c8594
 static char *pe_ordinal(const char *dll, uint16_t ord)
8955ca5b
 {
288057e9
     char name[64];
     name[0] = '\0';
 
     if (strncasecmp(dll, "WS2_32.dll", 10) == 0 ||
         strncasecmp(dll, "wsock32.dll", 11) == 0) {
         switch (ord) {
             case 1:
                 sprintf(name, "accept");
                 break;
             case 2:
                 sprintf(name, "bind");
                 break;
             case 3:
                 sprintf(name, "closesocket");
                 break;
             case 4:
                 sprintf(name, "connect");
                 break;
             case 5:
                 sprintf(name, "getpeername");
                 break;
             case 6:
                 sprintf(name, "getsockname");
                 break;
             case 7:
                 sprintf(name, "getsockopt");
                 break;
             case 8:
                 sprintf(name, "htonl");
                 break;
             case 9:
                 sprintf(name, "htons");
                 break;
             case 10:
                 sprintf(name, "ioctlsocket");
                 break;
             case 11:
                 sprintf(name, "inet_addr");
                 break;
             case 12:
                 sprintf(name, "inet_ntoa");
                 break;
             case 13:
                 sprintf(name, "listen");
                 break;
             case 14:
                 sprintf(name, "ntohl");
                 break;
             case 15:
                 sprintf(name, "ntohs");
                 break;
             case 16:
                 sprintf(name, "recv");
                 break;
             case 17:
                 sprintf(name, "recvfrom");
                 break;
             case 18:
                 sprintf(name, "select");
                 break;
             case 19:
                 sprintf(name, "send");
                 break;
             case 20:
                 sprintf(name, "sendto");
                 break;
             case 21:
                 sprintf(name, "setsockopt");
                 break;
             case 22:
                 sprintf(name, "shutdown");
                 break;
             case 23:
                 sprintf(name, "socket");
                 break;
             case 24:
                 sprintf(name, "GetAddrInfoW");
                 break;
             case 25:
                 sprintf(name, "GetNameInfoW");
                 break;
             case 26:
                 sprintf(name, "WSApSetPostRoutine");
                 break;
             case 27:
                 sprintf(name, "FreeAddrInfoW");
                 break;
             case 28:
                 sprintf(name, "WPUCompleteOverlappedRequest");
                 break;
             case 29:
                 sprintf(name, "WSAAccept");
                 break;
             case 30:
                 sprintf(name, "WSAAddressToStringA");
                 break;
             case 31:
                 sprintf(name, "WSAAddressToStringW");
                 break;
             case 32:
                 sprintf(name, "WSACloseEvent");
                 break;
             case 33:
                 sprintf(name, "WSAConnect");
                 break;
             case 34:
                 sprintf(name, "WSACreateEvent");
                 break;
             case 35:
                 sprintf(name, "WSADuplicateSocketA");
                 break;
             case 36:
                 sprintf(name, "WSADuplicateSocketW");
                 break;
             case 37:
                 sprintf(name, "WSAEnumNameSpaceProvidersA");
                 break;
             case 38:
                 sprintf(name, "WSAEnumNameSpaceProvidersW");
                 break;
             case 39:
                 sprintf(name, "WSAEnumNetworkEvents");
                 break;
             case 40:
                 sprintf(name, "WSAEnumProtocolsA");
                 break;
             case 41:
                 sprintf(name, "WSAEnumProtocolsW");
                 break;
             case 42:
                 sprintf(name, "WSAEventSelect");
                 break;
             case 43:
                 sprintf(name, "WSAGetOverlappedResult");
                 break;
             case 44:
                 sprintf(name, "WSAGetQOSByName");
                 break;
             case 45:
                 sprintf(name, "WSAGetServiceClassInfoA");
                 break;
             case 46:
                 sprintf(name, "WSAGetServiceClassInfoW");
                 break;
             case 47:
                 sprintf(name, "WSAGetServiceClassNameByClassIdA");
                 break;
             case 48:
                 sprintf(name, "WSAGetServiceClassNameByClassIdW");
                 break;
             case 49:
                 sprintf(name, "WSAHtonl");
                 break;
             case 50:
                 sprintf(name, "WSAHtons");
                 break;
             case 51:
                 sprintf(name, "gethostbyaddr");
                 break;
             case 52:
                 sprintf(name, "gethostbyname");
                 break;
             case 53:
                 sprintf(name, "getprotobyname");
                 break;
             case 54:
                 sprintf(name, "getprotobynumber");
                 break;
             case 55:
                 sprintf(name, "getservbyname");
                 break;
             case 56:
                 sprintf(name, "getservbyport");
                 break;
             case 57:
                 sprintf(name, "gethostname");
                 break;
             case 58:
                 sprintf(name, "WSAInstallServiceClassA");
                 break;
             case 59:
                 sprintf(name, "WSAInstallServiceClassW");
                 break;
             case 60:
                 sprintf(name, "WSAIoctl");
                 break;
             case 61:
                 sprintf(name, "WSAJoinLeaf");
                 break;
             case 62:
                 sprintf(name, "WSALookupServiceBeginA");
                 break;
             case 63:
                 sprintf(name, "WSALookupServiceBeginW");
                 break;
             case 64:
                 sprintf(name, "WSALookupServiceEnd");
                 break;
             case 65:
                 sprintf(name, "WSALookupServiceNextA");
                 break;
             case 66:
                 sprintf(name, "WSALookupServiceNextW");
                 break;
             case 67:
                 sprintf(name, "WSANSPIoctl");
                 break;
             case 68:
                 sprintf(name, "WSANtohl");
                 break;
             case 69:
                 sprintf(name, "WSANtohs");
                 break;
             case 70:
                 sprintf(name, "WSAProviderConfigChange");
                 break;
             case 71:
                 sprintf(name, "WSARecv");
                 break;
             case 72:
                 sprintf(name, "WSARecvDisconnect");
                 break;
             case 73:
                 sprintf(name, "WSARecvFrom");
                 break;
             case 74:
                 sprintf(name, "WSARemoveServiceClass");
                 break;
             case 75:
                 sprintf(name, "WSAResetEvent");
                 break;
             case 76:
                 sprintf(name, "WSASend");
                 break;
             case 77:
                 sprintf(name, "WSASendDisconnect");
                 break;
             case 78:
                 sprintf(name, "WSASendTo");
                 break;
             case 79:
                 sprintf(name, "WSASetEvent");
                 break;
             case 80:
                 sprintf(name, "WSASetServiceA");
                 break;
             case 81:
                 sprintf(name, "WSASetServiceW");
                 break;
             case 82:
                 sprintf(name, "WSASocketA");
                 break;
             case 83:
                 sprintf(name, "WSASocketW");
                 break;
             case 84:
                 sprintf(name, "WSAStringToAddressA");
                 break;
             case 85:
                 sprintf(name, "WSAStringToAddressW");
                 break;
             case 86:
                 sprintf(name, "WSAWaitForMultipleEvents");
                 break;
             case 87:
                 sprintf(name, "WSCDeinstallProvider");
                 break;
             case 88:
                 sprintf(name, "WSCEnableNSProvider");
                 break;
             case 89:
                 sprintf(name, "WSCEnumProtocols");
                 break;
             case 90:
                 sprintf(name, "WSCGetProviderPath");
                 break;
             case 91:
                 sprintf(name, "WSCInstallNameSpace");
                 break;
             case 92:
                 sprintf(name, "WSCInstallProvider");
                 break;
             case 93:
                 sprintf(name, "WSCUnInstallNameSpace");
                 break;
             case 94:
                 sprintf(name, "WSCUpdateProvider");
                 break;
             case 95:
                 sprintf(name, "WSCWriteNameSpaceOrder");
                 break;
             case 96:
                 sprintf(name, "WSCWriteProviderOrder");
                 break;
             case 97:
                 sprintf(name, "freeaddrinfo");
                 break;
             case 98:
                 sprintf(name, "getaddrinfo");
                 break;
             case 99:
                 sprintf(name, "getnameinfo");
                 break;
             case 101:
                 sprintf(name, "WSAAsyncSelect");
                 break;
             case 102:
                 sprintf(name, "WSAAsyncGetHostByAddr");
                 break;
             case 103:
                 sprintf(name, "WSAAsyncGetHostByName");
                 break;
             case 104:
                 sprintf(name, "WSAAsyncGetProtoByNumber");
                 break;
             case 105:
                 sprintf(name, "WSAAsyncGetProtoByName");
                 break;
             case 106:
                 sprintf(name, "WSAAsyncGetServByPort");
                 break;
             case 107:
                 sprintf(name, "WSAAsyncGetServByName");
                 break;
             case 108:
                 sprintf(name, "WSACancelAsyncRequest");
                 break;
             case 109:
                 sprintf(name, "WSASetBlockingHook");
                 break;
             case 110:
                 sprintf(name, "WSAUnhookBlockingHook");
                 break;
             case 111:
                 sprintf(name, "WSAGetLastError");
                 break;
             case 112:
                 sprintf(name, "WSASetLastError");
                 break;
             case 113:
                 sprintf(name, "WSACancelBlockingCall");
                 break;
             case 114:
                 sprintf(name, "WSAIsBlocking");
                 break;
             case 115:
                 sprintf(name, "WSAStartup");
                 break;
             case 116:
                 sprintf(name, "WSACleanup");
                 break;
             case 151:
                 sprintf(name, "__WSAFDIsSet");
                 break;
             case 500:
                 sprintf(name, "WEP");
                 break;
             default:
                 break;
         }
     } else if (strncasecmp(dll, "oleaut32.dll", 12) == 0) {
         switch (ord) {
             case 2:
                 sprintf(name, "SysAllocString");
                 break;
             case 3:
                 sprintf(name, "SysReAllocString");
                 break;
             case 4:
                 sprintf(name, "SysAllocStringLen");
                 break;
             case 5:
                 sprintf(name, "SysReAllocStringLen");
                 break;
             case 6:
                 sprintf(name, "SysFreeString");
                 break;
             case 7:
                 sprintf(name, "SysStringLen");
                 break;
             case 8:
                 sprintf(name, "VariantInit");
                 break;
             case 9:
                 sprintf(name, "VariantClear");
                 break;
             case 10:
                 sprintf(name, "VariantCopy");
                 break;
             case 11:
                 sprintf(name, "VariantCopyInd");
                 break;
             case 12:
                 sprintf(name, "VariantChangeType");
                 break;
             case 13:
                 sprintf(name, "VariantTimeToDosDateTime");
                 break;
             case 14:
                 sprintf(name, "DosDateTimeToVariantTime");
                 break;
             case 15:
                 sprintf(name, "SafeArrayCreate");
                 break;
             case 16:
                 sprintf(name, "SafeArrayDestroy");
                 break;
             case 17:
                 sprintf(name, "SafeArrayGetDim");
                 break;
             case 18:
                 sprintf(name, "SafeArrayGetElemsize");
                 break;
             case 19:
                 sprintf(name, "SafeArrayGetUBound");
                 break;
             case 20:
                 sprintf(name, "SafeArrayGetLBound");
                 break;
             case 21:
                 sprintf(name, "SafeArrayLock");
                 break;
             case 22:
                 sprintf(name, "SafeArrayUnlock");
                 break;
             case 23:
                 sprintf(name, "SafeArrayAccessData");
                 break;
             case 24:
                 sprintf(name, "SafeArrayUnaccessData");
                 break;
             case 25:
                 sprintf(name, "SafeArrayGetElement");
                 break;
             case 26:
                 sprintf(name, "SafeArrayPutElement");
                 break;
             case 27:
                 sprintf(name, "SafeArrayCopy");
                 break;
             case 28:
                 sprintf(name, "DispGetParam");
                 break;
             case 29:
                 sprintf(name, "DispGetIDsOfNames");
                 break;
             case 30:
                 sprintf(name, "DispInvoke");
                 break;
             case 31:
                 sprintf(name, "CreateDispTypeInfo");
                 break;
             case 32:
                 sprintf(name, "CreateStdDispatch");
                 break;
             case 33:
                 sprintf(name, "RegisterActiveObject");
                 break;
             case 34:
                 sprintf(name, "RevokeActiveObject");
                 break;
             case 35:
                 sprintf(name, "GetActiveObject");
                 break;
             case 36:
                 sprintf(name, "SafeArrayAllocDescriptor");
                 break;
             case 37:
                 sprintf(name, "SafeArrayAllocData");
                 break;
             case 38:
                 sprintf(name, "SafeArrayDestroyDescriptor");
                 break;
             case 39:
                 sprintf(name, "SafeArrayDestroyData");
                 break;
             case 40:
                 sprintf(name, "SafeArrayRedim");
                 break;
             case 41:
                 sprintf(name, "SafeArrayAllocDescriptorEx");
                 break;
             case 42:
                 sprintf(name, "SafeArrayCreateEx");
                 break;
             case 43:
                 sprintf(name, "SafeArrayCreateVectorEx");
                 break;
             case 44:
                 sprintf(name, "SafeArraySetRecordInfo");
                 break;
             case 45:
                 sprintf(name, "SafeArrayGetRecordInfo");
                 break;
             case 46:
                 sprintf(name, "VarParseNumFromStr");
                 break;
             case 47:
                 sprintf(name, "VarNumFromParseNum");
                 break;
             case 48:
                 sprintf(name, "VarI2FromUI1");
                 break;
             case 49:
                 sprintf(name, "VarI2FromI4");
                 break;
             case 50:
                 sprintf(name, "VarI2FromR4");
                 break;
             case 51:
                 sprintf(name, "VarI2FromR8");
                 break;
             case 52:
                 sprintf(name, "VarI2FromCy");
                 break;
             case 53:
                 sprintf(name, "VarI2FromDate");
                 break;
             case 54:
                 sprintf(name, "VarI2FromStr");
                 break;
             case 55:
                 sprintf(name, "VarI2FromDisp");
                 break;
             case 56:
                 sprintf(name, "VarI2FromBool");
                 break;
             case 57:
                 sprintf(name, "SafeArraySetIID");
                 break;
             case 58:
                 sprintf(name, "VarI4FromUI1");
                 break;
             case 59:
                 sprintf(name, "VarI4FromI2");
                 break;
             case 60:
                 sprintf(name, "VarI4FromR4");
                 break;
             case 61:
                 sprintf(name, "VarI4FromR8");
                 break;
             case 62:
                 sprintf(name, "VarI4FromCy");
                 break;
             case 63:
                 sprintf(name, "VarI4FromDate");
                 break;
             case 64:
                 sprintf(name, "VarI4FromStr");
                 break;
             case 65:
                 sprintf(name, "VarI4FromDisp");
                 break;
             case 66:
                 sprintf(name, "VarI4FromBool");
                 break;
             case 67:
                 sprintf(name, "SafeArrayGetIID");
                 break;
             case 68:
                 sprintf(name, "VarR4FromUI1");
                 break;
             case 69:
                 sprintf(name, "VarR4FromI2");
                 break;
             case 70:
                 sprintf(name, "VarR4FromI4");
                 break;
             case 71:
                 sprintf(name, "VarR4FromR8");
                 break;
             case 72:
                 sprintf(name, "VarR4FromCy");
                 break;
             case 73:
                 sprintf(name, "VarR4FromDate");
                 break;
             case 74:
                 sprintf(name, "VarR4FromStr");
                 break;
             case 75:
                 sprintf(name, "VarR4FromDisp");
                 break;
             case 76:
                 sprintf(name, "VarR4FromBool");
                 break;
             case 77:
                 sprintf(name, "SafeArrayGetVartype");
                 break;
             case 78:
                 sprintf(name, "VarR8FromUI1");
                 break;
             case 79:
                 sprintf(name, "VarR8FromI2");
                 break;
             case 80:
                 sprintf(name, "VarR8FromI4");
                 break;
             case 81:
                 sprintf(name, "VarR8FromR4");
                 break;
             case 82:
                 sprintf(name, "VarR8FromCy");
                 break;
             case 83:
                 sprintf(name, "VarR8FromDate");
                 break;
             case 84:
                 sprintf(name, "VarR8FromStr");
                 break;
             case 85:
                 sprintf(name, "VarR8FromDisp");
                 break;
             case 86:
                 sprintf(name, "VarR8FromBool");
                 break;
             case 87:
                 sprintf(name, "VarFormat");
                 break;
             case 88:
                 sprintf(name, "VarDateFromUI1");
                 break;
             case 89:
                 sprintf(name, "VarDateFromI2");
                 break;
             case 90:
                 sprintf(name, "VarDateFromI4");
                 break;
             case 91:
                 sprintf(name, "VarDateFromR4");
                 break;
             case 92:
                 sprintf(name, "VarDateFromR8");
                 break;
             case 93:
                 sprintf(name, "VarDateFromCy");
                 break;
             case 94:
                 sprintf(name, "VarDateFromStr");
                 break;
             case 95:
                 sprintf(name, "VarDateFromDisp");
                 break;
             case 96:
                 sprintf(name, "VarDateFromBool");
                 break;
             case 97:
                 sprintf(name, "VarFormatDateTime");
                 break;
             case 98:
                 sprintf(name, "VarCyFromUI1");
                 break;
             case 99:
                 sprintf(name, "VarCyFromI2");
                 break;
             case 100:
                 sprintf(name, "VarCyFromI4");
                 break;
             case 101:
                 sprintf(name, "VarCyFromR4");
                 break;
             case 102:
                 sprintf(name, "VarCyFromR8");
                 break;
             case 103:
                 sprintf(name, "VarCyFromDate");
                 break;
             case 104:
                 sprintf(name, "VarCyFromStr");
                 break;
             case 105:
                 sprintf(name, "VarCyFromDisp");
                 break;
             case 106:
                 sprintf(name, "VarCyFromBool");
                 break;
             case 107:
                 sprintf(name, "VarFormatNumber");
                 break;
             case 108:
                 sprintf(name, "VarBstrFromUI1");
                 break;
             case 109:
                 sprintf(name, "VarBstrFromI2");
                 break;
             case 110:
                 sprintf(name, "VarBstrFromI4");
                 break;
             case 111:
                 sprintf(name, "VarBstrFromR4");
                 break;
             case 112:
                 sprintf(name, "VarBstrFromR8");
                 break;
             case 113:
                 sprintf(name, "VarBstrFromCy");
                 break;
             case 114:
                 sprintf(name, "VarBstrFromDate");
                 break;
             case 115:
                 sprintf(name, "VarBstrFromDisp");
                 break;
             case 116:
                 sprintf(name, "VarBstrFromBool");
                 break;
             case 117:
                 sprintf(name, "VarFormatPercent");
                 break;
             case 118:
                 sprintf(name, "VarBoolFromUI1");
                 break;
             case 119:
                 sprintf(name, "VarBoolFromI2");
                 break;
             case 120:
                 sprintf(name, "VarBoolFromI4");
                 break;
             case 121:
                 sprintf(name, "VarBoolFromR4");
                 break;
             case 122:
                 sprintf(name, "VarBoolFromR8");
                 break;
             case 123:
                 sprintf(name, "VarBoolFromDate");
                 break;
             case 124:
                 sprintf(name, "VarBoolFromCy");
                 break;
             case 125:
                 sprintf(name, "VarBoolFromStr");
                 break;
             case 126:
                 sprintf(name, "VarBoolFromDisp");
                 break;
             case 127:
                 sprintf(name, "VarFormatCurrency");
                 break;
             case 128:
                 sprintf(name, "VarWeekdayName");
                 break;
             case 129:
                 sprintf(name, "VarMonthName");
                 break;
             case 130:
                 sprintf(name, "VarUI1FromI2");
                 break;
             case 131:
                 sprintf(name, "VarUI1FromI4");
                 break;
             case 132:
                 sprintf(name, "VarUI1FromR4");
                 break;
             case 133:
                 sprintf(name, "VarUI1FromR8");
                 break;
             case 134:
                 sprintf(name, "VarUI1FromCy");
                 break;
             case 135:
                 sprintf(name, "VarUI1FromDate");
                 break;
             case 136:
                 sprintf(name, "VarUI1FromStr");
                 break;
             case 137:
                 sprintf(name, "VarUI1FromDisp");
                 break;
             case 138:
                 sprintf(name, "VarUI1FromBool");
                 break;
             case 139:
                 sprintf(name, "VarFormatFromTokens");
                 break;
             case 140:
                 sprintf(name, "VarTokenizeFormatString");
                 break;
             case 141:
                 sprintf(name, "VarAdd");
                 break;
             case 142:
                 sprintf(name, "VarAnd");
                 break;
             case 143:
                 sprintf(name, "VarDiv");
                 break;
             case 144:
                 sprintf(name, "DllCanUnloadNow");
                 break;
             case 145:
                 sprintf(name, "DllGetClassObject");
                 break;
             case 146:
                 sprintf(name, "DispCallFunc");
                 break;
             case 147:
                 sprintf(name, "VariantChangeTypeEx");
                 break;
             case 148:
                 sprintf(name, "SafeArrayPtrOfIndex");
                 break;
             case 149:
                 sprintf(name, "SysStringByteLen");
                 break;
             case 150:
                 sprintf(name, "SysAllocStringByteLen");
                 break;
             case 151:
                 sprintf(name, "DllRegisterServer");
                 break;
             case 152:
                 sprintf(name, "VarEqv");
                 break;
             case 153:
                 sprintf(name, "VarIdiv");
                 break;
             case 154:
                 sprintf(name, "VarImp");
                 break;
             case 155:
                 sprintf(name, "VarMod");
                 break;
             case 156:
                 sprintf(name, "VarMul");
                 break;
             case 157:
                 sprintf(name, "VarOr");
                 break;
             case 158:
                 sprintf(name, "VarPow");
                 break;
             case 159:
                 sprintf(name, "VarSub");
                 break;
             case 160:
                 sprintf(name, "CreateTypeLib");
                 break;
             case 161:
                 sprintf(name, "LoadTypeLib");
                 break;
             case 162:
                 sprintf(name, "LoadRegTypeLib");
                 break;
             case 163:
                 sprintf(name, "RegisterTypeLib");
                 break;
             case 164:
                 sprintf(name, "QueryPathOfRegTypeLib");
                 break;
             case 165:
                 sprintf(name, "LHashValOfNameSys");
                 break;
             case 166:
                 sprintf(name, "LHashValOfNameSysA");
                 break;
             case 167:
                 sprintf(name, "VarXor");
                 break;
             case 168:
                 sprintf(name, "VarAbs");
                 break;
             case 169:
                 sprintf(name, "VarFix");
                 break;
             case 170:
                 sprintf(name, "OaBuildVersion");
                 break;
             case 171:
                 sprintf(name, "ClearCustData");
                 break;
             case 172:
                 sprintf(name, "VarInt");
                 break;
             case 173:
                 sprintf(name, "VarNeg");
                 break;
             case 174:
                 sprintf(name, "VarNot");
                 break;
             case 175:
                 sprintf(name, "VarRound");
                 break;
             case 176:
                 sprintf(name, "VarCmp");
                 break;
             case 177:
                 sprintf(name, "VarDecAdd");
                 break;
             case 178:
                 sprintf(name, "VarDecDiv");
                 break;
             case 179:
                 sprintf(name, "VarDecMul");
                 break;
             case 180:
                 sprintf(name, "CreateTypeLib2");
                 break;
             case 181:
                 sprintf(name, "VarDecSub");
                 break;
             case 182:
                 sprintf(name, "VarDecAbs");
                 break;
             case 183:
                 sprintf(name, "LoadTypeLibEx");
                 break;
             case 184:
                 sprintf(name, "SystemTimeToVariantTime");
                 break;
             case 185:
                 sprintf(name, "VariantTimeToSystemTime");
                 break;
             case 186:
                 sprintf(name, "UnRegisterTypeLib");
                 break;
             case 187:
                 sprintf(name, "VarDecFix");
                 break;
             case 188:
                 sprintf(name, "VarDecInt");
                 break;
             case 189:
                 sprintf(name, "VarDecNeg");
                 break;
             case 190:
                 sprintf(name, "VarDecFromUI1");
                 break;
             case 191:
                 sprintf(name, "VarDecFromI2");
                 break;
             case 192:
                 sprintf(name, "VarDecFromI4");
                 break;
             case 193:
                 sprintf(name, "VarDecFromR4");
                 break;
             case 194:
                 sprintf(name, "VarDecFromR8");
                 break;
             case 195:
                 sprintf(name, "VarDecFromDate");
                 break;
             case 196:
                 sprintf(name, "VarDecFromCy");
                 break;
             case 197:
                 sprintf(name, "VarDecFromStr");
                 break;
             case 198:
                 sprintf(name, "VarDecFromDisp");
                 break;
             case 199:
                 sprintf(name, "VarDecFromBool");
                 break;
             case 200:
                 sprintf(name, "GetErrorInfo");
                 break;
             case 201:
                 sprintf(name, "SetErrorInfo");
                 break;
             case 202:
                 sprintf(name, "CreateErrorInfo");
                 break;
             case 203:
                 sprintf(name, "VarDecRound");
                 break;
             case 204:
                 sprintf(name, "VarDecCmp");
                 break;
             case 205:
                 sprintf(name, "VarI2FromI1");
                 break;
             case 206:
                 sprintf(name, "VarI2FromUI2");
                 break;
             case 207:
                 sprintf(name, "VarI2FromUI4");
                 break;
             case 208:
                 sprintf(name, "VarI2FromDec");
                 break;
             case 209:
                 sprintf(name, "VarI4FromI1");
                 break;
             case 210:
                 sprintf(name, "VarI4FromUI2");
                 break;
             case 211:
                 sprintf(name, "VarI4FromUI4");
                 break;
             case 212:
                 sprintf(name, "VarI4FromDec");
                 break;
             case 213:
                 sprintf(name, "VarR4FromI1");
                 break;
             case 214:
                 sprintf(name, "VarR4FromUI2");
                 break;
             case 215:
                 sprintf(name, "VarR4FromUI4");
                 break;
             case 216:
                 sprintf(name, "VarR4FromDec");
                 break;
             case 217:
                 sprintf(name, "VarR8FromI1");
                 break;
             case 218:
                 sprintf(name, "VarR8FromUI2");
                 break;
             case 219:
                 sprintf(name, "VarR8FromUI4");
                 break;
             case 220:
                 sprintf(name, "VarR8FromDec");
                 break;
             case 221:
                 sprintf(name, "VarDateFromI1");
                 break;
             case 222:
                 sprintf(name, "VarDateFromUI2");
                 break;
             case 223:
                 sprintf(name, "VarDateFromUI4");
                 break;
             case 224:
                 sprintf(name, "VarDateFromDec");
                 break;
             case 225:
                 sprintf(name, "VarCyFromI1");
                 break;
             case 226:
                 sprintf(name, "VarCyFromUI2");
                 break;
             case 227:
                 sprintf(name, "VarCyFromUI4");
                 break;
             case 228:
                 sprintf(name, "VarCyFromDec");
                 break;
             case 229:
                 sprintf(name, "VarBstrFromI1");
                 break;
             case 230:
                 sprintf(name, "VarBstrFromUI2");
                 break;
             case 231:
                 sprintf(name, "VarBstrFromUI4");
                 break;
             case 232:
                 sprintf(name, "VarBstrFromDec");
                 break;
             case 233:
                 sprintf(name, "VarBoolFromI1");
                 break;
             case 234:
                 sprintf(name, "VarBoolFromUI2");
                 break;
             case 235:
                 sprintf(name, "VarBoolFromUI4");
                 break;
             case 236:
                 sprintf(name, "VarBoolFromDec");
                 break;
             case 237:
                 sprintf(name, "VarUI1FromI1");
                 break;
             case 238:
                 sprintf(name, "VarUI1FromUI2");
                 break;
             case 239:
                 sprintf(name, "VarUI1FromUI4");
                 break;
             case 240:
                 sprintf(name, "VarUI1FromDec");
                 break;
             case 241:
                 sprintf(name, "VarDecFromI1");
                 break;
             case 242:
                 sprintf(name, "VarDecFromUI2");
                 break;
             case 243:
                 sprintf(name, "VarDecFromUI4");
                 break;
             case 244:
                 sprintf(name, "VarI1FromUI1");
                 break;
             case 245:
                 sprintf(name, "VarI1FromI2");
                 break;
             case 246:
                 sprintf(name, "VarI1FromI4");
                 break;
             case 247:
                 sprintf(name, "VarI1FromR4");
                 break;
             case 248:
                 sprintf(name, "VarI1FromR8");
                 break;
             case 249:
                 sprintf(name, "VarI1FromDate");
                 break;
             case 250:
                 sprintf(name, "VarI1FromCy");
                 break;
             case 251:
                 sprintf(name, "VarI1FromStr");
                 break;
             case 252:
                 sprintf(name, "VarI1FromDisp");
                 break;
             case 253:
                 sprintf(name, "VarI1FromBool");
                 break;
             case 254:
                 sprintf(name, "VarI1FromUI2");
                 break;
             case 255:
                 sprintf(name, "VarI1FromUI4");
                 break;
             case 256:
                 sprintf(name, "VarI1FromDec");
                 break;
             case 257:
                 sprintf(name, "VarUI2FromUI1");
                 break;
             case 258:
                 sprintf(name, "VarUI2FromI2");
                 break;
             case 259:
                 sprintf(name, "VarUI2FromI4");
                 break;
             case 260:
                 sprintf(name, "VarUI2FromR4");
                 break;
             case 261:
                 sprintf(name, "VarUI2FromR8");
                 break;
             case 262:
                 sprintf(name, "VarUI2FromDate");
                 break;
             case 263:
                 sprintf(name, "VarUI2FromCy");
                 break;
             case 264:
                 sprintf(name, "VarUI2FromStr");
                 break;
             case 265:
                 sprintf(name, "VarUI2FromDisp");
                 break;
             case 266:
                 sprintf(name, "VarUI2FromBool");
                 break;
             case 267:
                 sprintf(name, "VarUI2FromI1");
                 break;
             case 268:
                 sprintf(name, "VarUI2FromUI4");
                 break;
             case 269:
                 sprintf(name, "VarUI2FromDec");
                 break;
             case 270:
                 sprintf(name, "VarUI4FromUI1");
                 break;
             case 271:
                 sprintf(name, "VarUI4FromI2");
                 break;
             case 272:
                 sprintf(name, "VarUI4FromI4");
                 break;
             case 273:
                 sprintf(name, "VarUI4FromR4");
                 break;
             case 274:
                 sprintf(name, "VarUI4FromR8");
                 break;
             case 275:
                 sprintf(name, "VarUI4FromDate");
                 break;
             case 276:
                 sprintf(name, "VarUI4FromCy");
                 break;
             case 277:
                 sprintf(name, "VarUI4FromStr");
                 break;
             case 278:
                 sprintf(name, "VarUI4FromDisp");
                 break;
             case 279:
                 sprintf(name, "VarUI4FromBool");
                 break;
             case 280:
                 sprintf(name, "VarUI4FromI1");
                 break;
             case 281:
                 sprintf(name, "VarUI4FromUI2");
                 break;
             case 282:
                 sprintf(name, "VarUI4FromDec");
                 break;
             case 283:
                 sprintf(name, "BSTR_UserSize");
                 break;
             case 284:
                 sprintf(name, "BSTR_UserMarshal");
                 break;
             case 285:
                 sprintf(name, "BSTR_UserUnmarshal");
                 break;
             case 286:
                 sprintf(name, "BSTR_UserFree");
                 break;
             case 287:
                 sprintf(name, "VARIANT_UserSize");
                 break;
             case 288:
                 sprintf(name, "VARIANT_UserMarshal");
                 break;
             case 289:
                 sprintf(name, "VARIANT_UserUnmarshal");
                 break;
             case 290:
                 sprintf(name, "VARIANT_UserFree");
                 break;
             case 291:
                 sprintf(name, "LPSAFEARRAY_UserSize");
                 break;
             case 292:
                 sprintf(name, "LPSAFEARRAY_UserMarshal");
                 break;
             case 293:
                 sprintf(name, "LPSAFEARRAY_UserUnmarshal");
                 break;
             case 294:
                 sprintf(name, "LPSAFEARRAY_UserFree");
                 break;
             case 295:
                 sprintf(name, "LPSAFEARRAY_Size");
                 break;
             case 296:
                 sprintf(name, "LPSAFEARRAY_Marshal");
                 break;
             case 297:
                 sprintf(name, "LPSAFEARRAY_Unmarshal");
                 break;
             case 298:
                 sprintf(name, "VarDecCmpR8");
                 break;
             case 299:
                 sprintf(name, "VarCyAdd");
                 break;
             case 300:
                 sprintf(name, "DllUnregisterServer");
                 break;
             case 301:
                 sprintf(name, "OACreateTypeLib2");
                 break;
             case 303:
                 sprintf(name, "VarCyMul");
                 break;
             case 304:
                 sprintf(name, "VarCyMulI4");
                 break;
             case 305:
                 sprintf(name, "VarCySub");
                 break;
             case 306:
                 sprintf(name, "VarCyAbs");
                 break;
             case 307:
                 sprintf(name, "VarCyFix");
                 break;
             case 308:
                 sprintf(name, "VarCyInt");
                 break;
             case 309:
                 sprintf(name, "VarCyNeg");
                 break;
             case 310:
                 sprintf(name, "VarCyRound");
                 break;
             case 311:
                 sprintf(name, "VarCyCmp");
                 break;
             case 312:
                 sprintf(name, "VarCyCmpR8");
                 break;
             case 313:
                 sprintf(name, "VarBstrCat");
                 break;
             case 314:
                 sprintf(name, "VarBstrCmp");
                 break;
             case 315:
                 sprintf(name, "VarR8Pow");
                 break;
             case 316:
                 sprintf(name, "VarR4CmpR8");
                 break;
             case 317:
                 sprintf(name, "VarR8Round");
                 break;
             case 318:
                 sprintf(name, "VarCat");
                 break;
             case 319:
                 sprintf(name, "VarDateFromUdateEx");
                 break;
             case 322:
                 sprintf(name, "GetRecordInfoFromGuids");
                 break;
             case 323:
                 sprintf(name, "GetRecordInfoFromTypeInfo");
                 break;
             case 325:
                 sprintf(name, "SetVarConversionLocaleSetting");
                 break;
             case 326:
                 sprintf(name, "GetVarConversionLocaleSetting");
                 break;
             case 327:
                 sprintf(name, "SetOaNoCache");
                 break;
             case 329:
                 sprintf(name, "VarCyMulI8");
                 break;
             case 330:
                 sprintf(name, "VarDateFromUdate");
                 break;
             case 331:
                 sprintf(name, "VarUdateFromDate");
                 break;
             case 332:
                 sprintf(name, "GetAltMonthNames");
                 break;
             case 333:
                 sprintf(name, "VarI8FromUI1");
                 break;
             case 334:
                 sprintf(name, "VarI8FromI2");
                 break;
             case 335:
                 sprintf(name, "VarI8FromR4");
                 break;
             case 336:
                 sprintf(name, "VarI8FromR8");
                 break;
             case 337:
                 sprintf(name, "VarI8FromCy");
                 break;
             case 338:
                 sprintf(name, "VarI8FromDate");
                 break;
             case 339:
                 sprintf(name, "VarI8FromStr");
                 break;
             case 340:
                 sprintf(name, "VarI8FromDisp");
                 break;
             case 341:
                 sprintf(name, "VarI8FromBool");
                 break;
             case 342:
                 sprintf(name, "VarI8FromI1");
                 break;
             case 343:
                 sprintf(name, "VarI8FromUI2");
                 break;
             case 344:
                 sprintf(name, "VarI8FromUI4");
                 break;
             case 345:
                 sprintf(name, "VarI8FromDec");
                 break;
             case 346:
                 sprintf(name, "VarI2FromI8");
                 break;
             case 347:
                 sprintf(name, "VarI2FromUI8");
                 break;
             case 348:
                 sprintf(name, "VarI4FromI8");
                 break;
             case 349:
                 sprintf(name, "VarI4FromUI8");
                 break;
             case 360:
                 sprintf(name, "VarR4FromI8");
                 break;
             case 361:
                 sprintf(name, "VarR4FromUI8");
                 break;
             case 362:
                 sprintf(name, "VarR8FromI8");
                 break;
             case 363:
                 sprintf(name, "VarR8FromUI8");
                 break;
             case 364:
                 sprintf(name, "VarDateFromI8");
                 break;
             case 365:
                 sprintf(name, "VarDateFromUI8");
                 break;
             case 366:
                 sprintf(name, "VarCyFromI8");
                 break;
             case 367:
                 sprintf(name, "VarCyFromUI8");
                 break;
             case 368:
                 sprintf(name, "VarBstrFromI8");
                 break;
             case 369:
                 sprintf(name, "VarBstrFromUI8");
                 break;
             case 370:
                 sprintf(name, "VarBoolFromI8");
                 break;
             case 371:
                 sprintf(name, "VarBoolFromUI8");
                 break;
             case 372:
                 sprintf(name, "VarUI1FromI8");
                 break;
             case 373:
                 sprintf(name, "VarUI1FromUI8");
                 break;
             case 374:
                 sprintf(name, "VarDecFromI8");
                 break;
             case 375:
                 sprintf(name, "VarDecFromUI8");
                 break;
             case 376:
                 sprintf(name, "VarI1FromI8");
                 break;
             case 377:
                 sprintf(name, "VarI1FromUI8");
                 break;
             case 378:
                 sprintf(name, "VarUI2FromI8");
                 break;
             case 379:
                 sprintf(name, "VarUI2FromUI8");
                 break;
             case 401:
                 sprintf(name, "OleLoadPictureEx");
                 break;
             case 402:
                 sprintf(name, "OleLoadPictureFileEx");
                 break;
             case 411:
                 sprintf(name, "SafeArrayCreateVector");
                 break;
             case 412:
                 sprintf(name, "SafeArrayCopyData");
                 break;
             case 413:
                 sprintf(name, "VectorFromBstr");
                 break;
             case 414:
                 sprintf(name, "BstrFromVector");
                 break;
             case 415:
                 sprintf(name, "OleIconToCursor");
                 break;
             case 416:
                 sprintf(name, "OleCreatePropertyFrameIndirect");
                 break;
             case 417:
                 sprintf(name, "OleCreatePropertyFrame");
                 break;
             case 418:
                 sprintf(name, "OleLoadPicture");
                 break;
             case 419:
                 sprintf(name, "OleCreatePictureIndirect");
                 break;
             case 420:
                 sprintf(name, "OleCreateFontIndirect");
                 break;
             case 421:
                 sprintf(name, "OleTranslateColor");
                 break;
             case 422:
                 sprintf(name, "OleLoadPictureFile");
                 break;
             case 423:
                 sprintf(name, "OleSavePictureFile");
                 break;
             case 424:
                 sprintf(name, "OleLoadPicturePath");
                 break;
             case 425:
                 sprintf(name, "VarUI4FromI8");
                 break;
             case 426:
                 sprintf(name, "VarUI4FromUI8");
                 break;
             case 427:
                 sprintf(name, "VarI8FromUI8");
                 break;
             case 428:
                 sprintf(name, "VarUI8FromI8");
                 break;
             case 429:
                 sprintf(name, "VarUI8FromUI1");
                 break;
             case 430:
                 sprintf(name, "VarUI8FromI2");
                 break;
             case 431:
                 sprintf(name, "VarUI8FromR4");
                 break;
             case 432:
                 sprintf(name, "VarUI8FromR8");
                 break;
             case 433:
                 sprintf(name, "VarUI8FromCy");
                 break;
             case 434:
                 sprintf(name, "VarUI8FromDate");
                 break;
             case 435:
                 sprintf(name, "VarUI8FromStr");
                 break;
             case 436:
                 sprintf(name, "VarUI8FromDisp");
                 break;
             case 437:
                 sprintf(name, "VarUI8FromBool");
                 break;
             case 438:
                 sprintf(name, "VarUI8FromI1");
                 break;
             case 439:
                 sprintf(name, "VarUI8FromUI2");
                 break;
             case 440:
                 sprintf(name, "VarUI8FromUI4");
                 break;
             case 441:
                 sprintf(name, "VarUI8FromDec");
                 break;
             case 442:
                 sprintf(name, "RegisterTypeLibForUser");
                 break;
             case 443:
                 sprintf(name, "UnRegisterTypeLibForUser");
                 break;
             default:
                 break;
         }
8955ca5b
     }
 
288057e9
     if (name[0] == '\0')
         sprintf(name, "ord%u", ord);
8955ca5b
 
288057e9
     return cli_strdup(name);
8955ca5b
 }
 
81610c3e
 static int validate_impname(const char *name, uint32_t length, int dll)
 {
288057e9
     uint32_t i    = 0;
81610c3e
     const char *c = name;
 
     if (!name || length == 0)
634c8594
         return 1;
81610c3e
 
     while (i < length && *c != '\0') {
         if ((*c >= '0' && *c <= '9') ||
             (*c >= 'a' && *c <= 'z') ||
             (*c >= 'A' && *c <= 'Z') ||
             (*c == '_') ||
             (dll && *c == '.')) {
 
             c++;
             i++;
         } else
634c8594
             return 0;
81610c3e
     }
 
634c8594
     return 1;
81610c3e
 }
 
afe940da
 static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, struct pe_image_import_descriptor *image, const char *dllname, struct cli_exe_info *peinfo, int *first)
211edda0
 {
cbf5017a
     uint32_t thuoff = 0, offset;
288057e9
     fmap_t *map     = *ctx->fmap;
8955ca5b
     size_t dlllen = 0, fsize = map->len;
7d4ca21b
     unsigned int err = 0;
4cd97da4
     int num_fns = 0, ret = CL_SUCCESS;
8955ca5b
     const char *buffer;
211edda0
     enum CLI_HASH_TYPE type;
4bf268ce
 #if HAVE_JSON
     json_object *imptbl = NULL;
32628142
 #else
     void *imptbl = NULL;
4bf268ce
 #endif
8955ca5b
 
6e50356c
     if (image->u.OriginalFirstThunk)
afe940da
         thuoff = cli_rawaddr(image->u.OriginalFirstThunk, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
6e50356c
     if (err || thuoff == 0)
afe940da
         thuoff = cli_rawaddr(image->FirstThunk, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
8955ca5b
     if (err) {
211edda0
         cli_dbgmsg("scan_pe: invalid rva for image first thunk\n");
634c8594
         return CL_EFORMAT;
8955ca5b
     }
 
4bf268ce
 #if HAVE_JSON
     if (ctx->wrkproperty) {
         imptbl = cli_jsonarray(ctx->wrkproperty, "ImportTable");
         if (!imptbl) {
211edda0
             cli_dbgmsg("scan_pe: cannot allocate import table json object\n");
4bf268ce
             return CL_EMEM;
         }
     }
 #endif
 
288057e9
 #define UPDATE_IMPHASH()                                                            \
     do {                                                                            \
         if (funcname) {                                                             \
             size_t i, j;                                                            \
             char *fname;                                                            \
             size_t funclen;                                                         \
                                                                                     \
             if (dlllen == 0) {                                                      \
                 char *ext = strstr(dllname, ".");                                   \
                                                                                     \
                 if (ext && (strncasecmp(ext, ".ocx", 4) == 0 ||                     \
                             strncasecmp(ext, ".sys", 4) == 0 ||                     \
                             strncasecmp(ext, ".dll", 4) == 0))                      \
                     dlllen = ext - dllname;                                         \
                 else                                                                \
                     dlllen = strlen(dllname);                                       \
             }                                                                       \
                                                                                     \
             funclen = strlen(funcname);                                             \
             if (validate_impname(funcname, funclen, 1) == 0) {                      \
                 cli_dbgmsg("scan_pe: invalid name for imported function\n");        \
                 ret = CL_EFORMAT;                                                   \
                 break;                                                              \
             }                                                                       \
                                                                                     \
             fname = cli_calloc(funclen + dlllen + 3, sizeof(char));                 \
             if (fname == NULL) {                                                    \
                 cli_dbgmsg("scan_pe: cannot allocate memory for imphash string\n"); \
                 ret = CL_EMEM;                                                      \
                 break;                                                              \
             }                                                                       \
             j = 0;                                                                  \
             if (!*first)                                                            \
                 fname[j++] = ',';                                                   \
             for (i = 0; i < dlllen; i++, j++)                                       \
                 fname[j] = tolower(dllname[i]);                                     \
             fname[j++] = '.';                                                       \
             for (i = 0; i < funclen; i++, j++)                                      \
                 fname[j] = tolower(funcname[i]);                                    \
                                                                                     \
             if (imptbl) {                                                           \
                 char *jname = *first ? fname : fname + 1;                           \
                 cli_jsonstr(imptbl, NULL, jname);                                   \
             }                                                                       \
                                                                                     \
             for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)          \
                 cl_update_hash(hashctx[type], fname, strlen(fname));                \
             *impsz += strlen(fname);                                                \
                                                                                     \
             *first = 0;                                                             \
             free(fname);                                                            \
         }                                                                           \
     } while (0)
32628142
 
afe940da
     if (!peinfo->is_pe32plus) {
8955ca5b
         struct pe_image_thunk32 thunk32;
 
211edda0
         while ((num_fns < PE_MAXIMPORTS) && (fmap_readn(map, &thunk32, thuoff, sizeof(struct pe_image_thunk32)) == sizeof(struct pe_image_thunk32)) && (thunk32.u.Ordinal != 0)) {
8955ca5b
             char *funcname = NULL;
211edda0
             thuoff += sizeof(struct pe_image_thunk32);
8955ca5b
 
634c8594
             thunk32.u.Ordinal = EC32(thunk32.u.Ordinal);
 
8d378420
             if (!(thunk32.u.Ordinal & PE_IMAGEDIR_ORDINAL_FLAG32)) {
afe940da
                 offset = cli_rawaddr(thunk32.u.Function, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
8955ca5b
 
1f9f2096
                 if (!ret) {
8955ca5b
                     /* Hint field is a uint16_t and precedes the Name field */
288057e9
                     if ((buffer = fmap_need_off_once(map, offset + sizeof(uint16_t), MIN(PE_MAXNAMESIZE, fsize - offset))) != NULL) {
                         funcname = cli_strndup(buffer, MIN(PE_MAXNAMESIZE, fsize - offset));
8955ca5b
                         if (funcname == NULL) {
211edda0
                             cli_dbgmsg("scan_pe: cannot duplicate function name\n");
8955ca5b
                             return CL_EMEM;
                         }
                     }
                 }
             } else {
                 /* ordinal lookup */
                 funcname = pe_ordinal(dllname, thunk32.u.Ordinal & 0xFFFF);
                 if (funcname == NULL) {
211edda0
                     cli_dbgmsg("scan_pe: cannot duplicate function name\n");
8955ca5b
                     return CL_EMEM;
                 }
             }
 
4cd97da4
             UPDATE_IMPHASH();
634c8594
             free(funcname);
             if (ret != CL_SUCCESS)
                 return ret;
8955ca5b
         }
     } else {
         struct pe_image_thunk64 thunk64;
 
211edda0
         while ((num_fns < PE_MAXIMPORTS) && (fmap_readn(map, &thunk64, thuoff, sizeof(struct pe_image_thunk64)) == sizeof(struct pe_image_thunk64)) && (thunk64.u.Ordinal != 0)) {
8955ca5b
             char *funcname = NULL;
211edda0
             thuoff += sizeof(struct pe_image_thunk64);
8955ca5b
 
634c8594
             thunk64.u.Ordinal = EC64(thunk64.u.Ordinal);
 
8d378420
             if (!(thunk64.u.Ordinal & PE_IMAGEDIR_ORDINAL_FLAG64)) {
afe940da
                 offset = cli_rawaddr(thunk64.u.Function, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
8955ca5b
 
1f9f2096
                 if (!err) {
8955ca5b
                     /* Hint field is a uint16_t and precedes the Name field */
288057e9
                     if ((buffer = fmap_need_off_once(map, offset + sizeof(uint16_t), MIN(PE_MAXNAMESIZE, fsize - offset))) != NULL) {
                         funcname = cli_strndup(buffer, MIN(PE_MAXNAMESIZE, fsize - offset));
8955ca5b
                         if (funcname == NULL) {
211edda0
                             cli_dbgmsg("scan_pe: cannot duplicate function name\n");
8955ca5b
                             return CL_EMEM;
                         }
                     }
                 }
             } else {
                 /* ordinal lookup */
4642fd04
                 funcname = pe_ordinal(dllname, thunk64.u.Ordinal & 0xFFFF);
8955ca5b
                 if (funcname == NULL) {
211edda0
                     cli_dbgmsg("scan_pe: cannot duplicate function name\n");
8955ca5b
                     return CL_EMEM;
                 }
             }
 
4cd97da4
             UPDATE_IMPHASH();
634c8594
             free(funcname);
             if (ret != CL_SUCCESS)
                 return ret;
8955ca5b
         }
     }
 
     return CL_SUCCESS;
 }
 
afe940da
 static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *impsz, int *genhash, struct cli_exe_info *peinfo)
211edda0
 {
8955ca5b
     struct pe_image_import_descriptor *image;
     fmap_t *map = *ctx->fmap;
     size_t left, fsize = map->len;
211edda0
     uint32_t impoff, offset;
     const char *impdes, *buffer;
     void *hashctx[CLI_HASH_AVAIL_TYPES];
     enum CLI_HASH_TYPE type;
1f9f2096
     int nimps = 0, ret = CL_SUCCESS;
     unsigned int err;
211edda0
     int first = 1;
 
d009f9e8
     /* If the PE doesn't have an import table then skip it. This is an
      * uncommon case but can happen. */
afe940da
     if (peinfo->dirs[1].VirtualAddress == 0 || peinfo->dirs[1].Size == 0) {
d009f9e8
         cli_dbgmsg("scan_pe: import table data dir does not exist (skipping .imp scanning)\n");
8955ca5b
         return CL_SUCCESS;
     }
 
afe940da
     // TODO Add EC32 wrappers
     impoff = cli_rawaddr(peinfo->dirs[1].VirtualAddress, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
     if (err || impoff + peinfo->dirs[1].Size > fsize) {
211edda0
         cli_dbgmsg("scan_pe: invalid rva for import table data\n");
8955ca5b
         return CL_SUCCESS;
     }
 
afe940da
     // TODO Add EC32 wrapper
     impdes = fmap_need_off(map, impoff, peinfo->dirs[1].Size);
288057e9
     if (impdes == NULL) {
211edda0
         cli_dbgmsg("scan_pe: failed to acquire fmap buffer\n");
48641f8b
         return CL_EREAD;
8955ca5b
     }
afe940da
     left = peinfo->dirs[1].Size;
8955ca5b
 
211edda0
     memset(hashctx, 0, sizeof(hashctx));
288057e9
     if (genhash[CLI_HASH_MD5]) {
211edda0
         hashctx[CLI_HASH_MD5] = cl_hash_init("md5");
634c8594
         if (hashctx[CLI_HASH_MD5] == NULL) {
afe940da
             fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
211edda0
             return CL_EMEM;
634c8594
         }
211edda0
     }
288057e9
     if (genhash[CLI_HASH_SHA1]) {
211edda0
         hashctx[CLI_HASH_SHA1] = cl_hash_init("sha1");
634c8594
         if (hashctx[CLI_HASH_SHA1] == NULL) {
afe940da
             fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
211edda0
             return CL_EMEM;
634c8594
         }
211edda0
     }
288057e9
     if (genhash[CLI_HASH_SHA256]) {
211edda0
         hashctx[CLI_HASH_SHA256] = cl_hash_init("sha256");
634c8594
         if (hashctx[CLI_HASH_SHA256] == NULL) {
afe940da
             fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
211edda0
             return CL_EMEM;
634c8594
         }
211edda0
     }
8955ca5b
 
     image = (struct pe_image_import_descriptor *)impdes;
288057e9
     while (left > sizeof(struct pe_image_import_descriptor) && image->Name != 0 && nimps < PE_MAXIMPORTS) {
8955ca5b
         char *dllname = NULL;
 
         left -= sizeof(struct pe_image_import_descriptor);
211edda0
         nimps++;
8955ca5b
 
634c8594
         /* Endian Conversion */
         image->u.OriginalFirstThunk = EC32(image->u.OriginalFirstThunk);
288057e9
         image->TimeDateStamp        = EC32(image->TimeDateStamp);
         image->ForwarderChain       = EC32(image->ForwarderChain);
         image->Name                 = EC32(image->Name);
         image->FirstThunk           = EC32(image->FirstThunk);
634c8594
 
7cd9337a
         /* DLL name acquisition */
afe940da
         offset = cli_rawaddr(image->Name, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
288057e9
         if (err || offset > fsize) {
211edda0
             cli_dbgmsg("scan_pe: invalid rva for dll name\n");
48641f8b
             /* TODO: ignore or return? */
8955ca5b
             /*
               image++;
               continue;
              */
634c8594
             ret = CL_EFORMAT;
             goto hash_imptbl_end;
8955ca5b
         }
 
288057e9
         buffer = fmap_need_off_once(map, offset, MIN(PE_MAXNAMESIZE, fsize - offset));
634c8594
         if (buffer == NULL) {
             cli_dbgmsg("scan_pe: failed to read name for dll\n");
             ret = CL_EREAD;
             goto hash_imptbl_end;
         }
 
288057e9
         if (validate_impname(dllname, MIN(PE_MAXNAMESIZE, fsize - offset), 1) == 0) {
634c8594
             cli_dbgmsg("scan_pe: invalid name for imported dll\n");
             ret = CL_EFORMAT;
             goto hash_imptbl_end;
         }
 
288057e9
         dllname = cli_strndup(buffer, MIN(PE_MAXNAMESIZE, fsize - offset));
634c8594
         if (dllname == NULL) {
             cli_dbgmsg("scan_pe: cannot duplicate dll name\n");
             ret = CL_EMEM;
             goto hash_imptbl_end;
8955ca5b
         }
 
48641f8b
         /* DLL function handling - inline function */
afe940da
         ret = hash_impfns(ctx, hashctx, impsz, image, dllname, peinfo, &first);
634c8594
         free(dllname);
         dllname = NULL;
         if (ret != CL_SUCCESS)
             goto hash_imptbl_end;
8955ca5b
 
         image++;
     }
 
288057e9
 hash_imptbl_end:
afe940da
     fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
288057e9
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
211edda0
         cl_finish_hash(hashctx[type], digest[type]);
634c8594
     return ret;
211edda0
 }
48641f8b
 
afe940da
 static int scan_pe_imp(cli_ctx *ctx, struct cli_exe_info *peinfo)
211edda0
 {
     struct cli_matcher *imp = ctx->engine->hm_imp;
     unsigned char *hashset[CLI_HASH_AVAIL_TYPES];
     const char *virname = NULL;
     int genhash[CLI_HASH_AVAIL_TYPES];
     uint32_t impsz = 0;
     enum CLI_HASH_TYPE type;
     int ret = CL_CLEAN;
 
     /* pick hashtypes to generate */
288057e9
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
211edda0
         genhash[type] = cli_hm_have_any(imp, type);
288057e9
         if (genhash[type]) {
634c8594
             hashset[type] = cli_malloc(hashlen[type]);
288057e9
             if (!hashset[type]) {
634c8594
                 cli_errmsg("scan_pe: cli_malloc failed!\n");
288057e9
                 for (; type > 0;)
634c8594
                     free(hashset[--type]);
                 return CL_EMEM;
             }
288057e9
         } else {
634c8594
             hashset[type] = NULL;
211edda0
         }
     }
 
     /* Force md5 hash generation for debug and preclass */
 #if HAVE_JSON
     if ((cli_debug_flag || ctx->wrkproperty) && !genhash[CLI_HASH_MD5]) {
 #else
     if (cli_debug_flag && !genhash[CLI_HASH_MD5]) {
 #endif
         genhash[CLI_HASH_MD5] = 1;
5ce31ac0
         hashset[CLI_HASH_MD5] = cli_calloc(hashlen[CLI_HASH_MD5], sizeof(char));
288057e9
         if (!hashset[CLI_HASH_MD5]) {
211edda0
             cli_errmsg("scan_pe: cli_malloc failed!\n");
288057e9
             for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
634c8594
                 free(hashset[type]);
211edda0
             return CL_EMEM;
         }
     }
 
     /* Generate hashes */
afe940da
     ret = hash_imptbl(ctx, hashset, &impsz, genhash, peinfo);
634c8594
     if (ret != CL_SUCCESS) {
288057e9
         for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
634c8594
             free(hashset[type]);
         return ret;
     }
211edda0
 
     /* Print hash */
4bf268ce
 #if HAVE_JSON
48641f8b
     if (cli_debug_flag || ctx->wrkproperty) {
 #else
     if (cli_debug_flag) {
4bf268ce
 #endif
288057e9
         char *dstr = cli_str2hex((char *)hashset[CLI_HASH_MD5], hashlen[CLI_HASH_MD5]);
211edda0
         cli_dbgmsg("IMP: %s:%u\n", dstr ? (char *)dstr : "(NULL)", impsz);
48641f8b
 #if HAVE_JSON
         if (ctx->wrkproperty)
             cli_jsonstr(ctx->wrkproperty, "Imphash", dstr ? dstr : "(NULL)");
 #endif
         if (dstr)
             free(dstr);
     }
 
211edda0
     /* Do scans */
288057e9
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
         if (cli_hm_scan(hashset[type], impsz, &virname, imp, type) == CL_VIRUS) {
cbf5017a
             ret = cli_append_virus(ctx, virname);
             if (ret != CL_CLEAN) {
                 if (ret != CL_VIRUS)
                     break;
048a88e6
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
             }
634c8594
         }
288057e9
         if (cli_hm_scan_wild(hashset[type], &virname, imp, type) == CL_VIRUS) {
32628142
             cli_append_virus(ctx, virname);
cbf5017a
             if (ret != CL_CLEAN) {
                 if (ret != CL_VIRUS)
                     break;
048a88e6
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
             }
288057e9
         }
32628142
     }
66c20d21
 
288057e9
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
634c8594
         free(hashset[type]);
8955ca5b
     return ret;
 }
 
172c4dd2
 #if HAVE_JSON
 static struct json_object *get_pe_property(cli_ctx *ctx)
 {
     struct json_object *pe;
 
     if (!(ctx) || !(ctx->wrkproperty))
         return NULL;
 
6c048b8a
     if (!json_object_object_get_ex(ctx->wrkproperty, "PE", &pe)) {
172c4dd2
         pe = json_object_new_object();
         if (!(pe))
             return NULL;
 
         json_object_object_add(ctx->wrkproperty, "PE", pe);
     }
 
     return pe;
 }
 
 static void pe_add_heuristic_property(cli_ctx *ctx, const char *key)
 {
     struct json_object *heuristics;
     struct json_object *pe;
     struct json_object *str;
 
     pe = get_pe_property(ctx);
     if (!(pe))
         return;
 
6c048b8a
     if (!json_object_object_get_ex(pe, "Heuristics", &heuristics)) {
172c4dd2
         heuristics = json_object_new_array();
         if (!(heuristics))
             return;
 
         json_object_object_add(pe, "Heuristics", heuristics);
     }
 
     str = json_object_new_string(key);
     if (!(str))
         return;
 
     json_object_array_add(heuristics, str);
 }
 
 static struct json_object *get_section_json(cli_ctx *ctx)
 {
     struct json_object *pe;
     struct json_object *section;
 
     pe = get_pe_property(ctx);
     if (!(pe))
         return NULL;
 
6c048b8a
     if (!json_object_object_get_ex(pe, "Sections", &section)) {
172c4dd2
         section = json_object_new_array();
         if (!(section))
             return NULL;
 
         json_object_object_add(pe, "Sections", section);
     }
 
     return section;
 }
 
 static void add_section_info(cli_ctx *ctx, struct cli_exe_section *s)
 {
     struct json_object *sections, *section, *obj;
     char address[16];
 
     sections = get_section_json(ctx);
     if (!(sections))
         return;
 
     section = json_object_new_object();
     if (!(section))
         return;
 
     obj = json_object_new_int((int32_t)(s->rsz));
     if (!(obj))
         return;
 
     json_object_object_add(section, "RawSize", obj);
 
     obj = json_object_new_int((int32_t)(s->raw));
     if (!(obj))
         return;
 
     json_object_object_add(section, "RawOffset", obj);
 
     snprintf(address, sizeof(address), "0x%08x", s->rva);
 
     obj = json_object_new_string(address);
     if (!(obj))
         return;
 
     json_object_object_add(section, "VirtualAddress", obj);
 
636b7178
     obj = json_object_new_boolean((s->chr & 0x20000000) == 0x20000000);
     if ((obj))
         json_object_object_add(section, "Executable", obj);
 
     obj = json_object_new_boolean((s->chr & 0x80000000) == 0x80000000);
     if ((obj))
         json_object_object_add(section, "Writable", obj);
 
288057e9
     obj = json_object_new_boolean(s->urva >> 31 || s->uvsz >> 31 || (s->rsz && s->uraw >> 31) || s->ursz >> 31);
636b7178
     if ((obj))
         json_object_object_add(section, "Signed", obj);
 
172c4dd2
     json_object_array_add(sections, section);
 }
 #endif
 
453d8180
 int cli_scanpe(cli_ctx *ctx)
6307ca15
 {
f98a68af
     uint8_t polipos = 0;
afe940da
     char epbuff[4096], *tempfile;
f98a68af
     uint32_t epsize;
6c03dc5d
     size_t bytes;
afe940da
     unsigned int i, j, found, upx_success = 0, err;
     unsigned int ssize = 0, dsize = 0, corrupted_cur;
f98a68af
     int (*upxfn)(const char *, uint32_t, char *, uint32_t *, uint32_t, uint32_t, uint32_t) = NULL;
288057e9
     const char *src                                                                        = NULL;
     char *dest                                                                             = NULL;
afe940da
     int ndesc, ret = CL_CLEAN, upack = 0;
f98a68af
     size_t fsize;
     struct cli_bc_ctx *bc_ctx;
     fmap_t *map;
     struct cli_pe_hook_data pedata;
16b28d07
 #ifdef HAVE__INTERNAL__SHA_COLLECT
f98a68af
     int sha_collect = ctx->sha_collect;
16b28d07
 #endif
f98a68af
     uint32_t viruses_found = 0;
172c4dd2
 #if HAVE_JSON
288057e9
     int toval                   = 0;
     struct json_object *pe_json = NULL;
172c4dd2
 #endif
6307ca15
 
288057e9
     if (!ctx) {
f98a68af
         cli_errmsg("cli_scanpe: ctx == NULL\n");
         return CL_ENULLARG;
b07f9a5e
     }
172c4dd2
 
 #if HAVE_JSON
20b45621
     if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
         return CL_ETIMEOUT;
     }
 
048a88e6
     if (SCAN_COLLECT_METADATA) {
172c4dd2
         pe_json = get_pe_property(ctx);
20b45621
     }
172c4dd2
 #endif
afe940da
     map   = *ctx->fmap;
     fsize = map->len;
6307ca15
 
afe940da
     struct cli_exe_info _peinfo;
     struct cli_exe_info *peinfo = &_peinfo;
7a0143cf
 
5319602c
     uint32_t opts = CLI_PEHEADER_OPT_DBG_PRINT_INFO | CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS;
6307ca15
 
172c4dd2
 #if HAVE_JSON
afe940da
     if (SCAN_COLLECT_METADATA) {
         opts |= CLI_PEHEADER_OPT_COLLECT_JSON;
6307ca15
     }
172c4dd2
 #endif
f98a68af
 
afe940da
     if (DETECT_BROKEN_PE) {
         opts |= CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS;
f86b5ac1
     }
f98a68af
 
afe940da
     cli_exe_info_init(peinfo, 0);
a9082ea2
 
afe940da
     ret = cli_peheader(map, peinfo, opts, ctx);
636b7178
 
afe940da
     // Warn the user if PE header parsing failed - if it's a binary that runs
     // successfully on Windows, we need to relax our PE parsing standards so
     // that we make sure the executable gets scanned appropriately
6307ca15
 
afe940da
 #define PE_HDR_PARSE_FAIL_CONSEQUENCE "won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n"
636b7178
 
afe940da
     if (CLI_PEHEADER_RET_BROKEN_PE == ret) {
288057e9
         if (DETECT_BROKEN_PE) {
afe940da
             // TODO Handle allmatch
288057e9
             ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
5319602c
             cli_exe_info_destroy(peinfo);
cbf5017a
             return ret;
f98a68af
         }
afe940da
         cli_dbgmsg("cli_scanpe: PE header appears broken - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
5319602c
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
 
afe940da
     } else if (CLI_PEHEADER_RET_JSON_TIMEOUT == ret) {
         cli_dbgmsg("cli_scanpe: JSON creation timed out - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
5319602c
         cli_exe_info_destroy(peinfo);
afe940da
         return CL_ETIMEOUT;
     } else if (CLI_PEHEADER_RET_GENERIC_ERROR == ret) {
         cli_dbgmsg("cli_scanpe: An error occurred when parsing the PE header - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
5319602c
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
57866af1
     }
 
afe940da
     if (!peinfo->is_pe32plus) { /* PE */
288057e9
         if (DCONF & PE_CONF_UPACK)
afe940da
             upack = (EC16(peinfo->file_hdr.SizeOfOptionalHeader) == 0x148);
6307ca15
     }
afe940da
     for (i = 0; i < peinfo->nsections; i++) {
6307ca15
 
afe940da
         if (peinfo->sections[i].rsz) { /* Don't bother with virtual only sections */
             // TODO Regarding the commented out check below:
             // This used to check that the section name was NULL, but now that
             // header parsing is done in cli_peheader (and since we don't yet
             // make the section name availabe via peinfo->sections[]) it would
             // be a pain to fetch the name here.  Since this is the only place
             // in cli_scanpe that needs the section name, and since I verified
             // that detection still occurs for Polipos without this check,
             // let's leave it commented out for now.
             if (SCAN_HEURISTICS && (DCONF & PE_CONF_POLIPOS) && /*!*peinfo->sections[i].sname &&*/ peinfo->sections[i].vsz > 40000 && peinfo->sections[i].vsz < 70000 && peinfo->sections[i].chr == 0xe0000060) polipos = i;
c09d6c19
 
f98a68af
             /* check hash section sigs */
288057e9
             if ((DCONF & PE_CONF_MD5SECT) && ctx->engine->hm_mdb) {
afe940da
                 ret = scan_pe_mdb(ctx, &(peinfo->sections[i]));
f98a68af
                 if (ret != CL_CLEAN) {
afe940da
                     // TODO Handle allmatch
f98a68af
                     if (ret != CL_VIRUS)
afe940da
                         cli_errmsg("cli_scanpe: scan_pe_mdb failed: %s!\n", cl_strerror(ret));
f0f7f92f
 
f98a68af
                     cli_dbgmsg("------------------------------------\n");
afe940da
                     cli_exe_info_destroy(peinfo);
f98a68af
                     return ret;
                 }
             }
         }
6307ca15
     }
 
afe940da
     // TODO Don't bail out here
     if (peinfo->is_pe32plus) { /* Do not continue for PE32+ files */
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
6307ca15
     }
 
afe940da
     epsize = fmap_readn(map, epbuff, peinfo->ep, 4096);
fa4f9e8b
 
59098a11
     /* Disasm scan disabled since it's now handled by the bytecode */
 
afe940da
     /* CLI_UNPTEMP("cli_scanpe: DISASM",(peinfo->sections,0)); */
59098a11
     /* if(disasmbuf((unsigned char*)epbuff, epsize, ndesc)) */
89f3be11
     /*  ret = cli_scandesc(ndesc, ctx, CL_TYPE_PE_DISASM, 1, NULL, AC_SCAN_VIR); */
59098a11
     /* close(ndesc); */
     /* if(ret == CL_VIRUS) { */
afe940da
     /*  cli_exe_info_destroy(peinfo); */
     /*  CLI_TMPUNLK(); */
     /*  free(tempfile); */
89f3be11
     /*  return ret; */
59098a11
     /* } */
afe940da
     /* CLI_TMPUNLK(); */
     /* free(tempfile); */
68a77450
 
afe940da
     if (peinfo->overlay_start && peinfo->overlay_size > 0) {
         ret = cli_scanishield(ctx, peinfo->overlay_start, peinfo->overlay_size);
         if (ret != CL_CLEAN) {
             // TODO Handle allmatch
             cli_exe_info_destroy(peinfo);
             return ret;
f98a68af
         }
56e5821b
     }
 
afe940da
     pedata.nsections = peinfo->nsections;
     pedata.ep        = peinfo->ep;
288057e9
     pedata.offset    = 0;
afe940da
     memcpy(&pedata.file_hdr, &(peinfo->file_hdr), sizeof(peinfo->file_hdr));
     // TODO no need to copy both of these for each binary
     memcpy(&pedata.opt32, &(peinfo->pe_opt.opt32), sizeof(peinfo->pe_opt.opt32));
     memcpy(&pedata.opt64, &(peinfo->pe_opt.opt64), sizeof(peinfo->pe_opt.opt64));
     memcpy(&pedata.dirs, &(peinfo->dirs), sizeof(peinfo->dirs));
     // Gross
     memcpy(&pedata.opt32_dirs, &(peinfo->dirs), sizeof(peinfo->dirs));
     memcpy(&pedata.opt64_dirs, &(peinfo->dirs), sizeof(peinfo->dirs));
     pedata.e_lfanew    = peinfo->e_lfanew;
     pedata.overlays    = peinfo->overlay_start;
     pedata.overlays_sz = peinfo->overlay_size;
     pedata.hdr_size    = peinfo->hdr_size;
0df99607
 
     /* Bytecode BC_PE_ALL hook */
     bc_ctx = cli_bytecode_context_alloc();
     if (!bc_ctx) {
f98a68af
         cli_errmsg("cli_scanpe: can't allocate memory for bc_ctx\n");
afe940da
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_EMEM;
0df99607
     }
f98a68af
 
afe940da
     cli_bytecode_context_setpe(bc_ctx, &pedata, peinfo->sections);
0df99607
     cli_bytecode_context_setctx(bc_ctx, ctx);
6ad45a29
     ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_ALL, map);
6a049897
     switch (ret) {
         case CL_ENULLARG:
             cli_warnmsg("cli_scanpe: NULL argument supplied\n");
             break;
         case CL_VIRUS:
         case CL_BREAK:
afe940da
             // TODO Handle allmatch
             cli_exe_info_destroy(peinfo);
6a049897
             cli_bytecode_context_destroy(bc_ctx);
             return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
0df99607
     }
042a78ac
     cli_bytecode_context_destroy(bc_ctx);
8955ca5b
 
     /* Attempt to run scans on import table */
4adb74a4
     /* Run if there are existing signatures and/or preclassing */
48641f8b
 #if HAVE_JSON
832d44e7
     if (DCONF & PE_CONF_IMPTBL && (ctx->engine->hm_imp || ctx->wrkproperty)) {
48641f8b
 #else
832d44e7
     if (DCONF & PE_CONF_IMPTBL && ctx->engine->hm_imp) {
48641f8b
 #endif
afe940da
         ret = scan_pe_imp(ctx, peinfo);
4adb74a4
         switch (ret) {
48641f8b
             case CL_SUCCESS:
                 break;
4adb74a4
             case CL_ENULLARG:
                 cli_warnmsg("cli_scanpe: NULL argument supplied\n");
8955ca5b
                 break;
4adb74a4
             case CL_VIRUS:
048a88e6
                 if (SCAN_ALLMATCHES)
4adb74a4
                     break;
                 /* intentional fall-through */
             case CL_BREAK:
afe940da
                 cli_exe_info_destroy(peinfo);
4adb74a4
                 return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
48641f8b
             default:
afe940da
                 cli_exe_info_destroy(peinfo);
48641f8b
                 return ret;
4adb74a4
         }
8955ca5b
     }
41fd7c2f
     /* Attempt to detect some popular polymorphic viruses */
 
     /* W32.Parite.B */
afe940da
     if (SCAN_HEURISTICS && (DCONF & PE_CONF_PARITE) && !peinfo->is_dll && epsize == 4096 && peinfo->ep == peinfo->sections[peinfo->nsections - 1].raw) {
95e31dc7
         const char *pt = cli_memstr(epbuff, 4040, "\x47\x65\x74\x50\x72\x6f\x63\x41\x64\x64\x72\x65\x73\x73\x00", 15);
288057e9
         if (pt) {
f98a68af
             pt += 15;
288057e9
             if ((((uint32_t)cli_readint32(pt) ^ (uint32_t)cli_readint32(pt + 4)) == 0x505a4f) && (((uint32_t)cli_readint32(pt + 8) ^ (uint32_t)cli_readint32(pt + 12)) == 0xffffb) && (((uint32_t)cli_readint32(pt + 16) ^ (uint32_t)cli_readint32(pt + 20)) == 0xb8)) {
                 ret = cli_append_virus(ctx, "Heuristics.W32.Parite.B");
cbf5017a
                 if (ret != CL_CLEAN) {
                     if (ret == CL_VIRUS) {
048a88e6
                         if (!SCAN_ALLMATCHES) {
afe940da
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
288057e9
                         } else
cbf5017a
                             viruses_found++;
                     } else {
afe940da
                         cli_exe_info_destroy(peinfo);
cbf5017a
                         return ret;
                     }
f98a68af
                 }
             }
         }
41fd7c2f
     }
 
343316ab
     /* Kriz */
afe940da
     if (SCAN_HEURISTICS && (DCONF & PE_CONF_KRIZ) && epsize >= 200 && CLI_ISCONTAINED(peinfo->sections[peinfo->nsections - 1].raw, peinfo->sections[peinfo->nsections - 1].rsz, peinfo->ep, 0x0fd2) && epbuff[1] == '\x9c' && epbuff[2] == '\x60') {
288057e9
         enum { KZSTRASH,
                KZSCDELTA,
                KZSPDELTA,
                KZSGETSIZE,
                KZSXORPRFX,
                KZSXOR,
                KZSDDELTA,
                KZSLOOP,
                KZSTOP };
         uint8_t kzs[]    = {KZSTRASH, KZSCDELTA, KZSPDELTA, KZSGETSIZE, KZSTRASH, KZSXORPRFX, KZSXOR, KZSTRASH, KZSDDELTA, KZSTRASH, KZSLOOP, KZSTOP};
f98a68af
         uint8_t *kzstate = kzs;
288057e9
         uint8_t *kzcode  = (uint8_t *)epbuff + 3;
         uint8_t kzdptr = 0xff, kzdsize = 0xff;
         int kzlen = 197, kzinitlen = 0xffff, kzxorlen = -1;
afe940da
         cli_dbgmsg("cli_scanpe: in kriz\n");
f98a68af
 
288057e9
         while (*kzstate != KZSTOP) {
f98a68af
             uint8_t op;
288057e9
             if (kzlen <= 6)
f98a68af
                 break;
 
             op = *kzcode++;
             kzlen--;
 
             switch (*kzstate) {
288057e9
                 case KZSTRASH:
                 case KZSGETSIZE: {
                     int opsz = 0;
                     switch (op) {
                         case 0x81:
                             kzcode += 5;
                             kzlen -= 5;
                             break;
                         case 0xb8:
                         case 0xb9:
                         case 0xba:
                         case 0xbb:
                         case 0xbd:
                         case 0xbe:
                         case 0xbf:
                             if (*kzstate == KZSGETSIZE && cli_readint32(kzcode) == 0x0fd2) {
                                 kzinitlen = kzlen - 5;
                                 kzdsize   = op - 0xb8;
                                 kzstate++;
                                 op = 4; /* fake the register to avoid breaking out */
 
afe940da
                                 cli_dbgmsg("cli_scanpe: kriz: using #%d as size counter\n", kzdsize);
288057e9
                             }
                             opsz = 4;
                         case 0x48:
                         case 0x49:
                         case 0x4a:
                         case 0x4b:
                         case 0x4d:
                         case 0x4e:
                         case 0x4f:
                             op &= 7;
                             if (op != kzdptr && op != kzdsize) {
                                 kzcode += opsz;
                                 kzlen -= opsz;
                                 break;
                             }
                         default:
                             kzcode--;
                             kzlen++;
                             kzstate++;
f98a68af
                     }
 
288057e9
                     break;
f98a68af
                 }
288057e9
                 case KZSCDELTA:
                     if (op == 0xe8 && (uint32_t)cli_readint32(kzcode) < 0xff) {
                         kzlen -= *kzcode + 4;
                         kzcode += *kzcode + 4;
                         kzstate++;
                     } else {
                         *kzstate = KZSTOP;
                     }
f98a68af
 
288057e9
                     break;
                 case KZSPDELTA:
                     if ((op & 0xf8) == 0x58 && (kzdptr = op - 0x58) != 4) {
                         kzstate++;
afe940da
                         cli_dbgmsg("cli_scanpe: kriz: using #%d as pointer\n", kzdptr);
288057e9
                     } else {
                         *kzstate = KZSTOP;
                     }
f98a68af
 
                     break;
288057e9
                 case KZSXORPRFX:
f98a68af
                     kzstate++;
288057e9
                     if (op == 0x3e)
                         break;
                 case KZSXOR:
                     if (op == 0x80 && *kzcode == kzdptr + 0xb0) {
                         kzxorlen = kzlen;
                         kzcode += +6;
                         kzlen -= +6;
                         kzstate++;
                     } else {
                         *kzstate = KZSTOP;
                     }
f98a68af
 
288057e9
                     break;
                 case KZSDDELTA:
                     if (op == kzdptr + 0x48)
                         kzstate++;
                     else
                         *kzstate = KZSTOP;
f98a68af
 
288057e9
                     break;
                 case KZSLOOP:
                     if (op == kzdsize + 0x48 && *kzcode == 0x75 && kzlen - (int8_t)kzcode[1] - 3 <= kzinitlen && kzlen - (int8_t)kzcode[1] >= kzxorlen) {
                         ret = cli_append_virus(ctx, "Heuristics.W32.Kriz");
                         if (ret != CL_CLEAN) {
                             if (ret == CL_VIRUS) {
                                 if (!SCAN_ALLMATCHES) {
afe940da
                                     cli_exe_info_destroy(peinfo);
288057e9
                                     return ret;
                                 } else
                                     viruses_found++;
                             } else {
afe940da
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
                             }
                         }
f98a68af
                     }
afe940da
                     cli_dbgmsg("cli_scanpe: kriz: loop out of bounds, corrupted sample?\n");
288057e9
                     kzstate++;
f98a68af
             }
         }
343316ab
     }
 
9ad59d16
     /* W32.Magistr.A/B */
afe940da
     if (SCAN_HEURISTICS && (DCONF & PE_CONF_MAGISTR) && !peinfo->is_dll && (peinfo->nsections > 1) && (peinfo->sections[peinfo->nsections - 1].chr & 0x80000000)) {
95e31dc7
         uint32_t rsize, vsize, dam = 0;
9ad59d16
 
afe940da
         vsize = peinfo->sections[peinfo->nsections - 1].uvsz;
         rsize = peinfo->sections[peinfo->nsections - 1].rsz;
         if (rsize < peinfo->sections[peinfo->nsections - 1].ursz) {
             rsize = peinfo->sections[peinfo->nsections - 1].ursz;
288057e9
             dam   = 1;
f98a68af
         }
9ad59d16
 
288057e9
         if (vsize >= 0x612c && rsize >= 0x612c && ((vsize & 0xff) == 0xec)) {
f98a68af
             int bw = rsize < 0x7000 ? rsize : 0x7000;
             const char *tbuff;
9ad59d16
 
afe940da
             if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
288057e9
                 if (cli_memstr(tbuff, 4091, "\xe8\x2c\x61\x00\x00", 5)) {
cbf5017a
                     ret = cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.A.dam" : "Heuristics.W32.Magistr.A");
                     if (ret != CL_CLEAN) {
                         if (ret == CL_VIRUS) {
048a88e6
                             if (!SCAN_ALLMATCHES) {
afe940da
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
288057e9
                             } else
cbf5017a
                                 viruses_found++;
                         } else {
afe940da
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
                         }
f98a68af
                     }
                 }
             }
288057e9
         } else if (rsize >= 0x7000 && vsize >= 0x7000 && ((vsize & 0xff) == 0xed)) {
f98a68af
             int bw = rsize < 0x8000 ? rsize : 0x8000;
             const char *tbuff;
 
afe940da
             if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
288057e9
                 if (cli_memstr(tbuff, 4091, "\xe8\x04\x72\x00\x00", 5)) {
                     ret = cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.B.dam" : "Heuristics.W32.Magistr.B");
cbf5017a
                     if (ret != CL_CLEAN) {
                         if (ret == CL_VIRUS) {
048a88e6
                             if (!SCAN_ALLMATCHES) {
afe940da
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
288057e9
                             } else
cbf5017a
                                 viruses_found++;
                         } else {
afe940da
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
                         }
f98a68af
                     }
288057e9
                 }
f98a68af
             }
         }
9ad59d16
     }
 
be62f8ce
     /* W32.Polipos.A */
afe940da
     // TODO Add endianness correction to SizeOfStackReserve access
     while (polipos && !peinfo->is_dll && peinfo->nsections > 2 && peinfo->nsections < 13 && peinfo->e_lfanew <= 0x800 && (EC16(peinfo->pe_opt.opt32.Subsystem) == 2 || EC16(peinfo->pe_opt.opt32.Subsystem) == 3) && EC16(peinfo->file_hdr.Machine) == 0x14c && peinfo->pe_opt.opt32.SizeOfStackReserve >= 0x80000) {
f98a68af
         uint32_t jump, jold, *jumps = NULL;
         const uint8_t *code;
         unsigned int xsjs = 0;
 
afe940da
         if (peinfo->sections[0].rsz > CLI_MAX_ALLOCATION)
f98a68af
             break;
afe940da
         if (peinfo->sections[0].rsz < 5)
f98a68af
             break;
afe940da
         if (!(code = fmap_need_off_once(map, peinfo->sections[0].raw, peinfo->sections[0].rsz)))
f98a68af
             break;
 
afe940da
         for (i = 0; i < peinfo->sections[0].rsz - 5; i++) {
288057e9
             if ((uint8_t)(code[i] - 0xe8) > 1)
f98a68af
                 continue;
 
afe940da
             jump = cli_rawaddr(peinfo->sections[0].rva + i + 5 + cli_readint32(&code[i + 1]), peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
             if (err || !CLI_ISCONTAINED(peinfo->sections[polipos].raw, peinfo->sections[polipos].rsz, jump, 9))
f98a68af
                 continue;
 
288057e9
             if (xsjs % 128 == 0) {
                 if (xsjs == 1280)
f98a68af
                     break;
 
288057e9
                 if (!(jumps = (uint32_t *)cli_realloc2(jumps, (xsjs + 128) * sizeof(uint32_t)))) {
afe940da
                     cli_exe_info_destroy(peinfo);
f98a68af
                     return CL_EMEM;
                 }
             }
 
288057e9
             j = 0;
             for (; j < xsjs; j++) {
                 if (jumps[j] < jump)
f98a68af
                     continue;
288057e9
                 if (jumps[j] == jump) {
f98a68af
                     xsjs--;
                     break;
                 }
 
288057e9
                 jold     = jumps[j];
                 jumps[j] = jump;
                 jump     = jold;
f98a68af
             }
 
288057e9
             jumps[j] = jump;
f98a68af
             xsjs++;
         }
 
288057e9
         if (!xsjs)
f98a68af
             break;
 
afe940da
         cli_dbgmsg("cli_scanpe: Polipos: Checking %d xsect jump(s)\n", xsjs);
288057e9
         for (i = 0; i < xsjs; i++) {
             if (!(code = fmap_need_off_once(map, jumps[i], 9)))
f98a68af
                 continue;
 
288057e9
             if ((jump = cli_readint32(code)) == 0x60ec8b55 || (code[4] == 0x0ec && ((jump == 0x83ec8b55 && code[6] == 0x60) || (jump == 0x81ec8b55 && !code[7] && !code[8])))) {
                 ret = cli_append_virus(ctx, "Heuristics.W32.Polipos.A");
cbf5017a
                 if (ret != CL_CLEAN) {
                     if (ret == CL_VIRUS) {
048a88e6
                         if (!SCAN_ALLMATCHES) {
cbf5017a
                             free(jumps);
afe940da
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
288057e9
                         } else
cbf5017a
                             viruses_found++;
                     } else {
                         free(jumps);
afe940da
                         cli_exe_info_destroy(peinfo);
cbf5017a
                         return ret;
                     }
f98a68af
                 }
             }
         }
 
         free(jumps);
         break;
be62f8ce
     }
 
42d26ac9
     /* Trojan.Swizzor.Gen */
afe940da
     if (SCAN_HEURISTICS && (DCONF & PE_CONF_SWIZZOR) && peinfo->nsections > 1 && fsize > 64 * 1024 && fsize < 4 * 1024 * 1024) {
         if (peinfo->dirs[2].Size) {
f98a68af
             struct swizz_stats *stats = cli_calloc(1, sizeof(*stats));
288057e9
             unsigned int m            = 1000;
             ret                       = CL_CLEAN;
f98a68af
 
             if (!stats) {
afe940da
                 cli_exe_info_destroy(peinfo);
cbf5017a
                 return CL_EMEM;
f98a68af
             } else {
afe940da
                 cli_parseres_special(EC32(peinfo->dirs[2].VirtualAddress), EC32(peinfo->dirs[2].VirtualAddress), map, peinfo, fsize, 0, 0, &m, stats);
cbf5017a
                 if ((ret = cli_detect_swizz(stats)) == CL_VIRUS) {
288057e9
                     ret = cli_append_virus(ctx, "Heuristics.Trojan.Swizzor.Gen");
cbf5017a
                     if (ret != CL_CLEAN) {
                         if (ret == CL_VIRUS) {
048a88e6
                             if (!SCAN_ALLMATCHES) {
cbf5017a
                                 free(stats);
afe940da
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
288057e9
                             } else
cbf5017a
                                 viruses_found++;
                         } else {
                             free(stats);
afe940da
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
                         }
                     }
f98a68af
                 }
             }
         }
42d26ac9
     }
3fcb62ca
 
50177b5c
     /* !!!!!!!!!!!!!!    PACKERS START HERE    !!!!!!!!!!!!!! */
288057e9
     corrupted_cur        = ctx->corrupted_input;
50177b5c
     ctx->corrupted_input = 2; /* caller will reset on return */
 
4aa4a05c
     /* UPX, FSG, MEW support */
a9082ea2
 
     /* try to find the first section with physical size == 0 */
     found = 0;
288057e9
     if (DCONF & (PE_CONF_UPX | PE_CONF_FSG | PE_CONF_MEW)) {
afe940da
         for (i = 0; i < (unsigned int)peinfo->nsections - 1; i++) {
             if (!peinfo->sections[i].rsz && peinfo->sections[i].vsz && peinfo->sections[i + 1].rsz && peinfo->sections[i + 1].vsz) {
f98a68af
                 found = 1;
afe940da
                 cli_dbgmsg("cli_scanpe: UPX/FSG/MEW: empty section found - assuming compression\n");
172c4dd2
 #if HAVE_JSON
fcff42e9
                 if (pe_json != NULL)
                     cli_jsonbool(pe_json, "HasEmptySection", 1);
172c4dd2
 #endif
f98a68af
                 break;
             }
         }
a9082ea2
     }
 
4aa4a05c
     /* MEW support */
288057e9
     if (found && (DCONF & PE_CONF_MEW) && epsize >= 16 && epbuff[0] == '\xe9') {
f98a68af
         uint32_t fileoffset;
         const char *tbuff;
4aa4a05c
 
8650c790
         // TODO shouldn't peinfo->ep be used here instead?  ep is the file
         // offset, vep is the entry point RVA
afe940da
         fileoffset = (peinfo->vep + cli_readint32(epbuff + 1) + 5);
f98a68af
         while (fileoffset == 0x154 || fileoffset == 0x158) {
             char *src;
             uint32_t offdiff, uselzma;
4aa4a05c
 
afe940da
             cli_dbgmsg("cli_scanpe: MEW: found MEW characteristics %08X + %08X + 5 = %08X\n",
                        cli_readint32(epbuff + 1), peinfo->vep, cli_readint32(epbuff + 1) + peinfo->vep + 5);
4aa4a05c
 
288057e9
             if (!(tbuff = fmap_need_off_once(map, fileoffset, 0xb0)))
f98a68af
                 break;
4aa4a05c
 
f98a68af
             if (fileoffset == 0x154)
afe940da
                 cli_dbgmsg("cli_scanpe: MEW: Win9x compatibility was set!\n");
f98a68af
             else
afe940da
                 cli_dbgmsg("cli_scanpe: MEW: Win9x compatibility was NOT set!\n");
4aa4a05c
 
afe940da
             offdiff = cli_readint32(tbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase);
             if ((offdiff <= peinfo->sections[i + 1].rva) ||
                 (offdiff >= peinfo->sections[i + 1].rva + peinfo->sections[i + 1].raw - 4)) {
                 cli_dbgmsg("cli_scanpe: MEW: ESI is not in proper section\n");
f98a68af
                 break;
             }
4aa4a05c
 
afe940da
             offdiff -= peinfo->sections[i + 1].rva;
4aa4a05c
 
afe940da
             if (!peinfo->sections[i + 1].rsz) {
                 cli_dbgmsg("cli_scanpe: MEW: mew section is empty\n");
f98a68af
                 break;
             }
4aa4a05c
 
afe940da
             ssize = peinfo->sections[i + 1].vsz;
             dsize = peinfo->sections[i].vsz;
a5241d27
 
7a1e6921
             /* Guard against integer overflow */
             if ((ssize + dsize < ssize) || (ssize + dsize < dsize)) {
afe940da
                 cli_dbgmsg("cli_scanpe: MEW: section size (%08x) + diff size (%08x) exceeds max size of unsigned int (%08x)\n", ssize, dsize, UINT32_MAX);
7a1e6921
                 break;
             }
 
             /* Verify that offdiff does not exceed the ssize + sdiff */
             if (offdiff >= ssize + dsize) {
afe940da
                 cli_dbgmsg("cli_scanpe: MEW: offdiff (%08x) exceeds section size + diff size (%08x)\n", offdiff, ssize + dsize);
7a1e6921
                 break;
             }
 
afe940da
             cli_dbgmsg("cli_scanpe: MEW: ssize %08x dsize %08x offdiff: %08x\n", ssize, dsize, offdiff);
4aa4a05c
 
afe940da
             CLI_UNPSIZELIMITS("cli_scanpe: MEW", MAX(ssize, dsize));
             CLI_UNPSIZELIMITS("cli_scanpe: MEW", MAX(ssize + dsize, peinfo->sections[i + 1].rsz));
f98a68af
 
afe940da
             if (peinfo->sections[i + 1].rsz < offdiff + 12 || peinfo->sections[i + 1].rsz > ssize) {
                 cli_dbgmsg("cli_scanpe: MEW: Size mismatch: %08x\n", peinfo->sections[i + 1].rsz);
f98a68af
                 break;
             }
 
             /* allocate needed buffer */
288057e9
             if (!(src = cli_calloc(ssize + dsize, sizeof(char)))) {
afe940da
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
 
6c03dc5d
             bytes = fmap_readn(map, src + dsize, peinfo->sections[i + 1].raw, peinfo->sections[i + 1].rsz);
             if (bytes != peinfo->sections[i + 1].rsz) {
                 cli_dbgmsg("cli_scanpe: MEW: Can't read %u bytes [read: %zu]\n", peinfo->sections[i + 1].rsz, bytes);
afe940da
                 cli_exe_info_destroy(peinfo);
f98a68af
                 free(src);
                 return CL_EREAD;
             }
 
6c03dc5d
             cli_dbgmsg("cli_scanpe: MEW: %zu (%08zx) bytes read\n", bytes, bytes);
f98a68af
 
             /* count offset to lzma proc, if lzma used, 0xe8 -> call */
             if (tbuff[0x7b] == '\xe8') {
afe940da
                 if (!CLI_ISCONTAINED(peinfo->sections[1].rva, peinfo->sections[1].vsz, cli_readint32(tbuff + 0x7c) + fileoffset + 0x80, 4)) {
                     cli_dbgmsg("cli_scanpe: MEW: lzma proc out of bounds!\n");
f98a68af
                     free(src);
                     break; /* to next unpacker in chain */
                 }
 
afe940da
                 uselzma = cli_readint32(tbuff + 0x7c) - (peinfo->sections[0].rva - fileoffset - 0x80);
f98a68af
             } else {
                 uselzma = 0;
             }
e9d44a24
 
172c4dd2
 #if HAVE_JSON
fcff42e9
             if (pe_json != NULL)
                 cli_jsonstr(pe_json, "Packer", "MEW");
172c4dd2
 #endif
 
afe940da
             CLI_UNPTEMP("cli_scanpe: MEW", (src, 0));
             CLI_UNPRESULTS("cli_scanpe: MEW", (unmew11(src, offdiff, ssize, dsize, EC32(peinfo->pe_opt.opt32.ImageBase), peinfo->sections[0].rva, uselzma, ndesc)), 1, (src, 0));
f98a68af
             break;
         }
95e31dc7
     }
03a2d04a
 
afe940da
     // TODO Why do we bail here
288057e9
     if (epsize < 168) {
afe940da
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
95e31dc7
     }
 
     if (found || upack) {
f98a68af
         /* Check EP for UPX vs. FSG vs. Upack */
 
         /* Upack 0.39 produces 2 types of executables
7cd9337a
          * 3 sections:           | 2 sections (one empty, I don't check found if !upack, since it's in OR above):
f98a68af
          *   mov esi, value      |   pusha
          *   lodsd               |   call $+0x9
          *   push eax            |
          *
          * Upack 1.1/1.2 Beta produces [based on 2 samples (sUx) provided by aCaB]:
          * 2 sections
          *   mov esi, value
          *   loads
          *   mov edi, eax
          *
          * Upack unknown [sample 0297729]
          * 3 sections
          *   mov esi, value
          *   push [esi]
          *   jmp
6c03dc5d
          *
f98a68af
          */
         /* upack 0.39-3s + sample 0151477*/
afe940da
         while (((upack && peinfo->nsections == 3) && /* 3 sections */
288057e9
                 ((
afe940da
                      epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) > peinfo->min && /* mov esi */
                      epbuff[5] == '\xad' && epbuff[6] == '\x50'                                                               /* lodsd; push eax */
288057e9
                      ) ||
                  /* based on 0297729 sample from aCaB */
afe940da
                  (epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) > peinfo->min && /* mov esi */
                   epbuff[5] == '\xff' && epbuff[6] == '\x36'                                                               /* push [esi] */
288057e9
                   ))) ||
afe940da
                ((!upack && peinfo->nsections == 2) &&                                            /* 2 sections */
288057e9
                 ((                                                                               /* upack 0.39-2s */
                   epbuff[0] == '\x60' && epbuff[1] == '\xe8' && cli_readint32(epbuff + 2) == 0x9 /* pusha; call+9 */
                   ) ||
afe940da
                  (                                                                                                         /* upack 1.1/1.2, based on 2 samples */
                   epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) < peinfo->min && /* mov esi */
                   cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) > 0 &&
288057e9
                   epbuff[5] == '\xad' && epbuff[6] == '\x8b' && epbuff[7] == '\xf8' /* loads;  mov edi, eax */
                   )))) {
f98a68af
             uint32_t vma, off;
288057e9
             int a, b, c;
f98a68af
 
afe940da
             cli_dbgmsg("cli_scanpe: Upack characteristics found.\n");
             a = peinfo->sections[0].vsz;
             b = peinfo->sections[1].vsz;
f98a68af
             if (upack) {
afe940da
                 cli_dbgmsg("cli_scanpe: Upack: var set\n");
f98a68af
 
afe940da
                 c     = peinfo->sections[2].vsz;
                 ssize = peinfo->sections[0].ursz + peinfo->sections[0].uraw;
                 off   = peinfo->sections[0].rva;
                 vma   = EC32(peinfo->pe_opt.opt32.ImageBase) + peinfo->sections[0].rva;
f98a68af
             } else {
afe940da
                 cli_dbgmsg("cli_scanpe: Upack: var NOT set\n");
                 c     = peinfo->sections[1].rva;
                 ssize = peinfo->sections[1].uraw;
288057e9
                 off   = 0;
afe940da
                 vma   = peinfo->sections[1].rva - peinfo->sections[1].uraw;
f98a68af
             }
e8042398
 
288057e9
             dsize = a + b + c;
e8042398
 
afe940da
             CLI_UNPSIZELIMITS("cli_scanpe: Upack", MAX(MAX(dsize, ssize), peinfo->sections[1].ursz));
e8042398
 
afe940da
             if (!CLI_ISCONTAINED(0, dsize, peinfo->sections[1].rva - off, peinfo->sections[1].ursz) || (upack && !CLI_ISCONTAINED(0, dsize, peinfo->sections[2].rva - peinfo->sections[0].rva, ssize)) || ssize > dsize) {
                 cli_dbgmsg("cli_scanpe: Upack: probably malformed pe-header, skipping to next unpacker\n");
f98a68af
                 break;
             }
288057e9
 
             if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
afe940da
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
e8042398
 
6c03dc5d
             if (fmap_readn(map, dest, 0, ssize) != ssize) {
afe940da
                 cli_dbgmsg("cli_scanpe: Upack: Can't read raw data of section 0\n");
f98a68af
                 free(dest);
                 break;
             }
e8042398
 
288057e9
             if (upack)
afe940da
                 memmove(dest + peinfo->sections[2].rva - peinfo->sections[0].rva, dest, ssize);
9a25caf3
 
6c03dc5d
             if (fmap_readn(map, dest + peinfo->sections[1].rva - off, peinfo->sections[1].uraw, peinfo->sections[1].ursz) != peinfo->sections[1].ursz) {
afe940da
                 cli_dbgmsg("cli_scanpe: Upack: Can't read raw data of section 1\n");
f98a68af
                 free(dest);
                 break;
             }
342e27a5
 
172c4dd2
 #if HAVE_JSON
fcff42e9
             if (pe_json != NULL)
                 cli_jsonstr(pe_json, "Packer", "Upack");
172c4dd2
 #endif
 
afe940da
             CLI_UNPTEMP("cli_scanpe: Upack", (dest, 0));
             CLI_UNPRESULTS("cli_scanpe: Upack", (unupack(upack, dest, dsize, epbuff, vma, peinfo->ep, EC32(peinfo->pe_opt.opt32.ImageBase), peinfo->sections[0].rva, ndesc)), 1, (dest, 0));
342e27a5
 
f98a68af
             break;
         }
     }
288057e9
 
     while (found && (DCONF & PE_CONF_FSG) && epbuff[0] == '\x87' && epbuff[1] == '\x25') {
f98a68af
         const char *dst;
         uint32_t newesi, newedi, newebx, newedx;
03a2d04a
 
f98a68af
         /* FSG v2.0 support - thanks to aCaB ! */
288057e9
 
afe940da
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz;
03a2d04a
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
5f1a932b
 
288057e9
         if (ssize <= 0x19 || dsize <= ssize) {
afe940da
             cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
288057e9
 
afe940da
         newedx = cli_readint32(epbuff + 2) - EC32(peinfo->pe_opt.opt32.ImageBase);
         if (!CLI_ISCONTAINED(peinfo->sections[i + 1].rva, peinfo->sections[i + 1].rsz, newedx, 4)) {
             cli_dbgmsg("cli_scanpe: FSG: xchg out of bounds (%x), giving up\n", newedx);
f98a68af
             break;
         }
288057e9
 
afe940da
         if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
             cli_dbgmsg("cli_scanpe: Can't read raw data of section %d\n", i + 1);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_ESEEK;
         }
5f1a932b
 
afe940da
         dst = src + newedx - peinfo->sections[i + 1].rva;
         if (newedx < peinfo->sections[i + 1].rva || !CLI_ISCONTAINED(src, ssize, dst, 4)) {
             cli_dbgmsg("cli_scanpe: FSG: New ESP out of bounds\n");
f98a68af
             break;
         }
5f1a932b
 
afe940da
         newedx = cli_readint32(dst) - EC32(peinfo->pe_opt.opt32.ImageBase);
         if (!CLI_ISCONTAINED(peinfo->sections[i + 1].rva, peinfo->sections[i + 1].rsz, newedx, 4)) {
             cli_dbgmsg("cli_scanpe: FSG: New ESP (%x) is wrong\n", newedx);
f98a68af
             break;
         }
288057e9
 
afe940da
         dst = src + newedx - peinfo->sections[i + 1].rva;
288057e9
         if (!CLI_ISCONTAINED(src, ssize, dst, 32)) {
afe940da
             cli_dbgmsg("cli_scanpe: FSG: New stack out of bounds\n");
f98a68af
             break;
         }
5f1a932b
 
afe940da
         newedi = cli_readint32(dst) - EC32(peinfo->pe_opt.opt32.ImageBase);
         newesi = cli_readint32(dst + 4) - EC32(peinfo->pe_opt.opt32.ImageBase);
         newebx = cli_readint32(dst + 16) - EC32(peinfo->pe_opt.opt32.ImageBase);
f98a68af
         newedx = cli_readint32(dst + 20);
5f1a932b
 
afe940da
         if (newedi != peinfo->sections[i].rva) {
             cli_dbgmsg("cli_scanpe: FSG: Bad destination buffer (edi is %x should be %x)\n", newedi, peinfo->sections[i].rva);
f98a68af
             break;
         }
5f1a932b
 
afe940da
         if (newesi < peinfo->sections[i + 1].rva || newesi - peinfo->sections[i + 1].rva >= peinfo->sections[i + 1].rsz) {
             cli_dbgmsg("cli_scanpe: FSG: Source buffer out of section bounds\n");
f98a68af
             break;
         }
5f1a932b
 
afe940da
         if (!CLI_ISCONTAINED(peinfo->sections[i + 1].rva, peinfo->sections[i + 1].rsz, newebx, 16)) {
             cli_dbgmsg("cli_scanpe: FSG: Array of functions out of bounds\n");
f98a68af
             break;
         }
5f1a932b
 
afe940da
         newedx = cli_readint32(newebx + 12 - peinfo->sections[i + 1].rva + src) - EC32(peinfo->pe_opt.opt32.ImageBase);
         cli_dbgmsg("cli_scanpe: FSG: found old EP @%x\n", newedx);
5f1a932b
 
288057e9
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
5f1a932b
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "FSG");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: FSG", (dest, 0));
         CLI_UNPRESULTSFSG2("cli_scanpe: FSG", (unfsg_200(newesi - peinfo->sections[i + 1].rva + src, dest, ssize + peinfo->sections[i + 1].rva - newesi, dsize, newedi, EC32(peinfo->pe_opt.opt32.ImageBase), newedx, ndesc)), 1, (dest, 0));
f98a68af
         break;
95e31dc7
     }
e0264472
 
afe940da
     while (found && (DCONF & PE_CONF_FSG) && epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) < peinfo->min) {
f98a68af
         int sectcnt = 0;
         const char *support;
         uint32_t newesi, newedi, oldep, gp, t;
         struct cli_exe_section *sections;
5f1a932b
 
f98a68af
         /* FSG support - v. 1.33 (thx trog for the many samples) */
03a2d04a
 
afe940da
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz;
5eb34fac
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
5eb34fac
 
288057e9
         if (ssize <= 0x19 || dsize <= ssize) {
afe940da
             cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
5eb34fac
 
afe940da
         if (!(t = cli_rawaddr(cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase), NULL, 0, &err, fsize, peinfo->hdr_size)) && err) {
             cli_dbgmsg("cli_scanpe: FSG: Support data out of padding area\n");
f98a68af
             break;
         }
5eb34fac
 
afe940da
         gp = peinfo->sections[i + 1].raw - t;
5eb34fac
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", gp);
5eb34fac
 
288057e9
         if (!(support = fmap_need_off_once(map, t, gp))) {
afe940da
             cli_dbgmsg("cli_scanpe: Can't read %d bytes from padding area\n", gp);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
f628a181
 
afe940da
         /* newebx = cli_readint32(support) - EC32(peinfo->pe_opt.opt32.ImageBase);  Unused */
         newedi = cli_readint32(support + 4) - EC32(peinfo->pe_opt.opt32.ImageBase); /* 1st dest */
         newesi = cli_readint32(support + 8) - EC32(peinfo->pe_opt.opt32.ImageBase); /* Source */
5eb34fac
 
afe940da
         if (newesi < peinfo->sections[i + 1].rva || newesi - peinfo->sections[i + 1].rva >= peinfo->sections[i + 1].rsz) {
             cli_dbgmsg("cli_scanpe: FSG: Source buffer out of section bounds\n");
f98a68af
             break;
         }
5eb34fac
 
afe940da
         if (newedi != peinfo->sections[i].rva) {
             cli_dbgmsg("cli_scanpe: FSG: Bad destination (is %x should be %x)\n", newedi, peinfo->sections[i].rva);
f98a68af
             break;
         }
5eb34fac
 
f98a68af
         /* Counting original sections */
288057e9
         for (t = 12; t < gp - 4; t += 4) {
             uint32_t rva = cli_readint32(support + t);
5eb34fac
 
288057e9
             if (!rva)
f98a68af
                 break;
5eb34fac
 
afe940da
             rva -= EC32(peinfo->pe_opt.opt32.ImageBase) + 1;
f98a68af
             sectcnt++;
5eb34fac
 
288057e9
             if (rva % 0x1000)
afe940da
                 cli_dbgmsg("cli_scanpe: FSG: Original section %d is misaligned\n", sectcnt);
5eb34fac
 
afe940da
             if (rva < peinfo->sections[i].rva || rva - peinfo->sections[i].rva >= peinfo->sections[i].vsz) {
                 cli_dbgmsg("cli_scanpe: FSG: Original section %d is out of bounds\n", sectcnt);
f98a68af
                 break;
             }
         }
5eb34fac
 
288057e9
         if (t >= gp - 4 || cli_readint32(support + t)) {
f98a68af
             break;
         }
5eb34fac
 
288057e9
         if ((sections = (struct cli_exe_section *)cli_malloc((sectcnt + 1) * sizeof(struct cli_exe_section))) == NULL) {
afe940da
             cli_errmsg("cli_scanpe: FSG: Unable to allocate memory for sections %llu\n", (long long unsigned)((sectcnt + 1) * sizeof(struct cli_exe_section)));
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
5eb34fac
 
f98a68af
         sections[0].rva = newedi;
288057e9
         for (t = 1; t <= (uint32_t)sectcnt; t++)
afe940da
             sections[t].rva = cli_readint32(support + 8 + t * 4) - 1 - EC32(peinfo->pe_opt.opt32.ImageBase);
5eb34fac
 
afe940da
         if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
             cli_dbgmsg("cli_scanpe: Can't read raw data of section %d\n", i);
             cli_exe_info_destroy(peinfo);
f98a68af
             free(sections);
             return CL_EREAD;
         }
5eb34fac
 
288057e9
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             free(sections);
             return CL_EMEM;
         }
5eb34fac
 
afe940da
         oldep = peinfo->vep + 161 + 6 + cli_readint32(epbuff + 163);
         cli_dbgmsg("cli_scanpe: FSG: found old EP @%x\n", oldep);
5eb34fac
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "FSG");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: FSG", (dest, sections, 0));
         CLI_UNPRESULTSFSG1("cli_scanpe: FSG", (unfsg_133(src + newesi - peinfo->sections[i + 1].rva, dest, ssize + peinfo->sections[i + 1].rva - newesi, dsize, sections, sectcnt, EC32(peinfo->pe_opt.opt32.ImageBase), oldep, ndesc)), 1, (dest, sections, 0));
f98a68af
         break; /* were done with 1.33 */
95e31dc7
     }
5eb34fac
 
afe940da
     while (found && (DCONF & PE_CONF_FSG) && epbuff[0] == '\xbb' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) < peinfo->min && epbuff[5] == '\xbf' && epbuff[10] == '\xbe' && peinfo->vep >= peinfo->sections[i + 1].rva && peinfo->vep - peinfo->sections[i + 1].rva > peinfo->sections[i + 1].rva - 0xe0) {
f98a68af
         int sectcnt = 0;
afe940da
         uint32_t gp, t = cli_rawaddr(cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase), NULL, 0, &err, fsize, peinfo->hdr_size);
f98a68af
         const char *support;
afe940da
         uint32_t newesi = cli_readint32(epbuff + 11) - EC32(peinfo->pe_opt.opt32.ImageBase);
         uint32_t newedi = cli_readint32(epbuff + 6) - EC32(peinfo->pe_opt.opt32.ImageBase);
         uint32_t oldep  = peinfo->vep - peinfo->sections[i + 1].rva;
f98a68af
         struct cli_exe_section *sections;
5eb34fac
 
f98a68af
         /* FSG support - v. 1.31 */
5eb34fac
 
afe940da
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz;
5eb34fac
 
288057e9
         if (err) {
afe940da
             cli_dbgmsg("cli_scanpe: FSG: Support data out of padding area\n");
f98a68af
             break;
         }
5eb34fac
 
afe940da
         if (newesi < peinfo->sections[i + 1].rva || newesi - peinfo->sections[i + 1].rva >= peinfo->sections[i + 1].raw) {
             cli_dbgmsg("cli_scanpe: FSG: Source buffer out of section bounds\n");
f98a68af
             break;
         }
f628a181
 
afe940da
         if (newedi != peinfo->sections[i].rva) {
             cli_dbgmsg("cli_scanpe: FSG: Bad destination (is %x should be %x)\n", newedi, peinfo->sections[i].rva);
f98a68af
             break;
         }
f628a181
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
f628a181
 
288057e9
         if (ssize <= 0x19 || dsize <= ssize) {
afe940da
             cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
f628a181
 
afe940da
         gp = peinfo->sections[i + 1].raw - t;
f628a181
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", gp)
f628a181
 
288057e9
         if (!(support = fmap_need_off_once(map, t, gp))) {
afe940da
             cli_dbgmsg("cli_scanpe: Can't read %d bytes from padding area\n", gp);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
f628a181
 
f98a68af
         /* Counting original sections */
288057e9
         for (t = 0; t < gp - 2; t += 2) {
             uint32_t rva = support[t] | (support[t + 1] << 8);
f628a181
 
f98a68af
             if (rva == 2 || rva == 1)
                 break;
f628a181
 
afe940da
             rva = ((rva - 2) << 12) - EC32(peinfo->pe_opt.opt32.ImageBase);
f98a68af
             sectcnt++;
f628a181
 
afe940da
             if (rva < peinfo->sections[i].rva || rva - peinfo->sections[i].rva >= peinfo->sections[i].vsz) {
                 cli_dbgmsg("cli_scanpe: FSG: Original section %d is out of bounds\n", sectcnt);
f98a68af
                 break;
             }
         }
f628a181
 
288057e9
         if (t >= gp - 10 || cli_readint32(support + t + 6) != 2)
f98a68af
             break;
f628a181
 
288057e9
         if ((sections = (struct cli_exe_section *)cli_malloc((sectcnt + 1) * sizeof(struct cli_exe_section))) == NULL) {
afe940da
             cli_errmsg("cli_scanpe: FSG: Unable to allocate memory for sections %llu\n", (long long unsigned)((sectcnt + 1) * sizeof(struct cli_exe_section)));
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
f628a181
 
f98a68af
         sections[0].rva = newedi;
288057e9
         for (t = 0; t <= (uint32_t)sectcnt - 1; t++)
afe940da
             sections[t + 1].rva = (((support[t * 2] | (support[t * 2 + 1] << 8)) - 2) << 12) - EC32(peinfo->pe_opt.opt32.ImageBase);
f628a181
 
afe940da
         if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
             cli_dbgmsg("cli_scanpe: FSG: Can't read raw data of section %d\n", i);
             cli_exe_info_destroy(peinfo);
f98a68af
             free(sections);
             return CL_EREAD;
         }
f628a181
 
288057e9
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             free(sections);
             return CL_EMEM;
         }
f628a181
 
288057e9
         gp    = 0xda + 6 * (epbuff[16] == '\xe8');
afe940da
         oldep = peinfo->vep + gp + 6 + cli_readint32(src + gp + 2 + oldep);
         cli_dbgmsg("cli_scanpe: FSG: found old EP @%x\n", oldep);
f628a181
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "FSG");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: FSG", (dest, sections, 0));
         CLI_UNPRESULTSFSG1("cli_scanpe: FSG", (unfsg_133(src + newesi - peinfo->sections[i + 1].rva, dest, ssize + peinfo->sections[i + 1].rva - newesi, dsize, sections, sectcnt, EC32(peinfo->pe_opt.opt32.ImageBase), oldep, ndesc)), 1, (dest, sections, 0));
f98a68af
 
         break; /* were done with 1.31 */
95e31dc7
     }
f628a181
 
288057e9
     if (found && (DCONF & PE_CONF_UPX)) {
afe940da
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz + peinfo->sections[i + 1].vsz;
e0264472
 
6c03dc5d
         /*
f98a68af
          * UPX support
          * we assume (i + 1) is UPX1
          */
f628a181
 
288057e9
         /* cli_dbgmsg("UPX: ssize %u dsize %u\n", ssize, dsize); */
f628a181
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: UPX", MAX(dsize, ssize));
10882c9f
 
288057e9
         if (ssize <= 0x19 || dsize <= ssize || dsize > CLI_MAX_ALLOCATION) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX: Size mismatch or dsize too big (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
f628a181
 
afe940da
         if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
             cli_dbgmsg("cli_scanpe: UPX: Can't read raw data of section %d\n", i + 1);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
f628a181
 
288057e9
         if ((dest = (char *)cli_calloc(dsize + 8192, sizeof(char))) == NULL) {
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
f628a181
 
f98a68af
         /* try to detect UPX code */
288057e9
         if (cli_memstr(UPX_NRV2B, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2B, 24, epbuff + 0x69 + 8, 13)) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2B decompression routine\n");
f98a68af
             upxfn = upx_inflate2b;
288057e9
         } else if (cli_memstr(UPX_NRV2D, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2D, 24, epbuff + 0x69 + 8, 13)) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2D decompression routine\n");
f98a68af
             upxfn = upx_inflate2d;
288057e9
         } else if (cli_memstr(UPX_NRV2E, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2E, 24, epbuff + 0x69 + 8, 13)) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2E decompression routine\n");
f98a68af
             upxfn = upx_inflate2e;
         }
e2ff1490
 
288057e9
         if (upxfn) {
afe940da
             int skew = cli_readint32(epbuff + 2) - EC32(peinfo->pe_opt.opt32.ImageBase) - peinfo->sections[i + 1].rva;
f98a68af
 
288057e9
             if (epbuff[1] != '\xbe' || skew <= 0 || skew > 0xfff) {
f98a68af
                 /* FIXME: legit skews?? */
288057e9
                 skew = 0;
f98a68af
             } else if ((unsigned int)skew > ssize) {
                 /* Ignore suggested skew larger than section size */
                 skew = 0;
             } else {
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: UPX1 seems skewed by %d bytes\n", skew);
f98a68af
             }
5f1a932b
 
f98a68af
             /* Try skewed first (skew may be zero) */
afe940da
             if (upxfn(src + skew, ssize - skew, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep - skew) >= 0) {
f98a68af
                 upx_success = 1;
             }
             /* If skew not successful and non-zero, try no skew */
afe940da
             else if (skew && (upxfn(src, ssize, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep) >= 0)) {
f98a68af
                 upx_success = 1;
             }
5f1a932b
 
288057e9
             if (upx_success)
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed\n");
f98a68af
             else
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: Preferred decompressor failed\n");
f98a68af
         }
e37613ad
 
288057e9
         if (!upx_success && upxfn != upx_inflate2b) {
afe940da
             if (upx_inflate2b(src, ssize, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep) == -1 && upx_inflate2b(src + 0x15, ssize - 0x15, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep - 0x15) == -1) {
5f1a932b
 
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: NRV2B decompressor failed\n");
f98a68af
             } else {
                 upx_success = 1;
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2B\n");
f98a68af
             }
         }
5f1a932b
 
288057e9
         if (!upx_success && upxfn != upx_inflate2d) {
afe940da
             if (upx_inflate2d(src, ssize, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep) == -1 && upx_inflate2d(src + 0x15, ssize - 0x15, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep - 0x15) == -1) {
4a24fe30
 
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: NRV2D decompressor failed\n");
f98a68af
             } else {
                 upx_success = 1;
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2D\n");
f98a68af
             }
         }
5f1a932b
 
288057e9
         if (!upx_success && upxfn != upx_inflate2e) {
afe940da
             if (upx_inflate2e(src, ssize, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep) == -1 && upx_inflate2e(src + 0x15, ssize - 0x15, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep - 0x15) == -1) {
                 cli_dbgmsg("cli_scanpe: UPX: NRV2E decompressor failed\n");
f98a68af
             } else {
                 upx_success = 1;
afe940da
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2E\n");
f98a68af
             }
         }
4a24fe30
 
288057e9
         if (cli_memstr(UPX_LZMA2, 20, epbuff + 0x2f, 20)) {
             uint32_t strictdsize = cli_readint32(epbuff + 0x21), skew = 0;
             if (ssize > 0x15 && epbuff[0] == '\x60' && epbuff[1] == '\xbe') {
afe940da
                 // TODO Add EC32
                 skew = cli_readint32(epbuff + 2) - peinfo->sections[i + 1].rva - peinfo->pe_opt.opt32.ImageBase;
288057e9
                 if (skew != 0x15)
f98a68af
                     skew = 0;
             }
5f1a932b
 
288057e9
             if (strictdsize <= dsize)
afe940da
                 upx_success = upx_inflatelzma(src + skew, ssize - skew, dest, &strictdsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep, 0x20003) >= 0;
1c6bead7
         } else if (cli_memstr(UPX_LZMA1_FIRST, 8, epbuff + 0x39, 8) && cli_memstr(UPX_LZMA1_SECOND, 8, epbuff + 0x45, 8)) {
288057e9
             uint32_t strictdsize = cli_readint32(epbuff + 0x2b), skew = 0;
             uint32_t properties = cli_readint32(epbuff + 0x41);
             if (ssize > 0x15 && epbuff[0] == '\x60' && epbuff[1] == '\xbe') {
afe940da
                 // TODO Add EC32
                 skew = cli_readint32(epbuff + 2) - peinfo->sections[i + 1].rva - peinfo->pe_opt.opt32.ImageBase;
288057e9
                 if (skew != 0x15)
f98a68af
                     skew = 0;
             }
342e27a5
 
288057e9
             if (strictdsize <= dsize)
afe940da
                 upx_success = upx_inflatelzma(src + skew, ssize - skew, dest, &strictdsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep, properties) >= 0;
f98a68af
         }
f2b223fe
 
288057e9
         if (!upx_success) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX: All decompressors failed\n");
f98a68af
             free(dest);
         }
95e31dc7
     }
e0264472
 
288057e9
     if (upx_success) {
afe940da
         cli_exe_info_destroy(peinfo);
342e27a5
 
afe940da
         CLI_UNPTEMP("cli_scanpe: UPX/FSG", (dest, 0));
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "UPX");
172c4dd2
 #endif
4a24fe30
 
288057e9
         if ((unsigned int)write(ndesc, dest, dsize) != dsize) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX/FSG: Can't write %d bytes\n", dsize);
f98a68af
             free(tempfile);
             free(dest);
             close(ndesc);
             return CL_EWRITE;
         }
 
         free(dest);
         if (lseek(ndesc, 0, SEEK_SET) == -1) {
afe940da
             cli_dbgmsg("cli_scanpe: UPX/FSG: lseek() failed\n");
f98a68af
             close(ndesc);
afe940da
             SHA_RESET;
f98a68af
             CLI_TMPUNLK();
             free(tempfile);
             return CL_ESEEK;
         }
4a24fe30
 
288057e9
         if (ctx->engine->keeptmp)
afe940da
             cli_dbgmsg("cli_scanpe: UPX/FSG: Decompressed data saved in %s\n", tempfile);
f98a68af
 
         cli_dbgmsg("***** Scanning decompressed file *****\n");
         SHA_OFF;
288057e9
         if ((ret = cli_magic_scandesc(ndesc, tempfile, ctx)) == CL_VIRUS) {
f98a68af
             close(ndesc);
afe940da
             SHA_RESET;
f98a68af
             CLI_TMPUNLK();
             free(tempfile);
             return CL_VIRUS;
         }
 
         SHA_RESET;
96914546
         close(ndesc);
         CLI_TMPUNLK();
         free(tempfile);
f98a68af
         return ret;
342e27a5
     }
ac75a532
 
95e31dc7
     /* Petite */
85dd8460
 
288057e9
     if (epsize < 200) {
afe940da
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
20c3d44d
     }
85dd8460
 
95e31dc7
     found = 2;
 
afe940da
     if (epbuff[0] != '\xb8' || (uint32_t)cli_readint32(epbuff + 1) != peinfo->sections[peinfo->nsections - 1].rva + EC32(peinfo->pe_opt.opt32.ImageBase)) {
         if (peinfo->nsections < 2 || epbuff[0] != '\xb8' || (uint32_t)cli_readint32(epbuff + 1) != peinfo->sections[peinfo->nsections - 2].rva + EC32(peinfo->pe_opt.opt32.ImageBase))
f98a68af
             found = 0;
         else
             found = 1;
85dd8460
     }
 
288057e9
     if (found && (DCONF & PE_CONF_PETITE)) {
afe940da
         cli_dbgmsg("cli_scanpe: Petite: v2.%d compression detected\n", found);
85dd8460
 
288057e9
         if (cli_readint32(epbuff + 0x80) == 0x163c988d) {
afe940da
             cli_dbgmsg("cli_scanpe: Petite: level zero compression is not supported yet\n");
f98a68af
         } else {
afe940da
             dsize = peinfo->max - peinfo->min;
85dd8460
 
afe940da
             CLI_UNPSIZELIMITS("cli_scanpe: Petite", dsize);
85dd8460
 
288057e9
             if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
afe940da
                 cli_dbgmsg("cli_scanpe: Petite: Can't allocate %d bytes\n", dsize);
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
85dd8460
 
afe940da
             for (i = 0; i < peinfo->nsections; i++) {
                 if (peinfo->sections[i].raw) {
288057e9
                     unsigned int r_ret;
8aeedf3c
 
afe940da
                     if (!peinfo->sections[i].rsz)
288057e9
                         goto out_no_petite;
8aeedf3c
 
288057e9
                     if (!CLI_ISCONTAINED(dest, dsize,
afe940da
                                          dest + peinfo->sections[i].rva - peinfo->min,
                                          peinfo->sections[i].ursz))
288057e9
                         goto out_no_petite;
8aeedf3c
 
afe940da
                     r_ret = fmap_readn(map, dest + peinfo->sections[i].rva - peinfo->min,
                                        peinfo->sections[i].raw,
                                        peinfo->sections[i].ursz);
                     if (r_ret != peinfo->sections[i].ursz) {
288057e9
                     out_no_petite:
afe940da
                         cli_exe_info_destroy(peinfo);
f98a68af
                         free(dest);
                         return CL_CLEAN;
                     }
                 }
             }
85dd8460
 
172c4dd2
 #if HAVE_JSON
fcff42e9
             if (pe_json != NULL)
                 cli_jsonstr(pe_json, "Packer", "Petite");
172c4dd2
 #endif
 
afe940da
             CLI_UNPTEMP("cli_scanpe: Petite", (dest, 0));
             CLI_UNPRESULTS("Petite", (petite_inflate2x_1to9(dest, peinfo->min, peinfo->max - peinfo->min, peinfo->sections, peinfo->nsections - (found == 1 ? 1 : 0), EC32(peinfo->pe_opt.opt32.ImageBase), peinfo->vep, ndesc, found, EC32(peinfo->dirs[2].VirtualAddress), EC32(peinfo->dirs[2].Size))), 0, (dest, 0));
f98a68af
         }
85dd8460
     }
 
c2dfe70e
     /* PESpin 1.1 */
 
afe940da
     if ((DCONF & PE_CONF_PESPIN) && peinfo->nsections > 1 &&
         peinfo->vep >= peinfo->sections[peinfo->nsections - 1].rva &&
         0x3217 - 4 <= peinfo->sections[peinfo->nsections - 1].rva + peinfo->sections[peinfo->nsections - 1].rsz &&
         peinfo->vep < peinfo->sections[peinfo->nsections - 1].rva + peinfo->sections[peinfo->nsections - 1].rsz - 0x3217 - 4 &&
288057e9
         memcmp(epbuff + 4, "\xe8\x00\x00\x00\x00\x8b\x1c\x24\x83\xc3", 10) == 0) {
c2dfe70e
 
f98a68af
         char *spinned;
81030038
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: PEspin", fsize);
c2dfe70e
 
288057e9
         if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
afe940da
             cli_errmsg("cli_scanpe: PESping: Unable to allocate memory for spinned %lu\n", (unsigned long)fsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
c2dfe70e
 
6c03dc5d
         if (fmap_readn(map, spinned, 0, fsize) != fsize) {
afe940da
             cli_dbgmsg("cli_scanpe: PESpin: Can't read %lu bytes\n", (unsigned long)fsize);
f98a68af
             free(spinned);
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
c2dfe70e
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "PEspin");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: PESpin", (spinned, 0));
         CLI_UNPRESULTS_("cli_scanpe: PEspin", SPINCASE(), (unspin(spinned, fsize, peinfo->sections, peinfo->nsections - 1, peinfo->vep, ndesc, ctx)), 0, (spinned, 0));
c2dfe70e
     }
de800f2a
 
d0b31fa3
     /* yC 1.3 & variants */
afe940da
     if ((DCONF & PE_CONF_YC) && peinfo->nsections > 1 &&
         (EC32(peinfo->pe_opt.opt32.AddressOfEntryPoint) == peinfo->sections[peinfo->nsections - 1].rva + 0x60)) {
d0b31fa3
 
f98a68af
         uint32_t ecx = 0;
         int16_t offset;
d0b31fa3
 
f98a68af
         /* yC 1.3 */
         if (!memcmp(epbuff, "\x55\x8B\xEC\x53\x56\x57\x60\xE8\x00\x00\x00\x00\x5D\x81\xED", 15) &&
288057e9
             !memcmp(epbuff + 0x26, "\x8D\x3A\x8B\xF7\x33\xC0\xEB\x04\x90\xEB\x01\xC2\xAC", 13) &&
f98a68af
             ((uint8_t)epbuff[0x13] == 0xB9) &&
288057e9
             ((uint16_t)(cli_readint16(epbuff + 0x18)) == 0xE981) &&
             !memcmp(epbuff + 0x1e, "\x8B\xD5\x81\xC2", 4)) {
d0b31fa3
 
f98a68af
             offset = 0;
288057e9
             if (0x6c - cli_readint32(epbuff + 0xf) + cli_readint32(epbuff + 0x22) == 0xC6)
                 ecx = cli_readint32(epbuff + 0x14) - cli_readint32(epbuff + 0x1a);
f98a68af
         }
d0b31fa3
 
f98a68af
         /* yC 1.3 variant */
         if (!ecx && !memcmp(epbuff, "\x55\x8B\xEC\x83\xEC\x40\x53\x56\x57", 9) &&
288057e9
             !memcmp(epbuff + 0x17, "\xe8\x00\x00\x00\x00\x5d\x81\xed", 8) &&
f98a68af
             ((uint8_t)epbuff[0x23] == 0xB9)) {
d0b31fa3
 
f98a68af
             offset = 0x10;
288057e9
             if (0x6c - cli_readint32(epbuff + 0x1f) + cli_readint32(epbuff + 0x32) == 0xC6)
                 ecx = cli_readint32(epbuff + 0x24) - cli_readint32(epbuff + 0x2a);
f98a68af
         }
d0b31fa3
 
f98a68af
         /* yC 1.x/modified */
288057e9
         if (!ecx && !memcmp(epbuff, "\x60\xe8\x00\x00\x00\x00\x5d\x81\xed", 9) &&
f98a68af
             ((uint8_t)epbuff[0xd] == 0xb9) &&
288057e9
             ((uint16_t)cli_readint16(epbuff + 0x12) == 0xbd8d) &&
             !memcmp(epbuff + 0x18, "\x8b\xf7\xac", 3)) {
d0b31fa3
 
f98a68af
             offset = -0x18;
288057e9
             if (0x66 - cli_readint32(epbuff + 0x9) + cli_readint32(epbuff + 0x14) == 0xae)
                 ecx = cli_readint32(epbuff + 0xe);
f98a68af
         }
d0b31fa3
 
f98a68af
         if (ecx > 0x800 && ecx < 0x2000 &&
288057e9
             !memcmp(epbuff + 0x63 + offset, "\xaa\xe2\xcc", 3) &&
afe940da
             (fsize >= peinfo->sections[peinfo->nsections - 1].raw + 0xC6 + ecx + offset)) {
822930fc
 
f98a68af
             char *spinned;
822930fc
 
288057e9
             if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
afe940da
                 cli_errmsg("cli_scanpe: yC: Unable to allocate memory for spinned %lu\n", (unsigned long)fsize);
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
822930fc
 
6c03dc5d
             if (fmap_readn(map, spinned, 0, fsize) != fsize) {
afe940da
                 cli_dbgmsg("cli_scanpe: yC: Can't read %lu bytes\n", (unsigned long)fsize);
f98a68af
                 free(spinned);
afe940da
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EREAD;
             }
95e31dc7
 
172c4dd2
 #if HAVE_JSON
fcff42e9
             if (pe_json != NULL)
                 cli_jsonstr(pe_json, "Packer", "yC");
172c4dd2
 #endif
 
771c2309
             do {
                 unsigned int yc_unp_num_viruses = ctx->num_viruses;
288057e9
                 const char *yc_unp_virname      = NULL;
771c2309
 
                 if (ctx->virname)
                     yc_unp_virname = ctx->virname[0];
 
afe940da
                 cli_dbgmsg("%d,%d,%d,%d\n", peinfo->nsections - 1, peinfo->e_lfanew, ecx, offset);
                 CLI_UNPTEMP("cli_scanpe: yC", (spinned, 0));
                 CLI_UNPRESULTS("cli_scanpe: yC", (yc_decrypt(ctx, spinned, fsize, peinfo->sections, peinfo->nsections - 1, peinfo->e_lfanew, ndesc, ecx, offset)), 0, (spinned, 0));
771c2309
 
048a88e6
                 if (SCAN_ALLMATCHES && yc_unp_num_viruses != ctx->num_viruses) {
afe940da
                     cli_exe_info_destroy(peinfo);
771c2309
                     return CL_VIRUS;
288057e9
                 } else if (ctx->virname && yc_unp_virname != ctx->virname[0]) {
afe940da
                     cli_exe_info_destroy(peinfo);
771c2309
                     return CL_VIRUS;
                 }
288057e9
             } while (0);
f98a68af
         }
822930fc
     }
 
60cd49c9
     /* WWPack */
 
afe940da
     while ((DCONF & PE_CONF_WWPACK) && peinfo->nsections > 1 &&
            peinfo->vep == peinfo->sections[peinfo->nsections - 1].rva &&
288057e9
            memcmp(epbuff, "\x53\x55\x8b\xe8\x33\xdb\xeb", 7) == 0 &&
            memcmp(epbuff + 0x68, "\xe8\x00\x00\x00\x00\x58\x2d\x6d\x00\x00\x00\x50\x60\x33\xc9\x50\x58\x50\x50", 19) == 0) {
afe940da
         uint32_t head = peinfo->sections[peinfo->nsections - 1].raw;
f377e052
         uint8_t *packer;
f98a68af
         char *src;
60cd49c9
 
f98a68af
         ssize = 0;
288057e9
         for (i = 0;; i++) {
afe940da
             if (peinfo->sections[i].raw < head)
                 head = peinfo->sections[i].raw;
f98a68af
 
afe940da
             if (i + 1 == peinfo->nsections)
f98a68af
                 break;
 
afe940da
             if (ssize < peinfo->sections[i].rva + peinfo->sections[i].vsz)
                 ssize = peinfo->sections[i].rva + peinfo->sections[i].vsz;
f98a68af
         }
 
288057e9
         if (!head || !ssize || head > ssize)
f98a68af
             break;
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: WWPack", ssize);
60cd49c9
 
288057e9
         if (!(src = (char *)cli_calloc(ssize, sizeof(char)))) {
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
 
6c03dc5d
         if (fmap_readn(map, src, 0, head) != head) {
afe940da
             cli_dbgmsg("cli_scanpe: WWPack: Can't read %d bytes from headers\n", head);
f98a68af
             free(src);
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
 
afe940da
         for (i = 0; i < (unsigned int)peinfo->nsections - 1; i++) {
             if (!peinfo->sections[i].rsz)
f98a68af
                 continue;
 
afe940da
             if (!CLI_ISCONTAINED(src, ssize, src + peinfo->sections[i].rva, peinfo->sections[i].rsz))
f98a68af
                 break;
 
6c03dc5d
             if (fmap_readn(map, src + peinfo->sections[i].rva, peinfo->sections[i].raw, peinfo->sections[i].rsz) != peinfo->sections[i].rsz)
f98a68af
                 break;
f377e052
         }
f98a68af
 
afe940da
         if (i + 1 != peinfo->nsections) {
             cli_dbgmsg("cli_scanpe: WWpack: Probably hacked/damaged file.\n");
f377e052
             free(src);
             break;
         }
f98a68af
 
afe940da
         if ((packer = (uint8_t *)cli_calloc(peinfo->sections[peinfo->nsections - 1].rsz, sizeof(char))) == NULL) {
f98a68af
             free(src);
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
 
6c03dc5d
         if (!peinfo->sections[peinfo->nsections - 1].rsz || fmap_readn(map, packer, peinfo->sections[peinfo->nsections - 1].raw, peinfo->sections[peinfo->nsections - 1].rsz) != peinfo->sections[peinfo->nsections - 1].rsz) {
afe940da
             cli_dbgmsg("cli_scanpe: WWPack: Can't read %d bytes from wwpack sect\n", peinfo->sections[peinfo->nsections - 1].rsz);
f98a68af
             free(src);
             free(packer);
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
60cd49c9
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "WWPack");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: WWPack", (src, packer, 0));
         CLI_UNPRESULTS("cli_scanpe: WWPack", (wwunpack((uint8_t *)src, ssize, packer, peinfo->sections, peinfo->nsections - 1, peinfo->e_lfanew, ndesc)), 0, (src, packer, 0));
f98a68af
         break;
60cd49c9
     }
 
2f73b977
     /* ASPACK support */
288057e9
     while ((DCONF & PE_CONF_ASPACK) &&
afe940da
            ((peinfo->ep + ASPACK_EP_OFFSET_212 < fsize) ||
             (peinfo->ep + ASPACK_EP_OFFSET_OTHER < fsize) ||
             (peinfo->ep + ASPACK_EP_OFFSET_242 < fsize)) &&
288057e9
            (!memcmp(epbuff, "\x60\xe8\x03\x00\x00\x00\xe9\xeb", 8))) {
f98a68af
         char *src;
86e29b67
         aspack_version_t aspack_ver = ASPACK_VER_NONE;
2f73b977
 
288057e9
         if (epsize < 0x3bf)
f98a68af
             break;
288057e9
 
         if (0 == memcmp(epbuff + ASPACK_EPBUFF_OFFSET_212, "\x68\x00\x00\x00\x00\xc3", 6)) {
86e29b67
             aspack_ver = ASPACK_VER_212;
288057e9
         } else if (0 == memcmp(epbuff + ASPACK_EPBUFF_OFFSET_OTHER, "\x68\x00\x00\x00\x00\xc3", 6)) {
86e29b67
             aspack_ver = ASPACK_VER_OTHER;
288057e9
         } else if (0 == memcmp(epbuff + ASPACK_EPBUFF_OFFSET_242, "\x68\x00\x00\x00\x00\xc3", 6)) {
86e29b67
             aspack_ver = ASPACK_VER_242;
         } else {
             break;
         }
f98a68af
         ssize = 0;
afe940da
         for (i = 0; i < peinfo->nsections; i++)
             if (ssize < peinfo->sections[i].rva + peinfo->sections[i].vsz)
                 ssize = peinfo->sections[i].rva + peinfo->sections[i].vsz;
f98a68af
 
288057e9
         if (!ssize)
f98a68af
             break;
95e31dc7
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: Aspack", ssize);
95e31dc7
 
288057e9
         if (!(src = (char *)cli_calloc(ssize, sizeof(char)))) {
afe940da
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
afe940da
         for (i = 0; i < (unsigned int)peinfo->nsections; i++) {
             if (!peinfo->sections[i].rsz)
f98a68af
                 continue;
 
afe940da
             if (!CLI_ISCONTAINED(src, ssize, src + peinfo->sections[i].rva, peinfo->sections[i].rsz))
f98a68af
                 break;
 
6c03dc5d
             if (fmap_readn(map, src + peinfo->sections[i].rva, peinfo->sections[i].raw, peinfo->sections[i].rsz) != peinfo->sections[i].rsz)
f98a68af
                 break;
2f73b977
         }
f98a68af
 
afe940da
         if (i != peinfo->nsections) {
             cli_dbgmsg("cli_scanpe: Aspack: Probably hacked/damaged Aspack file.\n");
2f73b977
             free(src);
             break;
         }
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "Aspack");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: Aspack", (src, 0));
         CLI_UNPRESULTS("cli_scanpe: Aspack", (unaspack((uint8_t *)src, ssize, peinfo->sections, peinfo->nsections, peinfo->vep - 1, EC32(peinfo->pe_opt.opt32.ImageBase), ndesc, aspack_ver)), 1, (src, 0));
f98a68af
         break;
2f73b977
     }
 
81030038
     /* NsPack */
 
bc93eda0
     while (DCONF & PE_CONF_NSPACK) {
afe940da
         uint32_t eprva = peinfo->vep;
         uint32_t start_of_stuff, rep = peinfo->ep;
f98a68af
         unsigned int nowinldr;
         const char *nbuff;
 
288057e9
         src = epbuff;
         if (*epbuff == '\xe9') { /* bitched headers */
afe940da
             eprva = cli_readint32(epbuff + 1) + peinfo->vep + 5;
             if (!(rep = cli_rawaddr(eprva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err)
f98a68af
                 break;
95e31dc7
 
f98a68af
             if (!(nbuff = fmap_need_off_once(map, rep, 24)))
                 break;
95e31dc7
 
f98a68af
             src = nbuff;
         }
95e31dc7
 
f98a68af
         if (memcmp(src, "\x9c\x60\xe8\x00\x00\x00\x00\x5d\xb8\x07\x00\x00\x00", 13))
             break;
81030038
 
288057e9
         nowinldr = 0x54 - cli_readint32(src + 17);
afe940da
         cli_dbgmsg("cli_scanpe: NsPack: Found *start_of_stuff @delta-%x\n", nowinldr);
81030038
 
288057e9
         if (!(nbuff = fmap_need_off_once(map, rep - nowinldr, 4)))
f98a68af
             break;
81030038
 
288057e9
         start_of_stuff = rep + cli_readint32(nbuff);
         if (!(nbuff = fmap_need_off_once(map, start_of_stuff, 20)))
f98a68af
             break;
81030038
 
f98a68af
         src = nbuff;
         if (!cli_readint32(nbuff)) {
288057e9
             start_of_stuff += 4; /* FIXME: more to do */
             src += 4;
f98a68af
         }
81030038
 
288057e9
         ssize = cli_readint32(src + 5) | 0xff;
         dsize = cli_readint32(src + 9);
f98a68af
 
afe940da
         CLI_UNPSIZELIMITS("cli_scanpe: NsPack", MAX(ssize, dsize));
f98a68af
 
afe940da
         if (!ssize || !dsize || dsize != peinfo->sections[0].vsz)
f98a68af
             break;
 
288057e9
         if (!(dest = cli_malloc(dsize))) {
afe940da
             cli_errmsg("cli_scanpe: NsPack: Unable to allocate memory for dest %u\n", dsize);
f98a68af
             break;
         }
         /* memset(dest, 0xfc, dsize); */
 
288057e9
         if (!(src = fmap_need_off(map, start_of_stuff, ssize))) {
f98a68af
             free(dest);
             break;
         }
         /* memset(src, 0x00, ssize); */
 
288057e9
         eprva += 0x27a;
afe940da
         if (!(rep = cli_rawaddr(eprva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err) {
288057e9
             free(dest);
             break;
f98a68af
         }
 
288057e9
         if (!(nbuff = fmap_need_off_once(map, rep, 5))) {
             free(dest);
             break;
f98a68af
         }
 
         fmap_unneed_off(map, start_of_stuff, ssize);
288057e9
         eprva = eprva + 5 + cli_readint32(nbuff + 1);
afe940da
         cli_dbgmsg("cli_scanpe: NsPack: OEP = %08x\n", eprva);
81030038
 
172c4dd2
 #if HAVE_JSON
fcff42e9
         if (pe_json != NULL)
             cli_jsonstr(pe_json, "Packer", "NsPack");
172c4dd2
 #endif
 
afe940da
         CLI_UNPTEMP("cli_scanpe: NsPack", (dest, 0));
         CLI_UNPRESULTS("cli_scanpe: NsPack", (unspack(src, dest, ctx, peinfo->sections[0].rva, EC32(peinfo->pe_opt.opt32.ImageBase), eprva, ndesc)), 0, (dest, 0));
f98a68af
         break;
81030038
     }
60cd49c9
 
6307ca15
     /* to be continued ... */
 
f5933780
     /* !!!!!!!!!!!!!!    PACKERS END HERE    !!!!!!!!!!!!!! */
     ctx->corrupted_input = corrupted_cur;
 
0df99607
     /* Bytecode BC_PE_UNPACKER hook */
ab636570
     bc_ctx = cli_bytecode_context_alloc();
     if (!bc_ctx) {
f98a68af
         cli_errmsg("cli_scanpe: can't allocate memory for bc_ctx\n");
         return CL_EMEM;
ab636570
     }
f98a68af
 
afe940da
     cli_bytecode_context_setpe(bc_ctx, &pedata, peinfo->sections);
3ae0a76d
     cli_bytecode_context_setctx(bc_ctx, ctx);
f98a68af
 
6ad45a29
     ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_UNPACKER, map);
3ae0a76d
     switch (ret) {
288057e9
         case CL_VIRUS:
afe940da
             cli_exe_info_destroy(peinfo);
288057e9
             cli_bytecode_context_destroy(bc_ctx);
afe940da
             // TODO Handle allmatch
288057e9
             return CL_VIRUS;
         case CL_SUCCESS:
             ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
             cli_bytecode_context_destroy(bc_ctx);
             if (ndesc != -1 && tempfile) {
afe940da
                 CLI_UNPRESULTS("cli_scanpe: bytecode PE hook", 1, 1, (0));
288057e9
             }
f98a68af
 
288057e9
             break;
         default:
             cli_bytecode_context_destroy(bc_ctx);
3ae0a76d
     }
ab636570
 
afe940da
     cli_exe_info_destroy(peinfo);
f98a68af
 
20b45621
 #if HAVE_JSON
f98a68af
     if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS)
20b45621
         return CL_ETIMEOUT;
 #endif
f98a68af
 
048a88e6
     if (SCAN_ALLMATCHES && viruses_found)
f98a68af
         return CL_VIRUS;
 
342e27a5
     return CL_CLEAN;
6307ca15
 }
cdbf8c8e
 
afe940da
 int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo)
cdbf8c8e
 {
afe940da
     return cli_peheader(map, peinfo, CLI_PEHEADER_OPT_EXTRACT_VINFO, NULL);
 }
 
 /** Parse the PE header and, if successful, populate peinfo
  *
  * @param map The fmap_t backing the file being scanned
  * @param peinfo A structure to populate with info from the PE header. This
  *               MUST be initialized via cli_exe_info_init prior to calling
  * @param opts A bitfield indicating various options related to PE header
  *             parsing.  The options are (prefixed with CLI_PEHEADER_OPT_):
  *              - NONE - Do default parsing
  *              - COLLECT_JSON - Populate ctx's json obj with PE header
5319602c
  *                               info
afe940da
  *              - DBG_PRINT_INFO - Print debug information about the
  *                                 PE file. Right now, cli_peheader is
  *                                 called multiple times for a given PE,
  *                                 so you don't want to print out the
  *                                 same info each time.
  *              - EXTRACT_VINFO - Parse the PEs VERSION_INFO metadata
  *                                and store it in peinfo->vinfo
  *              - STRICT_ON_PE_ERRORS - If specified, some cases that
  *                                      might be considered a broken
  *                                      executable cause RET_BROKEN_PE
  *                                      to be returned, but otherwise
  *                                      these will be tolerated.
5319602c
  *              - REMOVE_MISSING_SECTIONS - If a section exists outside of the
  *                                          file, remove it from
  *                                          peinfo->sections. Otherwise, the
  *                                          rsz is just set to 0 for it.
afe940da
  * @param ctx The overaching cli_ctx.  This is required with certain opts, but
  *            optional otherwise.
  * @return If the PE header is parsed successfully, CLI_PEHEADER_RET_SUCCESS
  *         is returned. If it seems like the PE is broken,
  *         CLI_PEHEADER_RET_BROKEN_PE is returned.  Otherwise, one of the
  *         other error codes is returned.
8650c790
  *         The caller MUST destroy peinfo, regardless of what this function
  *         returns.
afe940da
  *
  * TODO What constitutes a "broken PE" seems somewhat arbitrary in places.
  * I think a PE should only be considered broken if it will not run on
  * any version of Windows.  We should invest more time to ensure that our
  * broken PE detection more closely aligns with this.
  *
  * TODO Simplify and get rid of CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS if
  * possible.  We should either fail always or ignore always, IMO.
  *
5319602c
  * TODO Simplify and get rid of CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS if
  * possible.  I don't think it makes sense to have pieces of the code work
  * off of incomplete representations of the sections (for instance, I wonder
  * if this makes any of the bytecode APIs return unexpected values).  This
  * appears to have been implemented to prevent ClamAV from crashing, though,
  * (bb11155) so we need to ensure the underlying issues are addressed.
  *
afe940da
  * TODO Consolidate when information about the PE is printed (after successful
  * PE parsing).  This will allow us to simplify the code.  Some fail cases,
  * then, will cause PE info to not be printed at all, but I think this is
  * acceptable.  The debug messages generated in the fail cases should point to
  * what happened, and that's enough to track down the cause of any issues.
  *
  * TODO Same as above but with JSON creation
  */
 int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx)
 {
     uint16_t e_magic; /* DOS signature ("MZ") */
     const char *archtype = NULL, *subsystem = NULL;
     time_t timestamp;
     char timestr[32];
     uint32_t data_dirs_size;
     uint16_t opt_hdr_size;
     uint32_t stored_opt_hdr_size;
     struct pe_image_file_hdr *file_hdr;
     struct pe_image_optional_hdr32 *opt32;
     struct pe_image_optional_hdr64 *opt64;
8650c790
     struct pe_image_section_hdr *section_hdrs = NULL;
afe940da
     unsigned int i, j, section_pe_idx;
     unsigned int err;
     uint32_t salign, falign;
017f3490
     size_t fsize;
     ssize_t at;
afe940da
     uint32_t is_dll = 0;
     uint32_t is_exe = 0;
     int native      = 0;
6c03dc5d
     size_t read;
 
8650c790
     int ret = CLI_PEHEADER_RET_GENERIC_ERROR;
afe940da
 #if HAVE_JSON
     int toval                   = 0;
     struct json_object *pe_json = NULL;
     char jsonbuf[128];
 #endif
cdbf8c8e
 
afe940da
     if (ctx == NULL &&
         (opts & CLI_PEHEADER_OPT_COLLECT_JSON ||
          opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO)) {
         cli_errmsg("cli_peheader: ctx can't be NULL for options specified\n");
8650c790
         goto done;
afe940da
     }
 
 #if HAVE_JSON
     if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
         pe_json = get_pe_property(ctx);
     }
 #endif
cdbf8c8e
 
048d7677
     fsize = map->len - peinfo->offset;
288057e9
     if (fmap_readn(map, &e_magic, peinfo->offset, sizeof(e_magic)) != sizeof(e_magic)) {
afe940da
         cli_dbgmsg("cli_peheader: Can't read DOS signature\n");
8650c790
         goto done;
cdbf8c8e
     }
 
288057e9
     if (EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD) {
afe940da
         cli_dbgmsg("cli_peheader: Invalid DOS signature\n");
8650c790
         goto done;
cdbf8c8e
     }
 
afe940da
     if (fmap_readn(map, &(peinfo->e_lfanew), peinfo->offset + 58 + sizeof(e_magic), sizeof(peinfo->e_lfanew)) != sizeof(peinfo->e_lfanew)) {
017f3490
         /* truncated header? */
afe940da
         cli_dbgmsg("cli_peheader: Unable to read e_lfanew - truncated header?\n");
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
cdbf8c8e
     }
 
afe940da
     peinfo->e_lfanew = EC32(peinfo->e_lfanew);
     if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
         cli_dbgmsg("e_lfanew == %d\n", peinfo->e_lfanew);
     }
     if (!peinfo->e_lfanew) {
         cli_dbgmsg("cli_peheader: Not a PE file - e_lfanew == 0\n");
8650c790
         goto done;
cdbf8c8e
     }
 
afe940da
     if (fmap_readn(map, &(peinfo->file_hdr), peinfo->offset + peinfo->e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr)) {
017f3490
         /* bad information in e_lfanew - probably not a PE file */
cbf5017a
         cli_dbgmsg("cli_peheader: Can't read file header\n");
8650c790
         goto done;
cdbf8c8e
     }
 
afe940da
     file_hdr = &(peinfo->file_hdr);
 
     if (EC32(file_hdr->Magic) != PE_IMAGE_NT_SIGNATURE) {
         cli_dbgmsg("cli_peheader: Invalid PE signature (probably NE file)\n");
8650c790
         goto done;
cdbf8c8e
     }
 
afe940da
     if (EC16(file_hdr->Characteristics) & 0x2000) {
667a4b35
 
afe940da
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
             cli_jsonstr(pe_json, "Type", "DLL");
 #endif
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
             cli_dbgmsg("File type: DLL\n");
         }
 
         is_dll = 1;
     } else if (EC16(file_hdr->Characteristics) & 0x0002) {
 
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
             cli_jsonstr(pe_json, "Type", "EXE");
 #endif
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
             cli_dbgmsg("File type: Executable\n");
         }
 
         is_exe = 1;
57866af1
     }
 
afe940da
     if (!is_dll && !is_exe) {
         cli_dbgmsg("cli_peheader: Assumption Violated: PE is not a DLL or EXE\n");
         // TODO Don't continue if not an exe or dll?
57866af1
     }
 
afe940da
     peinfo->is_dll = is_dll;
 
     if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ||
         opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
         switch (EC16(file_hdr->Machine)) {
             case 0x0:
                 archtype = "Unknown";
                 break;
             case 0x1:
                 // New as of Windows 10, version 1607 and Windows Server 2016
                 archtype = "Target Host";
                 break;
             case 0x14c:
                 archtype = "80386";
                 break;
             case 0x14d:
                 archtype = "80486";
                 break;
             case 0x14e:
                 archtype = "80586";
                 break;
             case 0x160:
                 archtype = "R3000 MIPS BE";
                 break;
             case 0x162:
                 archtype = "R3000 MIPS LE";
                 break;
             case 0x166:
                 archtype = "R4000 MIPS LE";
                 break;
             case 0x168:
                 archtype = "R10000 MIPS LE";
                 break;
             case 0x169:
                 archtype = "WCE MIPS LE";
                 break;
             case 0x184:
                 archtype = "DEC Alpha AXP";
                 break;
             case 0x1a2:
                 archtype = "Hitachi SH3 LE";
                 break;
             case 0x1a3:
                 archtype = "Hitachi SH3-DSP";
                 break;
             case 0x1a4:
                 archtype = "Hitachi SH3-E LE";
                 break;
             case 0x1a6:
                 archtype = "Hitachi SH4 LE";
                 break;
             case 0x1a8:
                 archtype = "Hitachi SH5";
                 break;
             case 0x1c0:
                 archtype = "ARM LE";
                 break;
             case 0x1c2:
                 archtype = "ARM Thumb/Thumb-2 LE";
                 break;
             case 0x1c4:
                 archtype = "ARM Thumb-2 LE";
                 break;
             case 0x1d3:
                 archtype = "AM33";
                 break;
             case 0x1f0:
                 archtype = "PowerPC LE";
                 break;
             case 0x1f1:
                 archtype = "PowerPC FP";
                 break;
             case 0x200:
                 archtype = "IA64";
                 break;
             case 0x266:
                 archtype = "MIPS16";
                 break;
             case 0x268:
                 archtype = "M68k";
                 break;
             case 0x284:
                 archtype = "DEC Alpha AXP 64bit";
                 break;
             case 0x366:
                 archtype = "MIPS+FPU";
                 break;
             case 0x466:
                 archtype = "MIPS16+FPU";
                 break;
             case 0x520:
                 archtype = "Infineon TriCore";
                 break;
             case 0xcef:
                 archtype = "CEF";
                 break;
             case 0xebc:
                 archtype = "EFI Byte Code";
                 break;
             case 0x8664:
                 archtype = "AMD64";
                 break;
             case 0x9041:
                 archtype = "M32R";
                 break;
             case 0xaa64:
                 archtype = "ARM64 LE";
                 break;
             case 0xc0ee:
                 archtype = "CEE";
                 break;
             default:
                 archtype = "Unknown";
017f3490
         }
 
afe940da
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO)
             cli_dbgmsg("Machine type: %s\n", archtype);
 
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
             cli_jsonstr(pe_json, "ArchType", archtype);
 #endif
     }
 
     peinfo->nsections = EC16(file_hdr->NumberOfSections);
     if (peinfo->nsections == 0 || peinfo->nsections > PE_MAXSECTIONS) {
 
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
             pe_add_heuristic_property(ctx, "BadNumberOfSections");
         }
 #endif
         // TODO Investigate how corrupted_input is set and whether this
         // check is needed
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO &&
             !ctx->corrupted_input) {
             if (peinfo->nsections == 0) {
                 cli_dbgmsg("cli_peheader: Invalid NumberOfSections (0)\n");
             } else {
                 cli_dbgmsg("cli_peheader: Invalid NumberOfSections (>%d)\n", PE_MAXSECTIONS);
             }
         }
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
afe940da
     }
 
     timestamp    = (time_t)EC32(file_hdr->TimeDateStamp);
     opt_hdr_size = EC16(file_hdr->SizeOfOptionalHeader);
 
     if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
         cli_dbgmsg("NumberOfSections: %d\n", peinfo->nsections);
8650c790
         cli_dbgmsg("TimeDateStamp: %s", cli_ctime(&timestamp, timestr, sizeof(timestr)));
afe940da
         cli_dbgmsg("SizeOfOptionalHeader: 0x%x\n", opt_hdr_size);
     }
 
 #if HAVE_JSON
     if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
         cli_jsonint(pe_json, "NumberOfSections", peinfo->nsections);
8650c790
         /* NOTE: the TimeDateStamp value will look like "Wed Dec 31 19:00:00 1969\n" */
afe940da
         cli_jsonstr(pe_json, "TimeDateStamp", cli_ctime(&timestamp, timestr, sizeof(timestr)));
         cli_jsonint(pe_json, "SizeOfOptionalHeader", opt_hdr_size);
     }
 #endif
 
     // Ensure there are enough bytes to cover the full optional header,
     // not including the data directory entries (which aren't all gauranteed
     // to be there)
     if (opt_hdr_size < sizeof(struct pe_image_optional_hdr32)) {
         cli_dbgmsg("cli_peheader: SizeOfOptionalHeader too small\n");
 
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
             pe_add_heuristic_property(ctx, "BadOptionalHeaderSize");
         }
 #endif
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
afe940da
     }
 
     at = peinfo->offset + peinfo->e_lfanew + sizeof(struct pe_image_file_hdr);
     if (fmap_readn(map, &(peinfo->pe_opt.opt32), at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
         cli_dbgmsg("cli_peheader: Can't read optional file header\n");
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
afe940da
     }
     stored_opt_hdr_size = sizeof(struct pe_image_optional_hdr32);
     at += stored_opt_hdr_size;
 
     opt32 = &(peinfo->pe_opt.opt32);
 
     if (EC16(opt32->Magic) == PE32P_SIGNATURE) { /* PE+ */
         // The PE32+ optional header is bigger by 16 bytes, so map in the
         // additional bytes here
 
         if (opt_hdr_size < sizeof(struct pe_image_optional_hdr64)) {
             cli_dbgmsg("cli_peheader: Incorrect SizeOfOptionalHeader for PE32+\n");
 #if HAVE_JSON
             if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
                 pe_add_heuristic_property(ctx, "BadOptionalHeaderSizePE32Plus");
             }
 #endif
8650c790
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
afe940da
         }
 
         if (fmap_readn(map, (void *)(((size_t) & (peinfo->pe_opt.opt64)) + sizeof(struct pe_image_optional_hdr32)), at, OPT_HDR_SIZE_DIFF) != OPT_HDR_SIZE_DIFF) {
             cli_dbgmsg("cli_peheader: Can't read additional optional file header bytes\n");
8650c790
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
afe940da
         }
 
         stored_opt_hdr_size += OPT_HDR_SIZE_DIFF;
         at += OPT_HDR_SIZE_DIFF;
         peinfo->is_pe32plus = 1;
 
         opt64 = &(peinfo->pe_opt.opt64);
 
         peinfo->vep       = EC32(opt64->AddressOfEntryPoint);
         peinfo->hdr_size  = EC32(opt64->SizeOfHeaders);
         peinfo->ndatadirs = EC32(opt64->NumberOfRvaAndSizes);
 
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
             cli_dbgmsg("File format: PE32+\n");
             cli_dbgmsg("MajorLinkerVersion: %d\n", opt64->MajorLinkerVersion);
             cli_dbgmsg("MinorLinkerVersion: %d\n", opt64->MinorLinkerVersion);
             cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(opt64->SizeOfCode));
             cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(opt64->SizeOfInitializedData));
             cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(opt64->SizeOfUninitializedData));
             cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", peinfo->vep);
             cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(opt64->BaseOfCode));
             cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(opt64->SectionAlignment));
             cli_dbgmsg("FileAlignment: 0x%x\n", EC32(opt64->FileAlignment));
             cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(opt64->MajorSubsystemVersion));
             cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(opt64->MinorSubsystemVersion));
             cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(opt64->SizeOfImage));
             cli_dbgmsg("SizeOfHeaders: 0x%x\n", peinfo->hdr_size);
             cli_dbgmsg("NumberOfRvaAndSizes: %u\n", peinfo->ndatadirs);
         }
 
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
             cli_jsonint(pe_json, "MajorLinkerVersion", opt64->MajorLinkerVersion);
             cli_jsonint(pe_json, "MinorLinkerVersion", opt64->MinorLinkerVersion);
             cli_jsonint(pe_json, "SizeOfCode", EC32(opt64->SizeOfCode));
             cli_jsonint(pe_json, "SizeOfInitializedData", EC32(opt64->SizeOfInitializedData));
             cli_jsonint(pe_json, "SizeOfUninitializedData", EC32(opt64->SizeOfUninitializedData));
             cli_jsonint(pe_json, "NumberOfRvaAndSizes", EC32(opt64->NumberOfRvaAndSizes));
             cli_jsonint(pe_json, "MajorSubsystemVersion", EC16(opt64->MajorSubsystemVersion));
             cli_jsonint(pe_json, "MinorSubsystemVersion", EC16(opt64->MinorSubsystemVersion));
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->vep);
             cli_jsonstr(pe_json, "EntryPoint", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->BaseOfCode));
             cli_jsonstr(pe_json, "BaseOfCode", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->SectionAlignment));
             cli_jsonstr(pe_json, "SectionAlignment", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->FileAlignment));
             cli_jsonstr(pe_json, "FileAlignment", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->SizeOfImage));
             cli_jsonstr(pe_json, "SizeOfImage", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->hdr_size);
             cli_jsonstr(pe_json, "SizeOfHeaders", jsonbuf);
017f3490
         }
afe940da
 #endif
017f3490
 
21bf52c0
     } else { /* PE */
afe940da
         peinfo->is_pe32plus = 0;
         peinfo->vep         = EC32(opt32->AddressOfEntryPoint);
         peinfo->hdr_size    = EC32(opt32->SizeOfHeaders);
         peinfo->ndatadirs   = EC32(opt32->NumberOfRvaAndSizes);
 
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
             cli_dbgmsg("File format: PE\n");
             cli_dbgmsg("MajorLinkerVersion: %d\n", opt32->MajorLinkerVersion);
             cli_dbgmsg("MinorLinkerVersion: %d\n", opt32->MinorLinkerVersion);
             cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(opt32->SizeOfCode));
             cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(opt32->SizeOfInitializedData));
             cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(opt32->SizeOfUninitializedData));
             cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", peinfo->vep);
             cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(opt32->BaseOfCode));
             cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(opt32->SectionAlignment));
             cli_dbgmsg("FileAlignment: 0x%x\n", EC32(opt32->FileAlignment));
             cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(opt32->MajorSubsystemVersion));
             cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(opt32->MinorSubsystemVersion));
             cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(opt32->SizeOfImage));
             cli_dbgmsg("SizeOfHeaders: 0x%x\n", peinfo->hdr_size);
             cli_dbgmsg("NumberOfRvaAndSizes: %u\n", peinfo->ndatadirs);
017f3490
         }
 
afe940da
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
             cli_jsonint(pe_json, "MajorLinkerVersion", opt32->MajorLinkerVersion);
             cli_jsonint(pe_json, "MinorLinkerVersion", opt32->MinorLinkerVersion);
             cli_jsonint(pe_json, "SizeOfCode", EC32(opt32->SizeOfCode));
             cli_jsonint(pe_json, "SizeOfInitializedData", EC32(opt32->SizeOfInitializedData));
             cli_jsonint(pe_json, "SizeOfUninitializedData", EC32(opt32->SizeOfUninitializedData));
             cli_jsonint(pe_json, "NumberOfRvaAndSizes", EC32(opt32->NumberOfRvaAndSizes));
             cli_jsonint(pe_json, "MajorSubsystemVersion", EC16(opt32->MajorSubsystemVersion));
             cli_jsonint(pe_json, "MinorSubsystemVersion", EC16(opt32->MinorSubsystemVersion));
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->vep);
             cli_jsonstr(pe_json, "EntryPoint", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->BaseOfCode));
             cli_jsonstr(pe_json, "BaseOfCode", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->SectionAlignment));
             cli_jsonstr(pe_json, "SectionAlignment", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->FileAlignment));
             cli_jsonstr(pe_json, "FileAlignment", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->SizeOfImage));
             cli_jsonstr(pe_json, "SizeOfImage", jsonbuf);
 
             snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->hdr_size);
             cli_jsonstr(pe_json, "SizeOfHeaders", jsonbuf);
         }
 #endif
cdbf8c8e
     }
 
afe940da
     salign = (peinfo->is_pe32plus) ? EC32(opt64->SectionAlignment) : EC32(opt32->SectionAlignment);
     falign = (peinfo->is_pe32plus) ? EC32(opt64->FileAlignment) : EC32(opt32->FileAlignment);
57866af1
 
afe940da
     switch (peinfo->is_pe32plus ? EC16(opt64->Subsystem) : EC16(opt32->Subsystem)) {
         case 0:
             subsystem = "Unknown";
             break;
         case 1:
             subsystem = "Native (svc)";
             native    = 1;
             break;
         case 2:
             subsystem = "Win32 GUI";
             break;
         case 3:
             subsystem = "Win32 console";
             break;
         case 5:
             subsystem = "OS/2 console";
             break;
         case 7:
             subsystem = "POSIX console";
             break;
         case 8:
             subsystem = "Native Win9x driver";
             break;
         case 9:
             subsystem = "WinCE GUI";
             break;
         case 10:
             subsystem = "EFI application";
             break;
         case 11:
             subsystem = "EFI driver";
             break;
         case 12:
             subsystem = "EFI runtime driver";
             break;
         case 13:
             subsystem = "EFI ROM image";
             break;
         case 14:
             subsystem = "Xbox";
             break;
         case 16:
             subsystem = "Boot application";
             break;
         default:
             subsystem = "Unknown";
     }
 
     if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
         cli_dbgmsg("Subsystem: %s\n", subsystem);
         cli_dbgmsg("------------------------------------\n");
     }
 
 #if HAVE_JSON
     if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
         cli_jsonstr(pe_json, "Subsystem", subsystem);
 #endif
21bf52c0
 
afe940da
     if (!native && (!salign || (salign % 0x1000))) {
         cli_dbgmsg("cli_peheader: Bad section alignment\n");
         if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
8650c790
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
afe940da
         }
     }
cdbf8c8e
 
afe940da
     if (!native && (!falign || (falign % 0x200))) {
         cli_dbgmsg("cli_peheader: Bad file alignment\n");
         if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
8650c790
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
afe940da
         }
cdbf8c8e
     }
 
afe940da
     // Map in the optional header data directories.  The spec defines 16
     // directory entries, but NumberOfRvaAndSizes can be less than that
     // and the Windows loader will pretend that the data directory does
     // not exist. NumberOfRvaAndSizes can be larger than that too, which
     // the Windows loader is OK with.  To populate peinfo->dirs, we will
     // copy in as many data dirs are specified but for a max of 16 (and
     // adjust peinfo->ndatadirs accordingly)
cdbf8c8e
 
afe940da
     if (peinfo->ndatadirs > 0x10) {
         cli_dbgmsg("cli_peheader: Encountered NumberOfRvaAndSizes > 16 (suspicious)\n");
cdbf8c8e
     }
 
afe940da
     // In the case where we won't fully populate dirs with file data,
     // ensure that the underlying memory is zero so that existing code
     // can interact with peinfo->dirs without using peinfo->ndatadirs
     if (peinfo->ndatadirs < sizeof(peinfo->dirs) / sizeof(peinfo->dirs[0])) {
         memset(&(peinfo->dirs), '\0', sizeof(peinfo->dirs));
     }
 
     peinfo->ndatadirs = MIN(peinfo->ndatadirs, sizeof(peinfo->dirs) / sizeof(peinfo->dirs[0]));
 
     data_dirs_size = sizeof(struct pe_image_data_dir) * peinfo->ndatadirs;
 
     if (opt_hdr_size < (stored_opt_hdr_size + data_dirs_size)) {
         cli_dbgmsg("cli_peheader: SizeOfOptionalHeader too small (doesn't include data dir size)\n");
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
afe940da
     }
 
     read = fmap_readn(map, peinfo->dirs, at, data_dirs_size);
6c03dc5d
     if ((read == (size_t)-1) || (read != data_dirs_size)) {
afe940da
         cli_dbgmsg("cli_peheader: Can't read optional file header data dirs\n");
8650c790
         goto done;
afe940da
     }
     at += data_dirs_size;
 
     if (opt_hdr_size != (stored_opt_hdr_size + data_dirs_size)) {
         /* Seek to the end of the long header */
         cli_dbgmsg("cli_peheader: Encountered case where SizeOfOptionalHeader appears bigger than required\n");
         at += opt_hdr_size - (stored_opt_hdr_size + data_dirs_size);
     }
 
     // TODO This level of processing might not be needed in all cases
 
     // Sanity checks
     // TODO Also check that salign >= falign
     if (peinfo->hdr_size != PESALIGN(peinfo->hdr_size, salign)) {
         cli_dbgmsg("cli_peheader: SizeOfHeader is not aligned to the SectionAlignment\n");
     }
     if (peinfo->hdr_size != PESALIGN(peinfo->hdr_size, falign)) {
         cli_dbgmsg("cli_peheader: SizeOfHeader is not aligned to the FileAlignment\n");
     }
 
     // TODO Why align here? -- /* Aligned headers virtual size */
     // hdr_size should already be rounded up
     // to a multiple of the file alignment.
     // TODO in cli_checkpe_fp this aligned to falign, elsewhere it aligned to salign
     peinfo->hdr_size = PESALIGN(peinfo->hdr_size, salign);
 
     peinfo->sections = (struct cli_exe_section *)cli_calloc(peinfo->nsections, sizeof(struct cli_exe_section));
 
     if (!peinfo->sections) {
         cli_dbgmsg("cli_peheader: Can't allocate memory for section headers\n");
8650c790
         goto done;
afe940da
     }
 
     section_hdrs = (struct pe_image_section_hdr *)cli_calloc(peinfo->nsections, sizeof(struct pe_image_section_hdr));
 
     if (!section_hdrs) {
         cli_dbgmsg("cli_peheader: Can't allocate memory for section headers\n");
8650c790
         goto done;
afe940da
     }
 
     read = fmap_readn(map, section_hdrs, at, peinfo->nsections * sizeof(struct pe_image_section_hdr));
6c03dc5d
     if ((read == (size_t)-1) || (read != peinfo->nsections * sizeof(struct pe_image_section_hdr))) {
afe940da
         cli_dbgmsg("cli_peheader: Can't read section header - possibly broken PE file\n");
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
21bf52c0
     }
288057e9
     at += sizeof(struct pe_image_section_hdr) * peinfo->nsections;
cdbf8c8e
 
afe940da
     // TODO Verify that this performs correctly
     // TODO I'm not sure why this is necessary since the specification says
     // that PointerToRawData is expected to be a multiple of the file
     // alignment.  Should we report this is as a PE with an error?
 
288057e9
     for (i = 0; falign != 0x200 && i < peinfo->nsections; i++) {
017f3490
         /* file alignment fallback mode - blah */
afe940da
         if (falign && section_hdrs[i].SizeOfRawData && EC32(section_hdrs[i].PointerToRawData) % falign && !(EC32(section_hdrs[i].PointerToRawData) % 0x200)) {
             cli_dbgmsg("cli_peheader: Encountered section with unexpected alignment - triggering fallback mode\n");
017f3490
             falign = 0x200;
         }
21bf52c0
     }
cdbf8c8e
 
afe940da
     fsize = (map->len - peinfo->offset);
 
     // TODO Why do we fix up these alignments?  This shouldn't be needed?
     for (i = 0, section_pe_idx = 0; i < peinfo->nsections; i++, section_pe_idx++) {
 
         struct cli_exe_section *section          = &(peinfo->sections[i]);
         struct pe_image_section_hdr *section_hdr = &(section_hdrs[i]);
         char sname[9];
 
         // TODO I don't see any documentation that says VirtualAddress and VirtualSize must be aligned
         section->rva  = PEALIGN(EC32(section_hdr->VirtualAddress), salign);
         section->vsz  = PESALIGN(EC32(section_hdr->VirtualSize), salign);
         section->raw  = PEALIGN(EC32(section_hdr->PointerToRawData), falign);
         section->rsz  = PESALIGN(EC32(section_hdr->SizeOfRawData), falign);
         section->chr  = EC32(section_hdr->Characteristics);
         section->urva = EC32(section_hdr->VirtualAddress); /* Just in case */
         section->uvsz = EC32(section_hdr->VirtualSize);
         section->uraw = EC32(section_hdr->PointerToRawData);
         section->ursz = EC32(section_hdr->SizeOfRawData);
 
5319602c
         /* First, if a section exists totally outside of a file, remove the
          * section from the list or zero out it's size. */
afe940da
         if (section->rsz) { /* Don't bother with virtual only sections */
             if (section->raw >= fsize || section->uraw >= fsize) {
                 cli_dbgmsg("cli_peheader: Broken PE file - Section %d starts or exists beyond the end of file (Offset@ %lu, Total filesize %lu)\n", section_pe_idx, (unsigned long)section->raw, (unsigned long)fsize);
 
5319602c
                 if (opts & CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS) {
                     if (peinfo->nsections == 1) {
                         ret = CLI_PEHEADER_RET_BROKEN_PE;
                         goto done;
                     }
afe940da
 
5319602c
                     for (j = i; j < peinfo->nsections - 1; j++)
                         memcpy(&(peinfo->sections[j]), &(peinfo->sections[j + 1]), sizeof(struct cli_exe_section));
21bf52c0
 
5319602c
                     for (j = i; j < peinfo->nsections - 1; j++)
                         memcpy(&section_hdrs[j], &section_hdrs[j + 1], sizeof(struct pe_image_section_hdr));
afe940da
 
5319602c
                     peinfo->nsections--;
21bf52c0
 
5319602c
                     // Adjust i since we removed a section and continue on
                     i--;
                     continue;
 
                 } else {
                     section->rsz  = 0;
                     section->ursz = 0;
                 }
             } else {
afe940da
 
5319602c
                 /* If a section is truncated, adjust it's size value */
                 if (!CLI_ISCONTAINED(0, fsize, section->raw, section->rsz)) {
                     cli_dbgmsg("cli_peheader: PE Section %d raw+rsz extends past the end of the file by %lu bytes\n", section_pe_idx, (section->raw + section->rsz) - fsize);
                     section->rsz = fsize - section->raw;
                 }
 
                 if (!CLI_ISCONTAINED(0, fsize, section->uraw, section->ursz)) {
                     cli_dbgmsg("cli_peheader: PE Section %d uraw+ursz extends past the end of the file by %lu bytes\n", section_pe_idx, (section->uraw + section->ursz) - fsize);
                     section->ursz = fsize - section->uraw;
                 }
afe940da
             }
         }
 
         strncpy(sname, (char *)section_hdr->Name, 8);
         sname[8] = '\0';
 
 #if HAVE_JSON
         if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
             add_section_info(ctx, &peinfo->sections[i]);
 
             if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
8650c790
                 ret = CLI_PEHEADER_RET_JSON_TIMEOUT;
                 goto done;
afe940da
             }
         }
 #endif
 
         // TODO Why do we do this
         // TODO Should this be done before we dump the json
         if (!section->vsz && section->rsz)
             section->vsz = PESALIGN(section->ursz, salign);
 
         if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
             cli_dbgmsg("Section %d\n", section_pe_idx);
             cli_dbgmsg("Section name: %s\n", sname);
             cli_dbgmsg("Section data (from headers - in memory)\n");
             cli_dbgmsg("VirtualSize: 0x%x 0x%x\n", section->uvsz, section->vsz);
             cli_dbgmsg("VirtualAddress: 0x%x 0x%x\n", section->urva, section->rva);
             cli_dbgmsg("SizeOfRawData: 0x%x 0x%x\n", section->ursz, section->rsz);
             cli_dbgmsg("PointerToRawData: 0x%x 0x%x\n", section->uraw, section->raw);
 
             if (section->chr & 0x20) {
                 cli_dbgmsg("Section contains executable code\n");
             }
 
             if (section->vsz < section->rsz) {
                 cli_dbgmsg("Section contains free space\n");
                 /*
                 cli_dbgmsg("Dumping %d bytes\n", section_hdr.SizeOfRawData - section_hdr.VirtualSize);
                 ddump(desc, section_hdr.PointerToRawData + section_hdr.VirtualSize, section_hdr.SizeOfRawData - section_hdr.VirtualSize, cli_gentemp(NULL));
                 */
             }
 
             if (section->chr & 0x20000000)
                 cli_dbgmsg("Section's memory is executable\n");
 
             if (section->chr & 0x80000000)
                 cli_dbgmsg("Section's memory is writeable\n");
 
             cli_dbgmsg("------------------------------------\n");
         }
 
         if (!salign || (section->urva % salign)) { /* Bad section alignment */
             cli_dbgmsg("cli_peheader: Broken PE - section's VirtualAddress is misaligned\n");
             if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
8650c790
                 ret = CLI_PEHEADER_RET_BROKEN_PE;
                 goto done;
afe940da
             }
         }
 
         // TODO should we skip all of these checks if it's an empty
         // section? Why the exception for uraw?
         if (section->urva >> 31 || section->uvsz >> 31 || (section->rsz && section->uraw >> 31) || peinfo->sections[i].ursz >> 31) {
             cli_dbgmsg("cli_peheader: Found PE values with sign bit set\n");
8650c790
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
afe940da
         }
 
         if (!i) {
             if (section->urva != peinfo->hdr_size) { /* Bad first section RVA */
                 cli_dbgmsg("cli_peheader: First section doesn't start immediately after the header\n");
                 if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
8650c790
                     ret = CLI_PEHEADER_RET_BROKEN_PE;
                     goto done;
afe940da
                 }
             }
 
             peinfo->min = section->rva;
             peinfo->max = section->rva + section->rsz;
         } else {
             if (section->urva - peinfo->sections[i - 1].urva != peinfo->sections[i - 1].vsz) { /* No holes, no overlapping, no virtual disorder */
                 cli_dbgmsg("cli_peheader: Virtually misplaced section (wrong order, overlapping, non contiguous)\n");
                 if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
8650c790
                     ret = CLI_PEHEADER_RET_BROKEN_PE;
                     goto done;
afe940da
                 }
             }
 
             if (section->rva < peinfo->min)
                 peinfo->min = section->rva;
 
             if (section->rva + section->rsz > peinfo->max) {
                 peinfo->max           = section->rva + section->rsz;
                 peinfo->overlay_start = section->raw + section->rsz;
             }
 
             // TODO This case might be possible, which would lead to us
             // mislabelling the overlay
             if (section->raw + section->rsz > peinfo->max) {
                 cli_dbgmsg("cli_peheader: Assumption Violated: Last section end RVA isn't tied to the last section\n");
             }
         }
239983bc
     }
33f89aa5
 
afe940da
     peinfo->overlay_size = fsize - peinfo->overlay_start;
 
     // NOTE: For DLLs the entrypoint is likely to be zero
8650c790
     // TODO Should this offset include peinfo->offset?
afe940da
     if (!(peinfo->ep = cli_rawaddr(peinfo->vep, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err) {
         cli_dbgmsg("cli_peheader: Broken PE file - Can't map EntryPoint to a file offset\n");
8650c790
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
afe940da
     }
 
 #if HAVE_JSON
     if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
         cli_jsonint(pe_json, "EntryPointOffset", peinfo->ep);
 
         if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
8650c790
             ret = CLI_PEHEADER_RET_JSON_TIMEOUT;
             goto done;
afe940da
         }
d2ba6f98
     }
afe940da
 #endif
667a4b35
 
afe940da
     if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
         cli_dbgmsg("EntryPoint offset: 0x%x (%d)\n", peinfo->ep, peinfo->ep);
cdbf8c8e
     }
 
afe940da
     if (is_dll || peinfo->ndatadirs < 3 || !peinfo->dirs[2].Size)
017f3490
         peinfo->res_addr = 0;
453d8180
     else
afe940da
         peinfo->res_addr = EC32(peinfo->dirs[2].VirtualAddress);
453d8180
 
afe940da
     while (opts & CLI_PEHEADER_OPT_EXTRACT_VINFO &&
            peinfo->ndatadirs >= 3 && peinfo->dirs[2].Size) {
017f3490
         struct vinfo_list vlist;
         const uint8_t *vptr, *baseptr;
         uint32_t rva, res_sz;
d2ba6f98
 
8650c790
         // TODO This code assumes peinfo->offset == 0, which might not always
         // be the case.
         if (0 != peinfo->offset) {
             cli_dbgmsg("cli_peheader: Assumption Violated: Looking for version info when peinfo->offset != 0\n");
         }
 
017f3490
         memset(&vlist, 0, sizeof(vlist));
afe940da
         findres(0x10, 0xffffffff, map, peinfo, versioninfo_cb, &vlist);
288057e9
         if (!vlist.count)
017f3490
             break; /* No version_information */
d2ba6f98
 
288057e9
         if (cli_hashset_init(&peinfo->vinfo, 32, 80)) {
017f3490
             cli_errmsg("cli_peheader: Unable to init vinfo hashset\n");
8650c790
             goto done;
017f3490
         }
d2ba6f98
 
017f3490
         err = 0;
288057e9
         for (i = 0; i < vlist.count; i++) { /* enum all version_information res - RESUMABLE */
             cli_dbgmsg("cli_peheader: parsing version info @ rva %x (%u/%u)\n", vlist.rvas[i], i + 1, vlist.count);
afe940da
             rva = cli_rawaddr(vlist.rvas[i], peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
288057e9
             if (err)
017f3490
                 continue;
 
288057e9
             if (!(vptr = fmap_need_off_once(map, rva, 16)))
017f3490
                 continue;
 
             baseptr = vptr - rva;
             /* parse resource */
288057e9
             rva    = cli_readint32(vptr);     /* ptr to version_info */
             res_sz = cli_readint32(vptr + 4); /* sizeof(resource) */
afe940da
             rva    = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
288057e9
             if (err)
017f3490
                 continue;
288057e9
             if (!(vptr = fmap_need_off_once(map, rva, res_sz)))
017f3490
                 continue;
288057e9
 
             while (res_sz > 4) { /* look for version_info - NOT RESUMABLE (expecting exactly one versioninfo) */
017f3490
                 uint32_t vinfo_sz, vinfo_val_sz, got_varfileinfo = 0;
 
                 vinfo_sz = vinfo_val_sz = cli_readint32(vptr);
                 vinfo_sz &= 0xffff;
288057e9
                 if (vinfo_sz > res_sz)
017f3490
                     break; /* the content is larger than the container */
 
                 vinfo_val_sz >>= 16;
288057e9
                 if (vinfo_sz <= 6 + 0x20 + 2 + 0x34 ||
                     vinfo_val_sz != 0x34 ||
                     memcmp(vptr + 6, "V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0\0", 0x20) ||
                     (unsigned int)cli_readint32(vptr + 0x28) != 0xfeef04bd) {
017f3490
                     /* - there should be enough room for the header(6), the key "VS_VERSION_INFO"(20), the padding(2) and the value(34)
                      * - the value should be sizeof(fixedfileinfo)
                      * - the key should match
                      * - there should be some proper magic for fixedfileinfo */
                     break; /* there's no point in looking further */
                 }
d2ba6f98
 
017f3490
                 /* move to the end of fixedfileinfo where the child elements are located */
                 vptr += 6 + 0x20 + 2 + 0x34;
                 vinfo_sz -= 6 + 0x20 + 2 + 0x34;
85310158
 
288057e9
                 while (vinfo_sz > 6) { /* look for stringfileinfo - NOT RESUMABLE (expecting at most one stringfileinfo) */
017f3490
                     uint32_t sfi_sz = cli_readint32(vptr) & 0xffff;
d2ba6f98
 
288057e9
                     if (sfi_sz > vinfo_sz)
017f3490
                         break; /* the content is larger than the container */
d2ba6f98
 
288057e9
                     if (!got_varfileinfo && sfi_sz > 6 + 0x18 && !memcmp(vptr + 6, "V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0", 0x18)) {
017f3490
                         /* skip varfileinfo as it sometimes appear before stringtableinfo */
                         vptr += sfi_sz;
                         vinfo_sz -= sfi_sz;
                         got_varfileinfo = 1;
                         continue;
                     }
00f5854d
 
288057e9
                     if (sfi_sz <= 6 + 0x1e || memcmp(vptr + 6, "S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0", 0x1e)) {
017f3490
                         /* - there should be enough room for the header(6) and the key "StringFileInfo"(1e)
                          * - the key should match */
                         break; /* this is an implicit hard fail: parent is not resumable */
                     }
d2ba6f98
 
017f3490
                     /* move to the end of stringfileinfo where the child elements are located */
                     vptr += 6 + 0x1e;
                     sfi_sz -= 6 + 0x1e;
d2ba6f98
 
288057e9
                     while (sfi_sz > 6) { /* enum all stringtables - RESUMABLE */
                         uint32_t st_sz           = cli_readint32(vptr) & 0xffff;
017f3490
                         const uint8_t *next_vptr = vptr + st_sz;
288057e9
                         uint32_t next_sfi_sz     = sfi_sz - st_sz;
d2ba6f98
 
288057e9
                         if (st_sz > sfi_sz || st_sz <= 24) {
017f3490
                             /* - the content is larger than the container
                                - there's no room for a stringtables (headers(6) + key(16) + padding(2)) */
                             break; /* this is an implicit hard fail: parent is not resumable */
                         }
d2ba6f98
 
017f3490
                         /* move to the end of stringtable where the child elements are located */
                         vptr += 24;
                         st_sz -= 24;
 
288057e9
                         while (st_sz > 6) { /* enum all strings - RESUMABLE */
017f3490
                             uint32_t s_sz, s_key_sz, s_val_sz;
 
                             s_sz = (cli_readint32(vptr) & 0xffff) + 3;
                             s_sz &= ~3;
288057e9
                             if (s_sz > st_sz || s_sz <= 6 + 2 + 8) {
017f3490
                                 /* - the content is larger than the container
                                  * - there's no room for a minimal string
                                  * - there's no room for the value */
288057e9
                                 st_sz  = 0;
017f3490
                                 sfi_sz = 0;
                                 break; /* force a hard fail */
                             }
 
                             /* ~wcstrlen(key) */
288057e9
                             for (s_key_sz = 6; s_key_sz + 1 < s_sz; s_key_sz += 2) {
                                 if (vptr[s_key_sz] || vptr[s_key_sz + 1])
017f3490
                                     continue;
 
                                 s_key_sz += 2;
                                 break;
                             }
 
                             s_key_sz += 3;
                             s_key_sz &= ~3;
 
288057e9
                             if (s_key_sz >= s_sz) {
017f3490
                                 /* key overflow */
                                 vptr += s_sz;
                                 st_sz -= s_sz;
                                 continue;
                             }
 
                             s_val_sz = s_sz - s_key_sz;
                             s_key_sz -= 6;
 
288057e9
                             if (s_val_sz <= 2) {
017f3490
                                 /* skip unset value */
                                 vptr += s_sz;
                                 st_sz -= s_sz;
                                 continue;
                             }
 
288057e9
                             if (cli_hashset_addkey(&peinfo->vinfo, (uint32_t)(vptr - baseptr + 6))) {
017f3490
                                 cli_errmsg("cli_peheader: Unable to add rva to vinfo hashset\n");
8650c790
                                 goto done;
017f3490
                             }
 
288057e9
                             if (cli_debug_flag) {
017f3490
                                 char *k, *v, *s;
 
                                 /* FIXME: skip too long strings */
288057e9
                                 k = cli_utf16toascii((const char *)vptr + 6, s_key_sz);
                                 if (k) {
                                     v = cli_utf16toascii((const char *)vptr + s_key_sz + 6, s_val_sz);
                                     if (v) {
                                         s = cli_str2hex((const char *)vptr + 6, s_key_sz + s_val_sz);
                                         if (s) {
017f3490
                                             cli_dbgmsg("VersionInfo (%x): '%s'='%s' - VI:%s\n", (uint32_t)(vptr - baseptr + 6), k, v, s);
                                             free(s);
                                         }
                                         free(v);
                                     }
                                     free(k);
                                 }
                             }
                             vptr += s_sz;
                             st_sz -= s_sz;
                         } /* enum all strings - RESUMABLE */
288057e9
                         vptr   = next_vptr;
017f3490
                         sfi_sz = next_sfi_sz * (sfi_sz != 0);
                     } /* enum all stringtables - RESUMABLE */
                     break;
                 } /* look for stringfileinfo - NOT RESUMABLE */
                 break;
             } /* look for version_info - NOT RESUMABLE */
288057e9
         }     /* enum all version_information res - RESUMABLE */
017f3490
         break;
85310158
     } /* while(dirs[2].Size) */
d2ba6f98
 
afe940da
     // Do final preperations for peinfo to be passed back
     peinfo->is_dll = is_dll;
 
8650c790
     ret = CLI_PEHEADER_RET_SUCCESS;
 
 done:
     /* In the fail case, peinfo will get destroyed by the caller */
 
     if (NULL != section_hdrs) {
         free(section_hdrs);
     }
 
     return ret;
cdbf8c8e
 }
0393aa56
 
afe940da
 // TODO We should sort based on VirtualAddress instead, since PointerToRawData
 // will be zero for sections where SizeOfRawData is zero.  This also aligns
 // with what tools like pefile do.
288057e9
 static int sort_sects(const void *first, const void *second)
 {
0393aa56
     const struct cli_exe_section *a = first, *b = second;
     return (a->raw - b->raw);
 }
 
181fee0f
 /* Check the given PE file for an authenticode signature and return CL_CLEAN if
b9c3525b
  * the signature is valid.  There are two cases that this function should
  * handle:
  * - A PE file has an embedded Authenticode section
  * - The PE file has no embedded Authenticode section but is covered by a
6c03dc5d
  *   catalog file that was loaded in via a -d
  *
1d7f6b27
  * If peinfo is NULL, one will be created internally and used
6c03dc5d
  *
1d7f6b27
  * CL_VERIFIED will be returned if the file was whitelisted based on its
b9c3525b
  * signature.  CL_VIRUS will be returned if the file was blacklisted based on
d92c0129
  * its signature.  Otherwise, a cl_error_t error value will be returned.
6c03dc5d
  *
d92c0129
  * If CL_VIRUS is returned, cli_append_virus will get called, adding the
  * name associated with the blacklist CRB rules to the list of found viruses.*/
 cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo)
288057e9
 {
afe940da
     size_t at;
     unsigned int i, hlen;
0393aa56
     size_t fsize;
288057e9
     fmap_t *map   = *ctx->fmap;
     void *hashctx = NULL;
b7d7a7bd
     struct pe_certificate_hdr cert_hdr;
b9c3525b
     struct cli_mapped_region *regions = NULL;
0f53ea60
     unsigned int nregions;
b9c3525b
     cl_error_t ret = CL_EVERIFY;
     uint8_t authsha1[SHA1_HASH_SIZE];
     uint32_t sec_dir_offset;
     uint32_t sec_dir_size;
afe940da
     struct cli_exe_info _peinfo;
 
d92c0129
     // If Authenticode parsing has been disabled via DCONF or an engine
     // option, then don't continue on.
     if (!(DCONF & PE_CONF_CERTS))
         return CL_EVERIFY;
 
     if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS)
         return CL_EVERIFY;
b9c3525b
 
1d7f6b27
     // If peinfo is NULL, initialize one.  This makes it so that this function
     // can be used easily by sigtool
     if (NULL == peinfo) {
         peinfo = &_peinfo;
         cli_exe_info_init(peinfo, 0);
0393aa56
 
1d7f6b27
         if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
5319602c
             cli_exe_info_destroy(peinfo);
1d7f6b27
             return CL_EFORMAT;
         }
b7d7a7bd
     }
0393aa56
 
afe940da
     sec_dir_offset = EC32(peinfo->dirs[4].VirtualAddress);
     sec_dir_size   = EC32(peinfo->dirs[4].Size);
b9c3525b
 
b7d7a7bd
     // As an optimization, check the security DataDirectory here and if
     // it's less than 8-bytes (and we aren't relying on this code to compute
b9c3525b
     // the section hashes), bail out if we don't have any Authenticode hashes
     // loaded from .cat files
     if (sec_dir_size < 8 && !cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 2)) {
1d7f6b27
         ret = CL_BREAK;
         goto finish;
b7d7a7bd
     }
0393aa56
     fsize = map->len;
 
1d7f6b27
     // We'll build a list of the regions that need to be hashed and pass it to
     // asn1_check_mscat to do hash verification there (the hash algorithm is
     // specified in the PKCS7 structure).  We need to hash up to 4 regions
     regions = (struct cli_mapped_region *)cli_calloc(4, sizeof(struct cli_mapped_region));
     if (!regions) {
         ret = CL_EMEM;
         goto finish;
     }
     nregions = 0;
0f53ea60
 
dd25061b
 #define add_chunk_to_hash_list(_offset, _size) \
     do {                                       \
         regions[nregions].offset = (_offset);  \
         regions[nregions].size   = (_size);    \
         nregions++;                            \
288057e9
     } while (0)
0393aa56
 
1d7f6b27
     // Pretty much every case below should return CL_EFORMAT
     ret = CL_EFORMAT;
b9c3525b
 
1d7f6b27
     /* MZ to checksum */
     at   = 0;
     hlen = peinfo->e_lfanew + sizeof(struct pe_image_file_hdr) + (peinfo->is_pe32plus ? offsetof(struct pe_image_optional_hdr64, CheckSum) : offsetof(struct pe_image_optional_hdr32, CheckSum));
     add_chunk_to_hash_list(0, hlen);
     at = hlen + 4;
7db9ec74
 
1d7f6b27
     /* Checksum to security */
     if (peinfo->is_pe32plus)
         hlen = sizeof(struct pe_image_optional_hdr64) - offsetof(struct pe_image_optional_hdr64, CheckSum) - 4;
     else
         hlen = sizeof(struct pe_image_optional_hdr32) - offsetof(struct pe_image_optional_hdr32, CheckSum) - 4;
afe940da
 
1d7f6b27
     hlen += sizeof(struct pe_image_data_dir) * 4;
     add_chunk_to_hash_list(at, hlen);
     at += hlen + 8;
0393aa56
 
1d7f6b27
     if (at > peinfo->hdr_size) {
         goto finish;
     }
0393aa56
 
1d7f6b27
     /* Security to End of header */
     hlen = peinfo->hdr_size - at;
     add_chunk_to_hash_list(at, hlen);
     at += hlen;
0393aa56
 
1d7f6b27
     if (sec_dir_offset) {
b9c3525b
 
1d7f6b27
         // Verify that we have all the bytes we expect in the authenticode sig
         // and that the certificate table is the last thing in the file
         // (according to the MS13-098 bulletin, this is a requirement)
         if (fsize != sec_dir_size + sec_dir_offset) {
             cli_dbgmsg("cli_check_auth_header: expected authenticode data at the end of the file\n");
             goto finish;
         }
465aca6e
 
1d7f6b27
         // Hash everything from the end of the header to the start of the
         // security section
         if (at < sec_dir_offset) {
             hlen = sec_dir_offset - at;
             add_chunk_to_hash_list(at, hlen);
         } else {
             cli_dbgmsg("cli_check_auth_header: security directory offset appears to overlap with the PE header\n");
             goto finish;
         }
0393aa56
 
1d7f6b27
         // Parse the security directory header
b7d7a7bd
 
1d7f6b27
         if (fmap_readn(map, &cert_hdr, sec_dir_offset, sizeof(cert_hdr)) != sizeof(cert_hdr)) {
             goto finish;
         }
b9c3525b
 
1d7f6b27
         if (EC16(cert_hdr.revision) != WIN_CERT_REV_2) {
             cli_dbgmsg("cli_check_auth_header: unsupported authenticode data revision\n");
             goto finish;
         }
b9c3525b
 
1d7f6b27
         if (EC16(cert_hdr.type) != WIN_CERT_TYPE_PKCS7) {
             cli_dbgmsg("cli_check_auth_header: unsupported authenticode data type\n");
             goto finish;
         }
b9c3525b
 
1d7f6b27
         hlen = sec_dir_size;
b9c3525b
 
1d7f6b27
         if (EC32(cert_hdr.length) != hlen) {
             /* This is the case that MS13-098 aimed to address, but it got
b9c3525b
                  * pushback to where the fix (not allowing additional, non-zero
                  * bytes in the security directory) is now opt-in via a registry
                  * key.  Given that most machines will treat these binaries as
                  * valid, we'll still parse the signature and just trust that
                  * our whitelist signatures are tailored enough to where any
                  * instances of this are reasonable (for instance, I saw one
                  * binary that appeared to use this to embed a license key.) */
1d7f6b27
             cli_dbgmsg("cli_check_auth_header: MS13-098 violation detected, but continuing on to verify certificate\n");
         }
b9c3525b
 
1d7f6b27
         at = sec_dir_offset + sizeof(cert_hdr);
         hlen -= sizeof(cert_hdr);
b9c3525b
 
d92c0129
         ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, ctx);
b9c3525b
 
1d7f6b27
         if (CL_VERIFIED == ret) {
             // We validated the embedded signature.  Hooray!
             goto finish;
         } else if (CL_VIRUS == ret) {
             // A blacklist rule hit - don't continue on to check hm_fp for a match
             goto finish;
         }
b9c3525b
 
1d7f6b27
         // Otherwise, we still need to check to see whether this file is
         // covered by a .cat file (it's common these days for driver files
         // to have .cat files covering PEs with embedded signatures)
b9c3525b
 
1d7f6b27
     } else {
b7d7a7bd
 
1d7f6b27
         // Hash everything from the end of the header to the end of the
         // file
         if (at < fsize) {
             hlen = fsize - at;
             add_chunk_to_hash_list(at, hlen);
b7d7a7bd
         }
1d7f6b27
     }
b7d7a7bd
 
1d7f6b27
     // At this point we should compute the SHA1 authenticode hash to see
     // whether we've had any hashes added from external catalog files
     // TODO Is it gauranteed that the hashing algorithm will be SHA1?  If
     // not, figure out how to handle that case
     hashctx = cl_hash_init("sha1");
     if (NULL == hashctx) {
         ret = CL_EMEM;
         goto finish;
     }
b9c3525b
 
1d7f6b27
     for (i = 0; i < nregions; i++) {
         const uint8_t *hptr;
         if (0 == regions[i].size) {
             continue;
b7d7a7bd
         }
1d7f6b27
         if (!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))) {
b9c3525b
             break;
b7d7a7bd
         }
8fdd05d2
 
1d7f6b27
         cl_update_hash(hashctx, hptr, regions[i].size);
     }
8fdd05d2
 
1d7f6b27
     if (i != nregions) {
         goto finish;
     }
0f53ea60
 
1d7f6b27
     cl_finish_hash(hashctx, authsha1);
     hashctx = NULL;
 
     if (cli_hm_scan(authsha1, 2, NULL, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) {
         cli_dbgmsg("cli_check_auth_header: PE file whitelisted by catalog file\n");
         ret = CL_CLEAN;
         goto finish;
     }
dd25061b
 
1d7f6b27
     ret = CL_EVERIFY;
0f53ea60
 
1d7f6b27
 finish:
b9c3525b
     if (NULL != hashctx) {
         cl_hash_destroy(hashctx);
     }
0f53ea60
 
b9c3525b
     if (NULL != regions) {
0f53ea60
         free(regions);
43adde3e
     }
afe940da
 
1d7f6b27
     // If we created the peinfo, then destroy it.  Otherwise we don't own it
     if (&_peinfo == peinfo) {
         cli_exe_info_destroy(peinfo);
     }
b9c3525b
     return ret;
0393aa56
 }
3cc632ad
 
dd25061b
 /* Print out either the MD5, SHA1, or SHA256 associated with the imphash or
  * the individual sections. Also, this function computes the hashes of each
  * section (sorted based on the RVAs of the sections) if hashes is non-NULL.
  *
  * If the section hashes are to be computed and returned, this function
  * allocates memory for the section hashes, and it's up to the caller to free
  * it.  hashes->sections will be initialized to NULL at the beginning of the
  * function, and if after the call it's value is non-NULL, the memory should be
  * freed.  Furthermore, if hashes->sections is non-NULL, the hashes can assume
  * to be valid regardless of the return code.
  *
  * Also, a few other notes:
  *  - If a section has a virtual size of zero, it's corresponding hash value
  *    will not be computed and the hash contents will be all zeroes.
  *  - If a section extends beyond the end of the file, the section data and
  *    length will be truncated, and the hash generated accordingly
  *  - If a section exists completely outside of the file, it won't be included
  *    in the list of sections, and nsections will be adjusted accordingly.
  */
 int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_section_t *hashes)
3cc632ad
 {
afe940da
     unsigned int i;
     struct cli_exe_info _peinfo;
     struct cli_exe_info *peinfo = &_peinfo;
3cc632ad
 
211edda0
     unsigned char *hash, *hashset[CLI_HASH_AVAIL_TYPES];
     int genhash[CLI_HASH_AVAIL_TYPES];
     int hlen = 0;
 
dd25061b
     if (hashes) {
         hashes->sections = NULL;
 
         if (class != CL_GENHASH_PE_CLASS_SECTION || type != 1) {
             cli_dbgmsg("`hashes` can only be populated with MD5 PE section data\n");
             return CL_EARG;
         }
     }
 
3cc632ad
     if (class >= CL_GENHASH_PE_CLASS_LAST)
         return CL_EARG;
 
dd25061b
     // TODO see if peinfo can be passed in (or lives in ctx or something) and
     // if so, use that to avoid having to re-parse the header
afe940da
     cli_exe_info_init(peinfo, 0);
3cc632ad
 
afe940da
     if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
5319602c
         cli_exe_info_destroy(peinfo);
3cc632ad
         return CL_EFORMAT;
     }
 
afe940da
     cli_qsort(peinfo->sections, peinfo->nsections, sizeof(*(peinfo->sections)), sort_sects);
3cc632ad
 
211edda0
     /* pick hashtypes to generate */
     memset(genhash, 0, sizeof(genhash));
634c8594
     memset(hashset, 0, sizeof(hashset));
288057e9
     switch (type) {
         case 1:
             genhash[CLI_HASH_MD5] = 1;
             hlen                  = hashlen[CLI_HASH_MD5];
             hash = hashset[CLI_HASH_MD5] = cli_calloc(hlen, sizeof(char));
             break;
         case 2:
             genhash[CLI_HASH_SHA1] = 1;
             hlen                   = hashlen[CLI_HASH_SHA1];
             hash = hashset[CLI_HASH_SHA1] = cli_calloc(hlen, sizeof(char));
             break;
         default:
             genhash[CLI_HASH_SHA256] = 1;
             hlen                     = hashlen[CLI_HASH_SHA256];
             hash = hashset[CLI_HASH_SHA256] = cli_calloc(hlen, sizeof(char));
             break;
211edda0
     }
3cc632ad
 
288057e9
     if (!hash) {
211edda0
         cli_errmsg("cli_genhash_pe: cli_malloc failed!\n");
afe940da
         cli_exe_info_destroy(peinfo);
211edda0
         return CL_EMEM;
     }
 
dd25061b
     if (hashes) {
         hashes->nsections = peinfo->nsections;
         hashes->sections  = cli_calloc(peinfo->nsections, sizeof(struct cli_section_hash));
 
         if (!(hashes->sections)) {
             cli_exe_info_destroy(peinfo);
             free(hash);
             return CL_EMEM;
         }
     }
 
211edda0
     if (class == CL_GENHASH_PE_CLASS_SECTION) {
dd25061b
         char *dstr;
3cc632ad
 
afe940da
         for (i = 0; i < peinfo->nsections; i++) {
3cc632ad
             /* Generate hashes */
afe940da
             if (cli_hashsect(*ctx->fmap, &peinfo->sections[i], hashset, genhash, genhash) == 1) {
dd25061b
                 if (cli_debug_flag) {
                     dstr = cli_str2hex((char *)hash, hlen);
                     cli_dbgmsg("Section{%u}: %u:%s\n", i, peinfo->sections[i].rsz, dstr ? (char *)dstr : "(NULL)");
                     if (dstr != NULL) {
                         free(dstr);
                     }
634c8594
                 }
dd25061b
                 if (hashes) {
                     memcpy(hashes->sections[i].md5, hash, sizeof(hashes->sections[i].md5));
                     hashes->sections[i].len = peinfo->sections[i].rsz;
                 }
             } else if (peinfo->sections[i].rsz) {
634c8594
                 cli_dbgmsg("Section{%u}: failed to generate hash for section\n", i);
dd25061b
             } else {
                 cli_dbgmsg("Section{%u}: section contains no data\n", i);
634c8594
             }
3cc632ad
         }
     } else if (class == CL_GENHASH_PE_CLASS_IMPTBL) {
dd25061b
         char *dstr;
211edda0
         uint32_t impsz = 0;
634c8594
         int ret;
211edda0
 
         /* Generate hash */
afe940da
         ret = hash_imptbl(ctx, hashset, &impsz, genhash, peinfo);
634c8594
         if (ret == CL_SUCCESS) {
dd25061b
             if (cli_debug_flag) {
                 dstr = cli_str2hex((char *)hash, hlen);
                 cli_dbgmsg("Imphash: %s:%u\n", dstr ? (char *)dstr : "(NULL)", impsz);
                 if (dstr != NULL) {
                     free(dstr);
                 }
634c8594
             }
         } else {
             cli_dbgmsg("Imphash: failed to generate hash for import table (%d)\n", ret);
         }
3cc632ad
     } else {
         cli_dbgmsg("cli_genhash_pe: unknown pe genhash class: %u\n", class);
     }
 
dd25061b
     free(hash);
afe940da
     cli_exe_info_destroy(peinfo);
3cc632ad
     return CL_SUCCESS;
 }