View Javadoc
1   /*******************************************************************************
2    * This program and the accompanying materials
3    * are made available under the terms of the Common Public License v1.0
4    * which accompanies this distribution, and is available at
5    * http://www.eclipse.org/legal/cpl-v10.html
6    *
7    * Contributors:
8    *     Peter Smith
9    *******************************************************************************/
10  package org.owasp.dependencycheck.utils;
11  
12  import com.kichik.pecoff4j.COFFHeader;
13  import com.kichik.pecoff4j.DOSHeader;
14  import com.kichik.pecoff4j.DOSStub;
15  import com.kichik.pecoff4j.DebugDirectory;
16  import com.kichik.pecoff4j.ImageData;
17  import com.kichik.pecoff4j.OptionalHeader;
18  import com.kichik.pecoff4j.PE;
19  import com.kichik.pecoff4j.PESignature;
20  import com.kichik.pecoff4j.SectionData;
21  import com.kichik.pecoff4j.SectionTable;
22  import com.kichik.pecoff4j.io.DataEntry;
23  import com.kichik.pecoff4j.io.DataReader;
24  import com.kichik.pecoff4j.io.IDataReader;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import java.io.EOFException;
29  import java.io.File;
30  import java.io.FileInputStream;
31  import java.io.IOException;
32  
33  /**
34   * This includes a copy of {@link PE#read(IDataReader)} and a couple of private methods
35   * with some added error handling to swallow EOFExceptions when reading certain sections of the file
36   * to be a bit more lenient on some "corrupt" (or not fully handled) dlls, per
37   * <a href="https://github.com/dependency-check/DependencyCheck/issues/2601">...</a>
38   *
39   * @see com.kichik.pecoff4j.io.PEParser#parse(File) 
40   * @see PE#read(IDataReader)
41   */
42  public class PEParser {
43      private static final Logger LOGGER = LoggerFactory.getLogger(PEParser.class);
44  
45      public static PE parse(File file) throws IOException {
46          try (FileInputStream is = new FileInputStream(file); DataReader dr = new DataReader(is)) {
47              return read(dr, file.getPath());
48          }
49      }
50  
51      /**
52       * Duplicates {@link PE#read(IDataReader)} with added error handling to swallow EOFExceptions to be more lenient
53       * for certain file sections.
54       * @see PE#read(IDataReader)
55       */
56      private static PE read(IDataReader dr, String context) throws IOException {
57          PE pe = new PE();
58          pe.setDosHeader(DOSHeader.read(dr));
59  
60          // Check if we have an old file type
61          if (pe.getDosHeader().getAddressOfNewExeHeader() == 0
62                  || pe.getDosHeader().getAddressOfNewExeHeader() > 8192) {
63              return pe;
64          }
65  
66          pe.setStub(DOSStub.read(pe.getDosHeader(), dr));
67          pe.setSignature(PESignature.read(dr));
68  
69          // Check signature to ensure we have a pe/coff file
70          if (!pe.getSignature().isValid()) {
71              return pe;
72          }
73  
74          pe.setCoffHeader(COFFHeader.read(dr));
75          pe.setOptionalHeader(OptionalHeader.read(dr));
76          pe.setSectionTable(SectionTable.read(pe, dr));
77  
78          pe.set64(pe.getOptionalHeader().isPE32plus());
79  
80          // Now read the rest of the file
81          DataEntry entry;
82          while ((entry = pe.findNextEntry(dr.getPosition())) != null) {
83              DataEntry finalEntry = entry;
84              if (finalEntry.isSection) {
85                  SectionData.read(pe, finalEntry, dr);
86              } else if (entry.isDebugRawData) {
87                  withEofSwallowing(() -> readDebugRawData(pe, finalEntry, dr), "debug raw data: " + context);
88              } else {
89                  withEofSwallowing(() -> pe.getImageData().read(pe, finalEntry, dr), "image data: " + context);
90              }
91          }
92  
93          // Read any trailing data
94          withEofSwallowing(() -> {
95              byte[] tb = dr.readAll();
96              if (tb.length > 0) {
97                  pe.getImageData().setTrailingData(tb);
98              }
99          }, "trailing data: " + context);
100 
101         return pe;
102     }
103 
104     /**
105      * Duplicates {@link PE#readDebugRawData(PE, DataEntry, IDataReader)} since it is private.
106      * @see PE#readDebugRawData(PE, DataEntry, IDataReader)
107      */
108     private static void readDebugRawData(PE pe, DataEntry entry, IDataReader dr) throws IOException {
109         // Read any preamble data
110         ImageData id = pe.getImageData();
111         byte[] pa = dr.readNonZeroOrNull(entry.pointer);
112         if (pa != null) {
113             id.setDebugRawDataPreamble(pa);
114         }
115         DebugDirectory dd = id.getDebug();
116         byte[] b = new byte[dd.getSizeOfData()];
117         dr.read(b);
118         id.setDebugRawData(b);
119     }
120 
121     private static void withEofSwallowing(IOExceptionThrower throwingRunnable, String errorContext) throws IOException {
122         try {
123             throwingRunnable.read();
124         } catch (EOFException e) {
125             LOGGER.debug("Error reading {}. Trying to continue...", errorContext, e);
126         }
127     }
128 
129     private interface IOExceptionThrower {
130         void read() throws IOException;
131     }
132 }