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