Added malware protection

This commit is contained in:
M66B 2023-09-25 16:29:49 +02:00
parent 658ea3df84
commit 5003849260
5 changed files with 148 additions and 9 deletions

View File

@ -73,6 +73,9 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
public class ActivityMain extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -111,6 +114,8 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
public static final String EXTRA_METERED = "Metered";
public static final String EXTRA_SIZE = "Size";
private static final String MALWARE_URL = "https://urlhaus.abuse.ch/downloads/hostfile/";
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "Create version=" + Util.getSelfVersionName(this) + "/" + Util.getSelfVersionCode(this));
@ -858,6 +863,7 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
menu.findItem(R.id.menu_sort_name).setChecked(true);
menu.findItem(R.id.menu_lockdown).setChecked(prefs.getBoolean("lockdown", false));
menu.findItem(R.id.menu_malware).setChecked(prefs.getBoolean("malware", false));
return super.onPrepareOptionsMenu(menu);
}
@ -903,6 +909,10 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
menu_lockdown(item);
return true;
case R.id.menu_malware:
menu_malware(item);
return true;
case R.id.menu_log:
if (Util.canFilter(this))
if (IAB.isPurchased(ActivityPro.SKU_LOG, this))
@ -1198,6 +1208,42 @@ public class ActivityMain extends AppCompatActivity implements SharedPreferences
WidgetLockdown.updateWidgets(this);
}
private void menu_malware(MenuItem item) {
item.setChecked(!item.isChecked());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.edit().putBoolean("malware", item.isChecked()).apply();
if (item.isChecked())
try {
final File file = new File(getFilesDir(), "malware.txt");
new DownloadTask(this, new URL(MALWARE_URL), file, new DownloadTask.Listener() {
@Override
public void onCompleted() {
prefs.edit().putBoolean("filter", true).apply();
ServiceSinkhole.reload("malware download", ActivityMain.this, false);
}
@Override
public void onCancelled() {
prefs.edit().putBoolean("malware", false).apply();
}
@Override
public void onException(Throwable ex) {
Toast.makeText(ActivityMain.this, ex.getMessage(), Toast.LENGTH_LONG).show();
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} catch (MalformedURLException ex) {
Toast.makeText(this, ex.toString(), Toast.LENGTH_LONG).show();
}
else {
SharedPreferences.Editor editor = prefs.edit();
for (String key : prefs.getAll().keySet())
if (key.startsWith("malware."))
editor.remove(key);
editor.apply();
}
}
private void menu_about() {
// Create view
LayoutInflater inflater = LayoutInflater.from(this);

View File

@ -74,5 +74,10 @@ public class ApplicationEx extends Application {
access.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
access.setBypassDnd(true);
nm.createNotificationChannel(access);
NotificationChannel malware = new NotificationChannel("malware", getString(R.string.setting_malware), NotificationManager.IMPORTANCE_HIGH);
malware.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
malware.setBypassDnd(true);
nm.createNotificationChannel(malware);
}
}

View File

@ -144,7 +144,9 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
private boolean temporarilyStopped = false;
private long last_hosts_modified = 0;
private long last_malware_modified = 0;
private Map<String, Boolean> mapHostsBlocked = new HashMap<>();
private Map<String, Boolean> mapMalware = new HashMap<>();
private Map<Integer, Boolean> mapUidAllowed = new HashMap<>();
private Map<Integer, Integer> mapUidKnown = new HashMap<>();
private final Map<IPKey, Map<InetAddress, IPRule>> mapUidIPFilters = new HashMap<>();
@ -1471,6 +1473,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
if (filter) {
prepareUidAllowed(listAllowed, listRule);
prepareHostsBlocked();
prepareMalwareList();
prepareUidIPFilters(null);
prepareForwarding();
} else {
@ -1478,6 +1481,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
mapUidAllowed.clear();
mapUidKnown.clear();
mapHostsBlocked.clear();
mapMalware.clear();
mapUidIPFilters.clear();
mapForward.clear();
lock.writeLock().unlock();
@ -1555,6 +1559,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
mapUidAllowed.clear();
mapUidKnown.clear();
mapHostsBlocked.clear();
mapMalware.clear();
mapUidIPFilters.clear();
mapForward.clear();
mapNotify.clear();
@ -1633,6 +1638,63 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
lock.writeLock().unlock();
}
private void prepareMalwareList() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ServiceSinkhole.this);
boolean malware = prefs.getBoolean("filter", false) && prefs.getBoolean("malware", false);
File file = new File(getFilesDir(), "malware.txt");
if (!malware || !file.exists() || !file.canRead()) {
Log.i(TAG, "Malware use=" + malware + " exists=" + file.exists());
lock.writeLock().lock();
mapMalware.clear();
lock.writeLock().unlock();
return;
}
boolean changed = (file.lastModified() != last_malware_modified);
if (!changed && mapMalware.size() > 0) {
Log.i(TAG, "Malware unchanged");
return;
}
last_malware_modified = file.lastModified();
lock.writeLock().lock();
mapMalware.clear();
int count = 0;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
int hash = line.indexOf('#');
if (hash >= 0)
line = line.substring(0, hash);
line = line.trim();
if (line.length() > 0) {
String[] words = line.split("\\s+");
if (words.length > 1) {
count++;
mapMalware.put(words[1], true);
} else
Log.i(TAG, "Invalid malware file line: " + line);
}
}
Log.i(TAG, count + " malware read");
} catch (IOException ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
} finally {
if (br != null)
try {
br.close();
} catch (IOException exex) {
Log.e(TAG, exex.toString() + "\n" + Log.getStackTraceString(exex));
}
}
lock.writeLock().unlock();
}
private void prepareUidIPFilters(String dname) {
SharedPreferences lockdown = getSharedPreferences("lockdown", Context.MODE_PRIVATE);
@ -1884,6 +1946,20 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
Log.i(TAG, "New IP " + rr);
prepareUidIPFilters(rr.QName);
}
if (rr.uid > 0 && !TextUtils.isEmpty(rr.AName)) {
lock.readLock().lock();
boolean malware = (mapMalware.containsKey(rr.AName) && mapMalware.get(rr.AName));
lock.readLock().unlock();
if (malware) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean notified = prefs.getBoolean("malware." + rr.uid, false);
if (!notified) {
prefs.edit().putBoolean("malware." + rr.uid, true).apply();
notifyNewApplication(rr.uid, true);
}
}
}
}
// Called from native code
@ -2257,7 +2333,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (IAB.isPurchased(ActivityPro.SKU_NOTIFY, context) && prefs.getBoolean("install", true)) {
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
notifyNewApplication(uid);
notifyNewApplication(uid, false);
}
}
@ -2299,7 +2375,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
};
public void notifyNewApplication(int uid) {
public void notifyNewApplication(int uid, boolean malware) {
if (uid < 0)
return;
@ -2323,18 +2399,24 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
TypedValue tv = new TypedValue();
getTheme().resolveAttribute(R.attr.colorPrimary, tv, true);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "notify");
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,
malware ? "malware" : "notify");
builder.setSmallIcon(R.drawable.ic_security_white_24dp)
.setContentIntent(pi)
.setColor(tv.data)
.setAutoCancel(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
if (malware)
builder.setContentTitle(name)
.setContentText(getString(R.string.msg_installed_n));
else
builder.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.msg_installed, name));
.setContentText(getString(R.string.msg_malware, name));
else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builder.setContentTitle(name)
.setContentText(getString(R.string.msg_installed_n));
else
builder.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.msg_installed, name));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
builder.setCategory(NotificationCompat.CATEGORY_STATUS)
@ -2696,7 +2778,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
ServiceSinkhole.reload("notification", ServiceSinkhole.this, false);
// Update notification
notifyNewApplication(uid);
notifyNewApplication(uid, false);
// Update UI
Intent ruleset = new Intent(ActivityMain.ACTION_RULES_CHANGED);

View File

@ -51,6 +51,10 @@
android:id="@+id/menu_lockdown"
android:checkable="true"
android:title="@string/setting_lockdown"/>
<item
android:id="@+id/menu_malware"
android:checkable="true"
android:title="@string/setting_malware"/>
<item
android:id="@+id/menu_log"
android:title="@string/menu_log"/>

View File

@ -102,6 +102,7 @@
<string name="setting_handover">Seamless VPN handover on reload</string>
<string name="setting_clear_onreload">Close connections on reload</string>
<string name="setting_lockdown">Lockdown traffic</string>
<string name="setting_malware">Malware protection</string>
<string name="setting_track_usage">Track network usage</string>
<string name="setting_reset_usage">Reset network usage</string>
<string name="setting_show_resolved">Show resolved domain names</string>
@ -184,6 +185,7 @@
<string name="msg_revoked">NetGuard has been disabled, likely by using another VPN based app</string>
<string name="msg_installed">\'%1$s\' installed</string>
<string name="msg_installed_n">Has been installed</string>
<string name="msg_malware">Possible malware: \'%1$s\'</string>
<string name="msg_access">%1$s attempted internet access</string>
<string name="msg_access_n">Attempted internet access</string>
<string name="msg_completed">Action completed</string>