@@ -449,13 +449,6 @@ std::string calculate_checksum(peparse::parsed_pe *pe, checksum_kind kind) {
449449 return {};
450450 }
451451
452- /* We'll stash the bits of the PE that we need to hash in this buffer.
453- * Reserve the original PE's size upfront, since we expect the hashed data
454- * to be only slightly smaller.
455- */
456- std::vector<std::uint8_t > pe_bits;
457- pe_bits.reserve (pe->fileBuffer ->bufLen );
458-
459452 /* In both PEs and PE32+s, the PE checksum is 64 bytes into the optional header,
460453 * which itself is 24 bytes after the PE magic and COFF header from the offset
461454 * specified in the DOS header.
@@ -499,48 +492,13 @@ std::string calculate_checksum(peparse::parsed_pe *pe, checksum_kind kind) {
499492 return {};
500493 }
501494
502- pe_bits.insert (pe_bits.begin (), header_buf->buf , header_buf->buf + header_buf->bufLen );
503- delete header_buf;
504-
505- /* Erase the PE checksum and certificate table entry from pe_bits.
506- * Do the certificate table entry first, so that we don't have to rescale the checksum's offset.
507- */
508- pe_bits.erase (pe_bits.begin () + cert_table_offset, pe_bits.begin () + cert_table_offset + 8 );
509- pe_bits.erase (pe_bits.begin () + pe_checksum_offset, pe_bits.begin () + pe_checksum_offset + 4 );
510-
511- /* Build up the list of sections in the PE, in ascending order by PointerToRawData
512- * (i.e., by file offset).
513- *
514- * NOTE(ww): Ideally we'd use a capture with the C++ lambda here, but C++ lambdas can't be
515- * used within C callbacks unless they're captureless.
516- */
517- impl::SectionList sections;
518- peparse::IterSec (
519- pe,
520- [](void *cbd,
521- [[maybe_unused]] const peparse::VA &secBase,
522- [[maybe_unused]] const std::string §ionName,
523- [[maybe_unused]] const peparse::image_section_header &sec,
524- const peparse::bounded_buffer *b) -> int {
525- auto §ions = *static_cast <impl::SectionList *>(cbd);
526- sections.emplace_back (b);
527- return 0 ;
528- },
529- §ions);
530-
531- /* Copy each section's data into pe_bits, in ascending order.
495+ /* We'll stash the bits of the PE that we need to hash in this buffer.
532496 */
533- for (const auto §ion : sections) {
534- pe_bits.insert (pe_bits.end (), section->buf , section->buf + section->bufLen );
535- }
497+ std::vector<std::uint8_t > pe_bits;
498+ pe_bits.reserve (size_of_headers);
536499
537- /* Also copy any data that happens to be trailing the certificate table into pe_bits.
538- * Most PEs won't have any trailing data but the Authenticode specification is explicit about
539- * hashing any if it exists.
540- */
541- pe_bits.insert (pe_bits.end (),
542- pe->fileBuffer ->buf + security_dir.VirtualAddress + security_dir.Size ,
543- pe->fileBuffer ->buf + pe->fileBuffer ->bufLen );
500+ pe_bits.insert (pe_bits.begin (), header_buf->buf , header_buf->buf + header_buf->bufLen );
501+ delete header_buf;
544502
545503 /* This won't happen under normal conditions, but could with a tampered input.
546504 * We don't have to check pe_checksum_offset here since it'll always be strictly less
@@ -550,19 +508,42 @@ std::string calculate_checksum(peparse::parsed_pe *pe, checksum_kind kind) {
550508 return {};
551509 }
552510
553- /* Finally, hash the damn thing.
554- *
555- * NOTE(ww): Instead of building up pe_bits and hashing it in one pass, we
556- * could hash it incrementally with each section. This would also solve
557- * the capture problem with the C++ callback above and would reduce
558- * the number of needed allocations.
511+ /* Erase the PE checksum and certificate table entry from pe_bits.
512+ * Do the certificate table entry first, so that we don't have to rescale the checksum's offset.
513+ */
514+ pe_bits.erase (pe_bits.begin () + cert_table_offset, pe_bits.begin () + cert_table_offset + 8 );
515+ pe_bits.erase (pe_bits.begin () + pe_checksum_offset, pe_bits.begin () + pe_checksum_offset + 4 );
516+
517+ /* Hash pe_bits, which contains the rest of the header.
559518 */
560- std::array<std::uint8_t , EVP_MAX_MD_SIZE> md_buf;
561519 const auto *md = EVP_get_digestbynid (nid);
562520 auto *md_ctx = EVP_MD_CTX_new ();
563-
564521 EVP_DigestInit (md_ctx, md);
565522 EVP_DigestUpdate (md_ctx, pe_bits.data (), pe_bits.size ());
523+
524+ if (security_dir.VirtualAddress > 0 ) {
525+ /* If a certificate table exists, hash everything before and after it.
526+ */
527+ EVP_DigestUpdate (md_ctx,
528+ pe->fileBuffer ->buf + size_of_headers,
529+ security_dir.VirtualAddress - size_of_headers);
530+
531+ /* Most PEs won't have any trailing data but the Authenticode specification is explicit about
532+ * hashing any if it exists.
533+ */
534+ EVP_DigestUpdate (md_ctx,
535+ pe->fileBuffer ->buf + security_dir.VirtualAddress + security_dir.Size ,
536+ pe->fileBuffer ->bufLen - (security_dir.VirtualAddress + security_dir.Size ));
537+ } else {
538+ /* If there's no certificate table, just hash the rest of the file.
539+ */
540+ EVP_DigestUpdate (
541+ md_ctx, pe->fileBuffer ->buf + size_of_headers, pe->fileBuffer ->bufLen - size_of_headers);
542+ }
543+
544+ /* Finally, finish hashing the damn thing.
545+ */
546+ std::array<std::uint8_t , EVP_MAX_MD_SIZE> md_buf;
566547 EVP_DigestFinal (md_ctx, md_buf.data (), nullptr );
567548 EVP_MD_CTX_free (md_ctx);
568549
0 commit comments