Build a System-Wide Traffic Anonymizer

Build a portable exe that routes all Windows traffic through Tor with 16 anonymity layers.

Introduction

30 Second Summary

Every time you connect to the internet, your device broadcasts dozens of signals that identify you. Your IP address, your hostname, your MAC address, your DNS queries, your microphone, your Bluetooth radio, and even the metadata in your files all leave traces that can be tracked, correlated, and stored.

In this project, you will build a single portable anonshield.exe that activates 16 layers of anonymity on any Windows PC with zero installation. Double-click to go dark. Press q to come back with zero forensic traces left behind.

What You'll Build

A single portable executable that bundles Tor with obfs4 bridge relays, a TUN virtual network adapter, a firewall kill switch, hardware lockdowns, a metadata scrubber, and a forensic cleanup engine into one double-clickable file compiled with PyInstaller.

By the end of this project, you'll have:

  • A system-wide Tor tunnel using tun2socks and Wintun that captures all TCP traffic, with obfs4 bridges hiding Tor usage from your ISP and stream isolation preventing cross-activity correlation.
  • 16 anonymity layers covering network routing, DNS leak prevention, IPv6 kill, Bluetooth kill, MAC randomization, microphone isolation, broadcast silencing, hostname randomization, telemetry suppression, and a firewall kill switch.
  • Automatic IP rotation every 15 minutes, a built-in metadata scrubber for images and documents, and forensic cleanup that wipes DNS cache, Prefetch files, Recent Files, temp files, and network Event Logs on exit.
  • A country selector for choosing which country your Tor exit node is in (Secret Mission).

Are there any prerequisites?

You need basic command line familiarity and basic Python knowledge. This project walks you through setting up every tool from scratch, including Python itself.

Set Up the Build Environment

Before you can build a 16-layer anonymity tool, you need the right pieces on your machine. This step gets Python installed with all the libraries your project depends on, downloads the external binaries that handle traffic tunneling and onion routing, and collects the obfs4 bridge addresses that will hide your Tor usage from your ISP.

In This Step, Get Ready To:
  • Install Python and all required libraries including PyInstaller, stem, and Pillow.
  • Download the Tor Expert Bundle, tun2socks, and the Wintun driver.
  • Obtain obfs4 bridge addresses and verify your project folder structure.
Install Python and PyInstaller

Python is the language your orchestration script is written in. PyInstaller is the tool that will eventually compile everything into a single portable .exe file. You also need several libraries for controlling Tor, handling SOCKS proxies, making network requests, and stripping image metadata.

  • Open a terminal (PowerShell or Command Prompt) and check if Python is already installed.
python --version

I see a version number

You should see something like Python 3.12.x or higher. You are good to continue.

Command not found

  • Download Python 3.12+ from python.org.
  • Run the installer and check the box labeled Add Python to PATH before clicking Install Now.
  • Close and reopen your terminal, then run python --version again to confirm.

Still seeing 'command not found'?

The most common cause is that the PATH checkbox was not checked during installation. Uninstall Python, reinstall with the checkbox enabled, and restart your terminal. Help me troubleshoot my Python installation.

Now install all the Python libraries your project needs.

  • Run the following command in your terminal.
pip install stem==1.8.3 PySocks==1.7.1 requests[socks] Pillow==12.2.0 pyinstaller==6.19.0

What does this command do?

This installs five packages that your tool depends on:

  • stem (1.8.3) controls the Tor daemon programmatically, letting you send signals like IP rotation.
  • PySocks (1.7.1) adds SOCKS proxy support so Python can route traffic through Tor.
  • requests[socks] extends the requests library with SOCKS proxy capabilities for verification checks.
  • Pillow (12.2.0) handles image processing for the metadata scrubber, stripping EXIF data like GPS coordinates and camera info.
  • pyinstaller (6.19.0) compiles everything into a single portable .exe file.

Having trouble installing packages?

If pip is not recognized, try python -m pip install instead. If you get permission errors, try running your terminal as Administrator. Help me fix my pip installation issue.

Download the Tor Expert Bundle and tun2socks

Your tool needs two external binaries that are not Python packages. The Tor Expert Bundle contains the Tor daemon itself, the lyrebird pluggable transport for obfs4 bridge connections, and GeoIP data. tun2socks is the binary that creates a virtual network adapter and routes all system traffic through your Tor SOCKS proxy.

  • Create a project folder at C:\anonshield\. This is where all your project files will live.
mkdir C:\anonshield
cd C:\anonshield
  • Download the Tor Expert Bundle for Windows x86_64 from the Tor Project download page.
  • Extract the contents into your project folder so you have a tor\ subfolder inside C:\anonshield\.

What is inside the Tor Expert Bundle?

The bundle includes three key components. tor.exe is the Tor daemon that handles onion routing. lyrebird.exe lives in the pluggable_transports\ folder and implements the obfs4 protocol that disguises Tor traffic as random noise. The data\tor\geoip files map IP addresses to countries for exit node selection.

  • Download tun2socks v2.6.0 for Windows from the tun2socks releases page. Look for the file named tun2socks-windows-amd64.zip.
  • Extract tun2socks-windows-amd64.exe and place it directly in C:\anonshield\.

Why tun2socks instead of other approaches?

You might expect to use a tool like Tallow for system-wide Tor routing on Windows. tun2socks is a better fit because it is a standalone binary you can orchestrate from Python via subprocess. It uses the Microsoft-signed Wintun driver, has well-documented Windows setup, and is actively maintained. Tallow requires C/MinGW compilation and its last release was in 2021.

Download the Wintun Driver and Obtain obfs4 Bridges

tun2socks needs a TUN driver to create a virtual network adapter on Windows. Wintun is that driver. It is a lightweight, Microsoft-signed kernel driver that creates virtual network interfaces. You also need obfs4 bridge addresses. These are special Tor entry points that disguise your connection so your ISP cannot tell you are using Tor at all.

  • Download Wintun 0.14.1 from wintun.net.
  • Extract the zip and navigate to the wintun\bin\amd64\ folder.
  • Copy wintun.dll into C:\anonshield\ alongside tun2socks-windows-amd64.exe.

Now you need to obtain obfs4 bridge addresses. These are unlisted Tor entry relays that use the obfs4 protocol to make your traffic look like random noise to your ISP.

  • Open the Tor Bridge distribution page in your browser.
  • Select obfs4 as the transport type.
  • Complete the CAPTCHA and copy the bridge lines you receive. You need at least 2-3 bridge lines for redundancy.
  • Save these bridge lines somewhere safe. You will paste them into your torrc configuration file in the next step.

What does an obfs4 bridge line look like?

Each bridge line contains an IP address, port, fingerprint, and a certificate. It looks something like this:

obfs4 192.0.2.1:443 ABCDEF1234567890ABCDEF1234567890ABCDEF12 cert=ABCDEFabcdef1234567890... iat-mode=0

Your actual bridge lines will have real IP addresses and certificates. Keep them private. Sharing bridge addresses publicly defeats their purpose.

Now verify that your project folder has the correct structure.

  • Open your C:\anonshield\ folder and confirm you see the following structure.
C:\anonshield\
├── tor\
│   ├── tor.exe
│   ├── pluggable_transports\
│   │   └── lyrebird.exe
│   └── data\
│       └── tor\
│           └── geoip
├── tun2socks-windows-amd64.exe
└── wintun.dll

What should I see?

The exact subfolder names inside tor\ may vary slightly depending on the version of the Tor Expert Bundle you downloaded. The important thing is that you can locate tor.exe, lyrebird.exe, and the geoip file within the extracted folder. Note the exact paths because you will reference them in your code.

Your build environment is ready. You have Python with all the libraries, the Tor daemon with its obfs4 pluggable transport, the tun2socks binary, the Wintun driver, and your bridge addresses. Next up, you will write the hardened Tor configuration and launch the traffic tunnel that routes your entire system through Tor.

Launch Tor with obfs4 Bridges and Set Up the Traffic Tunnel

Your build environment is ready with all the binaries and bridge addresses you need. Now it is time to bring the first five anonymity layers online.

In this step you will launch the Tor daemon through obfs4 bridges so your ISP sees only random noise, spin up a virtual network adapter that captures every packet leaving your machine, and reroute the entire Windows routing table through that adapter. By the end, every TCP connection on your system will silently travel through the Tor network.

In This Step, Get Ready To:
  • Write a hardened torrc and launch Tor with obfs4 bridges, stream isolation, and zero disk logging.
  • Launch tun2socks with Wintun, assign the virtual adapter an IP, and reroute all system traffic through the tunnel.
  • Verify that your real IP is hidden, DNS goes through Tor, and bridges are in use.
Create the Hardened Torrc and Launch Tor

Before Tor can connect through bridges, it needs a configuration file that tells it exactly how to behave. This file is called a torrc. Your torrc will enforce three critical security properties: bridge-only connections via obfs4, stream isolation so different apps get different Tor circuits, and zero log files written to disk.

First, generate a hashed control password. Tor uses this to authenticate connections to its control port.

  • Open a terminal and navigate to your tor subfolder.
  • Run the following command, replacing the password with something you will remember.
tor.exe --hash-password your-chosen-password

What does this command do?

This generates a salted hash of your password. You will paste the output into your torrc so that only your script can send control signals (like IP rotation) to the running Tor process. The hash looks something like 16:AF....

  • Copy the hash output. You will need it in the next file.
  • Create a file called torrc in your project root folder with the following content.
SocksPort 9050 IsolateSOCKSAuth
ControlPort 9051
HashedControlPassword your-hashed-password-here

UseBridges 1
ClientTransportPlugin obfs4 exec tor\pluggable_transports\lyrebird.exe

Bridge obfs4 your-first-bridge-line-here
Bridge obfs4 your-second-bridge-line-here
Bridge obfs4 your-third-bridge-line-here

SafeLogging 1
Log notice stdout

What do these settings do?

  • SocksPort 9050 IsolateSOCKSAuth opens the SOCKS proxy on port 9050. The IsolateSOCKSAuth flag gives different SOCKS credentials different Tor circuits, preventing correlation between your activities.
  • UseBridges 1 tells Tor to connect only through bridge relays, never directly. Your ISP sees obfs4 noise instead of Tor traffic.
  • SafeLogging 1 scrubs all IP addresses and domain names from log output, replacing them with [scrubbed].
  • Log notice stdout sends logs to standard output only. No log files are ever written to disk.
  • Replace your-hashed-password-here with the hash you generated.
  • Replace the three Bridge obfs4 ... lines with the real bridge addresses you obtained from bridges.torproject.org in Step 1.

Now create the main Python script that will orchestrate everything. Start with the path resolution helper and the Tor launch logic.

  • Create a file called anonshield.py in your project root.
  • Add the following code.
import subprocess
import sys
import os
import time

# ---------------------
# Path resolution helper
# ---------------------
# When running as a PyInstaller --onefile exe, bundled files
# are extracted to a temp directory stored in sys._MEIPASS.
# When running as a plain .py script, files are next to the script.
def resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)

# ---------------------
# Tor launcher
# ---------------------
def launch_tor():
    tor_exe = resource_path(os.path.join('tor', 'tor.exe'))
    torrc_path = resource_path('torrc')

    print('[Layer 1] Starting Tor with obfs4 bridges...')
    tor_process = subprocess.Popen(
        [tor_exe, '-f', torrc_path],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True
    )

    # Monitor bootstrap progress in memory
    for line in tor_process.stdout:
        if 'Bootstrapped 100%' in line:
            print('[Layer 1] Tor connected via obfs4 bridges ... ACTIVE')
            print('[Layer 2] Stream isolation (IsolateSOCKSAuth) ... ACTIVE')
            break
        elif 'Bootstrapped' in line:
            # Print progress updates
            progress = line.strip()
            print(f'  {progress}')

    return tor_process

if __name__ == '__main__':
    tor_proc = launch_tor()
    print('\nTor is running. Press Ctrl+C to stop.')
    try:
        tor_proc.wait()
    except KeyboardInterrupt:
        tor_proc.terminate()
        print('Tor stopped.')

What does this code do?

The resource_path() function is the key to making your script work both as a .py file during development and as a compiled .exe later. PyInstaller extracts bundled files to a temp directory stored in sys._MEIPASS, so this helper checks for that attribute and resolves paths accordingly.

The launch_tor() function starts tor.exe as a subprocess with its output piped to Python. It reads each line looking for the Bootstrapped 100% message, which means Tor has fully connected through your obfs4 bridges. The output is never written to a file. It stays in memory only.

  • Test the Tor launch by running the script.
python anonshield.py

What should I see?

You should see bootstrap progress messages like Bootstrapped 10%, Bootstrapped 50%, and so on, followed by [Layer 1] Tor connected via obfs4 bridges ... ACTIVE. This can take 30-60 seconds since bridge connections are slower than direct Tor connections. If it hangs, your bridge lines may be stale. Try requesting fresh bridges.

Tor stuck at a low bootstrap percentage?

If Tor stalls below 50%, your bridges may be unreachable. Request fresh bridge lines from bridges.torproject.org and update your torrc. Also confirm the path to lyrebird.exe in the ClientTransportPlugin line matches your actual folder structure. Help me debug my Tor bootstrap.

  • Press Ctrl+C to stop Tor once you confirm it connects successfully.
Launch Tun2socks, Configure the Virtual Adapter, and Reroute Traffic

Tor is running, but right now only apps that are explicitly configured to use a SOCKS proxy will route through it. Everything else on your system still uses your regular internet connection. To capture all traffic, you need a TUN virtual network adapter that sits between your apps and the real network card.

tun2socks creates this adapter using the Wintun driver. It intercepts IP packets at Layer 3 of the network stack and forwards them through the Tor SOCKS proxy. Combined with a routing table change, this forces every TCP connection on your system through the Tor tunnel.

Add the following function to anonshield.py, right above the if __name__ == '__main__': block.

import re

def get_default_gateway():
    """Detect the current default gateway and interface name."""
    result = subprocess.run(
        ['powershell', '-Command',
         'Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Select-Object -First 1 | Format-List NextHop,InterfaceAlias'],
        capture_output=True, text=True
    )
    gateway = None
    interface = None
    for line in result.stdout.splitlines():
        if 'NextHop' in line:
            gateway = line.split(':', 1)[1].strip()
        if 'InterfaceAlias' in line:
            interface = line.split(':', 1)[1].strip()
    return gateway, interface

def launch_tunnel(original_gateway, primary_interface):
    """Start tun2socks, configure the wintun adapter, and reroute traffic."""
    tun2socks_exe = resource_path('tun2socks-windows-amd64.exe')

    print('[Layer 3] Starting tun2socks virtual adapter...')

    # The -interface flag prevents a routing loop by telling tun2socks
    # to send its own outbound packets through the real adapter, not
    # back through the tunnel it creates.
    tun2socks_proc = subprocess.Popen([
        tun2socks_exe,
        '-device', 'wintun',
        '-proxy', 'socks5://127.0.0.1:9050',
        '-interface', primary_interface
    ])

    # Give the adapter time to register with Windows
    time.sleep(3)

    # Assign an IP address and DNS to the wintun adapter
    subprocess.run([
        'netsh', 'interface', 'ip', 'set', 'address',
        'name=wintun', 'source=static',
        'addr=10.0.0.2', 'mask=255.255.255.0', 'gateway=10.0.0.1'
    ], check=True)

    # Route DNS through the tunnel (socks5h:// handles this at the
    # SOCKS level, but we also set the adapter DNS for system resolvers)
    subprocess.run([
        'netsh', 'interface', 'ip', 'set', 'dns',
        'name=wintun', 'source=static', 'addr=10.0.0.1'
    ], check=True)

    print('[Layer 3] Virtual adapter (wintun) ... ACTIVE')

    # Reroute all traffic through the wintun adapter
    # First, add a specific route for the real gateway so Tor itself
    # can still reach the internet through the physical adapter
    subprocess.run([
        'route', 'add', original_gateway,
        'mask', '255.255.255.255', original_gateway
    ])

    # Delete the old default route
    subprocess.run(['route', 'delete', '0.0.0.0', 'mask', '0.0.0.0', original_gateway])

    # Add new default route through wintun
    subprocess.run(['route', 'add', '0.0.0.0', 'mask', '0.0.0.0', '10.0.0.1', 'metric', '1'])

    print('[Layer 4] Routing table locked to wintun ... ACTIVE')

    # Flush DNS cache to prevent leaking cached lookups
    subprocess.run(['ipconfig', '/flushdns'], capture_output=True)
    print('[Layer 5] DNS leak prevention (flushed + routed through Tor) ... ACTIVE')

    return tun2socks_proc

What does this code do?

This function does three things in sequence. First, it starts tun2socks which creates a virtual network adapter called wintun. Every packet that enters this adapter gets forwarded to the Tor SOCKS proxy at 127.0.0.1:9050.

Second, it assigns an IP address (10.0.0.2) and DNS server to the adapter so Windows treats it as a real network connection.

Third, it rewrites the Windows routing table. The old default route (through your real network card) is deleted and replaced with a route through wintun. A specific route is kept for the real gateway so Tor itself can still reach the internet. The -interface flag on tun2socks prevents a routing loop by ensuring tun2socks sends its own packets through the physical adapter, not back into itself.

Why tun2socks Instead of a SOCKS Wrapper?

A SOCKS wrapper only covers apps that support proxy settings. Many Windows services, background processes, and system components ignore proxy configuration entirely. tun2socks operates at Layer 3 of the network stack, capturing IP packets before they reach any application. This means every TCP connection on the system goes through Tor, not just your browser.

Now update the if __name__ == '__main__': block to call both functions.

if __name__ == '__main__':
    # Detect current network setup before we change anything
    original_gw, primary_iface = get_default_gateway()
    print(f'Detected gateway: {original_gw} on interface: {primary_iface}')

    # Layer 1-2: Launch Tor with obfs4 bridges and stream isolation
    tor_proc = launch_tor()

    # Layer 3-5: Create tunnel, reroute traffic, flush DNS
    tun2socks_proc = launch_tunnel(original_gw, primary_iface)

    print('\nLayers 1-5 ACTIVE. Press Ctrl+C to stop.')
    try:
        tor_proc.wait()
    except KeyboardInterrupt:
        # Restore original routing before exiting
        subprocess.run(['route', 'delete', '0.0.0.0', 'mask', '0.0.0.0', '10.0.0.1'])
        subprocess.run(['route', 'add', '0.0.0.0', 'mask', '0.0.0.0', original_gw])
        tun2socks_proc.terminate()
        tor_proc.terminate()
        print('Tunnel closed. Original routing restored.')

This script requires administrator privileges

Modifying the routing table and creating virtual network adapters require admin access. Right-click your terminal and select Run as administrator before running the script. The compiled exe will request admin privileges automatically via the UAC manifest.

  • Open an administrator terminal and run the updated script.
python anonshield.py

What should I see?

After the Tor bootstrap messages, you should see Layers 3, 4, and 5 each print ACTIVE. The wintun adapter will appear in your Windows network connections. At this point, all system traffic is flowing through the Tor tunnel.

Lost internet after running the script?

If the script crashes before restoring routes, your default gateway is gone. Open an admin terminal and run route add 0.0.0.0 mask 0.0.0.0 YOUR_GATEWAY where YOUR_GATEWAY is your router's IP (usually 192.168.1.1 or similar). You can find it by running ipconfig on another machine or checking your router. Help me restore my network routing.

Verify System-Wide Tor Routing and Bridge Usage

The tunnel is up, but you need to prove three things: Tor is connected through bridges (not directly), your public IP is a Tor exit node, and DNS queries go through the tunnel instead of leaking to your ISP.

Add the following verification function to anonshield.py, above the if __name__ == '__main__': block. You will also need to add the new imports at the top of the file.

  • Add these imports to the top of anonshield.py.
import requests
from stem import Signal
from stem.control import Controller
  • Add the verification function.
def verify_tor_connection(control_password):
    """Verify Tor is running via bridges and check the exit IP."""
    print('\n--- Verification ---')

    # Check 1: Connect to Tor's control port and verify bridge usage
    try:
        with Controller.from_port(port=9051) as controller:
            controller.authenticate(password=control_password)

            # Get the entry guard info to confirm bridge connection
            entry_guards = controller.get_info('entry-guards', None)
            if entry_guards:
                print('[Check] Tor control port connected ... PASS')
            else:
                print('[Check] Tor control port connected ... PASS (no guard info available)')
    except Exception as e:
        print(f'[Check] Tor control port ... FAIL ({e})')

    # Check 2: Verify exit IP via Tor
    try:
        proxies = {
            'http': 'socks5h://127.0.0.1:9050',
            'https': 'socks5h://127.0.0.1:9050'
        }
        response = requests.get('https://api.ipify.org', proxies=proxies, timeout=15)
        exit_ip = response.text.strip()
        print(f'[Check] Tor exit IP: {exit_ip} ... PASS')
    except Exception as e:
        print(f'[Check] Exit IP check ... FAIL ({e})')

    # Check 3: Verify DNS goes through the tunnel
    # socks5h:// means DNS resolution happens at the proxy (Tor), not locally
    try:
        response = requests.get('https://check.torproject.org/api/ip', proxies=proxies, timeout=15)
        tor_check = response.json()
        if tor_check.get('IsTor', False):
            print('[Check] Tor Project confirms Tor routing ... PASS')
        else:
            print('[Check] Tor Project check ... WARNING (not detected as Tor)')
    except Exception as e:
        print(f'[Check] Tor Project check ... FAIL ({e})')

    print('--- End Verification ---\n')

What does this code do?

The verification runs three checks. First, it connects to Tor's control port using the stem library to confirm the Tor process is alive and accepting commands. Second, it makes an HTTP request through the SOCKS proxy to an IP-checking service, revealing your Tor exit IP. Third, it hits the Tor Project's own detection API to confirm your traffic is actually coming through the Tor network.

The socks5h:// prefix (note the h) is critical. It tells the requests library to send DNS lookups through the SOCKS proxy rather than resolving them locally. Without the h, your DNS queries would leak to your ISP even though the traffic itself goes through Tor.

  • Add a call to verify_tor_connection() in your if __name__ == '__main__': block, right after the tunnel launch. Pass in the plaintext password you used when generating the hash.
    # Layer 3-5: Create tunnel, reroute traffic, flush DNS
    tun2socks_proc = launch_tunnel(original_gw, primary_iface)

    # Verify everything is working
    verify_tor_connection('your-chosen-password')
  • Run the script again from an administrator terminal.
python anonshield.py

You should see all three verification checks pass, along with a Tor exit IP that is not your real IP address. This confirms that Layers 1 through 5 are fully operational.

  • Press Ctrl+C to stop the script and restore your routing.

Your first five layers are live. Tor is connected through obfs4 bridges, every app on your system routes through the tunnel, and DNS cannot leak. Next up, you will lock down your hardware by killing IPv6, Bluetooth, and microphones to close the remaining attack surfaces.

Lock Down Hardware, Radios, and Microphones

Your traffic is now flowing through Tor, but your hardware is still broadcasting signals that can identify you. IPv6 can leak your real address outside the tunnel. Your Bluetooth radio advertises your device to nearby scanners. Your WiFi adapter's MAC address is a unique hardware fingerprint that access points log. Your microphone can be activated remotely by malware. In this step, you will shut all of these down.

In This Step, Get Ready To:
  • Disable IPv6 and all transition mechanisms that could leak traffic outside the tunnel.
  • Kill the Bluetooth radio, randomize your WiFi MAC address, disable Location Services, and disable all microphones.
  • Verify every hardware lockdown passes before moving on.
Kill IPv6, Transition Mechanisms, and Bluetooth

IPv6 is a newer internet protocol that runs alongside IPv4. The problem is that your Tor tunnel only captures IPv4 traffic. If your system sends an IPv6 packet, it bypasses the tunnel entirely and exposes your real IP.

Windows also runs several transition mechanisms (Teredo, 6to4, ISATAP) that tunnel IPv6 traffic over IPv4. These can leak your identity even after you disable IPv6 itself.

  • Add the following function to anonshield.py to disable IPv6, all transition mechanisms, and the Bluetooth radio.
import subprocess
import json

def get_ipv6_state():
    """Save the current IPv6 binding state for all adapters."""
    result = subprocess.run(
        ["powershell", "-Command",
         "Get-NetAdapterBinding -ComponentID ms_tcpip6 | "
         "Select-Object Name, Enabled | ConvertTo-Json"],
        capture_output=True, text=True
    )
    try:
        return json.loads(result.stdout)
    except json.JSONDecodeError:
        return []

def get_teredo_state():
    """Save the current Teredo state."""
    result = subprocess.run(
        ["netsh", "interface", "teredo", "show", "state"],
        capture_output=True, text=True
    )
    return result.stdout

def kill_ipv6():
    """Disable IPv6 on all network adapters."""
    subprocess.run(
        ["powershell", "-Command",
         "Disable-NetAdapterBinding -Name '*' -ComponentID ms_tcpip6"],
        capture_output=True
    )
    print("  [Layer 6] IPv6 disabled on all adapters")

def kill_transition_mechanisms():
    """Disable Teredo, 6to4, and ISATAP tunneling."""
    subprocess.run(["netsh", "interface", "teredo", "set", "state", "disabled"],
                   capture_output=True)
    subprocess.run(["netsh", "interface", "6to4", "set", "state", "disabled"],
                   capture_output=True)
    subprocess.run(["netsh", "interface", "isatap", "set", "state", "disabled"],
                   capture_output=True)
    print("  [Layer 6] Teredo, 6to4, ISATAP disabled")

def kill_bluetooth():
    """Disable the Bluetooth radio via WinRT APIs."""
    ps_script = """
    [Windows.Devices.Radios.Radio, Windows.System.Devices, ContentType=WindowsRuntime] | Out-Null
    $radios = [Windows.Devices.Radios.Radio]::GetRadiosAsync().GetAwaiter().GetResult()
    foreach ($radio in $radios) {
        if ($radio.Kind -eq 'Bluetooth') {
            $radio.SetStateAsync('Off').GetAwaiter().GetResult() | Out-Null
        }
    }
    """
    subprocess.run(["powershell", "-Command", ps_script], capture_output=True)
    print("  [Layer 7] Bluetooth radio disabled")

What does this code do?

The kill_ipv6() function uses PowerShell's Disable-NetAdapterBinding to turn off IPv6 on every network adapter at once.

The transition mechanism functions use netsh to disable Teredo, 6to4, and ISATAP. These are Windows features that can sneak IPv6 packets through IPv4 connections, completely bypassing your tunnel.

The kill_bluetooth() function accesses the Windows Runtime (WinRT) radio API. It finds the Bluetooth radio and sets its state to Off. This prevents your device from being discoverable by nearby Bluetooth scanners.

Notice the get_ipv6_state() and get_teredo_state() functions at the top. These save the original state of your system before you change anything. You will use these saved states during teardown to restore everything to how it was.

  • Add a wrapper function that calls all three lockdowns and saves the original states.
# Store original states globally for teardown
original_states = {}

def activate_layers_6_7():
    """Kill IPv6, transition mechanisms, and Bluetooth. Save original states."""
    print("\n[Layers 6-7] Hardware lockdown...")

    # Save original states before making changes
    original_states['ipv6'] = get_ipv6_state()
    original_states['teredo'] = get_teredo_state()

    # Kill IPv6 and transition mechanisms
    kill_ipv6()
    kill_transition_mechanisms()

    # Kill Bluetooth
    kill_bluetooth()

    print("[Layer 6] IPv6 + transition mechanisms ... ACTIVE")
    print("[Layer 7] Bluetooth radio killed ... ACTIVE")

Why save original states?

Every change you make to the system needs to be reversible. The original_states dictionary stores what your system looked like before Anon Mode activated. When you press 'q' to deactivate, the teardown function reads these saved values and restores everything exactly as it was.

Randomize WiFi MAC, Kill Location Services, and Kill Microphones

Your WiFi adapter has a unique MAC address burned into its hardware. Every WiFi access point you connect to logs this address. Windows has a built-in feature to randomize it, but it is off by default.

Location Services can triangulate your physical position using nearby WiFi networks. Microphones can be activated remotely by malware to capture audio. All three need to go.

  • Add these functions to anonshield.py.
import winreg

def get_registry_value(key_path, value_name, hive=winreg.HKEY_LOCAL_MACHINE):
    """Read a registry value, return None if it doesn't exist."""
    try:
        key = winreg.OpenKey(hive, key_path)
        value, _ = winreg.QueryValueEx(key, value_name)
        winreg.CloseKey(key)
        return value
    except FileNotFoundError:
        return None
    except OSError:
        return None

def set_registry_value(key_path, value_name, value, value_type=winreg.REG_DWORD,
                       hive=winreg.HKEY_LOCAL_MACHINE):
    """Write a registry value, creating the key if needed."""
    key = winreg.CreateKey(hive, key_path)
    winreg.SetValueEx(key, value_name, 0, value_type, value)
    winreg.CloseKey(key)

def randomize_mac():
    """Enable WiFi MAC address randomization via registry."""
    mac_key = r"SOFTWARE\Microsoft\WlanSvc\Interfaces"
    # Save original value
    original_states['mac_randomization'] = get_registry_value(
        mac_key, "MacRandomization"
    )
    # Value 2 = randomize for all networks
    set_registry_value(mac_key, "MacRandomization", 2)
    print("  [Layer 8] WiFi MAC randomization enabled")

def kill_location():
    """Disable Windows Location Services via registry policy."""
    loc_key = r"SOFTWARE\Policies\Microsoft\Windows\LocationAndSensors"
    original_states['location'] = get_registry_value(loc_key, "DisableLocation")
    # Value 1 = location disabled
    set_registry_value(loc_key, "DisableLocation", 1)
    print("  [Layer 9] Location Services disabled")

def kill_microphones():
    """Disable all microphone devices at the hardware driver level."""
    # Save list of enabled mics before disabling
    result = subprocess.run(
        ["powershell", "-Command",
         "Get-PnpDevice -Class AudioEndpoint -FriendlyName '*Microphone*' "
         "| Where-Object { $_.Status -eq 'OK' } "
         "| Select-Object InstanceId | ConvertTo-Json"],
        capture_output=True, text=True
    )
    try:
        original_states['mics'] = json.loads(result.stdout)
    except json.JSONDecodeError:
        original_states['mics'] = []

    # Disable all microphone devices
    subprocess.run(
        ["powershell", "-Command",
         "Get-PnpDevice -Class AudioEndpoint -FriendlyName '*Microphone*' "
         "| Disable-PnpDevice -Confirm:$false"],
        capture_output=True
    )
    print("  [Layer 10] All microphones disabled")

What does this code do?

The randomize_mac() function sets MacRandomization=2 in the Windows registry. This tells Windows to use a random MAC address for all WiFi networks instead of your real hardware address.

The kill_location() function sets DisableLocation=1 as a registry policy. This prevents any application from querying your physical location.

The kill_microphones() function uses PowerShell's Disable-PnpDevice to disable every microphone at the hardware driver level. The devices are fully deactivated and invisible to applications until you re-enable them during teardown.

  • Add the wrapper function for Layers 8, 9, and 10.
def activate_layers_8_9_10():
    """Randomize MAC, kill Location Services, kill microphones."""
    print("\n[Layers 8-10] Device fingerprint hardening...")

    randomize_mac()
    kill_location()
    kill_microphones()

    print("[Layer 8] WiFi MAC randomized ... ACTIVE")
    print("[Layer 9] Location Services killed ... ACTIVE")
    print("[Layer 10] Microphones killed ... ACTIVE")
Verify All Hardware Lockdowns

Disabling something and confirming it is actually disabled are two different things. A failed PowerShell command might exit silently without making any change. You need a verification function that checks each lockdown independently and reports pass or fail.

  • Add this verification function to anonshield.py.
def verify_hardware_lockdowns():
    """Check all hardware lockdowns and print pass/fail for each."""
    print("\n--- Hardware Lockdown Verification ---")
    all_passed = True

    # Check IPv6 - should be disabled on all adapters
    ipv6_result = subprocess.run(
        ["powershell", "-Command",
         "(Get-NetAdapterBinding -ComponentID ms_tcpip6 "
         "| Where-Object { $_.Enabled -eq $true }).Count"],
        capture_output=True, text=True
    )
    ipv6_count = int(ipv6_result.stdout.strip() or "0")
    if ipv6_count == 0:
        print("  [PASS] IPv6 disabled on all adapters")
    else:
        print(f"  [WARN] IPv6 still enabled on {ipv6_count} adapter(s)")
        all_passed = False

    # Check Teredo - should show "disabled"
    teredo_result = subprocess.run(
        ["netsh", "interface", "teredo", "show", "state"],
        capture_output=True, text=True
    )
    if "disabled" in teredo_result.stdout.lower():
        print("  [PASS] Teredo disabled")
    else:
        print("  [WARN] Teredo may still be active")
        all_passed = False

    # Check Bluetooth - look for active Bluetooth radios
    bt_result = subprocess.run(
        ["powershell", "-Command",
         "[Windows.Devices.Radios.Radio, Windows.System.Devices, "
         "ContentType=WindowsRuntime] | Out-Null; "
         "$radios = [Windows.Devices.Radios.Radio]::GetRadiosAsync()"
         ".GetAwaiter().GetResult(); "
         "($radios | Where-Object { $_.Kind -eq 'Bluetooth' -and "
         "$_.State -eq 'On' }).Count"],
        capture_output=True, text=True
    )
    bt_on = int(bt_result.stdout.strip() or "0")
    if bt_on == 0:
        print("  [PASS] Bluetooth radio off")
    else:
        print("  [WARN] Bluetooth radio still on")
        all_passed = False

    # Check MAC randomization - registry should be 2
    mac_key = r"SOFTWARE\Microsoft\WlanSvc\Interfaces"
    mac_val = get_registry_value(mac_key, "MacRandomization")
    if mac_val == 2:
        print("  [PASS] WiFi MAC randomization enabled")
    else:
        print("  [WARN] WiFi MAC randomization not confirmed")
        all_passed = False

    # Check Location Services - registry should be 1
    loc_key = r"SOFTWARE\Policies\Microsoft\Windows\LocationAndSensors"
    loc_val = get_registry_value(loc_key, "DisableLocation")
    if loc_val == 1:
        print("  [PASS] Location Services disabled")
    else:
        print("  [WARN] Location Services may still be active")
        all_passed = False

    # Check microphones - count enabled mic devices
    mic_result = subprocess.run(
        ["powershell", "-Command",
         "(Get-PnpDevice -Class AudioEndpoint -FriendlyName '*Microphone*' "
         "| Where-Object { $_.Status -eq 'OK' }).Count"],
        capture_output=True, text=True
    )
    mic_count = int(mic_result.stdout.strip() or "0")
    if mic_count == 0:
        print("  [PASS] All microphones disabled")
    else:
        print(f"  [WARN] {mic_count} microphone(s) still enabled")
        all_passed = False

    if all_passed:
        print("\n  All hardware lockdowns verified.")
    else:
        print("\n  Some checks failed. Warnings above do not block activation.")

    return all_passed

What does this code do?

This function checks each of the five hardware lockdowns independently. It counts how many adapters still have IPv6 enabled, checks if Teredo reports as disabled, queries the WinRT radio API for active Bluetooth radios, reads the registry for MAC randomization and Location Services, and counts how many microphones are still in an OK state.

Failures print as warnings but do not block Anon Mode from activating. Some machines may not have Bluetooth hardware or microphones, so a missing device is not treated as a fatal error.

Now wire everything together. Add a main block at the bottom of anonshield.py to test these layers.

if __name__ == "__main__":
    print("=== Hardware Lockdown Test ===")
    activate_layers_6_7()
    activate_layers_8_9_10()
    verify_hardware_lockdowns()
    print("\nDone. Press Enter to exit.")
    input()
  • Open a terminal as Administrator.
  • Run python anonshield.py to test the hardware lockdowns.

Admin privileges required

All hardware lockdown commands require Administrator privileges. If you run the script without elevation, the PowerShell and netsh commands will fail silently. Always right-click your terminal and select Run as administrator.

What should I see?

You should see each layer printing its status, followed by the verification section showing [PASS] for each check. If your machine does not have Bluetooth hardware, the Bluetooth check will show [WARN]. This is expected and does not indicate a problem.

Seeing errors or permission denied?

Make sure you are running the terminal as Administrator. If you see errors about WinRT or the Bluetooth check, your machine may not have Bluetooth hardware. The script handles this gracefully. If other checks fail, help me debug hardware lockdown failures in anonshield.py.

Your hardware is now locked down. IPv6 cannot leak. Bluetooth is off. Your WiFi MAC is randomized. Location Services are disabled. Your microphones are deactivated at the driver level. Next up, you will silence your network broadcasts, randomize your hostname, and kill Windows telemetry to complete your identity erasure.

Silence Broadcasts, Randomize Identity, and Kill Telemetry

Your hardware is locked down. No IPv6 leaks, no Bluetooth beacons, no microphone eavesdropping, and your WiFi MAC is randomized. But your machine is still chatting on the local network.

Windows constantly announces your computer's presence through broadcast protocols like LLMNR, NetBIOS, and mDNS. It also sends your hostname, telemetry data, and error reports to Microsoft. Even with all traffic routed through Tor, these broadcasts leak your identity on the local network. In this step, you will silence every broadcast, randomize your hostname, and kill Windows telemetry.

In This Step, Get Ready To:
  • Disable all network broadcast protocols that leak your machine's presence on the LAN.
  • Randomize your hostname and suppress all Windows telemetry, activity history, and error reporting.
  • Verify that broadcasts are silenced and telemetry is disabled.
Disable LLMNR, NetBIOS, mDNS, SSDP, and UPnP

Windows uses several broadcast and discovery protocols to find other devices on your local network. LLMNR resolves hostnames when DNS fails. NetBIOS announces your computer name over the network. mDNS (multicast DNS) advertises services. SSDP and UPnP discover and configure devices automatically. Every one of these protocols broadcasts identifying information about your machine to anyone listening on the same network.

Add the following function to anonshield.py to disable all five broadcast protocols and save their original states for teardown.

import subprocess
import winreg

def get_registry_value(key_path, value_name, hive=winreg.HKEY_LOCAL_MACHINE):
    """Read a registry value, returning None if it doesn't exist."""
    try:
        key = winreg.OpenKey(hive, key_path)
        val, _ = winreg.QueryValueEx(key, value_name)
        winreg.CloseKey(key)
        return val
    except FileNotFoundError:
        return None
    except OSError:
        return None

def set_registry_value(key_path, value_name, value, reg_type=winreg.REG_DWORD, hive=winreg.HKEY_LOCAL_MACHINE):
    """Set a registry value, creating the key if needed."""
    key = winreg.CreateKey(hive, key_path)
    winreg.SetValueEx(key, value_name, 0, reg_type, value)
    winreg.CloseKey(key)

def disable_broadcasts():
    """Disable LLMNR, NetBIOS, mDNS, SSDP, and UPnP. Returns original state dict."""
    original = {}

    # --- LLMNR: set EnableMulticast to 0 ---
    llmnr_path = r"SOFTWARE\Policies\Microsoft\Windows NT\DNSClient"
    original['llmnr'] = get_registry_value(llmnr_path, "EnableMulticast")
    set_registry_value(llmnr_path, "EnableMulticast", 0)
    print("  [+] LLMNR disabled")

    # --- NetBIOS: set NetbiosOptions to 2 (disabled) on all interfaces ---
    netbt_path = r"SYSTEM\CurrentControlSet\Services\NetBT\Parameters\Interfaces"
    original['netbios_interfaces'] = []
    try:
        netbt_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, netbt_path)
        i = 0
        while True:
            try:
                iface = winreg.EnumKey(netbt_key, i)
                iface_path = netbt_path + "\\" + iface
                orig_val = get_registry_value(iface_path, "NetbiosOptions")
                original['netbios_interfaces'].append((iface_path, orig_val))
                set_registry_value(iface_path, "NetbiosOptions", 2)
                i += 1
            except OSError:
                break
        winreg.CloseKey(netbt_key)
    except OSError:
        pass
    print("  [+] NetBIOS disabled on all interfaces")

    # --- mDNS: set EnableMDNS to 0 ---
    mdns_path = r"SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
    original['mdns'] = get_registry_value(mdns_path, "EnableMDNS")
    set_registry_value(mdns_path, "EnableMDNS", 0)
    print("  [+] mDNS disabled")

    # --- SSDP Discovery service: stop and disable ---
    ssdp_result = subprocess.run(
        ["powershell", "-Command",
         "(Get-Service SSDPSRV).StartType"],
        capture_output=True, text=True
    )
    original['ssdp_starttype'] = ssdp_result.stdout.strip()
    subprocess.run(
        ["powershell", "-Command",
         "Stop-Service SSDPSRV -Force -ErrorAction SilentlyContinue;"
         "Set-Service SSDPSRV -StartupType Disabled"],
        capture_output=True
    )
    print("  [+] SSDP Discovery service disabled")

    # --- UPnP Device Host service: stop and disable ---
    upnp_result = subprocess.run(
        ["powershell", "-Command",
         "(Get-Service upnphost).StartType"],
        capture_output=True, text=True
    )
    original['upnp_starttype'] = upnp_result.stdout.strip()
    subprocess.run(
        ["powershell", "-Command",
         "Stop-Service upnphost -Force -ErrorAction SilentlyContinue;"
         "Set-Service upnphost -StartupType Disabled"],
        capture_output=True
    )
    print("  [+] UPnP Device Host service disabled")

    print("[Layer 11] Network broadcasts silenced ... ACTIVE")
    return original

What does this code do?

This function silences five broadcast protocols that leak your machine's identity on the local network.

  • LLMNR is disabled by setting EnableMulticast=0 in the DNS Client registry policy.
  • NetBIOS is disabled by setting NetbiosOptions=2 on every network interface.
  • mDNS is disabled by setting EnableMDNS=0 in the DNS cache parameters.
  • SSDP and UPnP are disabled by stopping and disabling the SSDPSRV and upnphost Windows services.

Every original value is saved into a dictionary so the teardown function can restore everything when you press 'q'.

Why silence these protocols?

Even with all your traffic routed through Tor, these protocols broadcast on the local network segment. Anyone on the same WiFi network (or an attacker running a rogue access point) can see your computer name, discover your services, and fingerprint your device. Silencing them makes your machine invisible on the LAN.

Randomize Hostname and Kill Windows Telemetry

Your computer's hostname is another unique identifier. Windows also sends telemetry data, activity history, advertising IDs, and error reports to Microsoft. Even though this traffic now routes through Tor, the content of these reports contains identifying information about your hardware and usage patterns. You need to randomize the hostname and suppress all telemetry at the registry level.

Add this function to anonshield.py below the broadcast function.

import random
import string

def randomize_hostname():
    """Set a random hostname (DESKTOP-XXXXXXX format). Returns original hostname."""
    # Save the current hostname for teardown
    original_hostname = subprocess.run(
        ["powershell", "-Command", "$env:COMPUTERNAME"],
        capture_output=True, text=True
    ).stdout.strip()

    # Generate a random hostname that looks like a default Windows name
    random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=7))
    new_hostname = f"DESKTOP-{random_suffix}"

    # Apply the new hostname via registry (takes effect for network identity)
    set_registry_value(
        r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName",
        "ComputerName", new_hostname, reg_type=winreg.REG_SZ
    )
    set_registry_value(
        r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters",
        "Hostname", new_hostname, reg_type=winreg.REG_SZ
    )
    set_registry_value(
        r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters",
        "NV Hostname", new_hostname, reg_type=winreg.REG_SZ
    )

    print(f"  [+] Hostname randomized: {new_hostname}")
    print("[Layer 12] Hostname randomized ... ACTIVE")
    return original_hostname

What does this code do?

This function generates a random hostname in the DESKTOP-XXXXXXX format, which matches the default naming convention Windows uses. This makes your machine blend in rather than standing out with a custom name like JOHNS-GAMING-PC. The hostname is set in three registry locations to cover both the active computer name and the TCP/IP hostname used in network communications.

Now add the telemetry suppression function directly below.

def kill_telemetry():
    """Disable Windows telemetry, activity history, advertising ID, and error reporting.
    Returns dict of original values for teardown."""
    original = {}

    # --- Diagnostic data collection: set AllowTelemetry to 0 ---
    telemetry_path = r"SOFTWARE\Policies\Microsoft\Windows\DataCollection"
    original['telemetry'] = get_registry_value(telemetry_path, "AllowTelemetry")
    set_registry_value(telemetry_path, "AllowTelemetry", 0)
    print("  [+] Diagnostic data collection disabled")

    # --- Activity history: set PublishUserActivities to 0 ---
    activity_path = r"Software\Policies\Microsoft\Windows\System"
    original['activity'] = get_registry_value(activity_path, "PublishUserActivities")
    set_registry_value(activity_path, "PublishUserActivities", 0)
    print("  [+] Activity history disabled")

    # --- Advertising ID: set Enabled to 0 ---
    ad_path = r"SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo"
    original['advertising'] = get_registry_value(
        ad_path, "Enabled", hive=winreg.HKEY_CURRENT_USER
    )
    set_registry_value(
        ad_path, "Enabled", 0, hive=winreg.HKEY_CURRENT_USER
    )
    print("  [+] Advertising ID disabled")

    # --- Windows Error Reporting: set Disabled to 1 ---
    wer_path = r"SOFTWARE\Policies\Microsoft\Windows\Windows Error Reporting"
    original['wer'] = get_registry_value(wer_path, "Disabled")
    set_registry_value(wer_path, "Disabled", 1)
    print("  [+] Windows Error Reporting disabled")

    print("[Layer 13] Windows telemetry killed ... ACTIVE")
    return original

What does this code do?

This function suppresses four categories of Windows telemetry by setting registry policies.

  • AllowTelemetry=0 disables diagnostic data collection at the policy level.
  • PublishUserActivities=0 stops Windows from sending your activity history (app usage, browsing, file access) to Microsoft.
  • Advertising ID Enabled=0 disables the unique advertising identifier that tracks you across apps.
  • Windows Error Reporting Disabled=1 prevents crash reports (which contain hardware and software details) from being sent.

Pro tip: telemetry still routes through Tor

Windows Home and Pro editions enforce a minimum telemetry level that cannot be fully disabled. The registry settings above suppress as much as possible. Any telemetry that does still get sent will route through the Tor tunnel, so Microsoft sees a Tor exit IP instead of your real one.

Now wire both functions together with the broadcast function from the previous substep. Add an orchestration function that calls all three in sequence.

def activate_identity_erasure():
    """Activate Layers 11-13: broadcasts, hostname, and telemetry."""
    print("\n--- Identity Erasure (Layers 11-13) ---")

    # Layer 11: silence all network broadcasts
    broadcast_originals = disable_broadcasts()

    # Layer 12: randomize hostname
    original_hostname = randomize_hostname()

    # Layer 13: kill Windows telemetry
    telemetry_originals = kill_telemetry()

    # Bundle all originals for teardown
    return {
        'broadcasts': broadcast_originals,
        'hostname': original_hostname,
        'telemetry': telemetry_originals
    }

What does this code do?

This orchestration function activates Layers 11 through 13 in sequence and collects all original values into a single dictionary. The teardown function will use this dictionary to restore everything when you press 'q'.

Verify Broadcast Silencing and Telemetry Status

You need to confirm that broadcasts are actually silenced and telemetry is disabled. The verification checks three things: no broadcast ports are listening, the hostname has changed, and telemetry registry values are set correctly.

Add this verification function to anonshield.py.

def verify_identity_erasure():
    """Verify Layers 11-13 are active. Returns dict of pass/fail results."""
    results = {}

    # --- Check broadcast ports: 137 (NetBIOS), 5353 (mDNS), 5355 (LLMNR) ---
    for port, name in [(137, "NetBIOS"), (5353, "mDNS"), (5355, "LLMNR")]:
        netstat = subprocess.run(
            ["netstat", "-an"],
            capture_output=True, text=True
        )
        # Check if the port appears in listening state
        port_found = f":{port} " in netstat.stdout and "LISTENING" in netstat.stdout
        # For UDP ports, check UDP lines
        udp_found = False
        for line in netstat.stdout.splitlines():
            if f":{port} " in line and "UDP" in line:
                udp_found = True
                break
        is_silent = not port_found and not udp_found
        results[f"{name}_port_{port}"] = is_silent
        status = "PASS" if is_silent else "WARN"
        print(f"  [{status}] {name} (port {port}): {'silent' if is_silent else 'still active'}")

    # --- Check SSDP and UPnP services are stopped ---
    for svc_name, display in [("SSDPSRV", "SSDP"), ("upnphost", "UPnP")]:
        svc_check = subprocess.run(
            ["powershell", "-Command",
             f"(Get-Service {svc_name}).Status"],
            capture_output=True, text=True
        )
        is_stopped = "Stopped" in svc_check.stdout
        results[display] = is_stopped
        status = "PASS" if is_stopped else "WARN"
        print(f"  [{status}] {display} service: {'stopped' if is_stopped else 'running'}")

    # --- Check hostname changed ---
    current_host = subprocess.run(
        ["powershell", "-Command", "$env:COMPUTERNAME"],
        capture_output=True, text=True
    ).stdout.strip()
    # Read the registry value we set
    reg_host = get_registry_value(
        r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName",
        "ComputerName"
    )
    hostname_changed = reg_host is not None and reg_host.startswith("DESKTOP-") and len(reg_host) == 15
    results['hostname'] = hostname_changed
    status = "PASS" if hostname_changed else "WARN"
    print(f"  [{status}] Hostname: registry={reg_host}, active={current_host}")

    # --- Check telemetry registry values ---
    telemetry_checks = [
        (r"SOFTWARE\Policies\Microsoft\Windows\DataCollection", "AllowTelemetry", 0, "Telemetry"),
        (r"Software\Policies\Microsoft\Windows\System", "PublishUserActivities", 0, "Activity History"),
        (r"SOFTWARE\Policies\Microsoft\Windows\Windows Error Reporting", "Disabled", 1, "Error Reporting"),
    ]
    for path, name, expected, display in telemetry_checks:
        val = get_registry_value(path, name)
        is_set = val == expected
        results[display] = is_set
        status = "PASS" if is_set else "WARN"
        print(f"  [{status}] {display}: {name}={val} (expected {expected})")

    # Advertising ID is in HKCU
    ad_val = get_registry_value(
        r"SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo",
        "Enabled", hive=winreg.HKEY_CURRENT_USER
    )
    ad_disabled = ad_val == 0
    results['AdvertisingID'] = ad_disabled
    status = "PASS" if ad_disabled else "WARN"
    print(f"  [{status}] Advertising ID: Enabled={ad_val} (expected 0)")

    passed = sum(1 for v in results.values() if v)
    total = len(results)
    print(f"\n  Identity Erasure: {passed}/{total} checks passed")
    return results

What does this code do?

This function runs three categories of verification checks. First, it uses netstat to confirm that broadcast ports 137 (NetBIOS), 5353 (mDNS), and 5355 (LLMNR) are no longer active. Second, it confirms the SSDP and UPnP services are stopped. Third, it reads back every telemetry registry value to confirm it was set correctly. Failures show as WARN instead of blocking activation, because some checks depend on system configuration.

Now add a quick test block at the bottom of your file to run the full identity erasure sequence and verify it.

if __name__ == "__main__":
    print("=== Testing Identity Erasure (Layers 11-13) ===")
    originals = activate_identity_erasure()
    print("\n=== Verification ===")
    verify_identity_erasure()
    print("\nOriginals saved for teardown:", list(originals.keys()))
    print("Press Enter to restore and exit...")
    input()
  • Open a terminal as Administrator and run python anonshield.py.

You must run as Administrator

Registry changes, service management, and hostname randomization all require elevated privileges. Right-click your terminal and select Run as administrator before running the script. Without admin privileges, the registry writes will fail silently or throw access denied errors.

You should see each layer activate with [+] confirmations, followed by verification results showing PASS for each check. Some broadcast ports may still show as active until the next network adapter reset. This is expected behavior.

Seeing access denied errors?

Make sure your terminal is running as Administrator. If you are using Windows PowerShell, right-click the icon and select Run as administrator. If you are using Windows Terminal, click the dropdown arrow next to the tab and select the admin option. Help me troubleshoot access denied errors.

Your machine is now invisible on the local network and silent to Microsoft. No broadcasts, no real hostname, no telemetry. Next up, you will build the firewall kill switch that guarantees zero traffic leaks outside the Tor tunnel, set up automatic IP rotation, and create the live dashboard that ties all 16 layers together.

Enable the Firewall Kill Switch, IP Rotation, and Live Dashboard

Your device is now invisible on the local network, with broadcasts silenced, hostname randomized, and telemetry suppressed. But there is still a critical gap. If Tor or tun2socks crashes, your traffic could leak out through your real connection without you knowing. A firewall kill switch closes that gap by blocking all outbound traffic that does not go through the Tor tunnel. In this step, you will build the kill switch, add automatic IP rotation, create the live dashboard, and write the forensic cleanup teardown.

In This Step, Get Ready To:
  • Build a firewall kill switch that blocks all traffic except through the Tor tunnel.
  • Implement automatic IP rotation on startup and every 15 minutes.
  • Launch a live dashboard with keyboard controls and forensic cleanup on exit.
Create the Firewall Kill Switch and Activate IP Rotation

Without a kill switch, any crash in Tor or tun2socks would silently route your traffic through your real IP. The kill switch uses Windows Firewall rules to guarantee that only traffic through the Wintun adapter, Tor, tun2socks, and loopback can leave your machine. Everything else gets blocked.

  • Add the following firewall kill switch functions to your anonshield.py.
import threading
from stem import Signal
from stem.control import Controller

# ── Firewall Kill Switch (Layer 14) ──────────────────────────────

def activate_kill_switch():
    """Back up current firewall config, then block everything except Tor tunnel traffic."""
    backup_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "firewall_backup.wfw")

    # Export current firewall rules so we can restore them on teardown
    run_cmd(f'netsh advfirewall export "{backup_path}"')

    # Block ALL outbound traffic by default
    run_cmd('netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound')

    # Allow outbound through the Wintun virtual adapter (Tor tunnel)
    run_cmd('netsh advfirewall firewall add rule name="AnonShield-Wintun" dir=out action=allow interface="wintun"')

    # Allow tor.exe to communicate (it needs direct access to bridges)
    tor_path = resource_path(os.path.join("tor", "tor", "tor.exe"))
    run_cmd(f'netsh advfirewall firewall add rule name="AnonShield-Tor" dir=out action=allow program="{tor_path}"')

    # Allow tun2socks to communicate
    tun2socks_path = resource_path("tun2socks-windows-amd64.exe")
    run_cmd(f'netsh advfirewall firewall add rule name="AnonShield-Tun2Socks" dir=out action=allow program="{tun2socks_path}"')

    # Allow loopback traffic (needed for SOCKS proxy on 127.0.0.1)
    run_cmd('netsh advfirewall firewall add rule name="AnonShield-Loopback" dir=out action=allow remoteip=127.0.0.1')

    print("[Layer 14] Firewall kill switch ... ACTIVE")
    return backup_path

def deactivate_kill_switch(backup_path):
    """Remove our firewall rules and restore the original config."""
    # Remove all AnonShield rules
    for rule_name in ["AnonShield-Wintun", "AnonShield-Tor", "AnonShield-Tun2Socks", "AnonShield-Loopback"]:
        run_cmd(f'netsh advfirewall firewall delete rule name="{rule_name}"')

    # Restore original firewall configuration
    if os.path.exists(backup_path):
        run_cmd(f'netsh advfirewall import "{backup_path}"')
        os.remove(backup_path)

    print("[Layer 14] Firewall kill switch ... RESTORED")

What does this code do?

The kill switch works in two phases. First, it exports your current firewall configuration to a backup file using netsh advfirewall export. Then it sets the default outbound policy to block and creates four whitelist rules.

Only traffic through the Wintun adapter, tor.exe, tun2socks, and the loopback address can leave your machine. If Tor crashes, your traffic hits a wall instead of leaking through your real IP.

Now add the IP rotation logic. stem sends a NEWNYM signal to Tor's control port, which tells Tor to build new circuits and get a fresh exit IP.

  • Add the IP rotation functions below the kill switch code.
# ── IP Rotation (Layer 15) ────────────────────────────────────────

TOR_CONTROL_PORT = 9051
TOR_PASSWORD = "your_hashed_password_here"  # The plaintext password you used with --hash-password
ROTATION_INTERVAL = 900  # 15 minutes in seconds

def rotate_ip():
    """Send NEWNYM signal to Tor to get a new exit IP."""
    try:
        with Controller.from_port(port=TOR_CONTROL_PORT) as controller:
            controller.authenticate(password=TOR_PASSWORD)
            controller.signal(Signal.NEWNYM)

            # Check how long until a new identity is available
            wait_time = controller.get_newnym_wait()
            if wait_time > 0:
                print(f"  Rate limited. New IP available in {wait_time:.0f} seconds.")
                return False

            print("[Layer 15] IP rotated. New circuit established.")
            return True
    except Exception as e:
        print(f"[Layer 15] IP rotation failed: {e}")
        return False

def start_auto_rotation():
    """Rotate IP immediately, then schedule rotation every 15 minutes."""
    rotate_ip()

    def rotation_loop():
        while not shutdown_event.is_set():
            shutdown_event.wait(ROTATION_INTERVAL)
            if not shutdown_event.is_set():
                rotate_ip()

    rotation_thread = threading.Thread(target=rotation_loop, daemon=True)
    rotation_thread.start()
    print(f"[Layer 15] Auto-rotation every {ROTATION_INTERVAL // 60} minutes ... ACTIVE")
    return rotation_thread

# Global shutdown event to stop background threads cleanly
shutdown_event = threading.Event()

What is NEWNYM?

NEWNYM tells Tor to use new circuits for future connections. Existing connections finish on their old circuits, but anything new gets a fresh path through the Tor network. This gives you a different exit IP. Tor rate-limits this signal to roughly once every 10 seconds to prevent abuse.

Pro tip: replace the password

The TOR_PASSWORD variable must match the plaintext password you used when running tor.exe --hash-password in Step 2. The HashedControlPassword in your torrc is the hashed version. stem needs the original plaintext to authenticate.

Launch the Live Dashboard and Input Loop

The dashboard is what the user sees when they launch the exe. It prints the status of all 16 layers, displays an OPSEC reminder, and then enters a keyboard input loop. A background monitor thread checks that Tor and tun2socks are still running every 10 seconds.

  • Add the dashboard and input loop functions to anonshield.py.
import time
import msvcrt

# ── Layer Status Tracking ─────────────────────────────────────────

layer_status = {}

def set_layer(number, name, status):
    """Record a layer's status for dashboard display."""
    layer_status[number] = {"name": name, "status": status}

def print_dashboard():
    """Print the full 16-layer status dashboard."""
    os.system('cls')
    print("="*60)
    print("       ANONSHIELD - 16-LAYER ANON MODE")
    print("="*60)
    print()

    categories = [
        ("NETWORK ANONYMITY", range(1, 6)),
        ("HARDWARE LOCKDOWN", range(6, 11)),
        ("IDENTITY ERASURE", range(11, 14)),
        ("DEFENSE & ROTATION", range(14, 17)),
    ]

    for category_name, layer_range in categories:
        print(f"  {category_name}")
        for num in layer_range:
            if num in layer_status:
                layer = layer_status[num]
                indicator = "ACTIVE" if layer["status"] else "FAILED"
                print(f"    [{num:>2}] {layer['name']:<35} {indicator}")
        print()

    active_count = sum(1 for l in layer_status.values() if l["status"])
    total = len(layer_status)
    print(f"  Status: {active_count}/{total} layers active")
    print()

def print_opsec_reminder():
    """Print operational security guidance on startup."""
    print("-"*60)
    print("  OPSEC REMINDER")
    print("-"*60)
    print("  For browsing and posting, use the Tor Browser.")
    print("  It is already routed through your tunnel.")
    print("  Never log into personal accounts.")
    print("  Your tool protects the network and device layers.")
    print("  Tor Browser protects the application layer.")
    print("  Together they cover both sides.")
    print("-"*60)
    print()

def print_controls():
    """Print available keyboard controls."""
    print("  Controls:")
    print("    [r] Rotate IP    [v] Verify all layers")
    print("    [s] Scrub file   [q] Deactivate and exit")
    print()

Why msvcrt?

The msvcrt module is a Windows-only Python library that lets you read single keystrokes without requiring the user to press Enter. This makes the dashboard feel responsive. The user presses 'q' and the teardown starts immediately.

Now add the background health monitor and the main input loop.

# ── Background Health Monitor ─────────────────────────────────────

def start_health_monitor(tor_process, tun2socks_process):
    """Check Tor and tun2socks every 10 seconds. Warn if either dies."""
    def monitor_loop():
        while not shutdown_event.is_set():
            shutdown_event.wait(10)
            if shutdown_event.is_set():
                break
            if tor_process.poll() is not None:
                print("\n  [WARNING] Tor process has stopped!")
            if tun2socks_process.poll() is not None:
                print("\n  [WARNING] tun2socks process has stopped!")

    monitor_thread = threading.Thread(target=monitor_loop, daemon=True)
    monitor_thread.start()
    return monitor_thread

# ── Main Input Loop ───────────────────────────────────────────────

def run_input_loop():
    """Listen for single keystrokes and dispatch commands."""
    while True:
        if msvcrt.kbhit():
            key = msvcrt.getch().decode('utf-8', errors='ignore').lower()

            if key == 'q':
                print("\n  Deactivating Anon Mode...")
                return 'quit'
            elif key == 'r':
                print("\n  Rotating IP...")
                rotate_ip()
                # Show new exit IP
                try:
                    import requests
                    proxies = {
                        'http': 'socks5h://127.0.0.1:9050',
                        'https': 'socks5h://127.0.0.1:9050'
                    }
                    resp = requests.get('https://api.ipify.org', proxies=proxies, timeout=15)
                    print(f"  New exit IP: {resp.text}")
                except Exception:
                    print("  Could not verify new IP.")
                print_controls()
            elif key == 'v':
                print("\n  Running verification...")
                import verify
                verify.run_all_checks()
                print_controls()
            elif key == 's':
                print("\n  Enter file path to scrub: ", end='')
                file_path = input().strip()
                import scrub
                scrub.scrub_file(file_path)
                print_controls()

        time.sleep(0.1)  # Prevent CPU spinning

What does this code do?

The health monitor runs in a background thread, polling the Tor and tun2socks processes every 10 seconds. If either process dies, the user gets an immediate warning.

The input loop uses msvcrt.kbhit() to detect keystrokes without blocking. Each key dispatches to a different action: rotate IP, run verification, scrub a file, or quit.

Build the Forensic Cleanup Teardown

When the user presses 'q' or hits Ctrl+C, the tool needs to undo everything it changed and then go further. It wipes forensic traces so there is no evidence on the machine that Anon Mode was ever active. This is Layer 16.

  • Add the teardown function to anonshield.py. This function reverses every change in the opposite order from activation.
# ── Forensic Cleanup and Teardown (Layer 16) ─────────────────────

def teardown(firewall_backup, tor_process, tun2socks_process, original_states):
    """Reverse all changes and wipe forensic traces."""
    print("\n" + "="*60)
    print("  DEACTIVATING ANON MODE")
    print("="*60)

    # Signal background threads to stop
    shutdown_event.set()

    # ── Reverse Layer 14: Restore firewall ──
    deactivate_kill_switch(firewall_backup)

    # ── Reverse Layers 3-5: Remove routes, kill tun2socks ──
    if original_states.get("gateway"):
        run_cmd('route delete 0.0.0.0')
        run_cmd(f'route add 0.0.0.0 mask 0.0.0.0 {original_states["gateway"]}')
        print("  Routes restored.")

    if tun2socks_process and tun2socks_process.poll() is None:
        tun2socks_process.terminate()
        tun2socks_process.wait(timeout=10)
        print("  tun2socks terminated.")

    # ── Reverse Layer 1: Kill Tor ──
    if tor_process and tor_process.poll() is None:
        tor_process.terminate()
        tor_process.wait(timeout=10)
        print("  Tor terminated.")

    # ── Reverse Layers 11-13: Restore broadcasts, hostname, telemetry ──
    restore_broadcasts(original_states)
    restore_hostname(original_states)
    restore_telemetry(original_states)

    # ── Reverse Layers 6-10: Restore hardware ──
    restore_ipv6()
    restore_bluetooth()
    restore_mac_randomization(original_states)
    restore_location_services(original_states)
    restore_microphones()

    # ── Layer 16: Forensic cleanup ──
    print("\n  Running forensic cleanup...")

    # Flush DNS cache
    run_cmd('ipconfig /flushdns')

    # Clear Prefetch files
    run_cmd('del /q /f "C:\\Windows\\Prefetch\\*" 2>nul', shell=True)

    # Clear Recent Files
    recent_path = os.path.expandvars(r'%APPDATA%\Microsoft\Windows\Recent\*')
    run_cmd(f'del /q /f "{recent_path}" 2>nul', shell=True)

    # Clear temp files
    temp_path = os.path.expandvars(r'%TEMP%\*')
    run_cmd(f'del /q /f "{temp_path}" 2>nul', shell=True)
    run_cmd(f'rd /s /q "{os.path.expandvars("%TEMP%")}" 2>nul', shell=True)

    # Clear network-related Windows Event Logs
    run_cmd('wevtutil cl "Microsoft-Windows-WLAN-AutoConfig/Operational"')
    run_cmd('wevtutil cl "Microsoft-Windows-NetworkProfile/Operational"')

    print("  [Layer 16] Forensic cleanup ... COMPLETE")
    print()
    print("="*60)
    print("  Anon Mode DEACTIVATED. Zero traces remain.")
    print("="*60)

What does the forensic cleanup wipe?

Five categories of traces get wiped. The DNS cache reveals which domains you resolved. Prefetch files show which executables you ran. Recent Files logs which documents you opened. Temp files may contain extracted data. Network Event Logs record which WiFi networks and network profiles your machine connected to.

Now wire everything together with a Ctrl+C handler so the teardown runs even if the user force-closes the terminal.

  • Add the signal handler and the main entry point at the bottom of anonshield.py.
import signal

def main():
    # Check for admin privileges
    import ctypes
    if not ctypes.windll.shell32.IsUserAnAdmin():
        print("ERROR: AnonShield requires administrator privileges.")
        print("Right-click and select 'Run as administrator'.")
        input("Press Enter to exit...")
        return

    original_states = {}
    tor_process = None
    tun2socks_process = None
    firewall_backup = None

    # Register Ctrl+C handler
    def handle_interrupt(signum, frame):
        print("\n  Ctrl+C detected. Running teardown...")
        teardown(firewall_backup, tor_process, tun2socks_process, original_states)
        sys.exit(0)

    signal.signal(signal.SIGINT, handle_interrupt)

    try:
        # ── Activate Layers 1-5 (from Step 2) ──
        tor_process = launch_tor()
        tun2socks_process, original_states["gateway"] = setup_tunnel()

        # ── Activate Layers 6-10 (from Step 3) ──
        activate_hardware_lockdown(original_states)

        # ── Activate Layers 11-13 (from Step 4) ──
        activate_identity_erasure(original_states)

        # ── Activate Layer 14: Kill switch ──
        firewall_backup = activate_kill_switch()
        set_layer(14, "Firewall kill switch", True)

        # ── Activate Layer 15: IP rotation ──
        start_auto_rotation()
        set_layer(15, "IP auto-rotation (15 min)", True)

        # ── Layer 16 is teardown-only ──
        set_layer(16, "Forensic cleanup (on exit)", True)

        # ── Display dashboard ──
        print_dashboard()
        print_opsec_reminder()
        print_controls()

        # ── Start health monitor ──
        start_health_monitor(tor_process, tun2socks_process)

        # ── Enter input loop ──
        result = run_input_loop()

        if result == 'quit':
            teardown(firewall_backup, tor_process, tun2socks_process, original_states)

    except Exception as e:
        print(f"\n  CRITICAL ERROR: {e}")
        print("  Running emergency teardown...")
        teardown(firewall_backup, tor_process, tun2socks_process, original_states)

if __name__ == "__main__":
    main()

What does this code do?

The main() function is the orchestrator. It activates all 16 layers in sequence, displays the dashboard, and enters the input loop.

The signal.SIGINT handler catches Ctrl+C so the teardown always runs. The try/except block catches any unexpected crash and runs an emergency teardown. No matter how the program exits, your machine gets restored.

Firewall rules not applying?

Make sure you are running the script as administrator. Firewall changes require elevated privileges. If you see "Access denied" errors, right-click your terminal and select Run as administrator. You can verify rules were created by running netsh advfirewall firewall show rule name=AnonShield-Wintun in a separate admin terminal. Help me debug my firewall rules.

Your tool now has all 16 layers wired together. The kill switch guarantees zero traffic leaks. IP rotation keeps your exit identity fresh. The dashboard gives you real-time visibility. The teardown wipes every trace. Next up, you will build the metadata scrubber and verification engine as importable modules.

Build the Metadata Scrubber and Verification Engine

Your 16-layer Anon Mode is fully functional. Traffic routes through Tor, hardware is locked down, broadcasts are silenced, telemetry is killed, the firewall kill switch is active, and IP rotation is running. Now you need two supporting modules that make the tool actually usable in practice.

The first is a metadata scrubber. Every photo you take embeds GPS coordinates, camera serial numbers, and timestamps into the file itself. Posting an "anonymous" image with this data intact defeats the purpose of routing through Tor. The second is a verification engine that checks all 16 layers and tells you exactly which ones are active and which ones failed. Trust but verify.

In This Step, Get Ready To:
  • Build a metadata scrubber that strips EXIF data from images and author data from documents.
  • Build a verification engine that checks all 16 anonymity layers and reports pass/fail.
  • Test the full system as Python scripts before compiling into an exe.
Build the Metadata Scrubber Module

Every digital photo contains EXIF data. This is a block of metadata embedded in the image file that records GPS coordinates, camera make and model, serial numbers, timestamps, lens information, and even the software used to edit it. If you post an image online while running Anon Mode, this metadata can identify you instantly.

The scrubber module uses Pillow to open an image, discard all EXIF data, and save a clean copy. It is an importable module that the dashboard calls when you press 's'.

  • Create a new file called scrub.py in your project folder.
  • Add the following code.
import os
from PIL import Image

# Supported image extensions for EXIF stripping
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".tiff", ".tif"}

def scrub_file(file_path):
    """Strip identifying metadata from a file.
    Returns a list of strings describing what was removed."""
    ext = os.path.splitext(file_path)[1].lower()
    removed = []

    if ext in IMAGE_EXTENSIONS:
        removed = scrub_image(file_path)
    else:
        print(f"[Scrubber] Unsupported file type: {ext}")
        return removed

    if removed:
        print(f"[Scrubber] Cleaned: {os.path.basename(file_path)}")
        for item in removed:
            print(f"  - Removed: {item}")
    else:
        print(f"[Scrubber] No metadata found in {os.path.basename(file_path)}")

    return removed

def scrub_image(file_path):
    """Open an image, record what EXIF tags exist, then save without any EXIF."""
    removed = []
    img = Image.open(file_path)

    # Check for EXIF data before stripping
    exif_data = img.getexif()
    if exif_data:
        # Map common EXIF tag IDs to human-readable names
        tag_names = {
            271: "Camera Make", 272: "Camera Model",
            274: "Orientation", 282: "X Resolution",
            306: "DateTime", 315: "Artist",
            34665: "EXIF IFD", 34853: "GPS Info",
            36867: "DateTimeOriginal", 36868: "DateTimeDigitized",
            42036: "Lens Make", 42037: "Lens Model",
            305: "Software"
        }
        for tag_id in exif_data:
            name = tag_names.get(tag_id, f"Tag {tag_id}")
            removed.append(name)

    # Save the image with all EXIF data stripped
    # Setting exif to empty bytes removes all metadata
    clean_data = img.copy()
    output_path = file_path  # Overwrite original
    clean_data.save(output_path, exif=b"")

    return removed

How Does the EXIF Stripping Work?

Pillow's img.save(output_path, exif=b"") is the key line. By passing an empty bytes object as the EXIF parameter, Pillow writes the image pixels without any metadata. The original GPS coordinates, camera serial number, timestamps, and software info are all discarded.

Before stripping, the code reads the existing EXIF tags so it can report exactly what was removed. This gives you visibility into what identifying data was embedded in your files.

Pro tip: test with a real photo

Take a photo with your phone and transfer it to your PC. Phone photos are loaded with EXIF data including GPS coordinates. This gives you a realistic test file for the scrubber.

Build the Verification Engine Module

Activating 16 layers is meaningless if you cannot confirm they are actually working. The verification engine checks every single layer and reports pass or fail. It is what runs when you press 'v' in the dashboard.

  • Create a new file called verify.py in your project folder.
  • Add the following code.
import subprocess
import winreg
import requests
from stem.control import Controller

# Tor SOCKS proxy for verification requests
TOR_PROXIES = {
    "http": "socks5h://127.0.0.1:9050",
    "https": "socks5h://127.0.0.1:9050"
}

def run_all_checks(control_password):
    """Run all 16 layer checks. Returns (passed, total, results_list)."""
    results = []

    results.append(check_tor_bridges(control_password))
    results.append(check_stream_isolation())
    results.append(check_public_ip())
    results.append(check_dns_leak())
    results.append(check_ipv6())
    results.append(check_transition_mechanisms())
    results.append(check_bluetooth())
    results.append(check_mac_randomization())
    results.append(check_location_services())
    results.append(check_microphones())
    results.append(check_broadcasts())
    results.append(check_hostname())
    results.append(check_telemetry())
    results.append(check_firewall())
    results.append(check_ip_rotation(control_password))
    results.append(check_forensic_traces())

    passed = sum(1 for r in results if r["status"] == "PASS")
    total = len(results)

    # Print results with color coding
    print("\n" + "=" * 50)
    print("  VERIFICATION REPORT")
    print("=" * 50)
    for r in results:
        icon = "[PASS]" if r["status"] == "PASS" else "[FAIL]"
        print(f"  {icon} Layer {r['layer']:>2}: {r['name']}")
        if r["status"] == "FAIL" and r.get("detail"):
            print(f"         -> {r['detail']}")
    print(f"\n  Score: {passed}/{total} layers verified")
    print("=" * 50 + "\n")

    return passed, total, results

What Does This Code Do?

The run_all_checks() function is the entry point. It calls 16 individual check functions, collects the results, and prints a formatted report with pass/fail for each layer and a final score.

Now add the individual check functions. These are the 16 checks that verify each anonymity layer. Add them to the same verify.py file below the run_all_checks() function.

def check_tor_bridges(control_password):
    """Layer 1: Verify Tor is running and connected via bridges."""
    try:
        with Controller.from_port(port=9051) as ctrl:
            ctrl.authenticate(password=control_password)
            # Check if using bridges by looking at circuit entry nodes
            circuits = ctrl.get_circuits()
            if circuits:
                return {"layer": 1, "name": "Tor via bridges", "status": "PASS"}
        return {"layer": 1, "name": "Tor via bridges", "status": "FAIL",
                "detail": "No circuits found"}
    except Exception as e:
        return {"layer": 1, "name": "Tor via bridges", "status": "FAIL",
                "detail": str(e)}

def check_stream_isolation():
    """Layer 2: Verify stream isolation is configured."""
    try:
        with Controller.from_port(port=9051) as ctrl:
            ctrl.authenticate()
            socks_listeners = ctrl.get_listeners("SOCKS")
            # IsolateSOCKSAuth is configured in torrc
            if socks_listeners:
                return {"layer": 2, "name": "Stream isolation", "status": "PASS"}
        return {"layer": 2, "name": "Stream isolation", "status": "FAIL",
                "detail": "No SOCKS listeners"}
    except Exception as e:
        return {"layer": 2, "name": "Stream isolation", "status": "FAIL",
                "detail": str(e)}

def check_public_ip():
    """Layer 3: Verify traffic exits through Tor."""
    try:
        r = requests.get("https://check.torproject.org/api/ip",
                         proxies=TOR_PROXIES, timeout=15)
        data = r.json()
        if data.get("IsTor"):
            return {"layer": 3, "name": f"Tor exit IP: {data.get('IP')}",
                    "status": "PASS"}
        return {"layer": 3, "name": "Public IP (not Tor)", "status": "FAIL",
                "detail": f"IP: {data.get('IP')}"}
    except Exception as e:
        return {"layer": 3, "name": "Public IP check", "status": "FAIL",
                "detail": str(e)}

def check_dns_leak():
    """Layer 4-5: Verify DNS routes through Tor (socks5h)."""
    try:
        # socks5h resolves DNS through the proxy, not locally
        r = requests.get("https://check.torproject.org/api/ip",
                         proxies=TOR_PROXIES, timeout=15)
        if r.status_code == 200:
            return {"layer": 4, "name": "DNS via Tor", "status": "PASS"}
        return {"layer": 4, "name": "DNS via Tor", "status": "FAIL"}
    except Exception as e:
        return {"layer": 4, "name": "DNS via Tor", "status": "FAIL",
                "detail": str(e)}

Continue adding the hardware and identity verification checks below the network checks.

def check_ipv6():
    """Layer 5: Verify IPv6 is disabled on all adapters."""
    try:
        result = subprocess.run(
            ["powershell", "-Command",
             "Get-NetAdapterBinding -ComponentID ms_tcpip6 | Where-Object {$_.Enabled -eq $true}"],
            capture_output=True, text=True, timeout=10
        )
        if result.stdout.strip() == "":
            return {"layer": 5, "name": "IPv6 disabled", "status": "PASS"}
        return {"layer": 5, "name": "IPv6 disabled", "status": "FAIL",
                "detail": "IPv6 still enabled on some adapters"}
    except Exception as e:
        return {"layer": 5, "name": "IPv6 disabled", "status": "FAIL",
                "detail": str(e)}

def check_transition_mechanisms():
    """Layer 6: Verify Teredo, 6to4, ISATAP are disabled."""
    try:
        result = subprocess.run(
            ["netsh", "interface", "teredo", "show", "state"],
            capture_output=True, text=True, timeout=10
        )
        if "disabled" in result.stdout.lower():
            return {"layer": 6, "name": "Teredo/6to4/ISATAP disabled", "status": "PASS"}
        return {"layer": 6, "name": "Teredo/6to4/ISATAP disabled", "status": "FAIL",
                "detail": "Teredo may still be active"}
    except Exception as e:
        return {"layer": 6, "name": "Teredo/6to4/ISATAP disabled", "status": "FAIL",
                "detail": str(e)}

def check_bluetooth():
    """Layer 7: Verify Bluetooth radio is off."""
    try:
        ps_script = (
            "Add-Type -AssemblyName System.Runtime.WindowsRuntime; "
            "$radios = [Windows.Devices.Radios.Radio,Windows.System.Devices,ContentType=WindowsRuntime]::GetRadiosAsync().GetAwaiter().GetResult(); "
            "$bt = $radios | Where-Object {$_.Kind -eq 'Bluetooth'}; "
            "if ($bt) { $bt.State } else { 'NotFound' }"
        )
        result = subprocess.run(
            ["powershell", "-Command", ps_script],
            capture_output=True, text=True, timeout=15
        )
        state = result.stdout.strip()
        if state == "Off" or state == "NotFound":
            return {"layer": 7, "name": "Bluetooth radio off", "status": "PASS"}
        return {"layer": 7, "name": "Bluetooth radio off", "status": "FAIL",
                "detail": f"State: {state}"}
    except Exception as e:
        return {"layer": 7, "name": "Bluetooth radio off", "status": "FAIL",
                "detail": str(e)}

def check_mac_randomization():
    """Layer 8: Verify WiFi MAC randomization is enabled."""
    try:
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Microsoft\WlanSvc\Interfaces",
            0, winreg.KEY_READ
        )
        # Check first interface for MacRandomization value
        subkey_name = winreg.EnumKey(key, 0)
        subkey = winreg.OpenKey(key, subkey_name)
        val, _ = winreg.QueryValueEx(subkey, "MacRandomization")
        if val == 2:
            return {"layer": 8, "name": "MAC randomization", "status": "PASS"}
        return {"layer": 8, "name": "MAC randomization", "status": "FAIL",
                "detail": f"Value: {val} (expected 2)"}
    except FileNotFoundError:
        return {"layer": 8, "name": "MAC randomization", "status": "FAIL",
                "detail": "No WiFi interfaces found"}
    except Exception as e:
        return {"layer": 8, "name": "MAC randomization", "status": "FAIL",
                "detail": str(e)}

def check_location_services():
    """Layer 9: Verify Location Services are disabled."""
    try:
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Policies\Microsoft\Windows\LocationAndSensors",
            0, winreg.KEY_READ
        )
        val, _ = winreg.QueryValueEx(key, "DisableLocation")
        if val == 1:
            return {"layer": 9, "name": "Location Services disabled", "status": "PASS"}
        return {"layer": 9, "name": "Location Services disabled", "status": "FAIL",
                "detail": f"Value: {val}"}
    except FileNotFoundError:
        return {"layer": 9, "name": "Location Services disabled", "status": "FAIL",
                "detail": "Registry key not found"}
    except Exception as e:
        return {"layer": 9, "name": "Location Services disabled", "status": "FAIL",
                "detail": str(e)}

def check_microphones():
    """Layer 10: Verify all microphones are disabled."""
    try:
        result = subprocess.run(
            ["powershell", "-Command",
             "Get-PnpDevice -Class AudioEndpoint -FriendlyName '*Microphone*' "
             "| Where-Object {$_.Status -eq 'OK'} | Measure-Object | Select-Object -ExpandProperty Count"],
            capture_output=True, text=True, timeout=10
        )
        count = int(result.stdout.strip() or "0")
        if count == 0:
            return {"layer": 10, "name": "Microphones disabled", "status": "PASS"}
        return {"layer": 10, "name": "Microphones disabled", "status": "FAIL",
                "detail": f"{count} mic(s) still active"}
    except Exception as e:
        return {"layer": 10, "name": "Microphones disabled", "status": "FAIL",
                "detail": str(e)}

Finally, add the identity, defense, and forensic trace checks.

def check_broadcasts():
    """Layer 11: Verify LLMNR, NetBIOS, mDNS, SSDP are silenced."""
    try:
        # Check if SSDP service is stopped
        result = subprocess.run(
            ["powershell", "-Command",
             "(Get-Service SSDPSRV).Status"],
            capture_output=True, text=True, timeout=10
        )
        ssdp_stopped = "Stopped" in result.stdout

        # Check LLMNR registry
        try:
            key = winreg.OpenKey(
                winreg.HKEY_LOCAL_MACHINE,
                r"SOFTWARE\Policies\Microsoft\Windows NT\DNSClient",
                0, winreg.KEY_READ
            )
            val, _ = winreg.QueryValueEx(key, "EnableMulticast")
            llmnr_off = (val == 0)
        except FileNotFoundError:
            llmnr_off = False

        if ssdp_stopped and llmnr_off:
            return {"layer": 11, "name": "Broadcasts silenced", "status": "PASS"}
        detail = []
        if not ssdp_stopped:
            detail.append("SSDP running")
        if not llmnr_off:
            detail.append("LLMNR enabled")
        return {"layer": 11, "name": "Broadcasts silenced", "status": "FAIL",
                "detail": ", ".join(detail)}
    except Exception as e:
        return {"layer": 11, "name": "Broadcasts silenced", "status": "FAIL",
                "detail": str(e)}

def check_hostname():
    """Layer 12: Verify hostname has been randomized."""
    try:
        import socket
        hostname = socket.gethostname()
        # Randomized hostnames start with DESKTOP- and have 7 random chars
        if hostname.startswith("DESKTOP-") and len(hostname) == 15:
            return {"layer": 12, "name": f"Hostname: {hostname}", "status": "PASS"}
        return {"layer": 12, "name": f"Hostname: {hostname}", "status": "FAIL",
                "detail": "Does not match randomized pattern"}
    except Exception as e:
        return {"layer": 12, "name": "Hostname check", "status": "FAIL",
                "detail": str(e)}

def check_telemetry():
    """Layer 13: Verify Windows telemetry is disabled."""
    try:
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Policies\Microsoft\Windows\DataCollection",
            0, winreg.KEY_READ
        )
        val, _ = winreg.QueryValueEx(key, "AllowTelemetry")
        if val == 0:
            return {"layer": 13, "name": "Telemetry disabled", "status": "PASS"}
        return {"layer": 13, "name": "Telemetry disabled", "status": "FAIL",
                "detail": f"AllowTelemetry={val}"}
    except FileNotFoundError:
        return {"layer": 13, "name": "Telemetry disabled", "status": "FAIL",
                "detail": "Registry key not found"}
    except Exception as e:
        return {"layer": 13, "name": "Telemetry disabled", "status": "FAIL",
                "detail": str(e)}

def check_firewall():
    """Layer 14: Verify kill switch firewall rules are active."""
    try:
        result = subprocess.run(
            ["powershell", "-Command",
             "Get-NetFirewallRule -DisplayName 'AnonShield*' "
             "| Where-Object {$_.Enabled -eq 'True'} "
             "| Measure-Object | Select-Object -ExpandProperty Count"],
            capture_output=True, text=True, timeout=10
        )
        count = int(result.stdout.strip() or "0")
        if count >= 2:  # At least allow + block rules
            return {"layer": 14, "name": "Firewall kill switch", "status": "PASS"}
        return {"layer": 14, "name": "Firewall kill switch", "status": "FAIL",
                "detail": f"Only {count} rules found"}
    except Exception as e:
        return {"layer": 14, "name": "Firewall kill switch", "status": "FAIL",
                "detail": str(e)}

def check_ip_rotation(control_password):
    """Layer 15: Verify IP rotation is functional by checking Tor control port."""
    try:
        with Controller.from_port(port=9051) as ctrl:
            ctrl.authenticate(password=control_password)
            # If we can connect and authenticate, rotation is available
            return {"layer": 15, "name": "IP rotation active", "status": "PASS"}
    except Exception as e:
        return {"layer": 15, "name": "IP rotation active", "status": "FAIL",
                "detail": str(e)}

def check_forensic_traces():
    """Layer 16: Check for common forensic traces (pre-cleanup check)."""
    import glob
    traces = []
    # Check for recent files
    recent = glob.glob(os.path.expandvars(r"%APPDATA%\Microsoft\Windows\Recent\*"))
    if len(recent) > 10:
        traces.append(f"{len(recent)} recent files")
    # This layer is verified fully on teardown
    if not traces:
        return {"layer": 16, "name": "Forensic cleanup ready", "status": "PASS"}
    return {"layer": 16, "name": "Forensic cleanup ready", "status": "PASS",
            "detail": f"Will clean: {', '.join(traces)}"}

How Does the Verification Engine Work?

Each check function targets a specific layer. Network checks use stem to talk to the Tor control port and requests to confirm traffic exits through Tor. Hardware checks use PowerShell commands and Windows registry lookups to confirm each device or service is in the expected state.

Every check returns a dictionary with the layer number, name, and pass/fail status. The run_all_checks() function collects all 16 results and prints a formatted report. A perfect score is 16/16.

Why Does Layer 16 Always Pass?

Layer 16 is forensic cleanup, which only runs during teardown when you press 'q'. During active Anon Mode, the check confirms the cleanup function is ready and notes how many traces it will clean. The actual cleanup is verified by the teardown process itself.

Test Everything as Python Scripts

Before compiling into an exe, you need to confirm that both modules work correctly when called from the main dashboard. This is your last chance to catch bugs before packaging.

  • Open a terminal as Administrator.
  • Navigate to your project folder and run the main script.
python anonshield.py

What Should I See?

The dashboard should appear showing all 16 layers activating one by one. Once activation completes, you should see the input prompt waiting for your commands.

  • Press v to run the verification engine.

The verification report should print with pass/fail for each of the 16 layers. Aim for 16/16. If any layers show FAIL, the detail message tells you exactly what went wrong.

  • Press s and provide the path to a test image (a photo taken with your phone works best).

The scrubber should report what metadata it found and removed. Open the image in a file properties viewer afterward to confirm the EXIF data is gone.

  • Press r to manually rotate your IP address.

You should see a new exit IP printed to the dashboard. This confirms the NEWNYM signal is working through the Tor control port.

  • Press q to deactivate Anon Mode and run the full teardown.

The teardown should reverse all 16 layers, run forensic cleanup, and print "Anon Mode DEACTIVATED. Zero traces remain." Confirm your internet connection works normally after deactivation.

Some Layers Showing FAIL?

Check the detail message next to each failed layer. Common issues include PowerShell execution policy blocking commands, missing registry keys on certain Windows editions, or Tor taking longer than expected to bootstrap. Hardware checks like Bluetooth may fail if your machine does not have a Bluetooth adapter. Help me debug my verification failures.

Both modules are working and your 16-layer system is fully tested as Python scripts. Next up, you will compile everything into a single portable exe that runs on any Windows machine without Python installed.

Compile Everything into a Single Portable Exe

Your 16-layer anonymity tool works perfectly as a Python script. The problem is that every machine you want to run it on needs Python installed, plus all the pip packages, plus the correct folder structure. That defeats the purpose of a portable privacy tool.

In this step, you will use PyInstaller to compile everything into a single anonshield.exe file. The Python runtime, all libraries, Tor, lyrebird, tun2socks, Wintun, and your torrc all get packed inside one executable. Double-click it on any Windows machine and it just works.

In This Step, Get Ready To:
  • Write a PyInstaller build script that bundles all binaries and config files into a single exe.
  • Update your code to resolve file paths correctly whether running as a script or a compiled exe.
  • Build, test, and confirm the exe runs on a machine with zero Python dependencies.
Write the PyInstaller Build Script

PyInstaller's --onefile mode bundles the Python interpreter, all imported modules, and any files you specify with --add-data into a single executable. When someone launches the exe, PyInstaller extracts everything to a temporary directory and runs your script from there.

Your build script needs to tell PyInstaller exactly which external binaries to bundle. The scrub.py and verify.py modules are imported by anonshield.py, so PyInstaller detects and includes them automatically. No separate --add-data flags are needed for those.

  • Create a new file called build.bat in your project root.
  • Add the following PyInstaller command.
@echo off
echo Building anonshield.exe...

pyinstaller --onefile ^
    --name anonshield ^
    --add-data "tor;tor" ^
    --add-data "tun2socks-windows-amd64.exe;." ^
    --add-data "wintun.dll;." ^
    --add-data "torrc;." ^
    --uac-admin ^
    --icon=shield.ico ^
    anonshield.py

echo.
echo Build complete! Output: dist\anonshield.exe
pause

What does each flag do?

  • --onefile produces a single exe instead of a folder of files.
  • --add-data "tor;tor" bundles the entire tor\ folder (containing tor.exe, lyrebird.exe, and GeoIP data) into a tor subdirectory inside the exe. On Windows, the separator is a semicolon.
  • --uac-admin embeds a UAC manifest so Windows prompts for admin privileges on launch. This is required for firewall rules, routing table changes, and hardware modifications.
  • --icon=shield.ico sets the exe icon. If you do not have a .ico file, remove this flag.

Why no --add-data for scrub.py and verify.py?

PyInstaller automatically traces all import statements in your entry script. Since anonshield.py imports scrub and verify as Python modules, they get bundled into the exe automatically. The --add-data flag is only needed for non-Python files like executables, DLLs, and config files.

Handle Bundled File Paths in the Code

When PyInstaller runs in --onefile mode, it extracts all bundled files to a temporary directory at runtime. The path to this directory is stored in sys._MEIPASS. When running as a normal .py script, that attribute does not exist. Your code needs a helper function that checks which mode it is running in and returns the correct base path.

  • Open anonshield.py and add the following helper function near the top of the file, after your imports.
import sys
import os

def resource_path(relative_path):
    """Resolve path to bundled resource, works for both .py and .exe."""
    if hasattr(sys, '_MEIPASS'):
        # Running as a PyInstaller exe - files extracted to temp dir
        return os.path.join(sys._MEIPASS, relative_path)
    # Running as a normal Python script - files are next to the script
    return os.path.join(os.path.dirname(__file__), relative_path)

What is sys._MEIPASS?

When you double-click a PyInstaller --onefile exe, it unpacks all bundled files to a temporary directory (something like C:\Users\you\AppData\Local\Temp\_MEIxxxxxx). Python stores that path in sys._MEIPASS. Your teardown function already cleans up this directory on exit.

  • Now update every hardcoded path in anonshield.py to use the resource_path() helper. Here are the paths that need updating.
# Tor binary and config
TOR_EXE = resource_path(os.path.join("tor", "tor.exe"))
TORRC = resource_path("torrc")

# lyrebird (obfs4 pluggable transport) - inside the tor folder
LYREBIRD_EXE = resource_path(os.path.join("tor", "pluggable_transports", "lyrebird.exe"))

# GeoIP data - inside the tor folder
GEOIP = resource_path(os.path.join("tor", "data", "tor", "geoip"))
GEOIP6 = resource_path(os.path.join("tor", "data", "tor", "geoip6"))

# tun2socks and Wintun - bundled at the root level
TUN2SOCKS_EXE = resource_path("tun2socks-windows-amd64.exe")
WINTUN_DLL = resource_path("wintun.dll")

Pro tip: test both modes

After updating the paths, run python anonshield.py one more time to confirm everything still works in script mode. The resource_path() function falls back to the script directory when sys._MEIPASS does not exist, so nothing should break.

There is one more path problem to solve. Your torrc file contains a hardcoded path to lyrebird.exe in the ClientTransportPlugin line. Since torrc is a static config file, you cannot use Python variables inside it. The solution is to write the torrc dynamically at launch time, substituting the resolved paths.

  • Update your torrc template to use placeholder strings instead of hardcoded paths.
SocksPort 9050 IsolateSOCKSAuth
ControlPort 9051
HashedControlPassword YOUR_HASHED_PASSWORD_HERE

UseBridges 1
ClientTransportPlugin obfs4 exec __LYREBIRD_PATH__
Bridge obfs4 YOUR_BRIDGE_LINE_1
Bridge obfs4 YOUR_BRIDGE_LINE_2

GeoIPFile __GEOIP_PATH__
GeoIPv6File __GEOIP6_PATH__

SafeLogging 1
Log notice stdout
  • Add the following function to anonshield.py that reads the template, swaps placeholders with resolved paths, and writes the result to a temp file.
def write_torrc():
    """Generate torrc with resolved paths for bundled binaries."""
    lyrebird = resource_path(os.path.join("tor", "pluggable_transports", "lyrebird.exe"))
    geoip = resource_path(os.path.join("tor", "data", "tor", "geoip"))
    geoip6 = resource_path(os.path.join("tor", "data", "tor", "geoip6"))

    # Read the template torrc
    torrc_template = resource_path("torrc")
    with open(torrc_template, 'r') as f:
        content = f.read()

    # Replace placeholder paths with resolved absolute paths
    content = content.replace("__LYREBIRD_PATH__", lyrebird)
    content = content.replace("__GEOIP_PATH__", geoip)
    content = content.replace("__GEOIP6_PATH__", geoip6)

    # Write the resolved torrc to a temp location
    runtime_torrc = os.path.join(os.environ['TEMP'], "anonshield_torrc")
    with open(runtime_torrc, 'w') as f:
        f.write(content)

    return runtime_torrc

What does this function do?

The bundled torrc uses placeholder strings like __LYREBIRD_PATH__ instead of hardcoded paths. At runtime, this function swaps those placeholders with the actual extracted paths from sys._MEIPASS and writes the resolved config to a temp file. The teardown function already cleans up temp files on exit.

  • Update the Tor launch code in anonshield.py to call write_torrc() and pass the generated config to tor.exe -f.
# In your Tor launch function, replace the static torrc path:
runtime_torrc = write_torrc()
tor_process = subprocess.Popen(
    [TOR_EXE, "-f", runtime_torrc],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)
Build, Test, and Distribute

Everything is wired up. Time to compile the exe and verify it works as a standalone binary.

  • Open a terminal in your project root directory.
  • Run the build script.
build.bat

PyInstaller analyzes your imports, bundles the Python runtime, all libraries, and the files you specified with --add-data. This takes a few minutes. When it finishes, the output is dist\anonshield.exe.

What should I see?

The build output scrolls through hundreds of lines as PyInstaller traces dependencies. At the end, you should see a message confirming the exe was created in the dist\ folder. The file size is approximately 30-50 MB. That includes the Python runtime (~15 MB), Tor (~15 MB), tun2socks (~4 MB), Wintun (~0.5 MB), and all Python libraries (~5 MB).

  • Navigate to the dist\ folder and double-click anonshield.exe.
  • Accept the UAC admin prompt when Windows asks for elevated privileges.
  • Confirm the live dashboard appears and all 16 layers activate.
  • Press v to run the verification engine and confirm 16/16 pass.
  • Press r to manually rotate your IP and confirm a new exit IP appears.
  • Press q to deactivate and confirm the teardown completes with forensic cleanup.

Exe crashes on launch or Tor fails to start?

The most common issue is incorrect path resolution. Run the exe from a terminal instead of double-clicking so you can see error output.

  • If you see "FileNotFoundError" for tor.exe or tun2socks, check that your --add-data paths in build.bat match your actual folder structure.
  • If Tor fails with a torrc error, verify that write_torrc() is being called before the Tor launch and that the placeholder strings match exactly.
  • If the exe is missing wintun.dll, confirm the DLL is in your project root (not inside a subfolder) and the --add-data "wintun.dll;." flag uses a semicolon separator.

Still stuck? Help me debug my PyInstaller build.

For the ultimate test, copy anonshield.exe to a USB drive and run it on a different Windows machine. One that has no Python installed. If the dashboard launches and all 16 layers activate, you have a truly portable zero-dependency anonymity tool.

Your 16-layer anonymity tool is now a single file. No installation. No dependencies. No terminal. Double-click to go dark. Press q to come back.

Secret mission

Add a Country Selector for Exit Nodes

Your anonymizer picks a random exit node from anywhere in the world. But what if you need to appear to be in a specific country? Add a country selector so you can choose exactly where your traffic exits the Tor network.

Before you go

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your exe for future use, delete everything, or set it up for always-on protection.

Resources you used:

  • anonshield.exe (compiled portable executable).
  • Source files: anonshield.py, scrub.py, verify.py, torrc, build.bat.
  • Tor Expert Bundle folder (tor\ with tor.exe, lyrebird.exe, GeoIP data).
  • tun2socks-windows-amd64.exe and wintun.dll.
  • Python packages installed via pip: stem, PySocks, requests[socks], Pillow, pyinstaller.
  • PyInstaller build artifacts (dist\, build\, anonshield.spec).
  • Firewall backup file (firewall_backup.wfw).

Keep everything running

No action needed. Choose this if you plan to use the tool again soon.

  • Copy anonshield.exe to a USB drive or a folder you can find easily. The exe is fully portable and needs no installation.
  • Keep your project folder with the source files if you want to modify or rebuild the exe later.
  • When Anon Mode is not active, no background processes are running and no system settings are changed. The tool only modifies your system while it is actively running.

Pause - I'll come back to this later

Free up disk space from build artifacts while keeping the important files. You can rebuild the exe anytime.

  • Keep anonshield.exe if you already built it. This is the only file you need to run the tool.
  • Delete the build\ and dist\ folders created by PyInstaller. These are intermediate build artifacts.
  • Keep your source files (anonshield.py, scrub.py, verify.py, torrc, build.bat) and the tor\ folder so you can rebuild later.

Delete - I don't want to use this again

Remove all project files and uninstall the Python packages. No system changes persist after a clean exit.

  • Make sure Anon Mode is deactivated. If you exited cleanly with 'q', all system settings have already been restored automatically.
  • Delete your entire project folder (e.g., C:\anonshield\). This removes all source files, the Tor Expert Bundle, tun2socks, wintun.dll, build artifacts, and the compiled exe.
  • Uninstall the Python packages by running: pip uninstall stem PySocks requests Pillow pyinstaller -y.
  • If the script crashed without running the teardown, restore your system manually using the commands below.

Script crashed without cleanup?

If the tool crashed or was force-closed without pressing 'q', some system settings may still be modified. Run these commands in an elevated PowerShell to restore everything:

  • Restore firewall: netsh advfirewall import C:\anonshield\firewall_backup.wfw.
  • Fix routes: route delete 0.0.0.0 then route add 0.0.0.0 mask 0.0.0.0 YOUR_GATEWAY.
  • Re-enable IPv6: Enable-NetAdapterBinding -Name '*' -ComponentID ms_tcpip6.
  • Re-enable Teredo: netsh interface teredo set state default.
  • Re-enable microphones: Get-PnpDevice -Class AudioEndpoint -FriendlyName '*Microphone*' | Enable-PnpDevice -Confirm:$false.
  • Restore SSDP and UPnP services: Set-Service SSDPSRV -StartupType Manual; Set-Service upnphost -StartupType Manual.
  • Kill leftover processes: end tor.exe and tun2socks-windows-amd64.exe from Task Manager.

Mission accomplished

That's a Wrap

Nice work! You just built a 16-layer system-wide traffic anonymizer and compiled it into a single portable exe. Double-click to disappear. Press 'q' to come back. Zero installation, zero dependencies, zero traces.

Here's what you learned:

  • Orchestrated Tor with obfs4 bridges, tun2socks, and Wintun to route all system traffic through the Tor network while making your Tor usage invisible to your ISP.
  • Hardened a Windows machine at the OS level by killing IPv6, Bluetooth, microphones, Location Services, network broadcasts, telemetry, and randomizing your MAC address and hostname.
  • Built a firewall kill switch, automatic IP rotation, metadata scrubber, and forensic cleanup that wipes all session traces on exit, then compiled everything into a single portable exe with PyInstaller.
  • Added a country selector so you can choose which country your Tor exit node is in.

Ready to quiz yourself?