Skip to content

Commit

Permalink
Version 1.1-stable
Browse files Browse the repository at this point in the history
  • Loading branch information
ThisIsLibra committed Jul 15, 2020
1 parent 92a066c commit 213e00b
Show file tree
Hide file tree
Showing 16 changed files with 689 additions and 295 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>malwarepuller</groupId>
<artifactId>MalPull</artifactId>
<version>1.0-stable</version>
<version>1.1-stable</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
149 changes: 149 additions & 0 deletions src/main/java/concurrency/DownloadWorker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, 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 <http://www.gnu.org/licenses/>.
*/
package concurrency;

import endpoints.*;
import exceptions.SampleNotFoundException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import malpull.MalPull;

/**
* A worker class that extends the runnable interface, meaning it can be
* executed as a thread. This class tries to download the given sample from any
* 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]
*/
public class DownloadWorker implements Runnable {

/**
* The list of endpoints iterate through in an attempt to download the hash
*/
private List<IEndpoint> endpoints;

/**
* The path to write the file to if it is found
*/
private String path;

/**
* The hash of the sample to download
*/
private String hash;

/**
* The number of this worker in the queue, used to print if the sample can
* be downloaded to display the progress to the user
*/
private int count;

/**
* The total amount of samples that are in the queue, used to display the
* progress to the user
*/
private int total;

/**
* Creates a worker object, which can be queued for the thread pool
*
* @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
* @param count the queue number of this worker, remains unchanged after
* creation
* @param total the total number of samples to be downloaded
*/
public DownloadWorker(List<IEndpoint> endpoints, String path, String hash, int count, int total) {
this.endpoints = endpoints;
this.path = path;
this.hash = hash;
this.count = count;
this.total = total;
}

/**
* Downloads the sample to the given location. If the sample cannot be
* found, the hash is added to the list of missing hashes, which is printed
* at the end.
*/
@Override
public void run() {
try {
//Add the hash to the file name
String filePath = path + File.separator + hash;
//The boolean to check if the sample has been downloaded
boolean isDownloaded = false;
//Iterate through all the endpoints
for (IEndpoint endpoint : endpoints) {
//Try to dowload the file
try {
/**
* If it is already downloaded, the iteration loop should be
* broken to avoid more API calls than are required, and to
* move on to the next worker in the queue
*/
if (isDownloaded) {
break;
}
//Get the sample from the endpoint
byte[] output = endpoint.getSample(hash);
//If the output is not zero bytes
if (output.length > 0) {
//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 + ")");
//The boolean is set to true, causing the next iteration to break out of the loop
isDownloaded = true;
}
} catch (SampleNotFoundException e) {
/**
* 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);
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}

/**
* Writes the given byte array to the disk to the given location
*
* @param output the data to write to the disk
* @param path the location to write the given data to
*/
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!");
}
}
}
18 changes: 17 additions & 1 deletion src/main/java/endpoints/GenericEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,31 @@ public abstract class GenericEndpoint {
*/
protected Downloader downloader;

/**
* The name of the endpoint
*/
public String name;

/**
* The base URL of the API, to which specific API actions can be appended
*/
protected String apiBase;

public GenericEndpoint(String apiBase) {
public GenericEndpoint(String apiBase, String name) {
//Sets the apiBase variable
this.apiBase = apiBase;
//Initialises the downloader class
downloader = new Downloader();
//Sets the name variable
this.name = name;
}

/**
* Gets the endpoint name
*
* @return the name of the endpoint
*/
public String getName() {
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,27 @@
* 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 exceptions;
package endpoints;

/**
* This exception is used when the HTTP request returns a 404 (Not Found) status
* code
*
* @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
*/
public class Error404NotFoundException extends Exception {
public interface IEndpoint {

public Error404NotFoundException() {
/**
* Gets the sample from the endpoint, based on the given hash
*
* @param hash the hash of the file to download
* @return a byte[] that contains the file's data
* @throws Exception in case any error occurs
*/
public byte[] getSample(String hash) throws Exception;

}
/**
* Gets the name of the endpoint
*
* @return
*/
public String getName();
}
21 changes: 7 additions & 14 deletions src/main/java/endpoints/Koodous.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
*/
package endpoints;

import exceptions.Error404NotFoundException;
import exceptions.Error429TooManyRequestsException;
import exceptions.HttpConnectionFailed;
import exceptions.SampleNotFoundException;
import okhttp3.Request;
import org.json.JSONArray;
Expand All @@ -29,7 +26,7 @@
*
* @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl]
*/
public class Koodous extends GenericEndpoint {
public class Koodous extends GenericEndpoint implements IEndpoint {

/**
* The API key that is used to interact with the MalShare API
Expand All @@ -43,7 +40,7 @@ public class Koodous extends GenericEndpoint {
*/
public Koodous(String key) {
//Sets the apiBase variable in the abstract GenericEndpoint class
super("https://koodous.com/api/apks");
super("https://koodous.com/api/apks", "Koodous");
//Sets the key variable
this.key = key;
}
Expand All @@ -54,14 +51,10 @@ public Koodous(String key) {
*
* @param hash the hash to look for
* @return the sample as a byte array
* @throws HttpConnectionFailed if no connection can be made from the
* current machine, or to the given host
* @throws SampleNotFoundException if the sample cannot be found
* @throws Error404NotFoundException if the target returns a 404 status code
* @throws Error429TooManyRequestsException if the target returns a 429
* status code
*/
public byte[] getSample(String hash) throws HttpConnectionFailed, SampleNotFoundException, Error404NotFoundException, Error429TooManyRequestsException {
@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);
//Return the API's response
Expand All @@ -81,7 +74,7 @@ public byte[] getSample(String hash) throws HttpConnectionFailed, SampleNotFound
* @throws Error404NotFoundException if the target returns a 404 status code
* @throws Error429TooManyRequestsException if the target returns a 429
*/
private String getSha256Hash(String hash) throws HttpConnectionFailed, SampleNotFoundException, Error404NotFoundException, Error429TooManyRequestsException {
private String getSha256Hash(String hash) throws SampleNotFoundException {
//Create the url
String url = apiBase + "?search=" + hash + "&page=%1&page_size=%100";
//Create the requested, based on the given URL and with the required header token
Expand All @@ -97,7 +90,7 @@ private String getSha256Hash(String hash) throws HttpConnectionFailed, SampleNot
int count = jsonObject.optInt("count", 0);
if (count == 0) {
//If there are no hits, the sample is not present
throw new SampleNotFoundException();
throw new SampleNotFoundException("Sample " + hash + " not found on Koodous!");
}
//Get the results if there are any
JSONArray results = jsonObject.getJSONArray("results");
Expand All @@ -115,7 +108,7 @@ private String getSha256Hash(String hash) throws HttpConnectionFailed, SampleNot
* @throws Error404NotFoundException if the target returns a 404 status code
* @throws Error429TooManyRequestsException if the target returns a 429
*/
private byte[] download(String hash) throws HttpConnectionFailed, Error404NotFoundException, Error429TooManyRequestsException {
private byte[] download(String hash) throws SampleNotFoundException {
//Create the URL
String url = apiBase + "/" + hash + "/download";
//Prepare the request with teh API token
Expand Down
24 changes: 5 additions & 19 deletions src/main/java/endpoints/MalShare.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,15 @@
*/
package endpoints;

import exceptions.Error404NotFoundException;
import exceptions.Error429TooManyRequestsException;
import exceptions.SampleNotFoundException;
import exceptions.HttpConnectionFailed;
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]
*/
public class MalShare extends GenericEndpoint {

/**
* The API key that is used to interact with the MalShare API
*/
private String key;
public class MalShare extends GenericEndpoint implements IEndpoint {

/**
* Creates an object to interact with the MalShare API
Expand All @@ -41,9 +33,7 @@ public class MalShare extends GenericEndpoint {
*/
public MalShare(String key) {
//Sets the apiBase variable in the abstract GenericEndpoint class
super("https://malshare.com/api.php?api_key=" + key + "&action=");
//Sets the key variable
this.key = key;
super("https://malshare.com/api.php?api_key=" + key + "&action=", "MalShare");
}

/**
Expand All @@ -64,14 +54,10 @@ private String getDownloadUrl(String hash) {
*
* @param hash the hash to look for
* @return the sample as a byte array
* @throws HttpConnectionFailed if no connection can be made from the
* current machine, or to the given host
* @throws SampleNotFoundException if the sample cannot be found
* @throws Error404NotFoundException if the target returns a 404 status code
* @throws Error429TooManyRequestsException if the target returns a 429
* status code
*/
public byte[] getSample(String hash) throws HttpConnectionFailed, SampleNotFoundException, Error404NotFoundException, Error429TooManyRequestsException {
@Override
public byte[] getSample(String hash) throws SampleNotFoundException {
//Gets the URL
String url = getDownloadUrl(hash);
//Create the request based on the URL
Expand All @@ -90,7 +76,7 @@ public byte[] getSample(String hash) throws HttpConnectionFailed, SampleNotFound
*/
if (temp.contains("Sample not found by hash")) {
//If the sample cannot be found, an exception is thrown
throw new SampleNotFoundException();
throw new SampleNotFoundException("Sample " + hash + " not found on MalShare!");
}
//Return the sample
return result;
Expand Down
Loading

0 comments on commit 213e00b

Please sign in to comment.