Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:

- name: Install MinGW-w64
run: >
sudo rm /var/lib/man-db/auto-update &&
sudo rm -f /var/lib/man-db/auto-update &&
sudo DEBIAN_FRONTEND=noninteractive XZ_DEFAULTS="-T0" XZ_OPT="-T0" eatmydata
apt install -y --no-install-recommends gcc-mingw-w64

Expand Down Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Prepare x86_64 directory
run: |
mkdir goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}
cp src/goodbyedpi.exe ${{ env.WINDIVERT_BASENAME }}/x64/*.{dll,sys} goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}
cp src/goodbyedpi.exe src/goodbyedpi-tui.exe ${{ env.WINDIVERT_BASENAME }}/x64/*.{dll,sys} goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}

- name: Upload output file x86_64
uses: actions/upload-artifact@v4
Expand All @@ -72,7 +72,7 @@ jobs:
- name: Prepare x86 directory
run: |
mkdir goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}
cp src/goodbyedpi.exe ${{ env.WINDIVERT_BASENAME }}/x86/*.{dll,sys} goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}
cp src/goodbyedpi.exe src/goodbyedpi-tui.exe ${{ env.WINDIVERT_BASENAME }}/x86/*.{dll,sys} goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}

- name: Upload output file x86
uses: actions/upload-artifact@v4
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
*.o
*.exe

# signing material and local build output
*.pfx
*.p12
*.cer
dist/
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,35 @@ Modern modesets (more stable, more compatible, faster):

Note: combination of --wrong-seq and --wrong-chksum generates two different fake packets.
```

## Presets (Vietnam)

```
goodbyedpi.exe --preset <name> [extra options]
goodbyedpi.exe --list-presets
```

| Preset | Expands to |
|-----------|------------------------------------------------------------------------|
| `viettel` | `-9 --dns-addr 1.1.1.1` |
| `fpt` | `-5 --dns-addr 8.8.8.8` |
| `vnpt` | `-6 --dns-addr 1.1.1.1` |
| `steam` | `-f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload=1200 --frag-by-sni --blacklist-builtin steam --dns-addr 1.1.1.1` |

The `steam` preset only touches connections to Steam domains (`--blacklist-builtin steam`), the rest of your traffic is left alone. The ISP presets apply to all traffic.

Options placed after `--preset` are added on top; your own `--dns-addr` replaces the preset's one.

**These are starting points, not guarantees.** Which mode works depends on your ISP, modem/router and changes over time. If a preset does not work, try another one (the ISP presets intentionally use different fake-packet modes), and use [blockcheck](https://github.com/ValdikSS/blockcheck) to find what works on your line. Presets only redirect plain UDP DNS: if your ISP intercepts DNS to 1.1.1.1/8.8.8.8 as well, use DNS-over-HTTPS in your browser. Steam game traffic over UDP is not handled by this tool.

### TUI launcher

`goodbyedpi-tui.exe` (next to `goodbyedpi.exe`) is a keyboard-driven menu on top of the presets: choose a preset, DNS server, QUIC blocking and verbose DNS log, see the resulting command line, and press Start. Up/Down moves, Left/Right changes a value, Space/Enter selects, Q quits. Ctrl+C stops goodbyedpi and returns to the menu.

With **Minimize to tray** turned on, Start runs goodbyedpi.exe hidden, closes the console once it reports it is running, and leaves an icon in the notification area. Right-click the icon and choose *Exit and stop GoodbyeDPI* to stop it. goodbyedpi.exe is also stopped if the launcher is killed. If it starts but later dies on its own, the icon shows a notification. Only one tray instance can run at a time. When goodbyedpi stops (Exit from the tray, or the foreground run ending) the launcher also stops and removes the WinDivert driver service, so `WinDivert64.sys` is not left locked ("file in use"); WinDivert loads it again on the next start. If another program is still using WinDivert, the driver is left alone. To skip the menu, for example from a shortcut: `goodbyedpi-tui.exe --tray steam` (preset name optional).

Note: if the tray icon is missing, check the hidden-icons arrow (^) next to the clock. Requires Windows 10 or newer and administrator rights (it asks for elevation, like goodbyedpi.exe).

## How to check
To check if your ISP's DPI could be circumvented, first make sure that your provider does not poison DNS answers by enabling "Secure DNS (DNS over HTTPS)" option in your browser.

Expand Down Expand Up @@ -151,6 +180,17 @@ Check examples in `service_install_russia_blacklist.cmd`, `service_install_russi

Modify them according to your own needs.

# Signing the executables for your own machine

`tools/sign-selfsigned.ps1` signs `goodbyedpi.exe` and `goodbyedpi-tui.exe` with a self-signed certificate (created in your user store, private key never exported). `tools/trust-selfsigned.ps1` (run as Administrator) makes your machine trust it; `-Remove` undoes that.

```powershell
.\tools\sign-selfsigned.ps1 -Folder .\dist
.\tools\trust-selfsigned.ps1 # elevated PowerShell
```

A self-signed certificate is only trusted where you install it, so it does not help other people who download the files. SmartScreen reputation and Smart App Control are decided by Microsoft from trusted publishers, which a self-signed certificate is not. For files you downloaded yourself, `Unblock-File .\dist\*` removes the "downloaded from the internet" mark that triggers SmartScreen.

# Known issues

* Horribly outdated Windows 7 installations are not able to load WinDivert driver due to missing support for SHA256 digital signatures. Install KB3033929 [x86](https://www.microsoft.com/en-us/download/details.aspx?id=46078)/[x64](https://www.microsoft.com/en-us/download/details.aspx?id=46148), or better, update the whole system using Windows Update.
Expand Down
15 changes: 12 additions & 3 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ endif

.PHONY: default all clean

default: $(TARGET)
TUI_TARGET = goodbyedpi-tui.exe

default: $(TARGET) $(TUI_TARGET)
all: default

OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c utils/*.c)) goodbyedpi-rc.o
Expand All @@ -55,6 +57,13 @@ goodbyedpi-rc.o:
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -s -o $@

tui/tui-rc.o: tui/tui-rc.rc tui/tui.exe.manifest
$(CCWINDRES) -I tui -I . tui/tui-rc.rc tui/tui-rc.o

# The TUI launcher does not use WinDivert, it only starts goodbyedpi.exe.
$(TUI_TARGET): tui/tui.c tui/tui-rc.o
$(CC) -std=c99 -pie -fPIE -O2 -Wall -Wextra -Wpedantic tui/tui.c tui/tui-rc.o $(LDFLAGS) -l:libssp.a -luser32 -lshell32 -ladvapi32 -s -o $@

clean:
-rm -f *.o utils/*.o
-rm -f $(TARGET)
-rm -f *.o utils/*.o tui/*.o
-rm -f $(TARGET) $(TUI_TARGET)
32 changes: 32 additions & 0 deletions src/blackwhitelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,38 @@ int blackwhitelist_load_list(const char *filename) {
return TRUE;
}

/* Subdomains match automatically, e.g. "steamstatic.com" covers cdn.steamstatic.com */
static const char *const builtin_steam[] = {
"steampowered.com",
"steamcommunity.com",
"steamstatic.com",
"steamcontent.com",
"steamusercontent.com",
"steamserver.net",
"steam-chat.com",
"steamgames.com",
"valvesoftware.com",
"steamcdn-a.akamaihd.net",
NULL
};

int blackwhitelist_load_builtin(const char *name) {
const char *const *hosts;
int cnt = 0;

if (strcmp(name, "steam") == 0)
hosts = builtin_steam;
else
return FALSE;

for (; *hosts; hosts++) {
if (add_hostname(*hosts))
cnt++;
}
printf("Loaded %d hosts from built-in list '%s'\n", cnt, name);
return TRUE;
}

int blackwhitelist_check_hostname(const char *host_addr, size_t host_len) {
char current_host[HOST_MAXLEN + 1];
char *tokenized_host = NULL;
Expand Down
1 change: 1 addition & 0 deletions src/blackwhitelist.h
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
int blackwhitelist_load_list(const char *filename);
int blackwhitelist_load_builtin(const char *name);
int blackwhitelist_check_hostname(const char *host_addr, size_t host_len);
32 changes: 31 additions & 1 deletion src/goodbyedpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "dnsredir.h"
#include "ttltrack.h"
#include "blackwhitelist.h"
#include "presets.h"
#include "fakepackets.h"

// My mingw installation does not load inet_pton definition for some reason
Expand Down Expand Up @@ -145,7 +146,8 @@ enum ERROR_CODE{
ERROR_BLACKLIST_LOAD,
ERROR_AUTOTTL,
ERROR_ATOUSI,
ERROR_AUTOB
ERROR_AUTOB,
ERROR_PRESET
};

static int running_from_service = 0;
Expand Down Expand Up @@ -177,6 +179,7 @@ static struct option long_options[] = {
{"dnsv6-port", required_argument, 0, '@' },
{"dns-verb", no_argument, 0, 'v' },
{"blacklist", required_argument, 0, 'b' },
{"blacklist-builtin", required_argument, 0, 'B' },
{"allow-no-sni",no_argument, 0, ']' },
{"frag-by-sni", no_argument, 0, '>' },
{"ip-id", required_argument, 0, 'i' },
Expand Down Expand Up @@ -636,6 +639,8 @@ int main(int argc, char *argv[]) {
char *host_addr, *useragent_addr, *method_addr;
unsigned int host_len, useragent_len;
int http_req_fragmented;
char **preset_argv = NULL;
int preset_argc = 0;

char *hdr_name_addr = NULL, *hdr_value_addr = NULL;
unsigned int hdr_value_len;
Expand All @@ -644,6 +649,9 @@ int main(int argc, char *argv[]) {
SetDllDirectory("");
SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT);

/* Output must reach a parent reading our stdout through a pipe (the TUI) right away */
setvbuf(stdout, NULL, _IONBF, 0);

if (!running_from_service) {
running_from_service = 1;
if (service_register(argc, argv)) {
Expand Down Expand Up @@ -673,6 +681,17 @@ int main(int argc, char *argv[]) {
"https://github.com/ValdikSS/GoodbyeDPI\n\n"
);

switch (presets_expand(argc, argv, &preset_argc, &preset_argv)) {
case PRESET_EXPANDED:
argc = preset_argc;
argv = preset_argv;
break;
case PRESET_ERROR:
exit(ERROR_PRESET);
default:
break;
}

if (argc == 1) {
/* enable mode -9 by default */
do_fragment_http = do_fragment_https = 1;
Expand Down Expand Up @@ -868,6 +887,13 @@ int main(int argc, char *argv[]) {
exit(ERROR_BLACKLIST_LOAD);
}
break;
case 'B': // --blacklist-builtin
do_blacklist = 1;
if (!blackwhitelist_load_builtin(optarg)) {
printf("Unknown built-in blacklist: %s\n", optarg);
exit(ERROR_BLACKLIST_LOAD);
}
break;
case ']': // --allow-no-sni
do_allow_no_sni = 1;
break;
Expand Down Expand Up @@ -994,6 +1020,7 @@ int main(int argc, char *argv[]) {
" --blacklist <txtfile> perform circumvention tricks only to host names and subdomains from\n"
" supplied text file (HTTP Host/TLS SNI).\n"
" This option can be supplied multiple times.\n"
" --blacklist-builtin <name> same as --blacklist, with a built-in list. Available: steam\n"
" --allow-no-sni perform circumvention if TLS SNI can't be detected with --blacklist enabled.\n"
" --frag-by-sni if SNI is detected in TLS packet, fragment the packet right before SNI value.\n"
" --set-ttl <value> activate Fake Request Mode and send it with supplied TTL value.\n"
Expand Down Expand Up @@ -1027,6 +1054,9 @@ int main(int argc, char *argv[]) {
" (up to 30).\n"
" --fake-resend <value> Send each fake packet value number of times.\n"
" Default: 1 (send each packet once).\n"
" --preset <name> apply a ready-made option set: viettel, fpt, vnpt, steam.\n"
" Options after it are added on top (own --dns-addr overrides the preset's).\n"
" --list-presets list available presets and exit.\n"
" --max-payload [value] packets with TCP payload data more than [value] won't be processed.\n"
" Use this option to reduce CPU usage by skipping huge amount of data\n"
" (like file transfers) in already established sessions.\n"
Expand Down
Loading