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> </value>
</option> </option>
</component> </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" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View file

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

View file

@ -2,6 +2,7 @@ package us.spotco.malwarescanner;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView; import android.widget.TextView;
import java.io.BufferedReader; 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")); //signatureDatabases.add(new SignatureDatabase("http://cdn.malware.expert/malware.expert.hdb", "malware.expert.hdb"));
} }
public static boolean doesDatabaseExist() { public static boolean isDatabaseLoaded() {
return databasePath.listFiles().length > 0; 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) { public static void updateDatabase(ArrayList<SignatureDatabase> signatureDatabases) {
@ -46,46 +51,49 @@ public class Database {
} }
} }
public static void loadDatabase(ArrayList<SignatureDatabase> signatureDatabases) { public static void loadDatabase(boolean ignoreifLoaded, ArrayList<SignatureDatabase> signatureDatabases) {
signaturesMD5.clear(); if (!isDatabaseLoaded() || !ignoreifLoaded && isDatabaseLoaded()) {
signaturesSHA1.clear(); Log.d("Theia", "Loading database");
signaturesSHA256.clear(); signaturesMD5.clear();
System.gc(); signaturesSHA1.clear();
for (SignatureDatabase database : signatureDatabases) { signaturesSHA256.clear();
File databaseLocation = new File(databasePath + "/" + database.getName()); System.gc();
if (databaseLocation.exists()) { for (SignatureDatabase database : signatureDatabases) {
if (database.getName().contains(".hdb")) {//.hdb format: md5, size, name File databaseLocation = new File(databasePath + "/" + database.getName());
try { if (databaseLocation.exists()) {
BufferedReader reader = new BufferedReader(new FileReader(databaseLocation)); if (database.getName().contains(".hdb")) {//.hdb format: md5, size, name
String line; try {
while ((line = reader.readLine()) != null) { BufferedReader reader = new BufferedReader(new FileReader(databaseLocation));
String[] lineS = line.split(":"); String line;
signaturesMD5.put(lineS[0], lineS[2]); while ((line = reader.readLine()) != null) {
} String[] lineS = line.split(":");
reader.close(); signaturesMD5.put(lineS[0], lineS[2]);
} catch (Exception e) {
e.printStackTrace();
}
} else if (database.getName().contains(".hsb")) {//.hsb format: sha256, size, name
try {
BufferedReader reader = new BufferedReader(new FileReader(databaseLocation));
String line;
while ((line = reader.readLine()) != null) {
String[] lineS = line.split(":");
if (lineS[0].length() == 32) {
signaturesSHA1.put(lineS[0], lineS[2]);
} else {
signaturesSHA256.put(lineS[0], lineS[2]);
} }
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
} else if (database.getName().contains(".hsb")) {//.hsb format: sha256, size, name
try {
BufferedReader reader = new BufferedReader(new FileReader(databaseLocation));
String line;
while ((line = reader.readLine()) != null) {
String[] lineS = line.split(":");
if (lineS[0].length() == 32) {
signaturesSHA1.put(lineS[0], lineS[2]);
} else {
signaturesSHA256.put(lineS[0], lineS[2]);
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
} }
reader.close();
} catch (Exception e) {
e.printStackTrace();
} }
} }
} }
System.gc();
} }
System.gc();
} }
public static class Downloader extends AsyncTask<String, String, String> { public static class Downloader extends AsyncTask<String, String, String> {

View file

@ -1,10 +1,12 @@
package us.spotco.malwarescanner; package us.spotco.malwarescanner;
import android.Manifest; import android.Manifest;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
@ -16,6 +18,10 @@ import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private boolean scanSystem = false; 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"); logView.append("\nDisclaimer: This is an extremely basic signature scanner\n\n");
final Database database = new Database(this, logView); 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); FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() { fab.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (!scanner.isScannerRunning()) { if (!malwareScanner.getStatus().equals(AsyncTask.Status.RUNNING)) {
MalwareScanner.startScanner(scanSystem, scanApps, scanInternal, scanExternal); 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 { } else {
scanner.stopScanner(); malwareScanner.cancel(true);
} }
} }
}); });
@ -67,7 +88,7 @@ public class MainActivity extends AppCompatActivity {
private void requestPermissions() { private void requestPermissions() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_PERMISSION_EXTERNAL_STORAGE); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_EXTERNAL_STORAGE);
} }
} }
@ -76,6 +97,9 @@ public class MainActivity extends AppCompatActivity {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.mnuUpdateDatabase: case R.id.mnuUpdateDatabase:
Database.updateDatabase(Database.signatureDatabases); Database.updateDatabase(Database.signatureDatabases);
if (Database.isDatabaseLoaded()) {
Database.loadDatabase(false, Database.signatureDatabases);
}
break; break;
case R.id.mnuScanSystem: case R.id.mnuScanSystem:
scanSystem = !item.isChecked(); scanSystem = !item.isChecked();

View file

@ -1,11 +1,11 @@
package us.spotco.malwarescanner; package us.spotco.malwarescanner;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Environment;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.widget.TextView; import android.widget.TextView;
import java.io.File; import java.io.File;
@ -14,173 +14,102 @@ import java.io.InputStream;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class MalwareScanner { public class MalwareScanner extends AsyncTask<Set<File>, Object, String> {
private static Context context = null; private Context context = null;
private static TextView log = null; private TextView logOutput = null;
private static AsyncTask<Boolean, String, String> malwareScannerTask = 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 public MalwareScanner(Activity activity, Context context, boolean userFacing) {
private static long scanTime = 0; this.context = context;
this.userFacing = userFacing;
private static HashMap<String, File> fileHashesMD5 = new HashMap<>(); if (activity != null) {
private static HashMap<String, File> fileHashesSHA1 = new HashMap<>(); logOutput = activity.findViewById(R.id.txtLogOutput);
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);
} else { } else {
log.append("No database found... download one first!\n"); notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
} }
} }
public void stopScanner() { private void logResult(String result, boolean userFacingOnly) {
malwareScannerTask.cancel(true); 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() { @Override
return malwareScannerTask != null && (malwareScannerTask.getStatus().equals(AsyncTask.Status.PENDING) || malwareScannerTask.getStatus().equals(AsyncTask.Status.RUNNING)); protected void onPreExecute() {
scanTime = SystemClock.elapsedRealtime();
logResult("Starting scan...", true);
} }
public static class MalwareScannerTask extends AsyncTask<Boolean, String, String> { @Override
protected String doInBackground(Set<File>[] filesToScan) {
//Pre
fileHashesMD5.clear();
fileHashesSHA1.clear();
fileHashesSHA256.clear();
@Override publishProgress(filesToScan[0].size() + " files pending scan\n", true);
protected void onPreExecute() {
scanTime = SystemClock.elapsedRealtime(); Database.loadDatabase(true, Database.signatureDatabases);
log.append("Initiating scan...\n"); publishProgress("Loaded database with " + Database.getSignatureCount() + " signatures\n", true);
fileHashesMD5.clear();
fileHashesSHA1.clear(); //Get file hashes
fileHashesSHA256.clear(); publishProgress("Hashing files...", true);
for (File file : filesToScan[0]) {
getFileHashes(file);
} }
publishProgress("Calculated MD5/SHA-1/SHA-256 hashes for all files\n", true);
@Override //Check the hashes
protected void onCancelled() { checkSignature("MD5", fileHashesMD5, Database.signaturesMD5);
log.append("Scan cancelled\n"); checkSignature("SHA-1", fileHashesSHA1, Database.signaturesSHA1);
} checkSignature("SHA-256", fileHashesSHA256, Database.signaturesSHA256);
@Override
protected String doInBackground(Boolean... booleans) {
boolean scanSystem = booleans[0];
boolean scanApps = booleans[1];
boolean scanInternal = booleans[2];
boolean scanExternal = booleans[3];
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) {
getFileHashes(file);
}
publishProgress("Calculated MD5/SHA-1/SHA-256 hashes for all files\n");
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");
}
System.gc();
return null;
}
@Override
protected void onProgressUpdate(String... progress) {
log.append(progress[0] + "\n");
}
@Override
protected void onPostExecute(String result) {
log.append("Scan completed in " + ((SystemClock.elapsedRealtime() - scanTime) / 1000) + " seconds!\n\n\n\n\n");
}
//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;
} }
private static Set<File> getFilesRecursive(File root) { @Override
Set<File> filesAll = new HashSet<>(); protected void onProgressUpdate(Object... objects) {
logResult((String) objects[0], (boolean) objects[1]);
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;
} }
private static void getFileHashes(File file) { 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);
}
}
publishProgress("Checked all " + hashType + " hashes against signature databases\n", true);
} else {
publishProgress("No " + hashType + " signatures available\n", true);
}
}
private void getFileHashes(File file) {
try { try {
InputStream fis = new FileInputStream(file); 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;
}
}