-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Use pecoff4j to read dll/exe version information metadata on windows Dll and exe on windows that are not .NET assembly are only analyzed by the filename. This is often not good enough because the filename can contain other numbers (x86, x64, ...) other than the version. To improve the situation I've reduced the confidence of the filename parsed version and created a new analyzer The FileVersionAnalyzer use the pecoff4j library to extract, if possible, the version from the file metadata * updated PR #2446 to move this to a more full fledged PE Analyzer retrieving more than just the version number from the PE Headers Co-authored-by: Amodio <[email protected]>
- Loading branch information
1 parent
a30e04d
commit 63c5489
Showing
18 changed files
with
423 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
282 changes: 282 additions & 0 deletions
282
core/src/main/java/org/owasp/dependencycheck/analyzer/PEAnalyzer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
/* | ||
* This file is part of dependency-check-core. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* Copyright (c) 2020 Jeremy Long. All Rights Reserved. | ||
*/ | ||
package org.owasp.dependencycheck.analyzer; | ||
|
||
import com.github.packageurl.MalformedPackageURLException; | ||
import java.io.File; | ||
import java.io.FileFilter; | ||
import java.io.IOException; | ||
|
||
import org.boris.pecoff4j.PE; | ||
import org.boris.pecoff4j.ResourceDirectory; | ||
import org.boris.pecoff4j.ResourceEntry; | ||
import org.boris.pecoff4j.constant.ResourceType; | ||
import org.boris.pecoff4j.io.PEParser; | ||
import org.boris.pecoff4j.io.ResourceParser; | ||
import org.boris.pecoff4j.resources.StringFileInfo; | ||
import org.boris.pecoff4j.resources.StringTable; | ||
import org.boris.pecoff4j.resources.VersionInfo; | ||
import org.boris.pecoff4j.util.ResourceHelper; | ||
|
||
import javax.annotation.concurrent.ThreadSafe; | ||
import org.apache.commons.lang3.StringUtils; | ||
|
||
import org.owasp.dependencycheck.Engine; | ||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; | ||
import org.owasp.dependencycheck.dependency.Confidence; | ||
import org.owasp.dependencycheck.dependency.Dependency; | ||
import org.owasp.dependencycheck.dependency.Evidence; | ||
import org.owasp.dependencycheck.dependency.EvidenceType; | ||
import org.owasp.dependencycheck.dependency.naming.GenericIdentifier; | ||
import org.owasp.dependencycheck.dependency.naming.PurlIdentifier; | ||
import org.owasp.dependencycheck.exception.InitializationException; | ||
import org.owasp.dependencycheck.utils.DependencyVersion; | ||
import org.owasp.dependencycheck.utils.DependencyVersionUtil; | ||
import org.owasp.dependencycheck.utils.FileFilterBuilder; | ||
import org.owasp.dependencycheck.utils.FileUtils; | ||
import org.owasp.dependencycheck.utils.Settings; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Takes a dependency and analyze the PE header for meta data that can be used | ||
* to identify the library. | ||
* | ||
* @author Amodio Pesce | ||
*/ | ||
@ThreadSafe | ||
@Experimental | ||
public class PEAnalyzer extends AbstractFileTypeAnalyzer { | ||
|
||
//<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer"> | ||
/** | ||
* Logger | ||
*/ | ||
private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyAnalyzer.class); | ||
/** | ||
* The name of the analyzer. | ||
*/ | ||
private static final String ANALYZER_NAME = "PE Analyzer"; | ||
|
||
/** | ||
* The phase that this analyzer is intended to run in. | ||
*/ | ||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION2; | ||
/** | ||
* The set of file extensions supported by this analyzer. | ||
*/ | ||
private static final String[] EXTENSIONS = {"exe", "dll"}; | ||
|
||
/** | ||
* The file filter used to determine which files this analyzer supports. | ||
*/ | ||
private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(EXTENSIONS).build(); | ||
/** | ||
* A descriptor for the type of dependencies processed or added by this | ||
* analyzer. | ||
*/ | ||
public static final String DEPENDENCY_ECOSYSTEM = "native"; | ||
|
||
/** | ||
* Returns the name of the analyzer. | ||
* | ||
* @return the name of the analyzer. | ||
*/ | ||
@Override | ||
public String getName() { | ||
return ANALYZER_NAME; | ||
} | ||
|
||
/** | ||
* Returns the phase that the analyzer is intended to run in. | ||
* | ||
* @return the phase that the analyzer is intended to run in. | ||
*/ | ||
@Override | ||
public AnalysisPhase getAnalysisPhase() { | ||
return ANALYSIS_PHASE; | ||
} | ||
|
||
/** | ||
* <p> | ||
* Returns the setting key to determine if the analyzer is enabled.</p> | ||
* | ||
* @return the key for the analyzer's enabled property | ||
*/ | ||
@Override | ||
protected String getAnalyzerEnabledSettingKey() { | ||
return Settings.KEYS.ANALYZER_PE_ENABLED; | ||
} | ||
|
||
/** | ||
* Returns the FileFilter. | ||
* | ||
* @return the FileFilter | ||
*/ | ||
@Override | ||
protected FileFilter getFileFilter() { | ||
return FILTER; | ||
} | ||
|
||
@Override | ||
protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { | ||
//nothing to prepare | ||
} | ||
|
||
/** | ||
* Collects information about the file name. | ||
* | ||
* @param dependency the dependency to analyze. | ||
* @param engine the engine that is scanning the dependencies | ||
* @throws AnalysisException is thrown if there is an error analyzing the PE | ||
* file. | ||
*/ | ||
@Override | ||
protected void analyzeDependency(final Dependency dependency, final Engine engine) throws AnalysisException { | ||
for (Evidence e : dependency.getEvidence()) { | ||
if ("grokassembly".equals(e.getSource())) { | ||
LOGGER.debug("Skipping {} because it was already analyzed by the Assembly Analyzer", dependency.getFileName()); | ||
return; | ||
} | ||
} | ||
try { | ||
final File fileToCheck = dependency.getActualFile(); | ||
final PE pe = PEParser.parse(fileToCheck.getPath()); | ||
final ResourceDirectory rd = pe.getImageData().getResourceTable(); | ||
final ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO); | ||
for (ResourceEntry entrie : entries) { | ||
final byte[] data = entrie.getData(); | ||
final VersionInfo version = ResourceParser.readVersionInfo(data); | ||
final StringFileInfo strings = version.getStringFileInfo(); | ||
final StringTable table = strings.getTable(0); | ||
String pVersion = null; | ||
String fVersion = null; | ||
|
||
for (int j = 0; j < table.getCount(); j++) { | ||
final String key = table.getString(j).getKey(); | ||
final String value = table.getString(j).getValue(); | ||
switch (key) { | ||
case "ProductVersion": | ||
dependency.addEvidence(EvidenceType.VERSION, "PE Header", "ProductVersion", value, Confidence.HIGHEST); | ||
pVersion = value; | ||
break; | ||
case "CompanyName": | ||
dependency.addEvidence(EvidenceType.VENDOR, "PE Header", "CompanyName", value, Confidence.HIGHEST); | ||
break; | ||
case "FileVersion": | ||
dependency.addEvidence(EvidenceType.VERSION, "PE Header", "FileVersion", value, Confidence.HIGH); | ||
fVersion = value; | ||
break; | ||
case "InternalName": | ||
dependency.addEvidence(EvidenceType.PRODUCT, "PE Header", "InternalName", value, Confidence.MEDIUM); | ||
determineDependencyName(dependency, value); | ||
break; | ||
case "LegalCopyright": | ||
dependency.addEvidence(EvidenceType.VENDOR, "PE Header", "LegalCopyright", value, Confidence.HIGHEST); | ||
if (dependency.getLicense() != null && dependency.getLicense().length() > 0) { | ||
dependency.setLicense(dependency.getLicense() + "/n/nLegal Copyright: " + value); | ||
} else { | ||
dependency.setLicense("Legal Copyright: " + value); | ||
} | ||
break; | ||
case "OriginalFilename": | ||
dependency.addEvidence(EvidenceType.VERSION, "PE Header", "OriginalFilename", value, Confidence.MEDIUM); | ||
determineDependencyName(dependency, value); | ||
break; | ||
case "ProductName": | ||
dependency.addEvidence(EvidenceType.PRODUCT, "PE Header", "ProductName", value, Confidence.HIGHEST); | ||
determineDependencyName(dependency, value); | ||
break; | ||
} | ||
if (fVersion != null && pVersion != null) { | ||
final int max = fVersion.length() > pVersion.length() ? pVersion.length() : fVersion.length(); | ||
int pos; | ||
for (pos = 0; pos < max; pos++) { | ||
if (fVersion.charAt(pos) != pVersion.charAt(pos)) { | ||
break; | ||
} | ||
} | ||
final DependencyVersion fileVersion = DependencyVersionUtil.parseVersion(fVersion, true); | ||
final DependencyVersion productVersion = DependencyVersionUtil.parseVersion(pVersion, true); | ||
if (pos > 0) { | ||
final DependencyVersion matchingVersion = DependencyVersionUtil.parseVersion(fVersion.substring(0, pos), true); | ||
if (fileVersion != null && fileVersion.toString().length() == fVersion.length()) { | ||
if (matchingVersion != null && matchingVersion.getVersionParts().size() > 2) { | ||
dependency.addEvidence(EvidenceType.VERSION, "PE Header", "FilteredVersion", | ||
matchingVersion.toString(), Confidence.HIGHEST); | ||
dependency.setVersion(matchingVersion.toString()); | ||
} | ||
} | ||
} | ||
if (dependency.getVersion() == null) { | ||
if (fVersion.length() >= pVersion.length()) { | ||
if (fileVersion != null && fileVersion.toString().length() == fVersion.length()) { | ||
dependency.setVersion(fileVersion.toString()); | ||
} else if (productVersion != null && productVersion.toString().length() == pVersion.length()) { | ||
dependency.setVersion(productVersion.toString()); | ||
} | ||
} else { | ||
if (productVersion != null && productVersion.toString().length() == pVersion.length()) { | ||
dependency.setVersion(productVersion.toString()); | ||
} else if (fileVersion != null && fileVersion.toString().length() == fVersion.length()) { | ||
dependency.setVersion(fileVersion.toString()); | ||
} | ||
} | ||
} | ||
} else if (pVersion != null) { | ||
final DependencyVersion productVersion = DependencyVersionUtil.parseVersion(pVersion, true); | ||
if (productVersion != null && dependency.getActualFile().getName().contains(productVersion.toString())) { | ||
dependency.setVersion(productVersion.toString()); | ||
} | ||
} else if (fVersion != null) { | ||
final DependencyVersion fileVersion = DependencyVersionUtil.parseVersion(fVersion, true); | ||
if (fileVersion != null && dependency.getActualFile().getName().contains(fileVersion.toString())) { | ||
dependency.setVersion(fileVersion.toString()); | ||
} | ||
} | ||
if (dependency.getName() != null && dependency.getVersion() != null) { | ||
try { | ||
dependency.addSoftwareIdentifier(new PurlIdentifier("generic", dependency.getName(), dependency.getVersion(), Confidence.MEDIUM)); | ||
} catch (MalformedPackageURLException ex) { | ||
LOGGER.debug("Unable to create Package URL Identifier for " + dependency.getName(), ex); | ||
dependency.addSoftwareIdentifier(new GenericIdentifier( | ||
String.format("%s@%s", dependency.getName(), dependency.getVersion()), | ||
Confidence.MEDIUM)); | ||
} | ||
} | ||
if (dependency.getEcosystem() == null) {//this could be an assembly | ||
dependency.setEcosystem(DEPENDENCY_ECOSYSTEM); | ||
} | ||
} | ||
} | ||
} catch (IOException ex) { | ||
throw new AnalysisException(ex); | ||
} | ||
} | ||
|
||
private void determineDependencyName(final Dependency dependency, final String value) { | ||
if (dependency.getName() == null && StringUtils.containsIgnoreCase(dependency.getActualFile().getName(), value)) { | ||
final String ext = FileUtils.getFileExtension(value); | ||
if (ext != null) { | ||
dependency.setName(value.substring(0, value.length() - ext.length() - 1)); | ||
} else { | ||
dependency.setName(value); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.