diff --git a/README.md b/README.md
index 0ba8376..ed202a3 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
# MalPull
-A CLI interface to search for a MD-5/SHA-1/SHA-256 hash on multiple malware databases and download the sample from the first hit. More information can be found here. If there are any questions, feature suggestions, or bug reports: please send me a direct message @Libranalysis,
+A CLI interface to search for a MD-5/SHA-1/SHA-256 hash on multiple malware databases and download the sample from the first hit. More information can be found here.If there are any questions, feature suggestions, or bug reports: please send me a message my Twitter (@Libranalysis).
diff --git a/pom.xml b/pom.xml
index 9d82498..908c5ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,9 +1,9 @@
4.0.0
- malwarepuller
+ malpull
MalPull
- 1.2.1-stable
+ 1.3-stable
jar
UTF-8
@@ -42,7 +42,7 @@
- malpull.MalPull
+ malpull.cli.MalPullCli
@@ -50,6 +50,32 @@
+
+ org.apache.maven.plugins
+ 3.2.1
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ 3.2.0
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
@@ -63,6 +89,12 @@
json
20190722
+
+
+ net.lingala.zip4j
+ zip4j
+ 2.7.0
+
MalPull
diff --git a/src/main/java/concurrency/DownloadWorker.java b/src/main/java/malpull/DownloadWorker.java
similarity index 77%
rename from src/main/java/concurrency/DownloadWorker.java
rename to src/main/java/malpull/DownloadWorker.java
index 4ae1acc..54a6144 100644
--- a/src/main/java/concurrency/DownloadWorker.java
+++ b/src/main/java/malpull/DownloadWorker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,10 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package concurrency;
+package malpull;
-import endpoints.*;
-import exceptions.SampleNotFoundException;
+import malpull.endpoints.IEndpoint;
+import malpull.exceptions.SampleNotFoundException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -30,10 +30,15 @@
* of the endpoints in the list. If it fails, the hash is added to the missing
* hashes list in the main class.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class DownloadWorker implements Runnable {
+ /**
+ * The instance of MalPull to connect back to
+ */
+ private MalPull malPull;
+
/**
* The list of endpoints iterate through in an attempt to download the hash
*/
@@ -64,6 +69,7 @@ public class DownloadWorker implements Runnable {
/**
* Creates a worker object, which can be queued for the thread pool
*
+ * @param malPull the MalPull instance to connect back to
* @param endpoints the list of endpoints to attempt to download from
* @param path the location to write the file to the disk
* @param hash the hash to look for
@@ -71,7 +77,8 @@ public class DownloadWorker implements Runnable {
* creation
* @param total the total number of samples to be downloaded
*/
- public DownloadWorker(List endpoints, String path, String hash, int count, int total) {
+ public DownloadWorker(MalPull malPull, List endpoints, String path, String hash, int count, int total) {
+ this.malPull = malPull;
this.endpoints = endpoints;
this.path = path;
this.hash = hash;
@@ -110,26 +117,29 @@ public void run() {
//The file is written to disk
writeToDisk(output, filePath);
//A message is printed for the user
- System.out.println("Wrote " + output.length + " bytes to " + filePath + " from " + endpoint.getName() + " (" + count + " / " + total + ")");
+ malPull.log("(" + count + " / " + total + ") Wrote " + output.length + " bytes to " + filePath + " from " + endpoint.getName());
+ //Add the hash to the log
+ malPull.addDownloadedHash(hash, endpoint.getName());
//The boolean is set to true, causing the next iteration to break out of the loop
isDownloaded = true;
}
- } catch (SampleNotFoundException e) {
+ } catch (SampleNotFoundException ex) {
/**
* The exception message can be ignored, as failure to
* download the sample results in the missing hash, but only
* if none of the configured endpoints has the hash
*/
- //System.out.println(e.getMessage());
}
}
//If the sample is not downloaded after the loop, it is missing
if (isDownloaded == false) {
//This method is thread safe
- MalPull.addMissingHash(hash);
+ malPull.addMissingHash(hash);
+ malPull.log("(" + count + " / " + total + ") Added \"" + hash + "\" to the missing hashes");
}
} catch (Exception ex) {
- System.out.println(ex.getMessage());
+ String message = "(" + count + " / " + total + ") An error occured when downloading " + hash + ":\n" + ex.getMessage();
+ malPull.log(message);
}
}
@@ -143,7 +153,7 @@ private void writeToDisk(byte[] output, String path) {
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(output);
} catch (IOException ex) {
- System.out.println("An error occured when writing the sample to \"" + path + "\". Verify your permissions and try again!");
+ malPull.log("An error occured when writing the sample to \"" + path + "\". Check the permissions and try again! Error:\n" + ex.getMessage());
}
}
}
diff --git a/src/main/java/malpull/MalPull.java b/src/main/java/malpull/MalPull.java
index 14ea381..8e0637c 100644
--- a/src/main/java/malpull/MalPull.java
+++ b/src/main/java/malpull/MalPull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -16,135 +16,192 @@
*/
package malpull;
-import concurrency.DownloadWorker;
-import endpoints.IEndpoint;
-import endpoints.Koodous;
-import endpoints.MalShare;
-import endpoints.MalwareBazaar;
-import endpoints.Triage;
-import endpoints.VirusTotal;
+import java.io.PrintStream;
+import malpull.model.Arguments;
+import malpull.endpoints.IEndpoint;
+import malpull.endpoints.Koodous;
+import malpull.endpoints.MalShare;
+import malpull.endpoints.MalwareBazaar;
+import malpull.endpoints.Triage;
+import malpull.endpoints.VirusTotal;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import malpull.exceptions.NoArgumentsSetException;
+import malpull.model.MalPullResult;
/**
- * This class is the main class of the project, and creates and calls all
- * objects. The project is set-up to have one malware repository service per
- * class, which is contacted via the service's API. The classes within the
- * endpoints
package all correspond to a single service.
+ * This class can be used to use MalPull as a library in any project. The
+ * constructors provide the option to log data. The download function requires
+ * all parameters, which can be parsed via any front-end that is made, or
+ * directly via code if this class is imported as a library.
+ *
+ * To compile this project as a single JAR, use:
+ * mvn clean compile assembly:single
. Install it in your local
+ * Maven repository to use it as a library in other projects, or copy the code
+ * into your project.
*
- * The Downloader class, which resides in this package, functions as a wrapper
- * class around the OkHttp3 library that is being used to perform the HTTP
- * requests.
- *
- * To compile this project, use: mvn clean compile assembly:single
- *
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class MalPull {
+ /**
+ * A mapping where the keys are equal to the user-provided hashes, and the
+ * value per key is the endpoint the hash was downloaded from
+ */
+ private Map downloadedSamples;
+
/**
* A list of hashes that could not be downloaded, taken from the
* deduplicated input
*/
- private static List missingHashes = new ArrayList<>();
+ private List missingHashes;
/**
- * Build using:
+ * The print stream to write the log to
+ */
+ private PrintStream outputStream;
+
+ /**
+ * Creates the MalPull object, automatically initialising the required
+ * fields.
*
- * mvn clean compile assembly:single
+ * @param outputStream the stream to write the logs to
+ */
+ public MalPull(PrintStream outputStream) {
+ //Initialise the downloaded samples mapping
+ downloadedSamples = new HashMap<>();
+ //Initialise the missing hashes list
+ missingHashes = new ArrayList<>();
+ //Set the output stream
+ this.outputStream = outputStream;
+ log(getVersionInformation());
+ }
+
+ /**
+ * Creates the MalPull object, automatically initialising the required
+ * fields. This instance will not have a logger, as it is not provided in
+ * the constructor
+ */
+ public MalPull() {
+ //Initialise the downloaded samples mapping
+ downloadedSamples = new HashMap<>();
+ //Initialise the missing hashes list
+ missingHashes = new ArrayList<>();
+ //Set the output stream to null, thereby disabling it
+ this.outputStream = null;
+ }
+
+ /**
+ * Creates a download worker object, which can be scheduled with the thread
+ * scheduler
*
- * @param args the command-line arguments
+ * @param arguments the object that contains the API keys
+ * @param hash the hash of the sample to use
+ * @param count the number that corresponds with the hash (used in the
+ * logging as count/total, where total is the total amount of
+ * hashes in the arguments object
+ * @return a list of download workers, ready to be scheduled
*/
- public static void main(String[] args) {
- //Get the start time
- long start = Instant.now().getEpochSecond();
- //Show the version information
- printVersionInformation();
- /**
- * Parse the arguments into a newly created object. If the arguments
- * cannot be parsed properly, the error message is displayed and MalPull
- * shuts down.
- */
- Arguments arguments = ArgumentHandler.handle(args);
+ private DownloadWorker getDownloadWorker(Arguments arguments, String hash, int count) {
+ //Create a list of endpoints
+ List endpoints = new ArrayList<>();
+ //Check all keys, and include endpoints for which keys are present
+ if (arguments.getTriageKey() != null) {
+ IEndpoint triage = new Triage(arguments.getTriageKey());
+ endpoints.add(triage);
+ }
+ if (arguments.getMalwareBazaarKey() != null) {
+ IEndpoint malwareBazaar = new MalwareBazaar();
+ endpoints.add(malwareBazaar);
+ }
+ if (arguments.getMalShareKey() != null) {
+ IEndpoint malShare = new MalShare(arguments.getMalShareKey());
+ endpoints.add(malShare);
+ }
+ if (arguments.getKoodousKey() != null) {
+ IEndpoint koodous = new Koodous(arguments.getKoodousKey());
+ endpoints.add(koodous);
+ }
+ if (arguments.getVirusTotalKey() != null) {
+ IEndpoint virusTotal = new VirusTotal(arguments.getVirusTotalKey());
+ endpoints.add(virusTotal);
+ }
+
+ //Create a download worker for the hash, with all configured endpoints embedded
+ DownloadWorker worker = new DownloadWorker(this, endpoints, arguments.getOutputPath(), hash, count, arguments.getHashes().size());
+ //Return the download worker
+ return worker;
+ }
- //Show the input back to the user, as this helps to avoid mistakes
- System.out.println("Read " + arguments.getHashes().size() + " hashes");
- System.out.println("Downloading will be done using " + arguments.getThreadCount() + " thread(s)");
- System.out.println("Output will be written to: " + arguments.getOutputPath());
- System.out.println("");
+ /**
+ * Downloads all samples that correspond to the given hashes, using the
+ * given services, to the given folder. Each sample is equal to the given
+ * hash name. The logging is written to the stream that the constructor of
+ * this object received. If none is given (or if it is equal to null), the
+ * logging is not printed.
+ *
+ * This function can take a while to return, depending on the amount of
+ * hashes that are provided, and the speed of the enabled services. Take
+ * note of this!
+ *
+ * @param arguments the arguments to use
+ * @return the download result, providing insight into all downloaded
+ * samples, all missing hashes, and the time it took
+ * @throws NoArgumentsSetException if the given argument object is null
+ */
+ public MalPullResult download(Arguments arguments) throws NoArgumentsSetException {
+ //Check if the arguments are set
+ if (arguments == null) {
+ throw new NoArgumentsSetException("The arguments need to be set prior to calling the download function");
+ }
- //Get all hashes in deduplicated form
- Set hashes = arguments.getHashes();
+ /**
+ * The validity of the arguments are checked upon the creation of the
+ * arguments object, meaning that the presence of the object indicates
+ * that the values can be trusted
+ */
+ //Get the start time
+ long start = Instant.now().getEpochSecond();
//Get the thread count from the parsed arguments
ExecutorService executor = Executors.newFixedThreadPool(arguments.getThreadCount());
- //Keep track of the count so each thread can print what number it had comapred to the total amount of downloads
+
int count = 0;
- //Iterate through all hashes, adding an endpoint for each of the configured endpoints
- for (String hash : hashes) {
+ //Iterate over all hashes
+ for (String hash : arguments.getHashes()) {
+ //Increase the count, which indicates is used in the logging to display the progress with respect to the total amount of hashes
count++;
- List endpoints = new ArrayList<>();
- if (arguments.getTriageKey() != null) {
- IEndpoint triage = new Triage(arguments.getTriageKey());
- endpoints.add(triage);
- }
- if (arguments.getMalwareBazaarKey() != null) {
- IEndpoint malwareBazaar = new MalwareBazaar();
- endpoints.add(malwareBazaar);
- }
- if (arguments.getMalShareKey() != null) {
- IEndpoint malShare = new MalShare(arguments.getMalShareKey());
- endpoints.add(malShare);
- }
- if (arguments.getKoodousKey() != null) {
- IEndpoint koodous = new Koodous(arguments.getKoodousKey());
- endpoints.add(koodous);
- }
- if (arguments.getVirusTotalKey() != null) {
- IEndpoint virusTotal = new VirusTotal(arguments.getVirusTotalKey());
- endpoints.add(virusTotal);
- }
-
- //Create a download worker for the hash, with all configured endpoints embedded
- DownloadWorker downloadWorker = new DownloadWorker(endpoints, arguments.getOutputPath(), hash, count, hashes.size());
- //Execute the download worker in the future, disregarding when specifically
- executor.execute(downloadWorker);
+ //Get the download worker for the given hash
+ DownloadWorker worker = getDownloadWorker(arguments, hash, count);
+ //Schedule the worker
+ executor.execute(worker);
}
- //Once all tasks are done, shut the executor down, meaning no new tasks can be added
+
+ //Once all workers are created, shut the executor down, meaning no new tasks can be added
executor.shutdown();
//Wait until the executor is terminated, which only happens when all downloads are finished
while (!executor.isTerminated()) {
}
- //Notify the user that all downloads are finished
- System.out.println("");
- System.out.println("All downloads finished! The sample number count is not always printed in ascending order, as the threads print the messages.");
-
- //If some hashes could not be found, these are printed
- if (missingHashes.size() > 0) {
- System.out.println("\n\nMissing " + missingHashes.size() + " hashes:");
- for (String missingHash : missingHashes) {
- System.out.println(missingHash);
- }
- }
- //Get the time since the start of the downloading
+
+ //Get the time since the downloading started
String time = getDuration(start);
- //Display the time that the download process took
- System.out.println("\nDownloaded " + (hashes.size() - missingHashes.size()) + " samples in " + time + "!");
- //Exit the program explicitly, as it sometimes remains open in some edge cases
- System.exit(0);
+
+ //Return the result to the caller
+ return new MalPullResult(downloadedSamples, missingHashes, time);
}
/**
* Gets the duration from the given starting point until the moment this
- * function is executed in the format of hh:mm:ss.
+ * function is executed in the format of hhh:mm:ss
.
*
* @param start the time to start in seconds from epoch
*/
- private static String getDuration(long start) {
+ private String getDuration(long start) {
//Get the end time
long end = Instant.now().getEpochSecond();
//Calculate the time difference
@@ -157,23 +214,47 @@ private static String getDuration(long start) {
//Calculate the amount of hours
long hours = (duration / (60 * 60)) % 24;
//Format the times into a single string and return those
- return String.format("%02d:%02d:%02d", hours, minutes, seconds);
+ return String.format("%03d:%02d:%02d", hours, minutes, seconds);
+ }
+
+ /**
+ * A thread safe function to write data to the given output stream
+ *
+ * @param message the message to log
+ */
+ protected synchronized void log(String message) {
+ if (outputStream == null) {
+ return;
+ }
+ outputStream.println(message);
}
/**
- * As the add function is not thread safe, this synchronised function is
- * used as a wrapper
+ * A thread safe function to add a hash (and the endpoint it was downloaded
+ * from) to the mapping
+ *
+ * @param hash the hash to add
+ * @param endpoint the endpoint where the hash was downloaded from
+ */
+ protected synchronized void addDownloadedHash(String hash, String endpoint) {
+ downloadedSamples.put(hash, endpoint);
+ }
+
+ /**
+ * A thread safe function to add a missing hash to the list
*
* @param missingHash the hash to add
*/
- public static synchronized void addMissingHash(String missingHash) {
+ protected synchronized void addMissingHash(String missingHash) {
missingHashes.add(missingHash);
}
/**
- * Prints the version information, together with an additional newline
+ * Gets the version information, together with an additional newline
+ *
+ * @return the version information
*/
- private static void printVersionInformation() {
- System.out.println("MalPull version 1.2-stable by Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]\n");
+ public String getVersionInformation() {
+ return "MalPull version 1.3-stable by Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]\n";
}
}
diff --git a/src/main/java/malpull/ArgumentHandler.java b/src/main/java/malpull/cli/ArgumentHandler.java
similarity index 89%
rename from src/main/java/malpull/ArgumentHandler.java
rename to src/main/java/malpull/cli/ArgumentHandler.java
index 75b7dbe..4531909 100644
--- a/src/main/java/malpull/ArgumentHandler.java
+++ b/src/main/java/malpull/cli/ArgumentHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,8 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package malpull;
+package malpull.cli;
+import malpull.model.Arguments;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
@@ -24,26 +25,28 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import malpull.exceptions.NoHashesFoundException;
+import malpull.exceptions.NoServicesSetException;
/**
* This class parses the given arguments into an Arguments object, thereby
* simplifying the code in the main function, and splitting the sanity checks
* from the rest of the code
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class ArgumentHandler {
/**
- * Parses the arguments into an Arguments object. Stops MalPull upon the
+ * Parses the arguments into an Arguments object.Stops MalPull upon the
* occurrence of an exception and prints a help message for the user
*
* @param args the arguments to parse
* @return the parsed arguments
+ * @throws NoServicesSetException if none of the services are enabled
+ * @throws NoHashesFoundException if no hashes are provided
*/
- public static Arguments handle(String[] args) {
+ public static Arguments handle(String[] args) throws NoServicesSetException, NoHashesFoundException {
//Only 4 arguments are accepted:
//java -jar malpull.jar threadCount /path/to/keys.txt /path/to/hashes.txt /path/to/write/samples/to
if (args.length != 4) {
@@ -81,7 +84,7 @@ public static Arguments handle(String[] args) {
//Iterate through all keys, note that the endpoint prefix is case insensitive
for (String key : keys) {
if (key.toLowerCase().startsWith("koodous=".toLowerCase())) {
- koodousKey = key.substring("koodous".length(), key.length());
+ koodousKey = key.substring("koodous=".length(), key.length());
} else if (key.toLowerCase().startsWith("malwarebazaar=".toLowerCase())) {
malwareBazaarKey = key.substring("malwarebazaar=".length(), key.length());
} else if (key.toLowerCase().startsWith("malshare=".toLowerCase())) {
@@ -140,7 +143,6 @@ private static List loadFile(String path) {
}
} catch (IOException ex) {
System.out.println("An exception occured when reading " + file.getAbsolutePath() + ":");
- Logger.getLogger(MalPull.class.getName()).log(Level.SEVERE, null, ex);
System.exit(0);
}
return output;
@@ -166,7 +168,7 @@ private static void printUsage() {
System.out.println("This tool downloads samples from MalShare, MalwareBazaar, Koodous, VirusTotal, and Hatching Triage based on given MD-5, SHA-1, or SHA-256 hashes.");
System.out.println("The sample is written to the given output directory. API Keys for any of the used services is required.");
System.out.println("Once all samples are downloaded, the hashes that couldn't be found will be listed.");
- System.out.println("For detailed information on the usage of MalPull, please visit https://maxkersten.nl/wordpress/projects/malpull/#usage");
+ System.out.println("For detailed information on the usage of MalPull, please visit https://maxkersten.nl/projects/malpull/#usage");
System.out.println("");
System.out.println("Sample usage of this program:");
System.out.println("\t\tjava -jar /path/toMalPull.jar threadCount /path/to/keys.txt /path/to/hashes.txt /path/to/write/samples/to");
diff --git a/src/main/java/malpull/cli/MalPullCli.java b/src/main/java/malpull/cli/MalPullCli.java
new file mode 100644
index 0000000..fd57f83
--- /dev/null
+++ b/src/main/java/malpull/cli/MalPullCli.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ *
+ * 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 .
+ */
+package malpull.cli;
+
+import malpull.MalPull;
+import malpull.exceptions.NoArgumentsSetException;
+import malpull.model.Arguments;
+import malpull.exceptions.NoHashesFoundException;
+import malpull.exceptions.NoServicesSetException;
+import malpull.model.MalPullResult;
+
+/**
+ * This class is the command-line interface wrapper for the MalPull object.
+ *
+ * Build using: mvn clean compile assembly:single
+ *
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ */
+public class MalPullCli {
+
+ /**
+ * The main function of the program, providing a CLI to use. The
+ * ArgumentHandler class provides help regarding the CLI arguments that are
+ * required.
+ *
+ * @param args the command-line arguments
+ */
+ public static void main(String[] args) {
+ //Create the MalPull instance, using the standard system output printstream to print the logging
+ MalPull malPull = new MalPull(System.out);
+
+ /**
+ * Parse the arguments into a newly created object. If the arguments
+ * cannot be parsed properly, the error message is displayed and MalPull
+ * shuts down.
+ */
+ try {
+ //Parse the arguments
+ Arguments arguments = ArgumentHandler.handle(args);
+
+ //Show the input to the user, as this helps to avoid mistakes
+ System.out.println("Read " + arguments.getHashes().size() + " hashes");
+ System.out.println("Downloading will be done using " + arguments.getThreadCount() + " thread(s)");
+ System.out.println("Output will be written to: " + arguments.getOutputPath());
+ System.out.println("");
+
+ //Start the downloading, where the progress will be printed to the standard output by the threads
+ MalPullResult result = malPull.download(arguments);
+
+ //Notify the user that all downloads are finished
+ System.out.println("\nAll downloads finished! The sample number count is not always printed in ascending order, as the threads print the messages.");
+
+ //If some hashes could not be found, these are printed
+ if (result.getMissingHashes().size() > 0) {
+ System.out.println("\n\nMissing " + result.getMissingHashes().size() + " hashes:");
+ for (String missingHash : result.getMissingHashes()) {
+ System.out.println(missingHash);
+ }
+ }
+ //Display the time that the download process took
+ System.out.println("\nDownloaded " + result.getDownloadedSamples().size() + " samples in " + result.getTime() + "!");
+ //Exit the program explicitly, as it sometimes remains open in some edge cases
+ System.exit(0);
+ } catch (NoServicesSetException | NoHashesFoundException | NoArgumentsSetException ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/endpoints/GenericEndpoint.java b/src/main/java/malpull/endpoints/GenericEndpoint.java
similarity index 82%
rename from src/main/java/endpoints/GenericEndpoint.java
rename to src/main/java/malpull/endpoints/GenericEndpoint.java
index 0b85f13..0493471 100644
--- a/src/main/java/endpoints/GenericEndpoint.java
+++ b/src/main/java/malpull/endpoints/GenericEndpoint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,22 +14,22 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
-import malpull.Downloader;
+import malpull.network.MalPullConnector;
/**
* This abstract class contains the shared code base for all endpoints, which is
* done to re-use code.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public abstract class GenericEndpoint {
/**
* The downloader wrapper class that is used to make HTTP requests
*/
- protected Downloader downloader;
+ protected MalPullConnector downloader;
/**
* The name of the endpoint
@@ -45,7 +45,7 @@ public GenericEndpoint(String apiBase, String name) {
//Sets the apiBase variable
this.apiBase = apiBase;
//Initialises the downloader class
- downloader = new Downloader();
+ downloader = new MalPullConnector();
//Sets the name variable
this.name = name;
}
diff --git a/src/main/java/endpoints/IEndpoint.java b/src/main/java/malpull/endpoints/IEndpoint.java
similarity index 84%
rename from src/main/java/endpoints/IEndpoint.java
rename to src/main/java/malpull/endpoints/IEndpoint.java
index 5f1cca2..10c5a41 100644
--- a/src/main/java/endpoints/IEndpoint.java
+++ b/src/main/java/malpull/endpoints/IEndpoint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,12 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
/**
* The interface to use for all endpoints that are used
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public interface IEndpoint {
@@ -35,7 +35,7 @@ public interface IEndpoint {
/**
* Gets the name of the endpoint
*
- * @return
+ * @return the name of the endpoint
*/
public String getName();
}
diff --git a/src/main/java/endpoints/Koodous.java b/src/main/java/malpull/endpoints/Koodous.java
similarity index 95%
rename from src/main/java/endpoints/Koodous.java
rename to src/main/java/malpull/endpoints/Koodous.java
index 4ed64f9..0d4017a 100644
--- a/src/main/java/endpoints/Koodous.java
+++ b/src/main/java/malpull/endpoints/Koodous.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,9 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
-import exceptions.SampleNotFoundException;
+import malpull.exceptions.SampleNotFoundException;
import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -24,7 +24,7 @@
/**
* This class is used to get a sample from the Koodous database via its API.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class Koodous extends GenericEndpoint implements IEndpoint {
diff --git a/src/main/java/endpoints/MalShare.java b/src/main/java/malpull/endpoints/MalShare.java
similarity index 92%
rename from src/main/java/endpoints/MalShare.java
rename to src/main/java/malpull/endpoints/MalShare.java
index 5cbc48f..978a58e 100644
--- a/src/main/java/endpoints/MalShare.java
+++ b/src/main/java/malpull/endpoints/MalShare.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,15 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
-import exceptions.SampleNotFoundException;
+import malpull.exceptions.SampleNotFoundException;
import okhttp3.Request;
/**
* This class is used to get a sample from the MalShare database via its API.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class MalShare extends GenericEndpoint implements IEndpoint {
diff --git a/src/main/java/endpoints/MalwareBazaar.java b/src/main/java/malpull/endpoints/MalwareBazaar.java
similarity index 57%
rename from src/main/java/endpoints/MalwareBazaar.java
rename to src/main/java/malpull/endpoints/MalwareBazaar.java
index 9911b33..15eadea 100644
--- a/src/main/java/endpoints/MalwareBazaar.java
+++ b/src/main/java/malpull/endpoints/MalwareBazaar.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,9 +14,18 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
-import exceptions.SampleNotFoundException;
+import malpull.exceptions.SampleNotFoundException;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.io.inputstream.ZipInputStream;
+import net.lingala.zip4j.model.FileHeader;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import org.json.JSONObject;
@@ -25,7 +34,7 @@
* This class is used to get a sample from the MalwareBazaar database via its
* API.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class MalwareBazaar extends GenericEndpoint implements IEndpoint {
@@ -48,14 +57,19 @@ public MalwareBazaar() {
*/
@Override
public byte[] getSample(String hash) throws SampleNotFoundException {
- //Get the SHA-256 hash via the search function of the API, as only SHA-256 hashes can be used when downloading a sample
- String sha256Hash = getSha256Hash(hash);
- //If it cannot be found, there is no such sample
- if (sha256Hash.isEmpty()) {
- throw new SampleNotFoundException("Sample " + hash + " not found on MalwareBazaar!");
+ try {
+ //Get the SHA-256 hash via the search function of the API, as only SHA-256 hashes can be used when downloading a sample
+ String sha256Hash = getSha256Hash(hash);
+ //If it cannot be found, there is no such sample
+ if (sha256Hash.isEmpty()) {
+ throw new SampleNotFoundException("Sample " + hash + " not found on MalwareBazaar!");
+ }
+ //If the sample exists, download it
+ return download(sha256Hash);
+ } catch (IOException e) {
+ throw new SampleNotFoundException("Something went wrong when handling the file in the temporary files folder");
}
- //If the sample exists, download it
- return download(sha256Hash);
+
}
/**
@@ -97,10 +111,12 @@ private String getSha256Hash(String hash) throws SampleNotFoundException {
*
* @param hash the SHA-256 hash of the sample to download
* @return the API's response, which is the raw file
- * @throws HttpConnectionFailed if no connection can be made from the
- * current machine, or to the given host
+ * @throws malpull.exceptions.SampleNotFoundException if the sample cannot be foun
+ * @throws java.io.IOException if the creation or deletion of the file in
+ * the temp folder fails for any reason
*/
- private byte[] download(String hash) throws SampleNotFoundException {
+ public byte[] download(String hash) throws SampleNotFoundException, IOException {
+ String tempPath = System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") + hash;
//Create the request body for the HTTP POST request with the form data
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
@@ -108,7 +124,34 @@ private byte[] download(String hash) throws SampleNotFoundException {
.addFormDataPart("sha256_hash", hash)
.build();
//Return the API's response
- return downloader.post(apiBase, requestBody);
+ byte[] zip = downloader.post(apiBase, requestBody);
+
+ File localFile = new File(tempPath);
+ localFile.getParentFile().mkdirs();
+ Files.write(localFile.toPath(), zip); //overwrites if it exists, not thread safe when downloading the same data, unless a unique path is given
+
+ String password = "infected";
+
+ try {
+ ZipFile zipFile = new ZipFile(localFile);
+ if (zipFile.isEncrypted()) {
+ zipFile.setPassword(password.toCharArray());
+ }
+
+ List headers = zipFile.getFileHeaders();
+ ZipInputStream inputStream = zipFile.getInputStream(headers.get(0));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ int offset = -1;
+ byte[] buff = new byte[1024];
+ while ((offset = inputStream.read(buff)) != -1) {
+ outputStream.write(buff, 0, offset);
+ }
+ localFile.delete();
+ return outputStream.toByteArray();
+ } catch (ZipException e) {
+ throw new IOException("Error whilst handling the ZIP archive:\n" + e.getMessage());
+ }
}
}
diff --git a/src/main/java/endpoints/Triage.java b/src/main/java/malpull/endpoints/Triage.java
similarity index 93%
rename from src/main/java/endpoints/Triage.java
rename to src/main/java/malpull/endpoints/Triage.java
index a01ab1f..3ff5842 100644
--- a/src/main/java/endpoints/Triage.java
+++ b/src/main/java/malpull/endpoints/Triage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2021 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,9 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
-import exceptions.SampleNotFoundException;
+import malpull.exceptions.SampleNotFoundException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@@ -26,7 +26,7 @@
/**
* The class that is used to get a sample from Triage
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class Triage extends GenericEndpoint implements IEndpoint {
diff --git a/src/main/java/endpoints/VirusTotal.java b/src/main/java/malpull/endpoints/VirusTotal.java
similarity index 89%
rename from src/main/java/endpoints/VirusTotal.java
rename to src/main/java/malpull/endpoints/VirusTotal.java
index 7f9f35e..5e19aa6 100644
--- a/src/main/java/endpoints/VirusTotal.java
+++ b/src/main/java/malpull/endpoints/VirusTotal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,15 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package endpoints;
+package malpull.endpoints;
-import exceptions.SampleNotFoundException;
+import malpull.exceptions.SampleNotFoundException;
import okhttp3.Request;
/**
* The class to get a sample from VirusTotal.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class VirusTotal extends GenericEndpoint implements IEndpoint {
diff --git a/src/main/java/malpull/exceptions/NoArgumentsSetException.java b/src/main/java/malpull/exceptions/NoArgumentsSetException.java
new file mode 100644
index 0000000..a678301
--- /dev/null
+++ b/src/main/java/malpull/exceptions/NoArgumentsSetException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ *
+ * 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 .
+ */
+package malpull.exceptions;
+
+/**
+ * This exception is thrown when the arguments object is not set prior to the
+ * call a function that requires it.
+ *
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ */
+public class NoArgumentsSetException extends Exception {
+
+ public NoArgumentsSetException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/malpull/exceptions/NoHashesFoundException.java b/src/main/java/malpull/exceptions/NoHashesFoundException.java
new file mode 100644
index 0000000..9086229
--- /dev/null
+++ b/src/main/java/malpull/exceptions/NoHashesFoundException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ *
+ * 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 .
+ */
+package malpull.exceptions;
+
+/**
+ * This exception is thrown if no hashes are provided
+ *
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ */
+public class NoHashesFoundException extends Exception {
+
+ public NoHashesFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/malpull/exceptions/NoServicesSetException.java b/src/main/java/malpull/exceptions/NoServicesSetException.java
new file mode 100644
index 0000000..d117e6a
--- /dev/null
+++ b/src/main/java/malpull/exceptions/NoServicesSetException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ *
+ * 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 .
+ */
+package malpull.exceptions;
+
+/**
+ * This exception is thrown if no services are set to use within MalPull
+ *
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ */
+public class NoServicesSetException extends Exception {
+
+ public NoServicesSetException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/exceptions/SampleNotFoundException.java b/src/main/java/malpull/exceptions/SampleNotFoundException.java
similarity index 83%
rename from src/main/java/exceptions/SampleNotFoundException.java
rename to src/main/java/malpull/exceptions/SampleNotFoundException.java
index 07c00fe..bf467d1 100644
--- a/src/main/java/exceptions/SampleNotFoundException.java
+++ b/src/main/java/malpull/exceptions/SampleNotFoundException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,13 +14,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package exceptions;
+package malpull.exceptions;
/**
* This exception is used when the sample cannot be found in the malware
* service's database
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class SampleNotFoundException extends Exception {
diff --git a/src/main/java/malpull/Arguments.java b/src/main/java/malpull/model/Arguments.java
similarity index 61%
rename from src/main/java/malpull/Arguments.java
rename to src/main/java/malpull/model/Arguments.java
index f5008a3..05990d6 100644
--- a/src/main/java/malpull/Arguments.java
+++ b/src/main/java/malpull/model/Arguments.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,15 +14,17 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package malpull;
+package malpull.model;
import java.util.Set;
+import malpull.exceptions.NoHashesFoundException;
+import malpull.exceptions.NoServicesSetException;
/**
- * The argument class, which contains all relevant information from the parsed
- * arguments
+ * The argument class, which contains all relevant information that MalPull
+ * requires
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
public class Arguments {
@@ -30,32 +32,33 @@ public class Arguments {
* The set that contains all loaded hashes
*/
private Set hashes;
-
+
/**
* The folder to write all samples to
*/
private String outputPath;
-
+
/**
* The amount of threads to use, minimally one
*/
private int threadCount;
-
+
/**
* The API key for Koodous
*/
private String koodousKey;
-
+
/**
- * The API key for Malware Bazaar is not required, meaning any value will enable downloading from this service
+ * The API key for Malware Bazaar is not required, meaning any value will
+ * enable downloading from this service
*/
private String malwareBazaarKey;
-
+
/**
* The API key for MalShare
*/
private String malShareKey;
-
+
/**
* The API key for VirusTotal
*/
@@ -68,16 +71,27 @@ public class Arguments {
/**
* Creates an object that contains all parsed arguments
- * @param hashes all loaded hashes
+ *
+ * @param hashes all loaded hashes, cannot be null or empty
* @param outputPath the folder to write the downloads to
- * @param threadCount the amount of threads to use
- * @param koodousKey the API key for Koodous
- * @param malwareBazaarKey the API key for Malware Bazaar
- * @param malShareKey the API key for MalShare
- * @param virusTotalKey the API key for VirusTotal
- * @param triageKey the API key for Triage
- */
- public Arguments(Set hashes, String outputPath, int threadCount, String koodousKey, String malwareBazaarKey, String malShareKey, String virusTotalKey, String triageKey) {
+ * @param threadCount the amount of threads to use, a value of zero or lower
+ * will set the count to 1
+ * @param koodousKey the API key for Koodous, null if the service is not to
+ * be used
+ * @param malwareBazaarKey the API key for Malware Bazaar, null if the
+ * service is not to be used
+ * @param malShareKey the API key for MalShare, null if the service is not
+ * to be used
+ * @param virusTotalKey the API key for VirusTotal, null if the service is
+ * not to be used
+ * @param triageKey the API key for Triage, null if the service is not to be
+ * used
+ * @throws NoServicesSetException if all of the API key strings are null at
+ * the same time, meaning no services will be reached
+ * @throws NoHashesFoundException if the given set of hashes is null or
+ * empty
+ */
+ public Arguments(Set hashes, String outputPath, int threadCount, String koodousKey, String malwareBazaarKey, String malShareKey, String virusTotalKey, String triageKey) throws NoServicesSetException, NoHashesFoundException {
this.hashes = hashes;
this.outputPath = outputPath;
this.threadCount = threadCount;
@@ -86,10 +100,27 @@ public Arguments(Set hashes, String outputPath, int threadCount, String
this.malShareKey = malShareKey;
this.virusTotalKey = virusTotalKey;
this.triageKey = triageKey;
+
+ if (koodousKey == null
+ && malwareBazaarKey == null
+ && malShareKey == null
+ && virusTotalKey == null
+ && triageKey == null) {
+ throw new NoServicesSetException("No services have been set in the arguments object, as all API keys are null!");
+ }
+
+ if (hashes == null || hashes.isEmpty()) {
+ throw new NoHashesFoundException("No hashes are provided when creating the arguments object, given that the list is either null or empty!");
+ }
+
+ if (this.threadCount <= 0) {
+ this.threadCount = 1;
+ }
}
/**
* Gets all loaded hashes without duplicates
+ *
* @return all loaded hashes
*/
public Set getHashes() {
@@ -98,6 +129,7 @@ public Set getHashes() {
/**
* Get the output folder where the downloads should be written to
+ *
* @return the path to the output folder
*/
public String getOutputPath() {
@@ -106,6 +138,7 @@ public String getOutputPath() {
/**
* Get the given thread count to use when downloading samples
+ *
* @return the thread count
*/
public int getThreadCount() {
@@ -114,6 +147,7 @@ public int getThreadCount() {
/**
* Get the API key of Koodous, can be null if the API key is not used
+ *
* @return the API key
*/
public String getKoodousKey() {
@@ -121,7 +155,10 @@ public String getKoodousKey() {
}
/**
- * The API key of Malware Bazaar does not exist, so this value is either null or not null. Exclusion from the keys file means the service wont be used
+ * The API key of Malware Bazaar does not exist, so this value is either
+ * null or not null. Exclusion from the keys file means the service wont be
+ * used
+ *
* @return a value to see if this endpoint is to be used
*/
public String getMalwareBazaarKey() {
@@ -130,6 +167,7 @@ public String getMalwareBazaarKey() {
/**
* Gets the API key of MalShare, can be null if the API key is not used
+ *
* @return the API key
*/
public String getMalShareKey() {
@@ -138,6 +176,7 @@ public String getMalShareKey() {
/**
* The API key of VirusTotal, can be null if the API key is not used
+ *
* @return the API key
*/
public String getVirusTotalKey() {
@@ -146,6 +185,7 @@ public String getVirusTotalKey() {
/**
* The API key of Triage, can be null if the API key is not used
+ *
* @return the API key
*/
public String getTriageKey() {
diff --git a/src/main/java/malpull/model/MalPullResult.java b/src/main/java/malpull/model/MalPullResult.java
new file mode 100644
index 0000000..a6a2be5
--- /dev/null
+++ b/src/main/java/malpull/model/MalPullResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ *
+ * 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 .
+ */
+package malpull.model;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class to contain all download related results, which is returned once all
+ * downloads have been completed
+ *
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
+ */
+public class MalPullResult {
+
+ private Map downloadedSamples;
+ private List missingHashes;
+ private String time;
+
+ public MalPullResult(Map downloadedSamples, List missingHashes, String time) {
+ this.downloadedSamples = downloadedSamples;
+ this.missingHashes = missingHashes;
+ this.time = time;
+ }
+
+ public Map getDownloadedSamples() {
+ return downloadedSamples;
+ }
+
+ public List getMissingHashes() {
+ return missingHashes;
+ }
+
+ public String getTime() {
+ return time;
+ }
+}
diff --git a/src/main/java/malpull/Downloader.java b/src/main/java/malpull/network/MalPullConnector.java
similarity index 87%
rename from src/main/java/malpull/Downloader.java
rename to src/main/java/malpull/network/MalPullConnector.java
index 0d30bac..7fd00a6 100644
--- a/src/main/java/malpull/Downloader.java
+++ b/src/main/java/malpull/network/MalPullConnector.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * Copyright (C) 2020 Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*
* 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
@@ -14,9 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package malpull;
+package malpull.network;
-import exceptions.SampleNotFoundException;
+import malpull.exceptions.SampleNotFoundException;
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@@ -30,9 +30,9 @@
* that contact the API endpoints can contain the logic for the API, and this
* code can be reused within all services.
*
- * @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
+ * @author Max 'Libra' Kersten [@Libranalysis, https://maxkersten.nl]
*/
-public class Downloader {
+public class MalPullConnector {
/**
* The HTTP client that sends out the request
@@ -40,10 +40,10 @@ public class Downloader {
protected final OkHttpClient httpClient;
/**
- * Creates the Downloader object, via which GET and POST requests can be
+ * Creates the connector object, via which GET and POST requests can be
* sent to a given address
*/
- public Downloader() {
+ public MalPullConnector() {
httpClient = new OkHttpClient();
}
@@ -73,7 +73,7 @@ public byte[] post(String url, RequestBody requestBody) throws SampleNotFoundExc
//Return response body
return response.body().bytes();
} catch (IOException e) {
- throw new SampleNotFoundException("IOException in Dowlnoader.post for " + request.url());
+ throw new SampleNotFoundException("IOException in Downloader.post for " + request.url() + ". Error:\n" + e.getMessage());
}
}
@@ -96,7 +96,7 @@ public byte[] get(Request request) throws SampleNotFoundException {
//Return response body
return response.body().bytes();
} catch (IOException e) {
- throw new SampleNotFoundException("IOException in Dowlnoader.get for " + request.url());
+ throw new SampleNotFoundException("IOException in Dowlnoader.get for " + request.url() + ". Error:\n" + e.getMessage());
}
}
}
diff --git a/target/MalPull-1.2.1-stable.jar b/target/MalPull-1.2.1-stable.jar
deleted file mode 100644
index 63458e0..0000000
Binary files a/target/MalPull-1.2.1-stable.jar and /dev/null differ
diff --git a/target/MalPull-1.2.1-stable-jar-with-dependencies.jar b/target/MalPull-1.3-stable-jar-with-dependencies.jar
similarity index 87%
rename from target/MalPull-1.2.1-stable-jar-with-dependencies.jar
rename to target/MalPull-1.3-stable-jar-with-dependencies.jar
index 5e18b99..e8f9bd6 100644
Binary files a/target/MalPull-1.2.1-stable-jar-with-dependencies.jar and b/target/MalPull-1.3-stable-jar-with-dependencies.jar differ
diff --git a/target/MalPull-1.3-stable-javadoc.jar b/target/MalPull-1.3-stable-javadoc.jar
new file mode 100644
index 0000000..f5c94b3
Binary files /dev/null and b/target/MalPull-1.3-stable-javadoc.jar differ
diff --git a/target/MalPull-1.3-stable-sources.jar b/target/MalPull-1.3-stable-sources.jar
new file mode 100644
index 0000000..257dce1
Binary files /dev/null and b/target/MalPull-1.3-stable-sources.jar differ
diff --git a/target/MalPull-1.3-stable.jar b/target/MalPull-1.3-stable.jar
new file mode 100644
index 0000000..9b93462
Binary files /dev/null and b/target/MalPull-1.3-stable.jar differ
diff --git a/target/apidocs.zip b/target/apidocs.zip
new file mode 100644
index 0000000..d55532e
Binary files /dev/null and b/target/apidocs.zip differ