From bb68ac674f5d14d59df821ea623a9b799726b196 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Mon, 1 Jan 2024 17:29:37 +0100 Subject: [PATCH 01/45] Disable battery optimization (Doze) for the mitm addon It can interfere with the mitm service, causing connections to get stuck until device is rebooted --- .../emanuelef/remote_capture/MitmAddon.java | 31 +++++++++++++++++-- .../remote_capture/MitmReceiver.java | 7 ++++- .../activities/AboutActivity.java | 7 ++++- .../main/java/com/pcapdroid/mitm/MitmAPI.java | 1 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java index 8d77e9c3d..cbab7793e 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2022 - Emanuele Faranda + * Copyright 2022-24 - Emanuele Faranda */ package com.emanuelef.remote_capture; @@ -34,6 +34,7 @@ import android.os.Message; import android.os.Messenger; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; import android.os.RemoteException; import androidx.annotation.NonNull; @@ -48,8 +49,8 @@ import java.lang.ref.WeakReference; public class MitmAddon { - public static final long PACKAGE_VERSION_CODE = 16; - public static final String PACKAGE_VERSION_NAME = "v0.16"; + public static final long PACKAGE_VERSION_CODE = 17; + public static final String PACKAGE_VERSION_NAME = "v1.0"; public static final String REPOSITORY = "https://github.com/emanuele-f/PCAPdroid-mitm"; private static final String TAG = "MitmAddon"; private final Context mContext; @@ -295,4 +296,28 @@ public boolean stopProxy() { return false; } } + + // NOTE: doze could be disabled by PCAPdroid itself, however this is moved to the addon to avoid + // any issues with the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS Google Play policies + public boolean disableDoze() { + if(mService == null) + return false; + + Log.i(TAG, "Send disable doze"); + Message msg = Message.obtain(null, MitmAPI.MSG_DISABLE_DOZE); + try { + mService.send(msg); + return true; + } catch (RemoteException e) { + e.printStackTrace(); + return false; + } + } + + public static boolean isDozeEnabled(Context context) { + final PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) && (manager != null) + && !manager.isIgnoringBatteryOptimizations(MitmAPI.PACKAGE_NAME); + } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java b/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java index 4650089c1..e3cfb8248 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2022 - Emanuele Faranda + * Copyright 2022-24 - Emanuele Faranda */ package com.emanuelef.remote_capture; @@ -483,6 +483,11 @@ public void onMitmGetCaCertificateResult(@Nullable String ca_pem) { return; } + if (MitmAddon.isDozeEnabled(mContext)) { + Utils.showToastLong(mContext, R.string.mitm_doze_notice); + mAddon.disableDoze(); + } + if(mThread != null) mThread.interrupt(); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java index f14a96072..ddd6614ab 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-24 - Emanuele Faranda */ package com.emanuelef.remote_capture.activities; @@ -55,6 +55,7 @@ import com.emanuelef.remote_capture.Billing; import com.emanuelef.remote_capture.CaptureService; import com.emanuelef.remote_capture.Log; +import com.emanuelef.remote_capture.MitmAddon; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; import com.emanuelef.remote_capture.model.Prefs; @@ -147,6 +148,7 @@ public boolean onMenuItemSelected(MenuItem item) { String deviceInfo = Utils.getBuildInfo(this) + "\n\n" + Prefs.asString(this); + // Private DNS Utils.PrivateDnsMode dns_mode = CaptureService.getPrivateDnsMode(); if(dns_mode == null) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -164,6 +166,9 @@ public boolean onMenuItemSelected(MenuItem item) { if(dns_mode != null) deviceInfo += "\n" + "PrivateDnsMode: " + dns_mode; + // Mitm doze + deviceInfo += "\n" + "MitmBatteryOptimized: " + ((MitmAddon.isInstalled(this) && MitmAddon.isDozeEnabled(this)) ? "true" : "false"); + LayoutInflater inflater = LayoutInflater.from(this); View view = inflater.inflate(R.layout.scrollable_dialog, null); ((TextView)view.findViewById(R.id.text)).setText(deviceInfo); diff --git a/app/src/main/java/com/pcapdroid/mitm/MitmAPI.java b/app/src/main/java/com/pcapdroid/mitm/MitmAPI.java index d88fa1287..923011c83 100644 --- a/app/src/main/java/com/pcapdroid/mitm/MitmAPI.java +++ b/app/src/main/java/com/pcapdroid/mitm/MitmAPI.java @@ -30,6 +30,7 @@ public class MitmAPI { public static final int MSG_START_MITM = 1; public static final int MSG_GET_CA_CERTIFICATE = 2; public static final int MSG_STOP_MITM = 3; + public static final int MSG_DISABLE_DOZE = 4; public static final String MITM_CONFIG = "mitm_config"; public static final String CERTIFICATE_RESULT = "certificate"; public static final String SSLKEYLOG_RESULT = "sslkeylog"; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f44df8e95..34f1d7d02 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -493,4 +493,5 @@ This connection will not be decrypted. Create a decryption rule to decrypt it TLS decryption is only applied to connections that match the configured rules. Do you want to create decryption rules now? Active VPN detected + Battery optimization may interfere with the mitm addon From bed393c95e467f397ba1c92ae84f71481be90fa0 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Mon, 1 Jan 2024 17:46:27 +0100 Subject: [PATCH 02/45] Fix disable battery optimization prompt in Android 14 --- app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java index cbab7793e..363e95f39 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java @@ -194,7 +194,8 @@ public boolean connect(int extra_flags) { Intent intent = new Intent(); intent.setComponent(new ComponentName(MitmAPI.PACKAGE_NAME, MitmAPI.MITM_SERVICE)); - if(!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | extra_flags)) { + if(!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | + Context.BIND_ALLOW_ACTIVITY_STARTS | extra_flags)) { try { mContext.unbindService(mConnection); } catch (IllegalArgumentException ignored) { From 0deabd135c4789ca2eef47a4900865834c66d255 Mon Sep 17 00:00:00 2001 From: elicec Date: Sat, 6 Jan 2024 15:40:37 +0800 Subject: [PATCH 03/45] Update README.md reanme capture_root.c to capture_libpcap.c also update the path --- app/src/main/jni/pcapd/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/jni/pcapd/README.md b/app/src/main/jni/pcapd/README.md index 9e8352813..7e9e247d0 100644 --- a/app/src/main/jni/pcapd/README.md +++ b/app/src/main/jni/pcapd/README.md @@ -58,7 +58,7 @@ Here are the steps to make it communicate with your app: 5. The app can now receive the packets on the UNIX socket. 6. When the app closes the UNIX socket, the pcapd daemon is automatically stopped. -Check out the [capture_root.c source](https://github.com/emanuele-f/PCAPdroid/blob/master/app/src/main/jni/core/capture_root.c) to see an example of integration. +Check out the [capture_libpcap.c source](https://github.com/emanuele-f/PCAPdroid/blob/master/app/src/main/jni/core/capture_libpcap.c) to see an example of integration. Packets Data ------------ From a6c547d0ab3acae4515add5cc2feed38e96ddcf6 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 12:55:59 +0100 Subject: [PATCH 04/45] Fix port mapping rule not applied to DNS --- app/src/main/jni/core/capture_vpn.c | 5 +++-- app/src/main/jni/core/pcapdroid.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/jni/core/capture_vpn.c b/app/src/main/jni/core/capture_vpn.c index 1c19f4877..1ca852c1a 100644 --- a/app/src/main/jni/core/capture_vpn.c +++ b/app/src/main/jni/core/capture_vpn.c @@ -580,7 +580,7 @@ int run_vpn(pcapdroid_t *pd) { // To be run before pd_process_packet/process_payload if(data->sent_pkts == 0) { if(pd_check_port_map(conn)) - /* port mapping applied */; + data->port_mapping_applied = true; else if(should_proxify(pd, tuple, data)) { zdtun_conn_proxy(conn); data->proxied = true; @@ -590,7 +590,8 @@ int run_vpn(pcapdroid_t *pd) { pd_process_packet(pd, &pkt, true, tuple, data, get_pkt_timestamp(pd, &tv), &pctx); if(data->sent_pkts == 0) { // Newly created connections - data->blacklisted_internal |= !check_dns_req_allowed(pd, conn, &pctx); + if (!data->port_mapping_applied) + data->blacklisted_internal |= !check_dns_req_allowed(pd, conn, &pctx); data->to_block |= data->blacklisted_internal; if(data->to_block) { diff --git a/app/src/main/jni/core/pcapdroid.h b/app/src/main/jni/core/pcapdroid.h index 591efd23c..045b30786 100644 --- a/app/src/main/jni/core/pcapdroid.h +++ b/app/src/main/jni/core/pcapdroid.h @@ -114,6 +114,7 @@ typedef struct { bool netd_block_missed; bool proxied; bool decryption_ignored; + bool port_mapping_applied; bool encrypted_l7; bool payload_truncated; bool has_payload[2]; // [0]: rx, [1] tx From c0bea0227b667a36309e05b813e4eb5257dc6f01 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 15:09:16 +0100 Subject: [PATCH 05/45] Add port mapping connection redirection indicator --- .../remote_capture/adapters/ConnectionsAdapter.java | 3 +++ .../remote_capture/fragments/ConnectionOverview.java | 4 ++++ .../remote_capture/model/ConnectionDescriptor.java | 9 +++++---- app/src/main/jni/core/jni_impl.c | 3 ++- app/src/main/res/drawable/reply.xml | 5 +++++ app/src/main/res/layout/about_activity.xml | 4 ++-- app/src/main/res/layout/connection_item.xml | 9 +++++++++ app/src/main/res/values/strings.xml | 2 ++ 8 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/drawable/reply.xml diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java index a9e0c74b5..bb724e75b 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java @@ -79,6 +79,7 @@ public static class ViewHolder extends RecyclerView.ViewHolder { ImageView jsInjectorInd; ImageView blacklistedInd; ImageView blockedInd; + ImageView redirectedInd; ImageView decryptionInd; TextView statusInd; TextView remote; @@ -103,6 +104,7 @@ public static class ViewHolder extends RecyclerView.ViewHolder { jsInjectorInd = itemView.findViewById(R.id.js_injector); blacklistedInd = itemView.findViewById(R.id.blacklisted); blockedInd = itemView.findViewById(R.id.blocked); + redirectedInd = itemView.findViewById(R.id.redirected); //countryFlag = itemView.findViewById(R.id.country_flag); Context context = itemView.getContext(); @@ -160,6 +162,7 @@ else if((conn.status == ConnectionDescriptor.CONN_STATUS_CLOSED) jsInjectorInd.setVisibility(((conn.js_injected_scripts != null) && !conn.js_injected_scripts.isEmpty()) ? View.VISIBLE : View.GONE); blacklistedInd.setVisibility(conn.isBlacklisted() ? View.VISIBLE : View.GONE); blockedInd.setVisibility(conn.is_blocked ? View.VISIBLE : View.GONE); + redirectedInd.setVisibility((conn.isPortMappingApplied() && !conn.is_blocked) ? View.VISIBLE : View.GONE); if(CaptureService.isDecryptingTLS()) { decryptionInd.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java index 7b73a22b8..1a7f76974 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java @@ -292,6 +292,10 @@ public void connectionUpdated() { mError.setTextColor(ContextCompat.getColor(context, R.color.warning)); mError.setText(context.getString(R.string.connection_start_not_seen)); mError.setVisibility(View.VISIBLE); + } else if(mConn.isPortMappingApplied()) { + mError.setTextColor(ContextCompat.getColor(context, R.color.colorTabText)); + mError.setText(context.getString(R.string.connection_redirected_port_map)); + mError.setVisibility(View.VISIBLE); } else if(mConn.payload_length == 0) { mError.setTextColor(ContextCompat.getColor(context, R.color.warning)); mError.setText(context.getString(R.string.warn_no_app_data)); 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 c02338110..decf1b229 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 @@ -111,6 +111,7 @@ public enum FilteringStatus { private boolean blacklisted_ip; private boolean blacklisted_host; public boolean is_blocked; + private boolean port_mapping_applied; public boolean decryption_ignored; public boolean netd_block_missed; private boolean payload_truncated; @@ -155,11 +156,12 @@ public void processUpdate(ConnectionUpdate update) { rcvd_pkts = update.rcvd_pkts; blocked_pkts = update.blocked_pkts; status = (update.status & 0x00FF); + port_mapping_applied = (update.status & 0x2000) != 0; decryption_ignored = (update.status & 0x1000) != 0; netd_block_missed = (update.status & 0x0800) != 0; is_blocked = (update.status & 0x0400) != 0; - blacklisted_ip = (update.status & 0x0100) != 0; blacklisted_host = (update.status & 0x0200) != 0; + blacklisted_ip = (update.status & 0x0100) != 0; last_seen = update.last_seen; tcp_flags = update.tcp_flags; // NOTE: only for root capture @@ -302,9 +304,8 @@ public void setPayloadTruncatedByAddon() { payload_truncated = true; } - public boolean isPayloadTruncated() { - return payload_truncated; - } + public boolean isPayloadTruncated() { return payload_truncated; } + public boolean isPortMappingApplied() { return port_mapping_applied; } public boolean isNotDecryptable() { return !decryption_ignored && (encrypted_payload || !mitm_decrypt); } public boolean isDecrypted() { return !decryption_ignored && !isNotDecryptable() && (getNumPayloadChunks() > 0); } diff --git a/app/src/main/jni/core/jni_impl.c b/app/src/main/jni/core/jni_impl.c index f330c6a95..e7312b4d9 100644 --- a/app/src/main/jni/core/jni_impl.c +++ b/app/src/main/jni/core/jni_impl.c @@ -167,7 +167,8 @@ static jobject getConnUpdate(pcapdroid_t *pd, const conn_and_tuple_t *conn) { (*env)->CallVoidMethod(env, update, mids.connUpdateSetStats, data->last_seen, data->payload_length, data->sent_bytes, data->rcvd_bytes, data->sent_pkts, data->rcvd_pkts, data->blocked_pkts, (data->tcp_flags[0] << 8) | data->tcp_flags[1], - (data->decryption_ignored << 12) | + (data->port_mapping_applied << 13) | + (data->decryption_ignored << 12) | (data->netd_block_missed << 11) | (blocked << 10) | (data->blacklisted_domain << 9) | diff --git a/app/src/main/res/drawable/reply.xml b/app/src/main/res/drawable/reply.xml new file mode 100644 index 000000000..6925f177a --- /dev/null +++ b/app/src/main/res/drawable/reply.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/about_activity.xml b/app/src/main/res/layout/about_activity.xml index ce0fbb9d3..1bd649175 100644 --- a/app/src/main/res/layout/about_activity.xml +++ b/app/src/main/res/layout/about_activity.xml @@ -52,7 +52,7 @@ android:layout_height="wrap_content" android:layout_marginTop="20dp" android:autoLink="email" - android:text="Copyright (C) 2020-23 - Emanuele Faranda black.silver@hotmail.it" /> + android:text="Copyright (C) 2020-24 - Emanuele Faranda black.silver@hotmail.it" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/connection_item.xml b/app/src/main/res/layout/connection_item.xml index a73598fd1..25aa5a2da 100644 --- a/app/src/main/res/layout/connection_item.xml +++ b/app/src/main/res/layout/connection_item.xml @@ -67,6 +67,15 @@ android:contentDescription="@string/injected" android:src="@drawable/ic_baseline_javascript" /> + + TLS decryption is only applied to connections that match the configured rules. Do you want to create decryption rules now? Active VPN detected Battery optimization may interfere with the mitm addon + redirected + This connection has been redirected due to a port mapping rule From 26287640fdb4a23a787bf9f1abcdf72f6f9b658b Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 18:37:48 +0100 Subject: [PATCH 06/45] Add link to the PCAPdroid DoH docs See #211 --- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/root_preferences.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 10f80c7df..8e21871a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -496,4 +496,5 @@ Battery optimization may interfere with the mitm addon redirected This connection has been redirected due to a port mapping rule + How to use DoH / DNSCrypt with PCAPdroid diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 46d11a208..38d2865ed 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -52,6 +52,14 @@ app:iconSpaceReserved="false" app:defaultValue="true" /> + + + + Date: Sun, 28 Jan 2024 20:04:51 +0100 Subject: [PATCH 07/45] Ability to block QUIC always or only on decryption Commit ddec1a8 limited the ability to block QUIC to only connections matching the decryption rules. Some users may still want to always block QUIC for different reasons, so this commit makes it possible to choose the block policy to apply. See #369 --- .../remote_capture/CaptureService.java | 2 +- .../activities/prefs/SettingsActivity.java | 12 ++++----- .../remote_capture/model/CaptureSettings.java | 6 ++--- .../emanuelef/remote_capture/model/Prefs.java | 25 ++++++++++++++++--- app/src/main/jni/core/capture_vpn.c | 14 +++++++---- app/src/main/jni/core/pcapdroid.h | 9 ++++++- app/src/main/res/values/arrays.xml | 11 ++++++++ app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/root_preferences.xml | 10 +++++--- docs/app_api.md | 2 +- 10 files changed, 70 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index 7e1041655..6d9fb2671 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -1309,7 +1309,7 @@ public int getMitmAddonUid() { public int getVpnMTU() { return VPN_MTU; } - public int blockQuick() { return(mSettings.block_quic ? 1 : 0); } + public int getBlockQuickMode() { return mSettings.block_quic_mode.ordinal(); } // returns 1 if dumpPcapData should be called public int pcapDumpEnabled() { diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java index 223befadf..240c5266d 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java @@ -132,13 +132,13 @@ public void onBackPressed() { public static class SettingsFragment extends PreferenceFragmentCompat { private SwitchPreference mTlsDecryption; - private SwitchPreference mBlockQuic; private SwitchPreference mFullPayloadEnabled; private SwitchPreference mRootCaptureEnabled; private SwitchPreference mAutoBlockPrivateDNS; private EditTextPreference mMitmproxyOpts; private DropDownPreference mIpMode; private DropDownPreference mCapInterface; + private DropDownPreference mBlockQuic; private Preference mVpnExceptions; private Preference mSocks5Settings; private Preference mDnsSettings; @@ -167,7 +167,8 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setupSecurityPrefs(); setupOtherPrefs(); - socks5ProxyAndQuicHideShow(mTlsDecryption.isChecked(), rootCaptureEnabled()); + socks5ProxyHideShow(mTlsDecryption.isChecked(), rootCaptureEnabled()); + mBlockQuic.setVisible(!rootCaptureEnabled()); rootCaptureHideShow(rootCaptureEnabled()); Intent intent = requireActivity().getIntent(); @@ -309,7 +310,7 @@ private void setupTrafficInspectionPrefs() { mMitmWizard.setVisible((boolean) newValue); mMitmproxyOpts.setVisible((boolean) newValue); - socks5ProxyAndQuicHideShow((boolean) newValue, rootCaptureEnabled()); + socks5ProxyHideShow((boolean) newValue, rootCaptureEnabled()); return true; }); @@ -343,9 +344,8 @@ private void setupTrafficInspectionPrefs() { mSocks5Settings = requirePreference("socks5_settings"); } - private void socks5ProxyAndQuicHideShow(boolean tlsDecryption, boolean rootEnabled) { + private void socks5ProxyHideShow(boolean tlsDecryption, boolean rootEnabled) { mSocks5Settings.setVisible(!tlsDecryption && !rootEnabled); - mBlockQuic.setVisible(tlsDecryption && !rootEnabled); } private void setupOtherPrefs() { @@ -406,7 +406,7 @@ private void rootCaptureHideShow(boolean enabled) { } else { mAutoBlockPrivateDNS.setVisible(true); mBlockQuic.setVisible(true); - socks5ProxyAndQuicHideShow(mTlsDecryption.isChecked(), false); + socks5ProxyHideShow(mTlsDecryption.isChecked(), false); } mIpMode.setVisible(!enabled); diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java index 5c752308b..a7aa65d1e 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java @@ -26,7 +26,7 @@ public class CaptureSettings implements Serializable { public boolean root_capture; public boolean pcapdroid_trailer; public boolean full_payload; - public boolean block_quic; + public Prefs.BlockQuicMode block_quic_mode; public boolean auto_block_private_dns; public boolean pcapng_format; public String capture_interface; @@ -54,7 +54,7 @@ public CaptureSettings(Context ctx, SharedPreferences prefs) { capture_interface = Prefs.getCaptureInterface(prefs); tls_decryption = Prefs.getTlsDecryptionEnabled(prefs); full_payload = Prefs.getFullPayloadMode(prefs); - block_quic = Prefs.blockQuic(prefs); + block_quic_mode = Prefs.getBlockQuicMode(prefs); auto_block_private_dns = Prefs.isPrivateDnsBlockingEnabled(prefs); mitmproxy_opts = Prefs.getMitmproxyOpts(prefs); pcapng_format = Prefs.isPcapngEnabled(ctx, prefs); @@ -82,7 +82,7 @@ public CaptureSettings(Context ctx, Intent intent) { max_dump_size = getInt(intent, Prefs.PREF_MAX_DUMP_SIZE, 0); tls_decryption = getBool(intent, Prefs.PREF_TLS_DECRYPTION_KEY, false); full_payload = false; - block_quic = getBool(intent, Prefs.PREF_BLOCK_QUIC, false); + block_quic_mode = Prefs.getBlockQuicMode(getString(intent, Prefs.PREF_BLOCK_QUIC, Prefs.BLOCK_QUIC_MODE_DEFAULT)); auto_block_private_dns = getBool(intent, Prefs.PREF_AUTO_BLOCK_PRIVATE_DNS, true); mitmproxy_opts = getString(intent, Prefs.PREF_MITMPROXY_OPTS, ""); pcapng_format = getBool(intent, Prefs.PREF_PCAPNG_ENABLED, false) && Billing.newInstance(ctx).isPurchased(Billing.PCAPNG_SKU); 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 474434d39..b9b7712c6 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 @@ -41,6 +41,11 @@ public class Prefs { public static final String IP_MODE_BOTH = "both"; public static final String IP_MODE_DEFAULT = IP_MODE_IPV4_ONLY; + public static final String BLOCK_QUIC_MODE_NEVER = "never"; + public static final String BLOCK_QUIC_MODE_ALWAYS = "always"; + public static final String BLOCK_QUIC_MODE_TO_DECRYPT = "to_decrypt"; + public static final String BLOCK_QUIC_MODE_DEFAULT = BLOCK_QUIC_MODE_NEVER; + public static final String PAYLOAD_MODE_NONE = "none"; public static final String PAYLOAD_MODE_MINIMAL = "minimal"; public static final String PAYLOAD_MODE_FULL = "full"; @@ -83,7 +88,7 @@ public class Prefs { public static final String PREF_TLS_DECRYPTION_SETUP_DONE = "tls_decryption_setup_ok"; public static final String PREF_CA_INSTALLATION_SKIPPED = "ca_install_skipped"; public static final String PREF_FULL_PAYLOAD = "full_payload"; - public static final String PREF_BLOCK_QUIC = "block_quic"; + public static final String PREF_BLOCK_QUIC = "block_quic_mode"; public static final String PREF_AUTO_BLOCK_PRIVATE_DNS = "auto_block_private_dns"; public static final String PREF_APP_VERSION = "appver"; public static final String PREF_LOCKDOWN_VPN_NOTICE_SHOWN = "vpn_lockdown_notice"; @@ -113,6 +118,12 @@ public enum IpMode { BOTH, } + public enum BlockQuicMode { + NEVER, + ALWAYS, + TO_DECRYPT + } + public enum PayloadMode { NONE, MINIMAL, @@ -136,6 +147,14 @@ public static IpMode getIPMode(String pref) { } } + public static BlockQuicMode getBlockQuicMode(String pref) { + switch (pref) { + case BLOCK_QUIC_MODE_ALWAYS: return BlockQuicMode.ALWAYS; + case BLOCK_QUIC_MODE_TO_DECRYPT: return BlockQuicMode.TO_DECRYPT; + default: return BlockQuicMode.NEVER; + } + } + public static PayloadMode getPayloadMode(String pref) { switch (pref) { case PAYLOAD_MODE_MINIMAL: return PayloadMode.MINIMAL; @@ -182,6 +201,7 @@ public static void setPortMappingEnabled(SharedPreferences p, boolean enabled) { public static String getSocks5Password(SharedPreferences p) { return(p.getString(PREF_SOCKS5_PASSWORD_KEY, "")); } public static String getAppFilter(SharedPreferences p) { return(p.getString(PREF_APP_FILTER, "")); } public static IpMode getIPMode(SharedPreferences p) { return(getIPMode(p.getString(PREF_IP_MODE, IP_MODE_DEFAULT))); } + public static BlockQuicMode getBlockQuicMode(SharedPreferences p) { return(getBlockQuicMode(p.getString(PREF_BLOCK_QUIC, BLOCK_QUIC_MODE_DEFAULT))); } public static boolean useEnglishLanguage(SharedPreferences p){ return("english".equals(p.getString(PREF_APP_LANGUAGE, "system")));} public static boolean isRootCaptureEnabled(SharedPreferences p) { return(Utils.isRootAvailable() && p.getBoolean(PREF_ROOT_CAPTURE, false)); } public static boolean isPcapdroidTrailerEnabled(SharedPreferences p) { return(p.getBoolean(PREF_PCAPDROID_TRAILER, false)); } @@ -202,7 +222,6 @@ public static boolean isPcapngEnabled(Context ctx, SharedPreferences p) { public static boolean startAtBoot(SharedPreferences p) { return(p.getBoolean(PREF_START_AT_BOOT, false)); } public static boolean isTLSDecryptionSetupDone(SharedPreferences p) { return(p.getBoolean(PREF_TLS_DECRYPTION_SETUP_DONE, false)); } public static boolean getFullPayloadMode(SharedPreferences p) { return(p.getBoolean(PREF_FULL_PAYLOAD, false)); } - public static boolean blockQuic(SharedPreferences p) { return(p.getBoolean(PREF_BLOCK_QUIC, false)); } public static boolean isPrivateDnsBlockingEnabled(SharedPreferences p) { return(p.getBoolean(PREF_AUTO_BLOCK_PRIVATE_DNS, true)); } public static boolean lockdownVpnNoticeShown(SharedPreferences p) { return(p.getBoolean(PREF_LOCKDOWN_VPN_NOTICE_SHOWN, false)); } public static boolean trailerNoticeShown(SharedPreferences p) { return(p.getBoolean(PREF_PCAPDROID_TRAILER_NOTICE_SHOWN, false)); } @@ -224,7 +243,7 @@ public static String asString(Context ctx) { "\nTLSDecryption: " + getTlsDecryptionEnabled(p) + "\nTLSSetupOk: " + isTLSDecryptionSetupDone(p) + "\nCAInstallSkipped: " + MitmAddon.isCAInstallationSkipped(ctx) + - "\nBlockQuic: " + blockQuic(p) + + "\nBlockQuic: " + getBlockQuicMode(p) + "\nRootCapture: " + isRootCaptureEnabled(p) + "\nSocks5: " + getSocks5Enabled(p) + "\nBlockPrivateDns: " + isPrivateDnsBlockingEnabled(p) + diff --git a/app/src/main/jni/core/capture_vpn.c b/app/src/main/jni/core/capture_vpn.c index 1ca852c1a..e653a2288 100644 --- a/app/src/main/jni/core/capture_vpn.c +++ b/app/src/main/jni/core/capture_vpn.c @@ -385,10 +385,14 @@ static bool should_proxify(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn /* ******************************************************* */ void vpn_process_ndpi(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn_t *data) { - if(pd->vpn.block_quic && (data->l7proto == NDPI_PROTOCOL_QUIC) && - pd->tls_decryption.enabled && matches_decryption_whitelist(pd, tuple, data)) { - data->blacklisted_internal = true; - data->to_block = true; + if(data->l7proto == NDPI_PROTOCOL_QUIC) { + block_quic_mode_t block_mode = pd->vpn.block_quic_mode; + + if ((block_mode == BLOCK_QUIC_MODE_ALWAYS) || + ((block_mode == BLOCK_QUIC_MODE_TO_DECRYPT) && matches_decryption_whitelist(pd, tuple, data))) { + data->blacklisted_internal = true; + data->to_block = true; + } } if(block_private_dns && !data->to_block && @@ -449,7 +453,7 @@ int run_vpn(pcapdroid_t *pd) { #if ANDROID pd->vpn.resolver = init_uid_resolver(pd->sdk_ver, pd->env, pd->capture_service); pd->vpn.known_dns_servers = blacklist_init(); - pd->vpn.block_quic = getIntPref(pd->env, pd->capture_service, "blockQuick"); + pd->vpn.block_quic_mode = getIntPref(pd->env, pd->capture_service, "getBlockQuickMode"); pd->vpn.ipv4.enabled = (bool) getIntPref(pd->env, pd->capture_service, "getIPv4Enabled"); pd->vpn.ipv4.dns_server = getIPv4Pref(pd->env, pd->capture_service, "getDnsServer"); diff --git a/app/src/main/jni/core/pcapdroid.h b/app/src/main/jni/core/pcapdroid.h index 045b30786..c90badb00 100644 --- a/app/src/main/jni/core/pcapdroid.h +++ b/app/src/main/jni/core/pcapdroid.h @@ -65,6 +65,13 @@ typedef enum { PAYLOAD_MODE_FULL } payload_mode_t; +// NOTE: sync with Prefs.BlockQuicMode +typedef enum { + BLOCK_QUIC_MODE_NEVER = 0, + BLOCK_QUIC_MODE_ALWAYS, + BLOCK_QUIC_MODE_TO_DECRYPT +} block_quic_mode_t; + typedef struct { jint incr_id; // an incremental number which identifies a specific connection @@ -205,7 +212,7 @@ typedef struct pcapdroid { union { struct { int tunfd; - bool block_quic; + block_quic_mode_t block_quic_mode; blacklist_t *known_dns_servers; uid_resolver_t *resolver; diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 122ce7568..d33152e2a 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -49,4 +49,15 @@ @string/theme_light @string/theme_dark + + + never + always + to_decrypt + + + @string/never + @string/always + @string/for_connections_to_decrypt + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e21871a0..7c7dc3ade 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -497,4 +497,7 @@ redirected This connection has been redirected due to a port mapping rule How to use DoH / DNSCrypt with PCAPdroid + Never + Always + Only for connections to decrypt diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 38d2865ed..3923a96f9 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -94,12 +94,14 @@ app:summary="@string/pcapng_format_summary" app:defaultValue="true" /> - + app:defaultValue="never" + app:useSimpleSummaryProvider="true" /> Date: Sun, 28 Jan 2024 21:16:40 +0100 Subject: [PATCH 08/45] Restore "block_quic" intent parameter name --- .../com/emanuelef/remote_capture/model/CaptureSettings.java | 2 +- docs/app_api.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java index a7aa65d1e..d46c01d41 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java @@ -82,7 +82,7 @@ public CaptureSettings(Context ctx, Intent intent) { max_dump_size = getInt(intent, Prefs.PREF_MAX_DUMP_SIZE, 0); tls_decryption = getBool(intent, Prefs.PREF_TLS_DECRYPTION_KEY, false); full_payload = false; - block_quic_mode = Prefs.getBlockQuicMode(getString(intent, Prefs.PREF_BLOCK_QUIC, Prefs.BLOCK_QUIC_MODE_DEFAULT)); + block_quic_mode = Prefs.getBlockQuicMode(getString(intent, "block_quic", Prefs.BLOCK_QUIC_MODE_DEFAULT)); auto_block_private_dns = getBool(intent, Prefs.PREF_AUTO_BLOCK_PRIVATE_DNS, true); mitmproxy_opts = getString(intent, Prefs.PREF_MITMPROXY_OPTS, ""); pcapng_format = getBool(intent, Prefs.PREF_PCAPNG_ENABLED, false) && Billing.newInstance(ctx).isPurchased(Billing.PCAPNG_SKU); diff --git a/docs/app_api.md b/docs/app_api.md index da989ad92..d590b6bae 100644 --- a/docs/app_api.md +++ b/docs/app_api.md @@ -101,7 +101,7 @@ As shown above, the capture settings can be specified by using intent extras. Th | pcapng_format | bool | 62 | | true to use the PCAPNG dump format (overrides pcapdroid_trailer)* | | socks5_username | string | 64 | vpn | username for the optional SOCKS5 proxy authentication | | socks5_password | string | 64 | vpn | password for the optional SOCKS5 proxy authentication | -| block_quic_mode | string | 73 | vpn | never | always | to_decrypt (matching the decryption whitelist) | +| block_quic | string | 73 | vpn | never | always | to_decrypt (matching the decryption whitelist) | \*: paid feature From bb08878419c3803dd1a3d1b60cc773ba587b2ad0 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 21:37:29 +0100 Subject: [PATCH 09/45] Fix decryption status for QUIC connections QUIC connections should be marked as "Not decryptable" when QUIC is not blocked, instead were reported as "Encrypted" with an incorrect message telling to create a decryption rule for them --- .../model/ConnectionDescriptor.java | 6 ++--- app/src/main/jni/core/capture_vpn.c | 23 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) 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 decf1b229..a5d859d0f 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 @@ -112,7 +112,7 @@ public enum FilteringStatus { private boolean blacklisted_host; public boolean is_blocked; private boolean port_mapping_applied; - public boolean decryption_ignored; + private boolean decryption_ignored; public boolean netd_block_missed; private boolean payload_truncated; private boolean encrypted_l7; // application layer is encrypted (e.g. TLS) @@ -255,10 +255,10 @@ public DecryptionStatus getDecryptionStatus() { return DecryptionStatus.CLEARTEXT; else if(decryption_error != null) return DecryptionStatus.ERROR; - else if(decryption_ignored) - return DecryptionStatus.ENCRYPTED; else if(isNotDecryptable()) return DecryptionStatus.NOT_DECRYPTABLE; + else if(decryption_ignored) + return DecryptionStatus.ENCRYPTED; else if(isDecrypted()) return DecryptionStatus.DECRYPTED; else diff --git a/app/src/main/jni/core/capture_vpn.c b/app/src/main/jni/core/capture_vpn.c index e653a2288..0e681eafb 100644 --- a/app/src/main/jni/core/capture_vpn.c +++ b/app/src/main/jni/core/capture_vpn.c @@ -362,24 +362,23 @@ static bool matches_decryption_whitelist(pcapdroid_t *pd, const zdtun_5tuple_t * /* ******************************************************* */ +// NOTE: this handles both user-specified SOCKS5 and TLS decryption static bool should_proxify(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn_t *data) { - // NOTE: connections must be proxified as soon as the first packet arrives. - // In case of TLS decryption, since we cannot reliably determine TLS connections with 1 packet, - // we must proxify all the TCP connections. - if(!pd->socks5.enabled || (tuple->ipproto != IPPROTO_TCP)) { - data->decryption_ignored = true; + if(!pd->socks5.enabled) return false; - } - if(pd->tls_decryption.list) { - if(matches_decryption_whitelist(pd, tuple, data)) - return true; + if (pd->tls_decryption.list) { + // TLS decryption + if(!matches_decryption_whitelist(pd, tuple, data)) { + data->decryption_ignored = true; + return false; + } - data->decryption_ignored = true; - return false; + // Since we cannot reliably determine TLS connections with 1 packet, and connections must be + // proxified on the 1st packet, we proxify all the TCP connections } - return true; + return (tuple->ipproto == IPPROTO_TCP); } /* ******************************************************* */ From a0b5fd7196a409daa7bda57cb987fb74b1f47e01 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 21:47:12 +0100 Subject: [PATCH 10/45] Add notice about QUIC decryption --- .../remote_capture/fragments/ConnectionOverview.java | 5 +++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java index 1a7f76974..2226f2ff4 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java @@ -308,6 +308,11 @@ public void connectionUpdated() { mError.setTextColor(ContextCompat.getColor(context, R.color.colorTabText)); mError.setText(R.string.decryption_info_no_rule); mError.setVisibility(View.VISIBLE); + } else if((mConn.getDecryptionStatus() == ConnectionDescriptor.DecryptionStatus.NOT_DECRYPTABLE) + && mConn.l7proto.equals("QUIC")) { + mError.setTextColor(ContextCompat.getColor(context, R.color.warning)); + mError.setText(R.string.decrypt_quic_notice); + mError.setVisibility(View.VISIBLE); } else mError.setVisibility(View.GONE); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c7dc3ade..58d274214 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -500,4 +500,5 @@ Never Always Only for connections to decrypt + Decrypting QUIC is currently not supported. As a workaround, stop the capture and select the option to block QUIC in the PCAPdroid settings From 1bab9e0a7fc1e2c33a72550884212e7bf23c1a82 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Sun, 28 Jan 2024 22:52:15 +0100 Subject: [PATCH 11/45] Update mitm addon with fixes for --ignore-hosts and --allow-hosts --- app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java index 363e95f39..2543c9919 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java @@ -49,8 +49,8 @@ import java.lang.ref.WeakReference; public class MitmAddon { - public static final long PACKAGE_VERSION_CODE = 17; - public static final String PACKAGE_VERSION_NAME = "v1.0"; + public static final long PACKAGE_VERSION_CODE = 18; + public static final String PACKAGE_VERSION_NAME = "v1.1"; public static final String REPOSITORY = "https://github.com/emanuele-f/PCAPdroid-mitm"; private static final String TAG = "MitmAddon"; private final Context mContext; From f9d36008efc573aa84fc63c2a819f0c6c8859fa3 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Tue, 30 Jan 2024 10:54:30 +0100 Subject: [PATCH 12/45] Fix no-filter warning with root decryption The warning incorrectly referenced the decryption whitelist, which is not actually applied to the root capture --- .../emanuelef/remote_capture/fragments/StatusFragment.java | 7 +++---- app/src/main/res/layout/status.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java index dd1aedab9..613b6198e 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -77,7 +76,7 @@ public class StatusFragment extends Fragment implements AppStateListener, MenuPr private TextView mFilterDescription; private SwitchCompat mAppFilterSwitch; private String mAppFilter; - private TextView mFilterWarning; + private TextView mFilterRootDecryptionWarning; private AppSelectDialog mAppSelDialog; @Override @@ -115,7 +114,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat mCollectorInfo = view.findViewById(R.id.collector_info); mCaptureStatus = view.findViewById(R.id.status_view); mQuickSettings = view.findViewById(R.id.quick_settings); - mFilterWarning = view.findViewById(R.id.app_filter_warning); + mFilterRootDecryptionWarning = view.findViewById(R.id.app_filter_root_decryption_warning); mPrefs = PreferenceManager.getDefaultSharedPreferences(mActivity); mAppFilter = Prefs.getAppFilter(mPrefs); @@ -194,7 +193,7 @@ public boolean onMenuItemSelected(@NonNull MenuItem item) { private void recheckFilterWarning() { boolean hasFilter = ((mAppFilter != null) && (!mAppFilter.isEmpty())); - mFilterWarning.setVisibility((Prefs.getTlsDecryptionEnabled(mPrefs) && + mFilterRootDecryptionWarning.setVisibility((Prefs.getTlsDecryptionEnabled(mPrefs) && Prefs.isRootCaptureEnabled(mPrefs) && !hasFilter) ? View.VISIBLE : View.GONE); } diff --git a/app/src/main/res/layout/status.xml b/app/src/main/res/layout/status.xml index 3cd17281f..d128707f1 100644 --- a/app/src/main/res/layout/status.xml +++ b/app/src/main/res/layout/status.xml @@ -91,7 +91,7 @@ android:layout_centerInParent="true" /> Learn more about the malware detection feature Private DNS prevents PCAPdroid from inspecting the DNS traffic. You can turn it off from the Android network settings Private DNS hinders detection - Select a target app or use the decryption whitelist when decrypting TLS to avoid losing your connection to the Internet + Select a target app when decrypting TLS to avoid losing your connection to the Internet Block… Unblock… Firewall From 59d283b2732d560778fadfba50f90c48d1204f1e Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Wed, 31 Jan 2024 12:16:33 +0100 Subject: [PATCH 13/45] pcapd: add ability to specify multiple uid filters The -u parameter now accepts a list of comma-separated UID filters --- app/src/main/jni/pcapd/pcapd.c | 78 ++++++++++++++++++++++++----- app/src/main/jni/pcapd/pcapd_priv.h | 2 +- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/app/src/main/jni/pcapd/pcapd.c b/app/src/main/jni/pcapd/pcapd.c index dd4c74615..fc56699eb 100644 --- a/app/src/main/jni/pcapd/pcapd.c +++ b/app/src/main/jni/pcapd/pcapd.c @@ -88,6 +88,62 @@ static int str2mac(const char *buf, uint64_t *mac) { /* ******************************************************* */ +static int* parse_uid_filter(char *s) { + int num_uids = 1; + + for(int i=0; s[i]; i++) { + if(s[i] == ',') + num_uids++; + } + + int* rv = malloc((num_uids + 1 /* terminator */) * sizeof(int)); + if(rv == NULL) { + fprintf(stderr, "parse_uid_filter: malloc failed[%d]: %s", + errno, strerror(errno)); + exit(PCAPD_ERROR); + } + + int i = 0; + char *token; + char *tmp; + token = strtok_r(s, ",", &tmp); + + while(token && (i < num_uids)) { + int uid = atoi(token); + if(uid < -1) { + fprintf(stderr, "Invalid UID: %s\n", token); + exit(PCAPD_ERROR); + } + + if(uid != -1) + rv[i++] = uid; + + token = strtok_r(NULL, ",", &tmp); + } + + // terminator + rv[i++] = -1; + + return rv; +} + +/* ******************************************************* */ + +static int matches_uid_filter(const int *filter, int uid) { + if (!filter || (*filter == -1)) + return 1; + + while (*filter != -1) { + if (*filter == uid) + return 1; + } + + // no match + return 0; +} + +/* ******************************************************* */ + static void logcb(int lvl, const char *msg) { char datetime[64]; struct tm res; @@ -779,7 +835,7 @@ static pcapd_rv read_pkt(pcapd_runtime_t *rt, pcapd_iface_t *iface, time_t now) } // export packet even if zdtun_parse_pkt failed - if((rt->conf->uid_filter == -1) || (rt->conf->uid_filter == uid)) { + if(!rt->conf->uid_filter || matches_uid_filter(rt->conf->uid_filter, uid)) { if(rt->conf->dump_datalink) { // Include the datalink header pkt -= to_skip; @@ -928,10 +984,10 @@ pcapd_rv run_pcap_dump(pcapd_conf_t *conf) { for(int i=0; inum_interfaces; i++) free(conf->ifnames[i]); - if(conf->bpf) - free(conf->bpf); - if(conf->log_file) - free(conf->log_file); + free(conf->uid_filter); + free(conf->bpf); + free(conf->log_file); + if(logf) fclose(logf); @@ -949,7 +1005,7 @@ static void usage() { " the internet interface\n" " -d daemonize the process\n" " -t dump the interface datalink header. Default: don't dump\n" - " -u [uid] filter packets by uid\n" + " -u [uid, ...] filter packets by the specified UIDs\n" " -b [bpf] filter packets by BPF filter\n" " -l [file] log output to the specified file\n" " -L uid specify the UID to use to create the log file\n" @@ -964,7 +1020,6 @@ static void usage() { void init_conf(pcapd_conf_t *conf) { memset(conf, 0, sizeof(pcapd_conf_t)); - conf->uid_filter = -1; conf->inet_ifid = -1; } @@ -1002,11 +1057,10 @@ static void parse_args(pcapd_conf_t *conf, int argc, char **argv) { conf->no_client = 1; break; case 'u': - conf->uid_filter = atoi(optarg); - if(conf->uid_filter < -1) { - fprintf(stderr, "Invalid UID: %s\n", optarg); - exit(PCAPD_ERROR); - } + if (conf->uid_filter) + free(conf->uid_filter); + + conf->uid_filter = parse_uid_filter(optarg); break; case 'b': if(conf->bpf) free(conf->bpf); diff --git a/app/src/main/jni/pcapd/pcapd_priv.h b/app/src/main/jni/pcapd/pcapd_priv.h index 65368a79e..355d3f172 100644 --- a/app/src/main/jni/pcapd/pcapd_priv.h +++ b/app/src/main/jni/pcapd/pcapd_priv.h @@ -38,7 +38,7 @@ typedef struct { char *ifnames[PCAPD_MAX_INTERFACES]; char *bpf; char *log_file; - int uid_filter; + int* uid_filter; int num_interfaces; int inet_ifid; uint8_t dump_datalink; From 8ac6428bf1585b9a861fb05bf1348276b2b07727 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Tue, 30 Jan 2024 18:52:37 +0100 Subject: [PATCH 14/45] Allow capturing multiple target apps The target app option has been extended to allow the selection of multiple apps. Closes #146 --- app/src/main/AndroidManifest.xml | 4 + .../remote_capture/CaptureService.java | 39 +++-- .../activities/AppFilterActivity.java | 142 +++++++++++++++++ .../prefs/VpnExemptionsActivity.java | 10 +- .../fragments/StatusFragment.java | 144 +++++++++--------- .../remote_capture/model/CaptureSettings.java | 28 +++- .../emanuelef/remote_capture/model/Prefs.java | 34 ++++- app/src/main/jni/core/capture_libpcap.c | 73 +++++++-- app/src/main/jni/core/jni_impl.c | 38 ++++- app/src/main/jni/core/pcapdroid.c | 2 +- app/src/main/jni/core/pcapdroid.h | 7 +- app/src/main/jni/pcapd/pcapd.c | 2 +- app/src/main/jni/tests/test_utils.c | 1 - app/src/main/res/menu/hint_menu.xml | 12 ++ app/src/main/res/values/strings.xml | 2 + docs/app_api.md | 4 +- 16 files changed, 430 insertions(+), 112 deletions(-) create mode 100644 app/src/main/java/com/emanuelef/remote_capture/activities/AppFilterActivity.java create mode 100644 app/src/main/res/menu/hint_menu.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f9157e14f..fc05e68bd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,10 @@ + uids = new ArrayList<>(); + + for (String package_name: mSettings.app_filter) { + int uid; + + try { + uid = Utils.getPackageUid(getPackageManager(), package_name, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + continue; + } + + uids.add(uid); } + + // populate the array only with resolved UIDs + mAppFilterUids = new int[uids.size()]; + + int i = 0; + for (Integer uid: uids) + mAppFilterUids[i++] = uid; } else - app_filter_uid = -1; + mAppFilterUids = new int[0]; mMalwareDetectionEnabled = Prefs.isMalwareDetectionEnabled(this, mPrefs); mFirewallEnabled = Prefs.isFirewallEnabled(this, mPrefs); @@ -458,7 +474,8 @@ else if(mSettings.dump_mode == Prefs.DumpMode.PCAP_FILE) { // NOTE: the API requires a package name, however it is converted to a UID // (see Vpn.java addUserToRanges). This means that vpn routing happens on a UID basis, // not on a package-name basis! - builder.addAllowedApplication(mSettings.app_filter); + for (String package_name: mSettings.app_filter) + builder.addAllowedApplication(package_name); } catch (PackageManager.NameNotFoundException e) { String msg = String.format(getResources().getString(R.string.app_not_found), mSettings.app_filter); Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); @@ -958,7 +975,7 @@ private String getIfname(int ifidx) { return rv; } - public static String getAppFilter() { + public static Set getAppFilter() { return((INSTANCE != null) ? INSTANCE.mSettings.app_filter : null); } @@ -1291,7 +1308,7 @@ public String getDnsServer() { public int isPcapngEnabled() { return(mSettings.pcapng_format ? 1 : 0); } - public int getAppFilterUid() { return(app_filter_uid); } + public int[] getAppFilterUids() { return(mAppFilterUids); } public int getMitmAddonUid() { return MitmAddon.getUid(this); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/AppFilterActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/AppFilterActivity.java new file mode 100644 index 000000000..d9c646db3 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/AppFilterActivity.java @@ -0,0 +1,142 @@ +package com.emanuelef.remote_capture.activities; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.MenuProvider; +import androidx.preference.PreferenceManager; + +import com.emanuelef.remote_capture.Log; +import com.emanuelef.remote_capture.R; +import com.emanuelef.remote_capture.Utils; +import com.emanuelef.remote_capture.fragments.AppsToggles; +import com.emanuelef.remote_capture.model.AppDescriptor; +import com.emanuelef.remote_capture.model.Prefs; + +import java.util.HashSet; +import java.util.Set; + +public class AppFilterActivity extends BaseActivity implements MenuProvider { + private static final String TAG = "AppFilterActivity"; + private AppFilterFragment mFragment; + + @Override + @SuppressWarnings("deprecation") + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle(R.string.target_apps); + setContentView(R.layout.fragment_activity); + addMenuProvider(this); + displayBackAction(); + + if (savedInstanceState != null) + mFragment = (AppFilterFragment) getSupportFragmentManager().getFragment(savedInstanceState, "fragment"); + if (mFragment == null) + mFragment = new AppFilterFragment(); + + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragment, mFragment) + .commit(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, 0, 0); + else + overridePendingTransition(0, 0); + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + getSupportFragmentManager().putFragment(outState, "fragment", mFragment); + } + + @Override + public void onCreateMenu(@NonNull Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.hint_menu, menu); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem item) { + int id = item.getItemId(); + + if (id == R.id.show_hint) { + Utils.showHelpDialog(this, R.string.target_apps_help); + return true; + } + + return false; + } + + @Override + @SuppressWarnings("deprecation") + public void onBackPressed() { + if(mFragment.onBackPressed()) + return; + + super.onBackPressed(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, 0, 0); + else + overridePendingTransition(0, 0); + } + + public static class AppFilterFragment extends AppsToggles { + private final Set mSelectedApps = new HashSet<>(); + private @Nullable SharedPreferences mPrefs; + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + mPrefs = PreferenceManager.getDefaultSharedPreferences(context); + assert mPrefs != null; + + mSelectedApps.clear(); + + Set saved = Prefs.getStringSet(mPrefs, Prefs.PREF_APP_FILTER); + if(!saved.isEmpty()) { + Log.d(TAG, "Loading " + saved.size() + " target apps"); + mSelectedApps.addAll(saved); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mPrefs = null; + } + + @Override + protected Set getCheckedApps() { + return mSelectedApps; + } + + @Override + public void onAppToggled(AppDescriptor app, boolean checked) { + String packageName = app.getPackageName(); + if(mSelectedApps.contains(packageName) == checked) + return; // nothing to do + + if(checked) + mSelectedApps.add(packageName); + else + mSelectedApps.remove(packageName); + + Log.d(TAG, "Saving " + mSelectedApps.size() + " target apps"); + + if(mPrefs == null) + return; + + mPrefs.edit() + .putStringSet(Prefs.PREF_APP_FILTER, mSelectedApps) + .apply(); + } + } +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/VpnExemptionsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/VpnExemptionsActivity.java index de1fc69c0..2a7c852f2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/VpnExemptionsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/VpnExemptionsActivity.java @@ -46,6 +46,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle(R.string.vpn_exemptions); setContentView(R.layout.fragment_activity); + displayBackAction(); if(savedInstanceState != null) mFragment = (VpnExceptionsFragment) getSupportFragmentManager().getFragment(savedInstanceState, "fragment"); @@ -63,15 +64,6 @@ protected void onSaveInstanceState(@NonNull Bundle outState) { getSupportFragmentManager().putFragment(outState, "fragment", mFragment); } - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if(item.getItemId() == android.R.id.home) { - onBackPressed(); - return true; - } - return super.onOptionsItemSelected(item); - } - @Override @SuppressWarnings("deprecation") public void onBackPressed() { diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java index 613b6198e..2956a1b60 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java @@ -14,19 +14,21 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-24 - Emanuele Faranda */ package com.emanuelef.remote_capture.fragments; import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.method.LinkMovementMethod; +import android.util.Pair; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -38,8 +40,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.SwitchCompat; +import androidx.core.content.ContextCompat; import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.Lifecycle; @@ -48,6 +50,7 @@ import com.emanuelef.remote_capture.AppsResolver; import com.emanuelef.remote_capture.Log; import com.emanuelef.remote_capture.MitmReceiver; +import com.emanuelef.remote_capture.activities.AppFilterActivity; import com.emanuelef.remote_capture.model.AppDescriptor; import com.emanuelef.remote_capture.model.AppState; import com.emanuelef.remote_capture.CaptureService; @@ -57,9 +60,11 @@ import com.emanuelef.remote_capture.interfaces.AppStateListener; import com.emanuelef.remote_capture.model.Prefs; import com.emanuelef.remote_capture.model.CaptureStats; -import com.emanuelef.remote_capture.views.AppSelectDialog; import com.emanuelef.remote_capture.views.PrefSpinner; +import java.util.ArrayList; +import java.util.Set; + public class StatusFragment extends Fragment implements AppStateListener, MenuProvider { private static final String TAG = "StatusFragment"; private Handler mHandler; @@ -75,9 +80,8 @@ public class StatusFragment extends Fragment implements AppStateListener, MenuPr private SharedPreferences mPrefs; private TextView mFilterDescription; private SwitchCompat mAppFilterSwitch; - private String mAppFilter; + private Set mAppFilter; private TextView mFilterRootDecryptionWarning; - private AppSelectDialog mAppSelDialog; @Override public void onAttach(@NonNull Context context) { @@ -88,7 +92,6 @@ public void onAttach(@NonNull Context context) { @Override public void onDetach() { super.onDetach(); - abortAppSelection(); mActivity.setAppStateListener(null); mActivity = null; } @@ -145,14 +148,11 @@ public void onGlobalLayout() { }); } - filterTitle.setText(R.string.app_filter); + filterTitle.setText(R.string.target_apps); - mAppFilterSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if(isChecked) { - if((mAppFilter == null) || (mAppFilter.isEmpty())) - openAppFilterSelector(); - } else - setAppFilter(null); + mAppFilterSwitch.setOnClickListener((buttonView) -> { + mAppFilterSwitch.setChecked(!mAppFilterSwitch.isChecked()); + openAppFilterSelector(); }); refreshFilterInfo(); @@ -222,34 +222,20 @@ private void refreshFilterInfo() { mAppFilterSwitch.setChecked(true); - AppDescriptor app = AppsResolver.resolveInstalledApp(context.getPackageManager(), mAppFilter, 0); - String description; + Pair pair = getAppFilterTextAndIcon(context); + + mFilterDescription.setText(pair.first); - if(app == null) - description = mAppFilter; - else { - description = app.getName() + " (" + app.getPackageName() + ")"; + if (pair.second != null) { int height = mFilterDescription.getMeasuredHeight(); - if((height > 0) && (app.getIcon() != null)) { - Drawable drawable = Utils.scaleDrawable(context.getResources(), app.getIcon(), height, height); + if(height > 0) { + Drawable drawable = Utils.scaleDrawable(context.getResources(), pair.second, height, height); if(drawable != null) mFilterDescription.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); } } - - mFilterDescription.setText(description); - } - - private void setAppFilter(AppDescriptor filter) { - SharedPreferences.Editor editor = mPrefs.edit(); - mAppFilter = (filter != null) ? filter.getPackageName() : ""; - - editor.putString(Prefs.PREF_APP_FILTER, mAppFilter); - editor.apply(); - refreshFilterInfo(); - recheckFilterWarning(); } private void onStatsUpdate(CaptureStats stats) { @@ -259,6 +245,42 @@ private void onStatsUpdate(CaptureStats stats) { mCaptureStatus.setText(Utils.formatBytes(stats.bytes_sent + stats.bytes_rcvd)); } + private Pair getAppFilterTextAndIcon(@NonNull Context context) { + Drawable icon = null; + String text = ""; + + if((mAppFilter != null) && (!mAppFilter.isEmpty())) { + if (mAppFilter.size() == 1) { + // only a single app is selected, show its image and text + String package_name = mAppFilter.iterator().next(); + AppDescriptor app = AppsResolver.resolveInstalledApp(requireContext().getPackageManager(), package_name, 0); + + if((app != null) && (app.getIcon() != null)) { + icon = app.getIcon(); + text = app.getName() + " (" + app.getPackageName() + ")"; + } + } else { + // multiple apps, show default icon and comprehensive text + icon = ContextCompat.getDrawable(context, R.drawable.ic_image); + ArrayList parts = new ArrayList<>(); + + for (String package_name: mAppFilter) { + AppDescriptor app = AppsResolver.resolveInstalledApp(requireContext().getPackageManager(), package_name, 0); + String tmp = package_name; + + if (app != null) + tmp = app.getName(); + + parts.add(tmp); + } + + text = Utils.shorten(String.join(", ", parts), 48); + } + } + + return new Pair<>(text, icon); + } + private void refreshPcapDumpInfo() { String info = ""; @@ -287,26 +309,29 @@ private void refreshPcapDumpInfo() { mCollectorInfo.setText(info); - // Check if a filter is set - if((mAppFilter != null) && (!mAppFilter.isEmpty())) { - AppDescriptor app = AppsResolver.resolveInstalledApp(requireContext().getPackageManager(), mAppFilter, 0); + // Rendering after mCollectorInfo.setText is deferred, so getMeasuredHeight must be postponed + mHandler.post(() -> { + Context context = getContext(); + if(context == null) + return; - if((app != null) && (app.getIcon() != null)) { - // Rendering after mCollectorInfo.setText is deferred, so getMeasuredHeight must be postponed - mHandler.post(() -> { - if(getContext() == null) - return; + // Check if a filter is set + if((mAppFilter != null) && (!mAppFilter.isEmpty())) { + Pair pair = getAppFilterTextAndIcon(context); + if (pair.second != null) { + // scale and set int height = mCollectorInfo.getMeasuredHeight(); - Drawable drawable = Utils.scaleDrawable(getResources(), app.getIcon(), height, height); + Drawable drawable = Utils.scaleDrawable(getResources(), pair.second, height, height); - if(drawable != null) + if (drawable != null) mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null); - }); - } else - mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); - } else - mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + } + } + }); + + // will be overriden in the above handler if necessary + mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); } @Override @@ -386,26 +411,7 @@ private void refreshStatus() { } private void openAppFilterSelector() { - mAppSelDialog = new AppSelectDialog((AppCompatActivity) requireActivity(), R.string.app_filter, - new AppSelectDialog.AppSelectListener() { - @Override - public void onSelectedApp(AppDescriptor app) { - abortAppSelection(); - setAppFilter(app); - } - - @Override - public void onAppSelectionAborted() { - abortAppSelection(); - setAppFilter(null); - } - }); - } - - private void abortAppSelection() { - if(mAppSelDialog != null) { - mAppSelDialog.abort(); - mAppSelDialog = null; - } + Intent intent = new Intent(requireContext(), AppFilterActivity.class); + startActivity(intent); } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java index d46c01d41..198ed4563 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/CaptureSettings.java @@ -8,10 +8,15 @@ import com.emanuelef.remote_capture.Billing; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class CaptureSettings implements Serializable { public Prefs.DumpMode dump_mode; - public String app_filter; + public Set app_filter; public String collector_address; public int collector_port; public int http_server_port; @@ -62,7 +67,7 @@ public CaptureSettings(Context ctx, SharedPreferences prefs) { public CaptureSettings(Context ctx, Intent intent) { dump_mode = Prefs.getDumpMode(getString(intent, "pcap_dump_mode", "none")); - app_filter = getString(intent, Prefs.PREF_APP_FILTER, ""); + app_filter = new HashSet<>(getStringList(intent, Prefs.PREF_APP_FILTER)); collector_address = getString(intent, Prefs.PREF_COLLECTOR_IP_KEY, "127.0.0.1"); collector_port = getInt(intent, Prefs.PREF_COLLECTOR_PORT_KEY, 1234); http_server_port = getInt(intent, Prefs.PREF_HTTP_SERVER_PORT, 8080); @@ -113,6 +118,25 @@ private static boolean getBool(Intent intent, String key, boolean def_value) { return bundle.getBoolean(key, def_value); } + // get a list of comma-separated strings from the bundle + private static List getStringList(Intent intent, String key) { + List rv; + + String s = intent.getStringExtra(key); + if(s != null) { + if (s.indexOf(',') < 0) { + rv = new ArrayList<>(); + rv.add(s); + } else { + String[] arr = s.split(","); + rv = Arrays.asList(arr); + } + } else + rv = new ArrayList<>(); + + return rv; + } + public boolean readFromPcap() { return input_pcap_path != null; } 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 b9b7712c6..fc998cede 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 @@ -19,9 +19,11 @@ package com.emanuelef.remote_capture.model; +import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; +import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import com.emanuelef.remote_capture.Billing; @@ -29,6 +31,9 @@ import com.emanuelef.remote_capture.MitmAddon; import com.emanuelef.remote_capture.Utils; +import java.util.HashSet; +import java.util.Set; + public class Prefs { public static final String DUMP_NONE = "none"; public static final String DUMP_HTTP_SERVER = "http_server"; @@ -199,7 +204,7 @@ public static void setPortMappingEnabled(SharedPreferences p, boolean enabled) { public static boolean isSocks5AuthEnabled(SharedPreferences p) { return(p.getBoolean(PREF_SOCKS5_AUTH_ENABLED_KEY, false)); } public static String getSocks5Username(SharedPreferences p) { return(p.getString(PREF_SOCKS5_USERNAME_KEY, "")); } public static String getSocks5Password(SharedPreferences p) { return(p.getString(PREF_SOCKS5_PASSWORD_KEY, "")); } - public static String getAppFilter(SharedPreferences p) { return(p.getString(PREF_APP_FILTER, "")); } + public static Set getAppFilter(SharedPreferences p) { return(getStringSet(p, PREF_APP_FILTER)); } public static IpMode getIPMode(SharedPreferences p) { return(getIPMode(p.getString(PREF_IP_MODE, IP_MODE_DEFAULT))); } public static BlockQuicMode getBlockQuicMode(SharedPreferences p) { return(getBlockQuicMode(p.getString(PREF_BLOCK_QUIC, BLOCK_QUIC_MODE_DEFAULT))); } public static boolean useEnglishLanguage(SharedPreferences p){ return("english".equals(p.getString(PREF_APP_LANGUAGE, "system")));} @@ -234,6 +239,31 @@ public static boolean isPcapngEnabled(Context ctx, SharedPreferences p) { public static String getDnsServerV4(SharedPreferences p) { return(p.getString(PREF_DNS_SERVER_V4, "1.1.1.1")); } public static String getDnsServerV6(SharedPreferences p) { return(p.getString(PREF_DNS_SERVER_V6, "2606:4700:4700::1111")); } + // Gets a StringSet from the prefs + // The preference should either be a StringSet or a String + // An empty set is returned as the default value + @SuppressLint("MutatingSharedPrefs") + public static @NonNull Set getStringSet(SharedPreferences p, String key) { + Set rv = null; + + try { + rv = p.getStringSet(key, null); + } catch (ClassCastException e) { + // retry with string + String s = p.getString(key, ""); + + if (!s.isEmpty()) { + rv = new HashSet<>(); + rv.add(s); + } + } + + if (rv == null) + rv = new HashSet<>(); + + return rv; + } + public static String asString(Context ctx) { SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(ctx); @@ -252,7 +282,7 @@ public static String asString(Context ctx) { "\nFirewall: " + isFirewallEnabled(ctx, p) + "\nPCAPNG: " + isPcapngEnabled(ctx, p) + "\nBlockNewApps: " + blockNewApps(p) + - "\nAppFilter: " + getAppFilter(p) + + "\nTargetApps: " + getAppFilter(p) + "\nIpMode: " + getIPMode(p) + "\nTrailer: " + isPcapdroidTrailerEnabled(p) + "\nStartAtBoot: " + startAtBoot(p); diff --git a/app/src/main/jni/core/capture_libpcap.c b/app/src/main/jni/core/capture_libpcap.c index 178c682ef..2aa89e79f 100644 --- a/app/src/main/jni/core/capture_libpcap.c +++ b/app/src/main/jni/core/capture_libpcap.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2021 - Emanuele Faranda + * Copyright 2021-24 - Emanuele Faranda */ #include @@ -103,6 +103,25 @@ static bool valid_bpf(const char *bpf) { /* ******************************************************* */ +static const char* get_uids_filter(pcapdroid_t *pd, char *buf, size_t buf_size) { + if (pd->tls_decryption.enabled || (pd->pcap.app_filter_uids_size <= 0)) + return "-1"; + + size_t off = 0; + + for (int i = 0; (i < pd->pcap.app_filter_uids_size) && (off < buf_size); i++) { + const char * fmt = (i == 0) ? "%d" : ",%d"; + off += snprintf(buf + off, (buf_size - off), fmt, pd->pcap.app_filter_uids[i]); + } + + if (off >= buf_size) + log_e("The UID filter has been truncated"); + + return buf; +} + +/* ******************************************************* */ + static int connectPcapd(pcapdroid_t *pd) { int sock; int client = -1; @@ -185,9 +204,11 @@ static int connectPcapd(pcapdroid_t *pd) { } // Start the daemon + char uids_filter_buf[128]; char args[256]; - snprintf(args, sizeof(args), "-l pcapd.log -L %u -i '%s' -u %d -t -b '%s'%s", - getuid(), pd->pcap.capture_interface, pd->tls_decryption.enabled ? -1 : pd->app_filter, + snprintf(args, sizeof(args), "-l pcapd.log -L %u -i '%s' -u %s -t -b '%s'%s", + getuid(), pd->pcap.capture_interface, + get_uids_filter(pd, uids_filter_buf, sizeof(uids_filter_buf)), bpf, pd->pcap.daemonize ? " -d" : ""); pid = start_subprocess(pcapd, args, pd->pcap.as_root, NULL); @@ -271,10 +292,10 @@ static int connectPcapd(pcapdroid_t *pd) { /* ******************************************************* */ -static char* get_mitm_redirection_args(pcapdroid_t *pd, char *buf, bool add) { +static char* get_mitm_redirection_args(pcapdroid_t *pd, char *buf, int uid, bool add) { int off = sprintf(buf, "-t nat -%c OUTPUT -p tcp -m owner ", add ? 'I' : 'D'); - if(pd->app_filter >= 0) - off += sprintf(buf + off, "--uid-owner %d", pd->app_filter); + if(uid >= 0) + off += sprintf(buf + off, "--uid-owner %d", uid); else off += sprintf(buf + off, "! --uid-owner %d", pd->mitm_addon_uid); sprintf(buf + off, " -j REDIRECT --to 7780"); @@ -604,6 +625,7 @@ int run_libpcap(pcapdroid_t *pd) { char bpf[256]; bpf[0] = '\0'; + pd->pcap.app_filter_uids_size = getIntArrayPref(pd->env, pd->capture_service, "getAppFilterUids", &pd->pcap.app_filter_uids); pd->pcap.as_root = !pd->pcap_file_capture; pd->pcap.bpf = getStringPref(pd, "getPcapDumperBpf", bpf, sizeof(bpf)); pd->pcap.capture_interface = getStringPref(pd, "getCaptureInterface", capture_interface, sizeof(capture_interface)); @@ -628,10 +650,23 @@ int run_libpcap(pcapdroid_t *pd) { if(pd->tls_decryption.enabled) { char args[128]; - if(run_shell_cmd("iptables", get_mitm_redirection_args(pd, args, true), true, true) != 0) - goto cleanup; + if(pd->pcap.app_filter_uids_size > 0) { + for (int i = 0; i < pd->pcap.app_filter_uids_size; i++) { + int uid = pd->pcap.app_filter_uids[i]; + + if (uid >= 0) { + if(run_shell_cmd("iptables", get_mitm_redirection_args(pd, args, uid, true), true, true) != 0) + goto cleanup; - iptables_cleanup = true; + iptables_cleanup = true; + } + } + } else { + if(run_shell_cmd("iptables", get_mitm_redirection_args(pd, args, -1, true), true, true) != 0) + goto cleanup; + + iptables_cleanup = true; + } } pd_refresh_time(pd); @@ -722,7 +757,16 @@ int run_libpcap(pcapdroid_t *pd) { if(iptables_cleanup) { char args[128]; - run_shell_cmd("iptables", get_mitm_redirection_args(pd, args, false), true, false); + + if(pd->pcap.app_filter_uids_size > 0) { + for (int i = 0; i < pd->pcap.app_filter_uids_size; i++) { + int uid = pd->pcap.app_filter_uids[i]; + + if (uid >= 0) + run_shell_cmd("iptables", get_mitm_redirection_args(pd, args, uid, false), true, false); + } + } else + run_shell_cmd("iptables", get_mitm_redirection_args(pd, args, -1, false), true, false); } if((pd->pcap.pcapd_pid > 0) && !pd->pcap.daemonize) { @@ -735,5 +779,14 @@ int run_libpcap(pcapdroid_t *pd) { process_pcapd_rv(pd, WEXITSTATUS(status)); } +#if ANDROID + if (pd->pcap.app_filter_uids) { + pd_free(pd->pcap.app_filter_uids); + + pd->pcap.app_filter_uids = NULL; + pd->pcap.app_filter_uids_size = 0; + } +#endif + return rv; } diff --git a/app/src/main/jni/core/jni_impl.c b/app/src/main/jni/core/jni_impl.c index e7312b4d9..76b5a37e1 100644 --- a/app/src/main/jni/core/jni_impl.c +++ b/app/src/main/jni/core/jni_impl.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2022 - Emanuele Faranda + * Copyright 2022-24 - Emanuele Faranda */ #if ANDROID @@ -585,7 +585,6 @@ Java_com_emanuelef_remote_1capture_CaptureService_runPacketLoop(JNIEnv *env, jcl .notify_blacklists_loaded = notifyBlacklistsLoaded, .dump_payload_chunk = dumpPayloadChunk, }, - .app_filter = getIntPref(env, vpn, "getAppFilterUid"), .mitm_addon_uid = getIntPref(env, vpn, "getMitmAddonUid"), .vpn_capture = (bool) getIntPref(env, vpn, "isVpnCapture"), .pcap_file_capture = (bool) getIntPref(env, vpn, "isPcapFileCapture"), @@ -1213,6 +1212,41 @@ int getIntPref(JNIEnv *env, jobject vpn_inst, const char *key) { /* ******************************************************* */ +// Retrieve a int[] pref. +// If rv is >0, out points to the allocated array. It's up to the caller to free it with pd_free +int getIntArrayPref(JNIEnv *env, jobject vpn_inst, const char *key, int **out) { + int rv = -1; + jmethodID midMethod = jniGetMethodID(env, cls.vpn_service, key, "()[I"); + jintArray jarr = (jintArray) (*env)->CallObjectMethod(env, vpn_inst, midMethod); + + if (!jniCheckException(env)) { + int size = (*env)->GetArrayLength(env, jarr); + log_d("getIntArrayPref(%s) = #%d", key, size); + + if (size > 0) { + jint *array = (*env)->GetIntArrayElements(env, jarr, NULL); + if (array) { + size_t arr_size = size * sizeof(int); + + *out = (int*) pd_malloc(arr_size); + if (*out) { + // success + memcpy(*out, array, arr_size); + rv = size; + } + + (*env)->ReleaseIntArrayElements(env, jarr, array, 0); + } + } else + rv = size; + } + + (*env)->DeleteLocalRef(env, jarr); + return rv; +} + +/* ******************************************************* */ + void getApplicationByUid(pcapdroid_t *pd, jint uid, char *buf, int bufsize) { JNIEnv *env = pd->env; const char *value = NULL; diff --git a/app/src/main/jni/core/pcapdroid.c b/app/src/main/jni/core/pcapdroid.c index 29db9f6fb..11495298b 100644 --- a/app/src/main/jni/core/pcapdroid.c +++ b/app/src/main/jni/core/pcapdroid.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-24 - Emanuele Faranda */ #include diff --git a/app/src/main/jni/core/pcapdroid.h b/app/src/main/jni/core/pcapdroid.h index c90badb00..724629154 100644 --- a/app/src/main/jni/core/pcapdroid.h +++ b/app/src/main/jni/core/pcapdroid.h @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-24 - Emanuele Faranda */ #ifndef __PCAPDROID_H__ @@ -195,7 +195,6 @@ typedef struct pcapdroid { int filesdir_len; // config - jint app_filter; jint mitm_addon_uid; bool vpn_capture; bool pcap_file_capture; @@ -233,6 +232,9 @@ typedef struct pcapdroid { char *bpf; char *capture_interface; int pcapd_pid; + + int *app_filter_uids; + int app_filter_uids_size; } pcap; }; @@ -406,6 +408,7 @@ uint16_t pd_ndpi2proto(ndpi_protocol proto); char* getStringPref(pcapdroid_t *pd, const char *key, char *buf, int bufsize); int getIntPref(JNIEnv *env, jobject vpn_inst, const char *key); +int getIntArrayPref(JNIEnv *env, jobject vpn_inst, const char *key, int **out); zdtun_ip_t getIPPref(JNIEnv *env, jobject vpn_inst, const char *key, int *ip_ver); uint32_t getIPv4Pref(JNIEnv *env, jobject vpn_inst, const char *key); struct in6_addr getIPv6Pref(JNIEnv *env, jobject vpn_inst, const char *key); diff --git a/app/src/main/jni/pcapd/pcapd.c b/app/src/main/jni/pcapd/pcapd.c index fc56699eb..f3ab5f584 100644 --- a/app/src/main/jni/pcapd/pcapd.c +++ b/app/src/main/jni/pcapd/pcapd.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2021-23 - Emanuele Faranda + * Copyright 2021-24 - Emanuele Faranda */ /* diff --git a/app/src/main/jni/tests/test_utils.c b/app/src/main/jni/tests/test_utils.c index 40de5c8d5..c2bec727c 100644 --- a/app/src/main/jni/tests/test_utils.c +++ b/app/src/main/jni/tests/test_utils.c @@ -89,7 +89,6 @@ pcapdroid_t* pd_init_test(const char *ifname) { pd->pcap_file_capture = true; pd->pcap.capture_interface = (char*) ifname; pd->pcap.as_root = false; // don't run as root - pd->app_filter = -1; // don't filter pd->cb.get_libprog_path = getPcapdPath; pd->payload_mode = PAYLOAD_MODE_FULL; diff --git a/app/src/main/res/menu/hint_menu.xml b/app/src/main/res/menu/hint_menu.xml new file mode 100644 index 000000000..9b0e9efa8 --- /dev/null +++ b/app/src/main/res/menu/hint_menu.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d292498c8..ada184138 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -501,4 +501,6 @@ Always Only for connections to decrypt Decrypting QUIC is currently not supported. As a workaround, stop the capture and select the option to block QUIC in the PCAPdroid settings + Target apps + Select the applications to capture diff --git a/docs/app_api.md b/docs/app_api.md index d590b6bae..badc3df71 100644 --- a/docs/app_api.md +++ b/docs/app_api.md @@ -79,7 +79,7 @@ As shown above, the capture settings can be specified by using intent extras. Th | Parameter | Type | Ver | Mode | Value | |-------------------------|--------|-----|------|--------------------------------------------------------------------| | pcap_dump_mode | string | | | none \| http_server \| udp_exporter \| pcap_file | -| app_filter | string | | | the package name of the app to capture | +| app_filter | string | | | package name of the app(s) to capture (73+: comma separated list) | | collector_ip_address | string | | | the IP address of the collector in udp_exporter mode | | collector_port | int | | | the UDP port of the collector in udp_exporter mode | | http_server_port | int | | | the HTTP server port in http_server mode | @@ -101,7 +101,7 @@ As shown above, the capture settings can be specified by using intent extras. Th | pcapng_format | bool | 62 | | true to use the PCAPNG dump format (overrides pcapdroid_trailer)* | | socks5_username | string | 64 | vpn | username for the optional SOCKS5 proxy authentication | | socks5_password | string | 64 | vpn | password for the optional SOCKS5 proxy authentication | -| block_quic | string | 73 | vpn | never | always | to_decrypt (matching the decryption whitelist) | +| block_quic | string | 73 | vpn | never \| always \| to_decrypt (matching the decryption whitelist) | \*: paid feature From 54d6ce4dd1e05900eea1911364083addaa6ae184 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Wed, 31 Jan 2024 22:59:32 +0100 Subject: [PATCH 15/45] Rework Status fragment icons to fix sizing issues --- .../fragments/StatusFragment.java | 94 ++++++------------- .../main/res/layout/quick_settings_item.xml | 30 ++++-- app/src/main/res/layout/status.xml | 32 +++++-- 3 files changed, 79 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java index 2956a1b60..824d191a0 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java @@ -25,8 +25,6 @@ import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.text.method.LinkMovementMethod; import android.util.Pair; import android.view.LayoutInflater; @@ -35,7 +33,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; +import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; @@ -67,13 +65,15 @@ public class StatusFragment extends Fragment implements AppStateListener, MenuProvider { private static final String TAG = "StatusFragment"; - private Handler mHandler; private Menu mMenu; private MenuItem mStartBtn; private MenuItem mStopBtn; + private ImageView mFilterIcon; private MenuItem mMenuSettings; private TextView mInterfaceInfo; - private TextView mCollectorInfo; + private View mCollectorInfoLayout; + private TextView mCollectorInfoText; + private ImageView mCollectorInfoIcon; private TextView mCaptureStatus; private View mQuickSettings; private MainActivity mActivity; @@ -112,9 +112,10 @@ public View onCreateView(LayoutInflater inflater, @SuppressLint("ClickableViewAccessibility") @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - mHandler = new Handler(Looper.getMainLooper()); mInterfaceInfo = view.findViewById(R.id.interface_info); - mCollectorInfo = view.findViewById(R.id.collector_info); + mCollectorInfoLayout = view.findViewById(R.id.collector_info_layout); + mCollectorInfoText = mCollectorInfoLayout.findViewById(R.id.collector_info_text); + mCollectorInfoIcon = mCollectorInfoLayout.findViewById(R.id.collector_info_icon); mCaptureStatus = view.findViewById(R.id.status_view); mQuickSettings = view.findViewById(R.id.quick_settings); mFilterRootDecryptionWarning = view.findViewById(R.id.app_filter_root_decryption_warning); @@ -129,24 +130,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat View filterRow = view.findViewById(R.id.app_filter_text); TextView filterTitle = filterRow.findViewById(R.id.title); mFilterDescription = filterRow.findViewById(R.id.description); - - // Needed to update the filter icon after mFilterDescription is measured - final ViewTreeObserver vto = mFilterDescription.getViewTreeObserver(); - if(vto.isAlive()) { - vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - refreshFilterInfo(); - - final ViewTreeObserver vto = mFilterDescription.getViewTreeObserver(); - - if(vto.isAlive()) { - vto.removeOnGlobalLayoutListener(this); - Log.d(TAG, "removeOnGlobalLayoutListener called"); - } - } - }); - } + mFilterIcon = filterRow.findViewById(R.id.icon); filterTitle.setText(R.string.target_apps); @@ -167,7 +151,7 @@ public void onGlobalLayout() { CaptureService.observeStats(this, this::onStatsUpdate); // Make URLs clickable - mCollectorInfo.setMovementMethod(LinkMovementMethod.getInstance()); + mCollectorInfoText.setMovementMethod(LinkMovementMethod.getInstance()); /* Important: call this after all the fields have been initialized */ mActivity.setAppStateListener(this); @@ -215,7 +199,7 @@ private void refreshFilterInfo() { if((mAppFilter == null) || (mAppFilter.isEmpty())) { mFilterDescription.setText(R.string.capture_all_apps); - mFilterDescription.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + mFilterIcon.setVisibility(View.GONE); mAppFilterSwitch.setChecked(false); return; } @@ -227,14 +211,8 @@ private void refreshFilterInfo() { mFilterDescription.setText(pair.first); if (pair.second != null) { - int height = mFilterDescription.getMeasuredHeight(); - - if(height > 0) { - Drawable drawable = Utils.scaleDrawable(context.getResources(), pair.second, height, height); - - if(drawable != null) - mFilterDescription.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); - } + mFilterIcon.setImageDrawable(pair.second); + mFilterIcon.setVisibility(View.VISIBLE); } } @@ -281,7 +259,7 @@ private Pair getAppFilterTextAndIcon(@NonNull Context context) return new Pair<>(text, icon); } - private void refreshPcapDumpInfo() { + private void refreshPcapDumpInfo(Context context) { String info = ""; Prefs.DumpMode mode = CaptureService.getDumpMode(); @@ -307,36 +285,26 @@ private void refreshPcapDumpInfo() { break; } - mCollectorInfo.setText(info); - - // Rendering after mCollectorInfo.setText is deferred, so getMeasuredHeight must be postponed - mHandler.post(() -> { - Context context = getContext(); - if(context == null) - return; + mCollectorInfoText.setText(info); - // Check if a filter is set - if((mAppFilter != null) && (!mAppFilter.isEmpty())) { - Pair pair = getAppFilterTextAndIcon(context); - - if (pair.second != null) { - // scale and set - int height = mCollectorInfo.getMeasuredHeight(); - Drawable drawable = Utils.scaleDrawable(getResources(), pair.second, height, height); - - if (drawable != null) - mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null); - } - } - }); + // Check if a filter is set + Drawable drawable = null; + if((mAppFilter != null) && (!mAppFilter.isEmpty())) { + Pair pair = getAppFilterTextAndIcon(context); + drawable = pair.second; + } - // will be overriden in the above handler if necessary - mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + if (drawable != null) { + mCollectorInfoIcon.setImageDrawable(drawable); + mCollectorInfoIcon.setVisibility(View.VISIBLE); + } else + mCollectorInfoIcon.setVisibility(View.GONE); } @Override public void appStateChanged(AppState state) { - if(getContext() == null) + Context context = getContext(); + if(context == null) return; if(mMenu != null) { @@ -356,7 +324,7 @@ public void appStateChanged(AppState state) { switch(state) { case ready: mCaptureStatus.setText(R.string.ready); - mCollectorInfo.setVisibility(View.GONE); + mCollectorInfoLayout.setVisibility(View.GONE); mInterfaceInfo.setVisibility(View.GONE); mQuickSettings.setVisibility(View.VISIBLE); mAppFilter = Prefs.getAppFilter(mPrefs); @@ -372,7 +340,7 @@ public void appStateChanged(AppState state) { break; case running: mCaptureStatus.setText(Utils.formatBytes(CaptureService.getBytes())); - mCollectorInfo.setVisibility(View.VISIBLE); + mCollectorInfoLayout.setVisibility(View.VISIBLE); mQuickSettings.setVisibility(View.GONE); CaptureService service = CaptureService.requireInstance(); @@ -397,7 +365,7 @@ else if(capiface.equals("any")) mInterfaceInfo.setVisibility(View.GONE); mAppFilter = CaptureService.getAppFilter(); - refreshPcapDumpInfo(); + refreshPcapDumpInfo(context); break; default: break; diff --git a/app/src/main/res/layout/quick_settings_item.xml b/app/src/main/res/layout/quick_settings_item.xml index ff0bbfcd9..e605dc91e 100644 --- a/app/src/main/res/layout/quick_settings_item.xml +++ b/app/src/main/res/layout/quick_settings_item.xml @@ -17,10 +17,28 @@ android:layout_marginBottom="2dp" android:layout_height="wrap_content"/> - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/status.xml b/app/src/main/res/layout/status.xml index d128707f1..01b166f23 100644 --- a/app/src/main/res/layout/status.xml +++ b/app/src/main/res/layout/status.xml @@ -14,19 +14,35 @@ android:layout_height="wrap_content" tools:context="com.emanuelef.remote_capture.activities.MainActivity"> - + app:layout_constraintTop_toBottomOf="@id/status_view"> + + + + + + app:layout_constraintTop_toBottomOf="@id/collector_info_layout" /> Date: Thu, 1 Feb 2024 10:50:33 +0100 Subject: [PATCH 16/45] Allow to specify what to copy from the payload - For the HTTP tab, allow to to copy only headers/body/both - For other tabs, allow to copy in plaintext or hexdump format See #362 --- .../adapters/PayloadAdapter.java | 70 ++++++++++++++++--- app/src/main/res/values/strings.xml | 3 + 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java index a1f1a35de..8c66fe679 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java @@ -14,12 +14,13 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-24 - Emanuele Faranda */ package com.emanuelef.remote_capture.adapters; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -113,10 +114,10 @@ PayloadChunk getPayloadChunk() { } @CheckResult - private String makeText(boolean expanded) { + private String makeText(boolean as_printable, boolean expanded) { int dump_len = expanded ? mChunk.payload.length : Math.min(mChunk.payload.length, COLLAPSE_CHUNK_SIZE); - if(!mShowAsPrintable) + if(!as_printable) return Utils.hexdump(mChunk.payload, 0, dump_len); else return new String(mChunk.payload, 0, dump_len, StandardCharsets.UTF_8); @@ -124,7 +125,7 @@ private String makeText(boolean expanded) { @CheckResult private String makeText() { - return makeText(mIsExpanded); + return makeText(mShowAsPrintable, mIsExpanded); } void expand() { @@ -154,8 +155,8 @@ String getText(int start, int end) { return mTheText.substring(start, end); } - String getExpandedText() { - return makeText(true); + String getExpandedText(boolean as_printable) { + return makeText(as_printable, true); } Page getPage(int pageIdx) { @@ -239,10 +240,61 @@ public PayloadViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewT }); holder.copybutton.setOnClickListener(v -> { - int pos = holder.getAbsoluteAdapterPosition(); - String payload = getItem(pos).adaptChunk.getExpandedText(); + int payload_pos = holder.getAbsoluteAdapterPosition(); - Utils.copyToClipboard(mContext, payload); + if(mMode == ChunkType.HTTP) { + String payload = getItem(payload_pos).adaptChunk.getExpandedText(true); + int crlf_pos = payload.indexOf("\r\n\r\n"); + + boolean has_body = (crlf_pos > 0) && (crlf_pos < (payload.length() - 4)); + if (!has_body) { + Utils.copyToClipboard(mContext, payload); + return; + } + + String[] choices = { + mContext.getString(R.string.headers), + mContext.getString(R.string.body), + mContext.getString(R.string.both), + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setTitle(R.string.copy_action); + builder.setSingleChoiceItems(choices, 2, (dialogInterface, i) -> {}); + builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); + builder.setPositiveButton(R.string.copy_to_clipboard, (dialogInterface, i) -> { + int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); + String to_copy = payload; + + if (choice != 2) { + if (choice == 0 /* Headers */) + to_copy = to_copy.substring(0, crlf_pos); + else /* body */ + to_copy = to_copy.substring(crlf_pos + 4); + } + + Utils.copyToClipboard(mContext, to_copy); + }); + builder.create().show(); + } else { + String[] choices = { + mContext.getString(R.string.printable_text), + mContext.getString(R.string.hexdump) + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setTitle(R.string.copy_action); + builder.setSingleChoiceItems(choices, mShowAsPrintable ? 0 : 1, (dialogInterface, i) -> {}); + + builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); + builder.setPositiveButton(R.string.copy_to_clipboard, (dialogInterface, i) -> { + int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); + String payload = getItem(payload_pos).adaptChunk.getExpandedText(choice == 0); + + Utils.copyToClipboard(mContext, payload); + }); + builder.create().show(); + } }); return holder; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ada184138..546aefdf2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -503,4 +503,7 @@ Decrypting QUIC is currently not supported. As a workaround, stop the capture and select the option to block QUIC in the PCAPdroid settings Target apps Select the applications to capture + Headers + Body + Both From 36c99bc84bf2050af787bc9713da7d4fe3e091fa Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Thu, 1 Feb 2024 11:48:07 +0100 Subject: [PATCH 17/45] Add export to file payload button See #362 --- .../activities/ConnectionDetailsActivity.java | 51 ++++++- .../adapters/PayloadAdapter.java | 132 +++++++++++------- .../fragments/ConnectionPayload.java | 7 + .../main/res/drawable/ic_save_alt_small.xml | 5 + app/src/main/res/layout/payload_item.xml | 19 +++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 162 insertions(+), 53 deletions(-) create mode 100644 app/src/main/res/drawable/ic_save_alt_small.xml diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java index f62cbdfca..f9ca0ccfc 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java @@ -14,11 +14,14 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-24 - Emanuele Faranda */ package com.emanuelef.remote_capture.activities; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -26,6 +29,7 @@ import androidx.viewpager2.widget.ViewPager2; import android.annotation.SuppressLint; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -36,6 +40,8 @@ import com.emanuelef.remote_capture.ConnectionsRegister; import com.emanuelef.remote_capture.Log; import com.emanuelef.remote_capture.R; +import com.emanuelef.remote_capture.Utils; +import com.emanuelef.remote_capture.adapters.PayloadAdapter; import com.emanuelef.remote_capture.fragments.ConnectionOverview; import com.emanuelef.remote_capture.fragments.ConnectionPayload; import com.emanuelef.remote_capture.interfaces.ConnectionsListener; @@ -44,9 +50,12 @@ import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.util.ArrayList; -public class ConnectionDetailsActivity extends BaseActivity implements ConnectionsListener { +public class ConnectionDetailsActivity extends BaseActivity implements ConnectionsListener, PayloadAdapter.ExportPayloadHandler { private static final String TAG = "ConnectionDetails"; public static final String CONN_ID_KEY = "conn_id"; private static final int MAX_CHUNKS_TO_CHECK = 10; @@ -60,6 +69,7 @@ public class ConnectionDetailsActivity extends BaseActivity implements Connectio private boolean mHasPayload; private boolean mHasHttpTab; private boolean mHasWsTab; + private String mPayloadToExport; private final ArrayList mListeners = new ArrayList<>(); private static final int POS_OVERVIEW = 0; @@ -67,6 +77,9 @@ public class ConnectionDetailsActivity extends BaseActivity implements Connectio private static final int POS_HTTP = 2; private static final int POS_RAW_PAYLOAD = 3; + private final ActivityResultLauncher payloadExportLauncher = + registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::payloadExportResult); + public interface ConnUpdateListener { void connectionUpdated(); } @@ -329,4 +342,38 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { return super.onKeyDown(keyCode, event); } + + @Override + public void exportPayload(String payload) { + mPayloadToExport = payload; + + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TITLE, Utils.getUniqueFileName(this, "txt")); + + Log.d(TAG, "exportPayload: launching dialog"); + Utils.launchFileDialog(this, intent, payloadExportLauncher); + } + + private void payloadExportResult(final ActivityResult result) { + Log.d(TAG, "payloadExportResult"); + + if (mPayloadToExport == null) + return; + + if((result.getResultCode() == RESULT_OK) && (result.getData() != null) && (result.getData().getData() != null)) { + try(OutputStream out = getContentResolver().openOutputStream(result.getData().getData(), "rwt")) { + try(OutputStreamWriter writer = new OutputStreamWriter(out)) { + writer.write(mPayloadToExport); + } + Utils.showToast(this, R.string.save_ok); + } catch (IOException e) { + e.printStackTrace(); + Utils.showToastLong(this, R.string.export_failed); + } + } + + mPayloadToExport = null; + } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java index 8c66fe679..b633c14d8 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java @@ -66,7 +66,13 @@ public class PayloadAdapter extends RecyclerView.Adapter mChunks = new ArrayList<>(); private final HTTPReassembly mHttpReq; private final HTTPReassembly mHttpRes; + private final boolean mSupportsFileDialog; private boolean mShowAsPrintable; + private ExportPayloadHandler mExportHandler; + + public interface ExportPayloadHandler { + void exportPayload(String payload); + } public PayloadAdapter(Context context, ConnectionDescriptor conn, ChunkType mode, boolean showAsPrintable) { mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -74,6 +80,7 @@ public PayloadAdapter(Context context, ConnectionDescriptor conn, ChunkType mode mContext = context; mMode = mode; mShowAsPrintable = showAsPrintable; + mSupportsFileDialog = Utils.supportsFileDialog(context); // Note: in minimal mode, only the first chunk is captured, so don't reassemble them boolean reassemble = (CaptureService.getCurPayloadMode() == Prefs.PayloadMode.FULL); @@ -85,6 +92,10 @@ public PayloadAdapter(Context context, ConnectionDescriptor conn, ChunkType mode handleChunksAdded(mConn.getNumPayloadChunks()); } + public void setExportPayloadHandler(ExportPayloadHandler handler) { + mExportHandler = handler; + } + private class AdapterChunk { private final PayloadChunk mChunk; private String mTheText; @@ -203,6 +214,7 @@ protected static class PayloadViewHolder extends RecyclerView.ViewHolder { TextView dump; MaterialButton expandButton; MaterialButton copybutton; + MaterialButton exportbutton; public PayloadViewHolder(View view) { super(view); @@ -213,6 +225,7 @@ public PayloadViewHolder(View view) { dumpBox = view.findViewById(R.id.dump_box); expandButton = view.findViewById(R.id.expand_button); copybutton = view.findViewById(R.id.copy_button); + exportbutton = view.findViewById(R.id.export_button); } } @@ -239,65 +252,82 @@ public PayloadViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewT } }); - holder.copybutton.setOnClickListener(v -> { - int payload_pos = holder.getAbsoluteAdapterPosition(); + holder.copybutton.setOnClickListener(v -> handleCopyExportButtons(holder, false)); + holder.exportbutton.setOnClickListener(v -> handleCopyExportButtons(holder, true)); + holder.exportbutton.setVisibility(mSupportsFileDialog ? View.VISIBLE : View.GONE); - if(mMode == ChunkType.HTTP) { - String payload = getItem(payload_pos).adaptChunk.getExpandedText(true); - int crlf_pos = payload.indexOf("\r\n\r\n"); + return holder; + } - boolean has_body = (crlf_pos > 0) && (crlf_pos < (payload.length() - 4)); - if (!has_body) { - Utils.copyToClipboard(mContext, payload); - return; - } + private void handleCopyExportButtons(PayloadViewHolder holder, boolean is_export) { + if(is_export && (mExportHandler == null)) + return; - String[] choices = { - mContext.getString(R.string.headers), - mContext.getString(R.string.body), - mContext.getString(R.string.both), - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - builder.setTitle(R.string.copy_action); - builder.setSingleChoiceItems(choices, 2, (dialogInterface, i) -> {}); - builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); - builder.setPositiveButton(R.string.copy_to_clipboard, (dialogInterface, i) -> { - int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); - String to_copy = payload; - - if (choice != 2) { - if (choice == 0 /* Headers */) - to_copy = to_copy.substring(0, crlf_pos); - else /* body */ - to_copy = to_copy.substring(crlf_pos + 4); - } + int payload_pos = holder.getAbsoluteAdapterPosition(); + int title = is_export ? R.string.export_ellipsis : R.string.copy_action; + int positive_action = is_export ? R.string.export_action : R.string.copy_to_clipboard; - Utils.copyToClipboard(mContext, to_copy); - }); - builder.create().show(); - } else { - String[] choices = { - mContext.getString(R.string.printable_text), - mContext.getString(R.string.hexdump) - }; + if(mMode == ChunkType.HTTP) { + String payload = getItem(payload_pos).adaptChunk.getExpandedText(true); + int crlf_pos = payload.indexOf("\r\n\r\n"); - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - builder.setTitle(R.string.copy_action); - builder.setSingleChoiceItems(choices, mShowAsPrintable ? 0 : 1, (dialogInterface, i) -> {}); + boolean has_body = (crlf_pos > 0) && (crlf_pos < (payload.length() - 4)); + if (!has_body) { + Utils.copyToClipboard(mContext, payload); + return; + } - builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); - builder.setPositiveButton(R.string.copy_to_clipboard, (dialogInterface, i) -> { - int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); - String payload = getItem(payload_pos).adaptChunk.getExpandedText(choice == 0); + String[] choices = { + mContext.getString(R.string.headers), + mContext.getString(R.string.body), + mContext.getString(R.string.both), + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setTitle(title); + builder.setSingleChoiceItems(choices, 2, (dialogInterface, i) -> {}); + builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); + builder.setPositiveButton(positive_action, (dialogInterface, i) -> { + int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); + String to_copy = payload; + + if (choice != 2) { + if (choice == 0 /* Headers */) + to_copy = to_copy.substring(0, crlf_pos); + else /* body */ + to_copy = to_copy.substring(crlf_pos + 4); + } + if (is_export) { + if (mExportHandler != null) + mExportHandler.exportPayload(to_copy); + } else + Utils.copyToClipboard(mContext, to_copy); + }); + builder.create().show(); + } else { + String[] choices = { + mContext.getString(R.string.printable_text), + mContext.getString(R.string.hexdump) + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setTitle(title); + builder.setSingleChoiceItems(choices, mShowAsPrintable ? 0 : 1, (dialogInterface, i) -> {}); + + builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); + builder.setPositiveButton(positive_action, (dialogInterface, i) -> { + int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); + String payload = getItem(payload_pos).adaptChunk.getExpandedText(choice == 0); + + if (is_export) { + if (mExportHandler != null) + mExportHandler.exportPayload(payload); + } else Utils.copyToClipboard(mContext, payload); - }); - builder.create().show(); - } - }); - - return holder; + }); + builder.create().show(); + } } private String getHeaderTag(PayloadChunk chunk) { diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionPayload.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionPayload.java index f3ce0a265..d8bf595c4 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionPayload.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionPayload.java @@ -76,6 +76,9 @@ public void onAttach(@NonNull Context context) { super.onAttach(context); mActivity = (ConnectionDetailsActivity) context; mActivity.addConnUpdateListener(this); + + if (mAdapter != null) + mAdapter.setExportPayloadHandler(mActivity); } @Override @@ -83,6 +86,9 @@ public void onDetach() { super.onDetach(); mActivity.removeConnUpdateListener(this); mActivity = null; + + if (mAdapter != null) + mAdapter.setExportPayloadHandler(null); } @Override @@ -123,6 +129,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat else mShowAsPrintable = false; mAdapter = new PayloadAdapter(requireContext(), mConn, mode, mShowAsPrintable); + mAdapter.setExportPayloadHandler(mActivity); mJustCreated = true; // only set adapter after acknowledged (see setMenuVisibility below) diff --git a/app/src/main/res/drawable/ic_save_alt_small.xml b/app/src/main/res/drawable/ic_save_alt_small.xml new file mode 100644 index 000000000..6d7013df5 --- /dev/null +++ b/app/src/main/res/drawable/ic_save_alt_small.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/payload_item.xml b/app/src/main/res/layout/payload_item.xml index 147216a27..e131a7691 100644 --- a/app/src/main/res/layout/payload_item.xml +++ b/app/src/main/res/layout/payload_item.xml @@ -49,6 +49,25 @@ app:iconPadding="0dp" app:iconTint="@color/colorTabText" style="@style/Widget.MaterialComponents.Button.TextButton" /> + + Mitm setup wizard Install Export + Export… Install the PCAPdroid mitm addon Configure Export the PCAPdroid CA certificate, then open the Android \"Encryption & Credentials\" settings and choose install it as a \"CA certificate\" From 339868ad8a9767fcc9cf65acc08f2eb53d737ac6 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Fri, 2 Feb 2024 09:47:35 +0100 Subject: [PATCH 18/45] Show what's new dialog on app update --- .../activities/MainActivity.java | 21 ++++++++++++++++++- app/src/main/res/values/strings.xml | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) 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 543bb1a5f..f9c680692 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 @@ -151,8 +151,12 @@ protected void onCreate(Bundle savedInstanceState) { startActivity(intent); finish(); return; - } else + } else { + if (appver < 73) + showWhatsNew(); + Prefs.refreshAppVersion(mPrefs); + } mIab = Billing.newInstance(this); mIab.setLicense(mIab.getLicense()); @@ -262,6 +266,21 @@ private void setupNavigationDrawer() { }); } + private void showWhatsNew() { + new AlertDialog.Builder(this) + .setTitle(R.string.whats_new) + .setMessage( + "- Select multiple target apps\n" + + "- Button to copy the connections payload\n" + + "- Android 14 support\n" + + "- Integrations to run with Tor and DNSCrypt\n" + + "- mitmproxy 10.1.6 and Doze fix\n" + + "- Use your own mitmproxy addons (experimental)\n" + ) + .setNeutralButton(R.string.ok, (dialogInterface, i) -> {}) + .show(); + } + // keep this in a separate function, used by play billing code private void checkPaidDrawerEntries() { if(mNavView == null) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a6ce71819..67636f600 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -507,4 +507,5 @@ Headers Body Both + What\'s new From 04d913149dcffcc16cd81aab911947e168a91e96 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Fri, 2 Feb 2024 09:58:59 +0100 Subject: [PATCH 19/45] Temporary revert string removed in c3a45bb3 Necessary to avoid conflicts with the weblate translations --- app/src/main/res/values-ar/strings.xml | 3 ++- app/src/main/res/values-de/strings.xml | 3 ++- app/src/main/res/values-es/strings.xml | 3 ++- app/src/main/res/values-in/strings.xml | 3 ++- app/src/main/res/values-it/strings.xml | 3 ++- app/src/main/res/values-nb-rNO/strings.xml | 3 ++- app/src/main/res/values-pl/strings.xml | 3 ++- app/src/main/res/values-pt-rBR/strings.xml | 3 ++- app/src/main/res/values-ru/strings.xml | 3 ++- app/src/main/res/values-tr/strings.xml | 3 ++- app/src/main/res/values-uk/strings.xml | 3 ++- app/src/main/res/values-zh-rCN/strings.xml | 3 ++- app/src/main/res/values/strings.xml | 1 + 13 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 09fae02c3..63e13011a 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -250,6 +250,7 @@ ثَبَّتَ تصدير تَهيئة + امنح PCAPdroid الإذن للتحكم في إضافة mitm الترتيب حسب أسم المستخدم فشلت الكتابة إلى وحدة التخزين الخارجية. تحقق من سجل التطبيق للحصول على التفاصيل @@ -486,4 +487,4 @@ نص غير مشفر RX بدء لالتقاط فشل . تأكد من منح حق الوصول إلى الجذر (root) لPCAPdroid - + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 50aec98ac..0cfa7c37d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -256,6 +256,7 @@ Protokoll Keine Daten Whitelist (Malware)… + Geben Sie PCAPdroid die Berechtigung, das mitm-Addon zu steuern Beim Exportieren des CA-Zertifikats ist ein Fehler aufgetreten \n \nWenn Ihr Gerät Autostart oder ähnliche Software implementiert, um die Ausführung von Hintergrunddiensten einzuschränken, stellen Sie sicher, dass Sie PCAPdroid auf die weiße Liste setzen @@ -465,4 +466,4 @@ Mitm addon läuft Diese Regeln geben an, welche Verbindungen zu entschlüsseln sind. Host-basierte Regeln funktionieren nur, wenn eine vorherige DNS-Antwort angezeigt wird Aktivier mit QR-Code - + \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3aca39f5b..df03254b2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -228,6 +228,7 @@ Instalar Exportar Configurar + Otorgar a PCAPdroid el permiso de controlar el complemento mitm Verificando el certificado… Certificado exportado, ahora instálalo en los ajustes de Android Un error ha ocurrido mientras se exportaba el certificado CA @@ -486,4 +487,4 @@ Se ha detectado una VPN activa Carga del fichero PCAP en curso, por favor espera Fallo al realizar la captura. Asegúrate de conceder acceso root a PCAPdroid - + \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index c31da88a3..56c231691 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -268,6 +268,7 @@ Instal Ekspor Konfigurasikan + Berikan PCAPdroid izin untuk mengontrol mitm addon Ekspor sertifikat CA PCAPdroid, lalu buka pengaturan \"Enkripsi & Kredensial\" Android dan pilih instal sebagai \"sertifikat CA\" Ekspor gagal Tidak dienkripsi @@ -394,4 +395,4 @@ Fitur berbayar tidak terkunci. Mulai ulang penangkapan jika berjalan Info build Fitur berbayar - + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 78780fe6f..354f485a5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -285,6 +285,7 @@ Esporta Configura Esporta il certificato CA di PCAPdroid, poi importalo dalle impostazioni Android di \"Crittografia e Credenziali\", installandolo come \"Certificato CA\" + Dai a PCAPdroid il permesso di controllare l\'addon di mitm Errore durante l\'esportazione del certificato CA \n \nSe il tuo dispositivo utilizza Autostart o software simile per limitare l\'esecuzione di processi in background, assicurati di escludere PCAPdroid @@ -485,4 +486,4 @@ La connessione non sarà decrittata. Per decrittarla, crea una regola di decrittazione La decrittazione TLS è applicata soltanto alle connessioni che matchano le regole configurate. Vuoi creare ora delle regole di decrittazione\? Avvio della cattura fallito. Assicurati di concedere l\'accesso root a PCAPdroid - + \ No newline at end of file diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index dde9c9929..0274e24f9 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -299,6 +299,7 @@ Eksporter TLS-dekryptering Dekrypter SSL/TLS-trafikk ved å utføre MITM. Dette kan fungere med noen programmer nå. Sjekk brukerveiledningen. + Gi PCAPdroid tilgang til å kontrollere MITM-tillegget Eksporter sertifikatmyndighetssertifikatet tilhørende PCAPdroid, åpne så «Kryptering og identitetsdetaljer» i Android og veld å installere det som et «CA-sertifikat». Sjekker sertifikatet … Sertifikat eksporter. Installer det nå fra Android-innstillingene. @@ -353,4 +354,4 @@ \n \nVia trailer-alternativet, kan du legge til programnavn i pakkene og vise dem i Wireshark. MITM-tillegget for PCAPdroid må oppgraderes. - + \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index cb8cdd5d5..f60ce808e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -273,6 +273,7 @@ Eksport Zainstaluj PCAPdroid dodatek mitm Skonfiguruj + Zezwól PCAPdroidowi na kontrolowanie dodatku mitm Wyeksportuj certyfikat PCAPdroid CA, a następnie otwórz ustawienia Androida \"Szyfrowanie i poświadczenia\" i wybierz zainstaluj go jako \"Certyfikat CA\"" Zainstaluj certyfikat PCAPdroid CA, wybierając \"VPN i aplikacje\". Android poprosi o ekran blokady lub hasło Sprawdzanie certyfikatu… @@ -467,4 +468,4 @@ Aktywacja licencji zakończona Dodatek Mitm jest uruchomiony Dodatek Mitm jest uruchamiany… - + \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9637094d3..62da0be73 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -228,6 +228,7 @@ Uso do heap Somente IPv6 Firewall: %1$s + Conceda ao PCAPdroid uma permissão para controlar a extensão de mitm A extensão de mitm do PCAPdroid precisa ser atualizada PCAPdroid fornece múltiplas formas para fazer o dump do tráfego no formato PCAP padrão para análise posterior \n @@ -402,4 +403,4 @@ Solicitando um token de desbloqueio, aguarde Mostrar Este é seu token de desbloqueio. Anote-o, porque será preciso para gerar seus códigos de licença - + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 59aae22cb..01aa67395 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -223,6 +223,7 @@ Мастер настройки mitm Установить Экспорт + Дать PCAPdroid разрешение контролировать mitm дополнение Настроить Сертификат экспортирован, теперь установите его из настроек Android Корневой сертификат не установлен, запустите мастер настройки mitm @@ -464,4 +465,4 @@ Не расшифровывать… Зашифровано Инжектировано - + \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 742fcaf1f..d2054780f 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -263,6 +263,7 @@ Ortadaki adam gerçekleştirerek SSL/TLS trafiğinin şifresini çöz. Bu artık bazı uygulamalarla çalışabilir, kullanım kılavuzuna bakın Trafik denetimi Dışa aktar + PCAPdroid\'e ortadaki adam eklentisini denetleme izni verin Ortadaki adam hizmeti başlatılamadı. Ortadaki adam eklentisini elle açmaya çalışın ve yeniden deneyin Şifrelenmedi CA sertifikası kurulmadı, ortadaki adam kurulum sihirbazını çalıştırın @@ -465,4 +466,4 @@ Bu kurallar hangi bağlantıların şifresinin çözüleceğini belirtir. Ana makine tabanlı kurallar yalnızca önceden bir DNS yanıtı görülürse çalışır Şifresini çöz… Şifreli - + \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index bc574fd59..eb4b49bb4 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -405,6 +405,7 @@ Встановити Експортувати Встановити доповнення mitm PCAPdroid + Надати дозвіл PCAPdroid для керування доповненням mitm Експортуйте сертифікат PCAPdroid CA. потім відкрийте налаштування Android \"Шифрування й облікові дані\" та виберіть встановити його як \"Сертифікат ЦС\" Встановіть сертифікат PCAPdroid CA, обираючи \"VPN та додатки\". Android запитає ваше блокування екрану або пароль Трапилася помилка при експортуванні сертифікату ЦС @@ -485,4 +486,4 @@ Встановлено Керування дозволами Домен чорного списку - + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ac5270c3d..fa5029974 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -250,6 +250,7 @@ Mitm 设置向导 安装 配置 + 给予 PCAPdroid 控制 mitm 附加组件的权限 检查证书… 证书已导出,从 Android 系统设置安装它 CA 证书已安装 @@ -511,4 +512,4 @@ 此连接不会被解密。创建解密规则来解密它 检测到活动 VPN 流量捕获启动失败。请确保你授予了 PCAPdroid 根权限 - + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 67636f600..c7313a175 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -277,6 +277,7 @@ Export… Install the PCAPdroid mitm addon Configure + Give PCAPdroid the permission to control the mitm addon Export the PCAPdroid CA certificate, then open the Android \"Encryption & Credentials\" settings and choose install it as a \"CA certificate\" Install the PCAPdroid CA certificate, choosing \"VPN and apps\". Android will ask for your lockscreen or password Checking the certificate… From 01d151df04babacb6911140c829ad727a797c4ff Mon Sep 17 00:00:00 2001 From: RyoidenshiAokigahara Date: Thu, 31 Aug 2023 10:17:06 +0000 Subject: [PATCH 20/45] Update Russian strings --- app/src/main/res/values-ru/strings.xml | 27 ++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 01aa67395..d0efd209c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -35,7 +35,7 @@ Исходный код Да Нет - Не удалось настроить VPN + Не удалось настроить VPN. Убедитесь, что опция Постоянный VPN отключена Не удалось найти приложение %1$s Показатели Активные соединения @@ -199,7 +199,7 @@ Сохранено Загрузка приложений… Файл сохранен как \"%1$s\" - Отключиться от активного VPN соединения и продолжить\? + Продолжение приведёт к отключению от активного VPN Скопировано Захват не производится Сначала запустите захват траффика @@ -282,7 +282,7 @@ Активно Экспорт не удался Не зашифровано - Содержимое сокращено. Включите \"%1$s\" чтобы отображать его полностью + Содержимое сокращено. Чтобы отобразить его полностью, остановите захват и включите %1$s в настройках PCAPdroid Дешифровано Дешифрование Ожидаем данные @@ -326,7 +326,9 @@ \nБолее того, приложение позволяет вам экспортировать дамп траффика в виде PCAP, извлекать метаданные и многое другое! PCAPdroid симулирует VPN чтобы захватывать весь траффик устройства без необходимости в root. \n -\nЧтобы начать захват, вам необходимо принять запрос на включение VPN на следующем экране +\nЧтобы начать захват, вам необходимо принять запрос на включение VPN на следующем экране +\n +\nПРИМЕЧАНИЕ: чтобы продолжить, отключите любой настроенный Постоянный VPN из настроек Android Со встроенным файрволом вы можете легко заблокировать доступ в интернет отдельным приложениям и доменам \n \nСкомбинируйте это со встроенной возможностью просматривать траффик и получите идеальный инструмент для защиты своей конфиденциальности @@ -465,4 +467,21 @@ Не расшифровывать… Зашифровано Инжектировано + Дешифрование TLS применяется только к соединениям, которые соответствуют настроенным правилам. Вы хотите создать правила дешифрования\? + Не удалось начать захват. Убедитесь, что вы разрешили root-доступ для PCAPdroid + Обнаружен активный VPN + Не удаётся записать файл + Прокси хост + PCAP файл загружается, пожалуйста подождите + Открыть PCAP файл… + Это соединение не будет дешифровано. Создайте правило для его дешифрования + Не удалось разрешить хост %1$s + Не удалось открыть интерфейс захвата + PCAP файл загружен + Некорректный PCAP файл + Указанного PCAP файла не существует + Ошибка чтения PCAP + Загрузка PCAP файла прервана + Не удалось начать захват. Убедитесь, что устройство рутовано с помощью Magisk + Чтобы отображать фактические приложения вместо \"%1$s\", убедитесь, что вы включили опцию \"%2$s\" перед экспортом PCAP файла \ No newline at end of file From 548f73cfabb8fe687ad657c59f7a315594920154 Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Fri, 1 Sep 2023 23:50:21 +0000 Subject: [PATCH 21/45] Update Ukrainian strings --- app/src/main/res/values-uk/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index eb4b49bb4..5cf289db6 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -208,14 +208,14 @@ Не дешифрується… Зашифровано Введено - Відкривається файл PCAP… + Відкрити файл PCAP… Файл PCAP завантажено Недійсний файл PCAP Не вдалося відкрити інтерфейс захоплення Файл PCAP не підтримується каналом передачі даних Запуск захоплення не вдався. Переконайтеся що пристрій рутовано з Magisk Вантаження файлу PCAP перервано - \"Не вдається визначити хост %1$s + Не вдається визначити хост %1$s Захоплення наживо Це з\'єднання не дешифрується. Створіть правило дешифрування, щоб дешифрувати його Дешифрування TLS застосовується лише для з\'єднань, які відповідають налаштованим правилам. Ви хочете створити правила дешифрування зараз\? From 57385af58c88cb6616455fa3b42a2fdb0540d547 Mon Sep 17 00:00:00 2001 From: jonnysemon Date: Mon, 25 Sep 2023 11:15:24 +0000 Subject: [PATCH 22/45] Update Arabic strings --- app/src/main/res/values-ar/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 63e13011a..2cf322f99 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -61,7 +61,7 @@ مشاركة حُفظت حركة المرور في ملف %1$s (%2$s) حذف - لا يمكن كتابة الملف + تعذر كتابة الملف الالتقاط يعمل مفعّل أول ظهور From 697b1aa55aba105ac9c5501936c80e78334e2114 Mon Sep 17 00:00:00 2001 From: Jo K Date: Sat, 14 Oct 2023 14:16:51 +0000 Subject: [PATCH 23/45] Update German strings --- app/src/main/res/values-de/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0cfa7c37d..8bbb75783 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -466,4 +466,5 @@ Mitm addon läuft Diese Regeln geben an, welche Verbindungen zu entschlüsseln sind. Host-basierte Regeln funktionieren nur, wenn eine vorherige DNS-Antwort angezeigt wird Aktivier mit QR-Code + Datei kann nicht geschrieben werden \ No newline at end of file From 785a36284eb886be451e5169b66f31d08e34d9c7 Mon Sep 17 00:00:00 2001 From: dedy prasetyo Date: Sat, 14 Oct 2023 19:44:25 +0000 Subject: [PATCH 24/45] Update Indonesian strings --- app/src/main/res/values-in/strings.xml | 5 +++- .../metadata/android/id/full_description.txt | 26 +++++++++++-------- .../metadata/android/id/short_description.txt | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 56c231691..021fc6059 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -186,7 +186,7 @@ Paket Durasi Mengirim PCAP ke penerima UDP jarak jauh - Tidak dapat mengatur VPN + Tidak dapat mengatur VPN. Inggris %1$d koneksi lama tidak ditampilkan Port server HTTP @@ -395,4 +395,7 @@ Fitur berbayar tidak terkunci. Mulai ulang penangkapan jika berjalan Info build Fitur berbayar + Konfigurasikan pengarahan ulang ke proxy SOCKS5 + Tidak dapat menulis berkas + Pengarahan ulang SOCKS5 \ No newline at end of file diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt index ec100964d..01c763c75 100644 --- a/fastlane/metadata/android/id/full_description.txt +++ b/fastlane/metadata/android/id/full_description.txt @@ -1,16 +1,20 @@ -PCAPdroid adalah aplikasi open source yang memungkinkan Anda memantau dan mengekspor lalu lintas jaringan. Aplikasi ini mensimulasikan VPN untuk tangkapan non-root, tetapi bertentangan dengan VPN, lalu lintas diproses secara lokal dalam perangkat. +PCAPdroid adalah aplikasi yang ramah privasi yang memungkinkan Anda melacak dan menganalisis koneksi yang dibuat oleh aplikasi lain di perangkat anda. +Ini juga memungkinkan Aada untuk mengekspor PCAP dump dari lalu lintas, memeriksa lalu lintas HTTP, mendekripsi lalu lintas TLS, dan banyak lagi. -Fitur: +PCAPdroid mensimulasikan VPN untuk menangkap lalu lintas jaringan tanpa perlu akses root. Ini tidak menggunakan server VPN jarak jauh, sebaliknya data diproses secara lokal di perangkat. -* Catat dan periksa koneksi yang dibuat oleh pengguna dan aplikasi sistem -* Ekstrak SNI, permintaan DNS, permintaan HTTP, URL HTTP, dan alamat IP jarak jauh -* Buat aturan untuk menyaring jaringan yang baik dan menemukan anomali -* Simpan lalu lintas ke file PCAP, unduh dari browser, atau ke penerima jarak jauh untuk analisis real-time (misalnya wireshark) -* Kombinasikan dengan mitmproxy untuk mendekripsi lalu lintas HTTPS/TLS (diperlukan pengetahuan teknis) -* Gunakan akses root untuk menangkap lalu lintas VPN dari aplikasi lain +Fitur: -Jika Anda berencana menggunakan PCAPdroid untuk melakukan analisis paket, silakan lihat bagian spesifik dari manual. +* Merekam dan memeriksa koneksi yang dibuat oleh aplikasi pengguna dan sistem +* Ekstrak SNI, pertanyaan DNS, URL HTTP, dan alamat IP jarak jauh +* Memeriksa permintaan dan balasan HTTP berkat decoder bawaan +* Memeriksa muatan koneksi lengkap sebagai hexdump/teks +* Mendekripsi lalu lintas HTTPS/TLS dan mengekspor SSLKEYLOGFILE +* Dump lalu lintas ke file PCAP, unduh dari browser, atau alirkan ke penerima jarak jauh untuk analisis waktu nyata (misalnya Wireshark) +* Buat aturan untuk menyaring lalu lintas yang baik dan dengan mudah menemukan anomali +* Identifikasi negara dan ASN dari server jarak jauh melalui pencarian database offline +* Pada perangkat yang sudah di-root, tangkap lalu lintas saat aplikasi VPN lain berjalan -Kunjungi repo Github proyek untuk detail selengkapnya. +Jika Anda berencana menggunakan PCAPdroid untuk melakukan analisis paket, silakan cek bagian khusus di manual. -Anda dapat bergabung dengan komunitas di telegram untuk mendiskusikan dan menerima pembaruan tentang fitur-fitur terbaru. +Bergabunglah dengan komunitas internasional PCAPdroid di Telegram atau di Matrix. diff --git a/fastlane/metadata/android/id/short_description.txt b/fastlane/metadata/android/id/short_description.txt index 075b822f8..1ff23546b 100644 --- a/fastlane/metadata/android/id/short_description.txt +++ b/fastlane/metadata/android/id/short_description.txt @@ -1 +1 @@ -Monitor jaringan tanpa root dan alat dump trafik untuk Android +Monitor jaringan tanpa root dan alat penyimpan lalu lintas data untuk perangkat Android From 77c6b20180e342b95a16cea917ea63ebfcbce3ba Mon Sep 17 00:00:00 2001 From: C Tieb Date: Mon, 6 Nov 2023 13:03:50 +0000 Subject: [PATCH 25/45] Update German strings --- app/src/main/res/values-de/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8bbb75783..d7aadc75e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -27,7 +27,7 @@ Quellcode Ja Nein - VPN konnte nicht eingerichtet werden + VPN konnte nicht eingerichtet werden. Stelle sicher, dass Always-on VPN deaktiviert ist. App %1$s konnte nicht gefunden werden Statistiken Aktive Verbindungen @@ -467,4 +467,5 @@ Diese Regeln geben an, welche Verbindungen zu entschlüsseln sind. Host-basierte Regeln funktionieren nur, wenn eine vorherige DNS-Antwort angezeigt wird Aktivier mit QR-Code Datei kann nicht geschrieben werden + Proxy Host \ No newline at end of file From 314d3844bc79c94b8e4f48aa8ad96fabe74f10e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Sun, 5 Nov 2023 18:44:26 +0000 Subject: [PATCH 26/45] Update Turkish strings --- app/src/main/res/values-tr/strings.xml | 29 ++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index d2054780f..cbc687d5e 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -11,7 +11,7 @@ Bu eylemi gerçekleştirecek uygulama bulunamadı Arayüz Artık satın alınan özelliği kullanabilirsiniz - Etkin VPN uygulamasının bağlantısını kesip devam edilsin mi\? + Devam edildiğinde etkin VPN bağlantısı kesilecektir HTTP sunucusu Sistem IP adresi: %1$s @@ -151,7 +151,7 @@ Paketler Toplayıcı IP adresi Evet - VPN kurulamadı + VPN kurulamadı. Her zaman açık VPN\'in devre dışı bırakıldığından emin olun %1$s uygulaması bulunamadı Kaynak Toplayıcı bağlantı noktası @@ -227,7 +227,7 @@ Yükselt İstek Genel bakış - Yükün bir kısmı gösteriliyor. Tamamını göstermek için \"%1$s\" etkinleştirin + Yükün bir kısmı gösteriliyor. Tam olarak göstermek için yakalamayı durdurun ve PCAPdroid ayarlarından \"%1$s\" seçeneğini etkinleştirin HTTP Yük Gönder (TX) @@ -308,7 +308,9 @@ Önce gizlilik PCAPdroid, ağ trafiğini root olmadan yakalamak için bir VPN gibi davranır. \n -\nYakalamayı başlatmak için, bir sonraki ekranda VPN isteğini kabul etmeniz gerekir +\nYakalamayı başlatmak için, bir sonraki ekranda VPN isteğini kabul etmeniz gerekir. +\n +\nNOT: Devam etmek için, Android ayarlarından yapılandırılmış her zaman açık VPN\'lerin tümünü devre dışı bırakın Tümleşik güvenlik duvarı ile tek tek uygulamalara ve etki alanlarına internet erişimini kolayca engelleyebilirsiniz \n \nBunu yerleşik trafik görünürlüğü ile birleştirerek gizliliğinizi korumak için en iyi aracı elde edin @@ -466,4 +468,23 @@ Bu kurallar hangi bağlantıların şifresinin çözüleceğini belirtir. Ana makine tabanlı kurallar yalnızca önceden bir DNS yanıtı görülürse çalışır Şifresini çöz… Şifreli + %1$s adresi çözümlenemedi + PCAP dosyası yükleme işlemi iptal edildi + TLS şifre çözme yalnızca yapılandırılan kurallarla eşleşen bağlantılara uygulanır. Şimdi şifre çözme kuralları oluşturmak istiyor musunuz\? + PCAP dosyası yüklendi + PCAP dosyası desteklenmeyen bir veri bağlantısına sahip + Belirtilen PCAP dosyası yok + Yakalama başlatma hatası. Aygıtın Magisk ile root yapıldığından emin olun + Bu bağlantının şifresi çözülmeyecek. Şifresini çözmek için bir şifre çözme kuralı oluşturun + PCAP okuma hatası + Etkin VPN algılandı + PCAP dosyası yükleniyor, lütfen bekleyin + Canlı yakala + \"%1$s\" yerine gerçek uygulamaları göstermek için, PCAP dosyasını dışa aktarmadan önce \"%2$s\" seçeneğini etkinleştirdiğinizden emin olun + Dosya yazılamadı + PCAP dosyasını aç… + Yakalama başlatma hatası. PCAPdroid\'e root erişimi verdiğinizden emin olun + Geçersiz PCAP dosyası + Vekil sunucu + Yakalama arayüzü açılamadı \ No newline at end of file From 2bdbb7680bb44e03d73a19c357096a9a84431ed0 Mon Sep 17 00:00:00 2001 From: odkate918c013d81c448ce Date: Sat, 9 Dec 2023 16:55:01 +0000 Subject: [PATCH 27/45] Update Ukrainian strings --- app/src/main/res/values-uk/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5cf289db6..2a3589920 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -486,4 +486,5 @@ Встановлено Керування дозволами Домен чорного списку + Збій запуску захоплення. Переконайтеся, що ви надали рут доступ PCAPdroid \ No newline at end of file From 530d49020330f1bcd5d4a4c378e6fd29f71b1300 Mon Sep 17 00:00:00 2001 From: Maximilian Borm Date: Sun, 31 Dec 2023 14:49:01 +0000 Subject: [PATCH 28/45] Update German strings --- app/src/main/res/values-de/strings.xml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d7aadc75e..2d9287e97 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -206,7 +206,7 @@ Einige Sperrlisten sind veraltet Kopiert Gekaufte Funktion jetzt verwenden werden - Aktive VPN-App trennen und fortfahren\? + Das Fortfahren wird das aktive VPN trennen Lade Apps… Menü öffnen Menü schließen @@ -380,7 +380,7 @@ Antwort HTTP-Anfrage HTTP-Antwort - Die Nutzlast ist gekürzt. Aktivieren Sie \"%1$s\", um sie vollständig anzuzeigen + Die Nutzlast ist gekürzt. Zur vollständigen Anzeige die Aufnahme stoppen und \"%1$s\" in den PCAPdroid Einstellungen aktivieren Zeigen Sie die volle Verbindung Payload (z.B. die volle HTTP-Anfrage und Antwort). Dies erfordert viel Speicher, nicht auf lange Sicht verwenden Nutzlast Senden @@ -412,7 +412,9 @@ \nDarüber hinaus können Sie einen PCAP-Dump des Datenverkehrs exportieren, Metadaten extrahieren und vieles mehr! PCAPdroid simuliert ein VPN, um den Netzwerkverkehr ohne Root zu erfassen. \n -\nUm die Aufzeichnung zu starten, müssen Sie die VPN-Anfrage auf dem nächsten Bildschirm akzeptieren +\nUm die Aufzeichnung zu starten, müssen Sie die VPN-Anfrage auf dem nächsten Bildschirm akzeptieren +\n +\nHINWEIS: um fortzufahren alle konfigurierten Always-on VPNs in den Android Einstellungen deaktivieren PCAPdroid bietet mehrere Möglichkeiten, um den Datenverkehr im Standard-PCAP-Format zur weiteren Analyse auszugeben \n \nÜber die Trailer-Option können Sie den Paketen App-Namen hinzufügen und diese in Wireshark anzeigen @@ -468,4 +470,10 @@ Aktivier mit QR-Code Datei kann nicht geschrieben werden Proxy Host + PCAP Datei geladen + Diese Verbindung wird nicht entschlüsselt. Erstellen Sie eine Entschlüsselungsregel um sie zu entschlüsseln + PCAP Lesefehler + Die PCAP Datei lädt gerade, bitte warten + PCAP Datei öffnen… + Ungültige PCAP Datei \ No newline at end of file From 2cea50b07bb75a4f9384415840fe1923541e5b5c Mon Sep 17 00:00:00 2001 From: Ettore Atalan Date: Sat, 13 Jan 2024 09:26:43 +0000 Subject: [PATCH 29/45] Update German strings --- app/src/main/res/values-de/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 2d9287e97..1130d1cbf 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -476,4 +476,6 @@ Die PCAP Datei lädt gerade, bitte warten PCAP Datei öffnen… Ungültige PCAP Datei + PCAP-Datei hat einen nicht unterstützten Datenlink + Die angegebene PCAP-Datei existiert nicht \ No newline at end of file From 2e3e939050448db612e4eab3079f5943d07b9bda Mon Sep 17 00:00:00 2001 From: Mario Herrmann Date: Fri, 19 Jan 2024 17:31:50 +0000 Subject: [PATCH 30/45] Update German strings --- app/src/main/res/values-de/strings.xml | 26 +++++++++++++------ .../metadata/android/de/full_description.txt | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1130d1cbf..f8525fd25 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -206,7 +206,7 @@ Einige Sperrlisten sind veraltet Kopiert Gekaufte Funktion jetzt verwenden werden - Das Fortfahren wird das aktive VPN trennen + Wenn Sie fortfahren wird die aktive VPN Verbindung unterbrochen. Lade Apps… Menü öffnen Menü schließen @@ -380,7 +380,7 @@ Antwort HTTP-Anfrage HTTP-Antwort - Die Nutzlast ist gekürzt. Zur vollständigen Anzeige die Aufnahme stoppen und \"%1$s\" in den PCAPdroid Einstellungen aktivieren + Der Payload ist gekürzt. Aktivieren Sie \"%1$s\", um den Payload vollständig anzuzeigen Zeigen Sie die volle Verbindung Payload (z.B. die volle HTTP-Anfrage und Antwort). Dies erfordert viel Speicher, nicht auf lange Sicht verwenden Nutzlast Senden @@ -410,11 +410,11 @@ PCAPdroid ist eine datenschutzfreundliche App, mit der Sie die von den Apps auf Ihrem Gerät hergestellten Verbindungen verfolgen und analysieren können \n \nDarüber hinaus können Sie einen PCAP-Dump des Datenverkehrs exportieren, Metadaten extrahieren und vieles mehr! - PCAPdroid simuliert ein VPN, um den Netzwerkverkehr ohne Root zu erfassen. + PCAPdroid simuliert einen VPN, um den Netzwerkverkehr ohne Root zu erfassen. \n -\nUm die Aufzeichnung zu starten, müssen Sie die VPN-Anfrage auf dem nächsten Bildschirm akzeptieren +\nUm die Aufzeichnung zu starten, müssen Sie die VPN-Anfrage auf dem nächsten Bildschirm akzeptieren. \n -\nHINWEIS: um fortzufahren alle konfigurierten Always-on VPNs in den Android Einstellungen deaktivieren +\nANMERKUNG: um fortzufahren deaktivieren Sie bitte alle Always-on VPNs in den Android Einstellungen PCAPdroid bietet mehrere Möglichkeiten, um den Datenverkehr im Standard-PCAP-Format zur weiteren Analyse auszugeben \n \nÜber die Trailer-Option können Sie den Paketen App-Namen hinzufügen und diese in Wireshark anzeigen @@ -471,11 +471,21 @@ Datei kann nicht geschrieben werden Proxy Host PCAP Datei geladen - Diese Verbindung wird nicht entschlüsselt. Erstellen Sie eine Entschlüsselungsregel um sie zu entschlüsseln + Die Verbindung wird nicht entschlüsselt. +\nErstelle eine Entschlüsselungsregel um die Verbindung zu entschlüsseln PCAP Lesefehler - Die PCAP Datei lädt gerade, bitte warten - PCAP Datei öffnen… + PCAP Datei wird geladen, bitte warten + Öffne PCAP Datei… Ungültige PCAP Datei PCAP-Datei hat einen nicht unterstützten Datenlink Die angegebene PCAP-Datei existiert nicht + Fehler beim starten der Aufzeichnung. Stelle sicher dass PCAPdroid root Berechtigungen erhalten hat + Um statt \"%1$s\" die tatsächlichen Apps anzuzeigen Stelle sicher dass die \"%2$s\" Option vor exportieren der PCAP Datei aktiviert wurde + Nur Verbindungen die mit den vorkonfigurierten Regeln übereinstimmen werden entschlüsselt. Möchtest du jetzt eine Entschlüsselungsregel erstellen? + Aktiver VPN festgestellt + Laden der PCAP Datei abgebrochen + Der Host %1$s konnte nicht gefunden werden + Die Aufnahmeschnittstelle konnte nicht geöffnet werden + Fehler beim starten der Aufzeichnung. Stelle sicher dass das Gerät mit Magisk gerootet ist. + Live Aufzeichnung \ No newline at end of file diff --git a/fastlane/metadata/android/de/full_description.txt b/fastlane/metadata/android/de/full_description.txt index cdddd705d..22eff9bcf 100644 --- a/fastlane/metadata/android/de/full_description.txt +++ b/fastlane/metadata/android/de/full_description.txt @@ -15,6 +15,6 @@ PCAPdroid simuliert ein VPN, um den Netzwerkverkehr ohne Root abzufangen. Es wir * Identifizieren Sie das Land und die ASN des Remote-Servers über Offline-DB-Lookups * Erfassen Sie auf gerooteten Geräten den Datenverkehr, während andere VPN-Apps ausgeführt werden -Wenn Sie planen, PCAPdroid zur Durchführung von Paketanalysen zu verwenden, lesen Sie bitte den spezifischen Abschnitt von das Handbuch. +Wenn Sie planen, PCAPdroid zur Durchführung von Paketanalysen zu verwenden, lesen Sie bitte den spezifischen Abschnitt im Handbuch. Treten Sie der internationalen PCAPdroid-Community auf Telegram oder auf Matrix. From 75aec972378d946a87345331bbea29c960c779d1 Mon Sep 17 00:00:00 2001 From: dedy prasetyo Date: Tue, 17 Oct 2023 21:14:22 +0000 Subject: [PATCH 31/45] Update Indonesian strings --- app/src/main/res/values-in/strings.xml | 105 +++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 021fc6059..e57cf374c 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -101,7 +101,7 @@ File disimpan sebagai \"%1$s\" Gelap Tema - Arahkan ulang semua koneksi TCP ke proxy SOCKS5 yang ditentukan + Arahkan ulang semua koneksi TCP ke proxy SOCKS5 Port proksi Capture dengan akses root Donasi @@ -170,7 +170,7 @@ Tidak ada item yang tersedia untuk dibeli Pelajari lebih lanjut Sekarang Anda dapat menggunakan fitur yang telah dibeli - Putuskan sambungan aplikasi VPN aktif dan lanjutkan\? + Melanjutkan akan memutuskan sambungan VPN yang aktif Buka drawer Tutup drawer Disimpan @@ -186,7 +186,7 @@ Paket Durasi Mengirim PCAP ke penerima UDP jarak jauh - Tidak dapat mengatur VPN. + Tidak dapat mengatur VPN. Pastikan VPN Selalu Aktif dinonaktifkan Inggris %1$d koneksi lama tidak ditampilkan Port server HTTP @@ -263,7 +263,7 @@ Dekripsi TLS Dekripsi lalu lintas SSL/TLS dengan melakukan mitm. Saat ini dapat bekerja dengan beberapa aplikasi, lihat panduan pengguna Inspeksi lalu lintas - Tidak dapat memulai layanan mitm. Instal ulang mitm addon dan coba lagi + Tidak dapat memulai layanan mitm. Coba buka aplikasi addon mitm secara manual dan coba lagi Petunjuk instalasi Mitm Instal Ekspor @@ -327,7 +327,9 @@ Utamakan privasi PCAPdroid mensimulasikan VPN untuk menangkap lalu lintas jaringan tanpa root. \n -\nUntuk memulai pengambilan, Anda harus menerima permintaan VPN di layar berikutnya +\nUntuk memulai pengambilan, Anda harus menerima permintaan VPN di layar berikutnya +\n +\nCATATAN: untuk melanjutkan, nonaktifkan VPN Selalu Aktif yang dikonfigurasi dari pengaturan Android Memeriksa permintaan HTTP, respons, dan data mentah menjadi mudah, berkat dekoder bawaan \n \nPunya koneksi terenkripsi\? Anda dapat mengaktifkan dekripsi TLS untuk mendapatkan data yang didekripsi @@ -340,7 +342,7 @@ PCAPdroid menyediakan berbagai cara untuk membuang lalu lintas dalam format PCAP standar untuk analisis lebih lanjut \n \nMelalui opsi cuplikan, Anda dapat menambahkan nama aplikasi ke paket - Memori rendah + Kekurangan Memori memori menumpuk Tidak ada yang cocok Teks bersih @@ -359,7 +361,7 @@ Alamat IP VPN Sertifikat mitm tampaknya tidak diinstal. Jika Anda melanjutkan, dekripsi mungkin gagal Konfigurasikan perangkat untuk dekripsi TLS - Pasang PCAPdroid add-on mitm + Instal PCAPdroid addon mitm %1$s izin tidak dapat diberikan Aplikasi kehabisan memori, perkirakan macet Opsi muatan penuh telah dinonaktifkan @@ -378,7 +380,7 @@ Meminta sebuah unlock token, tunggu sebentar Lihat Ini adalah token pembuka kunci Anda. Catat karena Anda akan memerlukannya untuk membuat kode lisensi - Koneksi teks yang jelas + Bersihkan koneksi teks Di Izinkan %1$s izin diberikan Firewall : %1$s @@ -398,4 +400,91 @@ Konfigurasikan pengarahan ulang ke proxy SOCKS5 Tidak dapat menulis berkas Pengarahan ulang SOCKS5 + Dalam mode ini semua koneksi akan diblokir, kecuali jika dimasukkan ke dalam daftar putih secara manual. Anda mungkin melewatkan notifikasi push tanpa daftar putih yang tepat + Tidak dapat menyelesaikan host %1$s + Mengonfigurasi server DNS yang akan digunakan selama pencatatan + Pemuatan file PCAP dibatalkan + Sandi + Anda mencoba mengimpor banyak aturan, yang bisa membuat aplikasi tidak responsif selama berinteraksi. Apakah Anda benar-benar ingin melanjutkan\? + addon mitm + Aturan dekripsi + Dekripsi TLS hanya diterapkan pada koneksi yang sesuai dengan aturan yang dikonfigurasi. Apakah Anda ingin membuat aturan dekripsi sekarang\? + File PCAP dimuat + Server DNS IPv4 + Aturan-aturan ini menentukan koneksi mana yang diizinkan. Aturan daftar blokir memiliki prioritas di atas aturan daftar putih + Terenkripsi + Alamat IP + Hapus dari daftar putih firewall + File PCAP memiliki datalink yang tidak didukung + Aturan yang sudah ditetapkan + Gunakan server DNS sistem jika memungkinkan + Kesalahan pembuatan lisensi [%1$d]: %2$s + Aktivasi melalui kode QR + Kode QR kedaluwarsa. Buat kode QR baru dan coba lagi + Anda telah mencapai batas lisensi untuk token buka kunci ini. Beli token baru untuk membuat lebih banyak lisensi + Autentikasi ke proxy melalui nama pengguna dan kata sandi + Alamat IP tujuan + Kesalahan saat memulai addon mitm. Periksa log untuk detailnya + Nama pengguna (username) + Total bytes + Apakah Anda benar-benar ingin mengatur ulang statistik ini\? + File PCAP yang ditentukan tidak ada + Server DNS + Aktivasi lisensi selesai + Urutkan berdasarkan + Pencatatan gagal dijalankan. Pastikan perangkat telah di-root dengan Magisk + Tambahkan ke daftar putih firewall + Izin penyimpanan eksternal diperlukan + Mengkonfigurasi aturan pemetaan port untuk mengalihkan koneksi ke host atau port yang berbeda + Gagal menyimpan ke penyimpanan eksternal. Periksa riwayat log aplikasi untuk detailnya + Menyediakan opsi tambahan untuk mitmproxy + Autentikasi SOCKS5 + Sambungan ini tidak akan didekripsi. Buat aturan dekripsi untuk mendekripsi + Dump paket dalam format dump pcapng, yang memungkinkan penyematan TLS terdekripsi dan rahasia + Jangan mendekripsi… + Kesalahan pembacaan PCAP + Tambahkan + VPN aktif terdeteksi + Pemuatan file PCAP sedang berlangsung, harap tunggu + Riwayat + dibutuhkan + format Pcapng + Server DNS IPv6 + Pemetaan port + Arahkan ke: + Koneksi ke addon mitm gagal. Sebagai solusi, Anda dapat mencoba membuka aplikasi addon mitm dan kembali ke PCAPdroid tanpa menutupnya. Apakah Anda ingin membukanya sekarang\? + Hapus item yang dipilih\? + Tidak ada data + Apakah Anda ingin membuat lisensi untuk perangkat \"%1$s\" menggunakan token pembuka kunci berikut ini\? + Instal PCAPdroid dari Google Play dan pindai kode QR ini + Injected + Kemampuan untuk menjalankan dekripsi TLS dengan root adalah fitur eksperimental. Ini adalah daftar bug yang diketahui: +\n +\n - di PCAPdroid Anda akan melihat lalu lintas yang dihasilkan dari aplikasi mitm-addon sebagai pengganti aplikasi asli +\n - ketika filter aplikasi ditetapkan, dekripsi hanya akan terjadi untuk aplikasi target, tetapi PCAPdroid masih akan menampilkan lalu lintas aplikasi lain +\n - jika penangkapan gagal dimulai, pastikan bahwa Anda benar-benar dapat menjalankan perintah iptables sebagai root (mis. melalui termux) +\n - ini mungkin tidak bekerja pada Android 12 dan yang lebih baru +\n - jika aplikasi VPN sedang berjalan, Anda harus menargetkan aplikasi tertentu untuk dekripsi atau mengecualikan addon mitm PCAPdroid dari VPN, jika tidak, lalu lintas akan berputar-putar + Meminta kode lisensi, harap tunggu + Addon mitm sedang berjalan + Pemcatatan secara live + Daftar putih (malware)… + Untuk menampilkan aplikasi yang sebenarnya, bukan \"%1$s\", pastikan untuk mengaktifkan opsi \"%2$s\" sebelum mengekspor file PCAP + Addon Mitm sudah dijalankan… + Port asli + Buka file PCAP… + Gunakan DNS sistem + Dekripsi… + Pencatatan gagal dijalankan. Pastikan Anda memberikan akses root ke PCAPdroid + Beli token pembuka kunci untuk melanjutkan aktivasi melalui kode QR + Kesalahan koneksi: %1$s + File PCAP tidak valid + Lisensi tidak valid + Aturan-aturan ini menentukan koneksi mana yang akan didekripsi. Aturan berbasis host hanya berfungsi jika balasan DNS sebelumnya terlihat + Proxy host + Pemetaan port telah ditentukan + Mode daftar putih + Opsi mitmproxy tambahan + Port tujuan + Tidak dapat membuka antarmuka pencatatan \ No newline at end of file From 9e1d3cc22f61a37b4fd8614cdca70699675d8754 Mon Sep 17 00:00:00 2001 From: Emanuele Faranda Date: Fri, 31 Mar 2023 08:17:24 +0000 Subject: [PATCH 32/45] Update Italian strings --- app/src/main/res/values-it/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 354f485a5..489b03abb 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -81,7 +81,7 @@ Chiaro Scuro Tema - Abilita proxy SOCKS5 + SOCKS5 Indirizzo IP del proxy Porta del proxy Cattura come root From e1591dbfe298676d74bd0967be81d613cae8c0e2 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Fri, 2 Feb 2024 10:26:11 +0100 Subject: [PATCH 33/45] Remove unused strings --- app/src/main/res/values-ar/strings.xml | 5 ----- app/src/main/res/values-de/strings.xml | 5 ----- app/src/main/res/values-es/strings.xml | 5 ----- app/src/main/res/values-fr/strings.xml | 3 --- app/src/main/res/values-in/strings.xml | 5 ----- app/src/main/res/values-it/strings.xml | 7 +------ app/src/main/res/values-ja/strings.xml | 2 -- app/src/main/res/values-ko/strings.xml | 2 -- app/src/main/res/values-nb-rNO/strings.xml | 5 ----- app/src/main/res/values-pl/strings.xml | 5 ----- app/src/main/res/values-pt-rBR/strings.xml | 5 ----- app/src/main/res/values-ru/strings.xml | 5 ----- app/src/main/res/values-tr/strings.xml | 5 ----- app/src/main/res/values-uk/strings.xml | 5 ----- app/src/main/res/values-zh-rCN/strings.xml | 5 ----- 15 files changed, 1 insertion(+), 68 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 2cf322f99..06dd6af1c 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -15,7 +15,6 @@ خادم HTTP: http://%1$s:%2$d استعلام استضافة - استهداف تطبيق مستعد حالة حول @@ -74,7 +73,6 @@ لا يمكن حذف الملف مصادقة SOCKS5 قم بالمصادقة على الوكيل عبر اسم المستخدم وكلمة المرور - عنوان IP الوكيل منفذ الوكيل اختيار الكل هدف SDK @@ -249,8 +247,6 @@ إعداد Mitm ثَبَّتَ تصدير - تَهيئة - امنح PCAPdroid الإذن للتحكم في إضافة mitm الترتيب حسب أسم المستخدم فشلت الكتابة إلى وحدة التخزين الخارجية. تحقق من سجل التطبيق للحصول على التفاصيل @@ -453,7 +449,6 @@ أخر حظر تم تعطيل جِدار الحماية تم بدء هذا الاتصال بواسطة netd لذلك لا يمكن حظره - قم بحظر اتصالات QUIC حتى تتمكن من العودة إلى TLS القابل لفك التشفير. قد تتوقف بعض التطبيقات عن العمل حظر DNS الخاص ذاكرة منخفضة لأسباب أمنية، غير مسموح بإرسال حركة المرور إلى الخادم البعيد \"%1$s\" diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f8525fd25..42c3b2242 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -15,7 +15,6 @@ HTTP-Server: http://%1$s:%2$d Abfrage Host - Ziel-App Über Status App @@ -104,7 +103,6 @@ Keine App für die Dateiauswahl gefunden PCAPdroid ist ein Open-Source-Netzwerkaufnahme- und Überwachungswerkzeug, das ohne Root-Rechte funktioniert Thema - Proxy-IP-Adresse Proxy-Port Erlaubt PCAPdroid die Ausführung mit anderen VPN-Apps Spenden @@ -240,7 +238,6 @@ Kaufe die Funktion %1$s, um Verbindungen zu sperren Klartext-Verbindungen QUIC sperren - QUIC-Verbindungen sperren, um möglicherweise auf entschlüsselbares TLS zurückzugreifen. Einige Apps funktionieren möglicherweise nicht mehr Private DNS erkennen und möglicherweise sperren, um DNS-Verkehr zu untersuchen. Die Deaktivierung dieser Funktion kann die Analyse des Datenverkehrs behindern Firewall: %1$s Dauerhaft entsperren @@ -251,12 +248,10 @@ Telefon Installieren Exportieren - Konfigurieren Zertifikat wird überprüft… Protokoll Keine Daten Whitelist (Malware)… - Geben Sie PCAPdroid die Berechtigung, das mitm-Addon zu steuern Beim Exportieren des CA-Zertifikats ist ein Fehler aufgetreten \n \nWenn Ihr Gerät Autostart oder ähnliche Software implementiert, um die Ausführung von Hintergrunddiensten einzuschränken, stellen Sie sicher, dass Sie PCAPdroid auf die weiße Liste setzen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index df03254b2..581fff4b1 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -8,7 +8,6 @@ %1$s recibidos — %2$s enviados Query Host - App objetivo Preparado Acerca de Estado @@ -82,7 +81,6 @@ Oscuro Tema SOCKS5 - Dirección IP proxy Puerto proxy Capturar como root Donar @@ -227,8 +225,6 @@ Asistente de configuración mitm Instalar Exportar - Configurar - Otorgar a PCAPdroid el permiso de controlar el complemento mitm Verificando el certificado… Certificado exportado, ahora instálalo en los ajustes de Android Un error ha ocurrido mientras se exportaba el certificado CA @@ -333,7 +329,6 @@ Bloquear Bloquea el acceso a internet de aplicaciones, configura reglas específicas para dominios y direcciones IP. Solo funciona con la captura sin root Bloquear QUIC - Bloquear las conexiones QUIC para recaer en TLS descifrable. Algunas aplicaciones pueden dejar de funcionar Siguiente Instala la extensión complemento mitm Funciones de pago diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 21ed7d86e..ec24b1070 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -15,7 +15,6 @@ Hôte Prêt À propos - Filtrer l\'appli Aucune connexion Source Destination @@ -111,7 +110,6 @@ Port de proxy Capturer comme administrateur Rediriger toutes les connexions TCP vers le proxy SOCKS5 spécifié - Adresse IP du proxy Autorise le lancement de PCAPdroid avec d\'autres applications de VPN Activer le proxy SOCKS5 Capture @@ -222,7 +220,6 @@ Inspection du trafic Installer Exporter - Configurer Le certificat CA est installé Vérification du certificat… Mettre à jour diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index e57cf374c..0748b77cd 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -58,7 +58,6 @@ Tangkap lalu lintas semua aplikasi Kolektor UDP: %1$s:%2$d Host - Aplikasi target Siap Tentang Detail koneksi @@ -210,7 +209,6 @@ Tidak diketahui Sistem Tidak terjangkau - Alamat IP proxy Terang Mengizinkan PCAPdroid menangkap koneksi VPN aplikasi lain yang sedang berjalan Lalu lintas @@ -267,8 +265,6 @@ Petunjuk instalasi Mitm Instal Ekspor - Konfigurasikan - Berikan PCAPdroid izin untuk mengontrol mitm addon Ekspor sertifikat CA PCAPdroid, lalu buka pengaturan \"Enkripsi & Kredensial\" Android dan pilih instal sebagai \"sertifikat CA\" Ekspor gagal Tidak dienkripsi @@ -306,7 +302,6 @@ Koneksi ini dimulai oleh netd sehingga tidak dapat diblokir Lanjut Blokir QUIC - Blokir koneksi QUIC untuk kemungkinan kembali ke TLS yang dapat didekripsi. Beberapa aplikasi mungkin berhenti bekerja Mendeteksi dan mungkin memblokir DNS pribadi untuk memeriksa lalu lintas DNS. Menonaktifkan ini dapat menghambat analisis lalu lintas Kembali Wizard ini akan memandu Anda melalui penginstalan PCAPdroid mitm addon dan otoritas sertifikasi, yang diperlukan untuk melakukan dekripsi TLS diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 489b03abb..2dbd87ddc 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -8,7 +8,6 @@ %1$s ricevuti — %2$s inviati Query Host - Filtro app Pronto Informazioni Stato @@ -82,7 +81,6 @@ Scuro Tema SOCKS5 - Indirizzo IP del proxy Porta del proxy Cattura come root Permette a PCAPdroid di funzionare assieme ad altre app VPN @@ -283,9 +281,7 @@ Ispezione del traffico Installa Esporta - Configura Esporta il certificato CA di PCAPdroid, poi importalo dalle impostazioni Android di \"Crittografia e Credenziali\", installandolo come \"Certificato CA\" - Dai a PCAPdroid il permesso di controllare l\'addon di mitm Errore durante l\'esportazione del certificato CA \n \nSe il tuo dispositivo utilizza Autostart o software simile per limitare l\'esecuzione di processi in background, assicurati di escludere PCAPdroid @@ -312,7 +308,6 @@ Blocca QUIC Blocca l\'accesso Internet alle app, configura regole per domini e indirizzi IP specifici. Funziona solamente con la cattura non-root Firewall senza root - Blocca le connessioni QUIC per cercare di forzare l\'utilizzo di TLS decrittabile. Alcune app potrebbero smettere di funzionare PCAPdroid è pronto a decrittare il traffico TLS \n \nPer saperne di più sulle misure di sicurezza adottate dalle app, che potrebbero interferire con la decrittazione, e su come bypassarle, far riferimento alla guida utente @@ -486,4 +481,4 @@ La connessione non sarà decrittata. Per decrittarla, crea una regola di decrittazione La decrittazione TLS è applicata soltanto alle connessioni che matchano le regole configurate. Vuoi creare ora delle regole di decrittazione\? Avvio della cattura fallito. Assicurati di concedere l\'accesso root a PCAPdroid - \ No newline at end of file + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index bc42e8074..ba08898cc 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -8,7 +8,6 @@ %1$s 受信 — %2$s 送信 クエリ ホスト - アプリ フィルター 準備完了 詳細 状態 @@ -84,7 +83,6 @@ テーマ SOCKS5 プロキシを有効にする すべての TCP 接続を指定した SOCKS5 プロキシにリダイレクトします - プロキシ IP アドレス プロキシ ポート ルートとしてキャプチャ PCAPdroid とほかの VPN アプリとの併用を許可します diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 39942edd7..adc4fdec9 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -15,7 +15,6 @@ ASN %1$s 받음 — %2$s 보냄 호스트 - 타겟 앱 준비 상태 커넥션 없음 @@ -83,7 +82,6 @@ SOCKS5 시스템 파일이 \"%1$s\"로 저장되었습니다 - 프록시 IP 주소 라이트 모든 TCP 커넥션을 지정된 SOCKS5 프록시로 리다이렉트합니다 루트로 캡처하기 diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 0274e24f9..ac8982b5d 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -25,7 +25,6 @@ Tilkoblinger Tilkoblingsdetaljer %1$s mottatt — %2$s sendt - Målprogram Program Protokoll Kilde @@ -79,7 +78,6 @@ Versjon Tilganger Android-systemet - IP-adresse for mellomtjener Mellomtjenerport Program: %1$s Mål-SDK @@ -295,11 +293,9 @@ Slett valgte regler\? Brannmur av Avblokker - Sett opp Eksporter TLS-dekryptering Dekrypter SSL/TLS-trafikk ved å utføre MITM. Dette kan fungere med noen programmer nå. Sjekk brukerveiledningen. - Gi PCAPdroid tilgang til å kontrollere MITM-tillegget Eksporter sertifikatmyndighetssertifikatet tilhørende PCAPdroid, åpne så «Kryptering og identitetsdetaljer» i Android og veld å installere det som et «CA-sertifikat». Sjekker sertifikatet … Sertifikat eksporter. Installer det nå fra Android-innstillingene. @@ -341,7 +337,6 @@ Brannmur uten rot-tilgang Blokker QUIC Blokker privat DNS - Blokker QUIC-tilkoblinger for å potensielt falle tilbake til dekrypterbar TLS. Noen programmer kan slutte å fungere. Velkommen til PCAPdroid Neste Hopp over diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f60ce808e..5269e59a8 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -43,7 +43,6 @@ %1$s otrzymane — %2$s wysłane Zapytanie Host - Skanuj aplikacje Gotowe Informacje Status @@ -127,7 +126,6 @@ SOCKS5 proxy: %1$s:%2$d Uwierzytelnianie SOCKS5n Uwierzytelnij serwer proxy za pomocą nazwy użytkownika i hasła - Adres IP serwera proxy Port proxy Przechwyć jako root Pozwala PCAPdroid działać z innymi aplikacjami VPN @@ -272,8 +270,6 @@ Instalacja Eksport Zainstaluj PCAPdroid dodatek mitm - Skonfiguruj - Zezwól PCAPdroidowi na kontrolowanie dodatku mitm Wyeksportuj certyfikat PCAPdroid CA, a następnie otwórz ustawienia Androida \"Szyfrowanie i poświadczenia\" i wybierz zainstaluj go jako \"Certyfikat CA\"" Zainstaluj certyfikat PCAPdroid CA, wybierając \"VPN i aplikacje\". Android poprosi o ekran blokady lub hasło Sprawdzanie certyfikatu… @@ -346,7 +342,6 @@ Blokuj dostęp aplikacji do Internetu, konfiguruj reguły dla określonych domen i adresów IP. Działa tylko z przechwytywaniem innym niż root Zapora bez roota Zablokuj QUIC - Blokuj połączenia QUIC, aby ewentualnie wrócić do odszyfrowywanego TLS. Niektóre aplikacje mogą przestać działać Zablokuj prywatny DNS Wykryj i prawdopodobnie zablokuj prywatny DNS, aby sprawdzić ruch DNS. Wyłączenie tej opcji może utrudnić analizę ruchu Ten kreator przeprowadzi Cię przez proces instalacji dodatku PCAPdroid mitm i certyfikacji, które są potrzebne do wykonania odszyfrowania TLS diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 62da0be73..b0536b82a 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -9,7 +9,6 @@ %1$s recebidos — %2$s enviados Consulta Host - App desejado Pronto Sobre Status @@ -83,7 +82,6 @@ Escuro Tema SOCKS5 - Endereço IP do proxy Porta do proxy Capturar como root Doar @@ -228,7 +226,6 @@ Uso do heap Somente IPv6 Firewall: %1$s - Conceda ao PCAPdroid uma permissão para controlar a extensão de mitm A extensão de mitm do PCAPdroid precisa ser atualizada PCAPdroid fornece múltiplas formas para fazer o dump do tráfego no formato PCAP padrão para análise posterior \n @@ -262,7 +259,6 @@ Inspeção de tráfego Não foi possível iniciar o serviço do mitm. Reinstale a extensão mitm e tente novamente Assistente de configuração do mitm - Configurar Verificando o certificado… Certificado exportado, agora instale-o em configurações do Android O certificado CA está instalado @@ -336,7 +332,6 @@ Esta conexão foi iniciada pela netd por isso não pôde ser bloqueada Firewall não-root Bloquear QUIC - Bloquear conexões QUIC para possivelmente usar o TLS descriptografável. Alguns apps podem parar de funcionar Bloquear DNS privado Este assistente mostrará você como instalar a extensão de mitm do PCAPdroid e autoridade de certificação, que são necessários para realizar a Decriptação do TLS Pular diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d0efd209c..8cc805f7d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -8,7 +8,6 @@ %1$s получено — %2$s отправлено Запрос Хост - Целевое приложение Готов О приложении Состояние @@ -82,7 +81,6 @@ Тёмная Тема Включить SOCKS5 прокси - IP-адрес прокси Порт прокси Захват в root режиме Пожертвовать @@ -223,8 +221,6 @@ Мастер настройки mitm Установить Экспорт - Дать PCAPdroid разрешение контролировать mitm дополнение - Настроить Сертификат экспортирован, теперь установите его из настроек Android Корневой сертификат не установлен, запустите мастер настройки mitm PCAPdroid mitm дополнение требуется обновить @@ -311,7 +307,6 @@ \nЕсли на вашем устройстве есть Autostart или похожие программы, ограничивающие активность фоновых процессов, убедитесь в том, что разрешили все для PCAPdroid Это соединение инициировано сервисом netd, поэтому оно не может быть заблокировано Блокируйте доступ в интернет для приложений, настраивайте правила для доменов и IP адресов. Работает только при захвате без root - Блокировка QUIC соединений возможно вернет к TLS, который можно дешифровать. Некоторые приложения могут перестать работать Назад Готово Этот мастер настройки проведет вас через установку дополнения PCAPdroid mitm и корневого сертификата, которые необходимы для проведения дешифрования TLS diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index cbc687d5e..95046b54e 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -27,7 +27,6 @@ %1$s alındı — %2$s gönderildi Sorgu Ana makine - Hedef uygulama Hazır Bağlantılar Bağlantı yok @@ -167,7 +166,6 @@ Ulaşılamıyor Dosya \"%1$s\" olarak kaydedildi Tüm TCP bağlantılarını bir SOCKS5 vekiline yönlendir - Vekil IP adresi Protokol: %1$s Android sistemi Yakala @@ -258,12 +256,10 @@ Telefon SOCKS5 Telefon hizmetleri - Yapılandır \"VPN ve uygulamalar\" seçeneğini seçerek PCAPdroid CA sertifikasını kurun. Android, kilit ekranınızı veya parolanızı soracak Ortadaki adam gerçekleştirerek SSL/TLS trafiğinin şifresini çöz. Bu artık bazı uygulamalarla çalışabilir, kullanım kılavuzuna bakın Trafik denetimi Dışa aktar - PCAPdroid\'e ortadaki adam eklentisini denetleme izni verin Ortadaki adam hizmeti başlatılamadı. Ortadaki adam eklentisini elle açmaya çalışın ve yeniden deneyin Şifrelenmedi CA sertifikası kurulmadı, ortadaki adam kurulum sihirbazını çalıştırın @@ -297,7 +293,6 @@ Güvenlik duvarı etkin Uygulamalara internet erişimini engelleyin, belirli etki alanları ve IP adresleri için kurallar yapılandırın. Yalnızca root olmayan yakalama ile çalışır QUIC\'i engelle - Muhtemelen şifresi çözülebilir TLS\'ye geri dönmek için QUIC bağlantılarını engelleyin. Bazı uygulamalar çalışmayı durdurabilir DNS trafiğini incelemek için özel DNS\'yi algılayın ve muhtemelen engelleyin. Bunu devre dışı bırakmak trafik incelemesini engelleyebilir PCAPdroid artık TLS trafiğinin şifresini çözmeye hazır \n diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2a3589920..39c761548 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -49,7 +49,6 @@ Тепер ви можете користуватися придбаною функцією Збережено Дешифрування TLS - Налаштувати Перевірка сертифікату… Сертифікат ЦС встановлено WebSocket @@ -226,7 +225,6 @@ %1$s отримано — %2$s відправлено Запит Хост - Цільовий додаток Про додаток Статус З\'єднання @@ -298,7 +296,6 @@ Проксі SOCKS5: %1$s:%2$d Автентифікація SOCKS5 Автентифікувати до проксі за допомогою імені користувача і пароля - Адреса IP проксі Хост проксі Порт проксі Захопити як рут @@ -405,7 +402,6 @@ Встановити Експортувати Встановити доповнення mitm PCAPdroid - Надати дозвіл PCAPdroid для керування доповненням mitm Експортуйте сертифікат PCAPdroid CA. потім відкрийте налаштування Android \"Шифрування й облікові дані\" та виберіть встановити його як \"Сертифікат ЦС\" Встановіть сертифікат PCAPdroid CA, обираючи \"VPN та додатки\". Android запитає ваше блокування екрану або пароль Трапилася помилка при експортуванні сертифікату ЦС @@ -460,7 +456,6 @@ Мережевий екран вимкнено Розблокувати Це з\'єднання ініційовано мережею, тому його не вдається заблокувати - Блокувати з\'єднання QUIC, щоб унеможливити повернення до дешифровного TLS. Деякі програми можуть перестати працювати Цей майстер допоможе вам встановити доповнення PCAPdroid mitm і центр сертифікації, які потрібно щоб дешифрувати TLS PCAPdroid тепер готовий до дешифрування трафіку TLS \n diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index fa5029974..6be9ac130 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -33,7 +33,6 @@ %1$s 已接收 — %2$s 已发送 查询 主机 - 目标应用 就绪 关于 状态 @@ -107,7 +106,6 @@ 深色 主题 启用 SOCKS5 代理 - 代理 IP 地址 代理端口 以 root 用户身份捕获流量 捐赠 @@ -249,8 +247,6 @@ 无法启动 mitm 服务。尝试手动打开 mitm 附加组件应用并重试 Mitm 设置向导 安装 - 配置 - 给予 PCAPdroid 控制 mitm 附加组件的权限 检查证书… 证书已导出,从 Android 系统设置安装它 CA 证书已安装 @@ -358,7 +354,6 @@ \n如果你的设备部署了 Autostart 或类似软件来限制后台服务执行,请确保将 PCAPdroid 加入白名单 拦截 QUIC 拦截私人 DNS - 拦截 QUIC 连接,使其有可能回退到可解密的 TLS。一些应用可能会停止工作 非 root 防火墙 该向导将指导你安装 PCAPdroid mitm 附加组件和证书颁发机构。需要这两样东西才能执行 TLS 解密 欢迎来到 PCAPdroid From a0a3430f19c41c7fdddb4834c51511b7b10dd290 Mon Sep 17 00:00:00 2001 From: Emanuele Faranda Date: Fri, 2 Feb 2024 12:30:11 +0100 Subject: [PATCH 34/45] Remove unused English strings For some reason, these were not removed in the latest commit --- app/src/main/res/values/strings.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7313a175..a7127764d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,7 +44,6 @@ %1$s received — %2$s sent Query Host - Target app Ready About Status @@ -129,7 +128,6 @@ SOCKS5 proxy: %1$s:%2$d SOCKS5 authentication Authenticate to the proxy via username and password - Proxy IP address Proxy host Proxy port Capture as root @@ -276,8 +274,6 @@ Export Export… Install the PCAPdroid mitm addon - Configure - Give PCAPdroid the permission to control the mitm addon Export the PCAPdroid CA certificate, then open the Android \"Encryption & Credentials\" settings and choose install it as a \"CA certificate\" Install the PCAPdroid CA certificate, choosing \"VPN and apps\". Android will ask for your lockscreen or password Checking the certificate… @@ -351,7 +347,6 @@ Block internet access to apps, configure rules for specific domains and IP addresses. Only works with the non-root capture No-root firewall Block QUIC - Block QUIC connections to possibly fall back to decryptable TLS. Some apps may stop working Block private DNS Detect and possibly block private DNS to inspect DNS traffic. Disabling this can hinder traffic analysis This wizard will guide you through the installation of the PCAPdroid mitm addon and certification authority, which are needed to perform the TLS decryption From 2bb955ab90bdef287e14f4ee7bf382425de55bf6 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Mon, 5 Feb 2024 12:37:50 +0100 Subject: [PATCH 35/45] Improve payload export - HTTP body is now exported in binary form, with file name - fix export when HTTP body is empty - set "body" as the default HTTP export action - add new "Raw bytes" export option for non-HTTP See #362 --- .../remote_capture/HTTPReassembly.java | 19 ++++ .../activities/ConnectionDetailsActivity.java | 56 ++++++++++-- .../adapters/PayloadAdapter.java | 87 ++++++++++++++++--- .../remote_capture/model/PayloadChunk.java | 1 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 142 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java b/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java index 44f98265f..d107f3175 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java +++ b/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java @@ -40,6 +40,7 @@ public class HTTPReassembly { private boolean mChunkedEncoding; private ContentEncoding mContentEncoding; private String mContentType; + private String mPath; private int mContentLength; private int mHeadersSize; private final ArrayList mHeaders = new ArrayList<>(); @@ -68,6 +69,7 @@ private void reset() { mChunkedEncoding = false; mContentLength = -1; mContentType = null; + mPath = null; mHeadersSize = 0; mHeaders.clear(); mBody.clear(); @@ -100,10 +102,26 @@ public void handleChunk(PayloadChunk chunk) { // Reading the HTTP headers int headers_end = Utils.getEndOfHTTPHeaders(payload); int headers_size = (headers_end == 0) ? payload.length : headers_end; + boolean is_first_line = (mHeadersSize == 0); mHeadersSize += headers_size; try(BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(payload, 0, headers_size)))) { String line = reader.readLine(); + + if (is_first_line && (line != null)) { + if (line.startsWith("GET ") || line.startsWith("POST ") + || line.startsWith("HEAD ") || line.startsWith("PUT ")) { + int first_space = line.indexOf(' '); + int second_space = line.indexOf(' ', first_space + 1); + + if ((first_space > 0) && (second_space > 0)) { + mPath = line.substring(first_space + 1, second_space); + log_d("Path: " + mPath); + } + } + is_first_line = false; + } + while((line != null) && (line.length() > 0)) { line = line.toLowerCase(); //log_d("[HEADER] " + line); @@ -256,6 +274,7 @@ public void handleChunk(PayloadChunk chunk) { to_add.type = PayloadChunk.ChunkType.RAW; to_add.contentType = mContentType; + to_add.path = mPath; mListener.onChunkReassembled(to_add); reset(); // mReadingHeaders = true } diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java index f9ca0ccfc..395eabc4c 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java @@ -69,7 +69,8 @@ public class ConnectionDetailsActivity extends BaseActivity implements Connectio private boolean mHasPayload; private boolean mHasHttpTab; private boolean mHasWsTab; - private String mPayloadToExport; + private String mStringPayloadToExport; + private byte[] mRawPayloadToExport; private final ArrayList mListeners = new ArrayList<>(); private static final int POS_OVERVIEW = 0; @@ -345,35 +346,72 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { @Override public void exportPayload(String payload) { - mPayloadToExport = payload; + mStringPayloadToExport = payload; + mRawPayloadToExport = null; Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TITLE, Utils.getUniqueFileName(this, "txt")); - Log.d(TAG, "exportPayload: launching dialog"); + Utils.launchFileDialog(this, intent, payloadExportLauncher); + } + + @Override + public void exportPayload(byte[] payload, String contentType, String fname) { + mStringPayloadToExport = null; + mRawPayloadToExport = payload; + + if (fname.isEmpty()) { + String ext; + + switch (contentType) { + case "text/html": + ext = "html"; + break; + case "application/octet-stream": + ext = "bin"; + break; + default: + ext = "txt"; + } + + fname = Utils.getUniqueFileName(this, ext); + } + + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType(contentType); + intent.putExtra(Intent.EXTRA_TITLE, fname); + Utils.launchFileDialog(this, intent, payloadExportLauncher); } private void payloadExportResult(final ActivityResult result) { Log.d(TAG, "payloadExportResult"); - if (mPayloadToExport == null) + if ((mRawPayloadToExport == null) && (mStringPayloadToExport == null)) return; if((result.getResultCode() == RESULT_OK) && (result.getData() != null) && (result.getData().getData() != null)) { try(OutputStream out = getContentResolver().openOutputStream(result.getData().getData(), "rwt")) { - try(OutputStreamWriter writer = new OutputStreamWriter(out)) { - writer.write(mPayloadToExport); - } - Utils.showToast(this, R.string.save_ok); + if (out != null) { + if (mStringPayloadToExport != null) { + try (OutputStreamWriter writer = new OutputStreamWriter(out)) { + writer.write(mStringPayloadToExport); + } + } else + out.write(mRawPayloadToExport); + Utils.showToast(this, R.string.save_ok); + } else + Utils.showToastLong(this, R.string.export_failed); } catch (IOException e) { e.printStackTrace(); Utils.showToastLong(this, R.string.export_failed); } } - mPayloadToExport = null; + mRawPayloadToExport = null; + mStringPayloadToExport = null; } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java index b633c14d8..ecbd1b6d9 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java @@ -46,7 +46,9 @@ import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Locale; /* An adapter to show PayloadChunk items. @@ -72,6 +74,7 @@ public class PayloadAdapter extends RecyclerView.Adapter 0) { + // Try to match the HTTP request, to determine the file name + AdapterChunk req_chunk = getItem(payload_pos - 1).adaptChunk; + if (req_chunk.mChunk.is_sent && (req_chunk.mChunk.path != null)) + fname = req_chunk.mChunk.path; + } + + if (!fname.isEmpty()) { + int last_slash = fname.lastIndexOf('/'); + if (last_slash > 0) + fname = fname.substring(last_slash + 1); + } + + if (fname.contains(".")) + Log.d(TAG, "File name: " + fname); + else + fname = ""; + + String filename = fname; boolean has_body = (crlf_pos > 0) && (crlf_pos < (payload.length() - 4)); if (!has_body) { - Utils.copyToClipboard(mContext, payload); + // only HTTP headers + if (is_export) { + if (mExportHandler != null) + mExportHandler.exportPayload(payload); + } else + Utils.copyToClipboard(mContext, payload); return; } @@ -285,7 +324,7 @@ private void handleCopyExportButtons(PayloadViewHolder holder, boolean is_export AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle(title); - builder.setSingleChoiceItems(choices, 2, (dialogInterface, i) -> {}); + builder.setSingleChoiceItems(choices, 1, (dialogInterface, i) -> {}); builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); builder.setPositiveButton(positive_action, (dialogInterface, i) -> { int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); @@ -299,32 +338,54 @@ private void handleCopyExportButtons(PayloadViewHolder holder, boolean is_export } if (is_export) { - if (mExportHandler != null) - mExportHandler.exportPayload(to_copy); + if (mExportHandler != null) { + boolean only_body = (choice == 1); + + if (only_body) { + // export the raw body bytes + byte[] payload_bytes = chunk.mChunk.payload; + + if (crlf_pos < (payload_bytes.length - 4)) + payload_bytes = Arrays.copyOfRange(payload_bytes, crlf_pos + 4, payload_bytes.length); + + mExportHandler.exportPayload(payload_bytes, content_type, filename); + } else + mExportHandler.exportPayload(to_copy); + } } else Utils.copyToClipboard(mContext, to_copy); }); builder.create().show(); } else { - String[] choices = { + List choices = new ArrayList<>(Arrays.asList( mContext.getString(R.string.printable_text), mContext.getString(R.string.hexdump) - }; + )); + if (is_export) + choices.add(mContext.getString(R.string.raw_bytes)); AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle(title); - builder.setSingleChoiceItems(choices, mShowAsPrintable ? 0 : 1, (dialogInterface, i) -> {}); + builder.setSingleChoiceItems(choices.toArray(new String[]{}), mShowAsPrintable ? 0 : 1, (dialogInterface, i) -> {}); builder.setNeutralButton(R.string.cancel_action, (dialogInterface, i) -> {}); builder.setPositiveButton(positive_action, (dialogInterface, i) -> { int choice = ((AlertDialog)dialogInterface).getListView().getCheckedItemPosition(); - String payload = getItem(payload_pos).adaptChunk.getExpandedText(choice == 0); - if (is_export) { + if (choice == 2 /* raw bytes */) { + assert (is_export); + if (mExportHandler != null) - mExportHandler.exportPayload(payload); - } else - Utils.copyToClipboard(mContext, payload); + mExportHandler.exportPayload(chunk.mChunk.payload, "application/octet-stream", ""); + } else { + String payload = getItem(payload_pos).adaptChunk.getExpandedText(choice == 0); + + if (is_export) { + if (mExportHandler != null) + mExportHandler.exportPayload(payload); + } else + Utils.copyToClipboard(mContext, payload); + } }); builder.create().show(); } diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/PayloadChunk.java b/app/src/main/java/com/emanuelef/remote_capture/model/PayloadChunk.java index 5ce971a69..33dd0bf2b 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/PayloadChunk.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/PayloadChunk.java @@ -28,6 +28,7 @@ public class PayloadChunk implements Serializable { public long timestamp; public ChunkType type; public String contentType; + public String path; // Serializable need in ConnectionPayload fragment public enum ChunkType implements Serializable { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a7127764d..664786ba3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -504,4 +504,5 @@ Body Both What\'s new + Raw bytes From 3a055e71572ddba8dd8abf52ce3695caa2269cc8 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Tue, 6 Feb 2024 22:11:51 +0100 Subject: [PATCH 36/45] Remove query string from file path See #362 --- .../java/com/emanuelef/remote_capture/HTTPReassembly.java | 6 +++++- .../emanuelef/remote_capture/adapters/PayloadAdapter.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java b/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java index d107f3175..868638ae2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java +++ b/app/src/main/java/com/emanuelef/remote_capture/HTTPReassembly.java @@ -116,10 +116,14 @@ public void handleChunk(PayloadChunk chunk) { if ((first_space > 0) && (second_space > 0)) { mPath = line.substring(first_space + 1, second_space); + + int query_start = mPath.indexOf('?'); + if (query_start >= 0) + mPath = mPath.substring(0, query_start); + log_d("Path: " + mPath); } } - is_first_line = false; } while((line != null) && (line.length() > 0)) { diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java index ecbd1b6d9..244dabcb2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/PayloadAdapter.java @@ -294,7 +294,7 @@ else if (payload_pos > 0) { if (!fname.isEmpty()) { int last_slash = fname.lastIndexOf('/'); - if (last_slash > 0) + if (last_slash >= 0) fname = fname.substring(last_slash + 1); } From 69f06c2586d7a39b6c6799f63eb982b1c37df6a0 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Tue, 6 Feb 2024 22:12:27 +0100 Subject: [PATCH 37/45] Arbitrary file extension in payload export If the mime-type is mapped, Android will force a specific file extension. "application/http" is now used as a workaround. See #362 --- .../activities/ConnectionDetailsActivity.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java index 395eabc4c..204e54b10 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java @@ -372,6 +372,9 @@ public void exportPayload(byte[] payload, String contentType, String fname) { case "application/octet-stream": ext = "bin"; break; + case "application/json": + ext = "json"; + break; default: ext = "txt"; } @@ -379,9 +382,14 @@ public void exportPayload(byte[] payload, String contentType, String fname) { fname = Utils.getUniqueFileName(this, ext); } + /* This is an unmapped mime type, which allows the user to specify the file, + * extension instead of Android forcing it, see + * https://android.googlesource.com/platform/external/mime-support/+/fa3f892f28db393b1411f046877ee48179f6a4cf/mime.types */ + final String generic_mime = "application/http"; + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(contentType); + intent.setType(generic_mime); intent.putExtra(Intent.EXTRA_TITLE, fname); Utils.launchFileDialog(this, intent, payloadExportLauncher); From fb7adf4307be0f8485bb956470b74b98fd56943d Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Wed, 7 Feb 2024 09:47:58 +0100 Subject: [PATCH 38/45] Fix inaccurate firewall grace period The timeout did not take into account the time spent during the device deep sleep Fixes #390 --- .../java/com/emanuelef/remote_capture/MitmReceiver.java | 4 ++-- .../emanuelef/remote_capture/activities/AboutActivity.java | 6 +++--- .../java/com/emanuelef/remote_capture/model/Blocklist.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java b/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java index e3cfb8248..66e5e22e0 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MitmReceiver.java @@ -109,7 +109,7 @@ private static class PendingMessage { type = _type; msg = _msg; port = _port; - pendingSince = SystemClock.uptimeMillis(); + pendingSince = SystemClock.elapsedRealtime(); when = _now; } } @@ -331,7 +331,7 @@ private void handleMessage(ConnectionDescriptor conn, MsgType type, byte[] messa private synchronized void addPendingMessage(PendingMessage pending) { // Purge unresolved connections (should not happen, just in case) if(mPendingMessages.size() > 32) { - long now = SystemClock.uptimeMillis(); + long now = SystemClock.elapsedRealtime(); for(int i = mPendingMessages.size()-1; i>=0; i--) { ArrayList pp = mPendingMessages.valueAt(i); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java index ddd6614ab..84c0cd395 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/AboutActivity.java @@ -290,7 +290,7 @@ private void showQrCode(View dialog, String instId) { return; } int timeout_ms = Integer.parseInt(timeout_s) * 1000; - long deadline = SystemClock.uptimeMillis() + timeout_ms; + long deadline = SystemClock.elapsedRealtime() + timeout_ms; Log.d(TAG, "QR request_id=" + qr_req_id + ", timeout=" + timeout_ms + " ms"); // Step 2: generate QR code @@ -357,7 +357,7 @@ private void onQrRequestReady(View dialog, Bitmap qrcode, long deadline) { View qrLoading = dialog.findViewById(R.id.qr_code_loading); View qrInfo = dialog.findViewById(R.id.qr_info_text); - mQrStartTime = SystemClock.uptimeMillis(); + mQrStartTime = SystemClock.elapsedRealtime(); mQrDeadline = deadline; updateQrProgress(dialog); @@ -373,7 +373,7 @@ private void updateQrProgress(View dialog) { return; long interval = mQrDeadline - mQrStartTime; - int progress = Math.min((int)((SystemClock.uptimeMillis() - mQrStartTime) * 100 / interval), 100); + int progress = Math.min((int)((SystemClock.elapsedRealtime() - mQrStartTime) * 100 / interval), 100); qrProgress.setProgress(100 - progress); mHandler.postDelayed(() -> updateQrProgress(dialog), 1000); diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/Blocklist.java b/app/src/main/java/com/emanuelef/remote_capture/model/Blocklist.java index 2bc7bb00b..bea29752a 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/Blocklist.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/Blocklist.java @@ -22,13 +22,13 @@ public Blocklist(Context ctx) { } public synchronized boolean unblockAppForMinutes(int uid, int minutes) { - Long old_val = mUidToGrace.put(uid, SystemClock.uptimeMillis() + (minutes * 60_000L)); + Long old_val = mUidToGrace.put(uid, SystemClock.elapsedRealtime() + (minutes * 60_000L)); Log.i(TAG, "Grace app: " + uid + " for " + minutes + " minutes (old: " + old_val + ")"); return (old_val == null); } public synchronized boolean checkGracePeriods() { - long now = SystemClock.uptimeMillis(); + long now = SystemClock.elapsedRealtime(); boolean changed = false; Iterator> iter = mUidToGrace.entrySet().iterator(); From 5f11d022cdeab62f1176ede6f17cb9d464059a2d Mon Sep 17 00:00:00 2001 From: topminipie <145812405+topminipie@users.noreply.github.com> Date: Thu, 8 Feb 2024 08:59:23 +0000 Subject: [PATCH 39/45] Dependabot (#403) * Create dependabot.yml * Update dependabot.yml Add Gradle package ecosystem --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..00dac92fb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: gradle + directory: "/" + schedule: + interval: daily + target-branch: "master" + open-pull-requests-limit: 0 + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + target-branch: "master" + open-pull-requests-limit: 0 From 60cb5f26cf5becf2bb190fbea55dd3d62318b9bb Mon Sep 17 00:00:00 2001 From: Emanuele Faranda Date: Fri, 2 Feb 2024 11:21:08 +0000 Subject: [PATCH 40/45] Update Italian strings --- app/src/main/res/values-it/strings.xml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 2dbd87ddc..9b1766541 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -210,7 +210,7 @@ Avvia la cattura in automatico all\'avvio del dispositivo Whitelist definita dall\'utente per inibire il rilevamento di malware. Tieni premuto su una connessione malevola per creare una regola Redirigi tutte le connessioni TCP verso un proxy SOCKS5 - Quando la decrittazione TLS è abilitata, è necessario selezionare un filtro app o usare la whitelist di decrittazione per evitare di perdere la connessione a Internet + Quando la decrittazione TLS è abilitata, è necessario selezionare un filtro app per evitare di perdere la connessione a Internet Salvataggio del traffico Attiva Telefono @@ -481,4 +481,20 @@ La connessione non sarà decrittata. Per decrittarla, crea una regola di decrittazione La decrittazione TLS è applicata soltanto alle connessioni che matchano le regole configurate. Vuoi creare ora delle regole di decrittazione\? Avvio della cattura fallito. Assicurati di concedere l\'accesso root a PCAPdroid - + Esporta… + redirezionata + Sempre + Filtri app + Novità + L\'ottimizzazione della batteria potrebbe interferire con l\'addon mitm + Mai + Solo per connessioni da decrittare + La decrittazione di QUIC non è attualmente supportata. Come soluzione temporanea, ferma la cattura e seleziona l\'opzione di blocco QUIC dalle opzioni PCAPdroid + Seleziona le applicazione da catturare + Header + Questa connessione è stata redirezionata per via di una regola di port mapping + Come usare DoH / DNSCrypt con PCAPdroid + Corpo + Entrambi + Byte grezzi + \ No newline at end of file From 7f65fae28007e31eb7f54ac6083e80ac06b204dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Fri, 2 Feb 2024 12:38:37 +0000 Subject: [PATCH 41/45] Update Chinese (Simplified) strings --- app/src/main/res/values-zh-rCN/strings.xml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 6be9ac130..d19f1e517 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -236,7 +236,7 @@ 允许 PCAPdroid 与其他 VPN 应用程序一起运行 用户自定义白名单,禁止恶意软件检测。长按“恶意连接”创建取消将它们标记为恶意软件的规则 隐藏连接列表条目的规则列表,以便关注相关的规则 - 解密 TLS 时,请选择目标应用程序或使用解密白名单,以免失去 Internet 连接 + 解密 TLS 时,请选择目标应用,以免失去 Internet 连接 构建于:%1$s 流量转储 活跃 @@ -507,4 +507,20 @@ 此连接不会被解密。创建解密规则来解密它 检测到活动 VPN 流量捕获启动失败。请确保你授予了 PCAPdroid 根权限 + 更新记录 + 电池优化可能干扰 mitim 附加组件的运作 + 已重定向 + 由于端口映射规则,此连接已被重定向 + 如何在 PCAPDroid 中使用 DoH / DNSCrypt + 从不 + 始终 + 只对要解密的连接使用 + 选择要捕获流量的应用 + 当前不支持解密 QUIC 连接。作为临时解决方案,停止流量捕获并在 PCAPdroid 设置中选择拦截 QUIC 连接的选项 + 目标应用 + 标头 + 正文 + 两者 + 导出… + 原始字节 \ No newline at end of file From 357b5c30f4a3db4d1f743ff342112f1d2cb46906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Fri, 2 Feb 2024 16:46:04 +0000 Subject: [PATCH 42/45] Update Turkish strings --- app/src/main/res/values-tr/strings.xml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 95046b54e..87ca24ac3 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -175,7 +175,7 @@ Listeyi düzenle Güncel Gizli DNS algılamayı engelliyor - İnternet bağlantınızı kaybetmemek için TLS\'nin şifresini çözerken bir hedef uygulama seçin veya şifre çözme beyaz listesini kullanın + İnternet bağlantınızı kaybetmemek için TLS\'nin şifresini çözerken bir hedef uygulama seçin Ücretli özellikler Arayüz: %1$s Satın alındı @@ -482,4 +482,20 @@ Geçersiz PCAP dosyası Vekil sunucu Yakalama arayüzü açılamadı + PCAPdroid ile DoH / DNSCrypt nasıl kullanılır + Hiçbir zaman + Her zaman + Yalnızca şifresi çözülecek bağlantılar için + Yenilikler + Dışa aktar… + Pil iyileştirmesi ortadaki adam eklentisinin çalışmasını engelleyebilir + yönlendirildi + Bu bağlantı bir bağlantı noktası eşleme kuralı nedeniyle yönlendirildi + Her ikisi de + QUIC şifresinin çözülmesi şu anda desteklenmiyor. Geçici bir çözüm olarak, yakalamayı durdurun ve PCAPdroid ayarlarında QUIC engelleme seçeneğini seçin + Hedef uygulamalar + Yakalanacak uygulamaları seçin + Başlıklar + Gövde + Ham baytlar \ No newline at end of file From 8b6621f4a83e682990266200797545f759bf1bfb Mon Sep 17 00:00:00 2001 From: RyoidenshiAokigahara Date: Mon, 5 Feb 2024 13:09:45 +0000 Subject: [PATCH 43/45] Update Russian strings --- app/src/main/res/values-ru/strings.xml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8cc805f7d..681bf53cc 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -210,7 +210,7 @@ Список правил для скрытия элементов из списка соединений, чтобы сфокусироваться только на нужном Перенаправляет все TCP соединения на SOCKS5 прокси Определенный пользователем белый список для подавления обнаружения вредоносов. Удерживайте вредоносное соединение, чтобы создать правило согласно которому данное соединение не будет считаться вредоносным - При расшифровке TLS выберите целевое приложение или используйте белый список дешифрования, чтобы избежать потери соединения с интернетом + При расшифровке TLS выберите целевое приложение, чтобы избежать потери соединения с интернетом Дамп траффика Телефон Службы телефонии @@ -479,4 +479,22 @@ Загрузка PCAP файла прервана Не удалось начать захват. Убедитесь, что устройство рутовано с помощью Magisk Чтобы отображать фактические приложения вместо \"%1$s\", убедитесь, что вы включили опцию \"%2$s\" перед экспортом PCAP файла + Live-захват + Никогда + Целевые приложения + Выберите приложения для захвата + Это соединение было перенаправлено по правилу сопоставления портов + Только для дешифруемых соединений + Что нового + Обе части + перенаправлено + Как использовать DoH / DNSCrypt с PCAPdroid + Всегда + Raw байты + Оптимизации батареи могут помешать работе mitm дополнения + Дешифрование QUIC в данный момент не поддерживается. В качестве временной меры, остановите захват и в настройках PCAPdroid включите опцию блокировки QUIC + Заголовки + Тело + PCAP файл содержит неподдерживаемый datalink + Экспорт… \ No newline at end of file From 366eea4f5a96f7444c1c18fef222a832198ba9ee Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Wed, 7 Feb 2024 10:22:18 +0000 Subject: [PATCH 44/45] Update Spanish strings --- app/src/main/res/values-es/strings.xml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 581fff4b1..4dd808f6f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -191,7 +191,7 @@ Ahora puedes utilizar la función comprada Guardado Cargando apps… - Seleccione una aplicación de destino o utilice la lista blanca de descifrado al descifrar TLS para evitar perder la conexión a Internet + Seleccione una aplicación de destino al descifrar TLS para evitar perder la conexión a Internet Archivo guardado como \"%1$s\" Cargando… La captura no se está ejecutando @@ -482,4 +482,20 @@ Se ha detectado una VPN activa Carga del fichero PCAP en curso, por favor espera Fallo al realizar la captura. Asegúrate de conceder acceso root a PCAPdroid + La optimización de la batería puede interferir con el addon mitm + redirigido + Esta conexión ha sido redirigida debido a una regla de asignación de puertos + ¿Cómo utilizar DoH / DNSCrypt con PCAPdroid? + Nunca + Siempre + Solo para conexiones a descifrar + Aplicaciones de destino + Seleccione las aplicaciones que desea capturar + Encabezados + Cuerpo + Ambos + Novedades + Bytes sin formato + Actualmente no es posible descifrar QUIC. Como solución, detenga la captura y seleccione la opción de bloquear QUIC en los ajustes de PCAPdroid + Exportar… \ No newline at end of file From fd2148facc825c40035cf8fd20e6473af61249b9 Mon Sep 17 00:00:00 2001 From: jonnysemon Date: Wed, 7 Feb 2024 12:26:27 +0000 Subject: [PATCH 45/45] Update Arabic strings --- app/src/main/res/values-ar/strings.xml | 42 +++++++++++++------ .../metadata/android/ar/full_description.txt | 20 +++++++++ .../metadata/android/ar/short_description.txt | 1 + 3 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 fastlane/metadata/android/ar/full_description.txt create mode 100644 fastlane/metadata/android/ar/short_description.txt diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 06dd6af1c..77f0100ac 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -16,7 +16,7 @@ استعلام استضافة مستعد - حالة + الحالة حول الاتصالات لا اتصالات @@ -52,7 +52,7 @@ التطبيقات ملف PCAP استقبلت %1$s - ارسلت %2$s - %1$s ملتقط و %2$s من الاتصالات + %1$s ملتقط و %2$s اتصالات أحصل عليه: قم بإنشاء ملف PCAP في مساحة تخزين الجهاز لا يمكن كتابة الملف @@ -75,7 +75,7 @@ قم بالمصادقة على الوكيل عبر اسم المستخدم وكلمة المرور منفذ الوكيل اختيار الكل - هدف SDK + SDK المستهدفة مثبت على آخر تحديث الأذونات @@ -211,7 +211,7 @@ تعرف المزيد حول ميزة الكشف عن البرامج الضارة يمنع DNS الخاص PCAPdroid من فحص حركة مرور DNS. يمكنك إيقاف تشغيله من إعدادات شبكة Android DNS الخاص يعيق الكشف - حدد تطبيقًا مستهدفًا أو استخدم القائمة البيضاء لفك التشفير عند فك تشفير TLS لتجنب فقدان اتصالك بالإنترنت + حدد تطبيقًا مستهدفًا عند فك تشفير TLS لتجنب فقدان اتصالك بالإنترنت حظر… محظور التحقق من صحة @@ -364,7 +364,7 @@ البلد و ASN رجوع تم - PCAPdroid هو تطبيق صديق للخصوصية يتيح لك تتبع وتحليل الاتصالات التي تجريها التطبيقات الموجودة على جهازك + تطبيق PCAPdroid صديق للخصوصية يتيح لك تتبع وتحليل الاتصالات التي تجريها التطبيقات الموجودة على جهازك \n \nعلاوة على ذلك، فهو يسمح لك بتصدير تفريغ PCAP لحركة المرور واستخراج البيانات الوصفية وغير ذلك الكثير! الخصوصية أولاً @@ -394,12 +394,12 @@ تم منح الإذن %1$s تعذر منح الإذن %1$s تعذر العثور على الاتصال المحدد - نفدت ذاكرة التطبيق، توقع حدوث أعطال + نفذت ذاكرة التطبيق، توقع حدوث أعطال تم تعطيل خيار الحمولة الكاملة لمنع التطبيقات من الوصول إلى الإنترنت عندما لا يكون PCAPdroid قيد التشغيل (على سبيل المثال، بعد إعادة التشغيل)، يمكنك تعيين PCAPdroid كشبكة VPN تعمل دائمًا في وضع التأمين. \n \nهل تريد فتح إعدادات VPN الآن؟ - تم حظر التطبيق + حُظر التطبيق IPv4 فقط لقد قمت بتكوين PCAPdroid لإرسال حركة المرور إلى خادم بعيد. سيكون الخادم قادرًا على الوصول إلى معلوماتك الحساسة. \n @@ -413,8 +413,8 @@ عرض البيانات قم بإلغاء الحظر عن %1$dm حظر التطبيقات المثبتة حديثًا - تم إلغاء حظر التطبيق %1$s - تم حظر تطبيق %1$s بواسطة جدار الحماية + أُلغي حظر التطبيق %1$s + حُظر تطبيق %1$s بواسطة جدار الحماية قد يحتوي هذا العرض على معلومات حساسة، مثل كلمات المرور ورموز الوصول المميزة. \n \nلا تقم أبدًا بإعطاء هذه المعلومات لأي شخص، فقد تكون محاولة احتيال @@ -424,7 +424,7 @@ رمز مميز يمكن استخدامه لفتح الميزات المدفوعة في إصدارات التطبيق غير التابعة لـ Google Play (مثل F-Droid/Github) خدمة ترخيص PCAPdroid غير متوفرة حاليًا. يرجى إعادة المحاولة خلال بضع دقائق هذا هو رمز إلغاء القفل الخاص بك. لاحظ ذلك لأنك ستحتاج إليه لإنشاء رموز الترخيص - قم بشراء ميزة %1$s لبدء حظر الاتصالات + اشترِ مِيزة %1$s لبدء حظر الاتصالات جارٍ طلب رمز إلغاء القفل، برجاء الانتظار يبدو أن شهادة mitm غير مثبتة. إذا قمت بالمتابعة، فقد تفشل عملية فك التشفير قم بتكوين الجهاز لفك تشفير TLS @@ -450,7 +450,7 @@ تم تعطيل جِدار الحماية تم بدء هذا الاتصال بواسطة netd لذلك لا يمكن حظره حظر DNS الخاص - ذاكرة منخفضة + الذاكرة منخفضة لأسباب أمنية، غير مسموح بإرسال حركة المرور إلى الخادم البعيد \"%1$s\" تعد القدرة على تشغيل فك تشفير TLS باستخدام الجذر ميزة تجريبية. هذه قائمة بالأخطاء المعروفة: \n @@ -460,7 +460,7 @@ \n- قد لا يعمل هذا على نظام Android 12 والإصدارات الأحدث \n- إذا كان تطبيق VPN قيد التشغيل، فيجب عليك إما استهداف تطبيق معين لفك التشفير أو استبعاد ملحق PCAPdroid mitm من VPN، وإلا ستستمر حركة المرور في حلقة راجع دليل المستخدم للتعرف على كيفية الوصول إلى الميزات المدفوعة - مَيزة مدفوعة + مِيزة مدفوعة في هذا الوضع، سيتم حظر جميع الاتصالات، ما لم يتم إدراجها يدويًا في القائمة البيضاء. قد تفوتك إشعارات الدفع بدون قائمة بيضاء مناسبة ميناء الوصول تعيين المنفذ محدد بالفعل @@ -474,7 +474,7 @@ يتم تطبيق فك تشفير TLS فقط على الاتصالات التي تطابق القواعد التي تم تكوينها. هل تريد إنشاء قواعد فك التشفير الآن؟ تم اكتشاف شبكة VPN نشطة %1$s ،%2$d - %1$s pkts + %1$s حِزم تَفَقُّد حركة المرور TX استهلاك الكومة @@ -482,4 +482,20 @@ نص غير مشفر RX بدء لالتقاط فشل . تأكد من منح حق الوصول إلى الجذر (root) لPCAPdroid + حُوّلت + كيفية استخدام DoH / DNSCrypt مع PCAPdroid + دائمًا + حدّد التطبيقات المراد التقاطها + المحتوى + البايتات الخام + قد يتداخل تحسين البطارية مع الملحق mitm + حُوّل توجيه هذا الاتصال بسبب قاعدة تعيين المنفذ + أبداً + فقط للاتصالات لفك التشفير + فك تشفير QUIC غير مدعوم حاليًا. كحل بديل، أوقِف الالتقاط وحدّد خيار حظر QUIC في إعدادات PCAPdroid + استهدف التطبيقات + الترويسات + كلاهما + ما الجديد + صدر… \ No newline at end of file diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt new file mode 100644 index 000000000..88643e37c --- /dev/null +++ b/fastlane/metadata/android/ar/full_description.txt @@ -0,0 +1,20 @@ +PCAPdroid هو تطبيق صديق للخصوصية يتيح لك تتبع وتحليل الاتصالات التي تجريها التطبيقات الأخرى في جهازك. +كما يسمح لك بتصدير تفريغ PCAP لحركة المرور، وفحص HTTP، وفك تشفير حركة مرور TLS، وغير ذلك الكثير. + +يحاكي PCAPdroid شبكة VPN لالتقاط حركة مرور الشبكة دون الحاجة إلى الجذر. ولا يستخدم خادم VPN عن بعد، وبدلاً من ذلك تتم معالجة البيانات محليًا على الجهاز. + +الميزات: + +* تسجيل وفحص الاتصالات التي أجراها المستخدم وتطبيقات النظام +* استخرج SNI واستعلام DNS وعنوان URL HTTP وعنوان IP البعيد +* فحص طلبات HTTP والردود بفضل وحدات فك التشفير المضمنة +* فحص حمولة الاتصالات الكاملة كـ hexdump/text +* فك تشفير حركة مرور HTTPS/TLS وتصدير ملف SSLKEYLOGFILE +* تفريغ حركة المرور إلى ملف PCAP، أو تنزيله من متصفح، أو بثه إلى جهاز استقبال بعيد لتحليله في الوقت الفعلي (مثل Wireshark) +* إنشاء قواعد لتصفية حركة المرور الجيدة واكتشاف الحالات الشاذة بسهولة +* تحديد البلد ورقم ASN للخادم البعيد عبر عمليات البحث في قاعدة البيانات دون اتصال بالإنترنت +* على الأجهزة ذات الجذور، يمكنك التقاط حركة المرور أثناء تشغيل تطبيقات VPN الأخرى + +إذا كنت تخطط لاستخدام PCAPdroid لإجراء تحليل الحزم، فيُرجى مراجعة القسم المحدّد من الدليل. + +انضم إلى مجتمع PCAPdroid الدولي على تيليجرام أو على ماتركس. diff --git a/fastlane/metadata/android/ar/short_description.txt b/fastlane/metadata/android/ar/short_description.txt new file mode 100644 index 000000000..628531376 --- /dev/null +++ b/fastlane/metadata/android/ar/short_description.txt @@ -0,0 +1 @@ +أداة مراقبة الشبكة بدون جذر (root) وتفريغ حركة المرور لأجهزة أندرويد