Lots of refractoring

This commit is contained in:
Tad 2017-12-16 06:15:17 -05:00
parent b8c68528f6
commit 4dfca28d22
6 changed files with 185 additions and 193 deletions

2
.idea/misc.xml generated
View file

@ -24,7 +24,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -20,7 +20,6 @@
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

View file

@ -2,6 +2,7 @@ package us.spotco.malwarescanner;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;
import java.io.BufferedReader;
@ -36,8 +37,12 @@ public class Database {
//signatureDatabases.add(new SignatureDatabase("http://cdn.malware.expert/malware.expert.hdb", "malware.expert.hdb"));
}
public static boolean doesDatabaseExist() {
return databasePath.listFiles().length > 0;
public static boolean isDatabaseLoaded() {
return signaturesMD5.size() > 0 && signaturesSHA1.size() > 0 && signaturesSHA256.size() > 0;
}
public static int getSignatureCount() {
return signaturesMD5.size() + signaturesSHA1.size() + signaturesSHA256.size();
}
public static void updateDatabase(ArrayList<SignatureDatabase> signatureDatabases) {
@ -46,7 +51,9 @@ public class Database {
}
}
public static void loadDatabase(ArrayList<SignatureDatabase> signatureDatabases) {
public static void loadDatabase(boolean ignoreifLoaded, ArrayList<SignatureDatabase> signatureDatabases) {
if (!isDatabaseLoaded() || !ignoreifLoaded && isDatabaseLoaded()) {
Log.d("Theia", "Loading database");
signaturesMD5.clear();
signaturesSHA1.clear();
signaturesSHA256.clear();
@ -87,6 +94,7 @@ public class Database {
}
System.gc();
}
}
public static class Downloader extends AsyncTask<String, String, String> {

View file

@ -1,10 +1,12 @@
package us.spotco.malwarescanner;
import android.Manifest;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
@ -16,6 +18,10 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public class MainActivity extends AppCompatActivity {
private boolean scanSystem = false;
@ -42,16 +48,31 @@ public class MainActivity extends AppCompatActivity {
logView.append("\nDisclaimer: This is an extremely basic signature scanner\n\n");
final Database database = new Database(this, logView);
final MalwareScanner scanner = new MalwareScanner(this, logView);
final MalwareScanner malwareScanner = new MalwareScanner(this, this, true);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!scanner.isScannerRunning()) {
MalwareScanner.startScanner(scanSystem, scanApps, scanInternal, scanExternal);
if (!malwareScanner.getStatus().equals(AsyncTask.Status.RUNNING)) {
Set<File> filesToScan = new HashSet<>();
if (scanSystem) {
filesToScan.addAll(Utils.getFilesRecursive(Environment.getRootDirectory()));
}
if (scanApps) {
for (ApplicationInfo packageInfo : getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA)) {
filesToScan.add(new File(packageInfo.sourceDir));
}
}
if (scanInternal) {
filesToScan.addAll(Utils.getFilesRecursive(Environment.getExternalStorageDirectory()));
}
if (scanExternal) {
filesToScan.addAll(Utils.getFilesRecursive(new File("/storage")));
}
malwareScanner.execute(filesToScan);
} else {
scanner.stopScanner();
malwareScanner.cancel(true);
}
}
});
@ -76,6 +97,9 @@ public class MainActivity extends AppCompatActivity {
switch (item.getItemId()) {
case R.id.mnuUpdateDatabase:
Database.updateDatabase(Database.signatureDatabases);
if (Database.isDatabaseLoaded()) {
Database.loadDatabase(false, Database.signatureDatabases);
}
break;
case R.id.mnuScanSystem:
scanSystem = !item.isChecked();

View file

@ -1,11 +1,11 @@
package us.spotco.malwarescanner;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.widget.TextView;
import java.io.File;
@ -14,173 +14,102 @@ import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class MalwareScanner {
public class MalwareScanner extends AsyncTask<Set<File>, Object, String> {
private static Context context = null;
private static TextView log = null;
private static AsyncTask<Boolean, String, String> malwareScannerTask = null;
private Context context = null;
private TextView logOutput = null;
private boolean userFacing = false;
private NotificationManager notificationManager = null;
private long scanTime = 0;
private HashMap<String, File> fileHashesMD5 = new HashMap<>();
private HashMap<String, File> fileHashesSHA1 = new HashMap<>();
private HashMap<String, File> fileHashesSHA256 = new HashMap<>();
private static final int MAX_FILE_SIZE = (1000 * 1000) * 50; //50MB
private static long scanTime = 0;
private static HashMap<String, File> fileHashesMD5 = new HashMap<>();
private static HashMap<String, File> fileHashesSHA1 = new HashMap<>();
private static HashMap<String, File> fileHashesSHA256 = new HashMap<>();
public MalwareScanner(Context context, TextView log) {
MalwareScanner.context = context;
MalwareScanner.log = log;
}
public static void startScanner(boolean scanSystem, boolean scanApps, boolean scanInternal, boolean scanExternal) {
if (Database.doesDatabaseExist()) {
malwareScannerTask = new MalwareScannerTask().execute(scanSystem, scanApps, scanInternal, scanExternal);
public MalwareScanner(Activity activity, Context context, boolean userFacing) {
this.context = context;
this.userFacing = userFacing;
if (activity != null) {
logOutput = activity.findViewById(R.id.txtLogOutput);
} else {
log.append("No database found... download one first!\n");
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
}
public void stopScanner() {
malwareScannerTask.cancel(true);
private void logResult(String result, boolean userFacingOnly) {
if (userFacing) {
logOutput.append(result + "\n");
} else if (!userFacingOnly) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Malware Detected!")
.setContentText(result);
notificationManager.notify(1, mBuilder.build());
}
public boolean isScannerRunning() {
return malwareScannerTask != null && (malwareScannerTask.getStatus().equals(AsyncTask.Status.PENDING) || malwareScannerTask.getStatus().equals(AsyncTask.Status.RUNNING));
}
public static class MalwareScannerTask extends AsyncTask<Boolean, String, String> {
@Override
protected void onPreExecute() {
scanTime = SystemClock.elapsedRealtime();
log.append("Initiating scan...\n");
logResult("Starting scan...", true);
}
@Override
protected String doInBackground(Set<File>[] filesToScan) {
//Pre
fileHashesMD5.clear();
fileHashesSHA1.clear();
fileHashesSHA256.clear();
}
@Override
protected void onCancelled() {
log.append("Scan cancelled\n");
}
publishProgress(filesToScan[0].size() + " files pending scan\n", true);
@Override
protected String doInBackground(Boolean... booleans) {
boolean scanSystem = booleans[0];
boolean scanApps = booleans[1];
boolean scanInternal = booleans[2];
boolean scanExternal = booleans[3];
Database.loadDatabase(true, Database.signatureDatabases);
publishProgress("Loaded database with " + Database.getSignatureCount() + " signatures\n", true);
Set<File> filesToScan = new HashSet<>();
if (scanSystem) {
filesToScan.addAll(getFilesRecursive(Environment.getRootDirectory()));
}
if (scanApps) {
for (ApplicationInfo packageInfo : context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA)) {
filesToScan.add(new File(packageInfo.sourceDir));
}
}
if (scanInternal) {
filesToScan.addAll(getFilesRecursive(Environment.getExternalStorageDirectory()));
}
if (scanExternal) {
filesToScan.addAll(getFilesRecursive(new File("/storage")));
}
publishProgress(filesToScan.size() + " files pending scan\n");
Database.loadDatabase(Database.signatureDatabases);
publishProgress("Loaded " + Database.signaturesMD5.size() + " MD5 signatures");
publishProgress("Loaded " + Database.signaturesSHA1.size() + " SHA-1 signatures");
publishProgress("Loaded " + Database.signaturesSHA256.size() + " SHA-256 signatures\n");
publishProgress("Hashing files...");
for (File file : filesToScan) {
//Get file hashes
publishProgress("Hashing files...", true);
for (File file : filesToScan[0]) {
getFileHashes(file);
}
publishProgress("Calculated MD5/SHA-1/SHA-256 hashes for all files\n");
publishProgress("Calculated MD5/SHA-1/SHA-256 hashes for all files\n", true);
if (Database.signaturesMD5.size() > 0) {
for (Map.Entry<String, File> file : fileHashesMD5.entrySet()) {
if (Database.signaturesMD5.containsKey(file.getKey())) {
String result = Database.signaturesMD5.get(file.getKey());
publishProgress(file.getValue() + " detected as " + result);
}
}
publishProgress("Checked all MD5 hashes against signature databases\n");
} else {
publishProgress("No MD5 signatures available\n");
}
if (Database.signaturesSHA1.size() > 0) {
for (Map.Entry<String, File> file : fileHashesSHA1.entrySet()) {
if (Database.signaturesSHA1.containsKey(file.getKey())) {
String result = Database.signaturesSHA1.get(file.getKey());
publishProgress(file.getValue() + " detected as " + result);
}
}
publishProgress("Checked all SHA-1 hashes against signature databases\n");
} else {
publishProgress("No SHA-1 signatures available\n");
}
if (Database.signaturesSHA256.size() > 0) {
for (Map.Entry<String, File> file : fileHashesSHA256.entrySet()) {
if (Database.signaturesSHA256.containsKey(file.getKey())) {
String result = Database.signaturesSHA256.get(file.getKey());
publishProgress(file.getValue() + " detected as " + result);
}
}
publishProgress("Checked all SHA-256 hashes against signature databases\n");
} else {
publishProgress("No SHA-256 signatures available\n");
}
//Check the hashes
checkSignature("MD5", fileHashesMD5, Database.signaturesMD5);
checkSignature("SHA-1", fileHashesSHA1, Database.signaturesSHA1);
checkSignature("SHA-256", fileHashesSHA256, Database.signaturesSHA256);
//Post
fileHashesMD5.clear();
fileHashesSHA1.clear();
fileHashesSHA256.clear();
System.gc();
publishProgress("Scan completed in " + ((SystemClock.elapsedRealtime() - scanTime) / 1000) + " seconds!\n\n\n\n", true);
return null;
}
@Override
protected void onProgressUpdate(String... progress) {
log.append(progress[0] + "\n");
protected void onProgressUpdate(Object... objects) {
logResult((String) objects[0], (boolean) objects[1]);
}
@Override
protected void onPostExecute(String result) {
log.append("Scan completed in " + ((SystemClock.elapsedRealtime() - scanTime) / 1000) + " seconds!\n\n\n\n\n");
private void checkSignature(String hashType, HashMap<String, File> signaturesToCheck, HashMap<String, String> signatureDatabase) {
if (signatureDatabase.size() > 0) {
for (Map.Entry<String, File> file : signaturesToCheck.entrySet()) {
if (signatureDatabase.containsKey(file.getKey())) {
String result = signatureDatabase.get(file.getKey());
publishProgress(result + " in " + file.getValue(), false);
}
}
private static Set<File> getFilesRecursive(File root) {
Set<File> filesAll = new HashSet<>();
File[] files = root.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isDirectory()) {
Set<File> filesTmp = getFilesRecursive(f);
if (filesTmp != null) {
filesAll.addAll(filesTmp);
}
publishProgress("Checked all " + hashType + " hashes against signature databases\n", true);
} else {
if(f.length() <= MAX_FILE_SIZE) {//Exclude files larger than 50MB for performance
filesAll.add(f);
}
}
publishProgress("No " + hashType + " signatures available\n", true);
}
}
return filesAll;
}
private static void getFileHashes(File file) {
private void getFileHashes(File file) {
try {
InputStream fis = new FileInputStream(file);

View file

@ -0,0 +1,32 @@
package us.spotco.malwarescanner;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public class Utils {
private static final int MAX_FILE_SIZE = (1000 * 1000) * 50; //50MB
public static Set<File> getFilesRecursive(File root) {
Set<File> filesAll = new HashSet<>();
File[] files = root.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isDirectory()) {
Set<File> filesTmp = getFilesRecursive(f);
if (filesTmp != null) {
filesAll.addAll(filesTmp);
}
} else {
if (f.length() <= MAX_FILE_SIZE) {//Exclude files larger than 50MB for performance
filesAll.add(f);
}
}
}
}
return filesAll;
}
}