Browse code

PE parsing code improvements, db loading bug fixes

Consolidate the PE parsing code into one function. I tried to preserve all existing functionality from the previous, distinct implementations to a large extent (with the exceptions mentioned below). If I noticed potential bugs/improvements, I added a TODO statement about those so that they can be fixed in a smaller commit later. Also, there are more TODOs in places where I'm not entirely sure why certain actions are performed - more research is needed for these.

I'm submitting a pull request now so that regression testing can be done, and because merging what I have thus far now will likely have fewer conflicts than if I try to merge later

PE parsing code improvements:
- PEs without all 16 data directories are parsed more appropriately now
- Added lots more debug statements

Also:
- Allow MAX_BC and MAX_TRACKED_PCRE to be specified via CFLAGS

When doing performance testing with the latest CVD, MAX_BC and
MAX_TRACKED_PCRE need to be raised to track all the events.
Allow these to be specified via CFLAGS by not redefining them
if they are already defined

- Fix an issue preventing wildcard sizes in .MDB/.MSB rules

I'm not sure what the original intent of the check I removed was,
but it prevents using wildcard sizes in .MDB/.MSB rules. AFAICT
these wildcard sizes should be handled appropriately by the MD5
section hash computation code, so I don't think a check on that
is needed.

- Fix several issues related to db loading
- .imp files will now get loaded if they exist in a directory passed
via clamscan's '-d' flag
- .pwdb files will now get loaded if they exist in a directory passed
via clamscan's '-d' flag even when compiling without yara support
- Changes to .imp, .ign, and .ign2 files will now be reflected in calls
to cl_statinidir and cl_statchkdir (and also .pwdb files, even when
compiling without yara support)
- The contents of .sfp files won't be included in some of the signature
counts, and the contents of .cud files will be
- Any local.gdb files will no longer be loaded twice

- For .imp files, you are no longer required to specify a minimum flevel for wildcard rules, since this isn't needed

Andrew authored on 2019/01/08 14:09:08
Showing 20 changed files
... ...
@@ -318,6 +318,7 @@ libclamav_la_SOURCES = \
318 318
 	pe.h \
319 319
 	pe_icons.c \
320 320
 	pe_icons.h \
321
+	pe_structs.h \
321 322
 	disasm.c \
322 323
 	disasm.h \
323 324
 	disasm-common.h \
... ...
@@ -391,6 +392,7 @@ libclamav_la_SOURCES = \
391 391
 	elf.c \
392 392
 	elf.h \
393 393
 	execs.h \
394
+	execs.c \
394 395
 	sis.c \
395 396
 	sis.h \
396 397
 	uuencode.c \
... ...
@@ -257,20 +257,21 @@ am__libclamav_la_SOURCES_DIST = matcher-ac.c matcher-ac.h matcher-bm.c \
257 257
 	filetypes.h filetypes_int.h rtf.c rtf.h blob.c blob.h mbox.c \
258 258
 	mbox.h message.c message.h table.c table.h text.c text.h \
259 259
 	ole2_extract.c ole2_extract.h vba_extract.c vba_extract.h \
260
-	msexpand.c msexpand.h pe.c pe.h pe_icons.c pe_icons.h disasm.c \
261
-	disasm.h disasm-common.h disasmpriv.h upx.c upx.h htmlnorm.c \
262
-	htmlnorm.h libmspack.c libmspack.h rebuildpe.c rebuildpe.h \
263
-	petite.c petite.h wwunpack.c wwunpack.h unsp.c unsp.h aspack.c \
264
-	aspack.h packlibs.c packlibs.h fsg.c fsg.h mew.c mew.h upack.c \
265
-	upack.h line.c line.h untar.c untar.h unzip.c unzip.h ooxml.c \
266
-	ooxml.h inflate64.c inflate64.h inffixed64.h inflate64_priv.h \
260
+	msexpand.c msexpand.h pe.c pe.h pe_icons.c \
261
+	pe_icons.h pe_structs.h disasm.c disasm.h disasm-common.h \
262
+	disasmpriv.h upx.c upx.h htmlnorm.c htmlnorm.h libmspack.c \
263
+	libmspack.h rebuildpe.c rebuildpe.h petite.c petite.h \
264
+	wwunpack.c wwunpack.h unsp.c unsp.h aspack.c aspack.h \
265
+	packlibs.c packlibs.h fsg.c fsg.h mew.c mew.h upack.c upack.h \
266
+	line.c line.h untar.c untar.h unzip.c unzip.h ooxml.c ooxml.h \
267
+	inflate64.c inflate64.h inffixed64.h inflate64_priv.h \
267 268
 	special.c special.h binhex.c binhex.h is_tar.c is_tar.h tnef.c \
268 269
 	tnef.h autoit.c autoit.h unarj.c unarj.h nsis/bzlib.c \
269 270
 	nsis/bzlib_private.h nsis/nsis_bzlib.h nsis/nulsft.c \
270 271
 	nsis/nulsft.h nsis/infblock.c nsis/nsis_zconf.h \
271 272
 	nsis/nsis_zlib.h nsis/nsis_zutil.h pdf.c pdf.h pdfng.c \
272 273
 	pdfdecode.c pdfdecode.h spin.c spin.h yc.c yc.h elf.c elf.h \
273
-	execs.h sis.c sis.h uuencode.c uuencode.h phishcheck.c \
274
+	execs.h execs.c sis.c sis.h uuencode.c uuencode.h phishcheck.c \
274 275
 	phishcheck.h phish_domaincheck_db.c phish_domaincheck_db.h \
275 276
 	phish_whitelist.c phish_whitelist.h iana_cctld.h iana_tld.h \
276 277
 	regex_list.c regex_list.h regex_suffix.c regex_suffix.h \
... ...
@@ -406,8 +407,9 @@ am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
406 406
 	libclamav_la-nulsft.lo libclamav_la-infblock.lo \
407 407
 	libclamav_la-pdf.lo libclamav_la-pdfng.lo \
408 408
 	libclamav_la-pdfdecode.lo libclamav_la-spin.lo \
409
-	libclamav_la-yc.lo libclamav_la-elf.lo libclamav_la-sis.lo \
410
-	libclamav_la-uuencode.lo libclamav_la-phishcheck.lo \
409
+	libclamav_la-yc.lo libclamav_la-elf.lo libclamav_la-execs.lo \
410
+	libclamav_la-sis.lo libclamav_la-uuencode.lo \
411
+	libclamav_la-phishcheck.lo \
411 412
 	libclamav_la-phish_domaincheck_db.lo \
412 413
 	libclamav_la-phish_whitelist.lo libclamav_la-regex_list.lo \
413 414
 	libclamav_la-regex_suffix.lo libclamav_la-entconv.lo \
... ...
@@ -1308,20 +1310,21 @@ libclamav_la_SOURCES = matcher-ac.c matcher-ac.h matcher-bm.c \
1308 1308
 	filetypes.h filetypes_int.h rtf.c rtf.h blob.c blob.h mbox.c \
1309 1309
 	mbox.h message.c message.h table.c table.h text.c text.h \
1310 1310
 	ole2_extract.c ole2_extract.h vba_extract.c vba_extract.h \
1311
-	msexpand.c msexpand.h pe.c pe.h pe_icons.c pe_icons.h disasm.c \
1312
-	disasm.h disasm-common.h disasmpriv.h upx.c upx.h htmlnorm.c \
1313
-	htmlnorm.h libmspack.c libmspack.h rebuildpe.c rebuildpe.h \
1314
-	petite.c petite.h wwunpack.c wwunpack.h unsp.c unsp.h aspack.c \
1315
-	aspack.h packlibs.c packlibs.h fsg.c fsg.h mew.c mew.h upack.c \
1316
-	upack.h line.c line.h untar.c untar.h unzip.c unzip.h ooxml.c \
1317
-	ooxml.h inflate64.c inflate64.h inffixed64.h inflate64_priv.h \
1311
+	msexpand.c msexpand.h pe.c pe.h pe_icons.c \
1312
+	pe_icons.h pe_structs.h disasm.c disasm.h disasm-common.h \
1313
+	disasmpriv.h upx.c upx.h htmlnorm.c htmlnorm.h libmspack.c \
1314
+	libmspack.h rebuildpe.c rebuildpe.h petite.c petite.h \
1315
+	wwunpack.c wwunpack.h unsp.c unsp.h aspack.c aspack.h \
1316
+	packlibs.c packlibs.h fsg.c fsg.h mew.c mew.h upack.c upack.h \
1317
+	line.c line.h untar.c untar.h unzip.c unzip.h ooxml.c ooxml.h \
1318
+	inflate64.c inflate64.h inffixed64.h inflate64_priv.h \
1318 1319
 	special.c special.h binhex.c binhex.h is_tar.c is_tar.h tnef.c \
1319 1320
 	tnef.h autoit.c autoit.h unarj.c unarj.h nsis/bzlib.c \
1320 1321
 	nsis/bzlib_private.h nsis/nsis_bzlib.h nsis/nulsft.c \
1321 1322
 	nsis/nulsft.h nsis/infblock.c nsis/nsis_zconf.h \
1322 1323
 	nsis/nsis_zlib.h nsis/nsis_zutil.h pdf.c pdf.h pdfng.c \
1323 1324
 	pdfdecode.c pdfdecode.h spin.c spin.h yc.c yc.h elf.c elf.h \
1324
-	execs.h sis.c sis.h uuencode.c uuencode.h phishcheck.c \
1325
+	execs.h execs.c sis.c sis.h uuencode.c uuencode.h phishcheck.c \
1325 1326
 	phishcheck.h phish_domaincheck_db.c phish_domaincheck_db.h \
1326 1327
 	phish_whitelist.c phish_whitelist.h iana_cctld.h iana_tld.h \
1327 1328
 	regex_list.c regex_list.h regex_suffix.c regex_suffix.h \
... ...
@@ -1612,6 +1615,7 @@ distclean-compile:
1612 1612
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-elf.Plo@am__quote@
1613 1613
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-entconv.Plo@am__quote@
1614 1614
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-events.Plo@am__quote@
1615
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-execs.Plo@am__quote@
1615 1616
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-explode.Plo@am__quote@
1616 1617
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-filetypes.Plo@am__quote@
1617 1618
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-filtering.Plo@am__quote@
... ...
@@ -2276,6 +2280,13 @@ libclamav_la-elf.lo: elf.c
2276 2276
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2277 2277
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-elf.lo `test -f 'elf.c' || echo '$(srcdir)/'`elf.c
2278 2278
 
2279
+libclamav_la-execs.lo: execs.c
2280
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-execs.lo -MD -MP -MF $(DEPDIR)/libclamav_la-execs.Tpo -c -o libclamav_la-execs.lo `test -f 'execs.c' || echo '$(srcdir)/'`execs.c
2281
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-execs.Tpo $(DEPDIR)/libclamav_la-execs.Plo
2282
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='execs.c' object='libclamav_la-execs.lo' libtool=yes @AMDEPBACKSLASH@
2283
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2284
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-execs.lo `test -f 'execs.c' || echo '$(srcdir)/'`execs.c
2285
+
2279 2286
 libclamav_la-sis.lo: sis.c
2280 2287
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-sis.lo -MD -MP -MF $(DEPDIR)/libclamav_la-sis.Tpo -c -o libclamav_la-sis.lo `test -f 'sis.c' || echo '$(srcdir)/'`sis.c
2281 2288
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-sis.Tpo $(DEPDIR)/libclamav_la-sis.Plo
... ...
@@ -2036,7 +2036,6 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine)
2036 2036
 {
2037 2037
     struct cli_asn1 c;
2038 2038
     unsigned int size;
2039
-    struct cli_matcher *db;
2040 2039
     int i;
2041 2040
 
2042 2041
     // TODO As currently implemented, loading in a .cat file with -d requires
... ...
@@ -2172,7 +2171,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine)
2172 2172
                 cli_dbgmsg("asn1_load_mscat: got hash %s (%s)\n", sha1, (hashtype == 2) ? "PE" : "CAB");
2173 2173
             }
2174 2174
             if (!engine->hm_fp) {
2175
-                if (!(engine->hm_fp = mpool_calloc(engine->mempool, 1, sizeof(*db)))) {
2175
+                if (!(engine->hm_fp = mpool_calloc(engine->mempool, 1, sizeof(*(engine->hm_fp))))) {
2176 2176
                     tag.size = 1;
2177 2177
                     return 1;
2178 2178
                 }
... ...
@@ -45,7 +45,9 @@
45 45
 #include "json.h"
46 46
 #endif
47 47
 
48
+#ifndef MAX_BC
48 49
 #define MAX_BC 64
50
+#endif
49 51
 #define BC_EVENTS_PER_SIG 2
50 52
 #define MAX_BC_SIGEVENT_ID MAX_BC *BC_EVENTS_PER_SIG
51 53
 
... ...
@@ -2776,7 +2778,7 @@ int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
2776 2776
     cli_bytecode_context_setctx(&ctx, cctx);
2777 2777
     cli_bytecode_context_setfile(&ctx, map);
2778 2778
     if (tinfo && tinfo->status == 1) {
2779
-        ctx.sections = tinfo->exeinfo.section;
2779
+        ctx.sections = tinfo->exeinfo.sections;
2780 2780
         memset(&pehookdata, 0, sizeof(pehookdata));
2781 2781
         pehookdata.offset    = tinfo->exeinfo.offset;
2782 2782
         pehookdata.ep        = tinfo->exeinfo.ep;
... ...
@@ -339,7 +339,7 @@ static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, un
339 339
         else
340 340
             off = ftell(dbio->fs);
341 341
 
342
-        if ((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && (CLI_DBEXT(name) || cli_strbcasestr(name, ".ign") || cli_strbcasestr(name, ".ign2")))) {
342
+        if ((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && CLI_DBEXT(name))) {
343 343
             ret = cli_load(name, engine, signo, options, dbio);
344 344
             if (ret) {
345 345
                 cli_errmsg("cli_tgzload: Can't load %s\n", name);
... ...
@@ -444,8 +444,8 @@ static int cli_elf_sh32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
444 444
         cli_dbgmsg("ELF: Section header table offset: %d\n", shoff);
445 445
 
446 446
     if (elfinfo) {
447
-        elfinfo->section = (struct cli_exe_section *)cli_calloc(shnum, sizeof(struct cli_exe_section));
448
-        if (!elfinfo->section) {
447
+        elfinfo->sections = (struct cli_exe_section *)cli_calloc(shnum, sizeof(struct cli_exe_section));
448
+        if (!elfinfo->sections) {
449 449
             cli_dbgmsg("ELF: Can't allocate memory for section headers\n");
450 450
             return CL_EMEM;
451 451
         }
... ...
@@ -455,10 +455,7 @@ static int cli_elf_sh32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
455 455
         section_hdr = (struct elf_section_hdr32 *)cli_calloc(shnum, shentsize);
456 456
         if (!section_hdr) {
457 457
             cli_errmsg("ELF: Can't allocate memory for section headers\n");
458
-            if (elfinfo) {
459
-                free(elfinfo->section);
460
-                elfinfo->section = NULL;
461
-            }
458
+            cli_exe_info_destroy(elfinfo);
462 459
             return CL_EMEM;
463 460
         }
464 461
         if (ctx) {
... ...
@@ -476,10 +473,7 @@ static int cli_elf_sh32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
476 476
                 cli_dbgmsg("ELF: Possibly broken ELF file\n");
477 477
             }
478 478
             free(section_hdr);
479
-            if (elfinfo) {
480
-                free(elfinfo->section);
481
-                elfinfo->section = NULL;
482
-            }
479
+            cli_exe_info_destroy(elfinfo);
483 480
             if (ctx && SCAN_HEURISTIC_BROKEN) {
484 481
                 cli_append_virus(ctx, "Heuristics.Broken.Executable");
485 482
                 return CL_VIRUS;
... ...
@@ -490,9 +484,9 @@ static int cli_elf_sh32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
490 490
         shoff += sizeof(struct elf_section_hdr32);
491 491
 
492 492
         if (elfinfo) {
493
-            elfinfo->section[i].rva = EC32(section_hdr[i].sh_addr, conv);
494
-            elfinfo->section[i].raw = EC32(section_hdr[i].sh_offset, conv);
495
-            elfinfo->section[i].rsz = EC32(section_hdr[i].sh_size, conv);
493
+            elfinfo->sections[i].rva = EC32(section_hdr[i].sh_addr, conv);
494
+            elfinfo->sections[i].raw = EC32(section_hdr[i].sh_offset, conv);
495
+            elfinfo->sections[i].rsz = EC32(section_hdr[i].sh_size, conv);
496 496
         }
497 497
         if (ctx) {
498 498
             cli_dbgmsg("ELF: Section %u\n", i);
... ...
@@ -553,8 +547,8 @@ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
553 553
         cli_dbgmsg("ELF: Section header table offset: " STDu64 "\n", shoff);
554 554
 
555 555
     if (elfinfo) {
556
-        elfinfo->section = (struct cli_exe_section *)cli_calloc(shnum, sizeof(struct cli_exe_section));
557
-        if (!elfinfo->section) {
556
+        elfinfo->sections = (struct cli_exe_section *)cli_calloc(shnum, sizeof(struct cli_exe_section));
557
+        if (!elfinfo->sections) {
558 558
             cli_dbgmsg("ELF: Can't allocate memory for section headers\n");
559 559
             return CL_EMEM;
560 560
         }
... ...
@@ -564,10 +558,7 @@ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
564 564
         section_hdr = (struct elf_section_hdr64 *)cli_calloc(shnum, shentsize);
565 565
         if (!section_hdr) {
566 566
             cli_errmsg("ELF: Can't allocate memory for section headers\n");
567
-            if (elfinfo) {
568
-                free(elfinfo->section);
569
-                elfinfo->section = NULL;
570
-            }
567
+            cli_exe_info_destroy(elfinfo);
571 568
             return CL_EMEM;
572 569
         }
573 570
         if (ctx) {
... ...
@@ -585,10 +576,7 @@ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
585 585
                 cli_dbgmsg("ELF: Possibly broken ELF file\n");
586 586
             }
587 587
             free(section_hdr);
588
-            if (elfinfo) {
589
-                free(elfinfo->section);
590
-                elfinfo->section = NULL;
591
-            }
588
+            cli_exe_info_destroy(elfinfo);
592 589
             if (ctx && SCAN_HEURISTIC_BROKEN) {
593 590
                 cli_append_virus(ctx, "Heuristics.Broken.Executable");
594 591
                 return CL_VIRUS;
... ...
@@ -599,9 +587,9 @@ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
599 599
         shoff += sizeof(struct elf_section_hdr64);
600 600
 
601 601
         if (elfinfo) {
602
-            elfinfo->section[i].rva = EC64(section_hdr[i].sh_addr, conv);
603
-            elfinfo->section[i].raw = EC64(section_hdr[i].sh_offset, conv);
604
-            elfinfo->section[i].rsz = EC64(section_hdr[i].sh_size, conv);
602
+            elfinfo->sections[i].rva = EC64(section_hdr[i].sh_addr, conv);
603
+            elfinfo->sections[i].raw = EC64(section_hdr[i].sh_offset, conv);
604
+            elfinfo->sections[i].rsz = EC64(section_hdr[i].sh_size, conv);
605 605
         }
606 606
         if (ctx) {
607 607
             cli_dbgmsg("ELF: Section " STDu32 "\n", (uint32_t)i);
608 608
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+/*
1
+ *  Copyright (C) 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2
+ *
3
+ *  Authors: Andrew Williams
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#include "execs.h"
21
+#include <string.h>
22
+
23
+void cli_exe_info_init(struct cli_exe_info *exeinfo, uint32_t offset)
24
+{
25
+
26
+    if (NULL == exeinfo) {
27
+        return;
28
+    }
29
+    // TODO the memset below might not be needed.  Instead, replace with:
30
+    // exeinfo->sections = NULL;
31
+    // memset(&exeinfo->vinfo, '\0', sizeof(exeinfo->vinfo));
32
+    memset(exeinfo, '\0', sizeof(*exeinfo));
33
+    exeinfo->offset = offset;
34
+}
35
+
36
+void cli_exe_info_destroy(struct cli_exe_info *exeinfo)
37
+{
38
+
39
+    if (NULL == exeinfo) {
40
+        return;
41
+    }
42
+
43
+    if (NULL != exeinfo->sections) {
44
+        free(exeinfo->sections);
45
+        exeinfo->sections = NULL;
46
+    }
47
+
48
+    cli_hashset_destroy(&(exeinfo->vinfo));
49
+}
0 50
\ No newline at end of file
... ...
@@ -25,10 +25,25 @@
25 25
 #include "clamav-types.h"
26 26
 #include "hashtab.h"
27 27
 #include "bcfeatures.h"
28
+#include "pe_structs.h"
28 29
 
29 30
 /** @file */
30 31
 /** Section of executable file.
31 32
   \group_pe
33
+ *  NOTE: This is used to store PE, MachO, and ELF section information. Not
34
+ *  all members are populated by the respective parsing functions.
35
+ *
36
+ *  NOTE: This structure MUST stay in-sync with the ones defined within the
37
+ *  clamav-bytecode-compiler source at:
38
+ *  - clang/lib/Headers/bytecode_execs.h
39
+ *  - llvm/tools/clang/lib/Headers/bytecode_execs.h
40
+ *  We allocate space for an array of these and pass it directly to the
41
+ *  bytecode sig runtime for use.
42
+ *
43
+ *  TODO Next time we are making changes to the clamav-bytecode-compiler
44
+ *  source, modify this structure to also include the section name (here and
45
+ *  there).  Then, populate this field in the PE/MachO/ELF header parsing
46
+ *  functions.  Choose a length that's reasonable for all platforms
32 47
 */
33 48
 struct cli_exe_section {
34 49
     uint32_t rva;  /**< Relative VirtualAddress */
... ...
@@ -43,25 +58,111 @@ struct cli_exe_section {
43 43
 };
44 44
 
45 45
 /** Executable file information
46
-  \group_pe
46
+ *  NOTE: This is used to store PE, MachO, and ELF executable information,
47
+ *  but it predominantly has fields for PE info.  Not all members are
48
+ *  populated by the respective parsing functions.
49
+ * 
50
+ *  TODO: Document which fields are PE only (maybe put those into a union of
51
+ *  structs containing the various type-specific members)
52
+
53
+ *  NOTE: This structure is also defined in the clamav-bytecode-compiler
54
+ *  source at:
55
+ *  - clang/lib/Headers/bytecode_execs.h
56
+ *  - llvm/tools/clang/lib/Headers/bytecode_execs.h
57
+ *  Based on how we use it, though, it doesn't need to stay in-sync
58
+ *
59
+ *  TODO Next time we are making changes to the clamav-bytecode-compiler
60
+ *  source, remove this structure definition there so it's not confusing
47 61
 */
48 62
 struct cli_exe_info {
49 63
     /** Information about all the sections of this file. 
50 64
      * This array has \p nsection elements */
51
-    struct cli_exe_section *section;
52
-    /** Offset where this executable start in file (nonzero if embedded) */
65
+    struct cli_exe_section *sections;
66
+
67
+    /** Offset where this executable starts in file (nonzero if embedded) */
53 68
     uint32_t offset;
54
-    /** Entrypoint of executable */
69
+
70
+    /** File offset to the entrypoint of the executable. */
55 71
     uint32_t ep;
56
-    /** Number of sections*/
72
+
73
+    /** Number of sections.
74
+     *  NOTE: If a section is determined to be invalid (exists outside of the
75
+     *  file) then it will not be included in this count (at least for PE).
76
+     */
57 77
     uint16_t nsections;
78
+
79
+    // TODO Remove - not required
58 80
     void *dummy; /* for compat - preserve offset */
81
+
59 82
     /** Resources RVA - PE ONLY */
83
+    // TODO Maybe get rid of this - it looks like it's only used by the
84
+    // icon matching code (and maybe the bytecode API more broadly?)
60 85
     uint32_t res_addr;
61
-    /** Address size - PE ONLY */
86
+
87
+    /** Size of the  header (aligned) - PE ONLY. This corresponds to
88
+     *  SizeOfHeaders in the optional header
89
+    */
62 90
     uint32_t hdr_size;
91
+
63 92
     /** Hashset for versioninfo matching */
64 93
     struct cli_hashset vinfo;
94
+
95
+    /** Entry point RVA */
96
+    uint32_t vep;
97
+
98
+    /** Number of data directory entries at the end of the optional header.
99
+     *  This also corresponds to the number of entries in dirs that has
100
+     *  been populated with information.
101
+     */
102
+    uint32_t ndatadirs;
103
+
104
+    /** Whether this file is a DLL */
105
+    uint32_t is_dll;
106
+
107
+    /** Whether this file is a PE32+ exe (64-bit) */
108
+    uint32_t is_pe32plus;
109
+
110
+    /**< address of new exe header */
111
+    uint32_t e_lfanew;
112
+
113
+    /** The lowest section RVA */
114
+    uint32_t min;
115
+
116
+    /** The RVA of the highest byte contained within a section */
117
+    uint32_t max;
118
+
119
+    /** Offset of any file overlays, as determined by parsing the PE header */
120
+    uint32_t overlay_start;
121
+
122
+    /**< size of overlay */
123
+    uint32_t overlay_size;
124
+
125
+    /* Raw data copied in from the EXE directly.
126
+     *
127
+     * NOTE: The data in the members below haven't been converted to host
128
+     * endianness, so all field accesses most account for this to ensure
129
+     * proper functionality on big endian systems (the PE header is always
130
+     * little-endian)
131
+     */
132
+
133
+    /** Image File Header for this PE file */
134
+    struct pe_image_file_hdr file_hdr;
135
+
136
+    /** PE optional header. Use is_pe32plus to determine whether the 32-bit
137
+     *  or 64-bit union member should be used. */
138
+    union {
139
+        struct pe_image_optional_hdr64 opt64;
140
+        struct pe_image_optional_hdr32 opt32;
141
+    } pe_opt;
142
+
143
+    /**< PE data directory header. If ndatadirs is less than 16,
144
+     * the unpopulated entries will be memset'd to zero.
145
+     */
146
+    struct pe_image_data_dir dirs[16];
65 147
 };
66 148
 
149
+// TODO Document
150
+void cli_exe_info_init(struct cli_exe_info *exeinfo, uint32_t offset);
151
+void cli_exe_info_destroy(struct cli_exe_info *exeinfo);
152
+
67 153
 #endif
... ...
@@ -483,7 +483,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
483 483
     if (matcher) {
484 484
         fileinfo->ep        = ep;
485 485
         fileinfo->nsections = sect;
486
-        fileinfo->section   = sections;
486
+        fileinfo->sections  = sections;
487 487
         return 0;
488 488
     } else {
489 489
         free(sections);
... ...
@@ -55,7 +55,9 @@
55 55
 #undef MATCHER_PCRE_DEBUG
56 56
 
57 57
 /* PERFORMANCE MACROS AND FUNCTIONS */
58
+#ifndef MAX_TRACKED_PCRE
58 59
 #define MAX_TRACKED_PCRE 64
60
+#endif
59 61
 #define PCRE_EVENTS_PER_SIG 2
60 62
 #define MAX_PCRE_SIGEVENT_ID MAX_TRACKED_PCRE *PCRE_EVENTS_PER_SIG
61 63
 
... ...
@@ -445,8 +445,12 @@ int cli_caloff(const char *offstr, const struct cli_target_info *info, unsigned
445 445
         *offset_min = CLI_OFF_NONE;
446 446
         if (offset_max)
447 447
             *offset_max = CLI_OFF_NONE;
448
-        if (info->status == -1)
448
+        if (info->status == -1) {
449
+            // TODO I wonder if this means that sigs with these types won't
450
+            // work (which kinda makes sense).  Warn the user of this for now
451
+            cli_dbgmsg("cli_caloff: cli_target_info is invalid - unable to determine offsets for ndb/ldb subsigs using EOF-n/EP+n/EP-n/Sx+n/SEx/SL+n\n");
449 452
             return CL_SUCCESS;
453
+        }
450 454
 
451 455
         switch (offdata[0]) {
452 456
             case CLI_OFF_EOF_MINUS:
... ...
@@ -462,23 +466,26 @@ int cli_caloff(const char *offstr, const struct cli_target_info *info, unsigned
462 462
                 break;
463 463
 
464 464
             case CLI_OFF_SL_PLUS:
465
-                *offset_min = info->exeinfo.section[info->exeinfo.nsections - 1].raw + offdata[1];
465
+                *offset_min = info->exeinfo.sections[info->exeinfo.nsections - 1].raw + offdata[1];
466 466
                 break;
467 467
 
468 468
             case CLI_OFF_SX_PLUS:
469 469
                 if (offdata[3] >= info->exeinfo.nsections)
470 470
                     *offset_min = CLI_OFF_NONE;
471 471
                 else
472
-                    *offset_min = info->exeinfo.section[offdata[3]].raw + offdata[1];
472
+                    *offset_min = info->exeinfo.sections[offdata[3]].raw + offdata[1];
473 473
                 break;
474 474
 
475 475
             case CLI_OFF_SE:
476 476
                 if (offdata[3] >= info->exeinfo.nsections) {
477 477
                     *offset_min = CLI_OFF_NONE;
478 478
                 } else {
479
-                    *offset_min = info->exeinfo.section[offdata[3]].raw;
479
+                    *offset_min = info->exeinfo.sections[offdata[3]].raw;
480 480
                     if (offset_max)
481
-                        *offset_max = *offset_min + info->exeinfo.section[offdata[3]].rsz + offdata[2];
481
+                        *offset_max = *offset_min + info->exeinfo.sections[offdata[3]].rsz + offdata[2];
482
+                    // TODO offdata[2] == MaxShift. Won't this make offset_max
483
+                    // extend beyond the end of the section?  This doesn't seem like
484
+                    // what we want...
482 485
                 }
483 486
                 break;
484 487
 
... ...
@@ -498,16 +505,31 @@ int cli_caloff(const char *offstr, const struct cli_target_info *info, unsigned
498 498
     return CL_SUCCESS;
499 499
 }
500 500
 
501
+void cli_targetinfo_init(struct cli_target_info *info)
502
+{
503
+
504
+    if (NULL == info) {
505
+        return;
506
+    }
507
+    // Things that must be initialized here:
508
+    // - status (set to 0)
509
+    // - vinfo
510
+    // Maybe other things.
511
+    // TODO Consider replacing with just the required member assignments
512
+    // for performance
513
+    memset(info, 0, sizeof(struct cli_target_info));
514
+
515
+    cli_hashset_init_noalloc(&info->exeinfo.vinfo);
516
+}
517
+
501 518
 void cli_targetinfo(struct cli_target_info *info, unsigned int target, fmap_t *map)
502 519
 {
503 520
     int (*einfo)(fmap_t *, struct cli_exe_info *) = NULL;
504 521
 
505
-    memset(info, 0, sizeof(struct cli_target_info));
506 522
     info->fsize = map->len;
507
-    cli_hashset_init_noalloc(&info->exeinfo.vinfo);
508 523
 
509 524
     if (target == 1)
510
-        einfo = cli_peheader;
525
+        einfo = cli_pe_targetinfo;
511 526
     else if (target == 6)
512 527
         einfo = cli_elfheader;
513 528
     else if (target == 9)
... ...
@@ -521,6 +543,16 @@ void cli_targetinfo(struct cli_target_info *info, unsigned int target, fmap_t *m
521 521
         info->status = 1;
522 522
 }
523 523
 
524
+void cli_targetinfo_destroy(struct cli_target_info *info)
525
+{
526
+
527
+    if (NULL == info || info->status != 1) {
528
+        return;
529
+    }
530
+
531
+    cli_exe_info_destroy(&(info->exeinfo));
532
+}
533
+
524 534
 int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx)
525 535
 {
526 536
     return cli_checkfp_virus(digest, size, ctx, NULL);
... ...
@@ -667,7 +699,7 @@ static int matchicon(cli_ctx *ctx, struct cli_exe_info *exeinfo, const char *grp
667 667
     cli_icongroupset_init(&iconset);
668 668
     cli_icongroupset_add(grp1 ? grp1 : "*", &iconset, 0, ctx);
669 669
     cli_icongroupset_add(grp2 ? grp2 : "*", &iconset, 1, ctx);
670
-    return cli_scanicon(&iconset, exeinfo->res_addr, ctx, exeinfo->section, exeinfo->nsections, exeinfo->hdr_size);
670
+    return cli_scanicon(&iconset, ctx, exeinfo);
671 671
 }
672 672
 
673 673
 int32_t cli_bcapi_matchicon(struct cli_bc_ctx *ctx, const uint8_t *grp1, int32_t grp1len,
... ...
@@ -700,7 +732,7 @@ int32_t cli_bcapi_matchicon(struct cli_bc_ctx *ctx, const uint8_t *grp1, int32_t
700 700
             info.res_addr = le32_to_host(ctx->hooks.pedata->dirs[2].VirtualAddress);
701 701
     } else
702 702
         info.res_addr = ctx->resaddr; /* from target_info */
703
-    info.section   = (struct cli_exe_section *)ctx->sections;
703
+    info.sections  = (struct cli_exe_section *)ctx->sections;
704 704
     info.nsections = ctx->hooks.pedata->nsections;
705 705
     info.hdr_size  = ctx->hooks.pedata->hdr_size;
706 706
     cli_dbgmsg("bytecode matchicon %s %s\n", group1, group2);
... ...
@@ -939,14 +971,12 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
939 939
             maxpatlen = groot->maxpatlen;
940 940
     }
941 941
 
942
+    cli_targetinfo_init(&info);
942 943
     cli_targetinfo(&info, i, map);
943 944
 
944 945
     if (!ftonly) {
945 946
         if ((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) {
946
-            if (info.exeinfo.section)
947
-                free(info.exeinfo.section);
948
-
949
-            cli_hashset_destroy(&info.exeinfo.vinfo);
947
+            cli_targetinfo_destroy(&info);
950 948
             cl_hash_destroy(md5ctx);
951 949
             cl_hash_destroy(sha1ctx);
952 950
             cl_hash_destroy(sha256ctx);
... ...
@@ -954,10 +984,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
954 954
         }
955 955
         if ((ret = cli_pcre_recaloff(groot, &gpoff, &info, ctx))) {
956 956
             cli_ac_freedata(&gdata);
957
-            if (info.exeinfo.section)
958
-                free(info.exeinfo.section);
959
-
960
-            cli_hashset_destroy(&info.exeinfo.vinfo);
957
+            cli_targetinfo_destroy(&info);
961 958
             cl_hash_destroy(md5ctx);
962 959
             cl_hash_destroy(sha1ctx);
963 960
             cl_hash_destroy(sha256ctx);
... ...
@@ -971,10 +998,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
971 971
                 cli_ac_freedata(&gdata);
972 972
                 cli_pcre_freeoff(&gpoff);
973 973
             }
974
-            if (info.exeinfo.section)
975
-                free(info.exeinfo.section);
976
-
977
-            cli_hashset_destroy(&info.exeinfo.vinfo);
974
+            cli_targetinfo_destroy(&info);
978 975
             cl_hash_destroy(md5ctx);
979 976
             cl_hash_destroy(sha1ctx);
980 977
             cl_hash_destroy(sha256ctx);
... ...
@@ -989,10 +1013,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
989 989
                     }
990 990
 
991 991
                     cli_ac_freedata(&tdata);
992
-                    if (info.exeinfo.section)
993
-                        free(info.exeinfo.section);
994
-
995
-                    cli_hashset_destroy(&info.exeinfo.vinfo);
992
+                    cli_targetinfo_destroy(&info);
996 993
                     cl_hash_destroy(md5ctx);
997 994
                     cl_hash_destroy(sha1ctx);
998 995
                     cl_hash_destroy(sha256ctx);
... ...
@@ -1011,10 +1032,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
1011 1011
             cli_ac_freedata(&tdata);
1012 1012
             if (bm_offmode)
1013 1013
                 cli_bm_freeoff(&toff);
1014
-            if (info.exeinfo.section)
1015
-                free(info.exeinfo.section);
1016
-
1017
-            cli_hashset_destroy(&info.exeinfo.vinfo);
1014
+            cli_targetinfo_destroy(&info);
1018 1015
             cl_hash_destroy(md5ctx);
1019 1016
             cl_hash_destroy(sha1ctx);
1020 1017
             cl_hash_destroy(sha256ctx);
... ...
@@ -1076,10 +1094,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
1076 1076
                     cli_bm_freeoff(&toff);
1077 1077
                 cli_pcre_freeoff(&tpoff);
1078 1078
 
1079
-                if (info.exeinfo.section)
1080
-                    free(info.exeinfo.section);
1081
-
1082
-                cli_hashset_destroy(&info.exeinfo.vinfo);
1079
+                cli_targetinfo_destroy(&info);
1083 1080
                 cl_hash_destroy(md5ctx);
1084 1081
                 cl_hash_destroy(sha1ctx);
1085 1082
                 cl_hash_destroy(sha256ctx);
... ...
@@ -1105,10 +1120,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
1105 1105
                     cli_pcre_freeoff(&tpoff);
1106 1106
                 }
1107 1107
 
1108
-                if (info.exeinfo.section)
1109
-                    free(info.exeinfo.section);
1110
-
1111
-                cli_hashset_destroy(&info.exeinfo.vinfo);
1108
+                cli_targetinfo_destroy(&info);
1112 1109
                 cl_hash_destroy(md5ctx);
1113 1110
                 cl_hash_destroy(sha1ctx);
1114 1111
                 cl_hash_destroy(sha256ctx);
... ...
@@ -1233,10 +1245,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
1233 1233
         cli_pcre_freeoff(&gpoff);
1234 1234
     }
1235 1235
 
1236
-    if (info.exeinfo.section)
1237
-        free(info.exeinfo.section);
1238
-
1239
-    cli_hashset_destroy(&info.exeinfo.vinfo);
1236
+    cli_targetinfo_destroy(&info);
1240 1237
 
1241 1238
     if (SCAN_ALLMATCHES && viruses_found) {
1242 1239
         return CL_VIRUS;
... ...
@@ -35,6 +35,9 @@ struct cli_target_info {
35 35
     int status; /* 0 == not initialised, 1 == initialised OK, -1 == error */
36 36
 };
37 37
 
38
+void cli_targetinfo_init(struct cli_target_info *info);
39
+void cli_targetinfo_destroy(struct cli_target_info *info);
40
+
38 41
 #include "matcher-ac.h"
39 42
 #include "matcher-bm.h"
40 43
 #include "matcher-hash.h"
... ...
@@ -94,9 +94,7 @@
94 94
 #define PE_IMAGE_NT_SIGNATURE 0x00004550
95 95
 #define PE32_SIGNATURE 0x010b
96 96
 #define PE32P_SIGNATURE 0x020b
97
-
98
-#define optional_hdr64 pe_opt.opt64
99
-#define optional_hdr32 pe_opt.opt32
97
+#define OPT_HDR_SIZE_DIFF (sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32))
100 98
 
101 99
 #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"
102 100
 #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"
... ...
@@ -119,20 +117,23 @@
119 119
 #define PEALIGN(o, a) (((a)) ? (((o) / (a)) * (a)) : (o))
120 120
 #define PESALIGN(o, a) (((a)) ? (((o) / (a) + ((o) % (a) != 0)) * (a)) : (o))
121 121
 
122
+// TODO Replace all of these with static inline functions
122 123
 #define CLI_UNPSIZELIMITS(NAME, CHK)                           \
123 124
     if (cli_checklimits(NAME, ctx, (CHK), 0, 0) != CL_CLEAN) { \
124
-        free(exe_sections);                                    \
125
+        cli_exe_info_destroy(peinfo);                          \
125 126
         return CL_CLEAN;                                       \
126 127
     }
127 128
 
128 129
 #define CLI_UNPTEMP(NAME, FREEME)                                                       \
129 130
     if (!(tempfile = cli_gentemp(ctx->engine->tmpdir))) {                               \
131
+        cli_exe_info_destroy(peinfo);                                                   \
130 132
         cli_multifree FREEME;                                                           \
131 133
         return CL_EMEM;                                                                 \
132 134
     }                                                                                   \
133 135
     if ((ndesc = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU)) < 0) { \
134 136
         cli_dbgmsg(NAME ": Can't create file %s\n", tempfile);                          \
135 137
         free(tempfile);                                                                 \
138
+        cli_exe_info_destroy(peinfo);                                                   \
136 139
         cli_multifree FREEME;                                                           \
137 140
         return CL_ECREAT;                                                               \
138 141
     }
... ...
@@ -168,7 +169,7 @@
168 168
         cli_dbgmsg(NAME ": Successfully decompressed\n"); \
169 169
         close(ndesc);                                     \
170 170
         if (cli_unlink(tempfile)) {                       \
171
-            free(exe_sections);                           \
171
+            cli_exe_info_destroy(peinfo);                 \
172 172
             free(tempfile);                               \
173 173
             FREESEC;                                      \
174 174
             return CL_EUNLINK;                            \
... ...
@@ -179,17 +180,17 @@
179 179
         upx_success = 1;                                  \
180 180
         break; /* FSG ONLY! - scan raw data after upx block */
181 181
 
182
-#define SPINCASE()                             \
183
-    case 2:                                    \
184
-        free(spinned);                         \
185
-        close(ndesc);                          \
186
-        if (cli_unlink(tempfile)) {            \
187
-            free(exe_sections);                \
188
-            free(tempfile);                    \
189
-            return CL_EUNLINK;                 \
190
-        }                                      \
191
-        cli_dbgmsg("PESpin: Size exceeded\n"); \
192
-        free(tempfile);                        \
182
+#define SPINCASE()                                         \
183
+    case 2:                                                \
184
+        free(spinned);                                     \
185
+        close(ndesc);                                      \
186
+        if (cli_unlink(tempfile)) {                        \
187
+            cli_exe_info_destroy(peinfo);                  \
188
+            free(tempfile);                                \
189
+            return CL_EUNLINK;                             \
190
+        }                                                  \
191
+        cli_dbgmsg("cli_scanpe: PESpin: Size exceeded\n"); \
192
+        free(tempfile);                                    \
193 193
         break;
194 194
 
195 195
 #define CLI_UNPRESULTS_(NAME, FSGSTUFF, EXPR, GOOD, FREEME)                                   \
... ...
@@ -200,15 +201,15 @@
200 200
             else                                                                              \
201 201
                 cli_dbgmsg(NAME ": Unpacked and rebuilt executable\n");                       \
202 202
             cli_multifree FREEME;                                                             \
203
-            free(exe_sections);                                                               \
203
+            cli_exe_info_destroy(peinfo);                                                     \
204 204
             lseek(ndesc, 0, SEEK_SET);                                                        \
205 205
             cli_dbgmsg("***** Scanning rebuilt PE file *****\n");                             \
206 206
             SHA_OFF;                                                                          \
207 207
             if (cli_magic_scandesc(ndesc, tempfile, ctx) == CL_VIRUS) {                       \
208 208
                 close(ndesc);                                                                 \
209
+                SHA_RESET;                                                                    \
209 210
                 CLI_TMPUNLK();                                                                \
210 211
                 free(tempfile);                                                               \
211
-                SHA_RESET;                                                                    \
212 212
                 return CL_VIRUS;                                                              \
213 213
             }                                                                                 \
214 214
             SHA_RESET;                                                                        \
... ...
@@ -223,7 +224,7 @@
223 223
             cli_dbgmsg(NAME ": Unpacking failed\n");                                          \
224 224
             close(ndesc);                                                                     \
225 225
             if (cli_unlink(tempfile)) {                                                       \
226
-                free(exe_sections);                                                           \
226
+                cli_exe_info_destroy(peinfo);                                                 \
227 227
                 free(tempfile);                                                               \
228 228
                 cli_multifree FREEME;                                                         \
229 229
                 return CL_EUNLINK;                                                            \
... ...
@@ -233,6 +234,9 @@
233 233
     }
234 234
 
235 235
 #define CLI_UNPRESULTS(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, (void)0, EXPR, GOOD, FREEME)
236
+// TODO The second argument to FSGCASE below should match what gets freed as
237
+// indicated by FREEME, otherwise a memory leak can occur (as currently used,
238
+// it looks like dest can get leaked by these macros).
236 239
 #define CLI_UNPRESULTSFSG1(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, FSGCASE(NAME, free(sections)), EXPR, GOOD, FREEME)
237 240
 #define CLI_UNPRESULTSFSG2(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, FSGCASE(NAME, (void)0), EXPR, GOOD, FREEME)
238 241
 
... ...
@@ -308,6 +312,8 @@ static int versioninfo_cb(void *opaque, uint32_t type, uint32_t name, uint32_t l
308 308
     return 0;
309 309
 }
310 310
 
311
+/* Given an RVA (relative to the ImageBase), return the file offset of the
312
+ * corresponding data */
311 313
 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)
312 314
 {
313 315
     int i, found = 0;
... ...
@@ -324,7 +330,7 @@ uint32_t cli_rawaddr(uint32_t rva, const struct cli_exe_section *shp, uint16_t n
324 324
     }
325 325
 
326 326
     for (i = nos - 1; i >= 0; i--) {
327
-        if (shp[i].rsz && shp[i].rva <= rva && shp[i].rsz > rva - shp[i].rva) {
327
+        if (shp[i].rsz && shp[i].rva <= rva && shp[i].rsz > (rva - shp[i].rva)) {
328 328
             found = 1;
329 329
             break;
330 330
         }
... ...
@@ -335,19 +341,19 @@ uint32_t cli_rawaddr(uint32_t rva, const struct cli_exe_section *shp, uint16_t n
335 335
         return 0;
336 336
     }
337 337
 
338
-    ret  = rva - shp[i].rva + shp[i].raw;
338
+    ret  = (rva - shp[i].rva) + shp[i].raw;
339 339
     *err = 0;
340 340
     return ret;
341 341
 }
342 342
 
343 343
 /* 
344
-   void findres(uint32_t by_type, uint32_t by_name, uint32_t res_rva, cli_ctx *ctx, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size, int (*cb)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *opaque)
344
+   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)
345 345
    callback based res lookup
346 346
 
347 347
    by_type: lookup type
348 348
    by_name: lookup name or (unsigned)-1 to look for any name
349 349
    res_rva: base resource rva (i.e. dirs[2].VirtualAddress)
350
-   ctx, exe_sections, nsections, hdr_size: same as in scanpe
350
+   map, peinfo: same as in scanpe
351 351
    cb: the callback function executed on each successful match
352 352
    opaque: an opaque pointer passed to the callback
353 353
 
... ...
@@ -355,14 +361,21 @@ uint32_t cli_rawaddr(uint32_t rva, const struct cli_exe_section *shp, uint16_t n
355 355
    int pe_res_cballback (void *opaque, uint32_t type, uint32_t name, uint32_t lang, uint32_t rva);
356 356
    the callback shall return 0 to continue the lookup or 1 to abort
357 357
 */
358
-void findres(uint32_t by_type, uint32_t by_name, uint32_t res_rva, fmap_t *map, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size, int (*cb)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *opaque)
358
+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)
359 359
 {
360 360
     unsigned int err = 0;
361 361
     uint32_t type, type_offs, name, name_offs, lang, lang_offs;
362 362
     const uint8_t *resdir, *type_entry, *name_entry, *lang_entry;
363 363
     uint16_t type_cnt, name_cnt, lang_cnt;
364
+    uint32_t res_rva;
365
+
366
+    if (NULL == peinfo || peinfo->ndatadirs < 3) {
367
+        return;
368
+    }
364 369
 
365
-    if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva, exe_sections, nsections, &err, map->len, hdr_size), 16)) || err)
370
+    res_rva = EC32(peinfo->dirs[2].VirtualAddress);
371
+
372
+    if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva, peinfo->sections, peinfo->nsections, &err, map->len, peinfo->hdr_size), 16)) || err)
366 373
         return;
367 374
 
368 375
     type_cnt   = (uint16_t)cli_readint16(resdir + 12);
... ...
@@ -379,7 +392,7 @@ void findres(uint32_t by_type, uint32_t by_name, uint32_t res_rva, fmap_t *map,
379 379
         type_offs = cli_readint32(type_entry + 4);
380 380
         if (type == by_type && (type_offs >> 31)) {
381 381
             type_offs &= 0x7fffffff;
382
-            if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva + type_offs, exe_sections, nsections, &err, map->len, hdr_size), 16)) || err)
382
+            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)
383 383
                 return;
384 384
 
385 385
             name_cnt   = (uint16_t)cli_readint16(resdir + 12);
... ...
@@ -397,7 +410,7 @@ void findres(uint32_t by_type, uint32_t by_name, uint32_t res_rva, fmap_t *map,
397 397
                 name_offs = cli_readint32(name_entry + 4);
398 398
                 if ((by_name == 0xffffffff || name == by_name) && (name_offs >> 31)) {
399 399
                     name_offs &= 0x7fffffff;
400
-                    if (!(resdir = fmap_need_off_once(map, cli_rawaddr(res_rva + name_offs, exe_sections, nsections, &err, map->len, hdr_size), 16)) || err)
400
+                    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)
401 401
                         return;
402 402
 
403 403
                     lang_cnt   = (uint16_t)cli_readint16(resdir + 12) + (uint16_t)cli_readint16(resdir + 14);
... ...
@@ -422,13 +435,13 @@ void findres(uint32_t by_type, uint32_t by_name, uint32_t res_rva, fmap_t *map,
422 422
     }
423 423
 }
424 424
 
425
-static void cli_parseres_special(uint32_t base, uint32_t rva, fmap_t *map, struct cli_exe_section *exe_sections, uint16_t nsections, size_t fsize, uint32_t hdr_size, unsigned int level, uint32_t type, unsigned int *maxres, struct swizz_stats *stats)
425
+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)
426 426
 {
427 427
     unsigned int err = 0, i;
428 428
     const uint8_t *resdir;
429 429
     const uint8_t *entry, *oentry;
430 430
     uint16_t named, unnamed;
431
-    uint32_t rawaddr = cli_rawaddr(rva, exe_sections, nsections, &err, fsize, hdr_size);
431
+    uint32_t rawaddr = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
432 432
     uint32_t entries;
433 433
 
434 434
     if (level > 2 || !*maxres) return;
... ...
@@ -453,7 +466,7 @@ static void cli_parseres_special(uint32_t base, uint32_t rva, fmap_t *map, struc
453 453
         id = cli_readint32(entry);
454 454
         offs = cli_readint32(entry+4);
455 455
         if(offs>>31)
456
-            cli_parseres( base, base + (offs&0x7fffffff), srcfd, exe_sections, nsections, fsize, hdr_size, level+1, type, maxres, stats);
456
+            cli_parseres( base, base + (offs&0x7fffffff), srcfd, peinfo, fsize, level+1, type, maxres, stats);
457 457
         entry+=8;
458 458
     }*/
459 459
     for (i = 0; i < unnamed; i++, entry += 8) {
... ...
@@ -489,14 +502,14 @@ static void cli_parseres_special(uint32_t base, uint32_t rva, fmap_t *map, struc
489 489
         }
490 490
         offs = cli_readint32(entry + 4);
491 491
         if (offs >> 31)
492
-            cli_parseres_special(base, base + (offs & 0x7fffffff), map, exe_sections, nsections, fsize, hdr_size, level + 1, type, maxres, stats);
492
+            cli_parseres_special(base, base + (offs & 0x7fffffff), map, peinfo, fsize, level + 1, type, maxres, stats);
493 493
         else {
494 494
             offs    = cli_readint32(entry + 4);
495
-            rawaddr = cli_rawaddr(base + offs, exe_sections, nsections, &err, fsize, hdr_size);
495
+            rawaddr = cli_rawaddr(base + offs, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
496 496
             if (!err && (resdir = fmap_need_off_once(map, rawaddr, 16))) {
497 497
                 uint32_t isz = cli_readint32(resdir + 4);
498 498
                 const uint8_t *str;
499
-                rawaddr = cli_rawaddr(cli_readint32(resdir), exe_sections, nsections, &err, fsize, hdr_size);
499
+                rawaddr = cli_rawaddr(cli_readint32(resdir), peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
500 500
                 if (err || !isz || isz >= fsize || rawaddr + isz >= fsize) {
501 501
                     cli_dbgmsg("cli_parseres_special: invalid resource table entry: %lu + %lu\n",
502 502
                                (unsigned long)rawaddr,
... ...
@@ -558,7 +571,7 @@ static int scan_pe_mdb(cli_ctx *ctx, struct cli_exe_section *exe_section)
558 558
         if (foundsize[type] || foundwild[type]) {
559 559
             hashset[type] = cli_malloc(hashlen[type]);
560 560
             if (!hashset[type]) {
561
-                cli_errmsg("scan_pe: cli_malloc failed!\n");
561
+                cli_errmsg("scan_pe_mdb: cli_malloc failed!\n");
562 562
                 for (; type > 0;)
563 563
                     free(hashset[--type]);
564 564
                 return CL_EMEM;
... ...
@@ -2228,7 +2241,7 @@ static int validate_impname(const char *name, uint32_t length, int dll)
2228 2228
     return 1;
2229 2229
 }
2230 2230
 
2231
-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_section *exe_sections, uint16_t nsections, uint32_t hdr_size, int pe_plus, int *first)
2231
+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)
2232 2232
 {
2233 2233
     uint32_t thuoff = 0, offset;
2234 2234
     fmap_t *map     = *ctx->fmap;
... ...
@@ -2244,9 +2257,9 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
2244 2244
 #endif
2245 2245
 
2246 2246
     if (image->u.OriginalFirstThunk)
2247
-        thuoff = cli_rawaddr(image->u.OriginalFirstThunk, exe_sections, nsections, &err, fsize, hdr_size);
2247
+        thuoff = cli_rawaddr(image->u.OriginalFirstThunk, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
2248 2248
     if (err || thuoff == 0)
2249
-        thuoff = cli_rawaddr(image->FirstThunk, exe_sections, nsections, &err, fsize, hdr_size);
2249
+        thuoff = cli_rawaddr(image->FirstThunk, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
2250 2250
     if (err) {
2251 2251
         cli_dbgmsg("scan_pe: invalid rva for image first thunk\n");
2252 2252
         return CL_EFORMAT;
... ...
@@ -2316,7 +2329,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
2316 2316
         }                                                                           \
2317 2317
     } while (0)
2318 2318
 
2319
-    if (!pe_plus) {
2319
+    if (!peinfo->is_pe32plus) {
2320 2320
         struct pe_image_thunk32 thunk32;
2321 2321
 
2322 2322
         while ((num_fns < PE_MAXIMPORTS) && (fmap_readn(map, &thunk32, thuoff, sizeof(struct pe_image_thunk32)) == sizeof(struct pe_image_thunk32)) && (thunk32.u.Ordinal != 0)) {
... ...
@@ -2326,7 +2339,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
2326 2326
             thunk32.u.Ordinal = EC32(thunk32.u.Ordinal);
2327 2327
 
2328 2328
             if (!(thunk32.u.Ordinal & PE_IMAGEDIR_ORDINAL_FLAG32)) {
2329
-                offset = cli_rawaddr(thunk32.u.Function, exe_sections, nsections, &err, fsize, hdr_size);
2329
+                offset = cli_rawaddr(thunk32.u.Function, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
2330 2330
 
2331 2331
                 if (!ret) {
2332 2332
                     /* Hint field is a uint16_t and precedes the Name field */
... ...
@@ -2362,7 +2375,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
2362 2362
             thunk64.u.Ordinal = EC64(thunk64.u.Ordinal);
2363 2363
 
2364 2364
             if (!(thunk64.u.Ordinal & PE_IMAGEDIR_ORDINAL_FLAG64)) {
2365
-                offset = cli_rawaddr(thunk64.u.Function, exe_sections, nsections, &err, fsize, hdr_size);
2365
+                offset = cli_rawaddr(thunk64.u.Function, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
2366 2366
 
2367 2367
                 if (!err) {
2368 2368
                     /* Hint field is a uint16_t and precedes the Name field */
... ...
@@ -2393,7 +2406,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
2393 2393
     return CL_SUCCESS;
2394 2394
 }
2395 2395
 
2396
-static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *impsz, int *genhash, struct pe_image_data_dir *datadir, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size, int pe_plus)
2396
+static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *impsz, int *genhash, struct cli_exe_info *peinfo)
2397 2397
 {
2398 2398
     struct pe_image_import_descriptor *image;
2399 2399
     fmap_t *map = *ctx->fmap;
... ...
@@ -2406,43 +2419,45 @@ static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *
2406 2406
     unsigned int err;
2407 2407
     int first = 1;
2408 2408
 
2409
-    if (datadir->VirtualAddress == 0 || datadir->Size == 0) {
2409
+    if (peinfo->dirs[1].VirtualAddress == 0 || peinfo->dirs[1].Size == 0) {
2410 2410
         cli_errmsg("scan_pe: import table data directory does not exist\n");
2411 2411
         return CL_SUCCESS;
2412 2412
     }
2413 2413
 
2414
-    impoff = cli_rawaddr(datadir->VirtualAddress, exe_sections, nsections, &err, fsize, hdr_size);
2415
-    if (err || impoff + datadir->Size > fsize) {
2414
+    // TODO Add EC32 wrappers
2415
+    impoff = cli_rawaddr(peinfo->dirs[1].VirtualAddress, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
2416
+    if (err || impoff + peinfo->dirs[1].Size > fsize) {
2416 2417
         cli_dbgmsg("scan_pe: invalid rva for import table data\n");
2417 2418
         return CL_SUCCESS;
2418 2419
     }
2419 2420
 
2420
-    impdes = fmap_need_off(map, impoff, datadir->Size);
2421
+    // TODO Add EC32 wrapper
2422
+    impdes = fmap_need_off(map, impoff, peinfo->dirs[1].Size);
2421 2423
     if (impdes == NULL) {
2422 2424
         cli_dbgmsg("scan_pe: failed to acquire fmap buffer\n");
2423 2425
         return CL_EREAD;
2424 2426
     }
2425
-    left = datadir->Size;
2427
+    left = peinfo->dirs[1].Size;
2426 2428
 
2427 2429
     memset(hashctx, 0, sizeof(hashctx));
2428 2430
     if (genhash[CLI_HASH_MD5]) {
2429 2431
         hashctx[CLI_HASH_MD5] = cl_hash_init("md5");
2430 2432
         if (hashctx[CLI_HASH_MD5] == NULL) {
2431
-            fmap_unneed_off(map, impoff, datadir->Size);
2433
+            fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
2432 2434
             return CL_EMEM;
2433 2435
         }
2434 2436
     }
2435 2437
     if (genhash[CLI_HASH_SHA1]) {
2436 2438
         hashctx[CLI_HASH_SHA1] = cl_hash_init("sha1");
2437 2439
         if (hashctx[CLI_HASH_SHA1] == NULL) {
2438
-            fmap_unneed_off(map, impoff, datadir->Size);
2440
+            fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
2439 2441
             return CL_EMEM;
2440 2442
         }
2441 2443
     }
2442 2444
     if (genhash[CLI_HASH_SHA256]) {
2443 2445
         hashctx[CLI_HASH_SHA256] = cl_hash_init("sha256");
2444 2446
         if (hashctx[CLI_HASH_SHA256] == NULL) {
2445
-            fmap_unneed_off(map, impoff, datadir->Size);
2447
+            fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
2446 2448
             return CL_EMEM;
2447 2449
         }
2448 2450
     }
... ...
@@ -2462,7 +2477,7 @@ static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *
2462 2462
         image->FirstThunk           = EC32(image->FirstThunk);
2463 2463
 
2464 2464
         /* DLL name acquisition */
2465
-        offset = cli_rawaddr(image->Name, exe_sections, nsections, &err, fsize, hdr_size);
2465
+        offset = cli_rawaddr(image->Name, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
2466 2466
         if (err || offset > fsize) {
2467 2467
             cli_dbgmsg("scan_pe: invalid rva for dll name\n");
2468 2468
             /* TODO: ignore or return? */
... ...
@@ -2495,7 +2510,7 @@ static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *
2495 2495
         }
2496 2496
 
2497 2497
         /* DLL function handling - inline function */
2498
-        ret = hash_impfns(ctx, hashctx, impsz, image, dllname, exe_sections, nsections, hdr_size, pe_plus, &first);
2498
+        ret = hash_impfns(ctx, hashctx, impsz, image, dllname, peinfo, &first);
2499 2499
         free(dllname);
2500 2500
         dllname = NULL;
2501 2501
         if (ret != CL_SUCCESS)
... ...
@@ -2505,13 +2520,13 @@ static unsigned int hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *
2505 2505
     }
2506 2506
 
2507 2507
 hash_imptbl_end:
2508
-    fmap_unneed_off(map, impoff, datadir->Size);
2508
+    fmap_unneed_off(map, impoff, peinfo->dirs[1].Size);
2509 2509
     for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
2510 2510
         cl_finish_hash(hashctx[type], digest[type]);
2511 2511
     return ret;
2512 2512
 }
2513 2513
 
2514
-static int scan_pe_imp(cli_ctx *ctx, struct pe_image_data_dir *dirs, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size, int pe_plus)
2514
+static int scan_pe_imp(cli_ctx *ctx, struct cli_exe_info *peinfo)
2515 2515
 {
2516 2516
     struct cli_matcher *imp = ctx->engine->hm_imp;
2517 2517
     unsigned char *hashset[CLI_HASH_AVAIL_TYPES];
... ...
@@ -2554,7 +2569,7 @@ static int scan_pe_imp(cli_ctx *ctx, struct pe_image_data_dir *dirs, struct cli_
2554 2554
     }
2555 2555
 
2556 2556
     /* Generate hashes */
2557
-    ret = hash_imptbl(ctx, hashset, &impsz, genhash, &dirs[1], exe_sections, nsections, hdr_size, pe_plus);
2557
+    ret = hash_imptbl(ctx, hashset, &impsz, genhash, peinfo);
2558 2558
     if (ret != CL_SUCCESS) {
2559 2559
         for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++)
2560 2560
             free(hashset[type]);
... ...
@@ -2719,44 +2734,27 @@ static void add_section_info(cli_ctx *ctx, struct cli_exe_section *s)
2719 2719
 
2720 2720
 int cli_scanpe(cli_ctx *ctx)
2721 2721
 {
2722
-    uint16_t e_magic; /* DOS signature ("MZ") */
2723
-    uint16_t nsections;
2724
-    uint32_t e_lfanew; /* address of new exe header */
2725
-    uint32_t ep, vep;  /* entry point (raw, virtual) */
2726 2722
     uint8_t polipos = 0;
2727
-    time_t timestamp;
2728
-    struct pe_image_file_hdr file_hdr;
2729
-    union {
2730
-        struct pe_image_optional_hdr64 opt64;
2731
-        struct pe_image_optional_hdr32 opt32;
2732
-    } pe_opt;
2733
-    struct pe_image_section_hdr *section_hdr;
2734
-    char sname[9], epbuff[4096], *tempfile;
2723
+    char epbuff[4096], *tempfile;
2735 2724
     uint32_t epsize;
2736
-    ssize_t bytes, at;
2737
-    unsigned int i, j, found, upx_success = 0, min = 0, max = 0, err, overlays = 0, rescan = 1;
2738
-    unsigned int ssize = 0, dsize = 0, dll = 0, pe_plus = 0, corrupted_cur;
2725
+    ssize_t bytes;
2726
+    unsigned int i, j, found, upx_success = 0, err;
2727
+    unsigned int ssize = 0, dsize = 0, corrupted_cur;
2739 2728
     int (*upxfn)(const char *, uint32_t, char *, uint32_t *, uint32_t, uint32_t, uint32_t) = NULL;
2740 2729
     const char *src                                                                        = NULL;
2741 2730
     char *dest                                                                             = NULL;
2742
-    int ndesc, ret = CL_CLEAN, upack = 0, native = 0;
2731
+    int ndesc, ret = CL_CLEAN, upack = 0;
2743 2732
     size_t fsize;
2744
-    uint32_t valign, falign, hdr_size;
2745
-    struct cli_exe_section *exe_sections;
2746
-    char timestr[32];
2747
-    struct pe_image_data_dir *dirs;
2748 2733
     struct cli_bc_ctx *bc_ctx;
2749 2734
     fmap_t *map;
2750 2735
     struct cli_pe_hook_data pedata;
2751 2736
 #ifdef HAVE__INTERNAL__SHA_COLLECT
2752 2737
     int sha_collect = ctx->sha_collect;
2753 2738
 #endif
2754
-    const char *archtype = NULL, *subsystem = NULL;
2755 2739
     uint32_t viruses_found = 0;
2756 2740
 #if HAVE_JSON
2757 2741
     int toval                   = 0;
2758 2742
     struct json_object *pe_json = NULL;
2759
-    char jsonbuf[128];
2760 2743
 #endif
2761 2744
 
2762 2745
     if (!ctx) {
... ...
@@ -2773,729 +2771,142 @@ int cli_scanpe(cli_ctx *ctx)
2773 2773
         pe_json = get_pe_property(ctx);
2774 2774
     }
2775 2775
 #endif
2776
-    map = *ctx->fmap;
2777
-    if (fmap_readn(map, &e_magic, 0, sizeof(e_magic)) != sizeof(e_magic)) {
2778
-        cli_dbgmsg("Can't read DOS signature\n");
2779
-        return CL_CLEAN;
2780
-    }
2781
-
2782
-    if (EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD) {
2783
-        cli_dbgmsg("Invalid DOS signature\n");
2784
-        return CL_CLEAN;
2785
-    }
2786
-
2787
-    if (fmap_readn(map, &e_lfanew, 58 + sizeof(e_magic), sizeof(e_lfanew)) != sizeof(e_lfanew)) {
2788
-        cli_dbgmsg("Can't read new header address\n");
2789
-        /* truncated header? */
2790
-        if (DETECT_BROKEN_PE) {
2791
-            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
2792
-            return ret;
2793
-        }
2794
-
2795
-        return CL_CLEAN;
2796
-    }
2776
+    map   = *ctx->fmap;
2777
+    fsize = map->len;
2797 2778
 
2798
-    e_lfanew = EC32(e_lfanew);
2799
-    cli_dbgmsg("e_lfanew == %d\n", e_lfanew);
2800
-    if (!e_lfanew) {
2801
-        cli_dbgmsg("Not a PE file\n");
2802
-        return CL_CLEAN;
2803
-    }
2779
+    struct cli_exe_info _peinfo;
2780
+    struct cli_exe_info *peinfo = &_peinfo;
2804 2781
 
2805
-    if (fmap_readn(map, &file_hdr, e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr)) {
2806
-        /* bad information in e_lfanew - probably not a PE file */
2807
-        cli_dbgmsg("cli_scanpe: Can't read file header\n");
2808
-        return CL_CLEAN;
2809
-    }
2782
+    uint32_t opts = CLI_PEHEADER_OPT_DBG_PRINT_INFO;
2783
+    ;
2810 2784
 
2811
-    if (EC32(file_hdr.Magic) != PE_IMAGE_NT_SIGNATURE) {
2812
-        cli_dbgmsg("Invalid PE signature (probably NE file)\n");
2813
-        return CL_CLEAN;
2814
-    }
2815
-
2816
-    if (EC16(file_hdr.Characteristics) & 0x2000) {
2817 2785
 #if HAVE_JSON
2818
-        if (pe_json != NULL)
2819
-            cli_jsonstr(pe_json, "Type", "DLL");
2820
-#endif
2821
-        cli_dbgmsg("File type: DLL\n");
2822
-        dll = 1;
2823
-    } else if (EC16(file_hdr.Characteristics) & 0x01) {
2824
-#if HAVE_JSON
2825
-        if (pe_json != NULL)
2826
-            cli_jsonstr(pe_json, "Type", "EXE");
2827
-#endif
2828
-        cli_dbgmsg("File type: Executable\n");
2829
-    }
2830
-
2831
-    switch (EC16(file_hdr.Machine)) {
2832
-        case 0x0:
2833
-            archtype = "Unknown";
2834
-            break;
2835
-        case 0x14c:
2836
-            archtype = "80386";
2837
-            break;
2838
-        case 0x14d:
2839
-            archtype = "80486";
2840
-            break;
2841
-        case 0x14e:
2842
-            archtype = "80586";
2843
-            break;
2844
-        case 0x160:
2845
-            archtype = "R30000 (big-endian)";
2846
-            break;
2847
-        case 0x162:
2848
-            archtype = "R3000";
2849
-            break;
2850
-        case 0x166:
2851
-            archtype = "R4000";
2852
-            break;
2853
-        case 0x168:
2854
-            archtype = "R10000";
2855
-            break;
2856
-        case 0x184:
2857
-            archtype = "DEC Alpha AXP";
2858
-            break;
2859
-        case 0x284:
2860
-            archtype = "DEC Alpha AXP 64bit";
2861
-            break;
2862
-        case 0x1f0:
2863
-            archtype = "PowerPC";
2864
-            break;
2865
-        case 0x200:
2866
-            archtype = "IA64";
2867
-            break;
2868
-        case 0x268:
2869
-            archtype = "M68k";
2870
-            break;
2871
-        case 0x266:
2872
-            archtype = "MIPS16";
2873
-            break;
2874
-        case 0x366:
2875
-            archtype = "MIPS+FPU";
2876
-            break;
2877
-        case 0x466:
2878
-            archtype = "MIPS16+FPU";
2879
-            break;
2880
-        case 0x1a2:
2881
-            archtype = "Hitachi SH3";
2882
-            break;
2883
-        case 0x1a3:
2884
-            archtype = "Hitachi SH3-DSP";
2885
-            break;
2886
-        case 0x1a4:
2887
-            archtype = "Hitachi SH3-E";
2888
-            break;
2889
-        case 0x1a6:
2890
-            archtype = "Hitachi SH4";
2891
-            break;
2892
-        case 0x1a8:
2893
-            archtype = "Hitachi SH5";
2894
-            break;
2895
-        case 0x1c0:
2896
-            archtype = "ARM";
2897
-            break;
2898
-        case 0x1c2:
2899
-            archtype = "THUMB";
2900
-            break;
2901
-        case 0x1d3:
2902
-            archtype = "AM33";
2903
-            break;
2904
-        case 0x520:
2905
-            archtype = "Infineon TriCore";
2906
-            break;
2907
-        case 0xcef:
2908
-            archtype = "CEF";
2909
-            break;
2910
-        case 0xebc:
2911
-            archtype = "EFI Byte Code";
2912
-            break;
2913
-        case 0x9041:
2914
-            archtype = "M32R";
2915
-            break;
2916
-        case 0xc0ee:
2917
-            archtype = "CEEE";
2918
-            break;
2919
-        case 0x8664:
2920
-            archtype = "AMD64";
2921
-            break;
2922
-        default:
2923
-            archtype = "Unknown";
2924
-    }
2925
-
2926
-    if ((archtype)) {
2927
-        cli_dbgmsg("Machine type: %s\n", archtype);
2928
-#if HAVE_JSON
2929
-        if (pe_json != NULL)
2930
-            cli_jsonstr(pe_json, "ArchType", archtype);
2931
-#endif
2786
+    if (SCAN_COLLECT_METADATA) {
2787
+        opts |= CLI_PEHEADER_OPT_COLLECT_JSON;
2932 2788
     }
2933
-
2934
-    nsections = EC16(file_hdr.NumberOfSections);
2935
-    if (nsections < 1 || nsections > PE_MAXSECTIONS) {
2936
-#if HAVE_JSON
2937
-        pe_add_heuristic_property(ctx, "BadNumberOfSections");
2938 2789
 #endif
2939
-        if (DETECT_BROKEN_PE) {
2940
-            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
2941
-            return ret;
2942
-        }
2943
-
2944
-        if (!ctx->corrupted_input) {
2945
-            if (nsections)
2946
-                cli_warnmsg("PE file contains %d sections\n", nsections);
2947
-            else
2948
-                cli_warnmsg("PE file contains no sections\n");
2949
-        }
2950 2790
 
2951
-        return CL_CLEAN;
2791
+    if (DETECT_BROKEN_PE) {
2792
+        opts |= CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS;
2952 2793
     }
2953 2794
 
2954
-    cli_dbgmsg("NumberOfSections: %d\n", nsections);
2795
+    cli_exe_info_init(peinfo, 0);
2955 2796
 
2956
-    timestamp = (time_t)EC32(file_hdr.TimeDateStamp);
2957
-    cli_dbgmsg("TimeDateStamp: %s", cli_ctime(&timestamp, timestr, sizeof(timestr)));
2958
-
2959
-#if HAVE_JSON
2960
-    if (pe_json != NULL)
2961
-        cli_jsonstr(pe_json, "TimeDateStamp", cli_ctime(&timestamp, timestr, sizeof(timestr)));
2962
-#endif
2797
+    ret = cli_peheader(map, peinfo, opts, ctx);
2963 2798
 
2964
-    cli_dbgmsg("SizeOfOptionalHeader: %x\n", EC16(file_hdr.SizeOfOptionalHeader));
2799
+    // Warn the user if PE header parsing failed - if it's a binary that runs
2800
+    // successfully on Windows, we need to relax our PE parsing standards so
2801
+    // that we make sure the executable gets scanned appropriately
2965 2802
 
2966
-#if HAVE_JSON
2967
-    if (pe_json != NULL)
2968
-        cli_jsonint(pe_json, "SizeOfOptionalHeader", EC16(file_hdr.SizeOfOptionalHeader));
2969
-#endif
2803
+#define PE_HDR_PARSE_FAIL_CONSEQUENCE "won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n"
2970 2804
 
2971
-    if (EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32)) {
2972
-#if HAVE_JSON
2973
-        pe_add_heuristic_property(ctx, "BadOptionalHeaderSize");
2974
-#endif
2975
-        cli_dbgmsg("SizeOfOptionalHeader too small\n");
2805
+    if (CLI_PEHEADER_RET_BROKEN_PE == ret) {
2976 2806
         if (DETECT_BROKEN_PE) {
2807
+            // TODO Handle allmatch
2977 2808
             ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
2978 2809
             return ret;
2979 2810
         }
2980
-
2811
+        cli_dbgmsg("cli_scanpe: PE header appears broken - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
2981 2812
         return CL_CLEAN;
2982
-    }
2983
-
2984
-    at = e_lfanew + sizeof(struct pe_image_file_hdr);
2985
-    if (fmap_readn(map, &optional_hdr32, at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
2986
-        cli_dbgmsg("Can't read optional file header\n");
2987
-        if (DETECT_BROKEN_PE) {
2988
-            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
2989
-            return ret;
2990
-        }
2991 2813
 
2814
+    } else if (CLI_PEHEADER_RET_JSON_TIMEOUT == ret) {
2815
+        cli_dbgmsg("cli_scanpe: JSON creation timed out - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
2816
+        return CL_ETIMEOUT;
2817
+    } else if (CLI_PEHEADER_RET_GENERIC_ERROR == ret) {
2818
+        cli_dbgmsg("cli_scanpe: An error occurred when parsing the PE header - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
2992 2819
         return CL_CLEAN;
2993 2820
     }
2994
-    at += sizeof(struct pe_image_optional_hdr32);
2995
-
2996
-    /* This will be a chicken and egg problem until we drop 9x */
2997
-    if (EC16(optional_hdr64.Magic) == PE32P_SIGNATURE) {
2998
-#if HAVE_JSON
2999
-        pe_add_heuristic_property(ctx, "BadOptionalHeaderSizePE32Plus");
3000
-#endif
3001
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr64)) {
3002
-            /* FIXME: need to play around a bit more with xp64 */
3003
-            cli_dbgmsg("Incorrect SizeOfOptionalHeader for PE32+\n");
3004
-
3005
-            if (DETECT_BROKEN_PE) {
3006
-                ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3007
-                return ret;
3008
-            }
3009
-
3010
-            return CL_CLEAN;
3011
-        }
3012
-        pe_plus = 1;
3013
-    }
3014
-
3015
-    if (!pe_plus) { /* PE */
3016
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr32)) {
3017
-            /* Seek to the end of the long header */
3018
-            at += EC16(file_hdr.SizeOfOptionalHeader) - sizeof(struct pe_image_optional_hdr32);
3019
-        }
3020 2821
 
2822
+    if (!peinfo->is_pe32plus) { /* PE */
3021 2823
         if (DCONF & PE_CONF_UPACK)
3022
-            upack = (EC16(file_hdr.SizeOfOptionalHeader) == 0x148);
3023
-
3024
-        vep      = EC32(optional_hdr32.AddressOfEntryPoint);
3025
-        hdr_size = EC32(optional_hdr32.SizeOfHeaders);
3026
-        cli_dbgmsg("File format: PE\n");
3027
-
3028
-        cli_dbgmsg("MajorLinkerVersion: %d\n", optional_hdr32.MajorLinkerVersion);
3029
-        cli_dbgmsg("MinorLinkerVersion: %d\n", optional_hdr32.MinorLinkerVersion);
3030
-        cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(optional_hdr32.SizeOfCode));
3031
-        cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(optional_hdr32.SizeOfInitializedData));
3032
-        cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(optional_hdr32.SizeOfUninitializedData));
3033
-        cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", vep);
3034
-        cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(optional_hdr32.BaseOfCode));
3035
-        cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(optional_hdr32.SectionAlignment));
3036
-        cli_dbgmsg("FileAlignment: 0x%x\n", EC32(optional_hdr32.FileAlignment));
3037
-        cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(optional_hdr32.MajorSubsystemVersion));
3038
-        cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(optional_hdr32.MinorSubsystemVersion));
3039
-        cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(optional_hdr32.SizeOfImage));
3040
-        cli_dbgmsg("SizeOfHeaders: 0x%x\n", hdr_size);
3041
-        cli_dbgmsg("NumberOfRvaAndSizes: %d\n", EC32(optional_hdr32.NumberOfRvaAndSizes));
3042
-        dirs = optional_hdr32.DataDirectory;
3043
-#if HAVE_JSON
3044
-        if (pe_json != NULL) {
3045
-            cli_jsonint(pe_json, "MajorLinkerVersion", optional_hdr32.MajorLinkerVersion);
3046
-            cli_jsonint(pe_json, "MinorLinkerVersion", optional_hdr32.MinorLinkerVersion);
3047
-            cli_jsonint(pe_json, "SizeOfCode", EC32(optional_hdr32.SizeOfCode));
3048
-            cli_jsonint(pe_json, "SizeOfInitializedData", EC32(optional_hdr32.SizeOfInitializedData));
3049
-            cli_jsonint(pe_json, "SizeOfUninitializedData", EC32(optional_hdr32.SizeOfUninitializedData));
3050
-            cli_jsonint(pe_json, "NumberOfRvaAndSizes", EC32(optional_hdr32.NumberOfRvaAndSizes));
3051
-            cli_jsonint(pe_json, "MajorSubsystemVersion", EC16(optional_hdr32.MajorSubsystemVersion));
3052
-            cli_jsonint(pe_json, "MinorSubsystemVersion", EC16(optional_hdr32.MinorSubsystemVersion));
3053
-
3054
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr32.BaseOfCode));
3055
-            cli_jsonstr(pe_json, "BaseOfCode", jsonbuf);
3056
-
3057
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr32.SectionAlignment));
3058
-            cli_jsonstr(pe_json, "SectionAlignment", jsonbuf);
3059
-
3060
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr32.FileAlignment));
3061
-            cli_jsonstr(pe_json, "FileAlignment", jsonbuf);
3062
-
3063
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr32.SizeOfImage));
3064
-            cli_jsonstr(pe_json, "SizeOfImage", jsonbuf);
3065
-
3066
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", hdr_size);
3067
-            cli_jsonstr(pe_json, "SizeOfHeaders", jsonbuf);
3068
-        }
3069
-#endif
3070
-
3071
-    } else { /* PE+ */
3072
-        /* read the remaining part of the header */
3073
-        if (fmap_readn(map, &optional_hdr32 + 1, at, sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) {
3074
-            cli_dbgmsg("Can't read optional file header\n");
3075
-            if (DETECT_BROKEN_PE) {
3076
-                ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3077
-                return ret;
3078
-            }
3079
-
3080
-            return CL_CLEAN;
3081
-        }
3082
-
3083
-        at += sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
3084
-        vep      = EC32(optional_hdr64.AddressOfEntryPoint);
3085
-        hdr_size = EC32(optional_hdr64.SizeOfHeaders);
3086
-        cli_dbgmsg("File format: PE32+\n");
3087
-
3088
-        cli_dbgmsg("MajorLinkerVersion: %d\n", optional_hdr64.MajorLinkerVersion);
3089
-        cli_dbgmsg("MinorLinkerVersion: %d\n", optional_hdr64.MinorLinkerVersion);
3090
-        cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(optional_hdr64.SizeOfCode));
3091
-        cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(optional_hdr64.SizeOfInitializedData));
3092
-        cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(optional_hdr64.SizeOfUninitializedData));
3093
-        cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", vep);
3094
-        cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(optional_hdr64.BaseOfCode));
3095
-        cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(optional_hdr64.SectionAlignment));
3096
-        cli_dbgmsg("FileAlignment: 0x%x\n", EC32(optional_hdr64.FileAlignment));
3097
-        cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(optional_hdr64.MajorSubsystemVersion));
3098
-        cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(optional_hdr64.MinorSubsystemVersion));
3099
-        cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(optional_hdr64.SizeOfImage));
3100
-        cli_dbgmsg("SizeOfHeaders: 0x%x\n", hdr_size);
3101
-        cli_dbgmsg("NumberOfRvaAndSizes: %d\n", EC32(optional_hdr64.NumberOfRvaAndSizes));
3102
-        dirs = optional_hdr64.DataDirectory;
3103
-#if HAVE_JSON
3104
-        if (pe_json != NULL) {
3105
-            cli_jsonint(pe_json, "MajorLinkerVersion", optional_hdr64.MajorLinkerVersion);
3106
-            cli_jsonint(pe_json, "MinorLinkerVersion", optional_hdr64.MinorLinkerVersion);
3107
-            cli_jsonint(pe_json, "SizeOfCode", EC32(optional_hdr64.SizeOfCode));
3108
-            cli_jsonint(pe_json, "SizeOfInitializedData", EC32(optional_hdr64.SizeOfInitializedData));
3109
-            cli_jsonint(pe_json, "SizeOfUninitializedData", EC32(optional_hdr64.SizeOfUninitializedData));
3110
-            cli_jsonint(pe_json, "NumberOfRvaAndSizes", EC32(optional_hdr64.NumberOfRvaAndSizes));
3111
-            cli_jsonint(pe_json, "MajorSubsystemVersion", EC16(optional_hdr64.MajorSubsystemVersion));
3112
-            cli_jsonint(pe_json, "MinorSubsystemVersion", EC16(optional_hdr64.MinorSubsystemVersion));
3113
-
3114
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr64.BaseOfCode));
3115
-            cli_jsonstr(pe_json, "BaseOfCode", jsonbuf);
3116
-
3117
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr64.SectionAlignment));
3118
-            cli_jsonstr(pe_json, "SectionAlignment", jsonbuf);
3119
-
3120
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr64.FileAlignment));
3121
-            cli_jsonstr(pe_json, "FileAlignment", jsonbuf);
3122
-
3123
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(optional_hdr64.SizeOfImage));
3124
-            cli_jsonstr(pe_json, "SizeOfImage", jsonbuf);
3125
-
3126
-            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", hdr_size);
3127
-            cli_jsonstr(pe_json, "SizeOfHeaders", jsonbuf);
3128
-        }
3129
-#endif
3130
-    }
3131
-
3132
-#if HAVE_JSON
3133
-    if (SCAN_COLLECT_METADATA) {
3134
-        snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", vep);
3135
-        if (pe_json != NULL)
3136
-            cli_jsonstr(pe_json, "EntryPoint", jsonbuf);
3137
-    }
3138
-#endif
3139
-
3140
-    switch (pe_plus ? EC16(optional_hdr64.Subsystem) : EC16(optional_hdr32.Subsystem)) {
3141
-        case 0:
3142
-            subsystem = "Unknown";
3143
-            break;
3144
-        case 1:
3145
-            subsystem = "Native (svc)";
3146
-            native    = 1;
3147
-            break;
3148
-        case 2:
3149
-            subsystem = "Win32 GUI";
3150
-            break;
3151
-        case 3:
3152
-            subsystem = "Win32 console";
3153
-            break;
3154
-        case 5:
3155
-            subsystem = "OS/2 console";
3156
-            break;
3157
-        case 7:
3158
-            subsystem = "POSIX console";
3159
-            break;
3160
-        case 8:
3161
-            subsystem = "Native Win9x driver";
3162
-            break;
3163
-        case 9:
3164
-            subsystem = "WinCE GUI";
3165
-            break;
3166
-        case 10:
3167
-            subsystem = "EFI application";
3168
-            break;
3169
-        case 11:
3170
-            subsystem = "EFI driver";
3171
-            break;
3172
-        case 12:
3173
-            subsystem = "EFI runtime driver";
3174
-            break;
3175
-        case 13:
3176
-            subsystem = "EFI ROM image";
3177
-            break;
3178
-        case 14:
3179
-            subsystem = "Xbox";
3180
-            break;
3181
-        case 16:
3182
-            subsystem = "Boot application";
3183
-            break;
3184
-        default:
3185
-            subsystem = "Unknown";
2824
+            upack = (EC16(peinfo->file_hdr.SizeOfOptionalHeader) == 0x148);
3186 2825
     }
2826
+    for (i = 0; i < peinfo->nsections; i++) {
3187 2827
 
3188
-    cli_dbgmsg("Subsystem: %s\n", subsystem);
3189
-
3190
-#if HAVE_JSON
3191
-    if (pe_json != NULL)
3192
-        cli_jsonstr(pe_json, "Subsystem", subsystem);
3193
-#endif
3194
-
3195
-    cli_dbgmsg("------------------------------------\n");
3196
-
3197
-    if (DETECT_BROKEN_PE && !native && (!(pe_plus ? EC32(optional_hdr64.SectionAlignment) : EC32(optional_hdr32.SectionAlignment)) || (pe_plus ? EC32(optional_hdr64.SectionAlignment) : EC32(optional_hdr32.SectionAlignment)) % 0x1000)) {
3198
-        cli_dbgmsg("Bad virtual alignment\n");
3199
-        ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3200
-        return ret;
3201
-    }
3202
-
3203
-    if (DETECT_BROKEN_PE && !native && (!(pe_plus ? EC32(optional_hdr64.FileAlignment) : EC32(optional_hdr32.FileAlignment)) || (pe_plus ? EC32(optional_hdr64.FileAlignment) : EC32(optional_hdr32.FileAlignment)) % 0x200)) {
3204
-        cli_dbgmsg("Bad file alignment\n");
3205
-        ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3206
-        return ret;
3207
-    }
3208
-
3209
-    fsize = map->len;
3210
-
3211
-    section_hdr = (struct pe_image_section_hdr *)cli_calloc(nsections, sizeof(struct pe_image_section_hdr));
3212
-
3213
-    if (!section_hdr) {
3214
-        cli_dbgmsg("Can't allocate memory for section headers\n");
3215
-        return CL_EMEM;
3216
-    }
3217
-
3218
-    exe_sections = (struct cli_exe_section *)cli_calloc(nsections, sizeof(struct cli_exe_section));
3219
-
3220
-    if (!exe_sections) {
3221
-        cli_dbgmsg("Can't allocate memory for section headers\n");
3222
-        free(section_hdr);
3223
-        return CL_EMEM;
3224
-    }
3225
-
3226
-    valign = (pe_plus) ? EC32(optional_hdr64.SectionAlignment) : EC32(optional_hdr32.SectionAlignment);
3227
-    falign = (pe_plus) ? EC32(optional_hdr64.FileAlignment) : EC32(optional_hdr32.FileAlignment);
3228
-
3229
-    if (fmap_readn(map, section_hdr, at, sizeof(struct pe_image_section_hdr) * nsections) != (int)(nsections * sizeof(struct pe_image_section_hdr))) {
3230
-        cli_dbgmsg("Can't read section header\n");
3231
-        cli_dbgmsg("Possibly broken PE file\n");
3232
-
3233
-        free(section_hdr);
3234
-        free(exe_sections);
3235
-
3236
-        if (DETECT_BROKEN_PE) {
3237
-            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3238
-            return ret;
3239
-        }
3240
-
3241
-        return CL_CLEAN;
3242
-    }
3243
-
3244
-    at += sizeof(struct pe_image_section_hdr) * nsections;
3245
-
3246
-    for (i = 0; falign != 0x200 && i < nsections; i++) {
3247
-        /* file alignment fallback mode - blah */
3248
-        if (falign && section_hdr[i].SizeOfRawData && EC32(section_hdr[i].PointerToRawData) % falign && !(EC32(section_hdr[i].PointerToRawData) % 0x200)) {
3249
-            cli_dbgmsg("Found misaligned section, using 0x200\n");
3250
-            falign = 0x200;
3251
-        }
3252
-    }
3253
-
3254
-    hdr_size = PESALIGN(hdr_size, valign); /* Aligned headers virtual size */
3255
-
3256
-#if HAVE_JSON
3257
-    if (pe_json != NULL)
3258
-        cli_jsonint(pe_json, "NumberOfSections", nsections);
3259
-#endif
3260
-
3261
-    while (rescan == 1) {
3262
-        rescan = 0;
3263
-        for (i = 0; i < nsections; i++) {
3264
-            exe_sections[i].rva  = PEALIGN(EC32(section_hdr[i].VirtualAddress), valign);
3265
-            exe_sections[i].vsz  = PESALIGN(EC32(section_hdr[i].VirtualSize), valign);
3266
-            exe_sections[i].raw  = PEALIGN(EC32(section_hdr[i].PointerToRawData), falign);
3267
-            exe_sections[i].rsz  = PESALIGN(EC32(section_hdr[i].SizeOfRawData), falign);
3268
-            exe_sections[i].chr  = EC32(section_hdr[i].Characteristics);
3269
-            exe_sections[i].urva = EC32(section_hdr[i].VirtualAddress); /* Just in case */
3270
-            exe_sections[i].uvsz = EC32(section_hdr[i].VirtualSize);
3271
-            exe_sections[i].uraw = EC32(section_hdr[i].PointerToRawData);
3272
-            exe_sections[i].ursz = EC32(section_hdr[i].SizeOfRawData);
3273
-
3274
-            if (exe_sections[i].rsz) { /* Don't bother with virtual only sections */
3275
-                if (exe_sections[i].raw >= fsize || exe_sections[i].uraw > fsize) {
3276
-                    cli_dbgmsg("Broken PE file - Section %d starts or exists beyond the end of file (Offset@ %lu, Total filesize %lu)\n", i, (unsigned long)exe_sections[i].raw, (unsigned long)fsize);
3277
-                    if (nsections == 1) {
3278
-                        free(section_hdr);
3279
-                        free(exe_sections);
3280
-
3281
-                        if (DETECT_BROKEN_PE) {
3282
-                            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3283
-                            return ret;
3284
-                        }
3285
-
3286
-                        return CL_CLEAN; /* no ninjas to see here! move along! */
3287
-                    }
3288
-
3289
-                    for (j = i; j < nsections - 1; j++)
3290
-                        memcpy(&exe_sections[j], &exe_sections[j + 1], sizeof(struct cli_exe_section));
3291
-
3292
-                    for (j = i; j < nsections - 1; j++)
3293
-                        memcpy(&section_hdr[j], &section_hdr[j + 1], sizeof(struct pe_image_section_hdr));
3294
-
3295
-                    nsections--;
3296
-                    rescan = 1;
3297
-                    break;
3298
-                }
3299
-
3300
-                if (!CLI_ISCONTAINED(0, (uint32_t)fsize, exe_sections[i].raw, exe_sections[i].rsz))
3301
-                    exe_sections[i].rsz = fsize - exe_sections[i].raw;
3302
-
3303
-                if (!CLI_ISCONTAINED(0, fsize, exe_sections[i].uraw, exe_sections[i].ursz))
3304
-                    exe_sections[i].ursz = fsize - exe_sections[i].uraw;
3305
-            }
3306
-        }
3307
-    }
3308
-
3309
-    for (i = 0; i < nsections; i++) {
3310
-        strncpy(sname, (char *)section_hdr[i].Name, 8);
3311
-        sname[8] = 0;
3312
-
3313
-#if HAVE_JSON
3314
-        add_section_info(ctx, &exe_sections[i]);
3315
-
3316
-        if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
3317
-            free(section_hdr);
3318
-            free(exe_sections);
3319
-            return CL_ETIMEOUT;
3320
-        }
3321
-#endif
3322
-
3323
-        if (!exe_sections[i].vsz && exe_sections[i].rsz)
3324
-            exe_sections[i].vsz = PESALIGN(exe_sections[i].ursz, valign);
3325
-
3326
-        cli_dbgmsg("Section %d\n", i);
3327
-        cli_dbgmsg("Section name: %s\n", sname);
3328
-        cli_dbgmsg("Section data (from headers - in memory)\n");
3329
-        cli_dbgmsg("VirtualSize: 0x%x 0x%x\n", exe_sections[i].uvsz, exe_sections[i].vsz);
3330
-        cli_dbgmsg("VirtualAddress: 0x%x 0x%x\n", exe_sections[i].urva, exe_sections[i].rva);
3331
-        cli_dbgmsg("SizeOfRawData: 0x%x 0x%x\n", exe_sections[i].ursz, exe_sections[i].rsz);
3332
-        cli_dbgmsg("PointerToRawData: 0x%x 0x%x\n", exe_sections[i].uraw, exe_sections[i].raw);
3333
-
3334
-        if (exe_sections[i].chr & 0x20) {
3335
-            cli_dbgmsg("Section contains executable code\n");
3336
-
3337
-            if (exe_sections[i].vsz < exe_sections[i].rsz) {
3338
-                cli_dbgmsg("Section contains free space\n");
3339
-                /*
3340
-                cli_dbgmsg("Dumping %d bytes\n", section_hdr.SizeOfRawData - section_hdr.VirtualSize);
3341
-                ddump(desc, section_hdr.PointerToRawData + section_hdr.VirtualSize, section_hdr.SizeOfRawData - section_hdr.VirtualSize, cli_gentemp(NULL));
3342
-                */
3343
-            }
3344
-        }
3345
-
3346
-        if (exe_sections[i].chr & 0x20000000)
3347
-            cli_dbgmsg("Section's memory is executable\n");
3348
-
3349
-        if (exe_sections[i].chr & 0x80000000)
3350
-            cli_dbgmsg("Section's memory is writeable\n");
3351
-
3352
-        if (DETECT_BROKEN_PE && (!valign || (exe_sections[i].urva % valign))) { /* Bad virtual alignment */
3353
-            cli_dbgmsg("VirtualAddress is misaligned\n");
3354
-            cli_dbgmsg("------------------------------------\n");
3355
-            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3356
-            free(section_hdr);
3357
-            free(exe_sections);
3358
-            return ret;
3359
-        }
3360
-
3361
-        if (exe_sections[i].rsz) { /* Don't bother with virtual only sections */
3362
-            if (SCAN_HEURISTICS && (DCONF & PE_CONF_POLIPOS) && !*sname && exe_sections[i].vsz > 40000 && exe_sections[i].vsz < 70000 && exe_sections[i].chr == 0xe0000060) polipos = i;
2828
+        if (peinfo->sections[i].rsz) { /* Don't bother with virtual only sections */
2829
+            // TODO Regarding the commented out check below:
2830
+            // This used to check that the section name was NULL, but now that
2831
+            // header parsing is done in cli_peheader (and since we don't yet
2832
+            // make the section name availabe via peinfo->sections[]) it would
2833
+            // be a pain to fetch the name here.  Since this is the only place
2834
+            // in cli_scanpe that needs the section name, and since I verified
2835
+            // that detection still occurs for Polipos without this check,
2836
+            // let's leave it commented out for now.
2837
+            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;
3363 2838
 
3364 2839
             /* check hash section sigs */
3365 2840
             if ((DCONF & PE_CONF_MD5SECT) && ctx->engine->hm_mdb) {
3366
-                ret = scan_pe_mdb(ctx, &exe_sections[i]);
2841
+                ret = scan_pe_mdb(ctx, &(peinfo->sections[i]));
3367 2842
                 if (ret != CL_CLEAN) {
2843
+                    // TODO Handle allmatch
3368 2844
                     if (ret != CL_VIRUS)
3369
-                        cli_errmsg("scan_pe: scan_pe_mdb failed: %s!\n", cl_strerror(ret));
2845
+                        cli_errmsg("cli_scanpe: scan_pe_mdb failed: %s!\n", cl_strerror(ret));
3370 2846
 
3371 2847
                     cli_dbgmsg("------------------------------------\n");
3372
-                    free(section_hdr);
3373
-                    free(exe_sections);
2848
+                    cli_exe_info_destroy(peinfo);
3374 2849
                     return ret;
3375 2850
                 }
3376 2851
             }
3377 2852
         }
3378
-        cli_dbgmsg("------------------------------------\n");
3379
-
3380
-        if (exe_sections[i].urva >> 31 || exe_sections[i].uvsz >> 31 || (exe_sections[i].rsz && exe_sections[i].uraw >> 31) || exe_sections[i].ursz >> 31) {
3381
-            cli_dbgmsg("Found PE values with sign bit set\n");
3382
-
3383
-            free(section_hdr);
3384
-            free(exe_sections);
3385
-            if (DETECT_BROKEN_PE) {
3386
-                ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3387
-                return ret;
3388
-            }
3389
-
3390
-            return CL_CLEAN;
3391
-        }
3392
-
3393
-        if (!i) {
3394
-            if (DETECT_BROKEN_PE && exe_sections[i].urva != hdr_size) { /* Bad first section RVA */
3395
-                cli_dbgmsg("First section is in the wrong place\n");
3396
-                ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3397
-                free(section_hdr);
3398
-                free(exe_sections);
3399
-                return ret;
3400
-            }
3401
-
3402
-            min = exe_sections[i].rva;
3403
-            max = exe_sections[i].rva + exe_sections[i].rsz;
3404
-        } else {
3405
-            if (DETECT_BROKEN_PE && exe_sections[i].urva - exe_sections[i - 1].urva != exe_sections[i - 1].vsz) { /* No holes, no overlapping, no virtual disorder */
3406
-                cli_dbgmsg("Virtually misplaced section (wrong order, overlapping, non contiguous)\n");
3407
-                ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3408
-                free(section_hdr);
3409
-                free(exe_sections);
3410
-                return ret;
3411
-            }
3412
-
3413
-            if (exe_sections[i].rva < min)
3414
-                min = exe_sections[i].rva;
3415
-
3416
-            if (exe_sections[i].rva + exe_sections[i].rsz > max) {
3417
-                max      = exe_sections[i].rva + exe_sections[i].rsz;
3418
-                overlays = exe_sections[i].raw + exe_sections[i].rsz;
3419
-            }
3420
-        }
3421 2853
     }
3422 2854
 
3423
-    free(section_hdr);
3424
-
3425
-    if (!(ep = cli_rawaddr(vep, exe_sections, nsections, &err, fsize, hdr_size)) && err) {
3426
-        cli_dbgmsg("EntryPoint out of file\n");
3427
-        free(exe_sections);
3428
-        if (DETECT_BROKEN_PE) {
3429
-            ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
3430
-            return ret;
3431
-        }
3432
-
2855
+    // TODO Don't bail out here
2856
+    if (peinfo->is_pe32plus) { /* Do not continue for PE32+ files */
2857
+        cli_exe_info_destroy(peinfo);
3433 2858
         return CL_CLEAN;
3434 2859
     }
3435 2860
 
3436
-#if HAVE_JSON
3437
-    if (pe_json != NULL)
3438
-        cli_jsonint(pe_json, "EntryPointOffset", ep);
3439
-
3440
-    if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
3441
-        return CL_ETIMEOUT;
3442
-    }
3443
-#endif
3444
-
3445
-    cli_dbgmsg("EntryPoint offset: 0x%x (%d)\n", ep, ep);
3446
-
3447
-    if (pe_plus) { /* Do not continue for PE32+ files */
3448
-        free(exe_sections);
3449
-        return CL_CLEAN;
3450
-    }
3451
-
3452
-    epsize = fmap_readn(map, epbuff, ep, 4096);
2861
+    epsize = fmap_readn(map, epbuff, peinfo->ep, 4096);
3453 2862
 
3454 2863
     /* Disasm scan disabled since it's now handled by the bytecode */
3455 2864
 
3456
-    /* CLI_UNPTEMP("DISASM",(exe_sections,0)); */
2865
+    /* CLI_UNPTEMP("cli_scanpe: DISASM",(peinfo->sections,0)); */
3457 2866
     /* if(disasmbuf((unsigned char*)epbuff, epsize, ndesc)) */
3458 2867
     /*  ret = cli_scandesc(ndesc, ctx, CL_TYPE_PE_DISASM, 1, NULL, AC_SCAN_VIR); */
3459 2868
     /* close(ndesc); */
3460
-    /* CLI_TMPUNLK(); */
3461
-    /* free(tempfile); */
3462 2869
     /* if(ret == CL_VIRUS) { */
3463
-    /*  free(exe_sections); */
2870
+    /*  cli_exe_info_destroy(peinfo); */
2871
+    /*  CLI_TMPUNLK(); */
2872
+    /*  free(tempfile); */
3464 2873
     /*  return ret; */
3465 2874
     /* } */
2875
+    /* CLI_TMPUNLK(); */
2876
+    /* free(tempfile); */
3466 2877
 
3467
-    if (overlays) {
3468
-        int overlays_sz = fsize - overlays;
3469
-        if (overlays_sz > 0) {
3470
-            ret = cli_scanishield(ctx, overlays, overlays_sz);
3471
-            if (ret != CL_CLEAN) {
3472
-                free(exe_sections);
3473
-                return ret;
3474
-            }
2878
+    if (peinfo->overlay_start && peinfo->overlay_size > 0) {
2879
+        ret = cli_scanishield(ctx, peinfo->overlay_start, peinfo->overlay_size);
2880
+        if (ret != CL_CLEAN) {
2881
+            // TODO Handle allmatch
2882
+            cli_exe_info_destroy(peinfo);
2883
+            return ret;
3475 2884
         }
3476 2885
     }
3477 2886
 
3478
-    pedata.nsections = nsections;
3479
-    pedata.ep        = ep;
2887
+    pedata.nsections = peinfo->nsections;
2888
+    pedata.ep        = peinfo->ep;
3480 2889
     pedata.offset    = 0;
3481
-    memcpy(&pedata.file_hdr, &file_hdr, sizeof(file_hdr));
3482
-    memcpy(&pedata.opt32, &pe_opt.opt32, sizeof(pe_opt.opt32));
3483
-    memcpy(&pedata.opt64, &pe_opt.opt64, sizeof(pe_opt.opt64));
3484
-    memcpy(&pedata.dirs, dirs, sizeof(pedata.dirs));
3485
-    pedata.e_lfanew    = e_lfanew;
3486
-    pedata.overlays    = overlays;
3487
-    pedata.overlays_sz = fsize - overlays;
3488
-    pedata.hdr_size    = hdr_size;
2890
+    memcpy(&pedata.file_hdr, &(peinfo->file_hdr), sizeof(peinfo->file_hdr));
2891
+    // TODO no need to copy both of these for each binary
2892
+    memcpy(&pedata.opt32, &(peinfo->pe_opt.opt32), sizeof(peinfo->pe_opt.opt32));
2893
+    memcpy(&pedata.opt64, &(peinfo->pe_opt.opt64), sizeof(peinfo->pe_opt.opt64));
2894
+    memcpy(&pedata.dirs, &(peinfo->dirs), sizeof(peinfo->dirs));
2895
+    // Gross
2896
+    memcpy(&pedata.opt32_dirs, &(peinfo->dirs), sizeof(peinfo->dirs));
2897
+    memcpy(&pedata.opt64_dirs, &(peinfo->dirs), sizeof(peinfo->dirs));
2898
+    pedata.e_lfanew    = peinfo->e_lfanew;
2899
+    pedata.overlays    = peinfo->overlay_start;
2900
+    pedata.overlays_sz = peinfo->overlay_size;
2901
+    pedata.hdr_size    = peinfo->hdr_size;
3489 2902
 
3490 2903
     /* Bytecode BC_PE_ALL hook */
3491 2904
     bc_ctx = cli_bytecode_context_alloc();
3492 2905
     if (!bc_ctx) {
3493 2906
         cli_errmsg("cli_scanpe: can't allocate memory for bc_ctx\n");
3494
-        free(exe_sections);
2907
+        cli_exe_info_destroy(peinfo);
3495 2908
         return CL_EMEM;
3496 2909
     }
3497 2910
 
3498
-    cli_bytecode_context_setpe(bc_ctx, &pedata, exe_sections);
2911
+    cli_bytecode_context_setpe(bc_ctx, &pedata, peinfo->sections);
3499 2912
     cli_bytecode_context_setctx(bc_ctx, ctx);
3500 2913
     ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_ALL, map);
3501 2914
     switch (ret) {
... ...
@@ -3504,7 +2915,8 @@ int cli_scanpe(cli_ctx *ctx)
3504 3504
             break;
3505 3505
         case CL_VIRUS:
3506 3506
         case CL_BREAK:
3507
-            free(exe_sections);
3507
+            // TODO Handle allmatch
3508
+            cli_exe_info_destroy(peinfo);
3508 3509
             cli_bytecode_context_destroy(bc_ctx);
3509 3510
             return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
3510 3511
     }
... ...
@@ -3517,7 +2929,7 @@ int cli_scanpe(cli_ctx *ctx)
3517 3517
 #else
3518 3518
     if (DCONF & PE_CONF_IMPTBL && ctx->engine->hm_imp) {
3519 3519
 #endif
3520
-        ret = scan_pe_imp(ctx, dirs, exe_sections, nsections, hdr_size, pe_plus);
3520
+        ret = scan_pe_imp(ctx, peinfo);
3521 3521
         switch (ret) {
3522 3522
             case CL_SUCCESS:
3523 3523
                 break;
... ...
@@ -3529,17 +2941,17 @@ int cli_scanpe(cli_ctx *ctx)
3529 3529
                     break;
3530 3530
                 /* intentional fall-through */
3531 3531
             case CL_BREAK:
3532
-                free(exe_sections);
3532
+                cli_exe_info_destroy(peinfo);
3533 3533
                 return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
3534 3534
             default:
3535
-                free(exe_sections);
3535
+                cli_exe_info_destroy(peinfo);
3536 3536
                 return ret;
3537 3537
         }
3538 3538
     }
3539 3539
     /* Attempt to detect some popular polymorphic viruses */
3540 3540
 
3541 3541
     /* W32.Parite.B */
3542
-    if (SCAN_HEURISTICS && (DCONF & PE_CONF_PARITE) && !dll && epsize == 4096 && ep == exe_sections[nsections - 1].raw) {
3542
+    if (SCAN_HEURISTICS && (DCONF & PE_CONF_PARITE) && !peinfo->is_dll && epsize == 4096 && peinfo->ep == peinfo->sections[peinfo->nsections - 1].raw) {
3543 3543
         const char *pt = cli_memstr(epbuff, 4040, "\x47\x65\x74\x50\x72\x6f\x63\x41\x64\x64\x72\x65\x73\x73\x00", 15);
3544 3544
         if (pt) {
3545 3545
             pt += 15;
... ...
@@ -3548,12 +2960,12 @@ int cli_scanpe(cli_ctx *ctx)
3548 3548
                 if (ret != CL_CLEAN) {
3549 3549
                     if (ret == CL_VIRUS) {
3550 3550
                         if (!SCAN_ALLMATCHES) {
3551
-                            free(exe_sections);
3551
+                            cli_exe_info_destroy(peinfo);
3552 3552
                             return ret;
3553 3553
                         } else
3554 3554
                             viruses_found++;
3555 3555
                     } else {
3556
-                        free(exe_sections);
3556
+                        cli_exe_info_destroy(peinfo);
3557 3557
                         return ret;
3558 3558
                     }
3559 3559
                 }
... ...
@@ -3562,7 +2974,7 @@ int cli_scanpe(cli_ctx *ctx)
3562 3562
     }
3563 3563
 
3564 3564
     /* Kriz */
3565
-    if (SCAN_HEURISTICS && (DCONF & PE_CONF_KRIZ) && epsize >= 200 && CLI_ISCONTAINED(exe_sections[nsections - 1].raw, exe_sections[nsections - 1].rsz, ep, 0x0fd2) && epbuff[1] == '\x9c' && epbuff[2] == '\x60') {
3565
+    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') {
3566 3566
         enum { KZSTRASH,
3567 3567
                KZSCDELTA,
3568 3568
                KZSPDELTA,
... ...
@@ -3577,7 +2989,7 @@ int cli_scanpe(cli_ctx *ctx)
3577 3577
         uint8_t *kzcode  = (uint8_t *)epbuff + 3;
3578 3578
         uint8_t kzdptr = 0xff, kzdsize = 0xff;
3579 3579
         int kzlen = 197, kzinitlen = 0xffff, kzxorlen = -1;
3580
-        cli_dbgmsg("in kriz\n");
3580
+        cli_dbgmsg("cli_scanpe: in kriz\n");
3581 3581
 
3582 3582
         while (*kzstate != KZSTOP) {
3583 3583
             uint8_t op;
... ...
@@ -3609,7 +3021,7 @@ int cli_scanpe(cli_ctx *ctx)
3609 3609
                                 kzstate++;
3610 3610
                                 op = 4; /* fake the register to avoid breaking out */
3611 3611
 
3612
-                                cli_dbgmsg("kriz: using #%d as size counter\n", kzdsize);
3612
+                                cli_dbgmsg("cli_scanpe: kriz: using #%d as size counter\n", kzdsize);
3613 3613
                             }
3614 3614
                             opsz = 4;
3615 3615
                         case 0x48:
... ...
@@ -3646,7 +3058,7 @@ int cli_scanpe(cli_ctx *ctx)
3646 3646
                 case KZSPDELTA:
3647 3647
                     if ((op & 0xf8) == 0x58 && (kzdptr = op - 0x58) != 4) {
3648 3648
                         kzstate++;
3649
-                        cli_dbgmsg("kriz: using #%d as pointer\n", kzdptr);
3649
+                        cli_dbgmsg("cli_scanpe: kriz: using #%d as pointer\n", kzdptr);
3650 3650
                     } else {
3651 3651
                         *kzstate = KZSTOP;
3652 3652
                     }
... ...
@@ -3680,30 +3092,30 @@ int cli_scanpe(cli_ctx *ctx)
3680 3680
                         if (ret != CL_CLEAN) {
3681 3681
                             if (ret == CL_VIRUS) {
3682 3682
                                 if (!SCAN_ALLMATCHES) {
3683
-                                    free(exe_sections);
3683
+                                    cli_exe_info_destroy(peinfo);
3684 3684
                                     return ret;
3685 3685
                                 } else
3686 3686
                                     viruses_found++;
3687 3687
                             } else {
3688
-                                free(exe_sections);
3688
+                                cli_exe_info_destroy(peinfo);
3689 3689
                                 return ret;
3690 3690
                             }
3691 3691
                         }
3692 3692
                     }
3693
-                    cli_dbgmsg("kriz: loop out of bounds, corrupted sample?\n");
3693
+                    cli_dbgmsg("cli_scanpe: kriz: loop out of bounds, corrupted sample?\n");
3694 3694
                     kzstate++;
3695 3695
             }
3696 3696
         }
3697 3697
     }
3698 3698
 
3699 3699
     /* W32.Magistr.A/B */
3700
-    if (SCAN_HEURISTICS && (DCONF & PE_CONF_MAGISTR) && !dll && (nsections > 1) && (exe_sections[nsections - 1].chr & 0x80000000)) {
3700
+    if (SCAN_HEURISTICS && (DCONF & PE_CONF_MAGISTR) && !peinfo->is_dll && (peinfo->nsections > 1) && (peinfo->sections[peinfo->nsections - 1].chr & 0x80000000)) {
3701 3701
         uint32_t rsize, vsize, dam = 0;
3702 3702
 
3703
-        vsize = exe_sections[nsections - 1].uvsz;
3704
-        rsize = exe_sections[nsections - 1].rsz;
3705
-        if (rsize < exe_sections[nsections - 1].ursz) {
3706
-            rsize = exe_sections[nsections - 1].ursz;
3703
+        vsize = peinfo->sections[peinfo->nsections - 1].uvsz;
3704
+        rsize = peinfo->sections[peinfo->nsections - 1].rsz;
3705
+        if (rsize < peinfo->sections[peinfo->nsections - 1].ursz) {
3706
+            rsize = peinfo->sections[peinfo->nsections - 1].ursz;
3707 3707
             dam   = 1;
3708 3708
         }
3709 3709
 
... ...
@@ -3711,18 +3123,18 @@ int cli_scanpe(cli_ctx *ctx)
3711 3711
             int bw = rsize < 0x7000 ? rsize : 0x7000;
3712 3712
             const char *tbuff;
3713 3713
 
3714
-            if ((tbuff = fmap_need_off_once(map, exe_sections[nsections - 1].raw + rsize - bw, 4096))) {
3714
+            if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
3715 3715
                 if (cli_memstr(tbuff, 4091, "\xe8\x2c\x61\x00\x00", 5)) {
3716 3716
                     ret = cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.A.dam" : "Heuristics.W32.Magistr.A");
3717 3717
                     if (ret != CL_CLEAN) {
3718 3718
                         if (ret == CL_VIRUS) {
3719 3719
                             if (!SCAN_ALLMATCHES) {
3720
-                                free(exe_sections);
3720
+                                cli_exe_info_destroy(peinfo);
3721 3721
                                 return ret;
3722 3722
                             } else
3723 3723
                                 viruses_found++;
3724 3724
                         } else {
3725
-                            free(exe_sections);
3725
+                            cli_exe_info_destroy(peinfo);
3726 3726
                             return ret;
3727 3727
                         }
3728 3728
                     }
... ...
@@ -3732,18 +3144,18 @@ int cli_scanpe(cli_ctx *ctx)
3732 3732
             int bw = rsize < 0x8000 ? rsize : 0x8000;
3733 3733
             const char *tbuff;
3734 3734
 
3735
-            if ((tbuff = fmap_need_off_once(map, exe_sections[nsections - 1].raw + rsize - bw, 4096))) {
3735
+            if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
3736 3736
                 if (cli_memstr(tbuff, 4091, "\xe8\x04\x72\x00\x00", 5)) {
3737 3737
                     ret = cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.B.dam" : "Heuristics.W32.Magistr.B");
3738 3738
                     if (ret != CL_CLEAN) {
3739 3739
                         if (ret == CL_VIRUS) {
3740 3740
                             if (!SCAN_ALLMATCHES) {
3741
-                                free(exe_sections);
3741
+                                cli_exe_info_destroy(peinfo);
3742 3742
                                 return ret;
3743 3743
                             } else
3744 3744
                                 viruses_found++;
3745 3745
                         } else {
3746
-                            free(exe_sections);
3746
+                            cli_exe_info_destroy(peinfo);
3747 3747
                             return ret;
3748 3748
                         }
3749 3749
                     }
... ...
@@ -3753,24 +3165,25 @@ int cli_scanpe(cli_ctx *ctx)
3753 3753
     }
3754 3754
 
3755 3755
     /* W32.Polipos.A */
3756
-    while (polipos && !dll && nsections > 2 && nsections < 13 && e_lfanew <= 0x800 && (EC16(optional_hdr32.Subsystem) == 2 || EC16(optional_hdr32.Subsystem) == 3) && EC16(file_hdr.Machine) == 0x14c && optional_hdr32.SizeOfStackReserve >= 0x80000) {
3756
+    // TODO Add endianness correction to SizeOfStackReserve access
3757
+    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) {
3757 3758
         uint32_t jump, jold, *jumps = NULL;
3758 3759
         const uint8_t *code;
3759 3760
         unsigned int xsjs = 0;
3760 3761
 
3761
-        if (exe_sections[0].rsz > CLI_MAX_ALLOCATION)
3762
+        if (peinfo->sections[0].rsz > CLI_MAX_ALLOCATION)
3762 3763
             break;
3763
-        if (exe_sections[0].rsz < 5)
3764
+        if (peinfo->sections[0].rsz < 5)
3764 3765
             break;
3765
-        if (!(code = fmap_need_off_once(map, exe_sections[0].raw, exe_sections[0].rsz)))
3766
+        if (!(code = fmap_need_off_once(map, peinfo->sections[0].raw, peinfo->sections[0].rsz)))
3766 3767
             break;
3767 3768
 
3768
-        for (i = 0; i < exe_sections[0].rsz - 5; i++) {
3769
+        for (i = 0; i < peinfo->sections[0].rsz - 5; i++) {
3769 3770
             if ((uint8_t)(code[i] - 0xe8) > 1)
3770 3771
                 continue;
3771 3772
 
3772
-            jump = cli_rawaddr(exe_sections[0].rva + i + 5 + cli_readint32(&code[i + 1]), exe_sections, nsections, &err, fsize, hdr_size);
3773
-            if (err || !CLI_ISCONTAINED(exe_sections[polipos].raw, exe_sections[polipos].rsz, jump, 9))
3773
+            jump = cli_rawaddr(peinfo->sections[0].rva + i + 5 + cli_readint32(&code[i + 1]), peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
3774
+            if (err || !CLI_ISCONTAINED(peinfo->sections[polipos].raw, peinfo->sections[polipos].rsz, jump, 9))
3774 3775
                 continue;
3775 3776
 
3776 3777
             if (xsjs % 128 == 0) {
... ...
@@ -3778,7 +3191,7 @@ int cli_scanpe(cli_ctx *ctx)
3778 3778
                     break;
3779 3779
 
3780 3780
                 if (!(jumps = (uint32_t *)cli_realloc2(jumps, (xsjs + 128) * sizeof(uint32_t)))) {
3781
-                    free(exe_sections);
3781
+                    cli_exe_info_destroy(peinfo);
3782 3782
                     return CL_EMEM;
3783 3783
                 }
3784 3784
             }
... ...
@@ -3804,7 +3217,7 @@ int cli_scanpe(cli_ctx *ctx)
3804 3804
         if (!xsjs)
3805 3805
             break;
3806 3806
 
3807
-        cli_dbgmsg("Polipos: Checking %d xsect jump(s)\n", xsjs);
3807
+        cli_dbgmsg("cli_scanpe: Polipos: Checking %d xsect jump(s)\n", xsjs);
3808 3808
         for (i = 0; i < xsjs; i++) {
3809 3809
             if (!(code = fmap_need_off_once(map, jumps[i], 9)))
3810 3810
                 continue;
... ...
@@ -3815,13 +3228,13 @@ int cli_scanpe(cli_ctx *ctx)
3815 3815
                     if (ret == CL_VIRUS) {
3816 3816
                         if (!SCAN_ALLMATCHES) {
3817 3817
                             free(jumps);
3818
-                            free(exe_sections);
3818
+                            cli_exe_info_destroy(peinfo);
3819 3819
                             return ret;
3820 3820
                         } else
3821 3821
                             viruses_found++;
3822 3822
                     } else {
3823 3823
                         free(jumps);
3824
-                        free(exe_sections);
3824
+                        cli_exe_info_destroy(peinfo);
3825 3825
                         return ret;
3826 3826
                     }
3827 3827
                 }
... ...
@@ -3833,30 +3246,30 @@ int cli_scanpe(cli_ctx *ctx)
3833 3833
     }
3834 3834
 
3835 3835
     /* Trojan.Swizzor.Gen */
3836
-    if (SCAN_HEURISTICS && (DCONF & PE_CONF_SWIZZOR) && nsections > 1 && fsize > 64 * 1024 && fsize < 4 * 1024 * 1024) {
3837
-        if (dirs[2].Size) {
3836
+    if (SCAN_HEURISTICS && (DCONF & PE_CONF_SWIZZOR) && peinfo->nsections > 1 && fsize > 64 * 1024 && fsize < 4 * 1024 * 1024) {
3837
+        if (peinfo->dirs[2].Size) {
3838 3838
             struct swizz_stats *stats = cli_calloc(1, sizeof(*stats));
3839 3839
             unsigned int m            = 1000;
3840 3840
             ret                       = CL_CLEAN;
3841 3841
 
3842 3842
             if (!stats) {
3843
-                free(exe_sections);
3843
+                cli_exe_info_destroy(peinfo);
3844 3844
                 return CL_EMEM;
3845 3845
             } else {
3846
-                cli_parseres_special(EC32(dirs[2].VirtualAddress), EC32(dirs[2].VirtualAddress), map, exe_sections, nsections, fsize, hdr_size, 0, 0, &m, stats);
3846
+                cli_parseres_special(EC32(peinfo->dirs[2].VirtualAddress), EC32(peinfo->dirs[2].VirtualAddress), map, peinfo, fsize, 0, 0, &m, stats);
3847 3847
                 if ((ret = cli_detect_swizz(stats)) == CL_VIRUS) {
3848 3848
                     ret = cli_append_virus(ctx, "Heuristics.Trojan.Swizzor.Gen");
3849 3849
                     if (ret != CL_CLEAN) {
3850 3850
                         if (ret == CL_VIRUS) {
3851 3851
                             if (!SCAN_ALLMATCHES) {
3852 3852
                                 free(stats);
3853
-                                free(exe_sections);
3853
+                                cli_exe_info_destroy(peinfo);
3854 3854
                                 return ret;
3855 3855
                             } else
3856 3856
                                 viruses_found++;
3857 3857
                         } else {
3858 3858
                             free(stats);
3859
-                            free(exe_sections);
3859
+                            cli_exe_info_destroy(peinfo);
3860 3860
                             return ret;
3861 3861
                         }
3862 3862
                     }
... ...
@@ -3874,10 +3287,10 @@ int cli_scanpe(cli_ctx *ctx)
3874 3874
     /* try to find the first section with physical size == 0 */
3875 3875
     found = 0;
3876 3876
     if (DCONF & (PE_CONF_UPX | PE_CONF_FSG | PE_CONF_MEW)) {
3877
-        for (i = 0; i < (unsigned int)nsections - 1; i++) {
3878
-            if (!exe_sections[i].rsz && exe_sections[i].vsz && exe_sections[i + 1].rsz && exe_sections[i + 1].vsz) {
3877
+        for (i = 0; i < (unsigned int)peinfo->nsections - 1; i++) {
3878
+            if (!peinfo->sections[i].rsz && peinfo->sections[i].vsz && peinfo->sections[i + 1].rsz && peinfo->sections[i + 1].vsz) {
3879 3879
                 found = 1;
3880
-                cli_dbgmsg("UPX/FSG/MEW: empty section found - assuming compression\n");
3880
+                cli_dbgmsg("cli_scanpe: UPX/FSG/MEW: empty section found - assuming compression\n");
3881 3881
 #if HAVE_JSON
3882 3882
                 if (pe_json != NULL)
3883 3883
                     cli_jsonbool(pe_json, "HasEmptySection", 1);
... ...
@@ -3892,85 +3305,85 @@ int cli_scanpe(cli_ctx *ctx)
3892 3892
         uint32_t fileoffset;
3893 3893
         const char *tbuff;
3894 3894
 
3895
-        fileoffset = (vep + cli_readint32(epbuff + 1) + 5);
3895
+        fileoffset = (peinfo->vep + cli_readint32(epbuff + 1) + 5);
3896 3896
         while (fileoffset == 0x154 || fileoffset == 0x158) {
3897 3897
             char *src;
3898 3898
             uint32_t offdiff, uselzma;
3899 3899
 
3900
-            cli_dbgmsg("MEW: found MEW characteristics %08X + %08X + 5 = %08X\n",
3901
-                       cli_readint32(epbuff + 1), vep, cli_readint32(epbuff + 1) + vep + 5);
3900
+            cli_dbgmsg("cli_scanpe: MEW: found MEW characteristics %08X + %08X + 5 = %08X\n",
3901
+                       cli_readint32(epbuff + 1), peinfo->vep, cli_readint32(epbuff + 1) + peinfo->vep + 5);
3902 3902
 
3903 3903
             if (!(tbuff = fmap_need_off_once(map, fileoffset, 0xb0)))
3904 3904
                 break;
3905 3905
 
3906 3906
             if (fileoffset == 0x154)
3907
-                cli_dbgmsg("MEW: Win9x compatibility was set!\n");
3907
+                cli_dbgmsg("cli_scanpe: MEW: Win9x compatibility was set!\n");
3908 3908
             else
3909
-                cli_dbgmsg("MEW: Win9x compatibility was NOT set!\n");
3909
+                cli_dbgmsg("cli_scanpe: MEW: Win9x compatibility was NOT set!\n");
3910 3910
 
3911
-            offdiff = cli_readint32(tbuff + 1) - EC32(optional_hdr32.ImageBase);
3912
-            if ((offdiff <= exe_sections[i + 1].rva) ||
3913
-                (offdiff >= exe_sections[i + 1].rva + exe_sections[i + 1].raw - 4)) {
3914
-                cli_dbgmsg("MEW: ESI is not in proper section\n");
3911
+            offdiff = cli_readint32(tbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase);
3912
+            if ((offdiff <= peinfo->sections[i + 1].rva) ||
3913
+                (offdiff >= peinfo->sections[i + 1].rva + peinfo->sections[i + 1].raw - 4)) {
3914
+                cli_dbgmsg("cli_scanpe: MEW: ESI is not in proper section\n");
3915 3915
                 break;
3916 3916
             }
3917 3917
 
3918
-            offdiff -= exe_sections[i + 1].rva;
3918
+            offdiff -= peinfo->sections[i + 1].rva;
3919 3919
 
3920
-            if (!exe_sections[i + 1].rsz) {
3921
-                cli_dbgmsg("MEW: mew section is empty\n");
3920
+            if (!peinfo->sections[i + 1].rsz) {
3921
+                cli_dbgmsg("cli_scanpe: MEW: mew section is empty\n");
3922 3922
                 break;
3923 3923
             }
3924 3924
 
3925
-            ssize = exe_sections[i + 1].vsz;
3926
-            dsize = exe_sections[i].vsz;
3925
+            ssize = peinfo->sections[i + 1].vsz;
3926
+            dsize = peinfo->sections[i].vsz;
3927 3927
 
3928 3928
             /* Guard against integer overflow */
3929 3929
             if ((ssize + dsize < ssize) || (ssize + dsize < dsize)) {
3930
-                cli_dbgmsg("MEW: section size (%08x) + diff size (%08x) exceeds max size of unsigned int (%08x)\n", ssize, dsize, UINT32_MAX);
3930
+                cli_dbgmsg("cli_scanpe: MEW: section size (%08x) + diff size (%08x) exceeds max size of unsigned int (%08x)\n", ssize, dsize, UINT32_MAX);
3931 3931
                 break;
3932 3932
             }
3933 3933
 
3934 3934
             /* Verify that offdiff does not exceed the ssize + sdiff */
3935 3935
             if (offdiff >= ssize + dsize) {
3936
-                cli_dbgmsg("MEW: offdiff (%08x) exceeds section size + diff size (%08x)\n", offdiff, ssize + dsize);
3936
+                cli_dbgmsg("cli_scanpe: MEW: offdiff (%08x) exceeds section size + diff size (%08x)\n", offdiff, ssize + dsize);
3937 3937
                 break;
3938 3938
             }
3939 3939
 
3940
-            cli_dbgmsg("MEW: ssize %08x dsize %08x offdiff: %08x\n", ssize, dsize, offdiff);
3940
+            cli_dbgmsg("cli_scanpe: MEW: ssize %08x dsize %08x offdiff: %08x\n", ssize, dsize, offdiff);
3941 3941
 
3942
-            CLI_UNPSIZELIMITS("MEW", MAX(ssize, dsize));
3943
-            CLI_UNPSIZELIMITS("MEW", MAX(ssize + dsize, exe_sections[i + 1].rsz));
3942
+            CLI_UNPSIZELIMITS("cli_scanpe: MEW", MAX(ssize, dsize));
3943
+            CLI_UNPSIZELIMITS("cli_scanpe: MEW", MAX(ssize + dsize, peinfo->sections[i + 1].rsz));
3944 3944
 
3945
-            if (exe_sections[i + 1].rsz < offdiff + 12 || exe_sections[i + 1].rsz > ssize) {
3946
-                cli_dbgmsg("MEW: Size mismatch: %08x\n", exe_sections[i + 1].rsz);
3945
+            if (peinfo->sections[i + 1].rsz < offdiff + 12 || peinfo->sections[i + 1].rsz > ssize) {
3946
+                cli_dbgmsg("cli_scanpe: MEW: Size mismatch: %08x\n", peinfo->sections[i + 1].rsz);
3947 3947
                 break;
3948 3948
             }
3949 3949
 
3950 3950
             /* allocate needed buffer */
3951 3951
             if (!(src = cli_calloc(ssize + dsize, sizeof(char)))) {
3952
-                free(exe_sections);
3952
+                cli_exe_info_destroy(peinfo);
3953 3953
                 return CL_EMEM;
3954 3954
             }
3955 3955
 
3956
-            if ((bytes = fmap_readn(map, src + dsize, exe_sections[i + 1].raw, exe_sections[i + 1].rsz)) != exe_sections[i + 1].rsz) {
3957
-                cli_dbgmsg("MEW: Can't read %d bytes [read: %lu]\n", exe_sections[i + 1].rsz, (unsigned long)bytes);
3958
-                free(exe_sections);
3956
+            if ((bytes = fmap_readn(map, src + dsize, peinfo->sections[i + 1].raw, peinfo->sections[i + 1].rsz)) != peinfo->sections[i + 1].rsz) {
3957
+                cli_dbgmsg("cli_scanpe: MEW: Can't read %d bytes [read: %lu]\n", peinfo->sections[i + 1].rsz, (unsigned long)bytes);
3958
+                cli_exe_info_destroy(peinfo);
3959 3959
                 free(src);
3960 3960
                 return CL_EREAD;
3961 3961
             }
3962 3962
 
3963
-            cli_dbgmsg("MEW: %u (%08x) bytes read\n", (unsigned int)bytes, (unsigned int)bytes);
3963
+            cli_dbgmsg("cli_scanpe: MEW: %u (%08x) bytes read\n", (unsigned int)bytes, (unsigned int)bytes);
3964 3964
 
3965 3965
             /* count offset to lzma proc, if lzma used, 0xe8 -> call */
3966 3966
             if (tbuff[0x7b] == '\xe8') {
3967
-                if (!CLI_ISCONTAINED(exe_sections[1].rva, exe_sections[1].vsz, cli_readint32(tbuff + 0x7c) + fileoffset + 0x80, 4)) {
3968
-                    cli_dbgmsg("MEW: lzma proc out of bounds!\n");
3967
+                if (!CLI_ISCONTAINED(peinfo->sections[1].rva, peinfo->sections[1].vsz, cli_readint32(tbuff + 0x7c) + fileoffset + 0x80, 4)) {
3968
+                    cli_dbgmsg("cli_scanpe: MEW: lzma proc out of bounds!\n");
3969 3969
                     free(src);
3970 3970
                     break; /* to next unpacker in chain */
3971 3971
                 }
3972 3972
 
3973
-                uselzma = cli_readint32(tbuff + 0x7c) - (exe_sections[0].rva - fileoffset - 0x80);
3973
+                uselzma = cli_readint32(tbuff + 0x7c) - (peinfo->sections[0].rva - fileoffset - 0x80);
3974 3974
             } else {
3975 3975
                 uselzma = 0;
3976 3976
             }
... ...
@@ -3980,14 +3393,15 @@ int cli_scanpe(cli_ctx *ctx)
3980 3980
                 cli_jsonstr(pe_json, "Packer", "MEW");
3981 3981
 #endif
3982 3982
 
3983
-            CLI_UNPTEMP("MEW", (src, exe_sections, 0));
3984
-            CLI_UNPRESULTS("MEW", (unmew11(src, offdiff, ssize, dsize, EC32(optional_hdr32.ImageBase), exe_sections[0].rva, uselzma, ndesc)), 1, (src, 0));
3983
+            CLI_UNPTEMP("cli_scanpe: MEW", (src, 0));
3984
+            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));
3985 3985
             break;
3986 3986
         }
3987 3987
     }
3988 3988
 
3989
+    // TODO Why do we bail here
3989 3990
     if (epsize < 168) {
3990
-        free(exe_sections);
3991
+        cli_exe_info_destroy(peinfo);
3991 3992
         return CL_CLEAN;
3992 3993
     }
3993 3994
 
... ...
@@ -4014,70 +3428,70 @@ int cli_scanpe(cli_ctx *ctx)
4014 4014
          * 
4015 4015
          */
4016 4016
         /* upack 0.39-3s + sample 0151477*/
4017
-        while (((upack && nsections == 3) && /* 3 sections */
4017
+        while (((upack && peinfo->nsections == 3) && /* 3 sections */
4018 4018
                 ((
4019
-                     epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase) > min && /* mov esi */
4020
-                     epbuff[5] == '\xad' && epbuff[6] == '\x50'                                                 /* lodsd; push eax */
4019
+                     epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) > peinfo->min && /* mov esi */
4020
+                     epbuff[5] == '\xad' && epbuff[6] == '\x50'                                                               /* lodsd; push eax */
4021 4021
                      ) ||
4022 4022
                  /* based on 0297729 sample from aCaB */
4023
-                 (epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase) > min && /* mov esi */
4024
-                  epbuff[5] == '\xff' && epbuff[6] == '\x36'                                                 /* push [esi] */
4023
+                 (epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) > peinfo->min && /* mov esi */
4024
+                  epbuff[5] == '\xff' && epbuff[6] == '\x36'                                                               /* push [esi] */
4025 4025
                   ))) ||
4026
-               ((!upack && nsections == 2) &&                                                    /* 2 sections */
4026
+               ((!upack && peinfo->nsections == 2) &&                                            /* 2 sections */
4027 4027
                 ((                                                                               /* upack 0.39-2s */
4028 4028
                   epbuff[0] == '\x60' && epbuff[1] == '\xe8' && cli_readint32(epbuff + 2) == 0x9 /* pusha; call+9 */
4029 4029
                   ) ||
4030
-                 (                                                                                           /* upack 1.1/1.2, based on 2 samples */
4031
-                  epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase) < min && /* mov esi */
4032
-                  cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase) > 0 &&
4030
+                 (                                                                                                         /* upack 1.1/1.2, based on 2 samples */
4031
+                  epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) < peinfo->min && /* mov esi */
4032
+                  cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) > 0 &&
4033 4033
                   epbuff[5] == '\xad' && epbuff[6] == '\x8b' && epbuff[7] == '\xf8' /* loads;  mov edi, eax */
4034 4034
                   )))) {
4035 4035
             uint32_t vma, off;
4036 4036
             int a, b, c;
4037 4037
 
4038
-            cli_dbgmsg("Upack characteristics found.\n");
4039
-            a = exe_sections[0].vsz;
4040
-            b = exe_sections[1].vsz;
4038
+            cli_dbgmsg("cli_scanpe: Upack characteristics found.\n");
4039
+            a = peinfo->sections[0].vsz;
4040
+            b = peinfo->sections[1].vsz;
4041 4041
             if (upack) {
4042
-                cli_dbgmsg("Upack: var set\n");
4042
+                cli_dbgmsg("cli_scanpe: Upack: var set\n");
4043 4043
 
4044
-                c     = exe_sections[2].vsz;
4045
-                ssize = exe_sections[0].ursz + exe_sections[0].uraw;
4046
-                off   = exe_sections[0].rva;
4047
-                vma   = EC32(optional_hdr32.ImageBase) + exe_sections[0].rva;
4044
+                c     = peinfo->sections[2].vsz;
4045
+                ssize = peinfo->sections[0].ursz + peinfo->sections[0].uraw;
4046
+                off   = peinfo->sections[0].rva;
4047
+                vma   = EC32(peinfo->pe_opt.opt32.ImageBase) + peinfo->sections[0].rva;
4048 4048
             } else {
4049
-                cli_dbgmsg("Upack: var NOT set\n");
4050
-                c     = exe_sections[1].rva;
4051
-                ssize = exe_sections[1].uraw;
4049
+                cli_dbgmsg("cli_scanpe: Upack: var NOT set\n");
4050
+                c     = peinfo->sections[1].rva;
4051
+                ssize = peinfo->sections[1].uraw;
4052 4052
                 off   = 0;
4053
-                vma   = exe_sections[1].rva - exe_sections[1].uraw;
4053
+                vma   = peinfo->sections[1].rva - peinfo->sections[1].uraw;
4054 4054
             }
4055 4055
 
4056 4056
             dsize = a + b + c;
4057 4057
 
4058
-            CLI_UNPSIZELIMITS("Upack", MAX(MAX(dsize, ssize), exe_sections[1].ursz));
4058
+            CLI_UNPSIZELIMITS("cli_scanpe: Upack", MAX(MAX(dsize, ssize), peinfo->sections[1].ursz));
4059 4059
 
4060
-            if (!CLI_ISCONTAINED(0, dsize, exe_sections[1].rva - off, exe_sections[1].ursz) || (upack && !CLI_ISCONTAINED(0, dsize, exe_sections[2].rva - exe_sections[0].rva, ssize)) || ssize > dsize) {
4061
-                cli_dbgmsg("Upack: probably malformed pe-header, skipping to next unpacker\n");
4060
+            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) {
4061
+                cli_dbgmsg("cli_scanpe: Upack: probably malformed pe-header, skipping to next unpacker\n");
4062 4062
                 break;
4063 4063
             }
4064 4064
 
4065 4065
             if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
4066
-                free(exe_sections);
4066
+                cli_exe_info_destroy(peinfo);
4067 4067
                 return CL_EMEM;
4068 4068
             }
4069 4069
 
4070 4070
             if ((unsigned int)fmap_readn(map, dest, 0, ssize) != ssize) {
4071
-                cli_dbgmsg("Upack: Can't read raw data of section 0\n");
4071
+                cli_dbgmsg("cli_scanpe: Upack: Can't read raw data of section 0\n");
4072 4072
                 free(dest);
4073 4073
                 break;
4074 4074
             }
4075 4075
 
4076 4076
             if (upack)
4077
-                memmove(dest + exe_sections[2].rva - exe_sections[0].rva, dest, ssize);
4077
+                memmove(dest + peinfo->sections[2].rva - peinfo->sections[0].rva, dest, ssize);
4078 4078
 
4079
-            if ((unsigned int)fmap_readn(map, dest + exe_sections[1].rva - off, exe_sections[1].uraw, exe_sections[1].ursz) != exe_sections[1].ursz) {
4080
-                cli_dbgmsg("Upack: Can't read raw data of section 1\n");
4079
+            if ((unsigned int)fmap_readn(map, dest + peinfo->sections[1].rva - off, peinfo->sections[1].uraw, peinfo->sections[1].ursz) != peinfo->sections[1].ursz) {
4080
+                cli_dbgmsg("cli_scanpe: Upack: Can't read raw data of section 1\n");
4081 4081
                 free(dest);
4082 4082
                 break;
4083 4083
             }
... ...
@@ -4087,8 +3501,8 @@ int cli_scanpe(cli_ctx *ctx)
4087 4087
                 cli_jsonstr(pe_json, "Packer", "Upack");
4088 4088
 #endif
4089 4089
 
4090
-            CLI_UNPTEMP("Upack", (dest, exe_sections, 0));
4091
-            CLI_UNPRESULTS("Upack", (unupack(upack, dest, dsize, epbuff, vma, ep, EC32(optional_hdr32.ImageBase), exe_sections[0].rva, ndesc)), 1, (dest, 0));
4090
+            CLI_UNPTEMP("cli_scanpe: Upack", (dest, 0));
4091
+            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));
4092 4092
 
4093 4093
             break;
4094 4094
         }
... ...
@@ -4100,72 +3514,72 @@ int cli_scanpe(cli_ctx *ctx)
4100 4100
 
4101 4101
         /* FSG v2.0 support - thanks to aCaB ! */
4102 4102
 
4103
-        ssize = exe_sections[i + 1].rsz;
4104
-        dsize = exe_sections[i].vsz;
4103
+        ssize = peinfo->sections[i + 1].rsz;
4104
+        dsize = peinfo->sections[i].vsz;
4105 4105
 
4106
-        CLI_UNPSIZELIMITS("FSG", MAX(dsize, ssize));
4106
+        CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
4107 4107
 
4108 4108
         if (ssize <= 0x19 || dsize <= ssize) {
4109
-            cli_dbgmsg("FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
4110
-            free(exe_sections);
4109
+            cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
4110
+            cli_exe_info_destroy(peinfo);
4111 4111
             return CL_CLEAN;
4112 4112
         }
4113 4113
 
4114
-        newedx = cli_readint32(epbuff + 2) - EC32(optional_hdr32.ImageBase);
4115
-        if (!CLI_ISCONTAINED(exe_sections[i + 1].rva, exe_sections[i + 1].rsz, newedx, 4)) {
4116
-            cli_dbgmsg("FSG: xchg out of bounds (%x), giving up\n", newedx);
4114
+        newedx = cli_readint32(epbuff + 2) - EC32(peinfo->pe_opt.opt32.ImageBase);
4115
+        if (!CLI_ISCONTAINED(peinfo->sections[i + 1].rva, peinfo->sections[i + 1].rsz, newedx, 4)) {
4116
+            cli_dbgmsg("cli_scanpe: FSG: xchg out of bounds (%x), giving up\n", newedx);
4117 4117
             break;
4118 4118
         }
4119 4119
 
4120
-        if (!exe_sections[i + 1].rsz || !(src = fmap_need_off_once(map, exe_sections[i + 1].raw, ssize))) {
4121
-            cli_dbgmsg("Can't read raw data of section %d\n", i + 1);
4122
-            free(exe_sections);
4120
+        if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
4121
+            cli_dbgmsg("cli_scanpe: Can't read raw data of section %d\n", i + 1);
4122
+            cli_exe_info_destroy(peinfo);
4123 4123
             return CL_ESEEK;
4124 4124
         }
4125 4125
 
4126
-        dst = src + newedx - exe_sections[i + 1].rva;
4127
-        if (newedx < exe_sections[i + 1].rva || !CLI_ISCONTAINED(src, ssize, dst, 4)) {
4128
-            cli_dbgmsg("FSG: New ESP out of bounds\n");
4126
+        dst = src + newedx - peinfo->sections[i + 1].rva;
4127
+        if (newedx < peinfo->sections[i + 1].rva || !CLI_ISCONTAINED(src, ssize, dst, 4)) {
4128
+            cli_dbgmsg("cli_scanpe: FSG: New ESP out of bounds\n");
4129 4129
             break;
4130 4130
         }
4131 4131
 
4132
-        newedx = cli_readint32(dst) - EC32(optional_hdr32.ImageBase);
4133
-        if (!CLI_ISCONTAINED(exe_sections[i + 1].rva, exe_sections[i + 1].rsz, newedx, 4)) {
4134
-            cli_dbgmsg("FSG: New ESP (%x) is wrong\n", newedx);
4132
+        newedx = cli_readint32(dst) - EC32(peinfo->pe_opt.opt32.ImageBase);
4133
+        if (!CLI_ISCONTAINED(peinfo->sections[i + 1].rva, peinfo->sections[i + 1].rsz, newedx, 4)) {
4134
+            cli_dbgmsg("cli_scanpe: FSG: New ESP (%x) is wrong\n", newedx);
4135 4135
             break;
4136 4136
         }
4137 4137
 
4138
-        dst = src + newedx - exe_sections[i + 1].rva;
4138
+        dst = src + newedx - peinfo->sections[i + 1].rva;
4139 4139
         if (!CLI_ISCONTAINED(src, ssize, dst, 32)) {
4140
-            cli_dbgmsg("FSG: New stack out of bounds\n");
4140
+            cli_dbgmsg("cli_scanpe: FSG: New stack out of bounds\n");
4141 4141
             break;
4142 4142
         }
4143 4143
 
4144
-        newedi = cli_readint32(dst) - EC32(optional_hdr32.ImageBase);
4145
-        newesi = cli_readint32(dst + 4) - EC32(optional_hdr32.ImageBase);
4146
-        newebx = cli_readint32(dst + 16) - EC32(optional_hdr32.ImageBase);
4144
+        newedi = cli_readint32(dst) - EC32(peinfo->pe_opt.opt32.ImageBase);
4145
+        newesi = cli_readint32(dst + 4) - EC32(peinfo->pe_opt.opt32.ImageBase);
4146
+        newebx = cli_readint32(dst + 16) - EC32(peinfo->pe_opt.opt32.ImageBase);
4147 4147
         newedx = cli_readint32(dst + 20);
4148 4148
 
4149
-        if (newedi != exe_sections[i].rva) {
4150
-            cli_dbgmsg("FSG: Bad destination buffer (edi is %x should be %x)\n", newedi, exe_sections[i].rva);
4149
+        if (newedi != peinfo->sections[i].rva) {
4150
+            cli_dbgmsg("cli_scanpe: FSG: Bad destination buffer (edi is %x should be %x)\n", newedi, peinfo->sections[i].rva);
4151 4151
             break;
4152 4152
         }
4153 4153
 
4154
-        if (newesi < exe_sections[i + 1].rva || newesi - exe_sections[i + 1].rva >= exe_sections[i + 1].rsz) {
4155
-            cli_dbgmsg("FSG: Source buffer out of section bounds\n");
4154
+        if (newesi < peinfo->sections[i + 1].rva || newesi - peinfo->sections[i + 1].rva >= peinfo->sections[i + 1].rsz) {
4155
+            cli_dbgmsg("cli_scanpe: FSG: Source buffer out of section bounds\n");
4156 4156
             break;
4157 4157
         }
4158 4158
 
4159
-        if (!CLI_ISCONTAINED(exe_sections[i + 1].rva, exe_sections[i + 1].rsz, newebx, 16)) {
4160
-            cli_dbgmsg("FSG: Array of functions out of bounds\n");
4159
+        if (!CLI_ISCONTAINED(peinfo->sections[i + 1].rva, peinfo->sections[i + 1].rsz, newebx, 16)) {
4160
+            cli_dbgmsg("cli_scanpe: FSG: Array of functions out of bounds\n");
4161 4161
             break;
4162 4162
         }
4163 4163
 
4164
-        newedx = cli_readint32(newebx + 12 - exe_sections[i + 1].rva + src) - EC32(optional_hdr32.ImageBase);
4165
-        cli_dbgmsg("FSG: found old EP @%x\n", newedx);
4164
+        newedx = cli_readint32(newebx + 12 - peinfo->sections[i + 1].rva + src) - EC32(peinfo->pe_opt.opt32.ImageBase);
4165
+        cli_dbgmsg("cli_scanpe: FSG: found old EP @%x\n", newedx);
4166 4166
 
4167 4167
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
4168
-            free(exe_sections);
4168
+            cli_exe_info_destroy(peinfo);
4169 4169
             return CL_EMEM;
4170 4170
         }
4171 4171
 
... ...
@@ -4174,12 +3588,12 @@ int cli_scanpe(cli_ctx *ctx)
4174 4174
             cli_jsonstr(pe_json, "Packer", "FSG");
4175 4175
 #endif
4176 4176
 
4177
-        CLI_UNPTEMP("FSG", (dest, exe_sections, 0));
4178
-        CLI_UNPRESULTSFSG2("FSG", (unfsg_200(newesi - exe_sections[i + 1].rva + src, dest, ssize + exe_sections[i + 1].rva - newesi, dsize, newedi, EC32(optional_hdr32.ImageBase), newedx, ndesc)), 1, (dest, 0));
4177
+        CLI_UNPTEMP("cli_scanpe: FSG", (dest, 0));
4178
+        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));
4179 4179
         break;
4180 4180
     }
4181 4181
 
4182
-    while (found && (DCONF & PE_CONF_FSG) && epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase) < min) {
4182
+    while (found && (DCONF & PE_CONF_FSG) && epbuff[0] == '\xbe' && cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase) < peinfo->min) {
4183 4183
         int sectcnt = 0;
4184 4184
         const char *support;
4185 4185
         uint32_t newesi, newedi, oldep, gp, t;
... ...
@@ -4187,43 +3601,43 @@ int cli_scanpe(cli_ctx *ctx)
4187 4187
 
4188 4188
         /* FSG support - v. 1.33 (thx trog for the many samples) */
4189 4189
 
4190
-        ssize = exe_sections[i + 1].rsz;
4191
-        dsize = exe_sections[i].vsz;
4190
+        ssize = peinfo->sections[i + 1].rsz;
4191
+        dsize = peinfo->sections[i].vsz;
4192 4192
 
4193
-        CLI_UNPSIZELIMITS("FSG", MAX(dsize, ssize));
4193
+        CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
4194 4194
 
4195 4195
         if (ssize <= 0x19 || dsize <= ssize) {
4196
-            cli_dbgmsg("FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
4197
-            free(exe_sections);
4196
+            cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
4197
+            cli_exe_info_destroy(peinfo);
4198 4198
             return CL_CLEAN;
4199 4199
         }
4200 4200
 
4201
-        if (!(t = cli_rawaddr(cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase), NULL, 0, &err, fsize, hdr_size)) && err) {
4202
-            cli_dbgmsg("FSG: Support data out of padding area\n");
4201
+        if (!(t = cli_rawaddr(cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase), NULL, 0, &err, fsize, peinfo->hdr_size)) && err) {
4202
+            cli_dbgmsg("cli_scanpe: FSG: Support data out of padding area\n");
4203 4203
             break;
4204 4204
         }
4205 4205
 
4206
-        gp = exe_sections[i + 1].raw - t;
4206
+        gp = peinfo->sections[i + 1].raw - t;
4207 4207
 
4208
-        CLI_UNPSIZELIMITS("FSG", gp);
4208
+        CLI_UNPSIZELIMITS("cli_scanpe: FSG", gp);
4209 4209
 
4210 4210
         if (!(support = fmap_need_off_once(map, t, gp))) {
4211
-            cli_dbgmsg("Can't read %d bytes from padding area\n", gp);
4212
-            free(exe_sections);
4211
+            cli_dbgmsg("cli_scanpe: Can't read %d bytes from padding area\n", gp);
4212
+            cli_exe_info_destroy(peinfo);
4213 4213
             return CL_EREAD;
4214 4214
         }
4215 4215
 
4216
-        /* newebx = cli_readint32(support) - EC32(optional_hdr32.ImageBase);  Unused */
4217
-        newedi = cli_readint32(support + 4) - EC32(optional_hdr32.ImageBase); /* 1st dest */
4218
-        newesi = cli_readint32(support + 8) - EC32(optional_hdr32.ImageBase); /* Source */
4216
+        /* newebx = cli_readint32(support) - EC32(peinfo->pe_opt.opt32.ImageBase);  Unused */
4217
+        newedi = cli_readint32(support + 4) - EC32(peinfo->pe_opt.opt32.ImageBase); /* 1st dest */
4218
+        newesi = cli_readint32(support + 8) - EC32(peinfo->pe_opt.opt32.ImageBase); /* Source */
4219 4219
 
4220
-        if (newesi < exe_sections[i + 1].rva || newesi - exe_sections[i + 1].rva >= exe_sections[i + 1].rsz) {
4221
-            cli_dbgmsg("FSG: Source buffer out of section bounds\n");
4220
+        if (newesi < peinfo->sections[i + 1].rva || newesi - peinfo->sections[i + 1].rva >= peinfo->sections[i + 1].rsz) {
4221
+            cli_dbgmsg("cli_scanpe: FSG: Source buffer out of section bounds\n");
4222 4222
             break;
4223 4223
         }
4224 4224
 
4225
-        if (newedi != exe_sections[i].rva) {
4226
-            cli_dbgmsg("FSG: Bad destination (is %x should be %x)\n", newedi, exe_sections[i].rva);
4225
+        if (newedi != peinfo->sections[i].rva) {
4226
+            cli_dbgmsg("cli_scanpe: FSG: Bad destination (is %x should be %x)\n", newedi, peinfo->sections[i].rva);
4227 4227
             break;
4228 4228
         }
4229 4229
 
... ...
@@ -4234,14 +3648,14 @@ int cli_scanpe(cli_ctx *ctx)
4234 4234
             if (!rva)
4235 4235
                 break;
4236 4236
 
4237
-            rva -= EC32(optional_hdr32.ImageBase) + 1;
4237
+            rva -= EC32(peinfo->pe_opt.opt32.ImageBase) + 1;
4238 4238
             sectcnt++;
4239 4239
 
4240 4240
             if (rva % 0x1000)
4241
-                cli_dbgmsg("FSG: Original section %d is misaligned\n", sectcnt);
4241
+                cli_dbgmsg("cli_scanpe: FSG: Original section %d is misaligned\n", sectcnt);
4242 4242
 
4243
-            if (rva < exe_sections[i].rva || rva - exe_sections[i].rva >= exe_sections[i].vsz) {
4244
-                cli_dbgmsg("FSG: Original section %d is out of bounds\n", sectcnt);
4243
+            if (rva < peinfo->sections[i].rva || rva - peinfo->sections[i].rva >= peinfo->sections[i].vsz) {
4244
+                cli_dbgmsg("cli_scanpe: FSG: Original section %d is out of bounds\n", sectcnt);
4245 4245
                 break;
4246 4246
             }
4247 4247
         }
... ...
@@ -4251,85 +3665,85 @@ int cli_scanpe(cli_ctx *ctx)
4251 4251
         }
4252 4252
 
4253 4253
         if ((sections = (struct cli_exe_section *)cli_malloc((sectcnt + 1) * sizeof(struct cli_exe_section))) == NULL) {
4254
-            cli_errmsg("FSG: Unable to allocate memory for sections %llu\n", (long long unsigned)((sectcnt + 1) * sizeof(struct cli_exe_section)));
4255
-            free(exe_sections);
4254
+            cli_errmsg("cli_scanpe: FSG: Unable to allocate memory for sections %llu\n", (long long unsigned)((sectcnt + 1) * sizeof(struct cli_exe_section)));
4255
+            cli_exe_info_destroy(peinfo);
4256 4256
             return CL_EMEM;
4257 4257
         }
4258 4258
 
4259 4259
         sections[0].rva = newedi;
4260 4260
         for (t = 1; t <= (uint32_t)sectcnt; t++)
4261
-            sections[t].rva = cli_readint32(support + 8 + t * 4) - 1 - EC32(optional_hdr32.ImageBase);
4261
+            sections[t].rva = cli_readint32(support + 8 + t * 4) - 1 - EC32(peinfo->pe_opt.opt32.ImageBase);
4262 4262
 
4263
-        if (!exe_sections[i + 1].rsz || !(src = fmap_need_off_once(map, exe_sections[i + 1].raw, ssize))) {
4264
-            cli_dbgmsg("Can't read raw data of section %d\n", i);
4265
-            free(exe_sections);
4263
+        if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
4264
+            cli_dbgmsg("cli_scanpe: Can't read raw data of section %d\n", i);
4265
+            cli_exe_info_destroy(peinfo);
4266 4266
             free(sections);
4267 4267
             return CL_EREAD;
4268 4268
         }
4269 4269
 
4270 4270
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
4271
-            free(exe_sections);
4271
+            cli_exe_info_destroy(peinfo);
4272 4272
             free(sections);
4273 4273
             return CL_EMEM;
4274 4274
         }
4275 4275
 
4276
-        oldep = vep + 161 + 6 + cli_readint32(epbuff + 163);
4277
-        cli_dbgmsg("FSG: found old EP @%x\n", oldep);
4276
+        oldep = peinfo->vep + 161 + 6 + cli_readint32(epbuff + 163);
4277
+        cli_dbgmsg("cli_scanpe: FSG: found old EP @%x\n", oldep);
4278 4278
 
4279 4279
 #if HAVE_JSON
4280 4280
         if (pe_json != NULL)
4281 4281
             cli_jsonstr(pe_json, "Packer", "FSG");
4282 4282
 #endif
4283 4283
 
4284
-        CLI_UNPTEMP("FSG", (dest, sections, exe_sections, 0));
4285
-        CLI_UNPRESULTSFSG1("FSG", (unfsg_133(src + newesi - exe_sections[i + 1].rva, dest, ssize + exe_sections[i + 1].rva - newesi, dsize, sections, sectcnt, EC32(optional_hdr32.ImageBase), oldep, ndesc)), 1, (dest, sections, 0));
4284
+        CLI_UNPTEMP("cli_scanpe: FSG", (dest, sections, 0));
4285
+        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));
4286 4286
         break; /* were done with 1.33 */
4287 4287
     }
4288 4288
 
4289
-    while (found && (DCONF & PE_CONF_FSG) && epbuff[0] == '\xbb' && cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase) < min && epbuff[5] == '\xbf' && epbuff[10] == '\xbe' && vep >= exe_sections[i + 1].rva && vep - exe_sections[i + 1].rva > exe_sections[i + 1].rva - 0xe0) {
4289
+    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) {
4290 4290
         int sectcnt = 0;
4291
-        uint32_t gp, t = cli_rawaddr(cli_readint32(epbuff + 1) - EC32(optional_hdr32.ImageBase), NULL, 0, &err, fsize, hdr_size);
4291
+        uint32_t gp, t = cli_rawaddr(cli_readint32(epbuff + 1) - EC32(peinfo->pe_opt.opt32.ImageBase), NULL, 0, &err, fsize, peinfo->hdr_size);
4292 4292
         const char *support;
4293
-        uint32_t newesi = cli_readint32(epbuff + 11) - EC32(optional_hdr32.ImageBase);
4294
-        uint32_t newedi = cli_readint32(epbuff + 6) - EC32(optional_hdr32.ImageBase);
4295
-        uint32_t oldep  = vep - exe_sections[i + 1].rva;
4293
+        uint32_t newesi = cli_readint32(epbuff + 11) - EC32(peinfo->pe_opt.opt32.ImageBase);
4294
+        uint32_t newedi = cli_readint32(epbuff + 6) - EC32(peinfo->pe_opt.opt32.ImageBase);
4295
+        uint32_t oldep  = peinfo->vep - peinfo->sections[i + 1].rva;
4296 4296
         struct cli_exe_section *sections;
4297 4297
 
4298 4298
         /* FSG support - v. 1.31 */
4299 4299
 
4300
-        ssize = exe_sections[i + 1].rsz;
4301
-        dsize = exe_sections[i].vsz;
4300
+        ssize = peinfo->sections[i + 1].rsz;
4301
+        dsize = peinfo->sections[i].vsz;
4302 4302
 
4303 4303
         if (err) {
4304
-            cli_dbgmsg("FSG: Support data out of padding area\n");
4304
+            cli_dbgmsg("cli_scanpe: FSG: Support data out of padding area\n");
4305 4305
             break;
4306 4306
         }
4307 4307
 
4308
-        if (newesi < exe_sections[i + 1].rva || newesi - exe_sections[i + 1].rva >= exe_sections[i + 1].raw) {
4309
-            cli_dbgmsg("FSG: Source buffer out of section bounds\n");
4308
+        if (newesi < peinfo->sections[i + 1].rva || newesi - peinfo->sections[i + 1].rva >= peinfo->sections[i + 1].raw) {
4309
+            cli_dbgmsg("cli_scanpe: FSG: Source buffer out of section bounds\n");
4310 4310
             break;
4311 4311
         }
4312 4312
 
4313
-        if (newedi != exe_sections[i].rva) {
4314
-            cli_dbgmsg("FSG: Bad destination (is %x should be %x)\n", newedi, exe_sections[i].rva);
4313
+        if (newedi != peinfo->sections[i].rva) {
4314
+            cli_dbgmsg("cli_scanpe: FSG: Bad destination (is %x should be %x)\n", newedi, peinfo->sections[i].rva);
4315 4315
             break;
4316 4316
         }
4317 4317
 
4318
-        CLI_UNPSIZELIMITS("FSG", MAX(dsize, ssize));
4318
+        CLI_UNPSIZELIMITS("cli_scanpe: FSG", MAX(dsize, ssize));
4319 4319
 
4320 4320
         if (ssize <= 0x19 || dsize <= ssize) {
4321
-            cli_dbgmsg("FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
4322
-            free(exe_sections);
4321
+            cli_dbgmsg("cli_scanpe: FSG: Size mismatch (ssize: %d, dsize: %d)\n", ssize, dsize);
4322
+            cli_exe_info_destroy(peinfo);
4323 4323
             return CL_CLEAN;
4324 4324
         }
4325 4325
 
4326
-        gp = exe_sections[i + 1].raw - t;
4326
+        gp = peinfo->sections[i + 1].raw - t;
4327 4327
 
4328
-        CLI_UNPSIZELIMITS("FSG", gp)
4328
+        CLI_UNPSIZELIMITS("cli_scanpe: FSG", gp)
4329 4329
 
4330 4330
         if (!(support = fmap_need_off_once(map, t, gp))) {
4331
-            cli_dbgmsg("Can't read %d bytes from padding area\n", gp);
4332
-            free(exe_sections);
4331
+            cli_dbgmsg("cli_scanpe: Can't read %d bytes from padding area\n", gp);
4332
+            cli_exe_info_destroy(peinfo);
4333 4333
             return CL_EREAD;
4334 4334
         }
4335 4335
 
... ...
@@ -4340,11 +3754,11 @@ int cli_scanpe(cli_ctx *ctx)
4340 4340
             if (rva == 2 || rva == 1)
4341 4341
                 break;
4342 4342
 
4343
-            rva = ((rva - 2) << 12) - EC32(optional_hdr32.ImageBase);
4343
+            rva = ((rva - 2) << 12) - EC32(peinfo->pe_opt.opt32.ImageBase);
4344 4344
             sectcnt++;
4345 4345
 
4346
-            if (rva < exe_sections[i].rva || rva - exe_sections[i].rva >= exe_sections[i].vsz) {
4347
-                cli_dbgmsg("FSG: Original section %d is out of bounds\n", sectcnt);
4346
+            if (rva < peinfo->sections[i].rva || rva - peinfo->sections[i].rva >= peinfo->sections[i].vsz) {
4347
+                cli_dbgmsg("cli_scanpe: FSG: Original section %d is out of bounds\n", sectcnt);
4348 4348
                 break;
4349 4349
             }
4350 4350
         }
... ...
@@ -4353,46 +3767,46 @@ int cli_scanpe(cli_ctx *ctx)
4353 4353
             break;
4354 4354
 
4355 4355
         if ((sections = (struct cli_exe_section *)cli_malloc((sectcnt + 1) * sizeof(struct cli_exe_section))) == NULL) {
4356
-            cli_errmsg("FSG: Unable to allocate memory for sections %llu\n", (long long unsigned)((sectcnt + 1) * sizeof(struct cli_exe_section)));
4357
-            free(exe_sections);
4356
+            cli_errmsg("cli_scanpe: FSG: Unable to allocate memory for sections %llu\n", (long long unsigned)((sectcnt + 1) * sizeof(struct cli_exe_section)));
4357
+            cli_exe_info_destroy(peinfo);
4358 4358
             return CL_EMEM;
4359 4359
         }
4360 4360
 
4361 4361
         sections[0].rva = newedi;
4362 4362
         for (t = 0; t <= (uint32_t)sectcnt - 1; t++)
4363
-            sections[t + 1].rva = (((support[t * 2] | (support[t * 2 + 1] << 8)) - 2) << 12) - EC32(optional_hdr32.ImageBase);
4363
+            sections[t + 1].rva = (((support[t * 2] | (support[t * 2 + 1] << 8)) - 2) << 12) - EC32(peinfo->pe_opt.opt32.ImageBase);
4364 4364
 
4365
-        if (!exe_sections[i + 1].rsz || !(src = fmap_need_off_once(map, exe_sections[i + 1].raw, ssize))) {
4366
-            cli_dbgmsg("FSG: Can't read raw data of section %d\n", i);
4367
-            free(exe_sections);
4365
+        if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
4366
+            cli_dbgmsg("cli_scanpe: FSG: Can't read raw data of section %d\n", i);
4367
+            cli_exe_info_destroy(peinfo);
4368 4368
             free(sections);
4369 4369
             return CL_EREAD;
4370 4370
         }
4371 4371
 
4372 4372
         if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
4373
-            free(exe_sections);
4373
+            cli_exe_info_destroy(peinfo);
4374 4374
             free(sections);
4375 4375
             return CL_EMEM;
4376 4376
         }
4377 4377
 
4378 4378
         gp    = 0xda + 6 * (epbuff[16] == '\xe8');
4379
-        oldep = vep + gp + 6 + cli_readint32(src + gp + 2 + oldep);
4380
-        cli_dbgmsg("FSG: found old EP @%x\n", oldep);
4379
+        oldep = peinfo->vep + gp + 6 + cli_readint32(src + gp + 2 + oldep);
4380
+        cli_dbgmsg("cli_scanpe: FSG: found old EP @%x\n", oldep);
4381 4381
 
4382 4382
 #if HAVE_JSON
4383 4383
         if (pe_json != NULL)
4384 4384
             cli_jsonstr(pe_json, "Packer", "FSG");
4385 4385
 #endif
4386 4386
 
4387
-        CLI_UNPTEMP("FSG", (dest, sections, exe_sections, 0));
4388
-        CLI_UNPRESULTSFSG1("FSG", (unfsg_133(src + newesi - exe_sections[i + 1].rva, dest, ssize + exe_sections[i + 1].rva - newesi, dsize, sections, sectcnt, EC32(optional_hdr32.ImageBase), oldep, ndesc)), 1, (dest, sections, 0));
4387
+        CLI_UNPTEMP("cli_scanpe: FSG", (dest, sections, 0));
4388
+        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));
4389 4389
 
4390 4390
         break; /* were done with 1.31 */
4391 4391
     }
4392 4392
 
4393 4393
     if (found && (DCONF & PE_CONF_UPX)) {
4394
-        ssize = exe_sections[i + 1].rsz;
4395
-        dsize = exe_sections[i].vsz + exe_sections[i + 1].vsz;
4394
+        ssize = peinfo->sections[i + 1].rsz;
4395
+        dsize = peinfo->sections[i].vsz + peinfo->sections[i + 1].vsz;
4396 4396
 
4397 4397
         /* 
4398 4398
          * UPX support
... ...
@@ -4401,39 +3815,39 @@ int cli_scanpe(cli_ctx *ctx)
4401 4401
 
4402 4402
         /* cli_dbgmsg("UPX: ssize %u dsize %u\n", ssize, dsize); */
4403 4403
 
4404
-        CLI_UNPSIZELIMITS("UPX", MAX(dsize, ssize));
4404
+        CLI_UNPSIZELIMITS("cli_scanpe: UPX", MAX(dsize, ssize));
4405 4405
 
4406 4406
         if (ssize <= 0x19 || dsize <= ssize || dsize > CLI_MAX_ALLOCATION) {
4407
-            cli_dbgmsg("UPX: Size mismatch or dsize too big (ssize: %d, dsize: %d)\n", ssize, dsize);
4408
-            free(exe_sections);
4407
+            cli_dbgmsg("cli_scanpe: UPX: Size mismatch or dsize too big (ssize: %d, dsize: %d)\n", ssize, dsize);
4408
+            cli_exe_info_destroy(peinfo);
4409 4409
             return CL_CLEAN;
4410 4410
         }
4411 4411
 
4412
-        if (!exe_sections[i + 1].rsz || !(src = fmap_need_off_once(map, exe_sections[i + 1].raw, ssize))) {
4413
-            cli_dbgmsg("UPX: Can't read raw data of section %d\n", i + 1);
4414
-            free(exe_sections);
4412
+        if (!peinfo->sections[i + 1].rsz || !(src = fmap_need_off_once(map, peinfo->sections[i + 1].raw, ssize))) {
4413
+            cli_dbgmsg("cli_scanpe: UPX: Can't read raw data of section %d\n", i + 1);
4414
+            cli_exe_info_destroy(peinfo);
4415 4415
             return CL_EREAD;
4416 4416
         }
4417 4417
 
4418 4418
         if ((dest = (char *)cli_calloc(dsize + 8192, sizeof(char))) == NULL) {
4419
-            free(exe_sections);
4419
+            cli_exe_info_destroy(peinfo);
4420 4420
             return CL_EMEM;
4421 4421
         }
4422 4422
 
4423 4423
         /* try to detect UPX code */
4424 4424
         if (cli_memstr(UPX_NRV2B, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2B, 24, epbuff + 0x69 + 8, 13)) {
4425
-            cli_dbgmsg("UPX: Looks like a NRV2B decompression routine\n");
4425
+            cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2B decompression routine\n");
4426 4426
             upxfn = upx_inflate2b;
4427 4427
         } else if (cli_memstr(UPX_NRV2D, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2D, 24, epbuff + 0x69 + 8, 13)) {
4428
-            cli_dbgmsg("UPX: Looks like a NRV2D decompression routine\n");
4428
+            cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2D decompression routine\n");
4429 4429
             upxfn = upx_inflate2d;
4430 4430
         } else if (cli_memstr(UPX_NRV2E, 24, epbuff + 0x69, 13) || cli_memstr(UPX_NRV2E, 24, epbuff + 0x69 + 8, 13)) {
4431
-            cli_dbgmsg("UPX: Looks like a NRV2E decompression routine\n");
4431
+            cli_dbgmsg("cli_scanpe: UPX: Looks like a NRV2E decompression routine\n");
4432 4432
             upxfn = upx_inflate2e;
4433 4433
         }
4434 4434
 
4435 4435
         if (upxfn) {
4436
-            int skew = cli_readint32(epbuff + 2) - EC32(optional_hdr32.ImageBase) - exe_sections[i + 1].rva;
4436
+            int skew = cli_readint32(epbuff + 2) - EC32(peinfo->pe_opt.opt32.ImageBase) - peinfo->sections[i + 1].rva;
4437 4437
 
4438 4438
             if (epbuff[1] != '\xbe' || skew <= 0 || skew > 0xfff) {
4439 4439
                 /* FIXME: legit skews?? */
... ...
@@ -4442,93 +3856,95 @@ int cli_scanpe(cli_ctx *ctx)
4442 4442
                 /* Ignore suggested skew larger than section size */
4443 4443
                 skew = 0;
4444 4444
             } else {
4445
-                cli_dbgmsg("UPX: UPX1 seems skewed by %d bytes\n", skew);
4445
+                cli_dbgmsg("cli_scanpe: UPX: UPX1 seems skewed by %d bytes\n", skew);
4446 4446
             }
4447 4447
 
4448 4448
             /* Try skewed first (skew may be zero) */
4449
-            if (upxfn(src + skew, ssize - skew, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep - skew) >= 0) {
4449
+            if (upxfn(src + skew, ssize - skew, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep - skew) >= 0) {
4450 4450
                 upx_success = 1;
4451 4451
             }
4452 4452
             /* If skew not successful and non-zero, try no skew */
4453
-            else if (skew && (upxfn(src, ssize, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep) >= 0)) {
4453
+            else if (skew && (upxfn(src, ssize, dest, &dsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep) >= 0)) {
4454 4454
                 upx_success = 1;
4455 4455
             }
4456 4456
 
4457 4457
             if (upx_success)
4458
-                cli_dbgmsg("UPX: Successfully decompressed\n");
4458
+                cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed\n");
4459 4459
             else
4460
-                cli_dbgmsg("UPX: Preferred decompressor failed\n");
4460
+                cli_dbgmsg("cli_scanpe: UPX: Preferred decompressor failed\n");
4461 4461
         }
4462 4462
 
4463 4463
         if (!upx_success && upxfn != upx_inflate2b) {
4464
-            if (upx_inflate2b(src, ssize, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep) == -1 && upx_inflate2b(src + 0x15, ssize - 0x15, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep - 0x15) == -1) {
4464
+            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) {
4465 4465
 
4466
-                cli_dbgmsg("UPX: NRV2B decompressor failed\n");
4466
+                cli_dbgmsg("cli_scanpe: UPX: NRV2B decompressor failed\n");
4467 4467
             } else {
4468 4468
                 upx_success = 1;
4469
-                cli_dbgmsg("UPX: Successfully decompressed with NRV2B\n");
4469
+                cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2B\n");
4470 4470
             }
4471 4471
         }
4472 4472
 
4473 4473
         if (!upx_success && upxfn != upx_inflate2d) {
4474
-            if (upx_inflate2d(src, ssize, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep) == -1 && upx_inflate2d(src + 0x15, ssize - 0x15, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep - 0x15) == -1) {
4474
+            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) {
4475 4475
 
4476
-                cli_dbgmsg("UPX: NRV2D decompressor failed\n");
4476
+                cli_dbgmsg("cli_scanpe: UPX: NRV2D decompressor failed\n");
4477 4477
             } else {
4478 4478
                 upx_success = 1;
4479
-                cli_dbgmsg("UPX: Successfully decompressed with NRV2D\n");
4479
+                cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2D\n");
4480 4480
             }
4481 4481
         }
4482 4482
 
4483 4483
         if (!upx_success && upxfn != upx_inflate2e) {
4484
-            if (upx_inflate2e(src, ssize, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep) == -1 && upx_inflate2e(src + 0x15, ssize - 0x15, dest, &dsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep - 0x15) == -1) {
4485
-                cli_dbgmsg("UPX: NRV2E decompressor failed\n");
4484
+            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) {
4485
+                cli_dbgmsg("cli_scanpe: UPX: NRV2E decompressor failed\n");
4486 4486
             } else {
4487 4487
                 upx_success = 1;
4488
-                cli_dbgmsg("UPX: Successfully decompressed with NRV2E\n");
4488
+                cli_dbgmsg("cli_scanpe: UPX: Successfully decompressed with NRV2E\n");
4489 4489
             }
4490 4490
         }
4491 4491
 
4492 4492
         if (cli_memstr(UPX_LZMA2, 20, epbuff + 0x2f, 20)) {
4493 4493
             uint32_t strictdsize = cli_readint32(epbuff + 0x21), skew = 0;
4494 4494
             if (ssize > 0x15 && epbuff[0] == '\x60' && epbuff[1] == '\xbe') {
4495
-                skew = cli_readint32(epbuff + 2) - exe_sections[i + 1].rva - optional_hdr32.ImageBase;
4495
+                // TODO Add EC32
4496
+                skew = cli_readint32(epbuff + 2) - peinfo->sections[i + 1].rva - peinfo->pe_opt.opt32.ImageBase;
4496 4497
                 if (skew != 0x15)
4497 4498
                     skew = 0;
4498 4499
             }
4499 4500
 
4500 4501
             if (strictdsize <= dsize)
4501
-                upx_success = upx_inflatelzma(src + skew, ssize - skew, dest, &strictdsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep, 0x20003) >= 0;
4502
+                upx_success = upx_inflatelzma(src + skew, ssize - skew, dest, &strictdsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep, 0x20003) >= 0;
4502 4503
         } else if (cli_memstr(UPX_LZMA1_FIRST, 8, epbuff + 0x39, 8) && cli_memstr(UPX_LZMA1_SECOND, 8, epbuff + 0x45, 8)) {
4503 4504
             uint32_t strictdsize = cli_readint32(epbuff + 0x2b), skew = 0;
4504 4505
             uint32_t properties = cli_readint32(epbuff + 0x41);
4505 4506
             if (ssize > 0x15 && epbuff[0] == '\x60' && epbuff[1] == '\xbe') {
4506
-                skew = cli_readint32(epbuff + 2) - exe_sections[i + 1].rva - optional_hdr32.ImageBase;
4507
+                // TODO Add EC32
4508
+                skew = cli_readint32(epbuff + 2) - peinfo->sections[i + 1].rva - peinfo->pe_opt.opt32.ImageBase;
4507 4509
                 if (skew != 0x15)
4508 4510
                     skew = 0;
4509 4511
             }
4510 4512
 
4511 4513
             if (strictdsize <= dsize)
4512
-                upx_success = upx_inflatelzma(src + skew, ssize - skew, dest, &strictdsize, exe_sections[i].rva, exe_sections[i + 1].rva, vep, properties) >= 0;
4514
+                upx_success = upx_inflatelzma(src + skew, ssize - skew, dest, &strictdsize, peinfo->sections[i].rva, peinfo->sections[i + 1].rva, peinfo->vep, properties) >= 0;
4513 4515
         }
4514 4516
 
4515 4517
         if (!upx_success) {
4516
-            cli_dbgmsg("UPX: All decompressors failed\n");
4518
+            cli_dbgmsg("cli_scanpe: UPX: All decompressors failed\n");
4517 4519
             free(dest);
4518 4520
         }
4519 4521
     }
4520 4522
 
4521 4523
     if (upx_success) {
4522
-        free(exe_sections);
4524
+        cli_exe_info_destroy(peinfo);
4523 4525
 
4524
-        CLI_UNPTEMP("UPX/FSG", (dest, 0));
4526
+        CLI_UNPTEMP("cli_scanpe: UPX/FSG", (dest, 0));
4525 4527
 #if HAVE_JSON
4526 4528
         if (pe_json != NULL)
4527 4529
             cli_jsonstr(pe_json, "Packer", "UPX");
4528 4530
 #endif
4529 4531
 
4530 4532
         if ((unsigned int)write(ndesc, dest, dsize) != dsize) {
4531
-            cli_dbgmsg("UPX/FSG: Can't write %d bytes\n", dsize);
4533
+            cli_dbgmsg("cli_scanpe: UPX/FSG: Can't write %d bytes\n", dsize);
4532 4534
             free(tempfile);
4533 4535
             free(dest);
4534 4536
             close(ndesc);
... ...
@@ -4537,24 +3953,24 @@ int cli_scanpe(cli_ctx *ctx)
4537 4537
 
4538 4538
         free(dest);
4539 4539
         if (lseek(ndesc, 0, SEEK_SET) == -1) {
4540
-            cli_dbgmsg("UPX/FSG: lseek() failed\n");
4540
+            cli_dbgmsg("cli_scanpe: UPX/FSG: lseek() failed\n");
4541 4541
             close(ndesc);
4542
+            SHA_RESET;
4542 4543
             CLI_TMPUNLK();
4543 4544
             free(tempfile);
4544
-            SHA_RESET;
4545 4545
             return CL_ESEEK;
4546 4546
         }
4547 4547
 
4548 4548
         if (ctx->engine->keeptmp)
4549
-            cli_dbgmsg("UPX/FSG: Decompressed data saved in %s\n", tempfile);
4549
+            cli_dbgmsg("cli_scanpe: UPX/FSG: Decompressed data saved in %s\n", tempfile);
4550 4550
 
4551 4551
         cli_dbgmsg("***** Scanning decompressed file *****\n");
4552 4552
         SHA_OFF;
4553 4553
         if ((ret = cli_magic_scandesc(ndesc, tempfile, ctx)) == CL_VIRUS) {
4554 4554
             close(ndesc);
4555
+            SHA_RESET;
4555 4556
             CLI_TMPUNLK();
4556 4557
             free(tempfile);
4557
-            SHA_RESET;
4558 4558
             return CL_VIRUS;
4559 4559
         }
4560 4560
 
... ...
@@ -4568,53 +3984,53 @@ int cli_scanpe(cli_ctx *ctx)
4568 4568
     /* Petite */
4569 4569
 
4570 4570
     if (epsize < 200) {
4571
-        free(exe_sections);
4571
+        cli_exe_info_destroy(peinfo);
4572 4572
         return CL_CLEAN;
4573 4573
     }
4574 4574
 
4575 4575
     found = 2;
4576 4576
 
4577
-    if (epbuff[0] != '\xb8' || (uint32_t)cli_readint32(epbuff + 1) != exe_sections[nsections - 1].rva + EC32(optional_hdr32.ImageBase)) {
4578
-        if (nsections < 2 || epbuff[0] != '\xb8' || (uint32_t)cli_readint32(epbuff + 1) != exe_sections[nsections - 2].rva + EC32(optional_hdr32.ImageBase))
4577
+    if (epbuff[0] != '\xb8' || (uint32_t)cli_readint32(epbuff + 1) != peinfo->sections[peinfo->nsections - 1].rva + EC32(peinfo->pe_opt.opt32.ImageBase)) {
4578
+        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))
4579 4579
             found = 0;
4580 4580
         else
4581 4581
             found = 1;
4582 4582
     }
4583 4583
 
4584 4584
     if (found && (DCONF & PE_CONF_PETITE)) {
4585
-        cli_dbgmsg("Petite: v2.%d compression detected\n", found);
4585
+        cli_dbgmsg("cli_scanpe: Petite: v2.%d compression detected\n", found);
4586 4586
 
4587 4587
         if (cli_readint32(epbuff + 0x80) == 0x163c988d) {
4588
-            cli_dbgmsg("Petite: level zero compression is not supported yet\n");
4588
+            cli_dbgmsg("cli_scanpe: Petite: level zero compression is not supported yet\n");
4589 4589
         } else {
4590
-            dsize = max - min;
4590
+            dsize = peinfo->max - peinfo->min;
4591 4591
 
4592
-            CLI_UNPSIZELIMITS("Petite", dsize);
4592
+            CLI_UNPSIZELIMITS("cli_scanpe: Petite", dsize);
4593 4593
 
4594 4594
             if ((dest = (char *)cli_calloc(dsize, sizeof(char))) == NULL) {
4595
-                cli_dbgmsg("Petite: Can't allocate %d bytes\n", dsize);
4596
-                free(exe_sections);
4595
+                cli_dbgmsg("cli_scanpe: Petite: Can't allocate %d bytes\n", dsize);
4596
+                cli_exe_info_destroy(peinfo);
4597 4597
                 return CL_EMEM;
4598 4598
             }
4599 4599
 
4600
-            for (i = 0; i < nsections; i++) {
4601
-                if (exe_sections[i].raw) {
4600
+            for (i = 0; i < peinfo->nsections; i++) {
4601
+                if (peinfo->sections[i].raw) {
4602 4602
                     unsigned int r_ret;
4603 4603
 
4604
-                    if (!exe_sections[i].rsz)
4604
+                    if (!peinfo->sections[i].rsz)
4605 4605
                         goto out_no_petite;
4606 4606
 
4607 4607
                     if (!CLI_ISCONTAINED(dest, dsize,
4608
-                                         dest + exe_sections[i].rva - min,
4609
-                                         exe_sections[i].ursz))
4608
+                                         dest + peinfo->sections[i].rva - peinfo->min,
4609
+                                         peinfo->sections[i].ursz))
4610 4610
                         goto out_no_petite;
4611 4611
 
4612
-                    r_ret = fmap_readn(map, dest + exe_sections[i].rva - min,
4613
-                                       exe_sections[i].raw,
4614
-                                       exe_sections[i].ursz);
4615
-                    if (r_ret != exe_sections[i].ursz) {
4612
+                    r_ret = fmap_readn(map, dest + peinfo->sections[i].rva - peinfo->min,
4613
+                                       peinfo->sections[i].raw,
4614
+                                       peinfo->sections[i].ursz);
4615
+                    if (r_ret != peinfo->sections[i].ursz) {
4616 4616
                     out_no_petite:
4617
-                        free(exe_sections);
4617
+                        cli_exe_info_destroy(peinfo);
4618 4618
                         free(dest);
4619 4619
                         return CL_CLEAN;
4620 4620
                     }
... ...
@@ -4626,33 +4042,33 @@ int cli_scanpe(cli_ctx *ctx)
4626 4626
                 cli_jsonstr(pe_json, "Packer", "Petite");
4627 4627
 #endif
4628 4628
 
4629
-            CLI_UNPTEMP("Petite", (dest, exe_sections, 0));
4630
-            CLI_UNPRESULTS("Petite", (petite_inflate2x_1to9(dest, min, max - min, exe_sections, nsections - (found == 1 ? 1 : 0), EC32(optional_hdr32.ImageBase), vep, ndesc, found, EC32(optional_hdr32.DataDirectory[2].VirtualAddress), EC32(optional_hdr32.DataDirectory[2].Size))), 0, (dest, 0));
4629
+            CLI_UNPTEMP("cli_scanpe: Petite", (dest, 0));
4630
+            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));
4631 4631
         }
4632 4632
     }
4633 4633
 
4634 4634
     /* PESpin 1.1 */
4635 4635
 
4636
-    if ((DCONF & PE_CONF_PESPIN) && nsections > 1 &&
4637
-        vep >= exe_sections[nsections - 1].rva &&
4638
-        0x3217 - 4 <= exe_sections[nsections - 1].rva + exe_sections[nsections - 1].rsz &&
4639
-        vep < exe_sections[nsections - 1].rva + exe_sections[nsections - 1].rsz - 0x3217 - 4 &&
4636
+    if ((DCONF & PE_CONF_PESPIN) && peinfo->nsections > 1 &&
4637
+        peinfo->vep >= peinfo->sections[peinfo->nsections - 1].rva &&
4638
+        0x3217 - 4 <= peinfo->sections[peinfo->nsections - 1].rva + peinfo->sections[peinfo->nsections - 1].rsz &&
4639
+        peinfo->vep < peinfo->sections[peinfo->nsections - 1].rva + peinfo->sections[peinfo->nsections - 1].rsz - 0x3217 - 4 &&
4640 4640
         memcmp(epbuff + 4, "\xe8\x00\x00\x00\x00\x8b\x1c\x24\x83\xc3", 10) == 0) {
4641 4641
 
4642 4642
         char *spinned;
4643 4643
 
4644
-        CLI_UNPSIZELIMITS("PEspin", fsize);
4644
+        CLI_UNPSIZELIMITS("cli_scanpe: PEspin", fsize);
4645 4645
 
4646 4646
         if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
4647
-            cli_errmsg("PESping: Unable to allocate memory for spinned %lu\n", (unsigned long)fsize);
4648
-            free(exe_sections);
4647
+            cli_errmsg("cli_scanpe: PESping: Unable to allocate memory for spinned %lu\n", (unsigned long)fsize);
4648
+            cli_exe_info_destroy(peinfo);
4649 4649
             return CL_EMEM;
4650 4650
         }
4651 4651
 
4652 4652
         if ((size_t)fmap_readn(map, spinned, 0, fsize) != fsize) {
4653
-            cli_dbgmsg("PESpin: Can't read %lu bytes\n", (unsigned long)fsize);
4653
+            cli_dbgmsg("cli_scanpe: PESpin: Can't read %lu bytes\n", (unsigned long)fsize);
4654 4654
             free(spinned);
4655
-            free(exe_sections);
4655
+            cli_exe_info_destroy(peinfo);
4656 4656
             return CL_EREAD;
4657 4657
         }
4658 4658
 
... ...
@@ -4661,13 +4077,13 @@ int cli_scanpe(cli_ctx *ctx)
4661 4661
             cli_jsonstr(pe_json, "Packer", "PEspin");
4662 4662
 #endif
4663 4663
 
4664
-        CLI_UNPTEMP("PESpin", (spinned, exe_sections, 0));
4665
-        CLI_UNPRESULTS_("PEspin", SPINCASE(), (unspin(spinned, fsize, exe_sections, nsections - 1, vep, ndesc, ctx)), 0, (spinned, 0));
4664
+        CLI_UNPTEMP("cli_scanpe: PESpin", (spinned, 0));
4665
+        CLI_UNPRESULTS_("cli_scanpe: PEspin", SPINCASE(), (unspin(spinned, fsize, peinfo->sections, peinfo->nsections - 1, peinfo->vep, ndesc, ctx)), 0, (spinned, 0));
4666 4666
     }
4667 4667
 
4668 4668
     /* yC 1.3 & variants */
4669
-    if ((DCONF & PE_CONF_YC) && nsections > 1 &&
4670
-        (EC32(optional_hdr32.AddressOfEntryPoint) == exe_sections[nsections - 1].rva + 0x60)) {
4669
+    if ((DCONF & PE_CONF_YC) && peinfo->nsections > 1 &&
4670
+        (EC32(peinfo->pe_opt.opt32.AddressOfEntryPoint) == peinfo->sections[peinfo->nsections - 1].rva + 0x60)) {
4671 4671
 
4672 4672
         uint32_t ecx = 0;
4673 4673
         int16_t offset;
... ...
@@ -4707,20 +4123,20 @@ int cli_scanpe(cli_ctx *ctx)
4707 4707
 
4708 4708
         if (ecx > 0x800 && ecx < 0x2000 &&
4709 4709
             !memcmp(epbuff + 0x63 + offset, "\xaa\xe2\xcc", 3) &&
4710
-            (fsize >= exe_sections[nsections - 1].raw + 0xC6 + ecx + offset)) {
4710
+            (fsize >= peinfo->sections[peinfo->nsections - 1].raw + 0xC6 + ecx + offset)) {
4711 4711
 
4712 4712
             char *spinned;
4713 4713
 
4714 4714
             if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
4715
-                cli_errmsg("yC: Unable to allocate memory for spinned %lu\n", (unsigned long)fsize);
4716
-                free(exe_sections);
4715
+                cli_errmsg("cli_scanpe: yC: Unable to allocate memory for spinned %lu\n", (unsigned long)fsize);
4716
+                cli_exe_info_destroy(peinfo);
4717 4717
                 return CL_EMEM;
4718 4718
             }
4719 4719
 
4720 4720
             if ((size_t)fmap_readn(map, spinned, 0, fsize) != fsize) {
4721
-                cli_dbgmsg("yC: Can't read %lu bytes\n", (unsigned long)fsize);
4721
+                cli_dbgmsg("cli_scanpe: yC: Can't read %lu bytes\n", (unsigned long)fsize);
4722 4722
                 free(spinned);
4723
-                free(exe_sections);
4723
+                cli_exe_info_destroy(peinfo);
4724 4724
                 return CL_EREAD;
4725 4725
             }
4726 4726
 
... ...
@@ -4736,15 +4152,15 @@ int cli_scanpe(cli_ctx *ctx)
4736 4736
                 if (ctx->virname)
4737 4737
                     yc_unp_virname = ctx->virname[0];
4738 4738
 
4739
-                cli_dbgmsg("%d,%d,%d,%d\n", nsections - 1, e_lfanew, ecx, offset);
4740
-                CLI_UNPTEMP("yC", (spinned, exe_sections, 0));
4741
-                CLI_UNPRESULTS("yC", (yc_decrypt(ctx, spinned, fsize, exe_sections, nsections - 1, e_lfanew, ndesc, ecx, offset)), 0, (spinned, 0));
4739
+                cli_dbgmsg("%d,%d,%d,%d\n", peinfo->nsections - 1, peinfo->e_lfanew, ecx, offset);
4740
+                CLI_UNPTEMP("cli_scanpe: yC", (spinned, 0));
4741
+                CLI_UNPRESULTS("cli_scanpe: yC", (yc_decrypt(ctx, spinned, fsize, peinfo->sections, peinfo->nsections - 1, peinfo->e_lfanew, ndesc, ecx, offset)), 0, (spinned, 0));
4742 4742
 
4743 4743
                 if (SCAN_ALLMATCHES && yc_unp_num_viruses != ctx->num_viruses) {
4744
-                    free(exe_sections);
4744
+                    cli_exe_info_destroy(peinfo);
4745 4745
                     return CL_VIRUS;
4746 4746
                 } else if (ctx->virname && yc_unp_virname != ctx->virname[0]) {
4747
-                    free(exe_sections);
4747
+                    cli_exe_info_destroy(peinfo);
4748 4748
                     return CL_VIRUS;
4749 4749
                 }
4750 4750
             } while (0);
... ...
@@ -4753,71 +4169,71 @@ int cli_scanpe(cli_ctx *ctx)
4753 4753
 
4754 4754
     /* WWPack */
4755 4755
 
4756
-    while ((DCONF & PE_CONF_WWPACK) && nsections > 1 &&
4757
-           vep == exe_sections[nsections - 1].rva &&
4756
+    while ((DCONF & PE_CONF_WWPACK) && peinfo->nsections > 1 &&
4757
+           peinfo->vep == peinfo->sections[peinfo->nsections - 1].rva &&
4758 4758
            memcmp(epbuff, "\x53\x55\x8b\xe8\x33\xdb\xeb", 7) == 0 &&
4759 4759
            memcmp(epbuff + 0x68, "\xe8\x00\x00\x00\x00\x58\x2d\x6d\x00\x00\x00\x50\x60\x33\xc9\x50\x58\x50\x50", 19) == 0) {
4760
-        uint32_t head = exe_sections[nsections - 1].raw;
4760
+        uint32_t head = peinfo->sections[peinfo->nsections - 1].raw;
4761 4761
         uint8_t *packer;
4762 4762
         char *src;
4763 4763
 
4764 4764
         ssize = 0;
4765 4765
         for (i = 0;; i++) {
4766
-            if (exe_sections[i].raw < head)
4767
-                head = exe_sections[i].raw;
4766
+            if (peinfo->sections[i].raw < head)
4767
+                head = peinfo->sections[i].raw;
4768 4768
 
4769
-            if (i + 1 == nsections)
4769
+            if (i + 1 == peinfo->nsections)
4770 4770
                 break;
4771 4771
 
4772
-            if (ssize < exe_sections[i].rva + exe_sections[i].vsz)
4773
-                ssize = exe_sections[i].rva + exe_sections[i].vsz;
4772
+            if (ssize < peinfo->sections[i].rva + peinfo->sections[i].vsz)
4773
+                ssize = peinfo->sections[i].rva + peinfo->sections[i].vsz;
4774 4774
         }
4775 4775
 
4776 4776
         if (!head || !ssize || head > ssize)
4777 4777
             break;
4778 4778
 
4779
-        CLI_UNPSIZELIMITS("WWPack", ssize);
4779
+        CLI_UNPSIZELIMITS("cli_scanpe: WWPack", ssize);
4780 4780
 
4781 4781
         if (!(src = (char *)cli_calloc(ssize, sizeof(char)))) {
4782
-            free(exe_sections);
4782
+            cli_exe_info_destroy(peinfo);
4783 4783
             return CL_EMEM;
4784 4784
         }
4785 4785
 
4786 4786
         if ((size_t)fmap_readn(map, src, 0, head) != head) {
4787
-            cli_dbgmsg("WWPack: Can't read %d bytes from headers\n", head);
4787
+            cli_dbgmsg("cli_scanpe: WWPack: Can't read %d bytes from headers\n", head);
4788 4788
             free(src);
4789
-            free(exe_sections);
4789
+            cli_exe_info_destroy(peinfo);
4790 4790
             return CL_EREAD;
4791 4791
         }
4792 4792
 
4793
-        for (i = 0; i < (unsigned int)nsections - 1; i++) {
4794
-            if (!exe_sections[i].rsz)
4793
+        for (i = 0; i < (unsigned int)peinfo->nsections - 1; i++) {
4794
+            if (!peinfo->sections[i].rsz)
4795 4795
                 continue;
4796 4796
 
4797
-            if (!CLI_ISCONTAINED(src, ssize, src + exe_sections[i].rva, exe_sections[i].rsz))
4797
+            if (!CLI_ISCONTAINED(src, ssize, src + peinfo->sections[i].rva, peinfo->sections[i].rsz))
4798 4798
                 break;
4799 4799
 
4800
-            if ((unsigned int)fmap_readn(map, src + exe_sections[i].rva, exe_sections[i].raw, exe_sections[i].rsz) != exe_sections[i].rsz)
4800
+            if ((unsigned int)fmap_readn(map, src + peinfo->sections[i].rva, peinfo->sections[i].raw, peinfo->sections[i].rsz) != peinfo->sections[i].rsz)
4801 4801
                 break;
4802 4802
         }
4803 4803
 
4804
-        if (i + 1 != nsections) {
4805
-            cli_dbgmsg("WWpack: Probably hacked/damaged file.\n");
4804
+        if (i + 1 != peinfo->nsections) {
4805
+            cli_dbgmsg("cli_scanpe: WWpack: Probably hacked/damaged file.\n");
4806 4806
             free(src);
4807 4807
             break;
4808 4808
         }
4809 4809
 
4810
-        if ((packer = (uint8_t *)cli_calloc(exe_sections[nsections - 1].rsz, sizeof(char))) == NULL) {
4810
+        if ((packer = (uint8_t *)cli_calloc(peinfo->sections[peinfo->nsections - 1].rsz, sizeof(char))) == NULL) {
4811 4811
             free(src);
4812
-            free(exe_sections);
4812
+            cli_exe_info_destroy(peinfo);
4813 4813
             return CL_EMEM;
4814 4814
         }
4815 4815
 
4816
-        if (!exe_sections[nsections - 1].rsz || (size_t)fmap_readn(map, packer, exe_sections[nsections - 1].raw, exe_sections[nsections - 1].rsz) != exe_sections[nsections - 1].rsz) {
4817
-            cli_dbgmsg("WWPack: Can't read %d bytes from wwpack sect\n", exe_sections[nsections - 1].rsz);
4816
+        if (!peinfo->sections[peinfo->nsections - 1].rsz || (size_t)fmap_readn(map, packer, peinfo->sections[peinfo->nsections - 1].raw, peinfo->sections[peinfo->nsections - 1].rsz) != peinfo->sections[peinfo->nsections - 1].rsz) {
4817
+            cli_dbgmsg("cli_scanpe: WWPack: Can't read %d bytes from wwpack sect\n", peinfo->sections[peinfo->nsections - 1].rsz);
4818 4818
             free(src);
4819 4819
             free(packer);
4820
-            free(exe_sections);
4820
+            cli_exe_info_destroy(peinfo);
4821 4821
             return CL_EREAD;
4822 4822
         }
4823 4823
 
... ...
@@ -4826,16 +4242,16 @@ int cli_scanpe(cli_ctx *ctx)
4826 4826
             cli_jsonstr(pe_json, "Packer", "WWPack");
4827 4827
 #endif
4828 4828
 
4829
-        CLI_UNPTEMP("WWPack", (src, packer, exe_sections, 0));
4830
-        CLI_UNPRESULTS("WWPack", (wwunpack((uint8_t *)src, ssize, packer, exe_sections, nsections - 1, e_lfanew, ndesc)), 0, (src, packer, 0));
4829
+        CLI_UNPTEMP("cli_scanpe: WWPack", (src, packer, 0));
4830
+        CLI_UNPRESULTS("cli_scanpe: WWPack", (wwunpack((uint8_t *)src, ssize, packer, peinfo->sections, peinfo->nsections - 1, peinfo->e_lfanew, ndesc)), 0, (src, packer, 0));
4831 4831
         break;
4832 4832
     }
4833 4833
 
4834 4834
     /* ASPACK support */
4835 4835
     while ((DCONF & PE_CONF_ASPACK) &&
4836
-           ((ep + ASPACK_EP_OFFSET_212 < fsize) ||
4837
-            (ep + ASPACK_EP_OFFSET_OTHER < fsize) ||
4838
-            (ep + ASPACK_EP_OFFSET_242 < fsize)) &&
4836
+           ((peinfo->ep + ASPACK_EP_OFFSET_212 < fsize) ||
4837
+            (peinfo->ep + ASPACK_EP_OFFSET_OTHER < fsize) ||
4838
+            (peinfo->ep + ASPACK_EP_OFFSET_242 < fsize)) &&
4839 4839
            (!memcmp(epbuff, "\x60\xe8\x03\x00\x00\x00\xe9\xeb", 8))) {
4840 4840
         char *src;
4841 4841
         aspack_version_t aspack_ver = ASPACK_VER_NONE;
... ...
@@ -4853,32 +4269,32 @@ int cli_scanpe(cli_ctx *ctx)
4853 4853
             break;
4854 4854
         }
4855 4855
         ssize = 0;
4856
-        for (i = 0; i < nsections; i++)
4857
-            if (ssize < exe_sections[i].rva + exe_sections[i].vsz)
4858
-                ssize = exe_sections[i].rva + exe_sections[i].vsz;
4856
+        for (i = 0; i < peinfo->nsections; i++)
4857
+            if (ssize < peinfo->sections[i].rva + peinfo->sections[i].vsz)
4858
+                ssize = peinfo->sections[i].rva + peinfo->sections[i].vsz;
4859 4859
 
4860 4860
         if (!ssize)
4861 4861
             break;
4862 4862
 
4863
-        CLI_UNPSIZELIMITS("Aspack", ssize);
4863
+        CLI_UNPSIZELIMITS("cli_scanpe: Aspack", ssize);
4864 4864
 
4865 4865
         if (!(src = (char *)cli_calloc(ssize, sizeof(char)))) {
4866
-            free(exe_sections);
4866
+            cli_exe_info_destroy(peinfo);
4867 4867
             return CL_EMEM;
4868 4868
         }
4869
-        for (i = 0; i < (unsigned int)nsections; i++) {
4870
-            if (!exe_sections[i].rsz)
4869
+        for (i = 0; i < (unsigned int)peinfo->nsections; i++) {
4870
+            if (!peinfo->sections[i].rsz)
4871 4871
                 continue;
4872 4872
 
4873
-            if (!CLI_ISCONTAINED(src, ssize, src + exe_sections[i].rva, exe_sections[i].rsz))
4873
+            if (!CLI_ISCONTAINED(src, ssize, src + peinfo->sections[i].rva, peinfo->sections[i].rsz))
4874 4874
                 break;
4875 4875
 
4876
-            if ((unsigned int)fmap_readn(map, src + exe_sections[i].rva, exe_sections[i].raw, exe_sections[i].rsz) != exe_sections[i].rsz)
4876
+            if ((unsigned int)fmap_readn(map, src + peinfo->sections[i].rva, peinfo->sections[i].raw, peinfo->sections[i].rsz) != peinfo->sections[i].rsz)
4877 4877
                 break;
4878 4878
         }
4879 4879
 
4880
-        if (i != nsections) {
4881
-            cli_dbgmsg("Aspack: Probably hacked/damaged Aspack file.\n");
4880
+        if (i != peinfo->nsections) {
4881
+            cli_dbgmsg("cli_scanpe: Aspack: Probably hacked/damaged Aspack file.\n");
4882 4882
             free(src);
4883 4883
             break;
4884 4884
         }
... ...
@@ -4888,23 +4304,23 @@ int cli_scanpe(cli_ctx *ctx)
4888 4888
             cli_jsonstr(pe_json, "Packer", "Aspack");
4889 4889
 #endif
4890 4890
 
4891
-        CLI_UNPTEMP("Aspack", (src, exe_sections, 0));
4892
-        CLI_UNPRESULTS("Aspack", (unaspack((uint8_t *)src, ssize, exe_sections, nsections, vep - 1, EC32(optional_hdr32.ImageBase), ndesc, aspack_ver)), 1, (src, 0));
4891
+        CLI_UNPTEMP("cli_scanpe: Aspack", (src, 0));
4892
+        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));
4893 4893
         break;
4894 4894
     }
4895 4895
 
4896 4896
     /* NsPack */
4897 4897
 
4898 4898
     while (DCONF & PE_CONF_NSPACK) {
4899
-        uint32_t eprva = vep;
4900
-        uint32_t start_of_stuff, rep = ep;
4899
+        uint32_t eprva = peinfo->vep;
4900
+        uint32_t start_of_stuff, rep = peinfo->ep;
4901 4901
         unsigned int nowinldr;
4902 4902
         const char *nbuff;
4903 4903
 
4904 4904
         src = epbuff;
4905 4905
         if (*epbuff == '\xe9') { /* bitched headers */
4906
-            eprva = cli_readint32(epbuff + 1) + vep + 5;
4907
-            if (!(rep = cli_rawaddr(eprva, exe_sections, nsections, &err, fsize, hdr_size)) && err)
4906
+            eprva = cli_readint32(epbuff + 1) + peinfo->vep + 5;
4907
+            if (!(rep = cli_rawaddr(eprva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err)
4908 4908
                 break;
4909 4909
 
4910 4910
             if (!(nbuff = fmap_need_off_once(map, rep, 24)))
... ...
@@ -4917,7 +4333,7 @@ int cli_scanpe(cli_ctx *ctx)
4917 4917
             break;
4918 4918
 
4919 4919
         nowinldr = 0x54 - cli_readint32(src + 17);
4920
-        cli_dbgmsg("NsPack: Found *start_of_stuff @delta-%x\n", nowinldr);
4920
+        cli_dbgmsg("cli_scanpe: NsPack: Found *start_of_stuff @delta-%x\n", nowinldr);
4921 4921
 
4922 4922
         if (!(nbuff = fmap_need_off_once(map, rep - nowinldr, 4)))
4923 4923
             break;
... ...
@@ -4935,13 +4351,13 @@ int cli_scanpe(cli_ctx *ctx)
4935 4935
         ssize = cli_readint32(src + 5) | 0xff;
4936 4936
         dsize = cli_readint32(src + 9);
4937 4937
 
4938
-        CLI_UNPSIZELIMITS("NsPack", MAX(ssize, dsize));
4938
+        CLI_UNPSIZELIMITS("cli_scanpe: NsPack", MAX(ssize, dsize));
4939 4939
 
4940
-        if (!ssize || !dsize || dsize != exe_sections[0].vsz)
4940
+        if (!ssize || !dsize || dsize != peinfo->sections[0].vsz)
4941 4941
             break;
4942 4942
 
4943 4943
         if (!(dest = cli_malloc(dsize))) {
4944
-            cli_errmsg("NsPack: Unable to allocate memory for dest %u\n", dsize);
4944
+            cli_errmsg("cli_scanpe: NsPack: Unable to allocate memory for dest %u\n", dsize);
4945 4945
             break;
4946 4946
         }
4947 4947
         /* memset(dest, 0xfc, dsize); */
... ...
@@ -4953,7 +4369,7 @@ int cli_scanpe(cli_ctx *ctx)
4953 4953
         /* memset(src, 0x00, ssize); */
4954 4954
 
4955 4955
         eprva += 0x27a;
4956
-        if (!(rep = cli_rawaddr(eprva, exe_sections, nsections, &err, fsize, hdr_size)) && err) {
4956
+        if (!(rep = cli_rawaddr(eprva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err) {
4957 4957
             free(dest);
4958 4958
             break;
4959 4959
         }
... ...
@@ -4965,15 +4381,15 @@ int cli_scanpe(cli_ctx *ctx)
4965 4965
 
4966 4966
         fmap_unneed_off(map, start_of_stuff, ssize);
4967 4967
         eprva = eprva + 5 + cli_readint32(nbuff + 1);
4968
-        cli_dbgmsg("NsPack: OEP = %08x\n", eprva);
4968
+        cli_dbgmsg("cli_scanpe: NsPack: OEP = %08x\n", eprva);
4969 4969
 
4970 4970
 #if HAVE_JSON
4971 4971
         if (pe_json != NULL)
4972 4972
             cli_jsonstr(pe_json, "Packer", "NsPack");
4973 4973
 #endif
4974 4974
 
4975
-        CLI_UNPTEMP("NsPack", (dest, exe_sections, 0));
4976
-        CLI_UNPRESULTS("NsPack", (unspack(src, dest, ctx, exe_sections[0].rva, EC32(optional_hdr32.ImageBase), eprva, ndesc)), 0, (dest, 0));
4975
+        CLI_UNPTEMP("cli_scanpe: NsPack", (dest, 0));
4976
+        CLI_UNPRESULTS("cli_scanpe: NsPack", (unspack(src, dest, ctx, peinfo->sections[0].rva, EC32(peinfo->pe_opt.opt32.ImageBase), eprva, ndesc)), 0, (dest, 0));
4977 4977
         break;
4978 4978
     }
4979 4979
 
... ...
@@ -4989,20 +4405,21 @@ int cli_scanpe(cli_ctx *ctx)
4989 4989
         return CL_EMEM;
4990 4990
     }
4991 4991
 
4992
-    cli_bytecode_context_setpe(bc_ctx, &pedata, exe_sections);
4992
+    cli_bytecode_context_setpe(bc_ctx, &pedata, peinfo->sections);
4993 4993
     cli_bytecode_context_setctx(bc_ctx, ctx);
4994 4994
 
4995 4995
     ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_UNPACKER, map);
4996 4996
     switch (ret) {
4997 4997
         case CL_VIRUS:
4998
-            free(exe_sections);
4998
+            cli_exe_info_destroy(peinfo);
4999 4999
             cli_bytecode_context_destroy(bc_ctx);
5000
+            // TODO Handle allmatch
5000 5001
             return CL_VIRUS;
5001 5002
         case CL_SUCCESS:
5002 5003
             ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
5003 5004
             cli_bytecode_context_destroy(bc_ctx);
5004 5005
             if (ndesc != -1 && tempfile) {
5005
-                CLI_UNPRESULTS("bytecode PE hook", 1, 1, (0));
5006
+                CLI_UNPRESULTS("cli_scanpe: bytecode PE hook", 1, 1, (0));
5006 5007
             }
5007 5008
 
5008 5009
             break;
... ...
@@ -5010,7 +4427,7 @@ int cli_scanpe(cli_ctx *ctx)
5010 5010
             cli_bytecode_context_destroy(bc_ctx);
5011 5011
     }
5012 5012
 
5013
-    free(exe_sections);
5013
+    cli_exe_info_destroy(peinfo);
5014 5014
 
5015 5015
 #if HAVE_JSON
5016 5016
     if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS)
... ...
@@ -5023,192 +4440,888 @@ int cli_scanpe(cli_ctx *ctx)
5023 5023
     return CL_CLEAN;
5024 5024
 }
5025 5025
 
5026
-int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo)
5026
+int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo)
5027 5027
 {
5028
-    uint16_t e_magic;  /* DOS signature ("MZ") */
5029
-    uint32_t e_lfanew; /* address of new exe header */
5030
-    /* Obsolete - see below
5031
-      uint32_t min = 0, max = 0;
5032
-    */
5033
-    struct pe_image_file_hdr file_hdr;
5034
-    union {
5035
-        struct pe_image_optional_hdr64 opt64;
5036
-        struct pe_image_optional_hdr32 opt32;
5037
-    } pe_opt;
5038
-    struct pe_image_section_hdr *section_hdr;
5039
-    unsigned int i;
5040
-    unsigned int err, pe_plus = 0;
5041
-    uint32_t valign, falign, hdr_size;
5028
+    return cli_peheader(map, peinfo, CLI_PEHEADER_OPT_EXTRACT_VINFO, NULL);
5029
+}
5030
+
5031
+/** Parse the PE header and, if successful, populate peinfo
5032
+ *
5033
+ * @param map The fmap_t backing the file being scanned
5034
+ * @param peinfo A structure to populate with info from the PE header. This
5035
+ *               MUST be initialized via cli_exe_info_init prior to calling
5036
+ *               being passed in.
5037
+ * @param opts A bitfield indicating various options related to PE header
5038
+ *             parsing.  The options are (prefixed with CLI_PEHEADER_OPT_):
5039
+ *              - NONE - Do default parsing
5040
+ *              - COLLECT_JSON - Populate ctx's json obj with PE header
5041
+ *                                   info
5042
+ *              - DBG_PRINT_INFO - Print debug information about the
5043
+ *                                 PE file. Right now, cli_peheader is
5044
+ *                                 called multiple times for a given PE,
5045
+ *                                 so you don't want to print out the
5046
+ *                                 same info each time.
5047
+ *              - EXTRACT_VINFO - Parse the PEs VERSION_INFO metadata
5048
+ *                                and store it in peinfo->vinfo
5049
+ *              - STRICT_ON_PE_ERRORS - If specified, some cases that
5050
+ *                                      might be considered a broken
5051
+ *                                      executable cause RET_BROKEN_PE
5052
+ *                                      to be returned, but otherwise
5053
+ *                                      these will be tolerated.
5054
+ * @param ctx The overaching cli_ctx.  This is required with certain opts, but
5055
+ *            optional otherwise.
5056
+ * @return If the PE header is parsed successfully, CLI_PEHEADER_RET_SUCCESS
5057
+ *         is returned. If it seems like the PE is broken,
5058
+ *         CLI_PEHEADER_RET_BROKEN_PE is returned.  Otherwise, one of the
5059
+ *         other error codes is returned.
5060
+ *         If CLI_PEHEADER_RET_SUCCESS is returned, it is the caller's
5061
+ *         responsibility to destroy peinfo.  Otherwise, it's safe to do
5062
+ *         so but not required.
5063
+ *
5064
+ * TODO What constitutes a "broken PE" seems somewhat arbitrary in places.
5065
+ * I think a PE should only be considered broken if it will not run on
5066
+ * any version of Windows.  We should invest more time to ensure that our
5067
+ * broken PE detection more closely aligns with this.
5068
+ *
5069
+ * TODO Simplify and get rid of CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS if
5070
+ * possible.  We should either fail always or ignore always, IMO.
5071
+ *
5072
+ * TODO Consolidate when information about the PE is printed (after successful
5073
+ * PE parsing).  This will allow us to simplify the code.  Some fail cases,
5074
+ * then, will cause PE info to not be printed at all, but I think this is
5075
+ * acceptable.  The debug messages generated in the fail cases should point to
5076
+ * what happened, and that's enough to track down the cause of any issues.
5077
+ *
5078
+ * TODO Same as above but with JSON creation
5079
+ */
5080
+int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx)
5081
+{
5082
+    uint16_t e_magic; /* DOS signature ("MZ") */
5083
+    const char *archtype = NULL, *subsystem = NULL;
5084
+    time_t timestamp;
5085
+    char timestr[32];
5086
+    uint32_t data_dirs_size;
5087
+    uint16_t opt_hdr_size;
5088
+    uint32_t stored_opt_hdr_size;
5089
+    struct pe_image_file_hdr *file_hdr;
5090
+    struct pe_image_optional_hdr32 *opt32;
5091
+    struct pe_image_optional_hdr64 *opt64;
5092
+    struct pe_image_section_hdr *section_hdrs;
5093
+    unsigned int i, j, section_pe_idx;
5094
+    unsigned int err;
5095
+    uint32_t salign, falign;
5042 5096
     size_t fsize;
5043 5097
     ssize_t at;
5044
-    struct pe_image_data_dir *dirs;
5098
+    uint32_t is_dll = 0;
5099
+    uint32_t is_exe = 0;
5100
+    int native      = 0;
5101
+    int read;
5102
+#if HAVE_JSON
5103
+    int toval                   = 0;
5104
+    struct json_object *pe_json = NULL;
5105
+    char jsonbuf[128];
5106
+#endif
5045 5107
 
5046
-    cli_dbgmsg("in cli_peheader\n");
5108
+    if (ctx == NULL &&
5109
+        (opts & CLI_PEHEADER_OPT_COLLECT_JSON ||
5110
+         opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO)) {
5111
+        cli_errmsg("cli_peheader: ctx can't be NULL for options specified\n");
5112
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5113
+    }
5114
+
5115
+#if HAVE_JSON
5116
+    if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5117
+        pe_json = get_pe_property(ctx);
5118
+    }
5119
+#endif
5047 5120
 
5048 5121
     fsize = map->len - peinfo->offset;
5049 5122
     if (fmap_readn(map, &e_magic, peinfo->offset, sizeof(e_magic)) != sizeof(e_magic)) {
5050
-        cli_dbgmsg("Can't read DOS signature\n");
5051
-        return -1;
5123
+        cli_dbgmsg("cli_peheader: Can't read DOS signature\n");
5124
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5052 5125
     }
5053 5126
 
5054 5127
     if (EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD) {
5055
-        cli_dbgmsg("Invalid DOS signature\n");
5056
-        return -1;
5128
+        cli_dbgmsg("cli_peheader: Invalid DOS signature\n");
5129
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5057 5130
     }
5058 5131
 
5059
-    if (fmap_readn(map, &e_lfanew, peinfo->offset + 58 + sizeof(e_magic), sizeof(e_lfanew)) != sizeof(e_lfanew)) {
5132
+    if (fmap_readn(map, &(peinfo->e_lfanew), peinfo->offset + 58 + sizeof(e_magic), sizeof(peinfo->e_lfanew)) != sizeof(peinfo->e_lfanew)) {
5060 5133
         /* truncated header? */
5061
-        return -1;
5134
+        cli_dbgmsg("cli_peheader: Unable to read e_lfanew - truncated header?\n");
5135
+        return CLI_PEHEADER_RET_BROKEN_PE;
5062 5136
     }
5063 5137
 
5064
-    e_lfanew = EC32(e_lfanew);
5065
-    if (!e_lfanew) {
5066
-        cli_dbgmsg("Not a PE file\n");
5067
-        return -1;
5138
+    peinfo->e_lfanew = EC32(peinfo->e_lfanew);
5139
+    if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5140
+        cli_dbgmsg("e_lfanew == %d\n", peinfo->e_lfanew);
5141
+    }
5142
+    if (!peinfo->e_lfanew) {
5143
+        cli_dbgmsg("cli_peheader: Not a PE file - e_lfanew == 0\n");
5144
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5068 5145
     }
5069 5146
 
5070
-    if (fmap_readn(map, &file_hdr, peinfo->offset + e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr)) {
5147
+    if (fmap_readn(map, &(peinfo->file_hdr), peinfo->offset + peinfo->e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr)) {
5071 5148
         /* bad information in e_lfanew - probably not a PE file */
5072 5149
         cli_dbgmsg("cli_peheader: Can't read file header\n");
5073
-        return -1;
5150
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5074 5151
     }
5075 5152
 
5076
-    if (EC32(file_hdr.Magic) != PE_IMAGE_NT_SIGNATURE) {
5077
-        cli_dbgmsg("Invalid PE signature (probably NE file)\n");
5078
-        return -1;
5153
+    file_hdr = &(peinfo->file_hdr);
5154
+
5155
+    if (EC32(file_hdr->Magic) != PE_IMAGE_NT_SIGNATURE) {
5156
+        cli_dbgmsg("cli_peheader: Invalid PE signature (probably NE file)\n");
5157
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5079 5158
     }
5080 5159
 
5081
-    if ((peinfo->nsections = EC16(file_hdr.NumberOfSections)) < 1 || peinfo->nsections > PE_MAXSECTIONS) return -1;
5160
+    if (EC16(file_hdr->Characteristics) & 0x2000) {
5082 5161
 
5083
-    if (EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32)) {
5084
-        cli_dbgmsg("SizeOfOptionalHeader too small\n");
5085
-        return -1;
5162
+#if HAVE_JSON
5163
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
5164
+            cli_jsonstr(pe_json, "Type", "DLL");
5165
+#endif
5166
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5167
+            cli_dbgmsg("File type: DLL\n");
5168
+        }
5169
+
5170
+        is_dll = 1;
5171
+    } else if (EC16(file_hdr->Characteristics) & 0x0002) {
5172
+
5173
+#if HAVE_JSON
5174
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
5175
+            cli_jsonstr(pe_json, "Type", "EXE");
5176
+#endif
5177
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5178
+            cli_dbgmsg("File type: Executable\n");
5179
+        }
5180
+
5181
+        is_exe = 1;
5086 5182
     }
5087 5183
 
5088
-    at = peinfo->offset + e_lfanew + sizeof(struct pe_image_file_hdr);
5089
-    if (fmap_readn(map, &optional_hdr32, at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
5090
-        cli_dbgmsg("Can't read optional file header\n");
5091
-        return -1;
5184
+    if (!is_dll && !is_exe) {
5185
+        cli_dbgmsg("cli_peheader: Assumption Violated: PE is not a DLL or EXE\n");
5186
+        // TODO Don't continue if not an exe or dll?
5092 5187
     }
5093
-    at += sizeof(struct pe_image_optional_hdr32);
5094 5188
 
5095
-    if (EC16(optional_hdr64.Magic) == PE32P_SIGNATURE) { /* PE+ */
5096
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr64)) {
5097
-            cli_dbgmsg("Incorrect SizeOfOptionalHeader for PE32+\n");
5098
-            return -1;
5189
+    peinfo->is_dll = is_dll;
5190
+
5191
+    if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO ||
5192
+        opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5193
+        switch (EC16(file_hdr->Machine)) {
5194
+            case 0x0:
5195
+                archtype = "Unknown";
5196
+                break;
5197
+            case 0x1:
5198
+                // New as of Windows 10, version 1607 and Windows Server 2016
5199
+                archtype = "Target Host";
5200
+                break;
5201
+            case 0x14c:
5202
+                archtype = "80386";
5203
+                break;
5204
+            case 0x14d:
5205
+                archtype = "80486";
5206
+                break;
5207
+            case 0x14e:
5208
+                archtype = "80586";
5209
+                break;
5210
+            case 0x160:
5211
+                archtype = "R3000 MIPS BE";
5212
+                break;
5213
+            case 0x162:
5214
+                archtype = "R3000 MIPS LE";
5215
+                break;
5216
+            case 0x166:
5217
+                archtype = "R4000 MIPS LE";
5218
+                break;
5219
+            case 0x168:
5220
+                archtype = "R10000 MIPS LE";
5221
+                break;
5222
+            case 0x169:
5223
+                archtype = "WCE MIPS LE";
5224
+                break;
5225
+            case 0x184:
5226
+                archtype = "DEC Alpha AXP";
5227
+                break;
5228
+            case 0x1a2:
5229
+                archtype = "Hitachi SH3 LE";
5230
+                break;
5231
+            case 0x1a3:
5232
+                archtype = "Hitachi SH3-DSP";
5233
+                break;
5234
+            case 0x1a4:
5235
+                archtype = "Hitachi SH3-E LE";
5236
+                break;
5237
+            case 0x1a6:
5238
+                archtype = "Hitachi SH4 LE";
5239
+                break;
5240
+            case 0x1a8:
5241
+                archtype = "Hitachi SH5";
5242
+                break;
5243
+            case 0x1c0:
5244
+                archtype = "ARM LE";
5245
+                break;
5246
+            case 0x1c2:
5247
+                archtype = "ARM Thumb/Thumb-2 LE";
5248
+                break;
5249
+            case 0x1c4:
5250
+                archtype = "ARM Thumb-2 LE";
5251
+                break;
5252
+            case 0x1d3:
5253
+                archtype = "AM33";
5254
+                break;
5255
+            case 0x1f0:
5256
+                archtype = "PowerPC LE";
5257
+                break;
5258
+            case 0x1f1:
5259
+                archtype = "PowerPC FP";
5260
+                break;
5261
+            case 0x200:
5262
+                archtype = "IA64";
5263
+                break;
5264
+            case 0x266:
5265
+                archtype = "MIPS16";
5266
+                break;
5267
+            case 0x268:
5268
+                archtype = "M68k";
5269
+                break;
5270
+            case 0x284:
5271
+                archtype = "DEC Alpha AXP 64bit";
5272
+                break;
5273
+            case 0x366:
5274
+                archtype = "MIPS+FPU";
5275
+                break;
5276
+            case 0x466:
5277
+                archtype = "MIPS16+FPU";
5278
+                break;
5279
+            case 0x520:
5280
+                archtype = "Infineon TriCore";
5281
+                break;
5282
+            case 0xcef:
5283
+                archtype = "CEF";
5284
+                break;
5285
+            case 0xebc:
5286
+                archtype = "EFI Byte Code";
5287
+                break;
5288
+            case 0x8664:
5289
+                archtype = "AMD64";
5290
+                break;
5291
+            case 0x9041:
5292
+                archtype = "M32R";
5293
+                break;
5294
+            case 0xaa64:
5295
+                archtype = "ARM64 LE";
5296
+                break;
5297
+            case 0xc0ee:
5298
+                archtype = "CEE";
5299
+                break;
5300
+            default:
5301
+                archtype = "Unknown";
5099 5302
         }
5100 5303
 
5101
-        if (fmap_readn(map, &optional_hdr32 + 1, at, sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) {
5102
-            cli_dbgmsg("Can't read optional file header\n");
5103
-            return -1;
5304
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO)
5305
+            cli_dbgmsg("Machine type: %s\n", archtype);
5306
+
5307
+#if HAVE_JSON
5308
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
5309
+            cli_jsonstr(pe_json, "ArchType", archtype);
5310
+#endif
5311
+    }
5312
+
5313
+    peinfo->nsections = EC16(file_hdr->NumberOfSections);
5314
+    if (peinfo->nsections == 0 || peinfo->nsections > PE_MAXSECTIONS) {
5315
+
5316
+#if HAVE_JSON
5317
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5318
+            pe_add_heuristic_property(ctx, "BadNumberOfSections");
5319
+        }
5320
+#endif
5321
+        // TODO Investigate how corrupted_input is set and whether this
5322
+        // check is needed
5323
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO &&
5324
+            !ctx->corrupted_input) {
5325
+            if (peinfo->nsections == 0) {
5326
+                cli_dbgmsg("cli_peheader: Invalid NumberOfSections (0)\n");
5327
+            } else {
5328
+                cli_dbgmsg("cli_peheader: Invalid NumberOfSections (>%d)\n", PE_MAXSECTIONS);
5329
+            }
5330
+        }
5331
+        return CLI_PEHEADER_RET_BROKEN_PE;
5332
+    }
5333
+
5334
+    timestamp    = (time_t)EC32(file_hdr->TimeDateStamp);
5335
+    opt_hdr_size = EC16(file_hdr->SizeOfOptionalHeader);
5336
+
5337
+    if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5338
+        cli_dbgmsg("NumberOfSections: %d\n", peinfo->nsections);
5339
+        cli_dbgmsg("TimeDateStamp: %s\n", cli_ctime(&timestamp, timestr, sizeof(timestr)));
5340
+        cli_dbgmsg("SizeOfOptionalHeader: 0x%x\n", opt_hdr_size);
5341
+    }
5342
+
5343
+#if HAVE_JSON
5344
+    if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5345
+        cli_jsonint(pe_json, "NumberOfSections", peinfo->nsections);
5346
+        cli_jsonstr(pe_json, "TimeDateStamp", cli_ctime(&timestamp, timestr, sizeof(timestr)));
5347
+        cli_jsonint(pe_json, "SizeOfOptionalHeader", opt_hdr_size);
5348
+    }
5349
+#endif
5350
+
5351
+    // Ensure there are enough bytes to cover the full optional header,
5352
+    // not including the data directory entries (which aren't all gauranteed
5353
+    // to be there)
5354
+    if (opt_hdr_size < sizeof(struct pe_image_optional_hdr32)) {
5355
+        cli_dbgmsg("cli_peheader: SizeOfOptionalHeader too small\n");
5356
+
5357
+#if HAVE_JSON
5358
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5359
+            pe_add_heuristic_property(ctx, "BadOptionalHeaderSize");
5360
+        }
5361
+#endif
5362
+        return CLI_PEHEADER_RET_BROKEN_PE;
5363
+    }
5364
+
5365
+    at = peinfo->offset + peinfo->e_lfanew + sizeof(struct pe_image_file_hdr);
5366
+    if (fmap_readn(map, &(peinfo->pe_opt.opt32), at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
5367
+        cli_dbgmsg("cli_peheader: Can't read optional file header\n");
5368
+        return CLI_PEHEADER_RET_BROKEN_PE;
5369
+    }
5370
+    stored_opt_hdr_size = sizeof(struct pe_image_optional_hdr32);
5371
+    at += stored_opt_hdr_size;
5372
+
5373
+    opt32 = &(peinfo->pe_opt.opt32);
5374
+
5375
+    if (EC16(opt32->Magic) == PE32P_SIGNATURE) { /* PE+ */
5376
+        // The PE32+ optional header is bigger by 16 bytes, so map in the
5377
+        // additional bytes here
5378
+
5379
+        if (opt_hdr_size < sizeof(struct pe_image_optional_hdr64)) {
5380
+            cli_dbgmsg("cli_peheader: Incorrect SizeOfOptionalHeader for PE32+\n");
5381
+#if HAVE_JSON
5382
+            if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5383
+                pe_add_heuristic_property(ctx, "BadOptionalHeaderSizePE32Plus");
5384
+            }
5385
+#endif
5386
+            return CLI_PEHEADER_RET_BROKEN_PE;
5387
+        }
5388
+
5389
+        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) {
5390
+            cli_dbgmsg("cli_peheader: Can't read additional optional file header bytes\n");
5391
+            return CLI_PEHEADER_RET_BROKEN_PE;
5392
+        }
5393
+
5394
+        stored_opt_hdr_size += OPT_HDR_SIZE_DIFF;
5395
+        at += OPT_HDR_SIZE_DIFF;
5396
+        peinfo->is_pe32plus = 1;
5397
+
5398
+        opt64 = &(peinfo->pe_opt.opt64);
5399
+
5400
+        peinfo->vep       = EC32(opt64->AddressOfEntryPoint);
5401
+        peinfo->hdr_size  = EC32(opt64->SizeOfHeaders);
5402
+        peinfo->ndatadirs = EC32(opt64->NumberOfRvaAndSizes);
5403
+
5404
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5405
+            cli_dbgmsg("File format: PE32+\n");
5406
+            cli_dbgmsg("MajorLinkerVersion: %d\n", opt64->MajorLinkerVersion);
5407
+            cli_dbgmsg("MinorLinkerVersion: %d\n", opt64->MinorLinkerVersion);
5408
+            cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(opt64->SizeOfCode));
5409
+            cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(opt64->SizeOfInitializedData));
5410
+            cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(opt64->SizeOfUninitializedData));
5411
+            cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", peinfo->vep);
5412
+            cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(opt64->BaseOfCode));
5413
+            cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(opt64->SectionAlignment));
5414
+            cli_dbgmsg("FileAlignment: 0x%x\n", EC32(opt64->FileAlignment));
5415
+            cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(opt64->MajorSubsystemVersion));
5416
+            cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(opt64->MinorSubsystemVersion));
5417
+            cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(opt64->SizeOfImage));
5418
+            cli_dbgmsg("SizeOfHeaders: 0x%x\n", peinfo->hdr_size);
5419
+            cli_dbgmsg("NumberOfRvaAndSizes: %u\n", peinfo->ndatadirs);
5420
+        }
5421
+
5422
+#if HAVE_JSON
5423
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5424
+            cli_jsonint(pe_json, "MajorLinkerVersion", opt64->MajorLinkerVersion);
5425
+            cli_jsonint(pe_json, "MinorLinkerVersion", opt64->MinorLinkerVersion);
5426
+            cli_jsonint(pe_json, "SizeOfCode", EC32(opt64->SizeOfCode));
5427
+            cli_jsonint(pe_json, "SizeOfInitializedData", EC32(opt64->SizeOfInitializedData));
5428
+            cli_jsonint(pe_json, "SizeOfUninitializedData", EC32(opt64->SizeOfUninitializedData));
5429
+            cli_jsonint(pe_json, "NumberOfRvaAndSizes", EC32(opt64->NumberOfRvaAndSizes));
5430
+            cli_jsonint(pe_json, "MajorSubsystemVersion", EC16(opt64->MajorSubsystemVersion));
5431
+            cli_jsonint(pe_json, "MinorSubsystemVersion", EC16(opt64->MinorSubsystemVersion));
5432
+
5433
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->vep);
5434
+            cli_jsonstr(pe_json, "EntryPoint", jsonbuf);
5435
+
5436
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->BaseOfCode));
5437
+            cli_jsonstr(pe_json, "BaseOfCode", jsonbuf);
5438
+
5439
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->SectionAlignment));
5440
+            cli_jsonstr(pe_json, "SectionAlignment", jsonbuf);
5441
+
5442
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->FileAlignment));
5443
+            cli_jsonstr(pe_json, "FileAlignment", jsonbuf);
5444
+
5445
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt64->SizeOfImage));
5446
+            cli_jsonstr(pe_json, "SizeOfImage", jsonbuf);
5447
+
5448
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->hdr_size);
5449
+            cli_jsonstr(pe_json, "SizeOfHeaders", jsonbuf);
5104 5450
         }
5451
+#endif
5105 5452
 
5106
-        at += sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5107
-        hdr_size = EC32(optional_hdr64.SizeOfHeaders);
5108
-        pe_plus  = 1;
5109 5453
     } else { /* PE */
5110
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr32)) {
5111
-            /* Seek to the end of the long header */
5112
-            at += EC16(file_hdr.SizeOfOptionalHeader) - sizeof(struct pe_image_optional_hdr32);
5454
+        peinfo->is_pe32plus = 0;
5455
+        peinfo->vep         = EC32(opt32->AddressOfEntryPoint);
5456
+        peinfo->hdr_size    = EC32(opt32->SizeOfHeaders);
5457
+        peinfo->ndatadirs   = EC32(opt32->NumberOfRvaAndSizes);
5458
+
5459
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5460
+            cli_dbgmsg("File format: PE\n");
5461
+            cli_dbgmsg("MajorLinkerVersion: %d\n", opt32->MajorLinkerVersion);
5462
+            cli_dbgmsg("MinorLinkerVersion: %d\n", opt32->MinorLinkerVersion);
5463
+            cli_dbgmsg("SizeOfCode: 0x%x\n", EC32(opt32->SizeOfCode));
5464
+            cli_dbgmsg("SizeOfInitializedData: 0x%x\n", EC32(opt32->SizeOfInitializedData));
5465
+            cli_dbgmsg("SizeOfUninitializedData: 0x%x\n", EC32(opt32->SizeOfUninitializedData));
5466
+            cli_dbgmsg("AddressOfEntryPoint: 0x%x\n", peinfo->vep);
5467
+            cli_dbgmsg("BaseOfCode: 0x%x\n", EC32(opt32->BaseOfCode));
5468
+            cli_dbgmsg("SectionAlignment: 0x%x\n", EC32(opt32->SectionAlignment));
5469
+            cli_dbgmsg("FileAlignment: 0x%x\n", EC32(opt32->FileAlignment));
5470
+            cli_dbgmsg("MajorSubsystemVersion: %d\n", EC16(opt32->MajorSubsystemVersion));
5471
+            cli_dbgmsg("MinorSubsystemVersion: %d\n", EC16(opt32->MinorSubsystemVersion));
5472
+            cli_dbgmsg("SizeOfImage: 0x%x\n", EC32(opt32->SizeOfImage));
5473
+            cli_dbgmsg("SizeOfHeaders: 0x%x\n", peinfo->hdr_size);
5474
+            cli_dbgmsg("NumberOfRvaAndSizes: %u\n", peinfo->ndatadirs);
5113 5475
         }
5114 5476
 
5115
-        hdr_size = EC32(optional_hdr32.SizeOfHeaders);
5477
+#if HAVE_JSON
5478
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5479
+            cli_jsonint(pe_json, "MajorLinkerVersion", opt32->MajorLinkerVersion);
5480
+            cli_jsonint(pe_json, "MinorLinkerVersion", opt32->MinorLinkerVersion);
5481
+            cli_jsonint(pe_json, "SizeOfCode", EC32(opt32->SizeOfCode));
5482
+            cli_jsonint(pe_json, "SizeOfInitializedData", EC32(opt32->SizeOfInitializedData));
5483
+            cli_jsonint(pe_json, "SizeOfUninitializedData", EC32(opt32->SizeOfUninitializedData));
5484
+            cli_jsonint(pe_json, "NumberOfRvaAndSizes", EC32(opt32->NumberOfRvaAndSizes));
5485
+            cli_jsonint(pe_json, "MajorSubsystemVersion", EC16(opt32->MajorSubsystemVersion));
5486
+            cli_jsonint(pe_json, "MinorSubsystemVersion", EC16(opt32->MinorSubsystemVersion));
5487
+
5488
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->vep);
5489
+            cli_jsonstr(pe_json, "EntryPoint", jsonbuf);
5490
+
5491
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->BaseOfCode));
5492
+            cli_jsonstr(pe_json, "BaseOfCode", jsonbuf);
5493
+
5494
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->SectionAlignment));
5495
+            cli_jsonstr(pe_json, "SectionAlignment", jsonbuf);
5496
+
5497
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->FileAlignment));
5498
+            cli_jsonstr(pe_json, "FileAlignment", jsonbuf);
5499
+
5500
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", EC32(opt32->SizeOfImage));
5501
+            cli_jsonstr(pe_json, "SizeOfImage", jsonbuf);
5502
+
5503
+            snprintf(jsonbuf, sizeof(jsonbuf), "0x%x", peinfo->hdr_size);
5504
+            cli_jsonstr(pe_json, "SizeOfHeaders", jsonbuf);
5505
+        }
5506
+#endif
5116 5507
     }
5117 5508
 
5118
-    valign = (pe_plus) ? EC32(optional_hdr64.SectionAlignment) : EC32(optional_hdr32.SectionAlignment);
5119
-    falign = (pe_plus) ? EC32(optional_hdr64.FileAlignment) : EC32(optional_hdr32.FileAlignment);
5509
+    salign = (peinfo->is_pe32plus) ? EC32(opt64->SectionAlignment) : EC32(opt32->SectionAlignment);
5510
+    falign = (peinfo->is_pe32plus) ? EC32(opt64->FileAlignment) : EC32(opt32->FileAlignment);
5120 5511
 
5121
-    peinfo->hdr_size = hdr_size = PESALIGN(hdr_size, valign);
5512
+    switch (peinfo->is_pe32plus ? EC16(opt64->Subsystem) : EC16(opt32->Subsystem)) {
5513
+        case 0:
5514
+            subsystem = "Unknown";
5515
+            break;
5516
+        case 1:
5517
+            subsystem = "Native (svc)";
5518
+            native    = 1;
5519
+            break;
5520
+        case 2:
5521
+            subsystem = "Win32 GUI";
5522
+            break;
5523
+        case 3:
5524
+            subsystem = "Win32 console";
5525
+            break;
5526
+        case 5:
5527
+            subsystem = "OS/2 console";
5528
+            break;
5529
+        case 7:
5530
+            subsystem = "POSIX console";
5531
+            break;
5532
+        case 8:
5533
+            subsystem = "Native Win9x driver";
5534
+            break;
5535
+        case 9:
5536
+            subsystem = "WinCE GUI";
5537
+            break;
5538
+        case 10:
5539
+            subsystem = "EFI application";
5540
+            break;
5541
+        case 11:
5542
+            subsystem = "EFI driver";
5543
+            break;
5544
+        case 12:
5545
+            subsystem = "EFI runtime driver";
5546
+            break;
5547
+        case 13:
5548
+            subsystem = "EFI ROM image";
5549
+            break;
5550
+        case 14:
5551
+            subsystem = "Xbox";
5552
+            break;
5553
+        case 16:
5554
+            subsystem = "Boot application";
5555
+            break;
5556
+        default:
5557
+            subsystem = "Unknown";
5558
+    }
5559
+
5560
+    if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5561
+        cli_dbgmsg("Subsystem: %s\n", subsystem);
5562
+        cli_dbgmsg("------------------------------------\n");
5563
+    }
5564
+
5565
+#if HAVE_JSON
5566
+    if (opts & CLI_PEHEADER_OPT_COLLECT_JSON)
5567
+        cli_jsonstr(pe_json, "Subsystem", subsystem);
5568
+#endif
5122 5569
 
5123
-    peinfo->section = (struct cli_exe_section *)cli_calloc(peinfo->nsections, sizeof(struct cli_exe_section));
5570
+    if (!native && (!salign || (salign % 0x1000))) {
5571
+        cli_dbgmsg("cli_peheader: Bad section alignment\n");
5572
+        if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
5573
+            return CLI_PEHEADER_RET_BROKEN_PE;
5574
+        }
5575
+    }
5124 5576
 
5125
-    if (!peinfo->section) {
5126
-        cli_dbgmsg("Can't allocate memory for section headers\n");
5127
-        return -1;
5577
+    if (!native && (!falign || (falign % 0x200))) {
5578
+        cli_dbgmsg("cli_peheader: Bad file alignment\n");
5579
+        if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
5580
+            return CLI_PEHEADER_RET_BROKEN_PE;
5581
+        }
5128 5582
     }
5129 5583
 
5130
-    section_hdr = (struct pe_image_section_hdr *)cli_calloc(peinfo->nsections, sizeof(struct pe_image_section_hdr));
5584
+    // Map in the optional header data directories.  The spec defines 16
5585
+    // directory entries, but NumberOfRvaAndSizes can be less than that
5586
+    // and the Windows loader will pretend that the data directory does
5587
+    // not exist. NumberOfRvaAndSizes can be larger than that too, which
5588
+    // the Windows loader is OK with.  To populate peinfo->dirs, we will
5589
+    // copy in as many data dirs are specified but for a max of 16 (and
5590
+    // adjust peinfo->ndatadirs accordingly)
5131 5591
 
5132
-    if (!section_hdr) {
5133
-        cli_dbgmsg("Can't allocate memory for section headers\n");
5134
-        free(peinfo->section);
5135
-        peinfo->section = NULL;
5136
-        return -1;
5592
+    if (peinfo->ndatadirs > 0x10) {
5593
+        cli_dbgmsg("cli_peheader: Encountered NumberOfRvaAndSizes > 16 (suspicious)\n");
5137 5594
     }
5138 5595
 
5139
-    if (fmap_readn(map, section_hdr, at, peinfo->nsections * sizeof(struct pe_image_section_hdr)) != peinfo->nsections * sizeof(struct pe_image_section_hdr)) {
5140
-        cli_dbgmsg("Can't read section header\n");
5141
-        cli_dbgmsg("Possibly broken PE file\n");
5142
-        free(section_hdr);
5143
-        free(peinfo->section);
5144
-        peinfo->section = NULL;
5145
-        return -1;
5596
+    // In the case where we won't fully populate dirs with file data,
5597
+    // ensure that the underlying memory is zero so that existing code
5598
+    // can interact with peinfo->dirs without using peinfo->ndatadirs
5599
+    if (peinfo->ndatadirs < sizeof(peinfo->dirs) / sizeof(peinfo->dirs[0])) {
5600
+        memset(&(peinfo->dirs), '\0', sizeof(peinfo->dirs));
5601
+    }
5602
+
5603
+    peinfo->ndatadirs = MIN(peinfo->ndatadirs, sizeof(peinfo->dirs) / sizeof(peinfo->dirs[0]));
5604
+
5605
+    data_dirs_size = sizeof(struct pe_image_data_dir) * peinfo->ndatadirs;
5606
+
5607
+    if (opt_hdr_size < (stored_opt_hdr_size + data_dirs_size)) {
5608
+        cli_dbgmsg("cli_peheader: SizeOfOptionalHeader too small (doesn't include data dir size)\n");
5609
+        return CLI_PEHEADER_RET_BROKEN_PE;
5610
+    }
5611
+
5612
+    read = fmap_readn(map, peinfo->dirs, at, data_dirs_size);
5613
+    if (read < 0 || (uint32_t)read != data_dirs_size) {
5614
+        cli_dbgmsg("cli_peheader: Can't read optional file header data dirs\n");
5615
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5616
+    }
5617
+    at += data_dirs_size;
5618
+
5619
+    if (opt_hdr_size != (stored_opt_hdr_size + data_dirs_size)) {
5620
+        /* Seek to the end of the long header */
5621
+        cli_dbgmsg("cli_peheader: Encountered case where SizeOfOptionalHeader appears bigger than required\n");
5622
+        at += opt_hdr_size - (stored_opt_hdr_size + data_dirs_size);
5623
+    }
5624
+
5625
+    // TODO This level of processing might not be needed in all cases
5626
+
5627
+    // Sanity checks
5628
+    // TODO Also check that salign >= falign
5629
+    if (peinfo->hdr_size != PESALIGN(peinfo->hdr_size, salign)) {
5630
+        cli_dbgmsg("cli_peheader: SizeOfHeader is not aligned to the SectionAlignment\n");
5631
+    }
5632
+    if (peinfo->hdr_size != PESALIGN(peinfo->hdr_size, falign)) {
5633
+        cli_dbgmsg("cli_peheader: SizeOfHeader is not aligned to the FileAlignment\n");
5634
+    }
5635
+
5636
+    // TODO Why align here? -- /* Aligned headers virtual size */
5637
+    // hdr_size should already be rounded up
5638
+    // to a multiple of the file alignment.
5639
+    // TODO in cli_checkpe_fp this aligned to falign, elsewhere it aligned to salign
5640
+    peinfo->hdr_size = PESALIGN(peinfo->hdr_size, salign);
5641
+
5642
+    peinfo->sections = (struct cli_exe_section *)cli_calloc(peinfo->nsections, sizeof(struct cli_exe_section));
5643
+
5644
+    if (!peinfo->sections) {
5645
+        cli_dbgmsg("cli_peheader: Can't allocate memory for section headers\n");
5646
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5647
+    }
5648
+
5649
+    section_hdrs = (struct pe_image_section_hdr *)cli_calloc(peinfo->nsections, sizeof(struct pe_image_section_hdr));
5650
+
5651
+    if (!section_hdrs) {
5652
+        cli_dbgmsg("cli_peheader: Can't allocate memory for section headers\n");
5653
+        free(peinfo->sections);
5654
+        peinfo->sections = NULL;
5655
+        return CLI_PEHEADER_RET_GENERIC_ERROR;
5656
+    }
5657
+
5658
+    read = fmap_readn(map, section_hdrs, at, peinfo->nsections * sizeof(struct pe_image_section_hdr));
5659
+    if (read < 0 || (uint32_t)read != peinfo->nsections * sizeof(struct pe_image_section_hdr)) {
5660
+        cli_dbgmsg("cli_peheader: Can't read section header - possibly broken PE file\n");
5661
+        free(section_hdrs);
5662
+        free(peinfo->sections);
5663
+        peinfo->sections = NULL;
5664
+        return CLI_PEHEADER_RET_BROKEN_PE;
5146 5665
     }
5147 5666
     at += sizeof(struct pe_image_section_hdr) * peinfo->nsections;
5148 5667
 
5668
+    // TODO Verify that this performs correctly
5669
+    // TODO I'm not sure why this is necessary since the specification says
5670
+    // that PointerToRawData is expected to be a multiple of the file
5671
+    // alignment.  Should we report this is as a PE with an error?
5672
+
5149 5673
     for (i = 0; falign != 0x200 && i < peinfo->nsections; i++) {
5150 5674
         /* file alignment fallback mode - blah */
5151
-        if (falign && section_hdr[i].SizeOfRawData && EC32(section_hdr[i].PointerToRawData) % falign && !(EC32(section_hdr[i].PointerToRawData) % 0x200)) {
5675
+        if (falign && section_hdrs[i].SizeOfRawData && EC32(section_hdrs[i].PointerToRawData) % falign && !(EC32(section_hdrs[i].PointerToRawData) % 0x200)) {
5676
+            cli_dbgmsg("cli_peheader: Encountered section with unexpected alignment - triggering fallback mode\n");
5152 5677
             falign = 0x200;
5153 5678
         }
5154 5679
     }
5155 5680
 
5156
-    for (i = 0; i < peinfo->nsections; i++) {
5157
-        peinfo->section[i].rva = PEALIGN(EC32(section_hdr[i].VirtualAddress), valign);
5158
-        peinfo->section[i].vsz = PESALIGN(EC32(section_hdr[i].VirtualSize), valign);
5159
-        peinfo->section[i].raw = PEALIGN(EC32(section_hdr[i].PointerToRawData), falign);
5160
-        peinfo->section[i].rsz = PESALIGN(EC32(section_hdr[i].SizeOfRawData), falign);
5681
+    fsize = (map->len - peinfo->offset);
5682
+
5683
+    // TODO Why do we fix up these alignments?  This shouldn't be needed?
5684
+    for (i = 0, section_pe_idx = 0; i < peinfo->nsections; i++, section_pe_idx++) {
5685
+
5686
+        struct cli_exe_section *section          = &(peinfo->sections[i]);
5687
+        struct pe_image_section_hdr *section_hdr = &(section_hdrs[i]);
5688
+        char sname[9];
5689
+
5690
+        // TODO I don't see any documentation that says VirtualAddress and VirtualSize must be aligned
5691
+        section->rva  = PEALIGN(EC32(section_hdr->VirtualAddress), salign);
5692
+        section->vsz  = PESALIGN(EC32(section_hdr->VirtualSize), salign);
5693
+        section->raw  = PEALIGN(EC32(section_hdr->PointerToRawData), falign);
5694
+        section->rsz  = PESALIGN(EC32(section_hdr->SizeOfRawData), falign);
5695
+        section->chr  = EC32(section_hdr->Characteristics);
5696
+        section->urva = EC32(section_hdr->VirtualAddress); /* Just in case */
5697
+        section->uvsz = EC32(section_hdr->VirtualSize);
5698
+        section->uraw = EC32(section_hdr->PointerToRawData);
5699
+        section->ursz = EC32(section_hdr->SizeOfRawData);
5700
+
5701
+        // First, if a section exists totally outside of a file, remove the
5702
+        // section from the list.
5703
+        // TODO Document that this happens in the function documentation
5704
+        if (section->rsz) { /* Don't bother with virtual only sections */
5705
+            if (section->raw >= fsize || section->uraw >= fsize) {
5706
+                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);
5707
+                if (peinfo->nsections == 1) {
5708
+                    free(section_hdrs);
5709
+                    free(peinfo->sections);
5710
+                    peinfo->sections = NULL;
5711
+                    return CLI_PEHEADER_RET_BROKEN_PE;
5712
+                }
5713
+
5714
+                for (j = i; j < peinfo->nsections - 1; j++)
5715
+                    memcpy(&(peinfo->sections[j]), &(peinfo->sections[j + 1]), sizeof(struct cli_exe_section));
5716
+
5717
+                for (j = i; j < peinfo->nsections - 1; j++)
5718
+                    memcpy(&section_hdrs[j], &section_hdrs[j + 1], sizeof(struct pe_image_section_hdr));
5161 5719
 
5162
-        if (!peinfo->section[i].vsz && peinfo->section[i].rsz)
5163
-            peinfo->section[i].vsz = PESALIGN(EC32(section_hdr[i].SizeOfRawData), valign);
5720
+                peinfo->nsections--;
5721
+
5722
+                // Adjust i since we removed a section and continue on
5723
+                i--;
5724
+                continue;
5725
+            }
5164 5726
 
5165
-        if (peinfo->section[i].rsz && !CLI_ISCONTAINED(0, (uint32_t)fsize, peinfo->section[i].raw, peinfo->section[i].rsz))
5166
-            peinfo->section[i].rsz = (fsize - peinfo->section[i].raw) * (fsize > peinfo->section[i].raw);
5727
+            // Verify that the section is fully contained within the file
5728
+            if (!CLI_ISCONTAINED(0, fsize, section->raw, section->rsz)) {
5729
+                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);
5730
+                section->rsz = fsize - section->raw;
5731
+            }
5732
+
5733
+            if (!CLI_ISCONTAINED(0, fsize, section->uraw, section->ursz)) {
5734
+                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);
5735
+                section->ursz = fsize - section->uraw;
5736
+            }
5737
+        }
5738
+
5739
+        strncpy(sname, (char *)section_hdr->Name, 8);
5740
+        sname[8] = '\0';
5741
+
5742
+#if HAVE_JSON
5743
+        if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5744
+            add_section_info(ctx, &peinfo->sections[i]);
5745
+
5746
+            if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
5747
+                free(section_hdrs);
5748
+                free(peinfo->sections);
5749
+                peinfo->sections = NULL;
5750
+                return CLI_PEHEADER_RET_JSON_TIMEOUT;
5751
+            }
5752
+        }
5753
+#endif
5754
+
5755
+        // TODO Why do we do this
5756
+        // TODO Should this be done before we dump the json
5757
+        if (!section->vsz && section->rsz)
5758
+            section->vsz = PESALIGN(section->ursz, salign);
5759
+
5760
+        if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5761
+            cli_dbgmsg("Section %d\n", section_pe_idx);
5762
+            cli_dbgmsg("Section name: %s\n", sname);
5763
+            cli_dbgmsg("Section data (from headers - in memory)\n");
5764
+            cli_dbgmsg("VirtualSize: 0x%x 0x%x\n", section->uvsz, section->vsz);
5765
+            cli_dbgmsg("VirtualAddress: 0x%x 0x%x\n", section->urva, section->rva);
5766
+            cli_dbgmsg("SizeOfRawData: 0x%x 0x%x\n", section->ursz, section->rsz);
5767
+            cli_dbgmsg("PointerToRawData: 0x%x 0x%x\n", section->uraw, section->raw);
5768
+
5769
+            if (section->chr & 0x20) {
5770
+                cli_dbgmsg("Section contains executable code\n");
5771
+            }
5772
+
5773
+            if (section->vsz < section->rsz) {
5774
+                cli_dbgmsg("Section contains free space\n");
5775
+                /*
5776
+                cli_dbgmsg("Dumping %d bytes\n", section_hdr.SizeOfRawData - section_hdr.VirtualSize);
5777
+                ddump(desc, section_hdr.PointerToRawData + section_hdr.VirtualSize, section_hdr.SizeOfRawData - section_hdr.VirtualSize, cli_gentemp(NULL));
5778
+                */
5779
+            }
5780
+
5781
+            if (section->chr & 0x20000000)
5782
+                cli_dbgmsg("Section's memory is executable\n");
5783
+
5784
+            if (section->chr & 0x80000000)
5785
+                cli_dbgmsg("Section's memory is writeable\n");
5786
+
5787
+            cli_dbgmsg("------------------------------------\n");
5788
+        }
5789
+
5790
+        if (!salign || (section->urva % salign)) { /* Bad section alignment */
5791
+            cli_dbgmsg("cli_peheader: Broken PE - section's VirtualAddress is misaligned\n");
5792
+            if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
5793
+                free(section_hdrs);
5794
+                free(peinfo->sections);
5795
+                peinfo->sections = NULL;
5796
+                return CLI_PEHEADER_RET_BROKEN_PE;
5797
+            }
5798
+        }
5799
+
5800
+        // TODO should we skip all of these checks if it's an empty
5801
+        // section? Why the exception for uraw?
5802
+        if (section->urva >> 31 || section->uvsz >> 31 || (section->rsz && section->uraw >> 31) || peinfo->sections[i].ursz >> 31) {
5803
+            cli_dbgmsg("cli_peheader: Found PE values with sign bit set\n");
5804
+
5805
+            free(section_hdrs);
5806
+            free(peinfo->sections);
5807
+            peinfo->sections = NULL;
5808
+
5809
+            return CLI_PEHEADER_RET_BROKEN_PE;
5810
+        }
5811
+
5812
+        if (!i) {
5813
+            if (section->urva != peinfo->hdr_size) { /* Bad first section RVA */
5814
+                cli_dbgmsg("cli_peheader: First section doesn't start immediately after the header\n");
5815
+                if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
5816
+                    free(section_hdrs);
5817
+                    free(peinfo->sections);
5818
+                    peinfo->sections = NULL;
5819
+                    return CLI_PEHEADER_RET_BROKEN_PE;
5820
+                }
5821
+            }
5822
+
5823
+            peinfo->min = section->rva;
5824
+            peinfo->max = section->rva + section->rsz;
5825
+        } else {
5826
+            if (section->urva - peinfo->sections[i - 1].urva != peinfo->sections[i - 1].vsz) { /* No holes, no overlapping, no virtual disorder */
5827
+                cli_dbgmsg("cli_peheader: Virtually misplaced section (wrong order, overlapping, non contiguous)\n");
5828
+                if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
5829
+                    free(section_hdrs);
5830
+                    free(peinfo->sections);
5831
+                    peinfo->sections = NULL;
5832
+                    return CLI_PEHEADER_RET_BROKEN_PE;
5833
+                }
5834
+            }
5835
+
5836
+            if (section->rva < peinfo->min)
5837
+                peinfo->min = section->rva;
5838
+
5839
+            if (section->rva + section->rsz > peinfo->max) {
5840
+                peinfo->max           = section->rva + section->rsz;
5841
+                peinfo->overlay_start = section->raw + section->rsz;
5842
+            }
5843
+
5844
+            // TODO This case might be possible, which would lead to us
5845
+            // mislabelling the overlay
5846
+            if (section->raw + section->rsz > peinfo->max) {
5847
+                cli_dbgmsg("cli_peheader: Assumption Violated: Last section end RVA isn't tied to the last section\n");
5848
+            }
5849
+        }
5167 5850
     }
5168 5851
 
5169
-    if (pe_plus) {
5170
-        peinfo->ep = EC32(optional_hdr64.AddressOfEntryPoint);
5171
-        dirs       = optional_hdr64.DataDirectory;
5172
-    } else {
5173
-        peinfo->ep = EC32(optional_hdr32.AddressOfEntryPoint);
5174
-        dirs       = optional_hdr32.DataDirectory;
5852
+    peinfo->overlay_size = fsize - peinfo->overlay_start;
5853
+
5854
+    free(section_hdrs);
5855
+
5856
+    // NOTE: For DLLs the entrypoint is likely to be zero
5857
+    // TODO ^^^ Does this mean this doesn't work for DLLs?
5858
+    if (!(peinfo->ep = cli_rawaddr(peinfo->vep, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err) {
5859
+        cli_dbgmsg("cli_peheader: Broken PE file - Can't map EntryPoint to a file offset\n");
5860
+        free(peinfo->sections);
5861
+        peinfo->sections = NULL;
5862
+        return CLI_PEHEADER_RET_BROKEN_PE;
5863
+    }
5864
+
5865
+#if HAVE_JSON
5866
+    if (opts & CLI_PEHEADER_OPT_COLLECT_JSON) {
5867
+        cli_jsonint(pe_json, "EntryPointOffset", peinfo->ep);
5868
+
5869
+        if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
5870
+            free(peinfo->sections);
5871
+            peinfo->sections = NULL;
5872
+            return CLI_PEHEADER_RET_JSON_TIMEOUT;
5873
+        }
5175 5874
     }
5875
+#endif
5176 5876
 
5177
-    if (!(peinfo->ep = cli_rawaddr(peinfo->ep, peinfo->section, peinfo->nsections, &err, fsize, hdr_size)) && err) {
5178
-        cli_dbgmsg("Broken PE file\n");
5179
-        free(section_hdr);
5180
-        free(peinfo->section);
5181
-        peinfo->section = NULL;
5182
-        return -1;
5877
+    if (opts & CLI_PEHEADER_OPT_DBG_PRINT_INFO) {
5878
+        cli_dbgmsg("EntryPoint offset: 0x%x (%d)\n", peinfo->ep, peinfo->ep);
5183 5879
     }
5184 5880
 
5185
-    if (EC16(file_hdr.Characteristics) & 0x2000 || !dirs[2].Size)
5881
+    if (is_dll || peinfo->ndatadirs < 3 || !peinfo->dirs[2].Size)
5186 5882
         peinfo->res_addr = 0;
5187 5883
     else
5188
-        peinfo->res_addr = EC32(dirs[2].VirtualAddress);
5884
+        peinfo->res_addr = EC32(peinfo->dirs[2].VirtualAddress);
5189 5885
 
5190
-    while (dirs[2].Size) {
5886
+    while (opts & CLI_PEHEADER_OPT_EXTRACT_VINFO &&
5887
+           peinfo->ndatadirs >= 3 && peinfo->dirs[2].Size) {
5191 5888
         struct vinfo_list vlist;
5192 5889
         const uint8_t *vptr, *baseptr;
5193 5890
         uint32_t rva, res_sz;
5194 5891
 
5195 5892
         memset(&vlist, 0, sizeof(vlist));
5196
-        findres(0x10, 0xffffffff, EC32(dirs[2].VirtualAddress), map, peinfo->section, peinfo->nsections, hdr_size, versioninfo_cb, &vlist);
5893
+        findres(0x10, 0xffffffff, map, peinfo, versioninfo_cb, &vlist);
5197 5894
         if (!vlist.count)
5198 5895
             break; /* No version_information */
5199 5896
 
5200 5897
         if (cli_hashset_init(&peinfo->vinfo, 32, 80)) {
5201 5898
             cli_errmsg("cli_peheader: Unable to init vinfo hashset\n");
5202
-            free(section_hdr);
5203
-            free(peinfo->section);
5204
-            peinfo->section = NULL;
5205
-            return -1;
5899
+            free(peinfo->sections);
5900
+            peinfo->sections = NULL;
5901
+            return CLI_PEHEADER_RET_GENERIC_ERROR;
5206 5902
         }
5207 5903
 
5208 5904
         err = 0;
5209 5905
         for (i = 0; i < vlist.count; i++) { /* enum all version_information res - RESUMABLE */
5210 5906
             cli_dbgmsg("cli_peheader: parsing version info @ rva %x (%u/%u)\n", vlist.rvas[i], i + 1, vlist.count);
5211
-            rva = cli_rawaddr(vlist.rvas[i], peinfo->section, peinfo->nsections, &err, fsize, hdr_size);
5907
+            rva = cli_rawaddr(vlist.rvas[i], peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
5212 5908
             if (err)
5213 5909
                 continue;
5214 5910
 
... ...
@@ -5219,7 +5332,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo)
5219 5219
             /* parse resource */
5220 5220
             rva    = cli_readint32(vptr);     /* ptr to version_info */
5221 5221
             res_sz = cli_readint32(vptr + 4); /* sizeof(resource) */
5222
-            rva    = cli_rawaddr(rva, peinfo->section, peinfo->nsections, &err, fsize, hdr_size);
5222
+            rva    = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size);
5223 5223
             if (err)
5224 5224
                 continue;
5225 5225
             if (!(vptr = fmap_need_off_once(map, rva, res_sz)))
... ...
@@ -5334,10 +5447,9 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo)
5334 5334
                             if (cli_hashset_addkey(&peinfo->vinfo, (uint32_t)(vptr - baseptr + 6))) {
5335 5335
                                 cli_errmsg("cli_peheader: Unable to add rva to vinfo hashset\n");
5336 5336
                                 cli_hashset_destroy(&peinfo->vinfo);
5337
-                                free(section_hdr);
5338
-                                free(peinfo->section);
5339
-                                peinfo->section = NULL;
5340
-                                return -1;
5337
+                                free(peinfo->sections);
5338
+                                peinfo->sections = NULL;
5339
+                                return CLI_PEHEADER_RET_GENERIC_ERROR;
5341 5340
                             }
5342 5341
 
5343 5342
                             if (cli_debug_flag) {
... ...
@@ -5372,10 +5484,15 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo)
5372 5372
         break;
5373 5373
     } /* while(dirs[2].Size) */
5374 5374
 
5375
-    free(section_hdr);
5376
-    return 0;
5375
+    // Do final preperations for peinfo to be passed back
5376
+    peinfo->is_dll = is_dll;
5377
+
5378
+    return CLI_PEHEADER_RET_SUCCESS;
5377 5379
 }
5378 5380
 
5381
+// TODO We should sort based on VirtualAddress instead, since PointerToRawData
5382
+// will be zero for sections where SizeOfRawData is zero.  This also aligns
5383
+// with what tools like pefile do.
5379 5384
 static int sort_sects(const void *first, const void *second)
5380 5385
 {
5381 5386
     const struct cli_exe_section *a = first, *b = second;
... ...
@@ -5415,21 +5532,9 @@ static int sort_sects(const void *first, const void *second)
5415 5415
  *    sections? */
5416 5416
 cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5417 5417
 {
5418
-    uint16_t e_magic; /* DOS signature ("MZ") */
5419
-    uint16_t nsections;
5420
-    uint32_t e_lfanew; /* address of new exe header */
5421
-    struct pe_image_file_hdr file_hdr;
5422
-    union {
5423
-        struct pe_image_optional_hdr64 opt64;
5424
-        struct pe_image_optional_hdr32 opt32;
5425
-    } pe_opt;
5426
-    const struct pe_image_section_hdr *section_hdr;
5427
-    ssize_t at;
5428
-    unsigned int i, pe_plus = 0, hlen;
5418
+    size_t at;
5419
+    unsigned int i, hlen;
5429 5420
     size_t fsize;
5430
-    uint32_t valign, falign, hdr_size;
5431
-    struct cli_exe_section *exe_sections;
5432
-    struct pe_image_data_dir *dirs;
5433 5421
     fmap_t *map   = *ctx->fmap;
5434 5422
     void *hashctx = NULL;
5435 5423
     struct pe_certificate_hdr cert_hdr;
... ...
@@ -5439,6 +5544,15 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5439 5439
     uint8_t authsha1[SHA1_HASH_SIZE];
5440 5440
     uint32_t sec_dir_offset;
5441 5441
     uint32_t sec_dir_size;
5442
+    struct cli_exe_info _peinfo;
5443
+    struct cli_exe_info *peinfo = &_peinfo;
5444
+
5445
+    // If Authenticode parsing has been disabled via DCONF, then don't
5446
+    // continue on.
5447
+    // TODO This should probably be named PE_CONF_AUTHENTICODE instead
5448
+    // of PE_CONF_CATALOG
5449
+    if (!(DCONF & PE_CONF_CATALOG))
5450
+        return CL_EFORMAT;
5442 5451
 
5443 5452
     if (flags == CL_CHECKFP_PE_FLAG_NONE)
5444 5453
         return CL_BREAK;
... ...
@@ -5449,77 +5563,16 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5449 5449
         hashes->sections = NULL;
5450 5450
     }
5451 5451
 
5452
-    // TODO What does this do?
5453
-    if (!(DCONF & PE_CONF_CATALOG))
5454
-        return CL_EFORMAT;
5455
-
5456
-    if (fmap_readn(map, &e_magic, 0, sizeof(e_magic)) != sizeof(e_magic))
5457
-        return CL_EFORMAT;
5458
-
5459
-    if (EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD)
5460
-        return CL_EFORMAT;
5452
+    // TODO see if peinfo can be passed in (or lives in ctx or something) and
5453
+    // if so, use that to avoid having to re-parse the header
5454
+    cli_exe_info_init(peinfo, 0);
5461 5455
 
5462
-    if (fmap_readn(map, &e_lfanew, 58 + sizeof(e_magic), sizeof(e_lfanew)) != sizeof(e_lfanew))
5463
-        return CL_EFORMAT;
5464
-
5465
-    e_lfanew = EC32(e_lfanew);
5466
-    if (!e_lfanew)
5467
-        return CL_EFORMAT;
5468
-
5469
-    if (fmap_readn(map, &file_hdr, e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr))
5470
-        return CL_EFORMAT;
5471
-
5472
-    if (EC32(file_hdr.Magic) != PE_IMAGE_NT_SIGNATURE)
5473
-        return CL_EFORMAT;
5474
-
5475
-    nsections = EC16(file_hdr.NumberOfSections);
5476
-    if (nsections < 1 || nsections > PE_MAXSECTIONS)
5477
-        return CL_EFORMAT;
5478
-
5479
-    // TODO the pe_image_optional_hdr32 structure includes space for all 16
5480
-    // data directories, but these might not all exist in a given binary.
5481
-    // We need to check NumberOfRvaAndSizes instead, and allow through any
5482
-    // with at least 5 (the security DataDirectory)
5483
-    if (EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32)) {
5484
-        cli_dbgmsg("cli_checkfp_pe: SizeOfOptionalHeader < less than the size expected (%lu)\n", sizeof(struct pe_image_optional_hdr32));
5456
+    if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
5485 5457
         return CL_EFORMAT;
5486 5458
     }
5487 5459
 
5488
-    at = e_lfanew + sizeof(struct pe_image_file_hdr);
5489
-    if (fmap_readn(map, &optional_hdr32, at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32))
5490
-        return CL_EFORMAT;
5491
-
5492
-    at += sizeof(struct pe_image_optional_hdr32);
5493
-
5494
-    /* This will be a chicken and egg problem until we drop 9x */
5495
-    if (EC16(optional_hdr64.Magic) == PE32P_SIGNATURE) {
5496
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr64))
5497
-            return CL_EFORMAT;
5498
-
5499
-        pe_plus = 1;
5500
-    }
5501
-
5502
-    if (!pe_plus) { /* PE */
5503
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr32)) {
5504
-            /* Seek to the end of the long header */
5505
-            at += EC16(file_hdr.SizeOfOptionalHeader) - sizeof(struct pe_image_optional_hdr32);
5506
-        }
5507
-
5508
-        hdr_size = EC32(optional_hdr32.SizeOfHeaders);
5509
-        dirs     = optional_hdr32.DataDirectory;
5510
-    } else { /* PE+ */
5511
-        size_t readlen = sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5512
-        /* read the remaining part of the header */
5513
-        if ((size_t)fmap_readn(map, &optional_hdr32 + 1, at, readlen) != readlen)
5514
-            return CL_EFORMAT;
5515
-
5516
-        at += sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5517
-        hdr_size = EC32(optional_hdr64.SizeOfHeaders);
5518
-        dirs     = optional_hdr64.DataDirectory;
5519
-    }
5520
-
5521
-    sec_dir_offset = EC32(dirs[4].VirtualAddress);
5522
-    sec_dir_size   = EC32(dirs[4].Size);
5460
+    sec_dir_offset = EC32(peinfo->dirs[4].VirtualAddress);
5461
+    sec_dir_size   = EC32(peinfo->dirs[4].Size);
5523 5462
 
5524 5463
     // As an optimization, check the security DataDirectory here and if
5525 5464
     // it's less than 8-bytes (and we aren't relying on this code to compute
... ...
@@ -5530,42 +5583,18 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5530 5530
             /* If stats is enabled, continue parsing the sample */
5531 5531
             flags ^= CL_CHECKFP_PE_FLAG_AUTHENTICODE;
5532 5532
         } else {
5533
+            cli_exe_info_destroy(peinfo);
5533 5534
             return CL_BREAK;
5534 5535
         }
5535 5536
     }
5536 5537
     fsize = map->len;
5537 5538
 
5538
-    valign = (pe_plus) ? EC32(optional_hdr64.SectionAlignment) : EC32(optional_hdr32.SectionAlignment);
5539
-    falign = (pe_plus) ? EC32(optional_hdr64.FileAlignment) : EC32(optional_hdr32.FileAlignment);
5540
-
5541
-    section_hdr = fmap_need_off_once(map, at, sizeof(*section_hdr) * nsections);
5542
-    if (!section_hdr)
5543
-        return CL_EFORMAT;
5544
-
5545
-    at += sizeof(*section_hdr) * nsections;
5546
-
5547
-    exe_sections = (struct cli_exe_section *)cli_calloc(nsections, sizeof(struct cli_exe_section));
5548
-    if (!exe_sections)
5549
-        return CL_EMEM;
5550
-
5551
-    // TODO I'm not sure why this is necessary since the specification says
5552
-    // that PointerToRawData is expected to be a multiple of the file
5553
-    // alignment.  Should we report this is as a PE with an error?
5554
-    for (i = 0; falign != 0x200 && i < nsections; i++) {
5555
-        /* file alignment fallback mode - blah */
5556
-        if (falign && section_hdr[i].SizeOfRawData && EC32(section_hdr[i].PointerToRawData) % falign && !(EC32(section_hdr[i].PointerToRawData) % 0x200))
5557
-            falign = 0x200;
5558
-    }
5559
-
5560
-    // TODO Why is this needed?  hdr_size should already be rounded up
5561
-    // to a multiple of the file alignment.
5562
-    hdr_size = PESALIGN(hdr_size, falign); /* Aligned headers virtual size */
5563
-
5564 5539
     if (flags & CL_CHECKFP_PE_FLAG_STATS) {
5565
-        hashes->nsections = nsections;
5566
-        hashes->sections  = cli_calloc(nsections, sizeof(struct cli_section_hash));
5540
+        hashes->nsections = peinfo->nsections;
5541
+        hashes->sections  = cli_calloc(peinfo->nsections, sizeof(struct cli_section_hash));
5542
+        ;
5567 5543
         if (!(hashes->sections)) {
5568
-            free(exe_sections);
5544
+            cli_exe_info_destroy(peinfo);
5569 5545
             return CL_EMEM;
5570 5546
         }
5571 5547
     }
... ...
@@ -5578,64 +5607,29 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5578 5578
         }                                       \
5579 5579
     } while (0)
5580 5580
 
5581
-    // TODO Why do we fix up these alignments?  This shouldn't be needed?
5582
-    for (i = 0; i < nsections; i++) {
5583
-        exe_sections[i].rva = PEALIGN(EC32(section_hdr[i].VirtualAddress), valign);
5584
-        exe_sections[i].vsz = PESALIGN(EC32(section_hdr[i].VirtualSize), valign);
5585
-        exe_sections[i].raw = PEALIGN(EC32(section_hdr[i].PointerToRawData), falign);
5586
-        exe_sections[i].rsz = PESALIGN(EC32(section_hdr[i].SizeOfRawData), falign);
5587
-
5588
-        // TODO exe_sections[i].ursz is not assigned to (will always be 0)
5589
-        // Figure out what this is meant to do and ensure that happens
5590
-        if (!exe_sections[i].vsz && exe_sections[i].rsz)
5591
-            exe_sections[i].vsz = PESALIGN(exe_sections[i].ursz, valign);
5592
-
5593
-        if (exe_sections[i].rsz && fsize > exe_sections[i].raw && !CLI_ISCONTAINED(0, (uint32_t)fsize, exe_sections[i].raw, exe_sections[i].rsz)) {
5594
-            cli_dbgmsg("cli_checkfp_pe: encountered section not fully contained within the file\n");
5595
-            free(exe_sections);
5596
-            free_section_hashes();
5597
-            return CL_EFORMAT;
5598
-        }
5599
-
5600
-        if (exe_sections[i].rsz && exe_sections[i].raw >= fsize) {
5601
-            cli_dbgmsg("cli_checkfp_pe: encountered section that doesn't exist within the file\n");
5602
-            free(exe_sections);
5603
-            free_section_hashes();
5604
-            return CL_EFORMAT;
5605
-        }
5606
-
5607
-        // TODO These checks aren't needed because the u vars are never assigned (always 0)
5608
-        // Figure out what this is meant to do and ensure that happens
5609
-        if (exe_sections[i].urva >> 31 || exe_sections[i].uvsz >> 31 || (exe_sections[i].rsz && exe_sections[i].uraw >> 31) || exe_sections[i].ursz >> 31) {
5610
-            free(exe_sections);
5611
-            free_section_hashes();
5612
-            return CL_EFORMAT;
5613
-        }
5614
-    }
5615
-
5616 5581
     // TODO This likely isn't needed anymore, since we no longer compute
5617 5582
     // the authenticode hash like the 2008 spec doc says (sort sections
5618 5583
     // and use the section info to compute the hash)
5619
-    cli_qsort(exe_sections, nsections, sizeof(*exe_sections), sort_sects);
5584
+    cli_qsort(peinfo->sections, peinfo->nsections, sizeof(*peinfo->sections), sort_sects);
5620 5585
 
5621 5586
     /* Hash the sections */
5622 5587
     if (flags & CL_CHECKFP_PE_FLAG_STATS) {
5623 5588
 
5624
-        for (i = 0; i < nsections; i++) {
5589
+        for (i = 0; i < peinfo->nsections; i++) {
5625 5590
             const uint8_t *hptr;
5626 5591
             void *md5ctx;
5627 5592
 
5628
-            if (!exe_sections[i].rsz)
5593
+            if (!peinfo->sections[i].rsz)
5629 5594
                 continue;
5630 5595
 
5631
-            if (!(hptr = fmap_need_off_once(map, exe_sections[i].raw, exe_sections[i].rsz))) {
5632
-                free(exe_sections);
5596
+            if (!(hptr = fmap_need_off_once(map, peinfo->sections[i].raw, peinfo->sections[i].rsz))) {
5597
+                cli_exe_info_destroy(peinfo);
5633 5598
                 free_section_hashes();
5634 5599
                 return CL_EFORMAT;
5635 5600
             }
5636 5601
             md5ctx = cl_hash_init("md5");
5637 5602
             if (md5ctx) {
5638
-                cl_update_hash(md5ctx, (void *)hptr, exe_sections[i].rsz);
5603
+                cl_update_hash(md5ctx, (void *)hptr, peinfo->sections[i].rsz);
5639 5604
                 cl_finish_hash(md5ctx, hashes->sections[i].md5);
5640 5605
             }
5641 5606
         }
... ...
@@ -5644,7 +5638,6 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5644 5644
     /* After this point it's the caller's responsibility to free
5645 5645
      * hashes->sections. Also, in the case where we are just computing the
5646 5646
      * stats, we are finished */
5647
-    free(exe_sections);
5648 5647
 
5649 5648
     while (flags & CL_CHECKFP_PE_FLAG_AUTHENTICODE) {
5650 5649
 
... ...
@@ -5653,6 +5646,7 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5653 5653
         // specified in the PKCS7 structure).  We need to hash up to 4 regions
5654 5654
         regions = (struct cli_mapped_region *)cli_calloc(4, sizeof(struct cli_mapped_region));
5655 5655
         if (!regions) {
5656
+            cli_exe_info_destroy(peinfo);
5656 5657
             return CL_EMEM;
5657 5658
         }
5658 5659
         nregions = 0;
... ...
@@ -5671,24 +5665,26 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5671 5671
 
5672 5672
         /* MZ to checksum */
5673 5673
         at   = 0;
5674
-        hlen = e_lfanew + sizeof(struct pe_image_file_hdr) + (pe_plus ? offsetof(struct pe_image_optional_hdr64, CheckSum) : offsetof(struct pe_image_optional_hdr32, CheckSum));
5674
+        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));
5675 5675
         add_chunk_to_hash_list(0, hlen);
5676 5676
         at = hlen + 4;
5677 5677
 
5678 5678
         /* Checksum to security */
5679
-        if (pe_plus)
5680
-            hlen = offsetof(struct pe_image_optional_hdr64, DataDirectory[4]) - offsetof(struct pe_image_optional_hdr64, CheckSum) - 4;
5679
+        if (peinfo->is_pe32plus)
5680
+            hlen = sizeof(struct pe_image_optional_hdr64) - offsetof(struct pe_image_optional_hdr64, CheckSum) - 4;
5681 5681
         else
5682
-            hlen = offsetof(struct pe_image_optional_hdr32, DataDirectory[4]) - offsetof(struct pe_image_optional_hdr32, CheckSum) - 4;
5682
+            hlen = sizeof(struct pe_image_optional_hdr32) - offsetof(struct pe_image_optional_hdr32, CheckSum) - 4;
5683
+
5684
+        hlen += sizeof(struct pe_image_data_dir) * 4;
5683 5685
         add_chunk_to_hash_list(at, hlen);
5684 5686
         at += hlen + 8;
5685 5687
 
5686
-        if (at > hdr_size) {
5688
+        if (at > peinfo->hdr_size) {
5687 5689
             break;
5688 5690
         }
5689 5691
 
5690 5692
         /* Security to End of header */
5691
-        hlen = hdr_size - at;
5693
+        hlen = peinfo->hdr_size - at;
5692 5694
         add_chunk_to_hash_list(at, hlen);
5693 5695
         at += hlen;
5694 5696
 
... ...
@@ -5815,27 +5811,16 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags)
5815 5815
     if (NULL != regions) {
5816 5816
         free(regions);
5817 5817
     }
5818
+
5819
+    cli_exe_info_destroy(peinfo);
5818 5820
     return ret;
5819 5821
 }
5820 5822
 
5821 5823
 int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type)
5822 5824
 {
5823
-    uint16_t e_magic; /* DOS signature ("MZ") */
5824
-    uint16_t nsections;
5825
-    uint32_t e_lfanew; /* address of new exe header */
5826
-    union {
5827
-        struct pe_image_optional_hdr64 opt64;
5828
-        struct pe_image_optional_hdr32 opt32;
5829
-    } pe_opt;
5830
-    const struct pe_image_section_hdr *section_hdr;
5831
-    ssize_t at;
5832
-    unsigned int i, pe_plus = 0;
5833
-    size_t fsize;
5834
-    uint32_t valign, falign, hdr_size;
5835
-    struct pe_image_file_hdr file_hdr;
5836
-    struct cli_exe_section *exe_sections;
5837
-    struct pe_image_data_dir *dirs;
5838
-    fmap_t *map = *ctx->fmap;
5825
+    unsigned int i;
5826
+    struct cli_exe_info _peinfo;
5827
+    struct cli_exe_info *peinfo = &_peinfo;
5839 5828
 
5840 5829
     unsigned char *hash, *hashset[CLI_HASH_AVAIL_TYPES];
5841 5830
     int genhash[CLI_HASH_AVAIL_TYPES];
... ...
@@ -5844,112 +5829,13 @@ int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type)
5844 5844
     if (class >= CL_GENHASH_PE_CLASS_LAST)
5845 5845
         return CL_EARG;
5846 5846
 
5847
-    if (fmap_readn(map, &e_magic, 0, sizeof(e_magic)) != sizeof(e_magic))
5848
-        return CL_EFORMAT;
5849
-
5850
-    if (EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE && EC16(e_magic) != PE_IMAGE_DOS_SIGNATURE_OLD)
5851
-        return CL_EFORMAT;
5852
-
5853
-    if (fmap_readn(map, &e_lfanew, 58 + sizeof(e_magic), sizeof(e_lfanew)) != sizeof(e_lfanew))
5854
-        return CL_EFORMAT;
5855
-
5856
-    e_lfanew = EC32(e_lfanew);
5857
-    if (!e_lfanew)
5858
-        return CL_EFORMAT;
5859
-
5860
-    if (fmap_readn(map, &file_hdr, e_lfanew, sizeof(struct pe_image_file_hdr)) != sizeof(struct pe_image_file_hdr))
5861
-        return CL_EFORMAT;
5862
-
5863
-    if (EC32(file_hdr.Magic) != PE_IMAGE_NT_SIGNATURE)
5864
-        return CL_EFORMAT;
5865
-
5866
-    nsections = EC16(file_hdr.NumberOfSections);
5867
-    if (nsections < 1 || nsections > PE_MAXSECTIONS)
5868
-        return CL_EFORMAT;
5869
-
5870
-    if (EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32))
5871
-        return CL_EFORMAT;
5872
-
5873
-    at = e_lfanew + sizeof(struct pe_image_file_hdr);
5874
-    if (fmap_readn(map, &optional_hdr32, at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32))
5875
-        return CL_EFORMAT;
5876
-
5877
-    at += sizeof(struct pe_image_optional_hdr32);
5878
-
5879
-    /* This will be a chicken and egg problem until we drop 9x */
5880
-    if (EC16(optional_hdr64.Magic) == PE32P_SIGNATURE) {
5881
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr64))
5882
-            return CL_EFORMAT;
5883
-
5884
-        pe_plus = 1;
5885
-    }
5886
-
5887
-    if (!pe_plus) { /* PE */
5888
-        if (EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr32)) {
5889
-            /* Seek to the end of the long header */
5890
-            at += EC16(file_hdr.SizeOfOptionalHeader) - sizeof(struct pe_image_optional_hdr32);
5891
-        }
5892
-
5893
-        hdr_size = EC32(optional_hdr32.SizeOfHeaders);
5894
-        dirs     = optional_hdr32.DataDirectory;
5895
-    } else { /* PE+ */
5896
-        size_t readlen = sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5897
-        /* read the remaining part of the header */
5898
-        if ((size_t)fmap_readn(map, &optional_hdr32 + 1, at, readlen) != readlen)
5899
-            return CL_EFORMAT;
5900
-
5901
-        at += sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32);
5902
-        hdr_size = EC32(optional_hdr64.SizeOfHeaders);
5903
-        dirs     = optional_hdr64.DataDirectory;
5904
-    }
5905
-
5906
-    fsize = map->len;
5907
-
5908
-    valign = (pe_plus) ? EC32(optional_hdr64.SectionAlignment) : EC32(optional_hdr32.SectionAlignment);
5909
-    falign = (pe_plus) ? EC32(optional_hdr64.FileAlignment) : EC32(optional_hdr32.FileAlignment);
5847
+    cli_exe_info_init(peinfo, 0);
5910 5848
 
5911
-    section_hdr = fmap_need_off_once(map, at, sizeof(*section_hdr) * nsections);
5912
-    if (!section_hdr)
5849
+    if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
5913 5850
         return CL_EFORMAT;
5914
-
5915
-    at += sizeof(*section_hdr) * nsections;
5916
-
5917
-    exe_sections = (struct cli_exe_section *)cli_calloc(nsections, sizeof(struct cli_exe_section));
5918
-    if (!exe_sections)
5919
-        return CL_EMEM;
5920
-
5921
-    for (i = 0; falign != 0x200 && i < nsections; i++) {
5922
-        /* file alignment fallback mode - blah */
5923
-        if (falign && section_hdr[i].SizeOfRawData && EC32(section_hdr[i].PointerToRawData) % falign && !(EC32(section_hdr[i].PointerToRawData) % 0x200))
5924
-            falign = 0x200;
5925
-    }
5926
-
5927
-    hdr_size = PESALIGN(hdr_size, falign); /* Aligned headers virtual size */
5928
-
5929
-    for (i = 0; i < nsections; i++) {
5930
-        exe_sections[i].rva = PEALIGN(EC32(section_hdr[i].VirtualAddress), valign);
5931
-        exe_sections[i].vsz = PESALIGN(EC32(section_hdr[i].VirtualSize), valign);
5932
-        exe_sections[i].raw = PEALIGN(EC32(section_hdr[i].PointerToRawData), falign);
5933
-        exe_sections[i].rsz = PESALIGN(EC32(section_hdr[i].SizeOfRawData), falign);
5934
-
5935
-        if (!exe_sections[i].vsz && exe_sections[i].rsz)
5936
-            exe_sections[i].vsz = PESALIGN(exe_sections[i].ursz, valign);
5937
-
5938
-        if (exe_sections[i].rsz && fsize > exe_sections[i].raw && !CLI_ISCONTAINED(0, (uint32_t)fsize, exe_sections[i].raw, exe_sections[i].rsz))
5939
-            exe_sections[i].rsz = fsize - exe_sections[i].raw;
5940
-
5941
-        if (exe_sections[i].rsz && exe_sections[i].raw >= fsize) {
5942
-            free(exe_sections);
5943
-            return CL_EFORMAT;
5944
-        }
5945
-
5946
-        if (exe_sections[i].urva >> 31 || exe_sections[i].uvsz >> 31 || (exe_sections[i].rsz && exe_sections[i].uraw >> 31) || exe_sections[i].ursz >> 31) {
5947
-            free(exe_sections);
5948
-            return CL_EFORMAT;
5949
-        }
5950 5851
     }
5951 5852
 
5952
-    cli_qsort(exe_sections, nsections, sizeof(*exe_sections), sort_sects);
5853
+    cli_qsort(peinfo->sections, peinfo->nsections, sizeof(*(peinfo->sections)), sort_sects);
5953 5854
 
5954 5855
     /* pick hashtypes to generate */
5955 5856
     memset(genhash, 0, sizeof(genhash));
... ...
@@ -5974,18 +5860,18 @@ int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type)
5974 5974
 
5975 5975
     if (!hash) {
5976 5976
         cli_errmsg("cli_genhash_pe: cli_malloc failed!\n");
5977
-        free(exe_sections);
5977
+        cli_exe_info_destroy(peinfo);
5978 5978
         return CL_EMEM;
5979 5979
     }
5980 5980
 
5981 5981
     if (class == CL_GENHASH_PE_CLASS_SECTION) {
5982 5982
         char *dstr = NULL;
5983 5983
 
5984
-        for (i = 0; i < nsections; i++) {
5984
+        for (i = 0; i < peinfo->nsections; i++) {
5985 5985
             /* Generate hashes */
5986
-            if (cli_hashsect(*ctx->fmap, &exe_sections[i], hashset, genhash, genhash) == 1) {
5986
+            if (cli_hashsect(*ctx->fmap, &peinfo->sections[i], hashset, genhash, genhash) == 1) {
5987 5987
                 dstr = cli_str2hex((char *)hash, hlen);
5988
-                cli_dbgmsg("Section{%u}: %u:%s\n", i, exe_sections[i].rsz, dstr ? (char *)dstr : "(NULL)");
5988
+                cli_dbgmsg("Section{%u}: %u:%s\n", i, peinfo->sections[i].rsz, dstr ? (char *)dstr : "(NULL)");
5989 5989
                 if (dstr != NULL) {
5990 5990
                     free(dstr);
5991 5991
                     dstr = NULL;
... ...
@@ -6000,7 +5886,7 @@ int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type)
6000 6000
         int ret;
6001 6001
 
6002 6002
         /* Generate hash */
6003
-        ret = hash_imptbl(ctx, hashset, &impsz, genhash, &dirs[1], exe_sections, nsections, hdr_size, pe_plus);
6003
+        ret = hash_imptbl(ctx, hashset, &impsz, genhash, peinfo);
6004 6004
         if (ret == CL_SUCCESS) {
6005 6005
             dstr = cli_str2hex((char *)hash, hlen);
6006 6006
             cli_dbgmsg("Imphash: %s:%u\n", dstr ? (char *)dstr : "(NULL)", impsz);
... ...
@@ -6017,6 +5903,6 @@ int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type)
6017 6017
 
6018 6018
     if (hash)
6019 6019
         free(hash);
6020
-    free(exe_sections);
6020
+    cli_exe_info_destroy(peinfo);
6021 6021
     return CL_SUCCESS;
6022 6022
 }
... ...
@@ -26,136 +26,29 @@
26 26
 #define __PE_H
27 27
 
28 28
 #include "clamav.h"
29
-#include "execs.h"
30 29
 #include "others.h"
31 30
 #include "fmap.h"
32 31
 #include "bcfeatures.h"
33
-/** @file */
34
-/** Header for this PE file
35
-  \group_pe */
36
-struct pe_image_file_hdr {
37
-    uint32_t Magic;                /**< PE magic header: PE\\0\\0 */
38
-    uint16_t Machine;              /**< CPU this executable runs on, see libclamav/pe.c for possible values */
39
-    uint16_t NumberOfSections;     /**< Number of sections in this executable */
40
-    uint32_t TimeDateStamp;        /**< Unreliable */
41
-    uint32_t PointerToSymbolTable; /**< debug */
42
-    uint32_t NumberOfSymbols;      /**< debug */
43
-    uint16_t SizeOfOptionalHeader; /**< == 224 */
44
-    uint16_t Characteristics;
45
-};
46
-
47
-/** PE data directory header
48
-  \group_pe */
49
-struct pe_image_data_dir {
50
-    uint32_t VirtualAddress;
51
-    uint32_t Size;
52
-};
53
-
54
-/** 32-bit PE optional header
55
-  \group_pe */
56
-struct pe_image_optional_hdr32 {
57
-    uint16_t Magic;
58
-    uint8_t MajorLinkerVersion;       /**< unreliable */
59
-    uint8_t MinorLinkerVersion;       /**< unreliable */
60
-    uint32_t SizeOfCode;              /**< unreliable */
61
-    uint32_t SizeOfInitializedData;   /**< unreliable */
62
-    uint32_t SizeOfUninitializedData; /**< unreliable */
63
-    uint32_t AddressOfEntryPoint;
64
-    uint32_t BaseOfCode;
65
-    uint32_t BaseOfData;
66
-    uint32_t ImageBase;                   /**< multiple of 64 KB */
67
-    uint32_t SectionAlignment;            /**< usually 32 or 4096 */
68
-    uint32_t FileAlignment;               /**< usually 32 or 512 */
69
-    uint16_t MajorOperatingSystemVersion; /**< not used */
70
-    uint16_t MinorOperatingSystemVersion; /**< not used */
71
-    uint16_t MajorImageVersion;           /**< unreliable */
72
-    uint16_t MinorImageVersion;           /**< unreliable */
73
-    uint16_t MajorSubsystemVersion;
74
-    uint16_t MinorSubsystemVersion;
75
-    uint32_t Win32VersionValue; /*< ? */
76
-    uint32_t SizeOfImage;
77
-    uint32_t SizeOfHeaders;
78
-    uint32_t CheckSum; /**< NT drivers only */
79
-    uint16_t Subsystem;
80
-    uint16_t DllCharacteristics;
81
-    uint32_t SizeOfStackReserve;
82
-    uint32_t SizeOfStackCommit;
83
-    uint32_t SizeOfHeapReserve;
84
-    uint32_t SizeOfHeapCommit;
85
-    uint32_t LoaderFlags;         /*< ? */
86
-    uint32_t NumberOfRvaAndSizes; /**< unreliable */
87
-    struct pe_image_data_dir DataDirectory[16];
88
-};
89
-
90
-/** PE 64-bit optional header
91
-  \group_pe */
92
-struct pe_image_optional_hdr64 {
93
-    uint16_t Magic;
94
-    uint8_t MajorLinkerVersion;       /**< unreliable */
95
-    uint8_t MinorLinkerVersion;       /**< unreliable */
96
-    uint32_t SizeOfCode;              /**< unreliable */
97
-    uint32_t SizeOfInitializedData;   /**< unreliable */
98
-    uint32_t SizeOfUninitializedData; /**< unreliable */
99
-    uint32_t AddressOfEntryPoint;
100
-    uint32_t BaseOfCode;
101
-    uint64_t ImageBase;                   /**< multiple of 64 KB */
102
-    uint32_t SectionAlignment;            /**< usually 32 or 4096 */
103
-    uint32_t FileAlignment;               /**< usually 32 or 512 */
104
-    uint16_t MajorOperatingSystemVersion; /**< not used */
105
-    uint16_t MinorOperatingSystemVersion; /**< not used */
106
-    uint16_t MajorImageVersion;           /**< unreliable */
107
-    uint16_t MinorImageVersion;           /**< unreliable */
108
-    uint16_t MajorSubsystemVersion;
109
-    uint16_t MinorSubsystemVersion;
110
-    uint32_t Win32VersionValue; /* ? */
111
-    uint32_t SizeOfImage;
112
-    uint32_t SizeOfHeaders;
113
-    uint32_t CheckSum; /**< NT drivers only */
114
-    uint16_t Subsystem;
115
-    uint16_t DllCharacteristics;
116
-    uint64_t SizeOfStackReserve;
117
-    uint64_t SizeOfStackCommit;
118
-    uint64_t SizeOfHeapReserve;
119
-    uint64_t SizeOfHeapCommit;
120
-    uint32_t LoaderFlags;         /* ? */
121
-    uint32_t NumberOfRvaAndSizes; /**< unreliable */
122
-    struct pe_image_data_dir DataDirectory[16];
123
-};
124
-
125
-/** PE section header
126
-  \group_pe */
127
-struct pe_image_section_hdr {
128
-    uint8_t Name[8]; /**< may not end with NULL */
129
-    /*
130
-    union {
131
-	uint32_t PhysicalAddress;
132
-	uint32_t VirtualSize;
133
-    } AddrSize;
134
-    */
135
-    uint32_t VirtualSize;
136
-    uint32_t VirtualAddress;
137
-    uint32_t SizeOfRawData;        /**< multiple of FileAlignment */
138
-    uint32_t PointerToRawData;     /**< offset to the section's data */
139
-    uint32_t PointerToRelocations; /**< object files only */
140
-    uint32_t PointerToLinenumbers; /**< object files only */
141
-    uint16_t NumberOfRelocations;  /**< object files only */
142
-    uint16_t NumberOfLinenumbers;  /**< object files only */
143
-    uint32_t Characteristics;
144
-};
145
-
146
-#define WIN_CERT_REV_2 0x0200
147
-#define WIN_CERT_TYPE_PKCS7 0x0002
148
-
149
-/** PE authenticode data header
150
-  \group_pe */
151
-struct pe_certificate_hdr {
152
-    uint32_t length; /** length of the certificate data, including the header */
153
-    uint16_t revision;
154
-    uint16_t type;
155
-};
32
+#include "pe_structs.h"
33
+#include "execs.h"
156 34
 
157 35
 /** Data for the bytecode PE hook
158
-  \group_pe */
36
+  \group_pe
37
+ *
38
+ *  NOTE: This structure must stay in-sync with the ones defined within the
39
+ *  clamav-bytecode-compiler source at:
40
+ *  - clang/lib/Headers/bytecode_pe.h
41
+ *  - llvm/tools/clang/lib/Headers/bytecode_pe.h
42
+ *  We allocate space for this, populate the values via cli_peheader, and pass
43
+ *  it to the bytecode sig runtime for use.
44
+ *
45
+ *  TODO Next time we are making changes to the clamav-bytecode-compiler
46
+ *  source, update pe_image_optional_hdr32 and pe_image_optional_hdr64 to
47
+ *  remove DataDirectory from both (like with the definitions here).  Then,
48
+ *  remove opt32_dirs and opt64_dirs below.  There's no need to have these
49
+ *  bytes in 3 places!  Also, consider using a union to hold opt32 and opt64,
50
+ *  since you never need more than one at a time.
51
+ */
159 52
 struct cli_pe_hook_data {
160 53
     uint32_t offset;
161 54
     uint32_t ep;                          /**< EntryPoint as file offset */
... ...
@@ -163,13 +56,18 @@ struct cli_pe_hook_data {
163 163
     uint16_t dummy;                       /* align */
164 164
     struct pe_image_file_hdr file_hdr;    /**< Header for this PE file */
165 165
     struct pe_image_optional_hdr32 opt32; /**< 32-bit PE optional header */
166
-    uint32_t dummy2;                      /* align */
167
-    struct pe_image_optional_hdr64 opt64; /**< 64-bit PE optional header */
168
-    struct pe_image_data_dir dirs[16];    /**< PE data directory header */
169
-    uint32_t e_lfanew;                    /**< address of new exe header */
170
-    uint32_t overlays;                    /**< number of overlays */
171
-    int32_t overlays_sz;                  /**< size of overlays */
172
-    uint32_t hdr_size;                    /**< internally needed by rawaddr */
166
+    /** Our opt32 no longer includes DataDirectory[16], but the one in the
167
+   * bytecode compiler source still does.  Add this here as a placeholder (and
168
+   * it gets used, so we need to populate it also */
169
+    struct pe_image_data_dir opt32_dirs[16];
170
+    uint32_t dummy2;                         /* align */
171
+    struct pe_image_optional_hdr64 opt64;    /**< 64-bit PE optional header */
172
+    struct pe_image_data_dir opt64_dirs[16]; /** See note about opt32_dirs */
173
+    struct pe_image_data_dir dirs[16];       /**< PE data directory header */
174
+    uint32_t e_lfanew;                       /**< address of new exe header */
175
+    uint32_t overlays;                       /**< number of overlays */
176
+    int32_t overlays_sz;                     /**< size of overlays */
177
+    uint32_t hdr_size;                       /**< internally needed by rawaddr */
173 178
 };
174 179
 
175 180
 int cli_scanpe(cli_ctx *ctx);
... ...
@@ -185,11 +83,25 @@ enum {
185 185
     CL_GENHASH_PE_CLASS_LAST
186 186
 };
187 187
 
188
-int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo);
188
+// For info about these, see the cli_peheader definition in pe.c
189
+#define CLI_PEHEADER_OPT_NONE 0x0
190
+#define CLI_PEHEADER_OPT_COLLECT_JSON 0x1
191
+#define CLI_PEHEADER_OPT_DBG_PRINT_INFO 0x2
192
+#define CLI_PEHEADER_OPT_EXTRACT_VINFO 0x4
193
+#define CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS 0x8
194
+
195
+#define CLI_PEHEADER_RET_SUCCESS 0
196
+#define CLI_PEHEADER_RET_GENERIC_ERROR -1
197
+#define CLI_PEHEADER_RET_BROKEN_PE -2
198
+#define CLI_PEHEADER_RET_JSON_TIMEOUT -3
199
+
200
+int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo);
201
+int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx);
202
+
189 203
 cl_error_t cli_checkfp_pe(cli_ctx *ctx, stats_section_t *hashes, uint32_t flags);
190 204
 int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type);
191 205
 
192 206
 uint32_t cli_rawaddr(uint32_t, const struct cli_exe_section *, uint16_t, unsigned int *, size_t, uint32_t);
193
-void findres(uint32_t, uint32_t, uint32_t, fmap_t *map, struct cli_exe_section *, uint16_t, uint32_t, int (*)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *);
207
+void findres(uint32_t, uint32_t, fmap_t *map, struct cli_exe_info *, int (*)(void *, uint32_t, uint32_t, uint32_t, uint32_t), void *);
194 208
 
195 209
 #endif
... ...
@@ -49,10 +49,7 @@ struct ICON_ENV {
49 49
     int result;
50 50
 
51 51
     icon_groupset *set;
52
-    uint32_t resdir_rva;
53
-    struct cli_exe_section *exe_sections;
54
-    uint16_t nsections;
55
-    uint32_t hdr_size;
52
+    struct cli_exe_info *peinfo;
56 53
 
57 54
     uint32_t icnt; /* number of icon entries parsed, declared images */
58 55
     uint32_t max_icons;
... ...
@@ -110,7 +107,7 @@ static int icon_scan_cb(void *ptr, uint32_t type, uint32_t name, uint32_t lang,
110 110
     return 0;
111 111
 }
112 112
 
113
-int cli_scanicon(icon_groupset *set, uint32_t resdir_rva, cli_ctx *ctx, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size)
113
+int cli_scanicon(icon_groupset *set, cli_ctx *ctx, struct cli_exe_info *peinfo)
114 114
 {
115 115
     struct ICON_ENV icon_env;
116 116
     fmap_t *map        = *ctx->fmap;
... ...
@@ -124,10 +121,7 @@ int cli_scanicon(icon_groupset *set, uint32_t resdir_rva, cli_ctx *ctx, struct c
124 124
     icon_env.result = CL_CLEAN;
125 125
 
126 126
     icon_env.set          = set;
127
-    icon_env.resdir_rva   = resdir_rva;
128
-    icon_env.exe_sections = exe_sections;
129
-    icon_env.nsections    = nsections;
130
-    icon_env.hdr_size     = hdr_size;
127
+    icon_env.peinfo = peinfo;
131 128
 
132 129
     icon_env.max_icons = ctx->engine->maxiconspe;
133 130
 
... ...
@@ -138,7 +132,7 @@ int cli_scanicon(icon_groupset *set, uint32_t resdir_rva, cli_ctx *ctx, struct c
138 138
     icon_env.err_insl  = 0;
139 139
 
140 140
     /* icon group scan callback --> groupicon_scan_cb() */
141
-    findres(14, 0xffffffff, resdir_rva, map, exe_sections, nsections, hdr_size, groupicon_scan_cb, &icon_env);
141
+    findres(14, 0xffffffff, map, peinfo, groupicon_scan_cb, &icon_env);
142 142
 
143 143
     /* CL_EMAXSIZE is used to track the icon limit */
144 144
     if (icon_env.result == CL_EMAXSIZE)
... ...
@@ -173,15 +167,12 @@ int cli_scanicon(icon_groupset *set, uint32_t resdir_rva, cli_ctx *ctx, struct c
173 173
 int cli_groupiconscan(struct ICON_ENV *icon_env, uint32_t rva)
174 174
 {
175 175
     /* import environment */
176
-    uint32_t resdir_rva                  = icon_env->resdir_rva;
177 176
     cli_ctx *ctx                         = icon_env->ctx;
178
-    struct cli_exe_section *exe_sections = icon_env->exe_sections;
179
-    uint16_t nsections                   = icon_env->nsections;
180
-    uint32_t hdr_size                    = icon_env->hdr_size;
177
+    struct cli_exe_info *peinfo = icon_env->peinfo;
181 178
 
182 179
     int err            = 0;
183 180
     fmap_t *map        = *ctx->fmap;
184
-    const uint8_t *grp = fmap_need_off_once(map, cli_rawaddr(rva, exe_sections, nsections, (unsigned int *)(&err), map->len, hdr_size), 16);
181
+    const uint8_t *grp = fmap_need_off_once(map, cli_rawaddr(rva, peinfo->sections, peinfo->nsections, (unsigned int *)(&err), map->len, peinfo->hdr_size), 16);
185 182
 
186 183
     if (grp && !err) {
187 184
         uint32_t gsz = cli_readint32(grp + 4);
... ...
@@ -199,7 +190,7 @@ int cli_groupiconscan(struct ICON_ENV *icon_env, uint32_t rva)
199 199
                 uint16_t id;
200 200
             } * dir;
201 201
 
202
-            raddr = cli_rawaddr(cli_readint32(grp), exe_sections, nsections, (unsigned int *)(&err), map->len, hdr_size);
202
+            raddr = cli_rawaddr(cli_readint32(grp), peinfo->sections, peinfo->nsections, (unsigned int *)(&err), map->len, peinfo->hdr_size);
203 203
             cli_dbgmsg("cli_scanicon: icon group @%x\n", raddr);
204 204
             grp = fmap_need_off_once(map, raddr, gsz);
205 205
             if (grp && !err) {
... ...
@@ -215,7 +206,7 @@ int cli_groupiconscan(struct ICON_ENV *icon_env, uint32_t rva)
215 215
                     cli_dbgmsg("cli_scanicon: Icongrp @%x - %ux%ux%u - (id=%x, rsvd=%u, planes=%u, palcnt=%u, sz=%x)\n", rva, dir->w, dir->h, cli_readint16(&dir->depth), cli_readint16(&dir->id), cli_readint16(&dir->planes), dir->palcnt, dir->rsvd, cli_readint32(&dir->sz));
216 216
 
217 217
                     /* icon scan callback --> icon_scan_cb() */
218
-                    findres(3, cli_readint16(&dir->id), resdir_rva, map, exe_sections, nsections, hdr_size, icon_scan_cb, icon_env);
218
+                    findres(3, cli_readint16(&dir->id), map, peinfo, icon_scan_cb, icon_env);
219 219
                     if (icon_env->result != CL_CLEAN)
220 220
                         return icon_env->result;
221 221
 
... ...
@@ -1345,9 +1336,7 @@ static int parseicon(struct ICON_ENV *icon_env, uint32_t rva)
1345 1345
 {
1346 1346
     icon_groupset *set                   = icon_env->set;
1347 1347
     cli_ctx *ctx                         = icon_env->ctx;
1348
-    struct cli_exe_section *exe_sections = icon_env->exe_sections;
1349
-    uint16_t nsections                   = icon_env->nsections;
1350
-    uint32_t hdr_size                    = icon_env->hdr_size;
1348
+    struct cli_exe_info *peinfo = icon_env->peinfo;
1351 1349
 
1352 1350
     struct
1353 1351
     {
... ...
@@ -1381,7 +1370,7 @@ static int parseicon(struct ICON_ENV *icon_env, uint32_t rva)
1381 1381
         return CL_SUCCESS;
1382 1382
     map   = *ctx->fmap;
1383 1383
     tempd = (cli_debug_flag && ctx->engine->keeptmp) ? (ctx->engine->tmpdir ? ctx->engine->tmpdir : cli_gettmpdir()) : NULL;
1384
-    icoff = cli_rawaddr(rva, exe_sections, nsections, &err, map->len, hdr_size);
1384
+    icoff = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, map->len, peinfo->hdr_size);
1385 1385
 
1386 1386
     /* read the bitmap header */
1387 1387
     if (err || !(rawimage = fmap_need_off_once(map, icoff, 4))) {
... ...
@@ -1391,7 +1380,7 @@ static int parseicon(struct ICON_ENV *icon_env, uint32_t rva)
1391 1391
     }
1392 1392
 
1393 1393
     rva   = cli_readint32(rawimage);
1394
-    icoff = cli_rawaddr(rva, exe_sections, nsections, &err, map->len, hdr_size);
1394
+    icoff = cli_rawaddr(rva, peinfo->sections, peinfo->nsections, &err, map->len, peinfo->hdr_size);
1395 1395
     if (err || fmap_readn(map, &bmphdr, icoff, sizeof(bmphdr)) != sizeof(bmphdr)) {
1396 1396
         icon_env->err_bhoof++;
1397 1397
         //cli_dbgmsg("parseicon: bmp header is out of file\n");
... ...
@@ -23,7 +23,7 @@
23 23
 #define __PE_ICONS_H
24 24
 #include "pe.h"
25 25
 
26
-int cli_scanicon(icon_groupset *set, uint32_t resdir_rva, cli_ctx *ctx, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size);
26
+int cli_scanicon(icon_groupset *set, cli_ctx *ctx, struct cli_exe_info *peinfo);
27 27
 
28 28
 void cli_icongroupset_add(const char *groupname, icon_groupset *set, unsigned int type, cli_ctx *ctx);
29 29
 static inline void cli_icongroupset_init(icon_groupset *set)
30 30
new file mode 100644
... ...
@@ -0,0 +1,154 @@
0
+/*
1
+ *  Copyright (C) 2015, 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2
+ *  Copyright (C) 2007-2008 Sourcefire, Inc.
3
+ *
4
+ *  Authors: Alberto Wu, Tomasz Kojm, Andrew Williams
5
+ * 
6
+ *  Acknowledgements: The header structures were based upon a PE format 
7
+ *                    analysis by B. Luevelsmeyer.
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2 as
11
+ *  published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License
19
+ *  along with this program; if not, write to the Free Software
20
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21
+ *  MA 02110-1301, USA.
22
+ */
23
+
24
+#ifndef __PE_STRUCTS_H
25
+#define __PE_STRUCTS_H
26
+
27
+#include "clamav.h"
28
+
29
+/** @file */
30
+/** Header for this PE file
31
+  \group_pe */
32
+struct pe_image_file_hdr {
33
+    uint32_t Magic;                /**< PE magic header: PE\\0\\0 */
34
+    uint16_t Machine;              /**< CPU this executable runs on, see libclamav/pe.c for possible values */
35
+    uint16_t NumberOfSections;     /**< Number of sections in this executable */
36
+    uint32_t TimeDateStamp;        /**< Unreliable */
37
+    uint32_t PointerToSymbolTable; /**< debug */
38
+    uint32_t NumberOfSymbols;      /**< debug */
39
+    uint16_t SizeOfOptionalHeader; /**< == 224 */
40
+    uint16_t Characteristics;
41
+};
42
+
43
+/** PE data directory header
44
+  \group_pe */
45
+struct pe_image_data_dir {
46
+    uint32_t VirtualAddress;
47
+    uint32_t Size;
48
+};
49
+
50
+/** 32-bit PE optional header
51
+  \group_pe */
52
+struct pe_image_optional_hdr32 {
53
+    uint16_t Magic;
54
+    uint8_t MajorLinkerVersion;       /**< unreliable */
55
+    uint8_t MinorLinkerVersion;       /**< unreliable */
56
+    uint32_t SizeOfCode;              /**< unreliable */
57
+    uint32_t SizeOfInitializedData;   /**< unreliable */
58
+    uint32_t SizeOfUninitializedData; /**< unreliable */
59
+    uint32_t AddressOfEntryPoint;
60
+    uint32_t BaseOfCode;
61
+    uint32_t BaseOfData;
62
+    uint32_t ImageBase;                   /**< multiple of 64 KB */
63
+    uint32_t SectionAlignment;            /**< usually 32 or 4096 */
64
+    uint32_t FileAlignment;               /**< usually 32 or 512 */
65
+    uint16_t MajorOperatingSystemVersion; /**< not used */
66
+    uint16_t MinorOperatingSystemVersion; /**< not used */
67
+    uint16_t MajorImageVersion;           /**< unreliable */
68
+    uint16_t MinorImageVersion;           /**< unreliable */
69
+    uint16_t MajorSubsystemVersion;
70
+    uint16_t MinorSubsystemVersion;
71
+    uint32_t Win32VersionValue; /*< ? */
72
+    uint32_t SizeOfImage;
73
+    uint32_t SizeOfHeaders;
74
+    uint32_t CheckSum; /**< NT drivers only */
75
+    uint16_t Subsystem;
76
+    uint16_t DllCharacteristics;
77
+    uint32_t SizeOfStackReserve;
78
+    uint32_t SizeOfStackCommit;
79
+    uint32_t SizeOfHeapReserve;
80
+    uint32_t SizeOfHeapCommit;
81
+    uint32_t LoaderFlags; /*< ? */
82
+    uint32_t NumberOfRvaAndSizes;
83
+    //struct pe_image_data_dir DataDirectory[16];
84
+};
85
+
86
+/** PE 64-bit optional header
87
+  \group_pe */
88
+struct pe_image_optional_hdr64 {
89
+    uint16_t Magic;
90
+    uint8_t MajorLinkerVersion;       /**< unreliable */
91
+    uint8_t MinorLinkerVersion;       /**< unreliable */
92
+    uint32_t SizeOfCode;              /**< unreliable */
93
+    uint32_t SizeOfInitializedData;   /**< unreliable */
94
+    uint32_t SizeOfUninitializedData; /**< unreliable */
95
+    uint32_t AddressOfEntryPoint;
96
+    uint32_t BaseOfCode;
97
+    uint64_t ImageBase;                   /**< multiple of 64 KB */
98
+    uint32_t SectionAlignment;            /**< usually 32 or 4096 */
99
+    uint32_t FileAlignment;               /**< usually 32 or 512 */
100
+    uint16_t MajorOperatingSystemVersion; /**< not used */
101
+    uint16_t MinorOperatingSystemVersion; /**< not used */
102
+    uint16_t MajorImageVersion;           /**< unreliable */
103
+    uint16_t MinorImageVersion;           /**< unreliable */
104
+    uint16_t MajorSubsystemVersion;
105
+    uint16_t MinorSubsystemVersion;
106
+    uint32_t Win32VersionValue; /* ? */
107
+    uint32_t SizeOfImage;
108
+    uint32_t SizeOfHeaders;
109
+    uint32_t CheckSum; /**< NT drivers only */
110
+    uint16_t Subsystem;
111
+    uint16_t DllCharacteristics;
112
+    uint64_t SizeOfStackReserve;
113
+    uint64_t SizeOfStackCommit;
114
+    uint64_t SizeOfHeapReserve;
115
+    uint64_t SizeOfHeapCommit;
116
+    uint32_t LoaderFlags; /* ? */
117
+    uint32_t NumberOfRvaAndSizes;
118
+    //struct pe_image_data_dir DataDirectory[16];
119
+};
120
+
121
+/** PE section header
122
+  \group_pe */
123
+struct pe_image_section_hdr {
124
+    uint8_t Name[8]; /**< may not end with NULL */
125
+    /*
126
+    union {
127
+	uint32_t PhysicalAddress;
128
+	uint32_t VirtualSize;
129
+    } AddrSize;
130
+    */
131
+    uint32_t VirtualSize;
132
+    uint32_t VirtualAddress;
133
+    uint32_t SizeOfRawData;        /**< multiple of FileAlignment */
134
+    uint32_t PointerToRawData;     /**< offset to the section's data */
135
+    uint32_t PointerToRelocations; /**< object files only */
136
+    uint32_t PointerToLinenumbers; /**< object files only */
137
+    uint16_t NumberOfRelocations;  /**< object files only */
138
+    uint16_t NumberOfLinenumbers;  /**< object files only */
139
+    uint32_t Characteristics;
140
+};
141
+
142
+#define WIN_CERT_REV_2 0x0200
143
+#define WIN_CERT_TYPE_PKCS7 0x0002
144
+
145
+/** PE authenticode data header
146
+  \group_pe */
147
+struct pe_certificate_hdr {
148
+    uint32_t length; /** length of the certificate data, including the header */
149
+    uint16_t revision;
150
+    uint16_t type;
151
+};
152
+
153
+#endif
0 154
\ No newline at end of file
... ...
@@ -2513,7 +2513,7 @@ static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo,
2513 2513
             }
2514 2514
         }
2515 2515
 
2516
-        if ((mode == MD5_MDB) || strcmp(tokens[size_field], "*")) {
2516
+        if (strcmp(tokens[size_field], "*")) {
2517 2517
             size = strtoul(tokens[size_field], (char **)&pt, 10);
2518 2518
             if (*pt || !size || size >= 0xffffffff) {
2519 2519
                 cli_errmsg("cli_loadhash: Invalid value for the size field\n");
... ...
@@ -2522,7 +2522,12 @@ static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo,
2522 2522
             }
2523 2523
         } else {
2524 2524
             size = 0;
2525
-            if ((tokens_count < MD5_TOKENS - 1) || (req_fl < 73)) {
2525
+            // The wildcard feature was added in FLEVEL 73, so for backwards
2526
+            // compatibility with older clients, ensure that a minimum FLEVEL
2527
+            // is specified.  This check doesn't apply to .imp rules, though,
2528
+            // since this rule category wasn't introduced until FLEVEL 90, and
2529
+            // has always supported wildcard usage in rules.
2530
+            if (mode != MD5_IMP && ((tokens_count < MD5_TOKENS - 1) || (req_fl < 73))) {
2526 2531
                 cli_errmsg("cli_loadhash: Minimum FLEVEL field must be at least 73 for wildcard size hash signatures."
2527 2532
                            " For reference, running FLEVEL is %d\n",
2528 2533
                            cl_retflevel());
... ...
@@ -4600,7 +4605,16 @@ static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned
4600 4600
     while ((dent = readdir(dd))) {
4601 4601
 #endif
4602 4602
         if (dent->d_ino) {
4603
-            if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && strcmp(dent->d_name, "daily.cvd") && strcmp(dent->d_name, "daily.cld") && strcmp(dent->d_name, "daily.cfg") && CLI_DBEXT(dent->d_name)) {
4603
+            if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) {
4604
+                continue;
4605
+            }
4606
+
4607
+            /* Skip everything that's already been loaded in or ignored */
4608
+            if (cli_strbcasestr(dent->d_name, ".ign") || cli_strbcasestr(dent->d_name, ".ign2") || !strcmp(dent->d_name, "daily.cvd") || !strcmp(dent->d_name, "daily.cld") || !strcmp(dent->d_name, "local.gdb") || !strcmp(dent->d_name, "daily.cfg")) {
4609
+                continue;
4610
+            }
4611
+
4612
+            if (CLI_DBEXT(dent->d_name)) {
4604 4613
                 if ((options & CL_DB_OFFICIAL_ONLY) && !strstr(dirname, "clamav-") && !cli_strbcasestr(dent->d_name, ".cld") && !cli_strbcasestr(dent->d_name, ".cvd")) {
4605 4614
                     cli_dbgmsg("Skipping unofficial database %s\n", dent->d_name);
4606 4615
                     continue;
... ...
@@ -5286,13 +5300,30 @@ static int countsigs(const char *dbname, unsigned int options, unsigned int *sig
5286 5286
             *sigs += cvd->sigs;
5287 5287
             cl_cvdfree(cvd);
5288 5288
         }
5289
+    } else if ((cli_strbcasestr(dbname, ".cud"))) {
5290
+        if (options & CL_COUNTSIGS_UNOFFICIAL) {
5291
+            struct cl_cvd *cvd = cl_cvdhead(dbname);
5292
+            if (!cvd) {
5293
+                cli_errmsg("countsigs: Can't parse %s\n", dbname);
5294
+                return CL_ECVD;
5295
+            }
5296
+            *sigs += cvd->sigs;
5297
+            cl_cvdfree(cvd);
5298
+        }
5289 5299
     } else if (cli_strbcasestr(dbname, ".cbc")) {
5290 5300
         if (options & CL_COUNTSIGS_UNOFFICIAL)
5291 5301
             (*sigs)++;
5292 5302
 
5293
-    } else if (cli_strbcasestr(dbname, ".wdb") || cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".ftm") || cli_strbcasestr(dbname, ".cfg") || cli_strbcasestr(dbname, ".cat")) {
5294
-        /* ignore */
5303
+    } else if (cli_strbcasestr(dbname, ".wdb") || cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".sfp") || cli_strbcasestr(dbname, ".ign") || cli_strbcasestr(dbname, ".ign2") || cli_strbcasestr(dbname, ".ftm") || cli_strbcasestr(dbname, ".cfg") || cli_strbcasestr(dbname, ".cat")) {
5304
+        /* ignore whitelist/FP signatures and metadata files */
5305
+
5306
+        // TODO .crb sigs can contain both whitelist and blacklist signatures.
5307
+        // For now we will just include both in the count by not excluding this
5308
+        // sig type here, but in the future we could extract just the number of
5309
+        // blacklist rules manually so that the count is more accurate.
5295 5310
 
5311
+        // NOTE: We implicitly ignore .info files because they aren't currently
5312
+        // in the list of ones checked for by CLI_DBEXT
5296 5313
     } else if ((options & CL_COUNTSIGS_UNOFFICIAL) && CLI_DBEXT(dbname)) {
5297 5314
         return countentries(dbname, sigs);
5298 5315
     }
... ...
@@ -28,6 +28,13 @@
28 28
 #include "str.h"
29 29
 #include "cvd.h"
30 30
 
31
+// TODO Is it safe to remove .db2 and .db3 from here?  These strings aren't
32
+// referenced anywhere else in the source.
33
+
34
+// NOTE: We don't include .info in CLI_DBEXT because they are only used for
35
+// one specific purpose - verifying the contents of database container files.
36
+// This list is geared towards file extensions of files that users can provide
37
+// to ClamAV directly.
31 38
 #ifdef HAVE_YARA
32 39
 #define CLI_DBEXT(ext)                   \
33 40
     (                                    \
... ...
@@ -67,7 +74,10 @@
67 67
         cli_strbcasestr(ext, ".ioc") ||  \
68 68
         cli_strbcasestr(ext, ".yar") ||  \
69 69
         cli_strbcasestr(ext, ".yara") || \
70
-        cli_strbcasestr(ext, ".pwdb"))
70
+        cli_strbcasestr(ext, ".pwdb") || \
71
+        cli_strbcasestr(ext, ".ign") ||  \
72
+        cli_strbcasestr(ext, ".ign2") || \
73
+        cli_strbcasestr(ext, ".imp"))
71 74
 #else
72 75
 #define CLI_DBEXT(ext)                  \
73 76
     (                                   \
... ...
@@ -104,7 +114,11 @@
104 104
         cli_strbcasestr(ext, ".cat") || \
105 105
         cli_strbcasestr(ext, ".crb") || \
106 106
         cli_strbcasestr(ext, ".idb") || \
107
-        cli_strbcasestr(ext, ".ioc"))
107
+        cli_strbcasestr(ext, ".ioc") ||  \
108
+        cli_strbcasestr(ext, ".pwdb") || \
109
+        cli_strbcasestr(ext, ".ign") ||  \
110
+        cli_strbcasestr(ext, ".ign2") || \
111
+        cli_strbcasestr(ext, ".imp"))
108 112
 #endif
109 113
 
110 114
 char *cli_virname(const char *virname, unsigned int official);
... ...
@@ -109,6 +109,7 @@
109 109
 #include "tiff.h"
110 110
 #include "hwp.h"
111 111
 #include "msdoc.h"
112
+#include "execs.h"
112 113
 
113 114
 #ifdef HAVE_BZLIB_H
114 115
 #include <bzlib.h>
... ...
@@ -1528,6 +1529,9 @@ static int cli_scanscript(cli_ctx *ctx)
1528 1528
     troot     = ctx->engine->root[7];
1529 1529
     maxpatlen = troot ? troot->maxpatlen : 0;
1530 1530
 
1531
+    // Initialize info so it's safe to pass to destroy later
1532
+    cli_targetinfo_init(&info);
1533
+
1531 1534
     cli_dbgmsg("in cli_scanscript()\n");
1532 1535
 
1533 1536
     /* CL_ENGINE_MAX_SCRIPTNORMALIZE */
... ...
@@ -1657,6 +1661,7 @@ static int cli_scanscript(cli_ctx *ctx)
1657 1657
     }
1658 1658
 
1659 1659
 done:
1660
+    cli_targetinfo_destroy(&info);
1660 1661
     free(normalized);
1661 1662
     cli_ac_freedata(&tmdata);
1662 1663
     cli_ac_freedata(&gmdata);
... ...
@@ -2604,14 +2609,15 @@ static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_
2604 2604
                                 break;
2605 2605
                             }
2606 2606
                             cli_set_container(ctx, CL_TYPE_MSEXE, csize);
2607
-                            memset(&peinfo, 0, sizeof(struct cli_exe_info));
2608
-                            peinfo.offset = fpt->offset;
2609
-                            if (cli_peheader(map, &peinfo) == 0) {
2607
+
2608
+                            cli_exe_info_init(&peinfo, fpt->offset);
2609
+                            // TODO We could probably substitute in a quicker
2610
+                            // method of determining whether a PE file exists
2611
+                            // at this offset.
2612
+                            if (cli_peheader(map, &peinfo, CLI_PEHEADER_OPT_NONE, NULL) == 0) {
2610 2613
                                 cli_dbgmsg("*** Detected embedded PE file at %u ***\n",
2611 2614
                                            (unsigned int)fpt->offset);
2612
-                                if (peinfo.section)
2613
-                                    free(peinfo.section);
2614
-                                cli_hashset_destroy(&peinfo.vinfo);
2615
+                                cli_exe_info_destroy(&peinfo);
2615 2616
 
2616 2617
                                 nret       = cli_scanembpe(ctx, fpt->offset);
2617 2618
                                 break_loop = 1; /* we can stop here and other