Skip to content

Commit f2de8fc

Browse files
committed
Fake Request Mode
1 parent 2b3e4a4 commit f2de8fc

File tree

5 files changed

+243
-5
lines changed

5 files changed

+243
-5
lines changed

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Usage: goodbyedpi.exe [OPTION...]
3333
--dns-verb print verbose DNS redirection messages
3434
--blacklist [txtfile] perform HTTP tricks only to host names and subdomains from
3535
supplied text file. This option can be supplied multiple times.
36+
--set-ttl [value] activate Fake Request Mode and send it with supplied TTL value.
37+
DANGEROUS! May break websites in unexpected ways. Use with care.
38+
--wrong-chksum activate Fake Request Mode and send it with incorrect TCP checksum.
39+
May not work in a VM or with some routers, but is safer than set-ttl.
3640
3741
-1 -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode, default)
3842
-2 -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible)
@@ -56,14 +60,15 @@ Most Passive DPI send HTTP 302 Redirect if you try to access blocked website ove
5660

5761
### Active DPI
5862

59-
Active DPI is more tricky to fool. Currently the software uses 6 methods to circumvent Active DPI:
63+
Active DPI is more tricky to fool. Currently the software uses 7 methods to circumvent Active DPI:
6064

6165
* TCP-level fragmentation for first data packet
6266
* TCP-level fragmentation for persistent (keep-alive) HTTP sessions
6367
* Replacing `Host` header with `hoSt`
6468
* Removing space between header name and value in `Host` header
6569
* Adding additional space between HTTP Method (GET, POST etc) and URI
6670
* Mixing case of Host header value
71+
* Sending fake HTTP/HTTPS packets with low Time-To-Live value or incorrect checksum to fool DPI and prevent delivering them to the destination
6772

6873
These methods should not break any website as they're fully compatible with TCP and HTTP standards, yet it's sufficient to prevent DPI data classification and to circumvent censorship. Additional space may break some websites, although it's acceptable by HTTP/1.1 specification (see 19.3 Tolerant Applications).
6974

src/fakepackets.c

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <ctype.h>
4+
#include <unistd.h>
5+
#include <in6addr.h>
6+
#include <ws2tcpip.h>
7+
#include "windivert.h"
8+
#include "goodbyedpi.h"
9+
10+
static const char fake_http_request[] = "GET / HTTP/1.1\r\nHost: www.w3.org\r\n"
11+
"User-Agent: curl/7.65.3\r\nAccept: */*\r\n"
12+
"Accept-Encoding: deflate, gzip, br\r\n\r\n";
13+
static const unsigned char fake_https_request[] = {
14+
0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x9a, 0x8f, 0xa7, 0x6a, 0x5d,
15+
0x57, 0xf3, 0x62, 0x19, 0xbe, 0x46, 0x82, 0x45, 0xe2, 0x59, 0x5c, 0xb4, 0x48, 0x31, 0x12, 0x15,
16+
0x14, 0x79, 0x2c, 0xaa, 0xcd, 0xea, 0xda, 0xf0, 0xe1, 0xfd, 0xbb, 0x20, 0xf4, 0x83, 0x2a, 0x94,
17+
0xf1, 0x48, 0x3b, 0x9d, 0xb6, 0x74, 0xba, 0x3c, 0x81, 0x63, 0xbc, 0x18, 0xcc, 0x14, 0x45, 0x57,
18+
0x6c, 0x80, 0xf9, 0x25, 0xcf, 0x9c, 0x86, 0x60, 0x50, 0x31, 0x2e, 0xe9, 0x00, 0x22, 0x13, 0x01,
19+
0x13, 0x03, 0x13, 0x02, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30,
20+
0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35,
21+
0x01, 0x00, 0x01, 0x91, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x77, 0x77, 0x77,
22+
0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,
23+
0x00, 0x0a, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00,
24+
0x01, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
25+
0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05,
26+
0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00,
27+
0x20, 0xb0, 0xe4, 0xda, 0x34, 0xb4, 0x29, 0x8d, 0xd3, 0x5c, 0x70, 0xd3, 0xbe, 0xe8, 0xa7, 0x2a,
28+
0x6b, 0xe4, 0x11, 0x19, 0x8b, 0x18, 0x9d, 0x83, 0x9a, 0x49, 0x7c, 0x83, 0x7f, 0xa9, 0x03, 0x8c,
29+
0x3c, 0x00, 0x17, 0x00, 0x41, 0x04, 0x4c, 0x04, 0xa4, 0x71, 0x4c, 0x49, 0x75, 0x55, 0xd1, 0x18,
30+
0x1e, 0x22, 0x62, 0x19, 0x53, 0x00, 0xde, 0x74, 0x2f, 0xb3, 0xde, 0x13, 0x54, 0xe6, 0x78, 0x07,
31+
0x94, 0x55, 0x0e, 0xb2, 0x6c, 0xb0, 0x03, 0xee, 0x79, 0xa9, 0x96, 0x1e, 0x0e, 0x98, 0x17, 0x78,
32+
0x24, 0x44, 0x0c, 0x88, 0x80, 0x06, 0x8b, 0xd4, 0x80, 0xbf, 0x67, 0x7c, 0x37, 0x6a, 0x5b, 0x46,
33+
0x4c, 0xa7, 0x98, 0x6f, 0xb9, 0x22, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03,
34+
0x02, 0x03, 0x01, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08,
35+
0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00,
36+
0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00, 0x15, 0x00, 0x96, 0x00,
37+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46+
0x00, 0x00, 0x00, 0x00, 0x00
47+
};
48+
49+
static int send_fake_data(const HANDLE w_filter,
50+
const PWINDIVERT_ADDRESS addr,
51+
const char *pkt,
52+
const UINT packetLen,
53+
const BOOL is_ipv6,
54+
const BOOL is_https,
55+
const BYTE set_ttl,
56+
const BYTE set_checksum
57+
) {
58+
char packet_fake[MAX_PACKET_SIZE];
59+
WINDIVERT_ADDRESS addr_new;
60+
PVOID packet_data;
61+
UINT packet_dataLen;
62+
UINT packetLen_new;
63+
PWINDIVERT_IPHDR ppIpHdr;
64+
PWINDIVERT_IPV6HDR ppIpV6Hdr;
65+
PWINDIVERT_TCPHDR ppTcpHdr;
66+
char *fake_request_data = is_https ? fake_https_request : fake_http_request;
67+
UINT fake_request_size = is_https ? sizeof(fake_https_request) : sizeof(fake_http_request) - 1;
68+
69+
memcpy(&addr_new, addr, sizeof(WINDIVERT_ADDRESS));
70+
memcpy(packet_fake, pkt, packetLen);
71+
72+
if (!is_ipv6) {
73+
// IPv4 TCP Data packet
74+
if (!WinDivertHelperParsePacket(packet_fake, packetLen, &ppIpHdr,
75+
NULL, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen))
76+
return 1;
77+
}
78+
else {
79+
// IPv6 TCP Data packet
80+
if (!WinDivertHelperParsePacket(packet_fake, packetLen, NULL,
81+
&ppIpV6Hdr, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen))
82+
return 1;
83+
}
84+
85+
if (packetLen + fake_request_size + 1 > MAX_PACKET_SIZE)
86+
return 2;
87+
88+
memcpy(packet_data, fake_request_data, fake_request_size);
89+
packetLen_new = packetLen - packet_dataLen + fake_request_size;
90+
91+
if (!is_ipv6) {
92+
ppIpHdr->Length = htons(
93+
ntohs(ppIpHdr->Length) -
94+
packet_dataLen + fake_request_size
95+
);
96+
97+
if (set_ttl)
98+
ppIpHdr->TTL = set_ttl;
99+
}
100+
else {
101+
ppIpV6Hdr->Length = htons(
102+
ntohs(ppIpV6Hdr->Length) -
103+
packet_dataLen + fake_request_size
104+
);
105+
106+
if (set_ttl)
107+
ppIpV6Hdr->HopLimit = set_ttl;
108+
}
109+
110+
// Recalculate the checksum
111+
addr_new.PseudoTCPChecksum = 0;
112+
WinDivertHelperCalcChecksums(packet_fake, packetLen_new, &addr_new, NULL);
113+
114+
if (set_checksum) {
115+
// ...and damage it
116+
ppTcpHdr->Checksum = htons(ntohs(ppTcpHdr->Checksum) - 1);
117+
}
118+
//printf("Pseudo checksum: %d\n", addr_new.PseudoTCPChecksum);
119+
120+
WinDivertSend(
121+
w_filter, packet_fake,
122+
packetLen_new,
123+
&addr_new, NULL
124+
);
125+
debug("Fake packet: OK");
126+
127+
return 0;
128+
}
129+
130+
int send_fake_http_request(const HANDLE w_filter,
131+
const PWINDIVERT_ADDRESS addr,
132+
const char *pkt,
133+
const UINT packetLen,
134+
const BOOL is_ipv6,
135+
const BYTE set_ttl,
136+
const BYTE set_checksum
137+
) {
138+
return send_fake_data(w_filter,
139+
addr,
140+
pkt,
141+
packetLen,
142+
is_ipv6,
143+
FALSE,
144+
set_ttl,
145+
set_checksum
146+
);
147+
}
148+
149+
int send_fake_https_request(const HANDLE w_filter,
150+
const PWINDIVERT_ADDRESS addr,
151+
const char *pkt,
152+
const UINT packetLen,
153+
const BOOL is_ipv6,
154+
const BYTE set_ttl,
155+
const BYTE set_checksum
156+
) {
157+
return send_fake_data(w_filter,
158+
addr,
159+
pkt,
160+
packetLen,
161+
is_ipv6,
162+
TRUE,
163+
set_ttl,
164+
set_checksum
165+
);
166+
}

src/fakepackets.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int send_fake_http_request(const HANDLE w_filter,
2+
const PWINDIVERT_ADDRESS addr,
3+
const char *pkt,
4+
const UINT packetLen,
5+
const BOOL is_ipv6,
6+
const BYTE set_ttl,
7+
const BYTE set_checksum
8+
);
9+
int send_fake_https_request(const HANDLE w_filter,
10+
const PWINDIVERT_ADDRESS addr,
11+
const char *pkt,
12+
const UINT packetLen,
13+
const BOOL is_ipv6,
14+
const BYTE set_ttl,
15+
const BYTE set_checksum
16+
);

src/goodbyedpi.c

+54-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "service.h"
1818
#include "dnsredir.h"
1919
#include "blackwhitelist.h"
20+
#include "fakepackets.h"
2021

2122
// My mingw installation does not load inet_pton definition for some reason
2223
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);
@@ -26,7 +27,6 @@ WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pA
2627
#define die() do { sleep(20); exit(EXIT_FAILURE); } while (0)
2728

2829
#define MAX_FILTERS 4
29-
#define MAX_PACKET_SIZE 9016
3030

3131
#define DIVERT_NO_LOCALNETSv4_DST "(" \
3232
"(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \
@@ -123,6 +123,8 @@ static struct option long_options[] = {
123123
{"dns-verb", no_argument, 0, 'v' },
124124
{"blacklist", required_argument, 0, 'b' },
125125
{"ip-id", required_argument, 0, 'i' },
126+
{"set-ttl", required_argument, 0, '$' },
127+
{"wrong-chksum",no_argument, 0, '%' },
126128
{0, 0, 0, 0 }
127129
};
128130

@@ -205,6 +207,19 @@ unsigned short int atousi(const char *str, const char *msg) {
205207
return (unsigned short int)res;
206208
}
207209

210+
BYTE atoub(const char *str, const char *msg) {
211+
long unsigned int res = strtoul(str, NULL, 10u);
212+
enum {
213+
limitValue=0xFFu
214+
};
215+
216+
if(res > limitValue) {
217+
puts(msg);
218+
exit(EXIT_FAILURE);
219+
}
220+
return (BYTE)res;
221+
}
222+
208223

209224
static HANDLE init(char *filter, UINT64 flags) {
210225
LPTSTR errormessage = NULL;
@@ -367,9 +382,11 @@ int main(int argc, char *argv[]) {
367382
do_http_allports = 0,
368383
do_host_mixedcase = 0,
369384
do_dnsv4_redirect = 0, do_dnsv6_redirect = 0,
370-
do_dns_verb = 0, do_blacklist = 0;
385+
do_dns_verb = 0, do_blacklist = 0,
386+
do_wrong_chksum = 0;
371387
unsigned int http_fragment_size = 0;
372388
unsigned int https_fragment_size = 0;
389+
BYTE ttl_of_fake_packet = 0;
373390
uint32_t dnsv4_addr = 0;
374391
struct in6_addr dnsv6_addr = {0};
375392
struct in6_addr dns_temp_addr = {0};
@@ -566,6 +583,12 @@ int main(int argc, char *argv[]) {
566583
exit(EXIT_FAILURE);
567584
}
568585
break;
586+
case '$':
587+
ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!");
588+
break;
589+
case '%':
590+
do_wrong_chksum = 1;
591+
break;
569592
default:
570593
puts("Usage: goodbyedpi.exe [OPTION...]\n"
571594
" -p block passive DPI\n"
@@ -587,6 +610,12 @@ int main(int argc, char *argv[]) {
587610
" --dns-verb print verbose DNS redirection messages\n"
588611
" --blacklist [txtfile] perform HTTP tricks only to host names and subdomains from\n"
589612
" supplied text file. This option can be supplied multiple times.\n"
613+
" --set-ttl [value] activate Fake Request Mode and send it with supplied TTL value.\n"
614+
" DANGEROUS! May break websites in unexpected ways. Use with care.\n"
615+
" Could be combined with --wrong-chksum.\n"
616+
" --wrong-chksum activate Fake Request Mode and send it with incorrect TCP checksum.\n"
617+
" May not work in a VM or with some routers, but is safer than set-ttl.\n"
618+
" Could be combined with --set-ttl\n."
590619
"\n"
591620
" -1 -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode, default)\n"
592621
" -2 -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible)\n"
@@ -604,13 +633,14 @@ int main(int argc, char *argv[]) {
604633
printf("Block passive: %d\nFragment HTTP: %d\nFragment persistent HTTP: %d\n"
605634
"Fragment HTTPS: %d\nhoSt: %d\nHost no space: %d\nAdditional space: %d\n"
606635
"Mix Host: %d\nHTTP AllPorts: %d\nHTTP Persistent Nowait: %d\n"
607-
"DNS redirect: %d\nDNSv6 redirect: %d\n",
636+
"DNS redirect: %d\nDNSv6 redirect: %d\n"
637+
"Fake requests, TTL: %hu\nFake requests, wrong checksum: %d\n",
608638
do_passivedpi, (do_fragment_http ? http_fragment_size : 0),
609639
(do_fragment_http_persistent ? http_fragment_size : 0),
610640
(do_fragment_https ? https_fragment_size : 0),
611641
do_host, do_host_removespace, do_additional_space, do_host_mixedcase,
612642
do_http_allports, do_fragment_http_persistent_nowait, do_dnsv4_redirect,
613-
do_dnsv6_redirect
643+
do_dnsv6_redirect, ttl_of_fake_packet, do_wrong_chksum
614644
);
615645

616646
if (do_fragment_http && http_fragment_size > 2) {
@@ -724,6 +754,21 @@ int main(int argc, char *argv[]) {
724754
}
725755
}
726756
}
757+
/* Handle OUTBOUND packet on port 443, search for something that resembles
758+
* TLS handshake, send fake request.
759+
*/
760+
else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND &&
761+
((do_fragment_https ? packet_dataLen == https_fragment_size : 0) ||
762+
packet_dataLen > 16) &&
763+
ppTcpHdr->DstPort != htons(80) &&
764+
(ttl_of_fake_packet || do_wrong_chksum)
765+
)
766+
{
767+
if (packet_dataLen >=2 && memcmp(packet_data, "\x16\x03", 2) == 0) {
768+
send_fake_https_request(w_filter, &addr, packet, packetLen, packet_v6,
769+
ttl_of_fake_packet, do_wrong_chksum);
770+
}
771+
}
727772
/* Handle OUTBOUND packet on port 80, search for Host header */
728773
else if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND &&
729774
packet_dataLen > 16 &&
@@ -744,6 +789,11 @@ int main(int argc, char *argv[]) {
744789
host_addr = hdr_value_addr;
745790
host_len = hdr_value_len;
746791

792+
if (ttl_of_fake_packet || do_wrong_chksum)
793+
send_fake_http_request(w_filter, &addr, packet, packetLen, packet_v6,
794+
ttl_of_fake_packet, do_wrong_chksum);
795+
796+
747797
/*
748798
* Handle new HTTP request in new
749799
* connection (when Window Size modification disabled)

src/goodbyedpi.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#define HOST_MAXLEN 253
2+
#define MAX_PACKET_SIZE 9016
23

34
#ifndef DEBUG
45
#define debug(...) do {} while (0)

0 commit comments

Comments
 (0)