001/** 002 * Copyright 2012 Emmanuel Bourg 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package net.jsign.pe; 018 019import java.io.Closeable; 020import java.io.File; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.io.PrintWriter; 024import java.nio.ByteBuffer; 025import java.nio.ByteOrder; 026import java.nio.channels.SeekableByteChannel; 027import java.nio.file.Files; 028import java.nio.file.StandardOpenOption; 029import java.security.MessageDigest; 030import java.util.ArrayList; 031import java.util.Date; 032import java.util.List; 033 034import org.bouncycastle.asn1.ASN1Encodable; 035import org.bouncycastle.asn1.ASN1Object; 036import org.bouncycastle.asn1.DERNull; 037import org.bouncycastle.asn1.cms.Attribute; 038import org.bouncycastle.asn1.cms.AttributeTable; 039import org.bouncycastle.asn1.cms.ContentInfo; 040import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 041import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 042import org.bouncycastle.asn1.x509.DigestInfo; 043import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; 044import org.bouncycastle.cert.X509CertificateHolder; 045import org.bouncycastle.cms.CMSProcessable; 046import org.bouncycastle.cms.CMSSignedData; 047import org.bouncycastle.cms.SignerInformation; 048 049import net.jsign.DigestAlgorithm; 050import net.jsign.Signable; 051import net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers; 052import net.jsign.asn1.authenticode.SpcAttributeTypeAndOptionalValue; 053import net.jsign.asn1.authenticode.SpcIndirectDataContent; 054import net.jsign.asn1.authenticode.SpcPeImageData; 055 056import static net.jsign.ChannelUtils.*; 057 058/** 059 * Portable Executable File. 060 * 061 * This class is thread safe. 062 * 063 * @see <a href="https://docs.microsoft.com/en-us/windows/win32/debug/pe-format">Microsoft PE and COFF Specification </a> 064 * 065 * @author Emmanuel Bourg 066 * @since 1.0 067 */ 068public class PEFile implements Signable, Closeable { 069 070 /** The position of the PE header in the file */ 071 private final long peHeaderOffset; 072 073 private File file; 074 final SeekableByteChannel channel; 075 076 /** Reusable buffer for reading bytes, words, dwords and qwords from the file */ 077 private final ByteBuffer valueBuffer = ByteBuffer.allocate(8); 078 { 079 valueBuffer.order(ByteOrder.LITTLE_ENDIAN); 080 } 081 082 /** 083 * Tells if the specified file is a Portable Executable file. 084 * 085 * @param file the file to check 086 * @return <code>true</code> if the file is a Portable Executable, <code>false</code> otherwise 087 * @throws IOException if an I/O error occurs 088 * @since 3.0 089 */ 090 public static boolean isPEFile(File file) throws IOException { 091 if (!file.exists() || !file.isFile()) { 092 return false; 093 } 094 095 try { 096 PEFile peFile = new PEFile(file); 097 peFile.close(); 098 return true; 099 } catch (IOException e) { 100 if (e.getMessage().contains("DOS header signature not found") || e.getMessage().contains("PE signature not found")) { 101 return false; 102 } else { 103 throw e; 104 } 105 } 106 } 107 108 /** 109 * Create a PEFile from the specified file. 110 * 111 * @param file the file to open 112 * @throws IOException if an I/O error occurs 113 */ 114 public PEFile(File file) throws IOException { 115 this(Files.newByteChannel(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)); 116 this.file = file; 117 } 118 119 /** 120 * Create a PEFile from the specified channel. 121 * 122 * @param channel the channel to read the file from 123 * @throws IOException if an I/O error occurs 124 * @since 2.0 125 */ 126 public PEFile(SeekableByteChannel channel) throws IOException { 127 this.channel = channel; 128 129 try { 130 // DOS Header 131 read(0, 0, 2); 132 if (valueBuffer.get() != 'M' || valueBuffer.get() != 'Z') { 133 throw new IOException("DOS header signature not found"); 134 } 135 136 // PE Header 137 read(0x3C, 0, 4); 138 peHeaderOffset = valueBuffer.getInt() & 0xFFFFFFFFL; 139 read(peHeaderOffset, 0, 4); 140 if (valueBuffer.get() != 'P' || valueBuffer.get() != 'E' || valueBuffer.get() != 0 || valueBuffer.get() != 0) { 141 throw new IOException("PE signature not found as expected at offset 0x" + Long.toHexString(peHeaderOffset)); 142 } 143 144 } catch (IOException e) { 145 channel.close(); 146 throw e; 147 } 148 } 149 150 public void save() { 151 } 152 153 /** 154 * Closes the file 155 * 156 * @throws IOException if an I/O error occurs 157 */ 158 public synchronized void close() throws IOException { 159 channel.close(); 160 } 161 162 synchronized int read(byte[] buffer, long base, int offset) { 163 try { 164 channel.position(base + offset); 165 return channel.read(ByteBuffer.wrap(buffer)); 166 } catch (IOException e) { 167 throw new RuntimeException(e); 168 } 169 } 170 171 private void read(long base, int offset, int length) { 172 try { 173 valueBuffer.limit(length); 174 valueBuffer.clear(); 175 channel.position(base + offset); 176 channel.read(valueBuffer); 177 valueBuffer.rewind(); 178 } catch (IOException e) { 179 throw new RuntimeException(e); 180 } 181 } 182 183 synchronized int read(long base, int offset) { 184 read(base, offset, 1); 185 return valueBuffer.get(); 186 } 187 188 synchronized int readWord(long base, int offset) { 189 read(base, offset, 2); 190 return valueBuffer.getShort() & 0xFFFF; 191 } 192 193 synchronized long readDWord(long base, int offset) { 194 read(base, offset, 4); 195 return valueBuffer.getInt() & 0xFFFFFFFFL; 196 } 197 198 synchronized long readQWord(long base, int offset) { 199 read(base, offset, 8); 200 return valueBuffer.getLong(); 201 } 202 203 synchronized void write(long base, byte[] data) { 204 try { 205 channel.position(base); 206 channel.write(ByteBuffer.wrap(data)); 207 } catch (IOException e) { 208 throw new RuntimeException(e); 209 } 210 } 211 212 public MachineType getMachineType() { 213 return MachineType.valueOf(readWord(peHeaderOffset, 4)); 214 } 215 216 /** 217 * The number of sections. This indicates the size of the section table, 218 * which immediately follows the headers. 219 * 220 * @return the number of sections 221 */ 222 public int getNumberOfSections() { 223 return readWord(peHeaderOffset, 6); 224 } 225 226 /** 227 * The low 32 bits of the number of seconds since 00:00 January 1, 1970 228 * (a C runtime time_t value), that indicates when the file was created. 229 * 230 * @return the PE file creation date 231 */ 232 public Date getTimeDateStamp() { 233 return new Date(1000 * readDWord(peHeaderOffset, 8)); 234 } 235 236 /** 237 * The file offset of the COFF symbol table, or zero if no COFF symbol table 238 * is present. This value should be zero for an image because COFF debugging 239 * information is deprecated. 240 * 241 * @return the offset of the COFF symbol table 242 */ 243 public long getPointerToSymbolTable() { 244 return readDWord(peHeaderOffset, 12); 245 } 246 247 /** 248 * The number of entries in the symbol table. This data can be used to 249 * locate the string table, which immediately follows the symbol table. 250 * This value should be zero for an image because COFF debugging 251 * information is deprecated. 252 * 253 * @return the number of entries in the symbol table 254 */ 255 public long getNumberOfSymbols() { 256 return readDWord(peHeaderOffset, 16); 257 } 258 259 /** 260 * The size of the optional header, which is required for executable files 261 * but not for object files. This value should be zero for an object file. 262 * 263 * @return the size of the optional header 264 */ 265 public int getSizeOfOptionalHeader() { 266 return readWord(peHeaderOffset, 20); 267 } 268 269 /** 270 * The flags that indicate the attributes of the file. 271 * 272 * @return the characteristics flag 273 */ 274 public int getCharacteristics() { 275 return readWord(peHeaderOffset, 22); 276 } 277 278 public PEFormat getFormat() { 279 return PEFormat.valueOf(readWord(peHeaderOffset, 24)); 280 } 281 282 /** 283 * The linker major version number. 284 * 285 * @return the linker major version number 286 */ 287 public int getMajorLinkerVersion() { 288 return read(peHeaderOffset, 26); 289 } 290 291 /** 292 * The linker minor version number. 293 * 294 * @return the linker minor version number 295 */ 296 public int getMinorLinkerVersion() { 297 return read(peHeaderOffset, 27); 298 } 299 300 /** 301 * The size of the code (text) section, or the sum of all code sections 302 * if there are multiple sections. 303 * 304 * @return the size of the code (text) section 305 */ 306 public long getSizeOfCode() { 307 return readDWord(peHeaderOffset, 28); 308 } 309 310 /** 311 * The size of the initialized data section, or the sum of all such 312 * sections if there are multiple data sections. 313 * 314 * @return the size of the initialized data section 315 */ 316 public long getSizeOfInitializedData() { 317 return readDWord(peHeaderOffset, 32); 318 } 319 320 /** 321 * The size of the uninitialized data section (BSS), or the sum of all such 322 * sections if there are multiple BSS sections. 323 * 324 * @return the size of the uninitialized data section (BSS) 325 */ 326 public long getSizeOfUninitializedData() { 327 return readDWord(peHeaderOffset, 36); 328 } 329 330 /** 331 * The address of the entry point relative to the image base when the 332 * executable file is loaded into memory. For program images, this is the 333 * starting address. For device drivers, this is the address of the 334 * initialization function. An entry point is optional for DLLs. When no 335 * entry point is present, this field must be zero. 336 * 337 * @return the address of the entry point 338 */ 339 public long getAddressOfEntryPoint() { 340 return readDWord(peHeaderOffset, 40); 341 } 342 343 /** 344 * The address that is relative to the image base of the beginning-of-code 345 * section when it is loaded into memory. 346 * 347 * @return the code base address 348 */ 349 public long getBaseOfCode() { 350 return readDWord(peHeaderOffset, 44); 351 } 352 353 /** 354 * The address that is relative to the image base of the beginning-of-data 355 * section when it is loaded into memory (PE32 only). 356 * 357 * @return the data base address 358 */ 359 public long getBaseOfData() { 360 if (PEFormat.PE32.equals(getFormat())) { 361 return readDWord(peHeaderOffset, 48); 362 } else { 363 return 0; 364 } 365 } 366 367 /** 368 * The preferred address of the first byte of image when loaded into memory; 369 * must be a multiple of 64 K. The default for DLLs is 0x10000000. The default 370 * for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 2000, 371 * Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000. 372 * 373 * @return the image base address 374 */ 375 public long getImageBase() { 376 if (PEFormat.PE32.equals(getFormat())) { 377 return readDWord(peHeaderOffset, 52); 378 } else { 379 return readQWord(peHeaderOffset, 48); 380 } 381 } 382 383 /** 384 * The alignment (in bytes) of sections when they are loaded into memory. 385 * It must be greater than or equal to FileAlignment. The default is the 386 * page size for the architecture. 387 * 388 * @return the size of the sections memory alignment (in bytes) 389 */ 390 public long getSectionAlignment() { 391 return readDWord(peHeaderOffset, 56); 392 } 393 394 /** 395 * The alignment factor (in bytes) that is used to align the raw data of 396 * sections in the image file. The value should be a power of 2 between 397 * 512 and 64 K, inclusive. The default is 512. If the SectionAlignment 398 * is less than the architecture?s page size, then FileAlignment must 399 * match SectionAlignment. 400 * 401 * @return the alignment factor (in bytes) 402 */ 403 public long getFileAlignment() { 404 return readDWord(peHeaderOffset, 60); 405 } 406 407 /** 408 * The major version number of the required operating system. 409 * 410 * @return the major version number of the required operating system 411 */ 412 public int getMajorOperatingSystemVersion() { 413 return readWord(peHeaderOffset, 64); 414 } 415 416 /** 417 * The minor version number of the required operating system. 418 * 419 * @return the minor version number of the required operating system 420 */ 421 public int getMinorOperatingSystemVersion() { 422 return readWord(peHeaderOffset, 66); 423 } 424 425 /** 426 * The major version number of the image. 427 * 428 * @return the major version number of the image 429 */ 430 public int getMajorImageVersion() { 431 return readWord(peHeaderOffset, 68); 432 } 433 434 /** 435 * The minor version number of the image. 436 * 437 * @return the minor version number of the image 438 */ 439 public int getMinorImageVersion() { 440 return readWord(peHeaderOffset, 70); 441 } 442 443 /** 444 * The major version number of the subsystem. 445 * 446 * @return the major version number of the subsystem 447 */ 448 public int getMajorSubsystemVersion() { 449 return readWord(peHeaderOffset, 72); 450 } 451 452 /** 453 * The minor version number of the subsystem. 454 * 455 * @return the minor version number of the subsystem 456 */ 457 public int getMinorSubsystemVersion() { 458 return readWord(peHeaderOffset, 74); 459 } 460 461 /** 462 * Reserved, must be zero. 463 * 464 * @return zero 465 */ 466 public long getWin32VersionValue() { 467 return readDWord(peHeaderOffset, 76); 468 } 469 470 /** 471 * The size (in bytes) of the image, including all headers, as the image 472 * is loaded in memory. It must be a multiple of SectionAlignment. 473 * 474 * @return the size of the image (in bytes) 475 */ 476 public long getSizeOfImage() { 477 return readDWord(peHeaderOffset, 80); 478 } 479 480 /** 481 * The combined size of an MS DOS stub, PE header, and section headers 482 * rounded up to a multiple of FileAlignment. 483 * 484 * @return the combined size of the headers 485 */ 486 public long getSizeOfHeaders() { 487 return readDWord(peHeaderOffset, 84); 488 } 489 490 /** 491 * The image file checksum. 492 * 493 * @return the checksum of the image 494 */ 495 public long getCheckSum() { 496 return readDWord(peHeaderOffset, 88); 497 } 498 499 /** 500 * Compute the checksum of the image file. The algorithm for computing 501 * the checksum is incorporated into IMAGHELP.DLL. 502 * 503 * @return the checksum of the image 504 */ 505 public synchronized long computeChecksum() { 506 PEImageChecksum checksum = new PEImageChecksum(peHeaderOffset + 88); 507 508 ByteBuffer b = ByteBuffer.allocate(64 * 1024); 509 510 try { 511 channel.position(0); 512 513 int len; 514 while ((len = channel.read(b)) > 0) { 515 b.flip(); 516 checksum.update(b.array(), 0, len); 517 } 518 } catch (IOException e) { 519 throw new RuntimeException(e); 520 } 521 522 return checksum.getValue(); 523 } 524 525 public synchronized void updateChecksum() { 526 ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); 527 buffer.putInt((int) computeChecksum()); 528 buffer.flip(); 529 530 try { 531 channel.position(peHeaderOffset + 88); 532 channel.write(buffer); 533 } catch (IOException e) { 534 throw new RuntimeException(e); 535 } 536 } 537 538 /** 539 * The subsystem that is required to run this image. 540 * 541 * @return the required subsystem 542 */ 543 public Subsystem getSubsystem() { 544 return Subsystem.valueOf(readWord(peHeaderOffset, 92)); 545 } 546 547 public int getDllCharacteristics() { 548 return readWord(peHeaderOffset, 94); 549 } 550 551 /** 552 * The size of the stack to reserve. Only SizeOfStackCommit is committed; 553 * the rest is made available one page at a time until the reserve size is reached. 554 * 555 * @return the size of the stack to reserve 556 */ 557 public long getSizeOfStackReserve() { 558 if (PEFormat.PE32.equals(getFormat())) { 559 return readDWord(peHeaderOffset, 96); 560 } else { 561 return readQWord(peHeaderOffset, 96); 562 } 563 } 564 565 /** 566 * The size of the stack to commit. 567 * 568 * @return the size of the stack to commit 569 */ 570 public long getSizeOfStackCommit() { 571 if (PEFormat.PE32.equals(getFormat())) { 572 return readDWord(peHeaderOffset, 100); 573 } else { 574 return readQWord(peHeaderOffset, 104); 575 } 576 } 577 578 /** 579 * The size of the local heap space to reserve. Only SizeOfHeapCommit is 580 * committed; the rest is made available one page at a time until the 581 * reserve size is reached. 582 * 583 * @return the size of the local heap space to reserve 584 */ 585 public long getSizeOfHeapReserve() { 586 if (PEFormat.PE32.equals(getFormat())) { 587 return readDWord(peHeaderOffset, 104); 588 } else { 589 return readQWord(peHeaderOffset, 112); 590 } 591 } 592 593 /** 594 * The size of the local heap space to commit. 595 * 596 * @return the size of the local heap space to commit 597 */ 598 public long getSizeOfHeapCommit() { 599 if (PEFormat.PE32.equals(getFormat())) { 600 return readDWord(peHeaderOffset, 108); 601 } else { 602 return readQWord(peHeaderOffset, 120); 603 } 604 } 605 606 /** 607 * Reserved, must be zero. 608 * 609 * @return zero 610 */ 611 public long getLoaderFlags() { 612 return readDWord(peHeaderOffset, PEFormat.PE32.equals(getFormat()) ? 112 : 128); 613 } 614 615 /** 616 * The number of data-directory entries in the remainder of the optional 617 * header. Each describes a location and size. 618 * 619 * @return the number of data-directory entries 620 */ 621 public int getNumberOfRvaAndSizes() { 622 return (int) readDWord(peHeaderOffset, PEFormat.PE32.equals(getFormat()) ? 116 : 132); 623 } 624 625 int getDataDirectoryOffset() { 626 return (int) peHeaderOffset + (PEFormat.PE32.equals(getFormat()) ? 120 : 136); 627 } 628 629 /** 630 * Returns the data directory of the specified type. 631 * 632 * @param type the type of data directory 633 * @return the data directory of the specified type 634 */ 635 public DataDirectory getDataDirectory(DataDirectoryType type) { 636 if (type.ordinal() >= getNumberOfRvaAndSizes()) { 637 return null; 638 } else { 639 return new DataDirectory(this, type.ordinal()); 640 } 641 } 642 643 /** 644 * Writes the data directory of the specified type. The data is either appended 645 * at the end of the file or written over the previous data of the same type if 646 * there is enough space. 647 * 648 * @param type the type of the data directory 649 * @param data the content of the data directory 650 * @throws IOException if an I/O error occurs 651 */ 652 public synchronized void writeDataDirectory(DataDirectoryType type, byte[] data) throws IOException { 653 DataDirectory directory = getDataDirectory(type); 654 655 if (!directory.exists()) { 656 // append the data directory at the end of the file 657 long offset = channel.size(); 658 659 channel.position(offset); 660 channel.write(ByteBuffer.wrap(data)); 661 662 // update the entry in the data directory table 663 directory.write(offset, data.length); 664 665 } else { 666 if (data.length == directory.getSize()) { 667 // same size as before, just overwrite 668 channel.position(directory.getVirtualAddress()); 669 channel.write(ByteBuffer.wrap(data)); 670 671 } else if (data.length < directory.getSize() && type != DataDirectoryType.CERTIFICATE_TABLE) { 672 // the new data is smaller, erase and rewrite in-place 673 // this doesn't work with the certificate table since it changes the file digest 674 directory.erase(); 675 channel.position(directory.getVirtualAddress()); 676 channel.write(ByteBuffer.wrap(data)); 677 678 // update the size in the data directory table 679 directory.write(directory.getVirtualAddress(), data.length); 680 681 } else if (directory.isTrailing()) { 682 // the data is at the end of the file, overwrite it 683 channel.position(directory.getVirtualAddress()); 684 channel.write(ByteBuffer.wrap(data)); 685 channel.truncate(directory.getVirtualAddress() + data.length); // trim the file if the data shrunk 686 687 // update the size in the data directory table 688 directory.write(directory.getVirtualAddress(), data.length); 689 690 } else { 691 if (type == DataDirectoryType.CERTIFICATE_TABLE) { 692 throw new IOException("The certificate table isn't at the end of the file and can't be moved without invalidating the signature"); 693 } 694 695 // the new data is larger, erase and relocate it at the end 696 directory.erase(); 697 698 long offset = channel.size(); 699 700 channel.position(offset); 701 channel.write(ByteBuffer.wrap(data)); 702 703 // update the entry in the data directory table 704 directory.write(offset, data.length); 705 } 706 } 707 708 updateChecksum(); 709 } 710 711 @Override 712 public synchronized List<CMSSignedData> getSignatures() { 713 List<CMSSignedData> signatures = new ArrayList<>(); 714 715 for (CertificateTableEntry entry : getCertificateTable()) { 716 try { 717 CMSSignedData signedData = entry.getSignature(); 718 signatures.add(signedData); 719 720 // look for nested signatures 721 SignerInformation signerInformation = signedData.getSignerInfos().getSigners().iterator().next(); 722 AttributeTable unsignedAttributes = signerInformation.getUnsignedAttributes(); 723 if (unsignedAttributes != null) { 724 Attribute nestedSignatures = unsignedAttributes.get(AuthenticodeObjectIdentifiers.SPC_NESTED_SIGNATURE_OBJID); 725 if (nestedSignatures != null) { 726 for (ASN1Encodable nestedSignature : nestedSignatures.getAttrValues()) { 727 signatures.add(new CMSSignedData((CMSProcessable) null, ContentInfo.getInstance(nestedSignature))); 728 } 729 } 730 } 731 } catch (UnsupportedOperationException e) { 732 // unsupported type, just skip 733 } catch (Exception e) { 734 e.printStackTrace(); 735 } 736 } 737 738 return signatures; 739 } 740 741 @Override 742 public void setSignature(CMSSignedData signature) throws IOException { 743 // pad the file before adding the certificate table 744 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 745 if (certificateTable == null || !certificateTable.exists()) { 746 pad(8); 747 } 748 749 CertificateTableEntry entry = new CertificateTableEntry(signature); 750 writeDataDirectory(DataDirectoryType.CERTIFICATE_TABLE, entry.toBytes()); 751 } 752 753 private synchronized List<CertificateTableEntry> getCertificateTable() { 754 List<CertificateTableEntry> entries = new ArrayList<>(); 755 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 756 757 if (certificateTable != null && certificateTable.exists()) { 758 long position = certificateTable.getVirtualAddress(); 759 760 try { 761 entries.add(new CertificateTableEntry(this, position)); 762 763 // todo read the remaining entries (but Authenticode use only one, extra signatures are appended as a SPC_NESTED_SIGNATURE unauthenticated attribute) 764 } catch (Exception e) { 765 e.printStackTrace(); 766 } 767 } 768 769 return entries; 770 } 771 772 public synchronized List<Section> getSections() { 773 List<Section> sections = new ArrayList<>(); 774 int sectionTableOffset = getDataDirectoryOffset() + 8 * getNumberOfRvaAndSizes(); 775 776 for (int i = 0; i < getNumberOfSections(); i++) { 777 sections.add(new Section(this, sectionTableOffset + 40 * i)); 778 } 779 780 return sections; 781 } 782 783 /** 784 * Print detailed informations about the PE file. 785 * 786 * @param out the output stream where the info is printed 787 */ 788 public void printInfo(OutputStream out) { 789 printInfo(new PrintWriter(out, true)); 790 } 791 792 /** 793 * Print detailed informations about the PE file. 794 * 795 * @param out the output writer where the info is printed 796 */ 797 public void printInfo(PrintWriter out) { 798 if (file != null) { 799 out.println("PE File"); 800 out.println(" Name: " + file.getName()); 801 out.println(" Size: " + file.length()); 802 out.println(" Last Modified: " + new Date(file.lastModified())); 803 out.println(); 804 } 805 806 out.println("PE Header"); 807 out.println(" Machine: " + getMachineType()); 808 out.println(" Number of sections: " + getNumberOfSections()); 809 out.println(" Timestamp: " + getTimeDateStamp()); 810 out.println(" Pointer to symbol table: 0x" + Long.toHexString(getPointerToSymbolTable())); 811 out.println(" Number of symbols: " + getNumberOfSymbols()); 812 out.println(" Size of optional header: " + getSizeOfOptionalHeader()); 813 out.println(" Characteristics: 0x" + Long.toBinaryString(getCharacteristics())); 814 out.println(); 815 816 out.println("Optional Header"); 817 PEFormat format = getFormat(); 818 out.println(" PE Format: 0x" + Integer.toHexString(format.value) + " (" + format.label + ")"); 819 out.println(" Linker version: " + getMajorLinkerVersion() + "." + getMinorLinkerVersion()); 820 out.println(" Size of code: " + getSizeOfCode()); 821 out.println(" Size of initialized data: " + getSizeOfInitializedData()); 822 out.println(" Size of uninitialized data: " + getSizeOfUninitializedData()); 823 out.println(" Address of entry point: 0x" + Long.toHexString(getAddressOfEntryPoint())); 824 out.println(" Base of code: 0x" + Long.toHexString(getBaseOfCode())); 825 if (PEFormat.PE32.equals(getFormat())) { 826 out.println(" Base of data: 0x" + Long.toHexString(getBaseOfData())); 827 } 828 out.println(" Image base: 0x" + Long.toHexString(getImageBase())); 829 out.println(" Section alignment: " + getSectionAlignment()); 830 out.println(" File alignment: " + getFileAlignment()); 831 out.println(" Operating system version: " + getMajorOperatingSystemVersion() + "." + getMinorOperatingSystemVersion()); 832 out.println(" Image version: " + getMajorImageVersion() + "." + getMinorImageVersion()); 833 out.println(" Subsystem version: " + getMajorSubsystemVersion() + "." + getMinorSubsystemVersion()); 834 out.println(" Size of image: " + getSizeOfImage()); 835 out.println(" Size of headers: " + getSizeOfHeaders()); 836 out.println(" Checksum: 0x" + Long.toHexString(getCheckSum())); 837 out.println(" Checksum (computed): 0x" + Long.toHexString(computeChecksum())); 838 out.println(" Subsystem: " + getSubsystem()); 839 out.println(" DLL characteristics: 0x" + Long.toBinaryString(getDllCharacteristics())); 840 out.println(" Size of stack reserve: " + getSizeOfStackReserve()); 841 out.println(" Size of stack commit: " + getSizeOfStackCommit()); 842 out.println(" Size of heap reserve: " + getSizeOfHeapReserve()); 843 out.println(" Size of heap commit: " + getSizeOfHeapCommit()); 844 out.println(" Number of RVA and sizes: " + getNumberOfRvaAndSizes()); 845 out.println(); 846 847 out.println("Data Directory"); 848 for (DataDirectoryType type : DataDirectoryType.values()) { 849 DataDirectory entry = getDataDirectory(type); 850 if (entry != null && entry.exists()) { 851 out.printf(" %-30s 0x%08x %8d bytes%n", type, entry.getVirtualAddress(), entry.getSize()); 852 } 853 } 854 out.println(); 855 856 out.println("Sections"); 857 out.println(" Name Virtual Size Virtual Address Raw Data Size Raw Data Ptr Characteristics"); 858 List<Section> sections = getSections(); 859 for (int i = 0; i < sections.size(); i++) { 860 Section section = sections.get(i); 861 out.printf(" #%d %-8s %8d 0x%08x %8d 0x%08x %s%n", i + 1, section.getName(), section.getVirtualSize(), section.getVirtualAddress(), section.getSizeOfRawData(), section.getPointerToRawData(), section.getCharacteristics()); 862 } 863 out.println(); 864 865 List<CMSSignedData> signatures = getSignatures(); 866 if (!signatures.isEmpty()) { 867 out.println("Signatures"); 868 for (CMSSignedData signedData : signatures) { 869 SignerInformation signerInformation = signedData.getSignerInfos().getSigners().iterator().next(); 870 X509CertificateHolder certificate = (X509CertificateHolder) signedData.getCertificates().getMatches(signerInformation.getSID()).iterator().next(); 871 872 String commonName = certificate.getSubject().getRDNs(X509ObjectIdentifiers.commonName)[0].getFirst().getValue().toString(); 873 874 AttributeTable unsignedAttributes = signerInformation.getUnsignedAttributes(); 875 boolean timestamped = unsignedAttributes != null && 876 (unsignedAttributes.get(PKCSObjectIdentifiers.pkcs_9_at_counterSignature) != null 877 || unsignedAttributes.get(AuthenticodeObjectIdentifiers.SPC_RFC3161_OBJID) != null); 878 DigestAlgorithm algorithm = DigestAlgorithm.of(signerInformation.getDigestAlgorithmID().getAlgorithm()); 879 out.println(" " + commonName + " " + (algorithm != null ? "[" + algorithm.id + "] " : "") + (timestamped ? "(timestamped)" : "")); 880 } 881 } 882 } 883 884 /** 885 * Compute the digest of the file. The checksum field, the certificate 886 * directory table entry and the certificate table are excluded from 887 * the digest. 888 * 889 * @param digest the message digest to update 890 * @return the digest of the file 891 * @throws IOException if an I/O error occurs 892 */ 893 @Override 894 public synchronized byte[] computeDigest(MessageDigest digest) throws IOException { 895 long checksumLocation = peHeaderOffset + 88; 896 897 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 898 899 // digest from the beginning to the checksum field (excluded) 900 updateDigest(channel, digest, 0, checksumLocation); 901 902 // skip the checksum field 903 long position = checksumLocation + 4; 904 905 // digest from the end of the checksum field to the beginning of the certificate table entry 906 int certificateTableOffset = getDataDirectoryOffset() + 8 * DataDirectoryType.CERTIFICATE_TABLE.ordinal(); 907 updateDigest(channel, digest, position, certificateTableOffset); 908 909 // skip the certificate entry 910 position = certificateTableOffset + 8; 911 912 // todo digest the sections in ascending address order 913 914 // digest from the end of the certificate table entry to the beginning of the certificate table 915 if (certificateTable != null && certificateTable.exists()) { 916 updateDigest(channel, digest, position, certificateTable.getVirtualAddress()); 917 position = certificateTable.getVirtualAddress() + certificateTable.getSize(); 918 } 919 920 // digest from the end of the certificate table to the end of the file 921 updateDigest(channel, digest, position, channel.size()); 922 923 if (certificateTable == null || !certificateTable.exists()) { 924 // if the file has never been signed before, update the digest as if the file was padded on a 8 byte boundary 925 int paddingLength = (int) (8 - channel.size() % 8) % 8; 926 digest.update(new byte[paddingLength]); 927 } 928 929 return digest.digest(); 930 } 931 932 /** 933 * Compute the checksum of the file using the specified digest algorithm. 934 * 935 * @param algorithm the digest algorithm, typically SHA1 936 * @return the checksum of the file 937 * @throws IOException if an I/O error occurs 938 */ 939 public byte[] computeDigest(DigestAlgorithm algorithm) throws IOException { 940 return computeDigest(algorithm.getMessageDigest()); 941 } 942 943 @Override 944 public ASN1Object createIndirectData(DigestAlgorithm digestAlgorithm) throws IOException { 945 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(digestAlgorithm.oid, DERNull.INSTANCE); 946 DigestInfo digestInfo = new DigestInfo(algorithmIdentifier, computeDigest(digestAlgorithm)); 947 SpcAttributeTypeAndOptionalValue data = new SpcAttributeTypeAndOptionalValue(AuthenticodeObjectIdentifiers.SPC_PE_IMAGE_DATA_OBJID, new SpcPeImageData()); 948 949 return new SpcIndirectDataContent(data, digestInfo); 950 } 951 952 /** 953 * Increase the size of the file up to a size that is a multiple of the specified value. 954 * 955 * @param multiple the size of the byte alignment 956 * @throws IOException if an I/O error occurs 957 */ 958 public synchronized void pad(int multiple) throws IOException { 959 long padding = (multiple - channel.size() % multiple) % multiple; 960 channel.position(channel.size()); 961 channel.write(ByteBuffer.allocate((int) padding)); 962 } 963}