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