From bd18c5e1954d14331dd8859a7210adfea15bae69 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Thu, 21 Oct 2021 14:49:23 +0200 Subject: [PATCH] Add ability to whitelist malicious connections Needed for #105 --- app/src/main/AndroidManifest.xml | 2 +- .../remote_capture/CaptureService.java | 6 +- .../remote_capture/ConnectionsRegister.java | 35 +++++++ .../emanuelef/remote_capture/PCAPdroid.java | 12 ++- .../activities/EditFilterActivity.java | 4 +- ...askActivity.java => EditListActivity.java} | 34 +++++-- .../activities/MainActivity.java | 6 ++ ...kEditAdapter.java => ListEditAdapter.java} | 4 +- .../fragments/ConnectionsFragment.java | 97 +++++++++++++------ ...askFragment.java => EditListFragment.java} | 39 ++++---- .../model/ConnectionDescriptor.java | 21 +++- .../remote_capture/model/ListInfo.java | 75 ++++++++++++++ .../remote_capture/model/MatchList.java | 80 ++++++++------- .../emanuelef/remote_capture/model/Prefs.java | 1 + .../main/res/layout/edit_filter_activity.xml | 2 +- ...sk_activity.xml => edit_list_activity.xml} | 2 +- ...sk_fragment.xml => edit_list_fragment.xml} | 4 +- .../main/res/menu/connection_context_menu.xml | 22 +++++ .../{mask_edit_cab.xml => list_edit_cab.xml} | 0 app/src/main/res/menu/nav_items.xml | 4 + app/src/main/res/values/strings.xml | 8 +- 21 files changed, 347 insertions(+), 111 deletions(-) rename app/src/main/java/com/emanuelef/remote_capture/activities/{EditMaskActivity.java => EditListActivity.java} (68%) rename app/src/main/java/com/emanuelef/remote_capture/adapters/{MaskEditAdapter.java => ListEditAdapter.java} (94%) rename app/src/main/java/com/emanuelef/remote_capture/fragments/{EditMaskFragment.java => EditListFragment.java} (82%) create mode 100644 app/src/main/java/com/emanuelef/remote_capture/model/ListInfo.java rename app/src/main/res/layout/{edit_mask_activity.xml => edit_list_activity.xml} (90%) rename app/src/main/res/layout/{edit_mask_fragment.xml => edit_list_fragment.xml} (89%) rename app/src/main/res/menu/{mask_edit_cab.xml => list_edit_cab.xml} (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2e3be4b2..c01757e5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,7 +73,7 @@ android:launchMode="singleTop" android:parentActivityName=".activities.MainActivity" /> mAppsStats; private final ArrayList mListeners; + private final MatchList mWhitelist; public ConnectionsRegister(int _size) { mTail = 0; @@ -55,6 +57,7 @@ public class ConnectionsRegister { mItemsRing = new ConnectionDescriptor[mSize]; mListeners = new ArrayList<>(); mAppsStats = new HashMap<>(); // uid -> AppStats + mWhitelist = PCAPdroid.getInstance().getMalwareWhitelist(); } private int firstPos() { @@ -124,6 +127,7 @@ public class ConnectionsRegister { mAppsStats.put(uid, stats); } + conn.updateWhitelist(mWhitelist); processConnectionStatus(conn); stats.num_connections++; @@ -165,7 +169,10 @@ public class ConnectionsRegister { stats.bytes += bytes_delta; //Log.d(TAG, "update " + update.incr_id + " -> " + update.update_type); + boolean host_changed = (update.info != null) && (!update.info.equals(conn.info)); conn.processUpdate(update); + if(host_changed) + conn.updateWhitelist(mWhitelist); processConnectionStatus(conn); changed_pos[k++] = (pos + mSize - first_pos) % mSize; @@ -196,6 +203,34 @@ public class ConnectionsRegister { listener.connectionsChanges(mNumItems); } + public synchronized void refreshConnectionsWhitelist() { + ArrayListchanged_pos = new ArrayList<>(); + + for(int i = 0; i< mNumItems; i++) { + ConnectionDescriptor conn = mItemsRing[i]; + + if(conn != null) { + boolean was_blacklisted = conn.isBlacklisted(); + + conn.updateWhitelist(mWhitelist); + if (conn.isBlacklisted() != was_blacklisted) + changed_pos.add(i); + } + } + + // Notify listeners + if(changed_pos.size() > 0) { + int[] changed = new int[changed_pos.size()]; + int i = 0; + + for(Integer item: changed_pos) + changed[i++] = item; + + for(ConnectionsListener listener: mListeners) + listener.connectionsUpdated(changed); + } + } + public synchronized void addListener(ConnectionsListener listener) { mListeners.add(listener); diff --git a/app/src/main/java/com/emanuelef/remote_capture/PCAPdroid.java b/app/src/main/java/com/emanuelef/remote_capture/PCAPdroid.java index d3033059..7bc283cd 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/PCAPdroid.java +++ b/app/src/main/java/com/emanuelef/remote_capture/PCAPdroid.java @@ -34,6 +34,7 @@ import java.lang.ref.WeakReference; public class PCAPdroid extends Application { private MatchList mVisMask; + private MatchList mMalwareWhitelist; private BlacklistsStatus mBlacklistsStatus; private Context mLocalizedContext; private static WeakReference mInstance; @@ -71,6 +72,10 @@ public class PCAPdroid extends Application { return mInstance.get(); } + public Billing getBilling(Context ctx) { + return new Billing(ctx); + } + public MatchList getVisualizationMask() { if(mVisMask == null) mVisMask = new MatchList(this, Prefs.PREF_VISUALIZATION_MASK); @@ -85,7 +90,10 @@ public class PCAPdroid extends Application { return mBlacklistsStatus; } - public Billing getBilling(Context ctx) { - return new Billing(ctx); + public MatchList getMalwareWhitelist() { + if(mMalwareWhitelist == null) + mMalwareWhitelist = new MatchList(this, Prefs.PREF_MALWARE_WHITELIST); + + return mMalwareWhitelist; } } \ No newline at end of file diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java index 2984cd04..c0790f47 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java @@ -32,6 +32,7 @@ import com.emanuelef.remote_capture.PCAPdroid; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.model.ConnectionDescriptor.Status; import com.emanuelef.remote_capture.model.FilterDescriptor; +import com.emanuelef.remote_capture.model.ListInfo; import com.emanuelef.remote_capture.model.MatchList; import com.google.android.material.chip.Chip; @@ -75,7 +76,8 @@ public class EditFilterActivity extends BaseActivity { mStatusError = findViewById(R.id.status_error); findViewById(R.id.edit_mask).setOnClickListener(v -> { - Intent editIntent = new Intent(this, EditMaskActivity.class); + Intent editIntent = new Intent(this, EditListActivity.class); + editIntent.putExtra(EditListActivity.LIST_TYPE_EXTRA, ListInfo.Type.VISUALIZATION_MASK); startActivity(editIntent); }); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/EditMaskActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/EditListActivity.java similarity index 68% rename from app/src/main/java/com/emanuelef/remote_capture/activities/EditMaskActivity.java rename to app/src/main/java/com/emanuelef/remote_capture/activities/EditListActivity.java index 391d14dd..2ae2a131 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/EditMaskActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/EditListActivity.java @@ -30,24 +30,38 @@ import androidx.annotation.NonNull; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; -import com.emanuelef.remote_capture.adapters.MaskEditAdapter; -import com.emanuelef.remote_capture.fragments.EditMaskFragment; +import com.emanuelef.remote_capture.adapters.ListEditAdapter; +import com.emanuelef.remote_capture.fragments.EditListFragment; +import com.emanuelef.remote_capture.model.ListInfo; +import com.emanuelef.remote_capture.model.MatchList; -public class EditMaskActivity extends BaseActivity { - private static final String TAG = "MaskEditActivity"; +/* An activity to edit a MatchList, specified via LIST_INFO_EXTRA */ +public class EditListActivity extends BaseActivity { + private static final String TAG = "EditListActivity"; + public static final String LIST_TYPE_EXTRA = "list_type"; + private ListInfo mListInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setTitle(R.string.edit_rules); - setContentView(R.layout.edit_mask_activity); + assert(getIntent() != null); + ListInfo.Type ltype = (ListInfo.Type) getIntent().getSerializableExtra(LIST_TYPE_EXTRA); + assert(ltype != null); + mListInfo = new ListInfo(ltype); + + setTitle(mListInfo.getTitle()); + setContentView(R.layout.edit_list_activity); getSupportFragmentManager().beginTransaction() - .replace(R.id.mask_fragment, new EditMaskFragment()) + .replace(R.id.fragment, new EditListFragment()) .commit(); } + public MatchList getList() { + return mListInfo.getList(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -65,15 +79,15 @@ public class EditMaskActivity extends BaseActivity { return false; if(id == R.id.copy_to_clipboard) { - String contents = Utils.adapter2Text((MaskEditAdapter)lv.getAdapter()); + String contents = Utils.adapter2Text((ListEditAdapter)lv.getAdapter()); Utils.copyToClipboard(this, contents); return true; } else if(id == R.id.share) { - String contents = Utils.adapter2Text((MaskEditAdapter)lv.getAdapter()); + String contents = Utils.adapter2Text((ListEditAdapter)lv.getAdapter()); Intent intent = new Intent(android.content.Intent.ACTION_SEND); intent.setType("text/plain"); - intent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.hidden_connections_rules)); + intent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(mListInfo.getShareSubject())); intent.putExtra(android.content.Intent.EXTRA_TEXT, contents); startActivity(Intent.createChooser(intent, getResources().getString(R.string.share))); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java index d5f3d4f3..b5736580 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java @@ -67,6 +67,7 @@ import com.emanuelef.remote_capture.interfaces.AppStateListener; import com.emanuelef.remote_capture.model.AppState; import com.emanuelef.remote_capture.CaptureService; import com.emanuelef.remote_capture.model.CaptureSettings; +import com.emanuelef.remote_capture.model.ListInfo; import com.emanuelef.remote_capture.model.Prefs; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; @@ -193,6 +194,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig Menu navMenu = mNavView.getMenu(); navMenu.findItem(R.id.open_root_log).setVisible(Prefs.isRootCaptureEnabled(mPrefs)); + navMenu.findItem(R.id.edit_malware_whitelist).setVisible(Prefs.isMalwareDetectionEnabled(this, mPrefs)); } private void setupNavigationDrawer() { @@ -347,6 +349,10 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig startActivity(intent); } else Utils.showToast(this, R.string.capture_not_started); + } else if(id == R.id.edit_malware_whitelist) { + Intent intent = new Intent(MainActivity.this, EditListActivity.class); + intent.putExtra(EditListActivity.LIST_TYPE_EXTRA, ListInfo.Type.MALWARE_WHITELIST); + startActivity(intent); } else if(id == R.id.open_root_log) { Intent intent = new Intent(MainActivity.this, LogviewActivity.class); startActivity(intent); diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/MaskEditAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java similarity index 94% rename from app/src/main/java/com/emanuelef/remote_capture/adapters/MaskEditAdapter.java rename to app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java index e31d3320..291155e9 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/MaskEditAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java @@ -35,10 +35,10 @@ import com.emanuelef.remote_capture.model.MatchList; import java.util.Iterator; -public class MaskEditAdapter extends ArrayAdapter implements TextAdapter { +public class ListEditAdapter extends ArrayAdapter implements TextAdapter { private final LayoutInflater mLayoutInflater; - public MaskEditAdapter(Context context, Iterator items) { + public ListEditAdapter(Context context, Iterator items) { super(context, R.layout.rule_item); mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java index 183614c4..49760f60 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java @@ -55,6 +55,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.emanuelef.remote_capture.AppsResolver; import com.emanuelef.remote_capture.CaptureService; import com.emanuelef.remote_capture.ConnectionsRegister; +import com.emanuelef.remote_capture.PCAPdroid; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; import com.emanuelef.remote_capture.activities.AppDetailsActivity; @@ -308,18 +309,24 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener if(app != null) { MenuItem item = menu.findItem(R.id.hide_app); - String label = MatchList.getLabel(ctx, RuleType.APP, app.getName()); + String label = MatchList.getRuleLabel(ctx, RuleType.APP, Integer.toString(app.getUid())); item.setTitle(label); item.setVisible(true); item = menu.findItem(R.id.search_app); item.setTitle(label); item.setVisible(true); + + if(conn.isBlacklisted()) { + item = menu.findItem(R.id.whitelist_app); + item.setTitle(label); + item.setVisible(true); + } } if((conn.info != null) && (!conn.info.isEmpty())) { MenuItem item = menu.findItem(R.id.hide_host); - String label = MatchList.getLabel(ctx, RuleType.HOST, conn.info); + String label = MatchList.getRuleLabel(ctx, RuleType.HOST, conn.info); item.setTitle(label); item.setVisible(true); @@ -331,18 +338,29 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener if(!rootDomain.equals(conn.info)) { item = menu.findItem(R.id.hide_root_domain); - item.setTitle(MatchList.getLabel(ctx, RuleType.ROOT_DOMAIN, rootDomain)); + item.setTitle(MatchList.getRuleLabel(ctx, RuleType.ROOT_DOMAIN, rootDomain)); + item.setVisible(true); + } + + if(conn.isBlacklistedHost()) { + item = menu.findItem(R.id.whitelist_host); + item.setTitle(label); item.setVisible(true); } } - String label = MatchList.getLabel(ctx, RuleType.IP, conn.dst_ip); + String label = MatchList.getRuleLabel(ctx, RuleType.IP, conn.dst_ip); menu.findItem(R.id.hide_ip).setTitle(label); menu.findItem(R.id.search_ip).setTitle(label); + if(conn.isBlacklistedIp()) + menu.findItem(R.id.whitelist_ip).setTitle(label); - label = MatchList.getLabel(ctx, RuleType.PROTOCOL, conn.l7proto); + label = MatchList.getRuleLabel(ctx, RuleType.PROTOCOL, conn.l7proto); menu.findItem(R.id.hide_proto).setTitle(label); menu.findItem(R.id.search_proto).setTitle(label); + + if(!conn.isBlacklisted()) + menu.findItem(R.id.whitelist_menu).setVisible(false); } private void setQuery(String query) { @@ -358,50 +376,73 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener }); } + private void recheckBlacklistedConnections() { + ConnectionsRegister reg = CaptureService.getConnsRegister(); + if(reg != null) + reg.refreshConnectionsWhitelist(); + } + @Override public boolean onContextItemSelected(@NonNull MenuItem item) { ConnectionDescriptor conn = mAdapter.getClickedItem(); + MatchList whitelist = PCAPdroid.getInstance().getMalwareWhitelist(); + boolean mask_changed = false; + boolean whitelist_changed = false; if(conn == null) return super.onContextItemSelected(item); int id = item.getItemId(); - String label = item.getTitle().toString(); - if(id == R.id.hide_app) - mAdapter.mMask.addApp(conn.uid, label); - else if(id == R.id.hide_host) - mAdapter.mMask.addHost(conn.info, label); - else if(id == R.id.hide_ip) - mAdapter.mMask.addIp(conn.dst_ip, label); - else if(id == R.id.hide_proto) - mAdapter.mMask.addProto(conn.l7proto, label); - else if(id == R.id.hide_root_domain) - mAdapter.mMask.addRootDomain(Utils.getRootDomain(conn.info), label); - else if(id == R.id.search_app) { + if(id == R.id.hide_app) { + mAdapter.mMask.addApp(conn.uid); + mask_changed = true; + } else if(id == R.id.hide_host) { + mAdapter.mMask.addHost(conn.info); + mask_changed = true; + } else if(id == R.id.hide_ip) { + mAdapter.mMask.addIp(conn.dst_ip); + mask_changed = true; + } else if(id == R.id.hide_proto) { + mAdapter.mMask.addProto(conn.l7proto); + mask_changed = true; + } else if(id == R.id.hide_root_domain) { + mAdapter.mMask.addRootDomain(Utils.getRootDomain(conn.info)); + mask_changed = true; + } else if(id == R.id.search_app) setQuery(Objects.requireNonNull( mApps.get(conn.uid, 0)).getPackageName()); - return true; - } else if(id == R.id.search_host) { + else if(id == R.id.search_host) setQuery(conn.info); - return true; - } else if(id == R.id.search_ip) { + else if(id == R.id.search_ip) setQuery(conn.dst_ip); - return true; - } else if(id == R.id.search_proto) { + else if(id == R.id.search_proto) setQuery(conn.l7proto); - return true; + else if(id == R.id.whitelist_app) { + whitelist.addApp(conn.uid); + whitelist_changed = true; + } else if(id == R.id.whitelist_ip) { + whitelist.addIp(conn.dst_ip); + whitelist_changed = true; + } else if(id == R.id.whitelist_host) { + whitelist.addHost(conn.info); + whitelist_changed = true; } else if(id == R.id.open_app_details) { Intent intent = new Intent(requireContext(), AppDetailsActivity.class); intent.putExtra(AppDetailsActivity.APP_UID_EXTRA, conn.uid); startActivity(intent); - return true; } else return super.onContextItemSelected(item); - mAdapter.mMask.save(); - mAdapter.mFilter.showMasked = false; - refreshFilteredConnections(); + if(mask_changed) { + mAdapter.mMask.save(); + mAdapter.mFilter.showMasked = false; + refreshFilteredConnections(); + } else if(whitelist_changed) { + whitelist.save(); + recheckBlacklistedConnections(); + } + return true; } diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/EditMaskFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/EditListFragment.java similarity index 82% rename from app/src/main/java/com/emanuelef/remote_capture/fragments/EditMaskFragment.java rename to app/src/main/java/com/emanuelef/remote_capture/fragments/EditListFragment.java index 68abfca1..58949ab1 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/EditMaskFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/EditListFragment.java @@ -35,35 +35,35 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import com.emanuelef.remote_capture.PCAPdroid; import com.emanuelef.remote_capture.R; -import com.emanuelef.remote_capture.adapters.MaskEditAdapter; +import com.emanuelef.remote_capture.activities.EditListActivity; +import com.emanuelef.remote_capture.adapters.ListEditAdapter; import com.emanuelef.remote_capture.model.MatchList; import java.util.ArrayList; import java.util.Iterator; -public class EditMaskFragment extends Fragment { - private MaskEditAdapter mAdapter; +public class EditListFragment extends Fragment { + private ListEditAdapter mAdapter; private TextView mEmptyText; private ArrayList mSelected = new ArrayList<>(); - private MatchList mMask; + private MatchList mList; private ListView mListView; - private static final String TAG = "MaskEditFragment"; + private static final String TAG = "EditListFragment"; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.edit_mask_fragment, container, false); + return inflater.inflate(R.layout.edit_list_fragment, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { mListView = view.findViewById(R.id.listview); - mEmptyText = view.findViewById(R.id.mask_empty); - mMask = PCAPdroid.getInstance().getVisualizationMask(); + mEmptyText = view.findViewById(R.id.list_empty); + mList = ((EditListActivity)requireActivity()).getList(); - mAdapter = new MaskEditAdapter(requireContext(), mMask.iterRules()); + mAdapter = new ListEditAdapter(requireContext(), mList.iterRules()); mListView.setAdapter(mAdapter); mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); mListView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() { @@ -82,7 +82,7 @@ public class EditMaskFragment extends Fragment { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = requireActivity().getMenuInflater(); - inflater.inflate(R.menu.mask_edit_cab, menu); + inflater.inflate(R.menu.list_edit_cab, menu); return true; } @@ -98,12 +98,12 @@ public class EditMaskFragment extends Fragment { if(id == R.id.delete_entry) { if(mSelected.size() >= mAdapter.getCount()) { mAdapter.clear(); - mMask.clear(); - mMask.save(); + mList.clear(); + mList.save(); } else { for(MatchList.Rule item : mSelected) mAdapter.remove(item); - updateMask(); + updateList(); } mode.finish(); @@ -137,12 +137,11 @@ public class EditMaskFragment extends Fragment { mEmptyText.setVisibility((mAdapter.getCount() == 0) ? View.VISIBLE : View.GONE); } - private void updateMask() { + private void updateList() { ArrayList toRemove = new ArrayList<>(); + Iterator iter = mList.iterRules(); - Iterator iter = mMask.iterRules(); - - // Remove the mMask rules which are not in the adapter dataset + // Remove the mList rules which are not in the adapter dataset while(iter.hasNext()) { MatchList.Rule rule = iter.next(); @@ -151,8 +150,8 @@ public class EditMaskFragment extends Fragment { } if(toRemove.size() > 0) { - mMask.removeRules(toRemove); - mMask.save(); + mList.removeRules(toRemove); + mList.save(); } } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java b/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java index 9b713f4a..82a670d6 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java @@ -73,11 +73,14 @@ public class ConnectionDescriptor implements Serializable { public int incr_id; public int status; private int tcp_flags; - public boolean blacklisted_ip; - public boolean blacklisted_domain; + private boolean blacklisted_ip; + private boolean blacklisted_host; /* Internal */ - public boolean alerted = false; + public boolean alerted; + private boolean whitelisted_ip; + private boolean whitelisted_host; + private boolean whitelisted_app; public ConnectionDescriptor(int _incr_id, int _ipver, int _ipproto, String _src_ip, String _dst_ip, int _src_port, int _dst_port, int _uid, long when) { @@ -101,7 +104,7 @@ public class ConnectionDescriptor implements Serializable { rcvd_pkts = update.rcvd_pkts; status = (update.status & 0x00FF); blacklisted_ip = (update.status & 0x0100) != 0; - blacklisted_domain = (update.status & 0x0200) != 0; + blacklisted_host = (update.status & 0x0200) != 0; last_seen = update.last_seen; tcp_flags = update.tcp_flags; } @@ -164,8 +167,16 @@ public class ConnectionDescriptor implements Serializable { return (tcp_flags & 0xFF); } + public boolean isBlacklistedIp() { return !whitelisted_app && !whitelisted_ip && blacklisted_ip; } + public boolean isBlacklistedHost() { return !whitelisted_app && !whitelisted_host && blacklisted_host; } public boolean isBlacklisted() { - return blacklisted_ip || blacklisted_domain; + return isBlacklistedIp() || isBlacklistedHost(); + } + + public void updateWhitelist(MatchList whitelist) { + whitelisted_app = whitelist.matchesApp(uid); + whitelisted_ip = whitelist.matchesIP(dst_ip); + whitelisted_host = whitelist.matchesHost(info); } @Override diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/ListInfo.java b/app/src/main/java/com/emanuelef/remote_capture/model/ListInfo.java new file mode 100644 index 00000000..c8c21211 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/model/ListInfo.java @@ -0,0 +1,75 @@ +/* + * This file is part of PCAPdroid. + * + * PCAPdroid 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. + * + * PCAPdroid 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 PCAPdroid. If not, see . + * + * Copyright 2020-21 - Emanuele Faranda + */ + +package com.emanuelef.remote_capture.model; + +import androidx.annotation.NonNull; + +import com.emanuelef.remote_capture.PCAPdroid; +import com.emanuelef.remote_capture.R; + + +public class ListInfo { + private final Type mType; + + public enum Type { + VISUALIZATION_MASK, + MALWARE_WHITELIST, + } + + public ListInfo(Type tp) { + mType = tp; + } + + public @NonNull MatchList getList() { + switch(mType) { + case VISUALIZATION_MASK: + return PCAPdroid.getInstance().getVisualizationMask(); + case MALWARE_WHITELIST: + return PCAPdroid.getInstance().getMalwareWhitelist(); + } + + assert false; + return null; + } + + public int getTitle() { + switch(mType) { + case VISUALIZATION_MASK: + return R.string.edit_hidden_connections; + case MALWARE_WHITELIST: + return R.string.edit_malware_whitelist; + } + + assert false; + return 0; + } + + public int getShareSubject() { + switch(mType) { + case VISUALIZATION_MASK: + return R.string.hidden_connections_rules; + case MALWARE_WHITELIST: + return R.string.malware_whitelist_rules; + } + + assert false; + return 0; + } +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java b/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java index e7f5b69c..bbb0ca5f 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java @@ -64,21 +64,17 @@ public class MatchList { PROTOCOL } - public static class Rule { + public class Rule { private final String mLabel; private final RuleType mType; private final Object mValue; - Rule(RuleType tp, Object value, String label) { - mLabel = label; + private Rule(RuleType tp, Object value) { + mLabel = MatchList.getRuleLabel(mContext, tp, value.toString()); mType = tp; mValue = value; } - public Rule(Context ctx, RuleType tp, Object value) { - this(tp, value, MatchList.getLabel(ctx, tp, value.toString())); - } - public String getLabel() { return mLabel; } @@ -124,7 +120,7 @@ public class MatchList { .apply(); } - public static String getLabel(Context ctx, RuleType tp, String value) { + public static String getRuleLabel(Context ctx, RuleType tp, String value) { int resid; switch(tp) { @@ -137,6 +133,14 @@ public class MatchList { return ""; } + if(tp == RuleType.APP) { + AppsResolver resolver = new AppsResolver(ctx); + AppDescriptor app = resolver.get(Integer.parseInt(value), 0); + + if(app != null) + value = app.getName(); + } + return Utils.formatTextValue(ctx, null, italic, resid, value).toString(); } @@ -167,8 +171,6 @@ public class MatchList { if(ruleArray == null) return; - AppsResolver resolver = new AppsResolver(mContext); - for(JsonElement el: ruleArray) { JsonObject ruleObj = el.getAsJsonObject(); RuleType type; @@ -181,25 +183,15 @@ public class MatchList { } String val = ruleObj.get("value").getAsString(); - String valLabel = val; - - if(type == RuleType.APP) { - AppDescriptor app = resolver.get(Integer.parseInt(val), 0); - - if(app != null) - valLabel = app.getName(); - } - - String label = getLabel(mContext, type, valLabel); - addRule(new Rule(type, val, label)); + addRule(new Rule(type, val)); } } - public void addApp(int uid, String label) { addRule(new Rule(RuleType.APP, uid, label)); } - public void addIp(String ip, String label) { addRule(new Rule(RuleType.IP, ip, label)); } - public void addHost(String info, String label) { addRule(new Rule(RuleType.HOST, info, label)); } - public void addProto(String proto, String label) { addRule(new Rule(RuleType.PROTOCOL, proto, label)); } - public void addRootDomain(String domain, String label) { addRule(new Rule(RuleType.ROOT_DOMAIN, domain, label)); } + public void addApp(int uid) { addRule(new Rule(RuleType.APP, uid)); } + public void addIp(String ip) { addRule(new Rule(RuleType.IP, ip)); } + public void addHost(String info) { addRule(new Rule(RuleType.HOST, info)); } + public void addProto(String proto) { addRule(new Rule(RuleType.PROTOCOL, proto)); } + public void addRootDomain(String domain) { addRule(new Rule(RuleType.ROOT_DOMAIN, domain)); } static private String matchKey(RuleType tp, Object val) { return tp + "@" + val; @@ -223,14 +215,36 @@ public class MatchList { } } - public boolean matches(ConnectionDescriptor conn) { - boolean hasInfo = ((conn.info != null) && (!conn.info.isEmpty())); + public boolean matchesApp(int uid) { + return mMatches.containsKey(matchKey(RuleType.APP, uid)); + } - return(mMatches.containsKey(matchKey(RuleType.APP, conn.uid)) || - mMatches.containsKey(matchKey(RuleType.IP, conn.dst_ip)) || - mMatches.containsKey(matchKey(RuleType.PROTOCOL, conn.l7proto)) || - (hasInfo && mMatches.containsKey(matchKey(RuleType.HOST, conn.info))) || - (hasInfo && mMatches.containsKey(matchKey(RuleType.ROOT_DOMAIN, Utils.getRootDomain(conn.info))))); + public boolean matchesIP(String ip) { + return mMatches.containsKey(matchKey(RuleType.IP, ip)); + } + + public boolean matchesProto(String l7proto) { + return mMatches.containsKey(matchKey(RuleType.PROTOCOL, l7proto)); + } + + public boolean matchesHost(String host) { + return mMatches.containsKey(matchKey(RuleType.HOST, host)); + } + + public boolean matchesRootDomain(String root_domain) { + return mMatches.containsKey(matchKey(RuleType.ROOT_DOMAIN, root_domain)); + } + + public boolean matches(ConnectionDescriptor conn) { + if(mMatches.isEmpty()) + return false; + + boolean hasInfo = ((conn.info != null) && (!conn.info.isEmpty())); + return(matchesApp(conn.uid) || + matchesIP(conn.dst_ip) || + matchesIP(conn.l7proto) || + (hasInfo && matchesHost(conn.info))) || + (hasInfo && matchesRootDomain(Utils.getRootDomain(conn.info))); } public Iterator iterRules() { diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java index 69a5342e..9d3ec5b2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java @@ -47,6 +47,7 @@ public class Prefs { public static final String PREF_APP_THEME = "app_theme"; public static final String PREF_ROOT_CAPTURE = "root_capture"; public static final String PREF_VISUALIZATION_MASK = "vis_mask"; + public static final String PREF_MALWARE_WHITELIST = "maware_whitelist"; public static final String PREF_PCAPDROID_TRAILER = "pcapdroid_trailer"; public enum DumpMode { diff --git a/app/src/main/res/layout/edit_filter_activity.xml b/app/src/main/res/layout/edit_filter_activity.xml index 90e196a8..42b97537 100644 --- a/app/src/main/res/layout/edit_filter_activity.xml +++ b/app/src/main/res/layout/edit_filter_activity.xml @@ -31,7 +31,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" - android:text="@string/edit_rules" + android:text="@string/edit_list" style="?attr/materialButtonOutlinedStyle" android:textColor="@color/colorTabText" /> diff --git a/app/src/main/res/layout/edit_mask_activity.xml b/app/src/main/res/layout/edit_list_activity.xml similarity index 90% rename from app/src/main/res/layout/edit_mask_activity.xml rename to app/src/main/res/layout/edit_list_activity.xml index e8591c99..a6a94592 100644 --- a/app/src/main/res/layout/edit_mask_activity.xml +++ b/app/src/main/res/layout/edit_list_activity.xml @@ -5,7 +5,7 @@ android:orientation="vertical"> diff --git a/app/src/main/res/layout/edit_mask_fragment.xml b/app/src/main/res/layout/edit_list_fragment.xml similarity index 89% rename from app/src/main/res/layout/edit_mask_fragment.xml rename to app/src/main/res/layout/edit_list_fragment.xml index 2ffdee5f..0677d81e 100644 --- a/app/src/main/res/layout/edit_mask_fragment.xml +++ b/app/src/main/res/layout/edit_list_fragment.xml @@ -10,13 +10,13 @@ android:scrollbarStyle="outsideOverlay" /> + android:text="@string/list_is_empty"> diff --git a/app/src/main/res/menu/connection_context_menu.xml b/app/src/main/res/menu/connection_context_menu.xml index 86041471..3247fe7a 100644 --- a/app/src/main/res/menu/connection_context_menu.xml +++ b/app/src/main/res/menu/connection_context_menu.xml @@ -61,6 +61,28 @@ + + + + + + + + + + diff --git a/app/src/main/res/menu/mask_edit_cab.xml b/app/src/main/res/menu/list_edit_cab.xml similarity index 100% rename from app/src/main/res/menu/mask_edit_cab.xml rename to app/src/main/res/menu/list_edit_cab.xml diff --git a/app/src/main/res/menu/nav_items.xml b/app/src/main/res/menu/nav_items.xml index 017509d7..590be011 100644 --- a/app/src/main/res/menu/nav_items.xml +++ b/app/src/main/res/menu/nav_items.xml @@ -9,6 +9,10 @@ android:id="@+id/action_stats" android:title="@string/stats" android:icon="@drawable/ic_list" /> + Capturing packets from \"%1$s\" Edit Filter Show hidden connections - Edit Rules + Edit List Hidden Connections Rules - No rules + List is empty Malicious connection detected (%1$s) Only malicious connections Security @@ -192,5 +192,9 @@ Blacklists Status Up-to-date: %1$d/%2$d ― Domain rules: %3$s ― IP rules: %4$s\nLast update: %5$s Reset + Malware Whitelist + Malware Whitelist Rules + Edit Hidden Connections + Edit Malware Whitelist