1 /*
2 	pev - the PE file analyzer toolkit
3 
4 	pehash.c - calculate hashes of PE pieces
5 
6 	Copyright (C) 2012 - 2017 pev authors
7 
8 	This program is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 2 of the License, or
11 	(at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
20 
21     In addition, as a special exception, the copyright holders give
22     permission to link the code of portions of this program with the
23     OpenSSL library under certain conditions as described in each
24     individual source file, and distribute linked combinations
25     including the two.
26 
27     You must obey the GNU General Public License in all respects
28     for all of the code used other than OpenSSL.  If you modify
29     file(s) with this exception, you may extend this exception to your
30     version of the file(s), but you are not obligated to do so.  If you
31     do not wish to do so, delete this exception statement from your
32     version.  If you delete this exception statement from all source
33     files in the program, then also delete it here.
34 */
35 
36 #include "common.h"
37 #include <openssl/evp.h>
38 #include <openssl/md5.h>
39 #include "../lib/libfuzzy/fuzzy.h"
40 #include "plugins.h"
41 #include "utlist.h"
42 #include "utils.h"
43 #include "ordlookup.h"
44 
45 #define PROGRAM "pehash"
46 
47 #define IMPHASH_FLAVOR_MANDIANT 1
48 #define IMPHASH_FLAVOR_PEFILE 2
49 
50 unsigned pefile_warn = 0;
51 
52 typedef struct {
53 	bool all;
54 	bool content;
55 	struct {
56 		bool all;
57 		bool dos;
58 		bool coff;
59 		bool optional;
60 	} headers;
61 	struct {
62 		char *name;
63 		uint16_t index;
64 	} sections;
65 } options_t;
66 
67 /* By liw. */
last_strstr(const char * haystack,const char * needle)68 static char *last_strstr(const char *haystack, const char *needle)
69 {
70     if (*needle == '\0')
71         return (char *) haystack;
72 
73     char *result = NULL;
74     for (;;) {
75         char *p = strstr(haystack, needle);
76         if (p == NULL)
77             break;
78         result = p;
79         haystack = p + 1;
80     }
81 
82     return result;
83 }
84 
usage(void)85 static void usage(void)
86 {
87 	static char formats[255];
88 	output_available_formats(formats, sizeof(formats), '|');
89 	printf("Usage: %s OPTIONS FILE\n"
90 		"Calculate hashes of PE pieces\n"
91 		"\nExample: %s -s '.text' winzip.exe\n"
92 		"\nOptions:\n"
93 		" -f, --format <%s> change output format (default: text)\n"
94 		" -a, --all                             hash file, sections and headers with md5, sha1, sha256, ssdeep and imphash\n"
95 		" -c, --content                         hash only the file content (default)\n"
96 		" -h, --header <dos|coff|optional>      hash only the header with the specified name\n"
97 		" -s, --section <section_name>          hash only the section with the specified name\n"
98 		" --section-index <section_index>       hash only the section at the specified index (1..n)\n"
99 		" -V, --version                         show version and exit\n"
100 		" --help                                show this help and exit\n",
101 		PROGRAM, PROGRAM, formats);
102 }
103 
parse_header_name(options_t * options,const char * optarg)104 static void parse_header_name(options_t *options, const char *optarg)
105 {
106 	if (strcmp(optarg, "dos") == 0)
107 		options->headers.dos = true;
108 	else if (strcmp(optarg, "coff") == 0)
109 		options->headers.coff = true;
110 	else if (strcmp(optarg, "optional") == 0)
111 		options->headers.optional = true;
112 	else
113 		EXIT_ERROR("invalid header name option");
114 }
115 
free_options(options_t * options)116 static void free_options(options_t *options)
117 {
118 	if (options == NULL)
119 		return;
120 
121 	if (options->sections.name != NULL)
122 		free(options->sections.name);
123 
124 	free(options);
125 }
126 
parse_options(int argc,char * argv[])127 static options_t *parse_options(int argc, char *argv[])
128 {
129 	options_t *options = malloc_s(sizeof(options_t));
130 	memset(options, 0, sizeof(options_t));
131 
132 	// parameters for getopt_long() function
133 	static const char short_options[] = "f:a:c:h:s:V";
134 
135 	static const struct option long_options[] = {
136 		{ "help",          no_argument,         NULL,  1  },
137 		{ "format",        required_argument,   NULL, 'f' },
138 		{ "all",           no_argument,         NULL, 'a' },
139 		{ "content",       no_argument,         NULL, 'c' },
140 		{ "header",        required_argument,   NULL, 'h' },
141 		{ "section-name",  required_argument,   NULL, 's' },
142 		{ "section-index", required_argument,   NULL,  2  },
143 		{ "version",       no_argument,         NULL, 'V' },
144 		{  NULL,           0,                   NULL,  0  }
145 	};
146 
147 	// Setting the default option
148 	options->content = true;
149 
150 	int c, ind;
151 	while ((c = getopt_long(argc, argv, short_options, long_options, &ind)))
152 	{
153 		if (c < 0)
154 			break;
155 
156 		switch (c)
157 		{
158 			case 1:     // --help option
159 				usage();
160 				exit(EXIT_SUCCESS);
161 			case 'f':
162 				if (output_set_format_by_name(optarg) < 0)
163 					EXIT_ERROR("invalid format option");
164 				break;
165 			case 'a':
166 				options->all = true;
167 				break;
168 			case 'c': // default
169 				options->all = false; //TODO remover?
170 				options->content = true;
171 				break;
172 			case 's':
173 				options->all = false;
174 				options->headers.all = false;
175 				// TODO: How do we need to handle non-ascii names?
176 				options->sections.name = strdup(optarg);
177 				break;
178 			case 2:
179 				options->all = false;
180 				options->headers.all = false;
181 				options->sections.index = strtol(optarg, NULL, 10);
182 				if (options->sections.index < 1 || options->sections.index > MAX_SECTIONS) {
183 					EXIT_ERROR("Bad argument for section-index,");
184 				}
185 				break;
186 			case 'V':
187 				printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY);
188 				exit(EXIT_SUCCESS);
189 			case 'h':
190 				options->all = false;
191 				options->headers.all = false;
192 				parse_header_name(options, optarg);
193 				break;
194 			default:
195 				fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM);
196 				exit(EXIT_FAILURE);
197 		}
198 	}
199 
200 	// TODO: Warn about simultaneous usage of -h, -s, and --section-index.
201 
202 	return options;
203 }
204 
calc_hash(const char * alg_name,const unsigned char * data,size_t size,char * output)205 static void calc_hash(const char *alg_name, const unsigned char *data, size_t size, char *output)
206 {
207 	if (strcmp("ssdeep", alg_name) == 0) {
208 		fuzzy_hash_buf(data, size, output);
209 		return;
210 	}
211 
212 	const EVP_MD *md = EVP_get_digestbyname(alg_name);
213 	//assert(md != NULL); // We already checked this in parse_hash_algorithm()
214 
215 	unsigned char md_value[EVP_MAX_MD_SIZE];
216 	unsigned int md_len;
217 
218 	// See https://wiki.openssl.org/index.php/1.1_API_Changes
219 #if OPENSSL_VERSION_NUMBER < 0x10100000L
220 	EVP_MD_CTX md_ctx_auto;
221 	EVP_MD_CTX *md_ctx = &md_ctx_auto;
222 #else
223 	EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
224 #endif
225 
226 	// FIXME: Handle errors - Check return values.
227 	EVP_MD_CTX_init(md_ctx);
228 	EVP_DigestInit_ex(md_ctx, md, NULL);
229 	EVP_DigestUpdate(md_ctx, data, size);
230 	EVP_DigestFinal_ex(md_ctx, md_value, &md_len);
231 
232 #if OPENSSL_VERSION_NUMBER < 0x10100000L
233 	EVP_MD_CTX_cleanup(md_ctx);
234 #else
235 	EVP_MD_CTX_free(md_ctx);
236 #endif
237 
238 	for (unsigned int i=0; i < md_len; i++)
239 		sprintf(&output[i * 2], "%02x", md_value[i]);
240 }
241 
print_basic_hash(const unsigned char * data,size_t size)242 static void print_basic_hash(const unsigned char *data, size_t size)
243 {
244 	char *basic_hashes[] = { "md5", "sha1", "sha256", "ssdeep" };
245 	char hash_value[EVP_MAX_MD_SIZE * 2 + 1];
246 
247 	if (!data || !size)
248 		return;
249 
250 	for (unsigned i=0; i < sizeof(basic_hashes) / sizeof(char *); i++) {
251 		calc_hash(basic_hashes[i], data, size, hash_value);
252 		output(basic_hashes[i], hash_value);
253 	}
254 }
255 
256 typedef struct element {
257     char *dll_name;
258     char *function_name;
259     //struct element *prev; /* needed for a doubly-linked list only */
260     struct element *next; /* needed for singly- or doubly-linked lists */
261 } element;
262 
imphash_load_imported_functions(pe_ctx_t * ctx,uint64_t offset,char * dll_name,struct element ** head,int flavor)263 static void imphash_load_imported_functions(pe_ctx_t *ctx, uint64_t offset, char *dll_name, struct element **head, int flavor)
264 {
265 	uint64_t ofs = offset;
266 
267 	char hint_str[16];
268 	char fname[MAX_FUNCTION_NAME];
269 	bool is_ordinal;
270 
271 	memset(hint_str, 0, sizeof(hint_str));
272 	memset(fname, 0, sizeof(fname));
273 
274 	while (1) {
275 		switch (ctx->pe.optional_hdr.type) {
276 			case MAGIC_PE32:
277 			{
278 				const IMAGE_THUNK_DATA32 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
279 				if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA32))) {
280 					// TODO: Should we report something?
281 					return;
282 				}
283 
284 				// Type punning
285 				const uint32_t thunk_type = *(uint32_t *)thunk;
286 				if (thunk_type == 0)
287 					return;
288 
289 				is_ordinal = (thunk_type & IMAGE_ORDINAL_FLAG32) != 0;
290 
291 				if (is_ordinal) {
292 					snprintf(hint_str, sizeof(hint_str)-1, "%"PRIu32,
293 						thunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG32);
294 				} else {
295 					const uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
296 					const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
297 					if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
298 						// TODO: Should we report something?
299 						return;
300 					}
301 
302 					snprintf(hint_str, sizeof(hint_str)-1, "%d", imp_name->Hint);
303 					strncpy(fname, (char *)imp_name->Name, sizeof(fname)-1);
304 					// Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
305 					fname[sizeof(fname) - 1] = '\0';
306 					//size_t fname_len = strlen(fname);
307 				}
308 				ofs += sizeof(IMAGE_THUNK_DATA32);
309 				break;
310 			}
311 			case MAGIC_PE64:
312 			{
313 				const IMAGE_THUNK_DATA64 *thunk = LIBPE_PTR_ADD(ctx->map_addr, ofs);
314 				if (!pe_can_read(ctx, thunk, sizeof(IMAGE_THUNK_DATA64))) {
315 					// TODO: Should we report something?
316 					return;
317 				}
318 
319 				// Type punning
320 				const uint64_t thunk_type = *(uint64_t *)thunk;
321 				if (thunk_type == 0)
322 					return;
323 
324 				is_ordinal = (thunk_type & IMAGE_ORDINAL_FLAG64) != 0;
325 
326 				if (is_ordinal) {
327 					snprintf(hint_str, sizeof(hint_str)-1, "%llu",
328 						thunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG64);
329 				} else {
330 					uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
331 					const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
332 					if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
333 						// TODO: Should we report something?
334 						return;
335 					}
336 
337 					snprintf(hint_str, sizeof(hint_str)-1, "%d", imp_name->Hint);
338 					strncpy(fname, (char *)imp_name->Name, sizeof(fname)-1);
339 					// Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
340 					fname[sizeof(fname) - 1] = '\0';
341 					//size_t fname_len = strlen(fname);
342 				}
343 				ofs += sizeof(IMAGE_THUNK_DATA64);
344 				break;
345 			}
346 		}
347 
348 		if (!dll_name)
349 			continue;
350 
351 		// Beginning of imphash logic - that's the weirdest thing I've even seen...
352 
353 		for (unsigned i=0; i < strlen(dll_name); i++)
354 			dll_name[i] = tolower(dll_name[i]);
355 
356 		char *aux = NULL;
357 
358 		//TODO use a reverse search function instead
359 
360 		if (flavor == IMPHASH_FLAVOR_MANDIANT)
361 			aux = last_strstr(dll_name, ".");
362 		else if (flavor == IMPHASH_FLAVOR_PEFILE) {
363 			aux = last_strstr(dll_name, ".dll");
364 			if (aux)
365 				*aux = '\0';
366 
367 			aux = last_strstr(dll_name, ".ocx");
368 			if (aux)
369 				*aux = '\0';
370 
371 			aux = last_strstr(dll_name, ".sys");
372 			if (aux)
373 				*aux = '\0';
374 		}
375 
376 		if (aux)
377 			*aux = '\0';
378 
379 		for (unsigned i=0; i < strlen(fname); i++)
380 			fname[i] = tolower(fname[i]);
381 
382 		struct element *el = (struct element *) malloc(sizeof(struct element));
383 
384 		el->dll_name = strdup(dll_name);
385 
386 		if (flavor == IMPHASH_FLAVOR_MANDIANT) {
387 			el->function_name = strdup(is_ordinal ? hint_str : fname);
388 		}
389 		else if (flavor == IMPHASH_FLAVOR_PEFILE) {
390 
391 			int hint = strtoul(hint_str, NULL, 10);
392 
393 			if ( strncmp(dll_name, "oleaut32", 8) == 0 && is_ordinal) {
394 				for (unsigned i=0; i < sizeof(oleaut32_arr) / sizeof(ord_t); i++)
395 					if (hint == oleaut32_arr[i].number)
396 						el->function_name = strdup(oleaut32_arr[i].fname);
397 			}
398 			else if ( strncmp(dll_name, "ws2_32", 6) == 0 && is_ordinal) {
399 				for (unsigned i=0; i < sizeof(ws2_32_arr) / sizeof(ord_t); i++)
400 					if (hint == ws2_32_arr[i].number)
401 						el->function_name = strdup(ws2_32_arr[i].fname);
402 			}
403 			else {
404 				char ord[MAX_FUNCTION_NAME];
405 				memset(ord, 0, MAX_FUNCTION_NAME);
406 
407 				if (is_ordinal) {
408 					snprintf(ord, MAX_FUNCTION_NAME, "ord%s", hint_str);
409 					el->function_name = strdup(ord);
410 				} else {
411 					el->function_name = strdup(fname);
412 				}
413 			}
414 		}
415 
416 		for (unsigned i=0; i < strlen(el->function_name); i++)
417 			el->function_name[i] = tolower(el->function_name[i]);
418 
419 		LL_APPEND(*head, el);
420 	}
421 }
422 
imphash(pe_ctx_t * ctx,int flavor)423 static void imphash(pe_ctx_t *ctx, int flavor)
424 {
425 	const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
426 	if (dir == NULL)
427 		return;
428 
429 	const uint64_t va = dir->VirtualAddress;
430 	if (va == 0) {
431 		fprintf(stderr, "import directory not found\n");
432 		return;
433 	}
434 	uint64_t ofs = pe_rva2ofs(ctx, va);
435 	element *elt, *tmp, *head = NULL;
436 	int count = 0;
437 
438 	while (1) {
439 		IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
440 		if (!pe_can_read(ctx, id, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
441 			// TODO: Should we report something?
442 			output_close_scope();
443 			return;
444 		}
445 
446 		if (!id->u1.OriginalFirstThunk && !id->FirstThunk)
447 			break;
448 
449 		ofs += sizeof(IMAGE_IMPORT_DESCRIPTOR);
450 		const uint64_t aux = ofs; // Store current ofs
451 
452 		ofs = pe_rva2ofs(ctx, id->Name);
453 		if (ofs == 0)
454 			break;
455 
456 		const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
457 		// Validate whether it's ok to access at least 1 byte after dll_name_ptr.
458 		// It might be '\0', for example.
459 		if (!pe_can_read(ctx, dll_name_ptr, 1)) {
460 			// TODO: Should we report something?
461 			break;
462 		}
463 
464 		char dll_name[MAX_DLL_NAME];
465 		strncpy(dll_name, dll_name_ptr, sizeof(dll_name)-1);
466 		// Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
467 		dll_name[sizeof(dll_name) - 1] = '\0';
468 
469 		//output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT);
470 		//output("Name", dll_name);
471 
472 		ofs = pe_rva2ofs(ctx, id->u1.OriginalFirstThunk ? id->u1.OriginalFirstThunk : id->FirstThunk);
473 		if (ofs == 0) {
474 			output_close_scope(); // Library
475 			break;
476 		}
477 
478 		imphash_load_imported_functions(ctx, ofs, dll_name, &head, flavor);
479 		ofs = aux; // Restore previous ofs
480 	}
481 
482 	LL_COUNT(head, elt, count);
483 	//printf("%d number of elements in list outside\n", count);
484 
485 	size_t imphash_string_size = sizeof(char) * count * MAX_DLL_NAME + MAX_FUNCTION_NAME;
486 
487 	char *imphash_string = (char *) malloc_s(imphash_string_size);
488 
489 	memset(imphash_string, 0, imphash_string_size);
490 
491 	LL_FOREACH_SAFE(head, elt, tmp) \
492 		sprintf(imphash_string, "%s%s.%s,", imphash_string, elt->dll_name, elt->function_name); \
493 		LL_DELETE(head, elt);
494 
495 	free(elt);
496 
497 	imphash_string_size = strlen(imphash_string);
498 	imphash_string[imphash_string_size-1] = '\0'; // remove the last comma sign
499 
500 	//puts(imphash_string); // DEBUG
501 
502 	char imphash[32];
503 	calc_hash("md5", (unsigned char *)imphash_string, strlen(imphash_string), imphash);
504 	free(imphash_string);
505 
506 	if (flavor == IMPHASH_FLAVOR_MANDIANT)
507 		output("imphash (Mandiant)", imphash);
508 	else if (flavor == IMPHASH_FLAVOR_PEFILE)
509 		output("imphash", imphash);
510 }
511 
main(int argc,char * argv[])512 int main(int argc, char *argv[])
513 {
514 	pev_config_t config;
515 	PEV_INITIALIZE(&config);
516 
517 	if (argc < 2) {
518 		usage();
519 		return EXIT_FAILURE;
520 	}
521 
522 	output_set_cmdline(argc, argv);
523 
524 	OpenSSL_add_all_digests();
525 
526 	options_t *options = parse_options(argc, argv);
527 
528 	pe_ctx_t ctx;
529 
530 	pe_err_e err = pe_load_file(&ctx, argv[argc-1]);
531 	if (err != LIBPE_E_OK) {
532 		pe_error_print(stderr, err);
533 		return EXIT_FAILURE;
534 	}
535 
536 	err = pe_parse(&ctx);
537 	if (err != LIBPE_E_OK) {
538 		pe_error_print(stderr, err);
539 		return EXIT_FAILURE;
540 	}
541 
542 	if (!pe_is_pe(&ctx))
543 		EXIT_ERROR("not a valid PE file");
544 
545 	const IMAGE_SECTION_HEADER *section_ptr = NULL;
546 	const unsigned char *data = NULL;
547 	uint64_t data_size = 0;
548 
549 	unsigned c = pe_sections_count(&ctx);
550 	IMAGE_SECTION_HEADER ** const sections = pe_sections(&ctx);
551 
552 	data = ctx.map_addr;
553 	data_size = pe_filesize(&ctx);
554 
555 	output_open_document();
556 
557 	if (options->headers.all || options->headers.dos || options->headers.coff || options->headers.optional ||
558 		options->sections.name || options->sections.index) {
559 		options->all = false;
560 		options->content = false;
561 	}
562 
563 	if (options->all) {
564 		options->content = true;
565 		options->headers.all = true;
566 	}
567 
568 	if (options->content) {
569 		output_open_scope("file", OUTPUT_SCOPE_TYPE_OBJECT);
570 		output("filepath", ctx.path);
571 		print_basic_hash(data, data_size);
572 		//imphash(&ctx, IMPHASH_FLAVOR_MANDIANT);
573 		imphash(&ctx, IMPHASH_FLAVOR_PEFILE);
574 
575 		output_close_scope(); // file
576 		if (!options->all) // whole file content only
577 			goto BYE;
578 	}
579 
580 	if (options->headers.all) {
581 		options->headers.dos = true;
582 		options->headers.coff = true;
583 		options->headers.optional = true;
584 	}
585 
586 	if (options->headers.all || options->headers.dos || options->headers.coff || options->headers.optional)
587 		output_open_scope("headers", OUTPUT_SCOPE_TYPE_ARRAY);
588 
589 	if (options->headers.all || options->headers.dos) {
590 		const IMAGE_DOS_HEADER *dos_hdr = pe_dos(&ctx);
591 		data = (const unsigned char *)dos_hdr;
592 		data_size = sizeof(IMAGE_DOS_HEADER);
593 
594 		output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT);
595 		output("header_name", "IMAGE_DOS_HEADER");
596 		print_basic_hash(data, data_size);
597 		output_close_scope(); // header
598 	}
599 
600 	if (options->headers.all || options->headers.coff) {
601 		const IMAGE_COFF_HEADER *coff_hdr = pe_coff(&ctx);
602 		data = (const unsigned char *)coff_hdr;
603 		data_size = sizeof(IMAGE_COFF_HEADER);
604 
605 		output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT);
606 		output("header_name", "IMAGE_COFF_HEADER");
607 		print_basic_hash(data, data_size);
608 		output_close_scope(); // header
609 	}
610 
611 	if (options->headers.all || options->headers.optional) {
612       const IMAGE_OPTIONAL_HEADER *opt_hdr = pe_optional(&ctx);
613       switch (opt_hdr->type) {
614          case MAGIC_ROM:
615             // Oh boy! We do not support ROM. Abort!
616             fprintf(stderr, "ROM image is not supported\n");
617             break;
618          case MAGIC_PE32:
619             if (!pe_can_read(&ctx, opt_hdr->_32, sizeof(IMAGE_OPTIONAL_HEADER_32))) {
620                // TODO: Should we report something?
621                break;
622             }
623             data = (const unsigned char *)opt_hdr->_32;
624             data_size = sizeof(IMAGE_OPTIONAL_HEADER_32);
625             break;
626          case MAGIC_PE64:
627             if (!pe_can_read(&ctx, opt_hdr->_64, sizeof(IMAGE_OPTIONAL_HEADER_64))) {
628                // TODO: Should we report something?
629                break;
630             }
631             data = (const unsigned char *)opt_hdr->_64;
632             data_size = sizeof(IMAGE_OPTIONAL_HEADER_64);
633             break;
634 		}
635 
636 		output_open_scope("header", OUTPUT_SCOPE_TYPE_OBJECT);
637 		output("header_name", "IMAGE_OPTIONAL_HEADER");
638 		print_basic_hash(data, data_size);
639 		output_close_scope(); // header
640 	}
641 
642 	if (options->headers.all || options->headers.dos || options->headers.coff || options->headers.optional)
643 		output_close_scope(); // headers
644 
645 	if (options->all || options->sections.name || options->sections.index)
646 		output_open_scope("sections", OUTPUT_SCOPE_TYPE_ARRAY);
647 
648 	if (options->all) {
649 		for (unsigned int i=0; i<c; i++) {
650 			data_size = sections[i]->SizeOfRawData;
651 			data = LIBPE_PTR_ADD(ctx.map_addr, sections[i]->PointerToRawData);
652 
653 			if (!pe_can_read(&ctx, data, data_size)) {
654 				EXIT_ERROR("Unable to read section data");
655 			}
656 
657 			output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT);
658 			output("section_name", (char *)sections[i]->Name);
659 			if (data_size) {
660 				print_basic_hash(data, data_size);
661 			}
662 			output_close_scope(); // section
663 		}
664 		//output_close_scope(); // sections
665 	} else if (options->sections.name != NULL) {
666 		const IMAGE_SECTION_HEADER *section = pe_section_by_name(&ctx, options->sections.name);
667 		if (section == NULL) {
668 			EXIT_ERROR("The requested section could not be found on this binary");
669 		}
670 		section_ptr = section;
671 	} else if (options->sections.index > 0) {
672 		const uint16_t num_sections = pe_sections_count(&ctx);
673 		if (num_sections == 0 || options->sections.index > num_sections) {
674 			EXIT_ERROR("The requested section could not be found on this binary");
675 		}
676 		IMAGE_SECTION_HEADER ** const sections = pe_sections(&ctx);
677 		const IMAGE_SECTION_HEADER *section = sections[options->sections.index - 1];
678 		section_ptr = section;
679 	}
680 
681 	if (section_ptr != NULL) {
682 		if (section_ptr->SizeOfRawData > 0) {
683 			const uint8_t *section_data_ptr = LIBPE_PTR_ADD(ctx.map_addr, section_ptr->PointerToRawData);
684 			// printf("map_addr = %p\n", ctx.map_addr);
685 			// printf("section_data_ptr = %p\n", section_data_ptr);
686 			// printf("SizeOfRawData = %u\n", section_ptr->SizeOfRawData);
687 			if (!pe_can_read(&ctx, section_data_ptr, section_ptr->SizeOfRawData)) {
688 				EXIT_ERROR("The requested section has an invalid size");
689 			}
690 			data = (const unsigned char *)section_data_ptr;
691 			data_size = section_ptr->SizeOfRawData;
692 		} else {
693 			data = (const unsigned char *)"";
694 			data_size = 0;
695 		}
696 	}
697 
698 	if (!options->all && data != NULL) {
699 		output_open_scope("section", OUTPUT_SCOPE_TYPE_OBJECT);
700 		output("section_name", options->sections.name);
701 		print_basic_hash(data, data_size);
702 		output_close_scope();
703 	}
704 
705 	if (options->all || options->sections.name || options->sections.index)
706 		output_close_scope();
707 
708 	BYE:
709 	output_close_document();
710 
711 	// free
712 	free_options(options);
713 
714 	err = pe_unload(&ctx);
715 	if (err != LIBPE_E_OK) {
716 		pe_error_print(stderr, err);
717 		return EXIT_FAILURE;
718 	}
719 
720 	EVP_cleanup(); // Clean OpenSSL_add_all_digests.
721 	PEV_FINALIZE(&config);
722 	return EXIT_SUCCESS;
723 }
724