libclamav/pe.c
6307ca15
 /*
e7f5f537
  *  Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
52cddcbc
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
2023340a
  *
92088f91
  *  Authors: Alberto Wu, Tomasz Kojm, Andrew Williams
5eb34fac
  *
4524c398
  *  Acknowledgements: The header structures were based upon a PE format
6289eda8
  *                    analysis by B. Luevelsmeyer.
4524c398
  *
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>
20e3cfc0
 #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"
d5a733ef
 #include "entconv.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
 
72fd33c8
 #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
7ba310e6
 #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"
72fd33c8
 #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
72fd33c8
 #define PE_MAXIMPORTS 1024
c7145595
 // 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
72fd33c8
 #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) */
72fd33c8
 #define PEALIGN(o, a) (((a)) ? (((o) / (a)) * (a)) : (o))
 #define PESALIGN(o, a) (((a)) ? (((o) / (a) + ((o) % (a) != 0)) * (a)) : (o))
a9082ea2
 
7ba310e6
 // TODO Replace all of these with static inline functions
72fd33c8
 #define CLI_UNPSIZELIMITS(NAME, CHK)                           \
     if (cli_checklimits(NAME, ctx, (CHK), 0, 0) != CL_CLEAN) { \
7ba310e6
         cli_exe_info_destroy(peinfo);                          \
72fd33c8
         return CL_CLEAN;                                       \
     }
95e31dc7
 
c1103927
 #define CLI_UNPTEMP(NAME, FREEME)                                                                 \
     if (!(tempfile = cli_gentemp(ctx->sub_tmpdir))) {                                             \
         cli_exe_info_destroy(peinfo);                                                             \
         cli_multifree FREEME;                                                                     \
         return CL_EMEM;                                                                           \
     }                                                                                             \
     if ((ndesc = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) { \
         cli_dbgmsg(NAME ": Can't create file %s\n", tempfile);                                    \
         free(tempfile);                                                                           \
         cli_exe_info_destroy(peinfo);                                                             \
         cli_multifree FREEME;                                                                     \
         return CL_ECREAT;                                                                         \
72fd33c8
     }
95e31dc7
 
72fd33c8
 #define CLI_TMPUNLK()               \
     if (!ctx->engine->keeptmp) {    \
         if (cli_unlink(tempfile)) { \
             free(tempfile);         \
             return CL_EUNLINK;      \
         }                           \
     }
95e31dc7
 
16b28d07
 #ifdef HAVE__INTERNAL__SHA_COLLECT
72fd33c8
 #define SHA_OFF                \
     do {                       \
         ctx->sha_collect = -1; \
     } while (0)
 #define SHA_RESET                       \
     do {                                \
         ctx->sha_collect = sha_collect; \
     } while (0)
16b28d07
 #else
72fd33c8
 #define SHA_OFF \
     do {        \
     } while (0)
 #define SHA_RESET \
     do {          \
     } while (0)
16b28d07
 #endif
 
72fd33c8
 #define FSGCASE(NAME, FREESEC)                            \
     case 0: /* Unpacked and NOT rebuilt */                \
         cli_dbgmsg(NAME ": Successfully decompressed\n"); \
         close(ndesc);                                     \
         if (cli_unlink(tempfile)) {                       \
7ba310e6
             cli_exe_info_destroy(peinfo);                 \
72fd33c8
             free(tempfile);                               \
             FREESEC;                                      \
             return CL_EUNLINK;                            \
         }                                                 \
         free(tempfile);                                   \
         FREESEC;                                          \
         found       = 0;                                  \
         upx_success = 1;                                  \
         break; /* FSG ONLY! - scan raw data after upx block */
 
7ba310e6
 #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);                                    \
72fd33c8
         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;                                                             \
7ba310e6
             cli_exe_info_destroy(peinfo);                                                     \
72fd33c8
             lseek(ndesc, 0, SEEK_SET);                                                        \
             cli_dbgmsg("***** Scanning rebuilt PE file *****\n");                             \
             SHA_OFF;                                                                          \
9b9999d7
             if (cli_magic_scan_desc(ndesc, tempfile, ctx, NULL) == CL_VIRUS) {                \
72fd33c8
                 close(ndesc);                                                                 \
7ba310e6
                 SHA_RESET;                                                                    \
72fd33c8
                 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)) {                                                       \
7ba310e6
                 cli_exe_info_destroy(peinfo);                                                 \
72fd33c8
                 free(tempfile);                                                               \
                 cli_multifree FREEME;                                                         \
                 return CL_EUNLINK;                                                            \
             }                                                                                 \
             cli_multifree FREEME;                                                             \
             free(tempfile);                                                                   \
95e31dc7
     }
 
72fd33c8
 #define CLI_UNPRESULTS(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, (void)0, EXPR, GOOD, FREEME)
7ba310e6
 // 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).
72fd33c8
 #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
 
d7979d4f
 #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;
 };
 
72fd33c8
 #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];
 };
 
72fd33c8
 static void cli_multifree(void *f, ...)
 {
95e31dc7
     void *ff;
     va_list ap;
     free(f);
     va_start(ap, f);
72fd33c8
     while ((ff = va_arg(ap, void *))) free(ff);
95e31dc7
     va_end(ap);
 }
 
85310158
 struct vinfo_list {
     uint32_t rvas[16];
     unsigned int count;
 };
 
72fd33c8
 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;
72fd33c8
     if (++vlist->count == sizeof(vlist->rvas) / sizeof(vlist->rvas[0]))
017f3490
         return 1;
f018e8b1
     return 0;
 }
 
7ba310e6
 /* 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
 
72fd33c8
     if (rva < hdr_size) { /* Out of section EP - mapped to imagebase+rva */
017f3490
         if (rva >= fsize) {
72fd33c8
             *err = 1;
017f3490
             return 0;
         }
 
72fd33c8
         *err = 0;
017f3490
         return rva;
57866af1
     }
ac75a532
 
72fd33c8
     for (i = nos - 1; i >= 0; i--) {
7ba310e6
         if (shp[i].rsz && shp[i].rva <= rva && shp[i].rsz > (rva - shp[i].rva)) {
017f3490
             found = 1;
             break;
         }
ac75a532
     }
 
72fd33c8
     if (!found) {
017f3490
         *err = 1;
         return 0;
ac75a532
     }
 
7ba310e6
     ret  = (rva - shp[i].rva) + shp[i].raw;
33f89aa5
     *err = 0;
57866af1
     return ret;
ac75a532
 }
 
4524c398
 /*
7ba310e6
    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)
7ba310e6
    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
 */
7ba310e6
 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)
72fd33c8
 {
235464bb
     unsigned int err = 0;
     uint32_t type, type_offs, name, name_offs, lang, lang_offs;
72fd33c8
     const uint8_t *resdir, *type_entry, *name_entry, *lang_entry;
235464bb
     uint16_t type_cnt, name_cnt, lang_cnt;
7ba310e6
     uint32_t res_rva;
 
     if (NULL == peinfo || peinfo->ndatadirs < 3) {
         return;
     }
235464bb
 
df8dfda9
     if (0 != peinfo->offset) {
         cli_dbgmsg("findres: Assumption Violated: Looking for version info when peinfo->offset != 0\n");
     }
 
7ba310e6
     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)
50873c8a
         return;
235464bb
 
72fd33c8
     type_cnt   = (uint16_t)cli_readint16(resdir + 12);
     type_entry = resdir + 16;
     if (!(by_type >> 31)) {
50873c8a
         type_entry += type_cnt * 8;
72fd33c8
         type_cnt = (uint16_t)cli_readint16(resdir + 14);
235464bb
     }
 
72fd33c8
     while (type_cnt--) {
         if (!fmap_need_ptr_once(map, type_entry, 8))
50873c8a
             return;
72fd33c8
         type      = cli_readint32(type_entry);
         type_offs = cli_readint32(type_entry + 4);
         if (type == by_type && (type_offs >> 31)) {
50873c8a
             type_offs &= 0x7fffffff;
7ba310e6
             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)
50873c8a
                 return;
 
72fd33c8
             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)) {
50873c8a
                 name_entry += name_cnt * 8;
72fd33c8
                 name_cnt = (uint16_t)cli_readint16(resdir + 14);
50873c8a
             }
72fd33c8
             while (name_cnt--) {
                 if (!fmap_need_ptr_once(map, name_entry, 8))
50873c8a
                     return;
72fd33c8
                 name      = cli_readint32(name_entry);
                 name_offs = cli_readint32(name_entry + 4);
                 if ((by_name == 0xffffffff || name == by_name) && (name_offs >> 31)) {
50873c8a
                     name_offs &= 0x7fffffff;
7ba310e6
                     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)
50873c8a
                         return;
 
72fd33c8
                     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))
50873c8a
                             return;
72fd33c8
                         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))
50873c8a
                                 return;
                         }
                         lang_entry += 8;
                     }
                 }
                 name_entry += 8;
             }
             return; /* FIXME: unless we want to find ALL types */
         }
         type_entry += 8;
235464bb
     }
 }
 
7ba310e6
 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)
72fd33c8
 {
42d26ac9
     unsigned int err = 0, i;
f304dc68
     const uint8_t *resdir;
     const uint8_t *entry, *oentry;
42d26ac9
     uint16_t named, unnamed;
7ba310e6
     uint32_t rawaddr = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
42d26ac9
     uint32_t entries;
 
72fd33c8
     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
 
72fd33c8
     entries = /*named+*/ unnamed;
42d26ac9
     if (!entries)
72fd33c8
         return;
     rawaddr += named * 8; /* skip named */
42d26ac9
     /* this is just used in a heuristic detection, so don't give error on failure */
72fd33c8
     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++) {
50873c8a
         uint32_t id, offs;
         id = cli_readint32(entry);
         offs = cli_readint32(entry+4);
         if(offs>>31)
7ba310e6
             cli_parseres( base, base + (offs&0x7fffffff), srcfd, peinfo, fsize, level+1, type, maxres, stats);
50873c8a
         entry+=8;
42d26ac9
     }*/
72fd33c8
     for (i = 0; i < unnamed; i++, entry += 8) {
50873c8a
         uint32_t id, offs;
         if (stats->errors >= SWIZZ_MAXERRORS) {
             cli_dbgmsg("cli_parseres_special: resources broken, ignoring\n");
             return;
         }
72fd33c8
         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 */
             }
50873c8a
         }
         if (!type) {
72fd33c8
             /* if we are not interested in this type, skip */
             continue;
50873c8a
         }
72fd33c8
         offs = cli_readint32(entry + 4);
         if (offs >> 31)
7ba310e6
             cli_parseres_special(base, base + (offs & 0x7fffffff), map, peinfo, fsize, level + 1, type, maxres, stats);
50873c8a
         else {
72fd33c8
             offs    = cli_readint32(entry + 4);
7ba310e6
             rawaddr = cli_rawaddr(base + offs, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
72fd33c8
             if (!err && (resdir = fmap_need_off_once(map, rawaddr, 16))) {
                 uint32_t isz = cli_readint32(resdir + 4);
                 const uint8_t *str;
7ba310e6
                 rawaddr = cli_rawaddr(cli_readint32(resdir), peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
72fd33c8
                 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);
             }
50873c8a
         }
42d26ac9
     }
72fd33c8
     fmap_unneed_ptr(map, oentry, entries * 8);
42d26ac9
 }
 
72fd33c8
 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;
     }
 
72fd33c8
     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;
     }
 
72fd33c8
     if (foundhash[CLI_HASH_MD5] || foundwild[CLI_HASH_MD5])
b2e7c931
         cl_hash_data("md5", hashme, s->rsz, digest[CLI_HASH_MD5], NULL);
72fd33c8
     if (foundhash[CLI_HASH_SHA1] || foundwild[CLI_HASH_SHA1])
b2e7c931
         cl_sha1(hashme, s->rsz, digest[CLI_HASH_SHA1], NULL);
72fd33c8
     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 */
72fd33c8
 static int scan_pe_mdb(cli_ctx *ctx, struct cli_exe_section *exe_section)
e37613ad
 {
72fd33c8
     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;
72fd33c8
     int ret            = CL_CLEAN;
     unsigned char *md5 = NULL;
 
e37613ad
     /* pick hashtypes to generate */
72fd33c8
     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);
72fd33c8
         if (foundsize[type] || foundwild[type]) {
8cb85148
             hashset[type] = cli_malloc(hashlen[type]);
72fd33c8
             if (!hashset[type]) {
7ba310e6
                 cli_errmsg("scan_pe_mdb: cli_malloc failed!\n");
72fd33c8
                 for (; type > 0;)
e37613ad
                     free(hashset[--type]);
                 return CL_EMEM;
             }
72fd33c8
         } 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",
72fd33c8
                        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",
72fd33c8
                        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 */
72fd33c8
     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;
d7979d4f
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
e37613ad
             }
72fd33c8
         }
         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;
d7979d4f
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
e37613ad
             }
72fd33c8
         }
e37613ad
     }
 
3ca11170
 end:
72fd33c8
     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
 {
72fd33c8
     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
     }
 
72fd33c8
     if (name[0] == '\0')
         sprintf(name, "ord%u", ord);
8955ca5b
 
72fd33c8
     return cli_strdup(name);
8955ca5b
 }
 
81610c3e
 static int validate_impname(const char *name, uint32_t length, int dll)
 {
72fd33c8
     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
 }
 
7ba310e6
 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;
72fd33c8
     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)
7ba310e6
         thuoff = cli_rawaddr(image->u.OriginalFirstThunk, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
6e50356c
     if (err || thuoff == 0)
7ba310e6
         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
 
72fd33c8
 #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
 
7ba310e6
     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)) {
7ba310e6
                 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 */
72fd33c8
                     if ((buffer = fmap_need_off_once(map, offset + sizeof(uint16_t), MIN(PE_MAXNAMESIZE, fsize - offset))) != NULL) {
bcb4505e
                         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)) {
7ba310e6
                 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 */
72fd33c8
                     if ((buffer = fmap_need_off_once(map, offset + sizeof(uint16_t), MIN(PE_MAXNAMESIZE, fsize - offset))) != NULL) {
bcb4505e
                         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;
 }
 
7ba310e6
 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;
 
e9691519
     /* If the PE doesn't have an import table then skip it. This is an
      * uncommon case but can happen. */
7ba310e6
     if (peinfo->dirs[1].VirtualAddress == 0 || peinfo->dirs[1].Size == 0) {
e9691519
         cli_dbgmsg("scan_pe: import table data dir does not exist (skipping .imp scanning)\n");
8955ca5b
         return CL_SUCCESS;
     }
 
7ba310e6
     // 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;
     }
 
7ba310e6
     // TODO Add EC32 wrapper
     impdes = fmap_need_off(map, impoff, peinfo->dirs[1].Size);
72fd33c8
     if (impdes == NULL) {
211edda0
         cli_dbgmsg("scan_pe: failed to acquire fmap buffer\n");
48641f8b
         return CL_EREAD;
8955ca5b
     }
7ba310e6
     left = peinfo->dirs[1].Size;
8955ca5b
 
211edda0
     memset(hashctx, 0, sizeof(hashctx));
72fd33c8
     if (genhash[CLI_HASH_MD5]) {
211edda0
         hashctx[CLI_HASH_MD5] = cl_hash_init("md5");
634c8594
         if (hashctx[CLI_HASH_MD5] == NULL) {
7ba310e6
             fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
211edda0
             return CL_EMEM;
634c8594
         }
211edda0
     }
72fd33c8
     if (genhash[CLI_HASH_SHA1]) {
211edda0
         hashctx[CLI_HASH_SHA1] = cl_hash_init("sha1");
634c8594
         if (hashctx[CLI_HASH_SHA1] == NULL) {
7ba310e6
             fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
211edda0
             return CL_EMEM;
634c8594
         }
211edda0
     }
72fd33c8
     if (genhash[CLI_HASH_SHA256]) {
211edda0
         hashctx[CLI_HASH_SHA256] = cl_hash_init("sha256");
634c8594
         if (hashctx[CLI_HASH_SHA256] == NULL) {
7ba310e6
             fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
211edda0
             return CL_EMEM;
634c8594
         }
211edda0
     }
8955ca5b
 
     image = (struct pe_image_import_descriptor *)impdes;
72fd33c8
     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);
72fd33c8
         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 */
7ba310e6
         offset = cli_rawaddr(image->Name, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
72fd33c8
         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
         }
 
72fd33c8
         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;
         }
 
72fd33c8
         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;
         }
 
bcb4505e
         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 */
7ba310e6
         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++;
     }
 
72fd33c8
 hash_imptbl_end:
7ba310e6
     fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
72fd33c8
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
211edda0
         cl_finish_hash(hashctx[type], digest[type]);
634c8594
     return ret;
211edda0
 }
48641f8b
 
7ba310e6
 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 */
72fd33c8
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
211edda0
         genhash[type] = cli_hm_have_any(imp, type);
72fd33c8
         if (genhash[type]) {
634c8594
             hashset[type] = cli_malloc(hashlen[type]);
72fd33c8
             if (!hashset[type]) {
634c8594
                 cli_errmsg("scan_pe: cli_malloc failed!\n");
72fd33c8
                 for (; type > 0;)
634c8594
                     free(hashset[--type]);
                 return CL_EMEM;
             }
72fd33c8
         } 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));
72fd33c8
         if (!hashset[CLI_HASH_MD5]) {
211edda0
             cli_errmsg("scan_pe: cli_malloc failed!\n");
72fd33c8
             for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
634c8594
                 free(hashset[type]);
211edda0
             return CL_EMEM;
         }
     }
 
     /* Generate hashes */
7ba310e6
     ret = hash_imptbl(ctx, hashset, &impsz, genhash, peinfo);
634c8594
     if (ret != CL_SUCCESS) {
72fd33c8
         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
72fd33c8
         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 */
72fd33c8
     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;
d7979d4f
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
             }
634c8594
         }
72fd33c8
         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;
d7979d4f
                 else if (!SCAN_ALLMATCHES)
cbf5017a
                     break;
             }
72fd33c8
         }
32628142
     }
66c20d21
 
72fd33c8
     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);
 
72fd33c8
     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;
7ba310e6
     char epbuff[4096], *tempfile;
f98a68af
     uint32_t epsize;
4524c398
     size_t bytes;
7ba310e6
     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;
72fd33c8
     const char *src                                                                        = NULL;
     char *dest                                                                             = NULL;
7ba310e6
     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
72fd33c8
     int toval                   = 0;
     struct json_object *pe_json = NULL;
172c4dd2
 #endif
6307ca15
 
72fd33c8
     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;
     }
 
d7979d4f
     if (SCAN_COLLECT_METADATA) {
172c4dd2
         pe_json = get_pe_property(ctx);
20b45621
     }
172c4dd2
 #endif
7ba310e6
     map   = *ctx->fmap;
     fsize = map->len;
6307ca15
 
7ba310e6
     struct cli_exe_info _peinfo;
     struct cli_exe_info *peinfo = &_peinfo;
7a0143cf
 
8b722343
     uint32_t opts = CLI_PEHEADER_OPT_DBG_PRINT_INFO | CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS;
6307ca15
 
172c4dd2
 #if HAVE_JSON
7ba310e6
     if (SCAN_COLLECT_METADATA) {
         opts |= CLI_PEHEADER_OPT_COLLECT_JSON;
6307ca15
     }
172c4dd2
 #endif
f98a68af
 
7ba310e6
     if (DETECT_BROKEN_PE) {
         opts |= CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS;
f86b5ac1
     }
f98a68af
 
7ba310e6
     cli_exe_info_init(peinfo, 0);
a9082ea2
 
7ba310e6
     ret = cli_peheader(map, peinfo, opts, ctx);
636b7178
 
7ba310e6
     // 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
 
7ba310e6
 #define PE_HDR_PARSE_FAIL_CONSEQUENCE "won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n"
636b7178
 
7ba310e6
     if (CLI_PEHEADER_RET_BROKEN_PE == ret) {
72fd33c8
         if (DETECT_BROKEN_PE) {
7ba310e6
             // TODO Handle allmatch
72fd33c8
             ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
8b722343
             cli_exe_info_destroy(peinfo);
cbf5017a
             return ret;
f98a68af
         }
7ba310e6
         cli_dbgmsg("cli_scanpe: PE header appears broken - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
8b722343
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
 
7ba310e6
     } else if (CLI_PEHEADER_RET_JSON_TIMEOUT == ret) {
         cli_dbgmsg("cli_scanpe: JSON creation timed out - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
8b722343
         cli_exe_info_destroy(peinfo);
7ba310e6
         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);
8b722343
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
57866af1
     }
 
7ba310e6
     if (!peinfo->is_pe32plus) { /* PE */
72fd33c8
         if (DCONF & PE_CONF_UPACK)
7ba310e6
             upack = (EC16(peinfo->file_hdr.SizeOfOptionalHeader) == 0x148);
6307ca15
     }
7ba310e6
     for (i = 0; i < peinfo->nsections; i++) {
6307ca15
 
7ba310e6
         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 */
72fd33c8
             if ((DCONF & PE_CONF_MD5SECT) && ctx->engine->hm_mdb) {
7ba310e6
                 ret = scan_pe_mdb(ctx, &(peinfo->sections[i]));
f98a68af
                 if (ret != CL_CLEAN) {
7ba310e6
                     // TODO Handle allmatch
f98a68af
                     if (ret != CL_VIRUS)
7ba310e6
                         cli_errmsg("cli_scanpe: scan_pe_mdb failed: %s!\n", cl_strerror(ret));
f0f7f92f
 
f98a68af
                     cli_dbgmsg("------------------------------------\n");
7ba310e6
                     cli_exe_info_destroy(peinfo);
f98a68af
                     return ret;
                 }
             }
         }
6307ca15
     }
 
7ba310e6
     // 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
     }
 
7ba310e6
     epsize = fmap_readn(map, epbuff, peinfo->ep, 4096);
fa4f9e8b
 
59098a11
     /* Disasm scan disabled since it's now handled by the bytecode */
 
7ba310e6
     /* CLI_UNPTEMP("cli_scanpe: DISASM",(peinfo->sections,0)); */
59098a11
     /* if(disasmbuf((unsigned char*)epbuff, epsize, ndesc)) */
9b9999d7
     /*  ret = cli_scan_desc(ndesc, ctx, CL_TYPE_PE_DISASM, 1, NULL, AC_SCAN_VIR); */
59098a11
     /* close(ndesc); */
     /* if(ret == CL_VIRUS) { */
7ba310e6
     /*  cli_exe_info_destroy(peinfo); */
     /*  CLI_TMPUNLK(); */
     /*  free(tempfile); */
50873c8a
     /*  return ret; */
59098a11
     /* } */
7ba310e6
     /* CLI_TMPUNLK(); */
     /* free(tempfile); */
68a77450
 
7ba310e6
     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
     }
 
7ba310e6
     pedata.nsections = peinfo->nsections;
     pedata.ep        = peinfo->ep;
72fd33c8
     pedata.offset    = 0;
7ba310e6
     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");
7ba310e6
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_EMEM;
0df99607
     }
f98a68af
 
7ba310e6
     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:
7ba310e6
             // 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
7ba310e6
         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:
d7979d4f
                 if (SCAN_ALLMATCHES)
4adb74a4
                     break;
                 /* intentional fall-through */
             case CL_BREAK:
7ba310e6
                 cli_exe_info_destroy(peinfo);
4adb74a4
                 return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
48641f8b
             default:
7ba310e6
                 cli_exe_info_destroy(peinfo);
48641f8b
                 return ret;
4adb74a4
         }
8955ca5b
     }
41fd7c2f
     /* Attempt to detect some popular polymorphic viruses */
 
     /* W32.Parite.B */
7ba310e6
     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);
72fd33c8
         if (pt) {
f98a68af
             pt += 15;
72fd33c8
             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) {
d7979d4f
                         if (!SCAN_ALLMATCHES) {
7ba310e6
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
72fd33c8
                         } else
cbf5017a
                             viruses_found++;
                     } else {
7ba310e6
                         cli_exe_info_destroy(peinfo);
cbf5017a
                         return ret;
                     }
f98a68af
                 }
             }
         }
41fd7c2f
     }
 
343316ab
     /* Kriz */
7ba310e6
     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') {
72fd33c8
         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;
72fd33c8
         uint8_t *kzcode  = (uint8_t *)epbuff + 3;
         uint8_t kzdptr = 0xff, kzdsize = 0xff;
         int kzlen = 197, kzinitlen = 0xffff, kzxorlen = -1;
7ba310e6
         cli_dbgmsg("cli_scanpe: in kriz\n");
f98a68af
 
72fd33c8
         while (*kzstate != KZSTOP) {
f98a68af
             uint8_t op;
72fd33c8
             if (kzlen <= 6)
f98a68af
                 break;
 
             op = *kzcode++;
             kzlen--;
 
             switch (*kzstate) {
72fd33c8
                 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 */
 
7ba310e6
                                 cli_dbgmsg("cli_scanpe: kriz: using #%d as size counter\n", kzdsize);
72fd33c8
                             }
                             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
                     }
 
72fd33c8
                     break;
f98a68af
                 }
72fd33c8
                 case KZSCDELTA:
                     if (op == 0xe8 && (uint32_t)cli_readint32(kzcode) < 0xff) {
                         kzlen -= *kzcode + 4;
                         kzcode += *kzcode + 4;
                         kzstate++;
                     } else {
                         *kzstate = KZSTOP;
                     }
f98a68af
 
72fd33c8
                     break;
                 case KZSPDELTA:
                     if ((op & 0xf8) == 0x58 && (kzdptr = op - 0x58) != 4) {
                         kzstate++;
7ba310e6
                         cli_dbgmsg("cli_scanpe: kriz: using #%d as pointer\n", kzdptr);
72fd33c8
                     } else {
                         *kzstate = KZSTOP;
                     }
f98a68af
 
                     break;
72fd33c8
                 case KZSXORPRFX:
f98a68af
                     kzstate++;
72fd33c8
                     if (op == 0x3e)
                         break;
                 case KZSXOR:
                     if (op == 0x80 && *kzcode == kzdptr + 0xb0) {
                         kzxorlen = kzlen;
                         kzcode += +6;
                         kzlen -= +6;
                         kzstate++;
                     } else {
                         *kzstate = KZSTOP;
                     }
f98a68af
 
72fd33c8
                     break;
                 case KZSDDELTA:
                     if (op == kzdptr + 0x48)
                         kzstate++;
                     else
                         *kzstate = KZSTOP;
f98a68af
 
72fd33c8
                     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) {
7ba310e6
                                     cli_exe_info_destroy(peinfo);
72fd33c8
                                     return ret;
                                 } else
                                     viruses_found++;
                             } else {
7ba310e6
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
                             }
                         }
f98a68af
                     }
7ba310e6
                     cli_dbgmsg("cli_scanpe: kriz: loop out of bounds, corrupted sample?\n");
72fd33c8
                     kzstate++;
f98a68af
             }
         }
343316ab
     }
 
9ad59d16
     /* W32.Magistr.A/B */
7ba310e6
     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
 
7ba310e6
         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;
72fd33c8
             dam   = 1;
f98a68af
         }
9ad59d16
 
72fd33c8
         if (vsize >= 0x612c && rsize >= 0x612c && ((vsize & 0xff) == 0xec)) {
f98a68af
             int bw = rsize < 0x7000 ? rsize : 0x7000;
             const char *tbuff;
9ad59d16
 
7ba310e6
             if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
72fd33c8
                 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) {
d7979d4f
                             if (!SCAN_ALLMATCHES) {
7ba310e6
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
72fd33c8
                             } else
cbf5017a
                                 viruses_found++;
                         } else {
7ba310e6
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
                         }
f98a68af
                     }
                 }
             }
72fd33c8
         } else if (rsize >= 0x7000 && vsize >= 0x7000 && ((vsize & 0xff) == 0xed)) {
f98a68af
             int bw = rsize < 0x8000 ? rsize : 0x8000;
             const char *tbuff;
 
7ba310e6
             if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
72fd33c8
                 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) {
d7979d4f
                             if (!SCAN_ALLMATCHES) {
7ba310e6
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
72fd33c8
                             } else
cbf5017a
                                 viruses_found++;
                         } else {
7ba310e6
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
                         }
f98a68af
                     }
72fd33c8
                 }
f98a68af
             }
         }
9ad59d16
     }
 
be62f8ce
     /* W32.Polipos.A */
7ba310e6
     // 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;
 
7ba310e6
         if (peinfo->sections[0].rsz > CLI_MAX_ALLOCATION)
f98a68af
             break;
7ba310e6
         if (peinfo->sections[0].rsz < 5)
f98a68af
             break;
7ba310e6
         if (!(code = fmap_need_off_once(map, peinfo->sections[0].raw, peinfo->sections[0].rsz)))
f98a68af
             break;
 
7ba310e6
         for (i = 0; i < peinfo->sections[0].rsz - 5; i++) {
72fd33c8
             if ((uint8_t)(code[i] - 0xe8) > 1)
f98a68af
                 continue;
 
7ba310e6
             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;
 
72fd33c8
             if (xsjs % 128 == 0) {
                 if (xsjs == 1280)
f98a68af
                     break;
 
72fd33c8
                 if (!(jumps = (uint32_t *)cli_realloc2(jumps, (xsjs + 128) * sizeof(uint32_t)))) {
7ba310e6
                     cli_exe_info_destroy(peinfo);
f98a68af
                     return CL_EMEM;
                 }
             }
 
72fd33c8
             j = 0;
             for (; j < xsjs; j++) {
                 if (jumps[j] < jump)
f98a68af
                     continue;
72fd33c8
                 if (jumps[j] == jump) {
f98a68af
                     xsjs--;
                     break;
                 }
 
72fd33c8
                 jold     = jumps[j];
                 jumps[j] = jump;
                 jump     = jold;
f98a68af
             }
 
72fd33c8
             jumps[j] = jump;
f98a68af
             xsjs++;
         }
 
72fd33c8
         if (!xsjs)
f98a68af
             break;
 
7ba310e6
         cli_dbgmsg("cli_scanpe: Polipos: Checking %d xsect jump(s)\n", xsjs);
72fd33c8
         for (i = 0; i < xsjs; i++) {
             if (!(code = fmap_need_off_once(map, jumps[i], 9)))
f98a68af
                 continue;
 
72fd33c8
             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) {
d7979d4f
                         if (!SCAN_ALLMATCHES) {
cbf5017a
                             free(jumps);
7ba310e6
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
72fd33c8
                         } else
cbf5017a
                             viruses_found++;
                     } else {
                         free(jumps);
7ba310e6
                         cli_exe_info_destroy(peinfo);
cbf5017a
                         return ret;
                     }
f98a68af
                 }
             }
         }
 
         free(jumps);
         break;
be62f8ce
     }
 
42d26ac9
     /* Trojan.Swizzor.Gen */
7ba310e6
     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));
72fd33c8
             unsigned int m            = 1000;
             ret                       = CL_CLEAN;
f98a68af
 
             if (!stats) {
7ba310e6
                 cli_exe_info_destroy(peinfo);
cbf5017a
                 return CL_EMEM;
f98a68af
             } else {
7ba310e6
                 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) {
72fd33c8
                     ret = cli_append_virus(ctx, "Heuristics.Trojan.Swizzor.Gen");
cbf5017a
                     if (ret != CL_CLEAN) {
                         if (ret == CL_VIRUS) {
d7979d4f
                             if (!SCAN_ALLMATCHES) {
cbf5017a
                                 free(stats);
7ba310e6
                                 cli_exe_info_destroy(peinfo);
cbf5017a
                                 return ret;
72fd33c8
                             } else
cbf5017a
                                 viruses_found++;
                         } else {
                             free(stats);
7ba310e6
                             cli_exe_info_destroy(peinfo);
cbf5017a
                             return ret;
                         }
                     }
f98a68af
                 }
             }
         }
42d26ac9
     }
3fcb62ca
 
50177b5c
     /* !!!!!!!!!!!!!!    PACKERS START HERE    !!!!!!!!!!!!!! */
72fd33c8
     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;
72fd33c8
     if (DCONF & (PE_CONF_UPX | PE_CONF_FSG | PE_CONF_MEW)) {
7ba310e6
         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;
7ba310e6
                 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 */
72fd33c8
     if (found && (DCONF & PE_CONF_MEW) && epsize >= 16 && epbuff[0] == '\xe9') {
f98a68af
         uint32_t fileoffset;
         const char *tbuff;
4aa4a05c
 
df8dfda9
         // TODO shouldn't peinfo->ep be used here instead?  ep is the file
         // offset, vep is the entry point RVA
7ba310e6
         fileoffset = (peinfo->vep + cli_readint32(epbuff + 1) + 5);
f98a68af
         while (fileoffset == 0x154 || fileoffset == 0x158) {
             char *src;
             uint32_t offdiff, uselzma;
4aa4a05c
 
7ba310e6
             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
 
72fd33c8
             if (!(tbuff = fmap_need_off_once(map, fileoffset, 0xb0)))
f98a68af
                 break;
4aa4a05c
 
f98a68af
             if (fileoffset == 0x154)
7ba310e6
                 cli_dbgmsg("cli_scanpe: MEW: Win9x compatibility was set!\n");
f98a68af
             else
7ba310e6
                 cli_dbgmsg("cli_scanpe: MEW: Win9x compatibility was NOT set!\n");
4aa4a05c
 
7ba310e6
             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
 
7ba310e6
             offdiff -= peinfo->sections[i + 1].rva;
4aa4a05c
 
7ba310e6
             if (!peinfo->sections[i + 1].rsz) {
                 cli_dbgmsg("cli_scanpe: MEW: mew section is empty\n");
f98a68af
                 break;
             }
4aa4a05c
 
7ba310e6
             ssize = peinfo->sections[i + 1].vsz;
             dsize = peinfo->sections[i].vsz;
a5241d27
 
20e3cfc0
             /* Guard against integer overflow */
             if ((ssize + dsize < ssize) || (ssize + dsize < dsize)) {
7ba310e6
                 cli_dbgmsg("cli_scanpe: MEW: section size (%08x) + diff size (%08x) exceeds max size of unsigned int (%08x)\n", ssize, dsize, UINT32_MAX);
20e3cfc0
                 break;
             }
 
             /* Verify that offdiff does not exceed the ssize + sdiff */
             if (offdiff >= ssize + dsize) {
7ba310e6
                 cli_dbgmsg("cli_scanpe: MEW: offdiff (%08x) exceeds section size + diff size (%08x)\n", offdiff, ssize + dsize);
20e3cfc0
                 break;
             }
 
7ba310e6
             cli_dbgmsg("cli_scanpe: MEW: ssize %08x dsize %08x offdiff: %08x\n", ssize, dsize, offdiff);
4aa4a05c
 
7ba310e6
             CLI_UNPSIZELIMITS("cli_scanpe: MEW", MAX(ssize, dsize));
             CLI_UNPSIZELIMITS("cli_scanpe: MEW", MAX(ssize + dsize, peinfo->sections[i + 1].rsz));
f98a68af
 
7ba310e6
             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 */
72fd33c8
             if (!(src = cli_calloc(ssize + dsize, sizeof(char)))) {
7ba310e6
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
 
4524c398
             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);
7ba310e6
                 cli_exe_info_destroy(peinfo);
f98a68af
                 free(src);
                 return CL_EREAD;
             }
 
4524c398
             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') {
7ba310e6
                 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 */
                 }
 
7ba310e6
                 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
 
7ba310e6
             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
 
7ba310e6
     // TODO Why do we bail here
72fd33c8
     if (epsize < 168) {
7ba310e6
         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
4524c398
          *
f98a68af
          */
         /* upack 0.39-3s + sample 0151477*/
7ba310e6
         while (((upack && peinfo->nsections == 3) && /* 3 sections */
72fd33c8
                 ((
7ba310e6
                      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 */
72fd33c8
                      ) ||
                  /* based on 0297729 sample from aCaB */
7ba310e6
                  (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] */
72fd33c8
                   ))) ||
7ba310e6
                ((!upack && peinfo->nsections == 2) &&                                            /* 2 sections */
72fd33c8
                 ((                                                                               /* upack 0.39-2s */
                   epbuff[0] == '\x60' && epbuff[1] == '\xe8' && cli_readint32(epbuff + 2) == 0x9 /* pusha; call+9 */
                   ) ||
7ba310e6
                  (                                                                                                         /* 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 &&
72fd33c8
                   epbuff[5] == '\xad' && epbuff[6] == '\x8b' && epbuff[7] == '\xf8' /* loads;  mov edi, eax */
                   )))) {
f98a68af
             uint32_t vma, off;
72fd33c8
             int a, b, c;
f98a68af
 
7ba310e6
             cli_dbgmsg("cli_scanpe: Upack characteristics found.\n");
             a = peinfo->sections[0].vsz;
             b = peinfo->sections[1].vsz;
f98a68af
             if (upack) {
7ba310e6
                 cli_dbgmsg("cli_scanpe: Upack: var set\n");
f98a68af
 
7ba310e6
                 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 {
7ba310e6
                 cli_dbgmsg("cli_scanpe: Upack: var NOT set\n");
                 c     = peinfo->sections[1].rva;
                 ssize = peinfo->sections[1].uraw;
72fd33c8
                 off   = 0;
7ba310e6
                 vma   = peinfo->sections[1].rva - peinfo->sections[1].uraw;
f98a68af
             }
e8042398
 
72fd33c8
             dsize = a + b + c;
e8042398
 
7ba310e6
             CLI_UNPSIZELIMITS("cli_scanpe: Upack", MAX(MAX(dsize, ssize), peinfo->sections[1].ursz));
e8042398
 
7ba310e6
             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;
             }
72fd33c8
 
             if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
7ba310e6
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
e8042398
 
4524c398
             if (fmap_readn(map, dest, 0, ssize) != ssize) {
7ba310e6
                 cli_dbgmsg("cli_scanpe: Upack: Can't read raw data of section 0\n");
f98a68af
                 free(dest);
                 break;
             }
e8042398
 
72fd33c8
             if (upack)
7ba310e6
                 memmove(dest + peinfo->sections[2].rva - peinfo->sections[0].rva, dest, ssize);
9a25caf3
 
4524c398
             if (fmap_readn(map, dest + peinfo->sections[1].rva - off, peinfo->sections[1].uraw, peinfo->sections[1].ursz) != peinfo->sections[1].ursz) {
7ba310e6
                 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
 
7ba310e6
             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;
         }
     }
72fd33c8
 
     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 ! */
72fd33c8
 
7ba310e6
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz;
03a2d04a
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
5f1a932b
 
72fd33c8
         if (ssize <= 0x19 || dsize <= ssize) {
7ba310e6
             cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
72fd33c8
 
7ba310e6
         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;
         }
72fd33c8
 
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
         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;
         }
72fd33c8
 
7ba310e6
         dst = src + newedx - peinfo->sections[i + 1].rva;
72fd33c8
         if (!CLI_ISCONTAINED(src, ssize, dst, 32)) {
7ba310e6
             cli_dbgmsg("cli_scanpe: FSG: New stack out of bounds\n");
f98a68af
             break;
         }
5f1a932b
 
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
         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
 
72fd33c8
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
7ba310e6
             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
 
7ba310e6
         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
 
7ba310e6
     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
 
7ba310e6
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz;
5eb34fac
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
5eb34fac
 
72fd33c8
         if (ssize <= 0x19 || dsize <= ssize) {
7ba310e6
             cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
5eb34fac
 
7ba310e6
         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
 
7ba310e6
         gp = peinfo->sections[i + 1].raw - t;
5eb34fac
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", gp);
5eb34fac
 
72fd33c8
         if (!(support = fmap_need_off_once(map, t, gp))) {
7ba310e6
             cli_dbgmsg("cli_scanpe: Can't read %d bytes from padding area\n", gp);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
f628a181
 
7ba310e6
         /* 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
 
7ba310e6
         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
 
7ba310e6
         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 */
72fd33c8
         for (t = 12; t < gp - 4; t += 4) {
             uint32_t rva = cli_readint32(support + t);
5eb34fac
 
72fd33c8
             if (!rva)
f98a68af
                 break;
5eb34fac
 
7ba310e6
             rva -= EC32(peinfo->pe_opt.opt32.ImageBase) + 1;
f98a68af
             sectcnt++;
5eb34fac
 
72fd33c8
             if (rva % 0x1000)
7ba310e6
                 cli_dbgmsg("cli_scanpe: FSG: Original section %d is misaligned\n", sectcnt);
5eb34fac
 
7ba310e6
             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
 
72fd33c8
         if (t >= gp - 4 || cli_readint32(support + t)) {
f98a68af
             break;
         }
5eb34fac
 
72fd33c8
         if ((sections = (struct cli_exe_section *)cli_malloc((sectcnt + 1) * sizeof(struct cli_exe_section))) == NULL) {
7ba310e6
             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;
72fd33c8
         for (t = 1; t <= (uint32_t)sectcnt; t++)
7ba310e6
             sections[t].rva = cli_readint32(support + 8 + t * 4) - 1 - EC32(peinfo->pe_opt.opt32.ImageBase);
5eb34fac
 
7ba310e6
         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
 
72fd33c8
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             free(sections);
             return CL_EMEM;
         }
5eb34fac
 
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
     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;
7ba310e6
         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;
7ba310e6
         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
 
7ba310e6
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz;
5eb34fac
 
72fd33c8
         if (err) {
7ba310e6
             cli_dbgmsg("cli_scanpe: FSG: Support data out of padding area\n");
f98a68af
             break;
         }
5eb34fac
 
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
f628a181
 
72fd33c8
         if (ssize <= 0x19 || dsize <= ssize) {
7ba310e6
             cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_CLEAN;
         }
f628a181
 
7ba310e6
         gp = peinfo->sections[i + 1].raw - t;
f628a181
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: FSG", gp)
f628a181
 
72fd33c8
         if (!(support = fmap_need_off_once(map, t, gp))) {
7ba310e6
             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 */
72fd33c8
         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
 
7ba310e6
             rva = ((rva - 2) << 12) - EC32(peinfo->pe_opt.opt32.ImageBase);
f98a68af
             sectcnt++;
f628a181
 
7ba310e6
             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
 
72fd33c8
         if (t >= gp - 10 || cli_readint32(support + t + 6) != 2)
f98a68af
             break;
f628a181
 
72fd33c8
         if ((sections = (struct cli_exe_section *)cli_malloc((sectcnt + 1) * sizeof(struct cli_exe_section))) == NULL) {
7ba310e6
             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;
72fd33c8
         for (t = 0; t <= (uint32_t)sectcnt - 1; t++)
7ba310e6
             sections[t + 1].rva = (((support[t * 2] | (support[t * 2 + 1] << 8)) - 2) << 12) - EC32(peinfo->pe_opt.opt32.ImageBase);
f628a181
 
7ba310e6
         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
 
72fd33c8
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             free(sections);
             return CL_EMEM;
         }
f628a181
 
72fd33c8
         gp    = 0xda + 6 * (epbuff[16] == '\xe8');
7ba310e6
         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
 
7ba310e6
         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
 
72fd33c8
     if (found && (DCONF & PE_CONF_UPX)) {
7ba310e6
         ssize = peinfo->sections[i + 1].rsz;
         dsize = peinfo->sections[i].vsz + peinfo->sections[i + 1].vsz;
e0264472
 
4524c398
         /*
f98a68af
          * UPX support
          * we assume (i + 1) is UPX1
          */
f628a181
 
72fd33c8
         /* cli_dbgmsg("UPX: ssize %u dsize %u\n", ssize, dsize); */
f628a181
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: UPX", MAX(dsize, ssize));
10882c9f
 
72fd33c8
         if (ssize <= 0x19 || dsize <= ssize || dsize > CLI_MAX_ALLOCATION) {
7ba310e6
             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
 
7ba310e6
         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
 
72fd33c8
         if ((dest = (char *)cli_calloc(dsize + 8192, sizeof(char))) == NULL) {
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
f628a181
 
f98a68af
         /* try to detect UPX code */
72fd33c8
         if (cli_memstr(UPX_NRV2B, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2B, 24, epbuff + 0x69 + 8, 13)) {
7ba310e6
             cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2B decompression routine\n");
f98a68af
             upxfn = upx_inflate2b;
72fd33c8
         } else if (cli_memstr(UPX_NRV2D, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2D, 24, epbuff + 0x69 + 8, 13)) {
7ba310e6
             cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2D decompression routine\n");
f98a68af
             upxfn = upx_inflate2d;
72fd33c8
         } else if (cli_memstr(UPX_NRV2E, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2E, 24, epbuff + 0x69 + 8, 13)) {
7ba310e6
             cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2E decompression routine\n");
f98a68af
             upxfn = upx_inflate2e;
         }
e2ff1490
 
72fd33c8
         if (upxfn) {
7ba310e6
             int skew = cli_readint32(epbuff + 2) - EC32(peinfo->pe_opt.opt32.ImageBase) - peinfo->sections[i + 1].rva;
f98a68af
 
72fd33c8
             if (epbuff[1] != '\xbe' || skew <= 0 || skew > 0xfff) {
f98a68af
                 /* FIXME: legit skews?? */
72fd33c8
                 skew = 0;
f98a68af
             } else if ((unsigned int)skew > ssize) {
                 /* Ignore suggested skew larger than section size */
                 skew = 0;
             } else {
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: UPX1 seems skewed by %d bytes\n", skew);
f98a68af
             }
5f1a932b
 
f98a68af
             /* Try skewed first (skew may be zero) */
7ba310e6
             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 */
7ba310e6
             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
 
72fd33c8
             if (upx_success)
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed\n");
f98a68af
             else
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: Preferred decompressor failed\n");
f98a68af
         }
e37613ad
 
72fd33c8
         if (!upx_success && upxfn != upx_inflate2b) {
7ba310e6
             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
 
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: NRV2B decompressor failed\n");
f98a68af
             } else {
                 upx_success = 1;
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2B\n");
f98a68af
             }
         }
5f1a932b
 
72fd33c8
         if (!upx_success && upxfn != upx_inflate2d) {
7ba310e6
             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
 
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: NRV2D decompressor failed\n");
f98a68af
             } else {
                 upx_success = 1;
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2D\n");
f98a68af
             }
         }
5f1a932b
 
72fd33c8
         if (!upx_success && upxfn != upx_inflate2e) {
7ba310e6
             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;
7ba310e6
                 cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2E\n");
f98a68af
             }
         }
4a24fe30
 
72fd33c8
         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') {
7ba310e6
                 // TODO Add EC32
                 skew = cli_readint32(epbuff + 2) - peinfo->sections[i + 1].rva - peinfo->pe_opt.opt32.ImageBase;
72fd33c8
                 if (skew != 0x15)
f98a68af
                     skew = 0;
             }
5f1a932b
 
72fd33c8
             if (strictdsize <= dsize)
7ba310e6
                 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)) {
72fd33c8
             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') {
7ba310e6
                 // TODO Add EC32
                 skew = cli_readint32(epbuff + 2) - peinfo->sections[i + 1].rva - peinfo->pe_opt.opt32.ImageBase;
72fd33c8
                 if (skew != 0x15)
f98a68af
                     skew = 0;
             }
342e27a5
 
72fd33c8
             if (strictdsize <= dsize)
7ba310e6
                 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
 
72fd33c8
         if (!upx_success) {
7ba310e6
             cli_dbgmsg("cli_scanpe: UPX: All decompressors failed\n");
f98a68af
             free(dest);
         }
95e31dc7
     }
e0264472
 
72fd33c8
     if (upx_success) {
7ba310e6
         cli_exe_info_destroy(peinfo);
342e27a5
 
7ba310e6
         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
 
72fd33c8
         if ((unsigned int)write(ndesc, dest, dsize) != dsize) {
7ba310e6
             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) {
7ba310e6
             cli_dbgmsg("cli_scanpe: UPX/FSG: lseek() failed\n");
f98a68af
             close(ndesc);
7ba310e6
             SHA_RESET;
f98a68af
             CLI_TMPUNLK();
             free(tempfile);
             return CL_ESEEK;
         }
4a24fe30
 
72fd33c8
         if (ctx->engine->keeptmp)
7ba310e6
             cli_dbgmsg("cli_scanpe: UPX/FSG: Decompressed data saved in %s\n", tempfile);
f98a68af
 
         cli_dbgmsg("***** Scanning decompressed file *****\n");
         SHA_OFF;
9b9999d7
         if ((ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL)) == CL_VIRUS) {
f98a68af
             close(ndesc);
7ba310e6
             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
 
72fd33c8
     if (epsize < 200) {
7ba310e6
         cli_exe_info_destroy(peinfo);
f98a68af
         return CL_CLEAN;
20c3d44d
     }
85dd8460
 
95e31dc7
     found = 2;
 
7ba310e6
     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
     }
 
72fd33c8
     if (found && (DCONF & PE_CONF_PETITE)) {
7ba310e6
         cli_dbgmsg("cli_scanpe: Petite: v2.%d compression detected\n", found);
85dd8460
 
72fd33c8
         if (cli_readint32(epbuff + 0x80) == 0x163c988d) {
7ba310e6
             cli_dbgmsg("cli_scanpe: Petite: level zero compression is not supported yet\n");
f98a68af
         } else {
7ba310e6
             dsize = peinfo->max - peinfo->min;
85dd8460
 
7ba310e6
             CLI_UNPSIZELIMITS("cli_scanpe: Petite", dsize);
85dd8460
 
72fd33c8
             if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
7ba310e6
                 cli_dbgmsg("cli_scanpe: Petite: Can't allocate %d bytes\n", dsize);
                 cli_exe_info_destroy(peinfo);
f98a68af
                 return CL_EMEM;
             }
85dd8460
 
7ba310e6
             for (i = 0; i < peinfo->nsections; i++) {
                 if (peinfo->sections[i].raw) {
72fd33c8
                     unsigned int r_ret;
8aeedf3c
 
7ba310e6
                     if (!peinfo->sections[i].rsz)
72fd33c8
                         goto out_no_petite;
8aeedf3c
 
72fd33c8
                     if (!CLI_ISCONTAINED(dest, dsize,
7ba310e6
                                          dest + peinfo->sections[i].rva - peinfo->min,
                                          peinfo->sections[i].ursz))
72fd33c8
                         goto out_no_petite;
8aeedf3c
 
7ba310e6
                     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) {
72fd33c8
                     out_no_petite:
7ba310e6
                         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
 
7ba310e6
             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 */
 
7ba310e6
     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 &&
72fd33c8
         memcmp(epbuff + 4, "\xe8\x00\x00\x00\x00\x8b\x1c\x24\x83\xc3", 10) == 0) {
c2dfe70e
 
f98a68af
         char *spinned;
81030038
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: PEspin", fsize);
c2dfe70e
 
72fd33c8
         if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
7ba310e6
             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
 
4524c398
         if (fmap_readn(map, spinned, 0, fsize) != fsize) {
7ba310e6
             cli_dbgmsg("cli_scanpe: PESpin: Can't read %lu bytes\n", (unsigned long)fsize);
f98a68af
             free(spinned);
7ba310e6
             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
 
7ba310e6
         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 */
7ba310e6
     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) &&
72fd33c8
             !memcmp(epbuff + 0x26, "\x8D\x3A\x8B\xF7\x33\xC0\xEB\x04\x90\xEB\x01\xC2\xAC", 13) &&
f98a68af
             ((uint8_t)epbuff[0x13] == 0xB9) &&
72fd33c8
             ((uint16_t)(cli_readint16(epbuff + 0x18)) == 0xE981) &&
             !memcmp(epbuff + 0x1e, "\x8B\xD5\x81\xC2", 4)) {
d0b31fa3
 
f98a68af
             offset = 0;
72fd33c8
             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) &&
72fd33c8
             !memcmp(epbuff + 0x17, "\xe8\x00\x00\x00\x00\x5d\x81\xed", 8) &&
f98a68af
             ((uint8_t)epbuff[0x23] == 0xB9)) {
d0b31fa3
 
f98a68af
             offset = 0x10;
72fd33c8
             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 */
72fd33c8
         if (!ecx && !memcmp(epbuff, "\x60\xe8\x00\x00\x00\x00\x5d\x81\xed", 9) &&
f98a68af
             ((uint8_t)epbuff[0xd] == 0xb9) &&
72fd33c8
             ((uint16_t)cli_readint16(epbuff + 0x12) == 0xbd8d) &&
             !memcmp(epbuff + 0x18, "\x8b\xf7\xac", 3)) {
d0b31fa3
 
f98a68af
             offset = -0x18;
72fd33c8
             if (0x66 - cli_readint32(epbuff + 0x9) + cli_readint32(epbuff + 0x14) == 0xae)
                 ecx = cli_readint32(epbuff + 0xe);
f98a68af
         }
d0b31fa3
 
f98a68af
         if (ecx > 0x800 && ecx < 0x2000 &&
72fd33c8
             !memcmp(epbuff + 0x63 + offset, "\xaa\xe2\xcc", 3) &&
7ba310e6
             (fsize >= peinfo->sections[peinfo->nsections - 1].raw + 0xC6 + ecx + offset)) {
822930fc
 
f98a68af
             char *spinned;
822930fc
 
72fd33c8
             if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
7ba310e6
                 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
 
4524c398
             if (fmap_readn(map, spinned, 0, fsize) != fsize) {
7ba310e6
                 cli_dbgmsg("cli_scanpe: yC: Can't read %lu bytes\n", (unsigned long)fsize);
f98a68af
                 free(spinned);
7ba310e6
                 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;
72fd33c8
                 const char *yc_unp_virname      = NULL;
771c2309
 
                 if (ctx->virname)
                     yc_unp_virname = ctx->virname[0];
 
7ba310e6
                 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
 
d7979d4f
                 if (SCAN_ALLMATCHES && yc_unp_num_viruses != ctx->num_viruses) {
7ba310e6
                     cli_exe_info_destroy(peinfo);
771c2309
                     return CL_VIRUS;
72fd33c8
                 } else if (ctx->virname && yc_unp_virname != ctx->virname[0]) {
7ba310e6
                     cli_exe_info_destroy(peinfo);
771c2309
                     return CL_VIRUS;
                 }
72fd33c8
             } while (0);
f98a68af
         }
822930fc
     }
 
60cd49c9
     /* WWPack */
 
7ba310e6
     while ((DCONF & PE_CONF_WWPACK) && peinfo->nsections > 1 &&
            peinfo->vep == peinfo->sections[peinfo->nsections - 1].rva &&
72fd33c8
            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) {
7ba310e6
         uint32_t head = peinfo->sections[peinfo->nsections - 1].raw;
f377e052
         uint8_t *packer;
f98a68af
         char *src;
60cd49c9
 
f98a68af
         ssize = 0;
72fd33c8
         for (i = 0;; i++) {
7ba310e6
             if (peinfo->sections[i].raw < head)
                 head = peinfo->sections[i].raw;
f98a68af
 
7ba310e6
             if (i + 1 == peinfo->nsections)
f98a68af
                 break;
 
7ba310e6
             if (ssize < peinfo->sections[i].rva + peinfo->sections[i].vsz)
                 ssize = peinfo->sections[i].rva + peinfo->sections[i].vsz;
f98a68af
         }
 
72fd33c8
         if (!head || !ssize || head > ssize)
f98a68af
             break;
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: WWPack", ssize);
60cd49c9
 
72fd33c8
         if (!(src = (char *)cli_calloc(ssize, sizeof(char)))) {
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
 
4524c398
         if (fmap_readn(map, src, 0, head) != head) {
7ba310e6
             cli_dbgmsg("cli_scanpe: WWPack: Can't read %d bytes from headers\n", head);
f98a68af
             free(src);
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EREAD;
         }
 
7ba310e6
         for (i = 0; i < (unsigned int)peinfo->nsections - 1; i++) {
             if (!peinfo->sections[i].rsz)
f98a68af
                 continue;
 
7ba310e6
             if (!CLI_ISCONTAINED(src, ssize, src + peinfo->sections[i].rva, peinfo->sections[i].rsz))
f98a68af
                 break;
 
4524c398
             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
 
7ba310e6
         if (i + 1 != peinfo->nsections) {
             cli_dbgmsg("cli_scanpe: WWpack: Probably hacked/damaged file.\n");
f377e052
             free(src);
             break;
         }
f98a68af
 
7ba310e6
         if ((packer = (uint8_t *)cli_calloc(peinfo->sections[peinfo->nsections - 1].rsz, sizeof(char))) == NULL) {
f98a68af
             free(src);
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
 
4524c398
         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) {
7ba310e6
             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);
7ba310e6
             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
 
7ba310e6
         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 */
72fd33c8
     while ((DCONF & PE_CONF_ASPACK) &&
7ba310e6
            ((peinfo->ep + ASPACK_EP_OFFSET_212 < fsize) ||
             (peinfo->ep + ASPACK_EP_OFFSET_OTHER < fsize) ||
             (peinfo->ep + ASPACK_EP_OFFSET_242 < fsize)) &&
72fd33c8
            (!memcmp(epbuff, "\x60\xe8\x03\x00\x00\x00\xe9\xeb", 8))) {
f98a68af
         char *src;
9dcc0f7a
         aspack_version_t aspack_ver = ASPACK_VER_NONE;
2f73b977
 
72fd33c8
         if (epsize < 0x3bf)
f98a68af
             break;
72fd33c8
 
         if (0 == memcmp(epbuff + ASPACK_EPBUFF_OFFSET_212, "\x68\x00\x00\x00\x00\xc3", 6)) {
9dcc0f7a
             aspack_ver = ASPACK_VER_212;
72fd33c8
         } else if (0 == memcmp(epbuff + ASPACK_EPBUFF_OFFSET_OTHER, "\x68\x00\x00\x00\x00\xc3", 6)) {
9dcc0f7a
             aspack_ver = ASPACK_VER_OTHER;
72fd33c8
         } else if (0 == memcmp(epbuff + ASPACK_EPBUFF_OFFSET_242, "\x68\x00\x00\x00\x00\xc3", 6)) {
9dcc0f7a
             aspack_ver = ASPACK_VER_242;
         } else {
             break;
         }
f98a68af
         ssize = 0;
7ba310e6
         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
 
72fd33c8
         if (!ssize)
f98a68af
             break;
95e31dc7
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: Aspack", ssize);
95e31dc7
 
72fd33c8
         if (!(src = (char *)cli_calloc(ssize, sizeof(char)))) {
7ba310e6
             cli_exe_info_destroy(peinfo);
f98a68af
             return CL_EMEM;
         }
7ba310e6
         for (i = 0; i < (unsigned int)peinfo->nsections; i++) {
             if (!peinfo->sections[i].rsz)
f98a68af
                 continue;
 
7ba310e6
             if (!CLI_ISCONTAINED(src, ssize, src + peinfo->sections[i].rva, peinfo->sections[i].rsz))
f98a68af
                 break;
 
4524c398
             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
 
7ba310e6
         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
 
7ba310e6
         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) {
7ba310e6
         uint32_t eprva = peinfo->vep;
         uint32_t start_of_stuff, rep = peinfo->ep;
f98a68af
         unsigned int nowinldr;
         const char *nbuff;
 
72fd33c8
         src = epbuff;
         if (*epbuff == '\xe9') { /* bitched headers */
7ba310e6
             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
 
72fd33c8
         nowinldr = 0x54 - cli_readint32(src + 17);
7ba310e6
         cli_dbgmsg("cli_scanpe: NsPack: Found *start_of_stuff @delta-%x\n", nowinldr);
81030038
 
72fd33c8
         if (!(nbuff = fmap_need_off_once(map, rep - nowinldr, 4)))
f98a68af
             break;
81030038
 
72fd33c8
         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)) {
72fd33c8
             start_of_stuff += 4; /* FIXME: more to do */
             src += 4;
f98a68af
         }
81030038
 
72fd33c8
         ssize = cli_readint32(src + 5) | 0xff;
         dsize = cli_readint32(src + 9);
f98a68af
 
7ba310e6
         CLI_UNPSIZELIMITS("cli_scanpe: NsPack", MAX(ssize, dsize));
f98a68af
 
7ba310e6
         if (!ssize || !dsize || dsize != peinfo->sections[0].vsz)
f98a68af
             break;
 
72fd33c8
         if (!(dest = cli_malloc(dsize))) {
7ba310e6
             cli_errmsg("cli_scanpe: NsPack: Unable to allocate memory for dest %u\n", dsize);
f98a68af
             break;
         }
         /* memset(dest, 0xfc, dsize); */
 
72fd33c8
         if (!(src = fmap_need_off(map, start_of_stuff, ssize))) {
f98a68af
             free(dest);
             break;
         }
         /* memset(src, 0x00, ssize); */
 
72fd33c8
         eprva += 0x27a;
7ba310e6
         if (!(rep = cli_rawaddr(eprva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err) {
72fd33c8
             free(dest);
             break;
f98a68af
         }
 
72fd33c8
         if (!(nbuff = fmap_need_off_once(map, rep, 5))) {
             free(dest);
             break;
f98a68af
         }
 
         fmap_unneed_off(map, start_of_stuff, ssize);
72fd33c8
         eprva = eprva + 5 + cli_readint32(nbuff + 1);
7ba310e6
         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
 
7ba310e6
         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
 
7ba310e6
     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) {
72fd33c8
         case CL_VIRUS:
7ba310e6
             cli_exe_info_destroy(peinfo);
72fd33c8
             cli_bytecode_context_destroy(bc_ctx);
7ba310e6
             // TODO Handle allmatch
72fd33c8
             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) {
7ba310e6
                 CLI_UNPRESULTS("cli_scanpe: bytecode PE hook", 1, 1, (0));
72fd33c8
             }
f98a68af
 
72fd33c8
             break;
         default:
             cli_bytecode_context_destroy(bc_ctx);
3ae0a76d
     }
ab636570
 
7ba310e6
     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
 
d7979d4f
     if (SCAN_ALLMATCHES && viruses_found)
f98a68af
         return CL_VIRUS;
 
342e27a5
     return CL_CLEAN;
6307ca15
 }
cdbf8c8e
 
7ba310e6
 int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo)
cdbf8c8e
 {
7ba310e6
     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
8b722343
  *                               info
7ba310e6
  *              - 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.
8b722343
  *              - 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.
7ba310e6
  * @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.
df8dfda9
  *         The caller MUST destroy peinfo, regardless of what this function
  *         returns.
7ba310e6
  *
  * 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.
  *
8b722343
  * 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.
  *
7ba310e6
  * 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;
df8dfda9
     struct pe_image_section_hdr *section_hdrs = NULL;
7ba310e6
     unsigned int i, j, section_pe_idx;
     unsigned int err;
     uint32_t salign, falign;
017f3490
     size_t fsize;
     ssize_t at;
7ba310e6
     uint32_t is_dll = 0;
     uint32_t is_exe = 0;
     int native      = 0;
4524c398
     size_t read;
 
df8dfda9
     int ret = CLI_PEHEADER_RET_GENERIC_ERROR;
7ba310e6
 #if HAVE_JSON
     int toval                   = 0;
     struct json_object *pe_json = NULL;
     char jsonbuf[128];
 #endif
cdbf8c8e
 
7ba310e6
     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");
df8dfda9
         goto done;
7ba310e6
     }
 
 #if HAVE_JSON
     if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
         pe_json = get_pe_property(ctx);
     }
 #endif
cdbf8c8e
 
048d7677
     fsize = map->len - peinfo->offset;
72fd33c8
     if (fmap_readn(map, &e_magic, peinfo->offset, sizeof(e_magic)) != sizeof(e_magic)) {
7ba310e6
         cli_dbgmsg("cli_peheader: Can't read DOS signature\n");
df8dfda9
         goto done;
cdbf8c8e
     }
 
72fd33c8
     if (EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD) {
7ba310e6
         cli_dbgmsg("cli_peheader: Invalid DOS signature\n");
df8dfda9
         goto done;
cdbf8c8e
     }
 
7ba310e6
     if (fmap_readn(map, &(peinfo->e_lfanew), peinfo->offset + 58 + sizeof(e_magic), sizeof(peinfo->e_lfanew)) != sizeof(peinfo->e_lfanew)) {
017f3490
         /* truncated header? */
7ba310e6
         cli_dbgmsg("cli_peheader: Unable to read e_lfanew - truncated header?\n");
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
cdbf8c8e
     }
 
7ba310e6
     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");
df8dfda9
         goto done;
cdbf8c8e
     }
 
7ba310e6
     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");
df8dfda9
         goto done;
cdbf8c8e
     }
 
7ba310e6
     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");
df8dfda9
         goto done;
cdbf8c8e
     }
 
7ba310e6
     if (EC16(file_hdr->Characteristics) & 0x2000) {
667a4b35
 
7ba310e6
 #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
     }
 
7ba310e6
     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
     }
 
7ba310e6
     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
         }
 
7ba310e6
         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);
             }
         }
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
7ba310e6
     }
 
     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);
df8dfda9
         cli_dbgmsg("TimeDateStamp: %s", cli_ctime(&timestamp, timestr, sizeof(timestr)));
7ba310e6
         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);
df8dfda9
         /* NOTE: the TimeDateStamp value will look like "Wed Dec 31 19:00:00 1969\n" */
7ba310e6
         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
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
7ba310e6
     }
 
     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");
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
7ba310e6
     }
     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
df8dfda9
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
7ba310e6
         }
 
         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");
df8dfda9
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
7ba310e6
         }
 
         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
         }
7ba310e6
 #endif
017f3490
 
21bf52c0
     } else { /* PE */
7ba310e6
         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
         }
 
7ba310e6
 #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
     }
 
7ba310e6
     salign = (peinfo->is_pe32plus) ? EC32(opt64->SectionAlignment) : EC32(opt32->SectionAlignment);
     falign = (peinfo->is_pe32plus) ? EC32(opt64->FileAlignment) : EC32(opt32->FileAlignment);
57866af1
 
7ba310e6
     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
 
7ba310e6
     if (!native && (!salign || (salign % 0x1000))) {
         cli_dbgmsg("cli_peheader: Bad section alignment\n");
         if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
df8dfda9
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
7ba310e6
         }
     }
cdbf8c8e
 
7ba310e6
     if (!native && (!falign || (falign % 0x200))) {
         cli_dbgmsg("cli_peheader: Bad file alignment\n");
         if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
df8dfda9
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
7ba310e6
         }
cdbf8c8e
     }
 
7ba310e6
     // 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
 
7ba310e6
     if (peinfo->ndatadirs > 0x10) {
         cli_dbgmsg("cli_peheader: Encountered NumberOfRvaAndSizes > 16 (suspicious)\n");
cdbf8c8e
     }
 
7ba310e6
     // 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");
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
7ba310e6
     }
 
     read = fmap_readn(map, peinfo->dirs, at, data_dirs_size);
4524c398
     if ((read == (size_t)-1) || (read != data_dirs_size)) {
7ba310e6
         cli_dbgmsg("cli_peheader: Can't read optional file header data dirs\n");
df8dfda9
         goto done;
7ba310e6
     }
     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");
df8dfda9
         goto done;
7ba310e6
     }
 
     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");
df8dfda9
         goto done;
7ba310e6
     }
 
     read = fmap_readn(map, section_hdrs, at, peinfo->nsections * sizeof(struct pe_image_section_hdr));
4524c398
     if ((read == (size_t)-1) || (read != peinfo->nsections * sizeof(struct pe_image_section_hdr))) {
7ba310e6
         cli_dbgmsg("cli_peheader: Can't read section header - possibly broken PE file\n");
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
21bf52c0
     }
72fd33c8
     at += sizeof(struct pe_image_section_hdr) * peinfo->nsections;
cdbf8c8e
 
7ba310e6
     // 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?
 
72fd33c8
     for (i = 0; falign != 0x200 && i < peinfo->nsections; i++) {
017f3490
         /* file alignment fallback mode - blah */
7ba310e6
         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
 
7ba310e6
     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);
 
8b722343
         /* First, if a section exists totally outside of a file, remove the
          * section from the list or zero out it's size. */
7ba310e6
         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);
 
8b722343
                 if (opts & CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS) {
                     if (peinfo->nsections == 1) {
                         ret = CLI_PEHEADER_RET_BROKEN_PE;
                         goto done;
                     }
7ba310e6
 
8b722343
                     for (j = i; j < peinfo->nsections - 1; j++)
                         memcpy(&(peinfo->sections[j]), &(peinfo->sections[j + 1]), sizeof(struct cli_exe_section));
21bf52c0
 
8b722343
                     for (j = i; j < peinfo->nsections - 1; j++)
                         memcpy(&section_hdrs[j], &section_hdrs[j + 1], sizeof(struct pe_image_section_hdr));
7ba310e6
 
8b722343
                     peinfo->nsections--;
21bf52c0
 
8b722343
                     // Adjust i since we removed a section and continue on
                     i--;
                     continue;
 
                 } else {
                     section->rsz  = 0;
                     section->ursz = 0;
                 }
             } else {
7ba310e6
 
8b722343
                 /* 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;
                 }
7ba310e6
             }
         }
 
         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) {
df8dfda9
                 ret = CLI_PEHEADER_RET_JSON_TIMEOUT;
                 goto done;
7ba310e6
             }
         }
 #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) {
df8dfda9
                 ret = CLI_PEHEADER_RET_BROKEN_PE;
                 goto done;
7ba310e6
             }
         }
 
         // 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");
df8dfda9
             ret = CLI_PEHEADER_RET_BROKEN_PE;
             goto done;
7ba310e6
         }
 
         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) {
df8dfda9
                     ret = CLI_PEHEADER_RET_BROKEN_PE;
                     goto done;
7ba310e6
                 }
             }
 
             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) {
df8dfda9
                     ret = CLI_PEHEADER_RET_BROKEN_PE;
                     goto done;
7ba310e6
                 }
             }
 
             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
 
7ba310e6
     peinfo->overlay_size = fsize - peinfo->overlay_start;
 
     // NOTE: For DLLs the entrypoint is likely to be zero
df8dfda9
     // TODO Should this offset include peinfo->offset?
7ba310e6
     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");
df8dfda9
         ret = CLI_PEHEADER_RET_BROKEN_PE;
         goto done;
7ba310e6
     }
 
 #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) {
df8dfda9
             ret = CLI_PEHEADER_RET_JSON_TIMEOUT;
             goto done;
7ba310e6
         }
d2ba6f98
     }
7ba310e6
 #endif
667a4b35
 
7ba310e6
     if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
         cli_dbgmsg("EntryPoint offset: 0x%x (%d)\n", peinfo->ep, peinfo->ep);
cdbf8c8e
     }
 
7ba310e6
     if (is_dll || peinfo->ndatadirs < 3 || !peinfo->dirs[2].Size)
017f3490
         peinfo->res_addr = 0;
453d8180
     else
7ba310e6
         peinfo->res_addr = EC32(peinfo->dirs[2].VirtualAddress);
453d8180
 
7ba310e6
     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
 
df8dfda9
         // 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));
7ba310e6
         findres(0x10, 0xffffffff, map, peinfo, versioninfo_cb, &vlist);
72fd33c8
         if (!vlist.count)
017f3490
             break; /* No version_information */
d2ba6f98
 
72fd33c8
         if (cli_hashset_init(&peinfo->vinfo, 32, 80)) {
017f3490
             cli_errmsg("cli_peheader: Unable to init vinfo hashset\n");
df8dfda9
             goto done;
017f3490
         }
d2ba6f98
 
017f3490
         err = 0;
72fd33c8
         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);
7ba310e6
             rva = cli_rawaddr(vlist.rvas[i], peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
72fd33c8
             if (err)
017f3490
                 continue;
 
72fd33c8
             if (!(vptr = fmap_need_off_once(map, rva, 16)))
017f3490
                 continue;
 
             baseptr = vptr - rva;
             /* parse resource */
72fd33c8
             rva    = cli_readint32(vptr);     /* ptr to version_info */
             res_sz = cli_readint32(vptr + 4); /* sizeof(resource) */
7ba310e6
             rva    = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
72fd33c8
             if (err)
017f3490
                 continue;
72fd33c8
             if (!(vptr = fmap_need_off_once(map, rva, res_sz)))
017f3490
                 continue;
72fd33c8
 
             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;
72fd33c8
                 if (vinfo_sz > res_sz)
017f3490
                     break; /* the content is larger than the container */
 
                 vinfo_val_sz >>= 16;
72fd33c8
                 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
 
72fd33c8
                 while (vinfo_sz > 6) { /* look for stringfileinfo - NOT RESUMABLE (expecting at most one stringfileinfo) */
017f3490
                     uint32_t sfi_sz = cli_readint32(vptr) & 0xffff;
d2ba6f98
 
72fd33c8
                     if (sfi_sz > vinfo_sz)
017f3490
                         break; /* the content is larger than the container */
d2ba6f98
 
72fd33c8
                     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
 
72fd33c8
                     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
 
72fd33c8
                     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;
72fd33c8
                         uint32_t next_sfi_sz     = sfi_sz - st_sz;
d2ba6f98
 
72fd33c8
                         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;
 
72fd33c8
                         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;
72fd33c8
                             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 */
72fd33c8
                                 st_sz  = 0;
017f3490
                                 sfi_sz = 0;
                                 break; /* force a hard fail */
                             }
 
                             /* ~wcstrlen(key) */
72fd33c8
                             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;
 
72fd33c8
                             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;
 
72fd33c8
                             if (s_val_sz <= 2) {
017f3490
                                 /* skip unset value */
                                 vptr += s_sz;
                                 st_sz -= s_sz;
                                 continue;
                             }
 
72fd33c8
                             if (cli_hashset_addkey(&peinfo->vinfo, (uint32_t)(vptr - baseptr + 6))) {
017f3490
                                 cli_errmsg("cli_peheader: Unable to add rva to vinfo hashset\n");
df8dfda9
                                 goto done;
017f3490
                             }
 
72fd33c8
                             if (cli_debug_flag) {
017f3490
                                 char *k, *v, *s;
 
                                 /* FIXME: skip too long strings */
72fd33c8
                                 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 */
72fd33c8
                         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 */
72fd33c8
         }     /* enum all version_information res - RESUMABLE */
017f3490
         break;
85310158
     } /* while(dirs[2].Size) */
d2ba6f98
 
7ba310e6
     // Do final preperations for peinfo to be passed back
     peinfo->is_dll = is_dll;
 
df8dfda9
     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
 
7ba310e6
 // 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.
72fd33c8
 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);
 }
 
4ef79cfc
 /* Check the given PE file for an authenticode signature and return CL_CLEAN if
64ecd109
  * 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
4524c398
  *   catalog file that was loaded in via a -d
  *
92088f91
  * If peinfo is NULL, one will be created internally and used
4524c398
  *
92088f91
  * CL_VERIFIED will be returned if the file was whitelisted based on its
64ecd109
  * signature.  CL_VIRUS will be returned if the file was blacklisted based on
e8169c70
  * its signature.  Otherwise, a cl_error_t error value will be returned.
4524c398
  *
e8169c70
  * 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)
72fd33c8
 {
7ba310e6
     size_t at;
     unsigned int i, hlen;
0393aa56
     size_t fsize;
72fd33c8
     fmap_t *map   = *ctx->fmap;
     void *hashctx = NULL;
18a813af
     struct pe_certificate_hdr cert_hdr;
64ecd109
     struct cli_mapped_region *regions = NULL;
c7145595
     unsigned int nregions;
64ecd109
     cl_error_t ret = CL_EVERIFY;
     uint8_t authsha1[SHA1_HASH_SIZE];
     uint32_t sec_dir_offset;
     uint32_t sec_dir_size;
7ba310e6
     struct cli_exe_info _peinfo;
 
e8169c70
     // 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;
64ecd109
 
92088f91
     // 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
 
92088f91
         if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
8b722343
             cli_exe_info_destroy(peinfo);
92088f91
             return CL_EFORMAT;
         }
18a813af
     }
0393aa56
 
7ba310e6
     sec_dir_offset = EC32(peinfo->dirs[4].VirtualAddress);
     sec_dir_size   = EC32(peinfo->dirs[4].Size);
64ecd109
 
18a813af
     // 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
64ecd109
     // 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)) {
92088f91
         ret = CL_BREAK;
         goto finish;
18a813af
     }
0393aa56
     fsize = map->len;
 
92088f91
     // 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;
c7145595
 
14d52d0c
 #define add_chunk_to_hash_list(_offset, _size) \
     do {                                       \
         regions[nregions].offset = (_offset);  \
         regions[nregions].size   = (_size);    \
         nregions++;                            \
72fd33c8
     } while (0)
0393aa56
 
92088f91
     // Pretty much every case below should return CL_EFORMAT
     ret = CL_EFORMAT;
64ecd109
 
92088f91
     /* 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
 
92088f91
     /* 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;
7ba310e6
 
92088f91
     hlen += sizeof(struct pe_image_data_dir) * 4;
     add_chunk_to_hash_list(at, hlen);
     at += hlen + 8;
0393aa56
 
92088f91
     if (at > peinfo->hdr_size) {
         goto finish;
     }
0393aa56
 
92088f91
     if (sec_dir_offset) {
64ecd109
 
92088f91
         // 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;
         }
e1a08b60
 
07990918
         // Hash everything else up to the start of the security section. Allow
         // the case where at == sec_dir_offset without adding another region
         // to hash, since this could technically be valid (although I haven't
         // verified this).
92088f91
         if (at < sec_dir_offset) {
             hlen = sec_dir_offset - at;
             add_chunk_to_hash_list(at, hlen);
898c08f0
         } else if (at > sec_dir_offset) {
92088f91
             cli_dbgmsg("cli_check_auth_header: security directory offset appears to overlap with the PE header\n");
             goto finish;
         }
0393aa56
 
92088f91
         // Parse the security directory header
18a813af
 
92088f91
         if (fmap_readn(map, &cert_hdr, sec_dir_offset, sizeof(cert_hdr)) != sizeof(cert_hdr)) {
             goto finish;
         }
64ecd109
 
92088f91
         if (EC16(cert_hdr.revision) != WIN_CERT_REV_2) {
             cli_dbgmsg("cli_check_auth_header: unsupported authenticode data revision\n");
             goto finish;
         }
64ecd109
 
92088f91
         if (EC16(cert_hdr.type) != WIN_CERT_TYPE_PKCS7) {
             cli_dbgmsg("cli_check_auth_header: unsupported authenticode data type\n");
             goto finish;
         }
64ecd109
 
92088f91
         hlen = sec_dir_size;
64ecd109
 
92088f91
         if (EC32(cert_hdr.length) != hlen) {
             /* This is the case that MS13-098 aimed to address, but it got
64ecd109
                  * 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.) */
92088f91
             cli_dbgmsg("cli_check_auth_header: MS13-098 violation detected, but continuing on to verify certificate\n");
         }
64ecd109
 
92088f91
         at = sec_dir_offset + sizeof(cert_hdr);
         hlen -= sizeof(cert_hdr);
64ecd109
 
e8169c70
         ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, ctx);
64ecd109
 
92088f91
         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;
         }
64ecd109
 
92088f91
         // 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)
64ecd109
 
92088f91
     } else {
18a813af
 
07990918
         // Hash everything else
92088f91
         if (at < fsize) {
             hlen = fsize - at;
             add_chunk_to_hash_list(at, hlen);
18a813af
         }
92088f91
     }
18a813af
 
92088f91
     // 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;
     }
64ecd109
 
92088f91
     for (i = 0; i < nregions; i++) {
         const uint8_t *hptr;
         if (0 == regions[i].size) {
             continue;
18a813af
         }
92088f91
         if (!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))) {
64ecd109
             break;
18a813af
         }
8fdd05d2
 
92088f91
         cl_update_hash(hashctx, hptr, regions[i].size);
     }
8fdd05d2
 
92088f91
     if (i != nregions) {
         goto finish;
     }
c7145595
 
92088f91
     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;
     }
14d52d0c
 
92088f91
     ret = CL_EVERIFY;
c7145595
 
92088f91
 finish:
64ecd109
     if (NULL != hashctx) {
         cl_hash_destroy(hashctx);
     }
c7145595
 
64ecd109
     if (NULL != regions) {
c7145595
         free(regions);
43adde3e
     }
7ba310e6
 
92088f91
     // If we created the peinfo, then destroy it.  Otherwise we don't own it
     if (&_peinfo == peinfo) {
         cli_exe_info_destroy(peinfo);
     }
64ecd109
     return ret;
0393aa56
 }
3cc632ad
 
14d52d0c
 /* 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
 {
7ba310e6
     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;
 
14d52d0c
     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;
 
14d52d0c
     // 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
7ba310e6
     cli_exe_info_init(peinfo, 0);
3cc632ad
 
7ba310e6
     if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
8b722343
         cli_exe_info_destroy(peinfo);
3cc632ad
         return CL_EFORMAT;
     }
 
7ba310e6
     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));
72fd33c8
     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
 
72fd33c8
     if (!hash) {
211edda0
         cli_errmsg("cli_genhash_pe: cli_malloc failed!\n");
7ba310e6
         cli_exe_info_destroy(peinfo);
211edda0
         return CL_EMEM;
     }
 
14d52d0c
     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) {
14d52d0c
         char *dstr;
3cc632ad
 
7ba310e6
         for (i = 0; i < peinfo->nsections; i++) {
3cc632ad
             /* Generate hashes */
7ba310e6
             if (cli_hashsect(*ctx->fmap, &peinfo->sections[i], hashset, genhash, genhash) == 1) {
14d52d0c
                 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
                 }
14d52d0c
                 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);
14d52d0c
             } else {
                 cli_dbgmsg("Section{%u}: section contains no data\n", i);
634c8594
             }
3cc632ad
         }
     } else if (class == CL_GENHASH_PE_CLASS_IMPTBL) {
14d52d0c
         char *dstr;
211edda0
         uint32_t impsz = 0;
634c8594
         int ret;
211edda0
 
         /* Generate hash */
7ba310e6
         ret = hash_imptbl(ctx, hashset, &impsz, genhash, peinfo);
634c8594
         if (ret == CL_SUCCESS) {
14d52d0c
             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);
     }
 
14d52d0c
     free(hash);
7ba310e6
     cli_exe_info_destroy(peinfo);
3cc632ad
     return CL_SUCCESS;
 }