mirror of
https://github.com/ValdikSS/GoodbyeDPI.git
synced 2025-12-17 21:04:36 +03:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
185a0e0211 | ||
|
|
aa28d3e12a | ||
|
|
dc4d0a36d9 | ||
|
|
cc770d302c | ||
|
|
2e23d93762 | ||
|
|
14ae107b53 | ||
|
|
1bdfbb8c45 | ||
|
|
5eddb5f18a | ||
|
|
f638e23ab6 | ||
|
|
bb8fc64f18 | ||
|
|
490525387d |
@@ -24,6 +24,8 @@ Usage: goodbyedpi.exe [OPTION...]
|
||||
-a additional space between Method and Request-URI (enables -s, may break sites)
|
||||
-w try to find and parse HTTP traffic on all processed ports (not only on port 80)
|
||||
--port [value] additional TCP port to perform fragmentation on (and HTTP tricks with -w)
|
||||
--ip-id [value] handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).
|
||||
This option can be supplied multiple times.
|
||||
--dns-addr [value] redirect UDP DNS requests to the supplied IP address (experimental)
|
||||
--dns-port [value] redirect UDP DNS requests to the supplied port (53 by default)
|
||||
--dns-verb print verbose DNS redirection messages
|
||||
@@ -48,11 +50,11 @@ Use `goodbyedpi.exe -4` if it works for your ISP's DPI. This is the fastest mode
|
||||
|
||||
### Passive DPI
|
||||
|
||||
Most Passive DPI send HTTP 302 Redirect if you try to access blocked website over HTTP and TCP Reset in case of HTTPS, faster than destination website. Packets sent by DPI always have IP Identification field equal to `0x0000` or `0x0001`, as seen with Russian providers. These packets, if they redirect you to another website (censorship page), are blocked by GoodbyeDPI.
|
||||
Most Passive DPI send HTTP 302 Redirect if you try to access blocked website over HTTP and TCP Reset in case of HTTPS, faster than destination website. Packets sent by DPI usually have IP Identification field equal to `0x0000` or `0x0001`, as seen with Russian providers. These packets, if they redirect you to another website (censorship page), are blocked by GoodbyeDPI.
|
||||
|
||||
### Active DPI
|
||||
|
||||
Active DPI is more tricky to fool. Currently the software uses 4 methods to circumvent Active DPI:
|
||||
Active DPI is more tricky to fool. Currently the software uses 6 methods to circumvent Active DPI:
|
||||
|
||||
* TCP-level fragmentation for first data packet
|
||||
* TCP-level fragmentation for persistent (keep-alive) HTTP sessions
|
||||
|
||||
@@ -239,6 +239,6 @@ int dns_handle_incoming(const uint32_t srcip[4], const uint16_t srcport,
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
debug("____dns_handle_incoming FALSE: srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport));
|
||||
debug("____dns_handle_incoming FALSE: srcport = %hu\n", ntohs(srcport));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
125
goodbyedpi.c
125
goodbyedpi.c
@@ -13,15 +13,15 @@
|
||||
#include <ws2tcpip.h>
|
||||
#include "windivert.h"
|
||||
#include "goodbyedpi.h"
|
||||
#include "repl_str.h"
|
||||
#include "service.h"
|
||||
#include "dnsredir.h"
|
||||
#include "blackwhitelist.h"
|
||||
|
||||
// My mingw installation does not load inet_pton definition for some reason
|
||||
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);
|
||||
|
||||
#define die() do { printf("Something went wrong!\n" \
|
||||
"Make sure you're running this program with administrator privileges\n"); \
|
||||
sleep(10); exit(EXIT_FAILURE); } while (0)
|
||||
#define die() do { sleep(20); exit(EXIT_FAILURE); } while (0)
|
||||
|
||||
#define MAX_FILTERS 4
|
||||
#define MAX_PACKET_SIZE 9016
|
||||
@@ -56,6 +56,29 @@ WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pA
|
||||
"(ipv6.SrcAddr < ff00::0 or ipv6.SrcAddr > ffff::0)" \
|
||||
")"
|
||||
|
||||
/* #IPID# is a template to find&replace */
|
||||
#define IPID_TEMPLATE "#IPID#"
|
||||
#define FILTER_STRING_TEMPLATE \
|
||||
"(tcp and " \
|
||||
"(inbound and (" \
|
||||
"(" \
|
||||
"(" \
|
||||
"((ip.Id >= 0x0 and ip.Id <= 0xF) " IPID_TEMPLATE \
|
||||
") and " \
|
||||
"tcp.SrcPort == 80 and tcp.Ack" \
|
||||
") or " \
|
||||
"((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)" \
|
||||
")" \
|
||||
" and (" DIVERT_NO_LOCALNETSv4_SRC " or " DIVERT_NO_LOCALNETSv6_SRC ")) or " \
|
||||
"(outbound and " \
|
||||
"(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and " \
|
||||
"(" DIVERT_NO_LOCALNETSv4_DST " or " DIVERT_NO_LOCALNETSv6_DST "))" \
|
||||
"))"
|
||||
#define FILTER_PASSIVE_STRING_TEMPLATE "inbound and ip and tcp and " \
|
||||
"((ip.Id <= 0xF and ip.Id >= 0x0) " IPID_TEMPLATE ") and " \
|
||||
"(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and " \
|
||||
DIVERT_NO_LOCALNETSv4_SRC
|
||||
|
||||
#define SET_HTTP_FRAGMENT_SIZE_OPTION(fragment_size) do { \
|
||||
if (!http_fragment_size) { \
|
||||
if (fragment_size <= 0 || fragment_size > 65535) { \
|
||||
@@ -100,24 +123,12 @@ static struct option long_options[] = {
|
||||
{"dnsv6-port", required_argument, 0, '@' },
|
||||
{"dns-verb", no_argument, 0, 'v' },
|
||||
{"blacklist", required_argument, 0, 'b' },
|
||||
{"ip-id", required_argument, 0, 'i' },
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static char *filter_string = NULL;
|
||||
static char *filter_string_template = "(tcp and "
|
||||
"(inbound and ("
|
||||
"("
|
||||
"("
|
||||
"(ip.Id >= 0x0 and ip.Id <= 0xF) and "
|
||||
"tcp.SrcPort == 80 and tcp.Ack"
|
||||
") or "
|
||||
"((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)"
|
||||
")"
|
||||
" and (" DIVERT_NO_LOCALNETSv4_SRC " or " DIVERT_NO_LOCALNETSv6_SRC ")) or "
|
||||
"(outbound and "
|
||||
"(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and "
|
||||
"(" DIVERT_NO_LOCALNETSv4_DST " or " DIVERT_NO_LOCALNETSv6_DST "))"
|
||||
"))";
|
||||
static char *filter_passive_string = NULL;
|
||||
|
||||
static void add_filter_str(int proto, int port) {
|
||||
const char *udp = " or (udp and (udp.SrcPort == %d or udp.DstPort == %d))";
|
||||
@@ -138,6 +149,34 @@ static void add_filter_str(int proto, int port) {
|
||||
free(current_filter);
|
||||
}
|
||||
|
||||
static void add_ip_id_str(int id) {
|
||||
char *newstr;
|
||||
const char *ipid = " or ip.Id == %d";
|
||||
char *addfilter = malloc(strlen(ipid) + 16);
|
||||
|
||||
sprintf(addfilter, ipid, id);
|
||||
|
||||
newstr = repl_str(filter_string, IPID_TEMPLATE, addfilter);
|
||||
free(filter_string);
|
||||
filter_string = newstr;
|
||||
|
||||
newstr = repl_str(filter_passive_string, IPID_TEMPLATE, addfilter);
|
||||
free(filter_passive_string);
|
||||
filter_passive_string = newstr;
|
||||
}
|
||||
|
||||
static void finalize_filter_strings() {
|
||||
char *newstr;
|
||||
|
||||
newstr = repl_str(filter_string, IPID_TEMPLATE, "");
|
||||
free(filter_string);
|
||||
filter_string = newstr;
|
||||
|
||||
newstr = repl_str(filter_passive_string, IPID_TEMPLATE, "");
|
||||
free(filter_passive_string);
|
||||
filter_passive_string = newstr;
|
||||
}
|
||||
|
||||
static char* dumb_memmem(const char* haystack, int hlen, const char* needle, int nlen) {
|
||||
// naive implementation
|
||||
if (nlen > hlen) return NULL;
|
||||
@@ -152,15 +191,27 @@ static char* dumb_memmem(const char* haystack, int hlen, const char* needle, int
|
||||
|
||||
static HANDLE init(char *filter, UINT64 flags) {
|
||||
LPTSTR errormessage = NULL;
|
||||
DWORD errorcode = 0;
|
||||
filter = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags);
|
||||
if (filter != INVALID_HANDLE_VALUE)
|
||||
return filter;
|
||||
errorcode = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(), MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
||||
NULL, errorcode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&errormessage, 0, NULL);
|
||||
puts(errormessage);
|
||||
free(errormessage);
|
||||
printf("Error opening filter: %s", errormessage);
|
||||
LocalFree(errormessage);
|
||||
if (errorcode == 577)
|
||||
printf("Windows Server 2016 systems must have secure boot disabled to be "
|
||||
"able to load WinDivert driver.\n"
|
||||
"Windows 7 systems must be up-to-date or at least have KB3033929 installed.\n"
|
||||
"https://www.microsoft.com/en-us/download/details.aspx?id=46078\n\n"
|
||||
"WARNING! If you see this error on Windows 7, it means your system is horribly "
|
||||
"outdated and SHOULD NOT BE USED TO ACCESS THE INTERNET!\n"
|
||||
"Most probably, you don't have security patches installed and anyone in you LAN or "
|
||||
"public Wi-Fi network can get full access to your computer (MS17-010 and others).\n"
|
||||
"You should install updates IMMEDIATELY.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -314,6 +365,10 @@ int main(int argc, char *argv[]) {
|
||||
char *hdr_name_addr = NULL, *hdr_value_addr = NULL;
|
||||
int hdr_value_len;
|
||||
|
||||
// Make sure to search DLLs only in safe path, not in current working dir.
|
||||
SetDllDirectory("");
|
||||
SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT);
|
||||
|
||||
if (!running_from_service) {
|
||||
running_from_service = 1;
|
||||
if (service_register(argc, argv)) {
|
||||
@@ -333,9 +388,14 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (filter_string == NULL)
|
||||
filter_string = strdup(filter_string_template);
|
||||
filter_string = strdup(FILTER_STRING_TEMPLATE);
|
||||
if (filter_passive_string == NULL)
|
||||
filter_passive_string = strdup(FILTER_PASSIVE_STRING_TEMPLATE);
|
||||
|
||||
printf("GoodbyeDPI: Passive DPI blocker and Active DPI circumvention utility\n");
|
||||
printf(
|
||||
"GoodbyeDPI: Passive DPI blocker and Active DPI circumvention utility\n"
|
||||
"https://github.com/ValdikSS/GoodbyeDPI\n\n"
|
||||
);
|
||||
|
||||
if (argc == 1) {
|
||||
/* enable mode -1 by default */
|
||||
@@ -417,6 +477,16 @@ int main(int argc, char *argv[]) {
|
||||
add_filter_str(IPPROTO_TCP, i);
|
||||
i = 0;
|
||||
break;
|
||||
case 'i':
|
||||
/* i is used as a temporary variable here */
|
||||
i = atoi(optarg);
|
||||
if (i < 0 || i > 65535) {
|
||||
printf("IP ID parameter error!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
add_ip_id_str(i);
|
||||
i = 0;
|
||||
break;
|
||||
case 'd':
|
||||
if ((inet_pton(AF_INET, optarg, dns_temp_addr.s6_addr) == 1) &&
|
||||
!do_dnsv4_redirect)
|
||||
@@ -506,6 +576,7 @@ int main(int argc, char *argv[]) {
|
||||
" -e [value] set HTTPS fragmentation to value\n"
|
||||
" -w try to find and parse HTTP traffic on all processed ports (not only on port 80)\n"
|
||||
" --port [value] additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n"
|
||||
" --ip-id [value] handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n"
|
||||
" --dns-addr [value] redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n"
|
||||
" --dns-port [value] redirect UDPv4 DNS requests to the supplied port (53 by default)\n"
|
||||
" --dnsv6-addr [value] redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n"
|
||||
@@ -542,16 +613,16 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
printf("\nOpening filter\n");
|
||||
finalize_filter_strings();
|
||||
filter_num = 0;
|
||||
|
||||
if (do_passivedpi) {
|
||||
/* IPv4 only filter for inbound RST packets with ID = 0 or 1 */
|
||||
/* IPv4 only filter for inbound RST packets with ID [0x0; 0xF] */
|
||||
filters[filter_num] = init(
|
||||
"inbound and ip and tcp and "
|
||||
"(ip.Id >= 0x0000 and ip.Id <= 0x000F) and "
|
||||
"(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and "
|
||||
DIVERT_NO_LOCALNETSv4_SRC,
|
||||
filter_passive_string,
|
||||
WINDIVERT_FLAG_DROP);
|
||||
if (filters[filter_num] == NULL)
|
||||
die();
|
||||
filter_num++;
|
||||
}
|
||||
|
||||
|
||||
90
repl_str.c
Normal file
90
repl_str.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if (__STDC_VERSION__ >= 199901L)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
char *repl_str(const char *str, const char *from, const char *to) {
|
||||
|
||||
/* Adjust each of the below values to suit your needs. */
|
||||
|
||||
/* Increment positions cache size initially by this number. */
|
||||
size_t cache_sz_inc = 16;
|
||||
/* Thereafter, each time capacity needs to be increased,
|
||||
* multiply the increment by this factor. */
|
||||
const size_t cache_sz_inc_factor = 3;
|
||||
/* But never increment capacity by more than this number. */
|
||||
const size_t cache_sz_inc_max = 1048576;
|
||||
|
||||
char *pret, *ret = NULL;
|
||||
const char *pstr2, *pstr = str;
|
||||
size_t i, count = 0;
|
||||
#if (__STDC_VERSION__ >= 199901L)
|
||||
uintptr_t *pos_cache_tmp, *pos_cache = NULL;
|
||||
#else
|
||||
ptrdiff_t *pos_cache_tmp, *pos_cache = NULL;
|
||||
#endif
|
||||
size_t cache_sz = 0;
|
||||
size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
|
||||
|
||||
/* Find all matches and cache their positions. */
|
||||
while ((pstr2 = strstr(pstr, from)) != NULL) {
|
||||
count++;
|
||||
|
||||
/* Increase the cache size when necessary. */
|
||||
if (cache_sz < count) {
|
||||
cache_sz += cache_sz_inc;
|
||||
pos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz);
|
||||
if (pos_cache_tmp == NULL) {
|
||||
goto end_repl_str;
|
||||
} else pos_cache = pos_cache_tmp;
|
||||
cache_sz_inc *= cache_sz_inc_factor;
|
||||
if (cache_sz_inc > cache_sz_inc_max) {
|
||||
cache_sz_inc = cache_sz_inc_max;
|
||||
}
|
||||
}
|
||||
|
||||
pos_cache[count-1] = pstr2 - str;
|
||||
pstr = pstr2 + fromlen;
|
||||
}
|
||||
|
||||
orglen = pstr - str + strlen(pstr);
|
||||
|
||||
/* Allocate memory for the post-replacement string. */
|
||||
if (count > 0) {
|
||||
tolen = strlen(to);
|
||||
retlen = orglen + (tolen - fromlen) * count;
|
||||
} else retlen = orglen;
|
||||
ret = malloc(retlen + 1);
|
||||
if (ret == NULL) {
|
||||
goto end_repl_str;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
/* If no matches, then just duplicate the string. */
|
||||
strcpy(ret, str);
|
||||
} else {
|
||||
/* Otherwise, duplicate the string whilst performing
|
||||
* the replacements using the position cache. */
|
||||
pret = ret;
|
||||
memcpy(pret, str, pos_cache[0]);
|
||||
pret += pos_cache[0];
|
||||
for (i = 0; i < count; i++) {
|
||||
memcpy(pret, to, tolen);
|
||||
pret += tolen;
|
||||
pstr = str + pos_cache[i] + fromlen;
|
||||
cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen;
|
||||
memcpy(pret, pstr, cpylen);
|
||||
pret += cpylen;
|
||||
}
|
||||
ret[retlen] = '\0';
|
||||
}
|
||||
|
||||
end_repl_str:
|
||||
/* Free the cache and return the post-replacement string,
|
||||
* which will be NULL in the event of an error. */
|
||||
free(pos_cache);
|
||||
return ret;
|
||||
}
|
||||
1
repl_str.h
Normal file
1
repl_str.h
Normal file
@@ -0,0 +1 @@
|
||||
char *repl_str(const char *str, const char *from, const char *to);
|
||||
Reference in New Issue
Block a user