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 net.jsign.DigestAlgorithm; 035import net.jsign.Signable; 036import net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers; 037import net.jsign.asn1.authenticode.SpcAttributeTypeAndOptionalValue; 038import net.jsign.asn1.authenticode.SpcIndirectDataContent; 039import net.jsign.asn1.authenticode.SpcPeImageData; 040 041import org.bouncycastle.asn1.ASN1Encodable; 042import org.bouncycastle.asn1.ASN1Object; 043import org.bouncycastle.asn1.DERNull; 044import org.bouncycastle.asn1.cms.Attribute; 045import org.bouncycastle.asn1.cms.AttributeTable; 046import org.bouncycastle.asn1.cms.ContentInfo; 047import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 048import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 049import org.bouncycastle.asn1.x509.DigestInfo; 050import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; 051import org.bouncycastle.cert.X509CertificateHolder; 052import org.bouncycastle.cms.CMSProcessable; 053import org.bouncycastle.cms.CMSSignedData; 054import org.bouncycastle.cms.SignerInformation; 055 056import static net.jsign.ChannelUtils.*; 057 058/** 059 * Portable Executable File. 060 * 061 * This class is thread safe. 062 * 063 * @see <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx">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 public 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 CertificateTableEntry entry = new CertificateTableEntry(signature); 744 writeDataDirectory(DataDirectoryType.CERTIFICATE_TABLE, entry.toBytes()); 745 } 746 747 private synchronized List<CertificateTableEntry> getCertificateTable() { 748 List<CertificateTableEntry> entries = new ArrayList<>(); 749 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 750 751 if (certificateTable != null && certificateTable.exists()) { 752 long position = certificateTable.getVirtualAddress(); 753 754 try { 755 entries.add(new CertificateTableEntry(this, position)); 756 757 // todo read the remaining entries (but Authenticode use only one, extra signatures are appended as a SPC_NESTED_SIGNATURE unauthenticated attribute) 758 } catch (Exception e) { 759 e.printStackTrace(); 760 } 761 } 762 763 return entries; 764 } 765 766 public synchronized List<Section> getSections() { 767 List<Section> sections = new ArrayList<>(); 768 int sectionTableOffset = getDataDirectoryOffset() + 8 * getNumberOfRvaAndSizes(); 769 770 for (int i = 0; i < getNumberOfSections(); i++) { 771 sections.add(new Section(this, sectionTableOffset + 40 * i)); 772 } 773 774 return sections; 775 } 776 777 /** 778 * Print detailed informations about the PE file. 779 * 780 * @param out the output stream where the info is printed 781 */ 782 public void printInfo(OutputStream out) { 783 printInfo(new PrintWriter(out, true)); 784 } 785 786 /** 787 * Print detailed informations about the PE file. 788 * 789 * @param out the output writer where the info is printed 790 */ 791 public void printInfo(PrintWriter out) { 792 if (file != null) { 793 out.println("PE File"); 794 out.println(" Name: " + file.getName()); 795 out.println(" Size: " + file.length()); 796 out.println(" Last Modified: " + new Date(file.lastModified())); 797 out.println(); 798 } 799 800 out.println("PE Header"); 801 out.println(" Machine: " + getMachineType()); 802 out.println(" Number of sections: " + getNumberOfSections()); 803 out.println(" Timestamp: " + getTimeDateStamp()); 804 out.println(" Pointer to symbol table: 0x" + Long.toHexString(getPointerToSymbolTable())); 805 out.println(" Number of symbols: " + getNumberOfSymbols()); 806 out.println(" Size of optional header: " + getSizeOfOptionalHeader()); 807 out.println(" Characteristics: 0x" + Long.toBinaryString(getCharacteristics())); 808 out.println(); 809 810 out.println("Optional Header"); 811 PEFormat format = getFormat(); 812 out.println(" PE Format: 0x" + Integer.toHexString(format.value) + " (" + format.label + ")"); 813 out.println(" Linker version: " + getMajorLinkerVersion() + "." + getMinorLinkerVersion()); 814 out.println(" Size of code: " + getSizeOfCode()); 815 out.println(" Size of initialized data: " + getSizeOfInitializedData()); 816 out.println(" Size of uninitialized data: " + getSizeOfUninitializedData()); 817 out.println(" Address of entry point: 0x" + Long.toHexString(getAddressOfEntryPoint())); 818 out.println(" Base of code: 0x" + Long.toHexString(getBaseOfCode())); 819 if (PEFormat.PE32.equals(getFormat())) { 820 out.println(" Base of data: 0x" + Long.toHexString(getBaseOfData())); 821 } 822 out.println(" Image base: 0x" + Long.toHexString(getImageBase())); 823 out.println(" Section alignment: " + getSectionAlignment()); 824 out.println(" File alignment: " + getFileAlignment()); 825 out.println(" Operating system version: " + getMajorOperatingSystemVersion() + "." + getMinorOperatingSystemVersion()); 826 out.println(" Image version: " + getMajorImageVersion() + "." + getMinorImageVersion()); 827 out.println(" Subsystem version: " + getMajorSubsystemVersion() + "." + getMinorSubsystemVersion()); 828 out.println(" Size of image: " + getSizeOfImage()); 829 out.println(" Size of headers: " + getSizeOfHeaders()); 830 out.println(" Checksum: 0x" + Long.toHexString(getCheckSum())); 831 out.println(" Checksum (computed): 0x" + Long.toHexString(computeChecksum())); 832 out.println(" Subsystem: " + getSubsystem()); 833 out.println(" DLL characteristics: 0x" + Long.toBinaryString(getDllCharacteristics())); 834 out.println(" Size of stack reserve: " + getSizeOfStackReserve()); 835 out.println(" Size of stack commit: " + getSizeOfStackCommit()); 836 out.println(" Size of heap reserve: " + getSizeOfHeapReserve()); 837 out.println(" Size of heap commit: " + getSizeOfHeapCommit()); 838 out.println(" Number of RVA and sizes: " + getNumberOfRvaAndSizes()); 839 out.println(); 840 841 out.println("Data Directory"); 842 for (DataDirectoryType type : DataDirectoryType.values()) { 843 DataDirectory entry = getDataDirectory(type); 844 if (entry != null && entry.exists()) { 845 out.printf(" %-30s 0x%08x %8d bytes%n", type, entry.getVirtualAddress(), entry.getSize()); 846 } 847 } 848 out.println(); 849 850 out.println("Sections"); 851 out.println(" Name Virtual Size Virtual Address Raw Data Size Raw Data Ptr Characteristics"); 852 List<Section> sections = getSections(); 853 for (int i = 0; i < sections.size(); i++) { 854 Section section = sections.get(i); 855 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()); 856 } 857 out.println(); 858 859 List<CMSSignedData> signatures = getSignatures(); 860 if (!signatures.isEmpty()) { 861 out.println("Signatures"); 862 for (CMSSignedData signedData : signatures) { 863 SignerInformation signerInformation = signedData.getSignerInfos().getSigners().iterator().next(); 864 X509CertificateHolder certificate = (X509CertificateHolder) signedData.getCertificates().getMatches(signerInformation.getSID()).iterator().next(); 865 866 String commonName = certificate.getSubject().getRDNs(X509ObjectIdentifiers.commonName)[0].getFirst().getValue().toString(); 867 868 AttributeTable unsignedAttributes = signerInformation.getUnsignedAttributes(); 869 boolean timestamped = unsignedAttributes != null && 870 (unsignedAttributes.get(PKCSObjectIdentifiers.pkcs_9_at_counterSignature) != null 871 || unsignedAttributes.get(AuthenticodeObjectIdentifiers.SPC_RFC3161_OBJID) != null); 872 DigestAlgorithm algorithm = DigestAlgorithm.of(signerInformation.getDigestAlgorithmID().getAlgorithm()); 873 out.println(" " + commonName + " " + (algorithm != null ? "[" + algorithm.id + "] " : "") + (timestamped ? "(timestamped)" : "")); 874 } 875 } 876 } 877 878 /** 879 * Compute the digest of the file. The checksum field, the certificate 880 * directory table entry and the certificate table are excluded from 881 * the digest. 882 * 883 * @param digest the message digest to update 884 * @return the digest of the file 885 * @throws IOException if an I/O error occurs 886 */ 887 @Override 888 public synchronized byte[] computeDigest(MessageDigest digest) throws IOException { 889 long checksumLocation = peHeaderOffset + 88; 890 891 DataDirectory certificateTable = getDataDirectory(DataDirectoryType.CERTIFICATE_TABLE); 892 893 // digest from the beginning to the checksum field (excluded) 894 updateDigest(channel, digest, 0, checksumLocation); 895 896 // skip the checksum field 897 long position = checksumLocation + 4; 898 899 // digest from the end of the checksum field to the beginning of the certificate table entry 900 int certificateTableOffset = getDataDirectoryOffset() + 8 * DataDirectoryType.CERTIFICATE_TABLE.ordinal(); 901 updateDigest(channel, digest, position, certificateTableOffset); 902 903 // skip the certificate entry 904 position = certificateTableOffset + 8; 905 906 // todo digest the sections in ascending address order 907 908 // digest from the end of the certificate table entry to the beginning of the certificate table 909 if (certificateTable != null && certificateTable.exists()) { 910 updateDigest(channel, digest, position, certificateTable.getVirtualAddress()); 911 position = certificateTable.getVirtualAddress() + certificateTable.getSize(); 912 } 913 914 // digest from the end of the certificate table to the end of the file 915 updateDigest(channel, digest, position, channel.size()); 916 917 return digest.digest(); 918 } 919 920 /** 921 * Compute the checksum of the file using the specified digest algorithm. 922 * 923 * @param algorithm the digest algorithm, typically SHA1 924 * @return the checksum of the file 925 * @throws IOException if an I/O error occurs 926 */ 927 public byte[] computeDigest(DigestAlgorithm algorithm) throws IOException { 928 return computeDigest(algorithm.getMessageDigest()); 929 } 930 931 @Override 932 public ASN1Object createIndirectData(DigestAlgorithm digestAlgorithm) throws IOException { 933 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(digestAlgorithm.oid, DERNull.INSTANCE); 934 DigestInfo digestInfo = new DigestInfo(algorithmIdentifier, computeDigest(digestAlgorithm)); 935 SpcAttributeTypeAndOptionalValue data = new SpcAttributeTypeAndOptionalValue(AuthenticodeObjectIdentifiers.SPC_PE_IMAGE_DATA_OBJID, new SpcPeImageData()); 936 937 return new SpcIndirectDataContent(data, digestInfo); 938 } 939 940 /** 941 * Increase the size of the file up to a size that is a multiple of the specified value. 942 * 943 * @param multiple the size of the byte alignment 944 * @throws IOException if an I/O error occurs 945 */ 946 public synchronized void pad(int multiple) throws IOException { 947 long padding = (multiple - channel.size() % multiple) % multiple; 948 channel.position(channel.size()); 949 channel.write(ByteBuffer.allocate((int) padding)); 950 } 951}