Skip to content

Instantly share code, notes, and snippets.

@ShutovKS
Last active February 2, 2026 23:01
Show Gist options
  • Select an option

  • Save ShutovKS/885869207544dca895c1f1a7c7bf2ec1 to your computer and use it in GitHub Desktop.

Select an option

Save ShutovKS/885869207544dca895c1f1a7c7bf2ec1 to your computer and use it in GitHub Desktop.
WireGuard DPI Bypass (Windows) — pre-UDP before handshake

WireGuard DPI Bypass (Windows) — Pre-UDP helper (no tunnel control)

Иногда провайдер/DPI распознаёт WireGuard по первому handshake и начинает “душить” соединение.
Обход: перед включением туннеля отправить любой UDP-пакет на Endpoint с того же локального порта, который использует WireGuard (ListenPort).

Этот репозиторий/гист содержит только helper для pre-UDP.
Он НЕ останавливает/устанавливает/запускает туннель — вы включаете туннель в WireGuard GUI как обычно.


Файлы

  • wireguard-client.conf — пример клиентского конфига (важно: фиксированный ListenPort)
  • wg-preudp.ps1 — PowerShell helper: читает ListenPort и Endpoint из .conf и отправляет pre-UDP
  • wg-preudp-click.vbs — запуск helper по клику (без окна консоли), ищет wg-preudp.ps1 в Documents

1) Конфиг WireGuard (обязательно фиксируем ListenPort)

Пример wireguard-client.conf:

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.8.0.3/32

# IMPORTANT: fixed local UDP port (needed for DPI bypass)
ListenPort = 57932

DNS = 1.1.1.1, 1.0.0.1

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <CLIENT_PSK>
Endpoint = <SERVER_PUBLIC_IP_OR_DOMAIN>:51820
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25

Важно:

  • ListenPort должен быть постоянным.
  • Endpoint должен быть в виде host:port (или [ipv6]:port).

2) Установка

Рекомендуемое размещение:

  • wg-preudp.ps1C:\Users\<USER>\Documents\wg-preudp.ps1
  • ваш WireGuard .conf (туннель) → на Desktop или Documents (helper сам найдёт)
  • wg-preudp-click.vbs → на Desktop (чтобы запускать кликом)

Helper выберет самый свежий .conf на Desktop, если там нет — в Documents.


3) Использование

  1. В WireGuard GUI выключите (Deactivate) туннель (важно: ListenPort должен быть свободен)
  2. Двойной клик по wg-preudp-click.vbs (отправится pre-UDP)
  3. Сразу включите (Activate) туннель в WireGuard GUI

Скрипты

wg-preudp.ps1

param(
  [string]$ConfigPath = ""   # optional: if not set -> uses newest *.conf from Desktop, else Documents
)

function Find-ConfigFallback {
  $desktop = [Environment]::GetFolderPath("Desktop")
  $docs    = [Environment]::GetFolderPath("MyDocuments")

  foreach ($dir in @($desktop, $docs)) {
    if (Test-Path $dir) {
      $f = Get-ChildItem -Path $dir -Filter "*.conf" -File -ErrorAction SilentlyContinue |
           Sort-Object LastWriteTime -Descending |
           Select-Object -First 1
      if ($f) { return $f.FullName }
    }
  }
  return $null
}

function Get-ConfigValue([string]$path, [string]$key) {
  $regex = "^\s*$([Regex]::Escape($key))\s*=\s*([^#]+?)\s*(?:#.*)?$"
  $m = Select-String -Path $path -Pattern $regex -ErrorAction SilentlyContinue | Select-Object -First 1
  if ($m -and $m.Matches.Count -gt 0) { return $m.Matches[0].Groups[1].Value.Trim() }
  return $null
}

function Parse-Endpoint([string]$endpointLine) {
  if ($endpointLine -match '^\[(.+)\]:(\d+)$') {
    return @{ Host = $Matches[1]; Port = [int]$Matches[2] }
  }
  if ($endpointLine -match '^(.+):(\d+)$') {
    return @{ Host = $Matches[1]; Port = [int]$Matches[2] }
  }
  throw "Cannot parse Endpoint: $endpointLine"
}

try {
  # Resolve config path
  if ($ConfigPath -and (Test-Path $ConfigPath)) {
    # ok
  } else {
    $fallback = Find-ConfigFallback
    if (-not $fallback) { throw "No .conf found on Desktop or Documents. Provide -ConfigPath." }
    $ConfigPath = $fallback
  }

  $tunnelName = [IO.Path]::GetFileNameWithoutExtension($ConfigPath)

  # Read ListenPort + Endpoint
  $listenPortStr = Get-ConfigValue $ConfigPath "ListenPort"
  if (-not $listenPortStr) { throw "ListenPort missing in config" }
  $listenPort = [int]$listenPortStr

  $endpointStr = Get-ConfigValue $ConfigPath "Endpoint"
  if (-not $endpointStr) { throw "Endpoint missing in config" }
  $ep = Parse-Endpoint $endpointStr
  $endpointHost = $ep.Host
  $endpointPort = $ep.Port

  # Resolve IPv4
  $ip = ([System.Net.Dns]::GetHostAddresses($endpointHost) |
        Where-Object { $_.AddressFamily -eq "InterNetwork" } |
        Select-Object -First 1)
  if (-not $ip) { throw "IPv4 resolve failed for $endpointHost" }

  # pre-UDP: bind to ListenPort and send 2 bytes
  $endp = New-Object System.Net.IPEndPoint($ip, $endpointPort)
  $sock = New-Object System.Net.Sockets.UdpClient($listenPort)
  [void]$sock.Send([Text.Encoding]::ASCII.GetBytes(":)"), 2, $endp)
  $sock.Close()

  Write-Host "OK: pre-UDP sent"
  Write-Host "Tunnel=$tunnelName"
  Write-Host "Config=$ConfigPath"
  Write-Host "ListenPort=$listenPort Endpoint=$endpointHost`:$endpointPort"
} catch {
  Write-Host "ERROR: $($_.Exception.Message)"
  Write-Host "Hint: Tunnel must be OFF (ListenPort must be free) before running pre-UDP."
}

wg-preudp-click.vbs

Option Explicit

Dim fso, shell, docsDir, ps1Path, cmd
Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")

docsDir = shell.SpecialFolders("MyDocuments")
ps1Path = docsDir & "\wg-preudp.ps1"

If Not fso.FileExists(ps1Path) Then
  shell.Popup "Not found: " & ps1Path, 5, "WG pre-UDP", 48
  WScript.Quit 1
End If

cmd = "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File """ & ps1Path & """"
shell.Run cmd, 0, False

Notes

  • Если после запуска helper вы слишком долго не включили туннель — просто повторите: Deactivate → helper → Activate.
  • Если у вас несколько .conf — helper выберет самый свежий файл. При необходимости можно жёстко указать -ConfigPath.
  • Не публикуйте PrivateKey / PresharedKey публично.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment