A Windows utility that removes WDA_EXCLUDEFROMCAPTURE display-affinity protection from application windows, allowing them to be recorded normally by OBS, the Snipping Tool, and any other screen-capture software.
Legal notice: Read DISCLAIMER.md before use. Only use this tool on windows and processes you own or have explicit permission to capture.
Pre-built releases are on the Releases page — no Rust or build tools required.
| Download | What's inside | Best for |
|---|---|---|
capture-bypass-setup-*.exe |
Windows installer with prompts for shortcuts, startup, and install path | Most users |
capture-bypass-gui-*.zip |
Portable zip — capture_bypass_gui.exe + DLLs, just unzip and run |
No-install preference |
capture-bypass-python-*.zip |
app.py + all binaries pre-arranged |
Python frontend users |
The installer lets you optionally add a desktop shortcut, Start Menu entry, and a Launch at Windows startup entry (UAC prompt will appear at each login since the app requires Administrator rights).
Note: Windows Defender or other AV software may flag the payload DLLs due to the DLL injection technique. This is a false positive — see DISCLAIMER.md.
Windows exposes SetWindowDisplayAffinity which lets a process mark its own windows as protected from capture. Protected windows appear black/blank in screenshots and screen recordings.
Because the API only allows a process to modify its own windows, bypassing it requires running code inside the target process. This tool does that via classic LoadLibrary DLL injection:
OpenProcess— open a handle to the target with VM + thread rights.VirtualAllocEx/WriteProcessMemory— write the payload DLL path into the target's address space.CreateRemoteThread(LoadLibraryA)— start a thread inside the target that loads the DLL.- The DLL's
DllMainspawns a worker thread that callsSetWindowDisplayAffinity(hwnd, WDA_NONE)on every window owned by that process, clearing the protection flag.
capture-bypass/
├── core/ Shared Rust library — inject_dll()
├── cli/ Rust CLI binary — called by the Python frontend
├── gui/ Rust/egui GUI frontend (feature-parity with app.py)
├── payload_dll/ One-shot payload DLL (strips once and exits)
├── payload_dll_persistent/ Persistent payload DLL (loops every 500 ms)
├── stress_tester/ Rust stress-test window (port of test_protection.py)
├── installer/
│ └── capture-bypass.iss Inno Setup installer script
├── frontend/
│ └── app.py Python/customtkinter GUI frontend
└── test_protection.py Python stress-test window (original)
| Dependency | Notes |
|---|---|
| Rust + Cargo | Stable toolchain, x86_64-pc-windows-msvc target |
| Python 3.10+ | For the frontend |
| customtkinter | pip install customtkinter |
| pystray + Pillow | pip install pystray pillow (system tray support) |
| Windows 10 2004+ | WDA_EXCLUDEFROMCAPTURE requires build 19041+ |
| Administrator privileges | Required by OpenProcess on other processes |
# Clone the repo
git clone https://github.com/Londopy/capture-bypass.git
cd capture-bypass
# Build all Rust crates for 64-bit (GUI, CLI, stress tester, and both payload DLLs)
cargo build --release -p payload_dll -p payload_dll_persistent -p cli -p gui -p stress_tester
# Install Python dependencies (only needed for the Python frontend)
pip install customtkinter pystray pillowThe frontend auto-detects 32-bit target processes (shown with an orange 32 badge) and routes injection through the x86 binaries when they are present. If you skip this step, 32-bit targets will fail gracefully with a status-bar error message.
# Add the 32-bit Windows MSVC target
rustup target add i686-pc-windows-msvc
# Build all Rust crates for 32-bit
cargo build --release --target i686-pc-windows-msvc -p payload_dll -p payload_dll_persistent -p cliThe x86 binaries land in target/i686-pc-windows-msvc/release/. The frontend
picks them up automatically — no configuration needed.
# Python frontend
python frontend/app.py
# Rust GUI (no Python required)
target\release\capture_bypass_gui.exe- The app lists all visible, titled windows with their PID, process name, and live protection status.
- Use the Filter bar to search by process name or window title.
- Tick Protected only to hide unprotected windows.
- Click Strip Protection on any row, or ⚡ Strip All Protected to batch-clear everything at once.
- Toggle Mode between One-shot (strips once) and Persistent (re-strips every 500 ms — for apps that fight back).
- Enable 🤖 Auto-inject to have the app silently strip any newly protected window in the background; close to tray so it keeps running.
- Enable 🚀 Start with Windows to add a startup registry entry so the app launches automatically at login.
- Click 📖 Help in the header to open the built-in wiki — covers injection modes, browser handling, 32-bit support, troubleshooting, and more.
The cli.exe and payload_dll*.dll binaries must be present in target/release/ (and optionally target/i686-pc-windows-msvc/release/) relative to the repo root.
Built-in docs: click 📖 Help in the app header for a full wiki covering injection modes, browser handling, 32-bit support, and troubleshooting — no browser needed.
Injecting Chrome, Edge, Firefox, Brave, Opera, Vivaldi, or Thorium automatically enumerates and injects all child processes, ensuring renderer processes that own the DRM video windows are covered.
Two stress-test windows are available — pick whichever suits your setup:
| Option | How to run | Requires |
|---|---|---|
| Rust (recommended) | target\release\stress_tester.exe |
Just build the crate |
| Python (original) | python test_protection.py |
Python 3.10+ |
Both are functionally identical. The Rust binary is included in the capture-bypass-gui-*.zip release bundle, so you can test without any build step.
# Build and run the Rust stress tester
cargo build --release -p stress_tester
target\release\stress_tester.exe
# Or run the Python original
python test_protection.pyThe window marks itself as WDA_EXCLUDEFROMCAPTURE on launch and polls GetWindowDisplayAffinity
every 100 ms to show the live state. Use it to:
- Verify one-shot injection — click Strip Protection in the main app; the window should flip green.
- Stress-test persistent mode — enable Fight Mode in the test window (adjustable 50–2000 ms re-apply rate) and inject the persistent DLL. The strip counter should climb while the fight counter stays ahead — until persistent mode catches up.
capture-bypass ships two frontends. Both use the same injection core. Pick whichever suits your workflow.
| Frontend | Language | How to run | Notes |
|---|---|---|---|
| Native GUI | Rust / egui | target/release/capture_bypass_gui.exe |
No dependencies, single .exe |
| Python GUI | Python / customtkinter | python frontend/app.py |
Requires Python 3.10+ and pip deps |
Both frontends have feature-parity: window table with live protection badges, filter bar, one-shot / persistent mode toggle, batch strip, auto-inject, system tray, launch-at-startup toggle, and built-in help.
cargo build --release -p gui
# Produces: target/release/capture_bypass_gui.exe
# The payload DLLs must be in the same directory — build them first:
cargo build --release -p payload_dll -p payload_dll_persistentSee CONTRIBUTING.md.
MIT — see LICENSE.
See DISCLAIMER.md.