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