From 3678715929c3c39c7324e0ee65cde57070a9cd3a Mon Sep 17 00:00:00 2001 From: Loukious Date: Mon, 7 Sep 2026 22:40:48 +0100 Subject: [PATCH] Add optional toggle to allow deauth on the internal Wi-Fi radio The app hard-assumed the internal radio (wlan0/swlan0) can never do deauthentication, so attacks either silently swapped the deauth interface to the scan adapter or skipped deauth entirely. That is wrong for devices whose internal radio does support monitor mode and injection (e.g. Qualcomm qcacld chipsets). Gate the old behavior behind a new Settings switch instead of removing it, so devices whose internal radio genuinely cannot inject are unaffected: - Off (default): exactly the previous behavior - internal-radio deauth interfaces are swapped to the scan adapter or deauth is skipped - On: the internal radio is allowed as a deauth interface in the handshake attack (type 3) and the standalone deauth dialog (type 7) Monitor-mode handling is untouched: enabling/disabling still uses the existing per-adapter custom commands, so airmon-ng-based setups keep working via Settings -> Custom monitor commands. --- .../settings/SettingsHomeFragment.java | 11 ++++ .../java/com/zalexdev/stryker/utils/Core.java | 4 ++ .../zalexdev/stryker/wifi/WiFIAdapter.java | 11 ++-- app/src/main/res/layout/settings_main.xml | 62 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 + 5 files changed, 86 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/zalexdev/stryker/settings/SettingsHomeFragment.java b/app/src/main/java/com/zalexdev/stryker/settings/SettingsHomeFragment.java index 8ba7386..d4dc570 100644 --- a/app/src/main/java/com/zalexdev/stryker/settings/SettingsHomeFragment.java +++ b/app/src/main/java/com/zalexdev/stryker/settings/SettingsHomeFragment.java @@ -61,6 +61,9 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { SwitchMaterial autoBanner = view.findViewById(R.id.banner_detect); SwitchMaterial pixieIfaceDown = view.findViewById(R.id.pixie_iface_down_switch); LinearLayout pixieIfaceDownRow = view.findViewById(R.id.pixie_iface_down_row); + LinearLayout internalDeauthRow = view.findViewById(R.id.internal_deauth_row); + View internalDeauthDivider = view.findViewById(R.id.internal_deauth_divider); + SwitchMaterial internalDeauth = view.findViewById(R.id.internal_deauth_switch); LinearLayout autoWifiRow = view.findViewById(R.id.autowifi_row); LinearLayout saveApsRow = view.findViewById(R.id.save_aps_row); LinearLayout autoScanRow = view.findViewById(R.id.autoscan_row); @@ -81,6 +84,7 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { saveAps.setChecked(core.isStoreEnabled()); autoBanner.setChecked(core.isBannerScanEnabled()); pixieIfaceDown.setChecked(core.isPixieIfaceDown()); + internalDeauth.setChecked(core.isInternalDeauthEnabled()); hide.setChecked(core.getBoolean("hide")); autoWifi.setChecked(core.getBoolean("wifi")); autoScan.setChecked(core.getBoolean("autoScan")); @@ -89,6 +93,7 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { saveAps.setOnCheckedChangeListener((btn, b) -> core.putBoolean("save_aps", b)); autoBanner.setOnCheckedChangeListener((btn, b) -> core.putBoolean("autoBanner", b)); pixieIfaceDown.setOnCheckedChangeListener((btn, b) -> core.putBoolean("pixie_iface_down", b)); + internalDeauth.setOnCheckedChangeListener((btn, b) -> core.putBoolean("internal_deauth", b)); hide.setOnCheckedChangeListener((btn, b) -> core.putBoolean("hide", b)); autoWifi.setOnCheckedChangeListener((btn, b) -> core.putBoolean("wifi", b)); autoScan.setOnCheckedChangeListener((btn, b) -> core.putBoolean("autoScan", b)); @@ -96,6 +101,12 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { bindRowToSwitch(autoWifiRow, autoWifi); bindRowToSwitch(saveApsRow, saveAps); bindRowToSwitch(pixieIfaceDownRow, pixieIfaceDown); + bindRowToSwitch(internalDeauthRow, internalDeauth); + if (core.isRootless()) { + // The internal radio doesn't exist in the rootless VM — the toggle has no effect there + internalDeauthRow.setVisibility(View.GONE); + internalDeauthDivider.setVisibility(View.GONE); + } bindRowToSwitch(autoScanRow, autoScan); bindRowToSwitch(bannerRow, autoBanner); bindRowToSwitch(hideRow, hide); diff --git a/app/src/main/java/com/zalexdev/stryker/utils/Core.java b/app/src/main/java/com/zalexdev/stryker/utils/Core.java index c335364..893576f 100644 --- a/app/src/main/java/com/zalexdev/stryker/utils/Core.java +++ b/app/src/main/java/com/zalexdev/stryker/utils/Core.java @@ -1387,6 +1387,10 @@ public boolean isPixieIfaceDown(){ return preferences.getBoolean("pixie_iface_down", true); } + public boolean isInternalDeauthEnabled(){ + return preferences.getBoolean("internal_deauth", false); + } + public String wpsIfaceDownFlag(){ return isPixieIfaceDown() ? " --iface-down" : ""; } diff --git a/app/src/main/java/com/zalexdev/stryker/wifi/WiFIAdapter.java b/app/src/main/java/com/zalexdev/stryker/wifi/WiFIAdapter.java index afbd4bb..12d74f8 100644 --- a/app/src/main/java/com/zalexdev/stryker/wifi/WiFIAdapter.java +++ b/app/src/main/java/com/zalexdev/stryker/wifi/WiFIAdapter.java @@ -678,10 +678,11 @@ public void doOnBackground() { String wlanscan = core.getHSInterface(); String deauthPref = core.getDeauthInterface(); ArrayList clients = new ArrayList<>(); - if (!core.isRootless() && MonitorManager.isInternalRadio(deauthPref)){ + if (!core.isRootless() && !core.isInternalDeauthEnabled() + && MonitorManager.isInternalRadio(deauthPref)){ if (!MonitorManager.isInternalRadio(wlanscan)) { deauthPref = wlanscan; - sendEvent("Deauth interface is the internal radio — using " + wlanscan + " instead"); + sendEvent("Deauth interface is the internal radio — using " + wlanscan + " instead (enable 'Deauth with internal adapter' in Settings to use it)"); } else { deauth = false; } @@ -787,6 +788,7 @@ public void onEvent(String line) { ? wlandeauth + "mon" : wlandeauth; final String hsIface = capIface; final boolean internalDeauth = !core.isRootless() + && !core.isInternalDeauthEnabled() && MonitorManager.isInternalRadio(deauthIface); final String[] lastRelock = {""}; if (deauth) { @@ -1295,10 +1297,11 @@ else if (type == 7){ if (dialogCanceled.get()) return; String deauthIface = core.getDeauthInterface(); boolean ok = false; - if (core.isRootless() || !MonitorManager.isInternalRadio(deauthIface)){ + if (core.isRootless() || !MonitorManager.isInternalRadio(deauthIface) + || core.isInternalDeauthEnabled()){ ok = core.enableMonitorMode(deauthIface, String.valueOf(network.getChannel())); }else{ - activity.runOnUiThread(() -> outputtext.append("Internal wifi adapter (wlan0) does not support packet injection! Please use external wifi adapter!\n")); + activity.runOnUiThread(() -> outputtext.append("Internal wifi adapter (wlan0) deauth is disabled! Enable 'Deauth with internal adapter' in Settings, or use an external wifi adapter!\n")); } if (dialogCanceled.get()) return; final String monIface = core.getDeauthInterface(); diff --git a/app/src/main/res/layout/settings_main.xml b/app/src/main/res/layout/settings_main.xml index 192c003..7b8aff5 100644 --- a/app/src/main/res/layout/settings_main.xml +++ b/app/src/main/res/layout/settings_main.xml @@ -332,6 +332,68 @@ android:layout_marginStart="72dp" android:background="@color/light_lite_contrast" /> + + + + + + + + + + + + + + + + Pick which adapter handles scan, handshake, deauth, WPS Turn interface down Disable Wi‑Fi and drop the adapter during Pixie/WPS attacks. Needed for most internal adapters; turn off for external adapters that must stay up + Deauth with internal adapter + Allow deauthentication on the internal Wi‑Fi radio (wlan0). Enable only if your device supports packet injection on the internal adapter; otherwise an external adapter is used Custom monitor commands Override how monitor mode is enabled per adapter