/*
* PDF Scrutinizer, a library for detecting and analyzing malicious PDF documents.
* Copyright 2013 Florian Schmitt <florian@florianschmitt.de>, Fraunhofer FKIE
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.pdf_scrutinizer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import de.pdf_scrutinizer.data.AnalysisResult;
import de.pdf_scrutinizer.data.Vulnerability;
import de.pdf_scrutinizer.dynamic_heuristics.LibemuShellcodeTester;
import de.pdf_scrutinizer.utils.OutputNull;
public class ScrutinizeDirectory {
private Log log = LogFactory.getLog(ScrutinizeDirectory.class);
private static final String LIBEMU_DEFAULT_PATH = "/opt/libemu/sctest";
private static final String INDENT = " ";
private final PrintWriter out;
private final String directory;
private final Date analysisStart = new Date();
private Date analysisEnd;
ScrutinizeDirectory(String directory, String outputfilename) throws FileNotFoundException {
OutputStream o = new FileOutputStream(outputfilename);
this.out = new PrintWriter(o);
this.directory = directory;
}
private String currentIndent = "";
private void analysisHeader() {
out.println("<?xml version=\"1.0\"?>");
out.println("<analysis>");
plusIndent();
out.println(String.format("%s<path>%s</path>", currentIndent, directory));
out.println(String.format("%s<analysisStart>%s</analysisStart>", currentIndent, analysisStart.toString()));
out.println(currentIndent + "<result>");
plusIndent();
}
private void analysisFooter() {
minusIndent();
out.println(currentIndent + "</result>");
out.println(String.format("%s<analysisEnd>%s</analysisEnd>", currentIndent, analysisEnd.toString()));
out.println(String.format("%s<analysisTime>%s</analysisTime>", currentIndent, DateFormatUtils.format(getAnalysisTimeString(analysisStart, analysisEnd), "HH:mm:ss,S")));
minusIndent();
out.println(String.format("%s</analysis>", currentIndent));
}
private void plusIndent() {
currentIndent = currentIndent + INDENT;
}
private void minusIndent() {
if (currentIndent.length() - INDENT.length() >= 0) {
currentIndent = currentIndent.substring(0, currentIndent.length() - INDENT.length());
}
}
public static long getAnalysisTimeString(Date analysisStart, Date analysisEnd) {
return analysisEnd.getTime() - analysisStart.getTime() - TimeUnit.HOURS.toMillis(1L);
}
private void addSample(AnalysisResult result) {
out.println(String.format("%s<sample md5='%s'>", currentIndent, result.hash));
plusIndent();
out.println(String.format("%s<filename>%s</filename>", currentIndent, result.filename));
out.println(String.format("%s<class>%s</class>", currentIndent, result.classification));
out.println(String.format("%s<error>%b</error>", currentIndent, result.error));
out.println(String.format("%s<code>%b</code>", currentIndent, result.codeFound));
out.println(String.format("%s<time>%s</time>", currentIndent, DateFormatUtils.format(getAnalysisTimeString(result.analysisStart, result.analysisEnd), "HH:mm:ss,S")));
if (result.getUsedVulnerabilities().size() > 0) {
out.println(String.format("%s<vulns>", currentIndent));
plusIndent();
for (Vulnerability v : result.getUsedVulnerabilities()) {
out.println(String.format("%s<vuln>%s</vuln>", currentIndent, v.getCVEID()));
}
minusIndent();
out.println(String.format("%s</vulns>", currentIndent));
}
if (result.getFulfilledHeuristics().size() > 0) {
out.println(String.format("%s<heuristics>", currentIndent));
plusIndent();
for (String v : result.getFulfilledHeuristics()) {
out.println(String.format("%s<heuristic>%s</heuristic>", currentIndent, v));
}
minusIndent();
out.println(String.format("%s</heuristics>", currentIndent));
}
if (result.getExceptions() != null && result.getExceptions().size() > 0) {
out.println(String.format("%s<exceptions>", currentIndent));
plusIndent();
for (Exception v : result.getExceptions()) {
out.println(String.format("%s<exception>%s</exception>", currentIndent, v.toString()));
}
minusIndent();
out.println(String.format("%s</exceptions>", currentIndent));
}
minusIndent();
out.println(String.format("%s</sample>", currentIndent));
}
public void doIt() {
File dir = new File(directory);
if (!dir.isDirectory()) {
System.err.println("provided invalid directory");
return;
}
String[] fileList = dir.list();
HashMap<String, List<Exception>> exceptions = new HashMap<String, List<Exception>>();
analysisHeader();
for (String x : fileList) {
File file = new File(dir + File.separator + x);
if (file.isDirectory())
continue;
Scrutinizer scrutinizer = new Scrutinizer();
Logger l = Logger.getRootLogger();
l.setLevel(Level.INFO);
try {
scrutinizer.setRootDocument(file);
} catch (FileNotFoundException e) {
log.error(e);
continue;
}
try {
scrutinizer.getDynamicHeuristics().setShellcodeTester(new LibemuShellcodeTester(scrutinizer, LIBEMU_DEFAULT_PATH));
} catch (FileNotFoundException e) {
log.error(e.getMessage());
}
scrutinizer.setOutput(new OutputNull());
try {
addSample(scrutinizer.analyze());
} catch (Exception e) {
String filename = file.getName();
List<Exception> tmp;
if ((tmp = exceptions.get(filename)) != null) {
tmp.add(e);
exceptions.put(filename, tmp);
} else {
tmp = new ArrayList<Exception>();
tmp.add(e);
exceptions.put(filename, tmp);
}
}
}
analysisEnd = new Date();
analysisFooter();
out.close();
if (exceptions.keySet().size() > 0) {
log.info("exceptions:");
}
for (String x : exceptions.keySet()) {
log.info("filename: " + x);
for (Exception l : exceptions.get(x)) {
log.info(l.getMessage(), l);
}
}
}
public static void main(String[] args) throws FileNotFoundException {
String directory = args[0];
String outputfilename;
if (args.length > 1) {
outputfilename = args[1];
} else {
outputfilename = "output.xml";
}
new ScrutinizeDirectory(directory, outputfilename).doIt();
}
}