Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PE analyzer #2448

Merged
merged 4 commits into from
Feb 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ velocity.log
/core/data/

# Apple macOS operating system Desktop Services Store files
.DS_Store
.DS_Store
/.vscode
/maven/.factorypath
1 change: 1 addition & 0 deletions cli/src/test/resources/sample.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ analyzer.dependencybundling.enabled=true
analyzer.dependencymerging.enabled=true
analyzer.falsepositive.enabled=true
analyzer.filename.enabled=true
analyzer.pe.enabled=true
analyzer.hint.enabled=true
analyzer.nvdcve.enabled=true
analyzer.vulnerabilitysuppression.enabled=true
Expand Down
1 change: 1 addition & 0 deletions cli/src/test/resources/sample2.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ analyzer.dependencybundling.enabled=false
analyzer.dependencymerging.enabled=false
analyzer.falsepositive.enabled=false
analyzer.filename.enabled=false
analyzer.pe.enabled=false
analyzer.hint.enabled=false
analyzer.nvdcve.enabled=false
analyzer.vulnerabilitysuppression.enabled=false
Expand Down
4 changes: 4 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.whitesource</groupId>
<artifactId>pecoff4j</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jcs-core</artifactId>
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/owasp/dependencycheck/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public enum Mode {
INITIAL,
PRE_INFORMATION_COLLECTION,
INFORMATION_COLLECTION,
INFORMATION_COLLECTION2,
POST_INFORMATION_COLLECTION
),
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public enum AnalysisPhase {
* Information collection phase.
*/
INFORMATION_COLLECTION,
/**
* Information collection phase 2.
*/
INFORMATION_COLLECTION2,
/**
* Post information collection phase.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public void analyzeDependency(Dependency dependency, Engine engine) throws Analy
dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "ProductVersion", data.getProductVersion(), Confidence.HIGHEST);
}
if (!StringUtils.isEmpty(data.getFileVersion())) {
dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "FileVersion", data.getFileVersion(), Confidence.HIGHEST);
dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "FileVersion", data.getFileVersion(), Confidence.HIGH);
}

if (data.getFileVersion() != null && data.getProductVersion() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An
if (version.getVersionParts() == null || version.getVersionParts().size() < 2) {
dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.MEDIUM);
} else {
dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.HIGHEST);
dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.HIGH);
}
dependency.addEvidence(EvidenceType.VERSION, "file", "name", packageName, Confidence.MEDIUM);
}
Expand Down
282 changes: 282 additions & 0 deletions core/src/main/java/org/owasp/dependencycheck/analyzer/PEAnalyzer.java
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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ public synchronized Set<Evidence> getEvidence(EvidenceType type) {
return null;
}

/**
* Returns the unmodifiable set of evidence.
*
* @return the unmodifiable set of evidence
*/
public synchronized Set<Evidence> getEvidence() {
Set e = new HashSet<>(vendors);
e.addAll(products);
e.addAll(versions);
return Collections.unmodifiableSet(e);
}

/**
* Tests if the evidence collection contains the given evidence.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
org.owasp.dependencycheck.analyzer.ArchiveAnalyzer
org.owasp.dependencycheck.analyzer.FileNameAnalyzer
org.owasp.dependencycheck.analyzer.PEAnalyzer
org.owasp.dependencycheck.analyzer.JarAnalyzer
org.owasp.dependencycheck.analyzer.HintAnalyzer
org.owasp.dependencycheck.analyzer.CPEAnalyzer
Expand Down
1 change: 1 addition & 0 deletions core/src/main/resources/dependencycheck.properties
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ analyzer.dependencybundling.enabled=true
analyzer.dependencymerging.enabled=true
analyzer.falsepositive.enabled=true
analyzer.filename.enabled=true
analyzer.pe.enabled=true
analyzer.hint.enabled=true
analyzer.nvdcve.enabled=true
analyzer.vulnerabilitysuppression.enabled=true
Expand Down
Loading