This commit is contained in:
ruti 2023-06-03 21:52:10 +02:00
commit 08e1e1d8c7
12 changed files with 1947 additions and 0 deletions

10
Makefile Normal file
View file

@ -0,0 +1,10 @@
TARGET = ciadpi
CC ?= gcc
CFLAGS += -std=c99 -O2
SOURCES = packets.c main.c conev.c proxy.c desync.c
all:
$(CC) $(CFLAGS) $(SOURCES) -I . -o $(TARGET)
clean:
rm -f $(TARGET) *.o

194
conev.c Normal file
View file

@ -0,0 +1,194 @@
#define CONEV_H
#include <conev.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
struct poolhd *init_pool(int count)
{
struct poolhd *pool = malloc(sizeof(struct poolhd));
if (!pool) {
return 0;
}
pool->max = count;
pool->count = 0;
#ifndef NOEPOLL
int efd = epoll_create(count);
if (efd < 0) {
perror("epoll_create");
free(pool);
return 0;
}
pool->efd = efd;
#endif
pool->pevents = malloc(sizeof(*pool->pevents) * count);
pool->links = malloc(sizeof(*pool->links) * count);
pool->items = malloc(sizeof(*pool->items) * count);
if (!pool->pevents || !pool->links || !pool->items) {
destroy_pool(pool);
return 0;
}
for (int i = 0; i < count; i++) {
pool->links[i] = &(pool->items[i]);
}
return pool;
}
struct eval *add_event(struct poolhd *pool, enum eid type,
int fd, int e)
{
if (pool->count >= pool->max)
return 0;
struct eval *val = pool->links[pool->count];
val->fd = fd;
val->index = pool->count;
val->type = type;
val->pair = 0;
val->send_count = 0;
val->flag = 0;
#ifndef NOEPOLL
struct epoll_event ev = {
EPOLLIN | EPOLLERR | EPOLLRDHUP | e, {val}
};
if (epoll_ctl(pool->efd, EPOLL_CTL_ADD, fd, &ev)) {
return 0;
}
val->events = ev.events;
#else
struct pollfd *pfd = &(pool->pevents[pool->count]);
pfd->fd = fd;
pfd->events = POLLIN | POLLERR | POLLRDHUP | e;
pfd->revents = 0;
#endif
pool->count++;
return val;
}
void del_event(struct poolhd *pool, struct eval *val)
{
if (!val->fd) {
return;
}
close(val->fd);
val->fd = 0;
pool->count--;
struct eval *ev = pool->links[pool->count];
if (ev != val)
{
int index = val->index;
pool->links[index] = ev;
pool->links[pool->count] = val;
#ifdef NOEPOLL
pool->pevents[index] = pool->pevents[pool->count];
#endif
ev->index = index;
}
if (val->pair) {
val->pair->pair = 0;
del_event(pool, val->pair);
}
}
void destroy_pool(struct poolhd *pool)
{
for (int x = 0; x < pool->count; x++) {
struct eval *val = pool->links[x];
if (val->fd) close(val->fd);
}
if (pool->items) {
free(pool->items);
}
if (pool->links) {
free(pool->links);
}
if (pool->pevents) {
free(pool->pevents);
}
#ifndef NOEPOLL
if (pool->efd)
close(pool->efd);
#endif
memset(pool, 0, sizeof(*pool));
free(pool);
}
#ifndef NOEPOLL
struct eval *next_event(struct poolhd *pool, int *offs, int *type)
{
int i = *offs;
if (i < 0) {
i = (epoll_wait(pool->efd, pool->pevents, pool->max, -1) - 1);
if (i < 0) {
perror("epoll_wait");
return 0;
}
}
*offs = i - 1;
*type = pool->pevents[i].events;
return pool->pevents[i].data.ptr;
}
int mod_etype(struct poolhd *pool, struct eval *val, int type, char add)
{
struct epoll_event ev = {
.events = val->events, .data = {val}
};
if (add)
ev.events |= type;
else
ev.events &= ~type;
val->events = ev.events;
int s = epoll_ctl(pool->efd, EPOLL_CTL_MOD, val->fd, &ev);
if (s)
perror("epoll_ctl");
return s;
}
#else
struct eval *next_event(struct poolhd *pool, int *offs, int *typel)
{
for (int i = *offs; ; i--) {
if (i < 0) {
if (poll(pool->pevents, pool->count, -1) <= 0) {
perror("poll");
return 0;
}
i = pool->count - 1;
}
short type = pool->pevents[i].revents;
if (!type)
continue;
pool->pevents[i].revents = 0;
*offs = i - 1;
*typel = type;
return pool->links[i];
}
}
int mod_etype(struct poolhd *pool, struct eval *val, int type, char add)
{
int index = val->index;
if (add)
pool->pevents[index].events |= type;
else
pool->pevents[index].events &= ~type;
return 0;
}
#endif

83
conev.h Normal file
View file

@ -0,0 +1,83 @@
#include <stdint.h>
#include <netinet/in.h>
#ifndef __linux__
#define NOEPOLL
#endif
#ifndef NOEPOLL
#include <sys/epoll.h>
#define POLLIN EPOLLIN
#define POLLOUT EPOLLOUT
#define POLLERR EPOLLERR
#define POLLHUP EPOLLHUP
#define POLLRDHUP EPOLLRDHUP
#else
#include <sys/poll.h>
#ifndef POLLRDHUP
#define POLLRDHUP POLLHUP
#endif
#endif
enum eid {
EV_ACCEPT,
EV_REQUEST,
EV_CONNECT,
EV_IGNORE,
EV_TUNNEL
};
#define FLAG_NOSEND 1
#define FLAG_HTTP 2
#define FLAG_S4 4
#define FLAG_S5 8
#define FLAG_CONN 16
#ifndef CONEV_H
char *eid_name[] = {
"EV_ACCEPT",
"EV_REQUEST",
"EV_CONNECT",
"EV_IGNORE",
"EV_TUNNEL"
};
#endif
struct eval {
int fd;
int index;
enum eid type;
struct eval *pair;
size_t send_count;
int flag;
#ifndef NOEPOLL
uint32_t events;
#endif
};
struct poolhd {
int max;
int count;
int efd;
struct eval **links;
struct eval *items;
#ifndef NOEPOLL
struct epoll_event *pevents;
#else
struct pollfd *pevents;
#endif
};
struct poolhd *init_pool(int count);
struct eval *add_event(struct poolhd *pool, enum eid type, int fd, int e);
struct eval *add_pair(struct poolhd *pool, struct eval *val, int sfd, int e);
void del_event(struct poolhd *pool, struct eval *val);
void destroy_pool(struct poolhd *pool);
struct eval *next_event(struct poolhd *pool, int *offs, int *type);
int mod_etype(struct poolhd *pool, struct eval *val, int type, char rm);

159
desync.c Normal file
View file

@ -0,0 +1,159 @@
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/mman.h>
#ifdef __linux__
#include <sys/sendfile.h>
#else
#include <sys/uio.h>
#define sendfile(outfd, infd, start, len) sendfile(infd, outfd, start, len, 0, 0)
#endif
#ifdef MFD_CLOEXEC
#include <sys/syscall.h>
#define memfd_create(name, flags) syscall(__NR_memfd_create, name, flags);
#else
#define memfd_create(name, flags) fileno(tmpfile())
#endif
#include <params.h>
#include <packets.h>
int fake_attack(int sfd, char *buffer, ssize_t n, int cnt, int pos)
{
struct packet pkt = cnt != IS_HTTP ? fake_tls : fake_http;
size_t psz = pkt.size;
int ffd = memfd_create("name", O_RDWR);
if (ffd < 0) {
perror("memfd_create");
return -1;
}
char *p = 0;
int status = -1;
while (status) {
if (ftruncate(ffd, pos) < 0) {
perror("ftruncate");
break;
}
p = mmap(0, pos, PROT_WRITE, MAP_SHARED, ffd, 0);
if (p == MAP_FAILED) {
perror("mmap");
break;
}
memcpy(p, pkt.data, psz < pos ? psz : pos);
if (setsockopt(sfd, IPPROTO_IP, IP_TTL,
&params.ttl, sizeof(params.ttl)) < 0) {
perror("setsockopt IP_TTL");
break;
}
if (sendfile(sfd, ffd, 0, pos) < 0) {
perror("sendfile");
break;
}
usleep(params.sfdelay);
memcpy(p, buffer, pos);
if (setsockopt(sfd, IPPROTO_IP, IP_TTL,
&params.def_ttl, sizeof(int)) < 0) {
perror("setsockopt IP_TTL");
break;
}
if (send(sfd, buffer + pos, n - pos, 0) < 0) {
perror("send");
break;
}
status = 0;
}
if (p) munmap(p, pos);
close(ffd);
return status;
}
int disorder_attack(int sfd, char *buffer, ssize_t n, int pos)
{
int bttl = 1;
if (setsockopt(sfd, IPPROTO_IP, IP_TTL,
(char *)&bttl, sizeof(bttl)) < 0) {
perror("setsockopt IP_TTL");
return -1;
}
if (send(sfd, buffer, pos, 0) < 0) {
perror("send");
return -1;
}
if (setsockopt(sfd, IPPROTO_IP, IP_TTL,
(char *)&params.def_ttl, sizeof(int)) < 0) {
perror("setsockopt IP_TTL");
return -1;
}
if (send(sfd, buffer + pos, n - pos, 0) < 0) {
perror("send");
return -1;
}
return 0;
}
int desync(int sfd, char *buffer, ssize_t n)
{
int pos = params.split;
char *host = 0;
int len = 0, type = 0;
if ((len = parse_tls(buffer, n, &host))) {
type = IS_HTTPS;
}
else if ((len = parse_http(buffer, n, &host, 0))) {
type = IS_HTTP;
}
LOG(LOG_S, "host: %.*s\n", len, host);
if (type == IS_HTTP && params.mod_http) {
if (mod_http(buffer, n, params.mod_http)) {
fprintf(stderr, "mod http error\n");
return -1;
}
}
if (host && params.split_host)
pos += (host - buffer);
else if (pos < 0)
pos += n;
LOG(LOG_L, "split pos: %d, n: %ld\n", pos, n);
if (pos <= 0 || pos >= n ||
params.attack == DESYNC_NONE ||
(!type && params.de_known))
{
if (send(sfd, buffer, n, 0) < 0) {
perror("send");
return -1;
}
}
else switch (params.attack) {
case DESYNC_FAKE:
return fake_attack(sfd, buffer, n, type, pos);
case DESYNC_DISORDER:
return disorder_attack(sfd, buffer, n, pos);
case DESYNC_SPLIT:
default:
if (send(sfd, buffer, pos, 0) < 0) {
perror("send");
return -1;
}
if (send(sfd, buffer + pos, n - pos, 0) < 0) {
perror("send");
return -1;
}
}
return 0;
}

1
desync.h Normal file
View file

@ -0,0 +1 @@
int desync(int sfd, char *buffer, ssize_t n);

407
main.c Normal file
View file

@ -0,0 +1,407 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <params.h>
#include <proxy.h>
#include <packets.h>
#define VERSION 1
struct packet fake_tls = {
sizeof(tls_data), tls_data
},
fake_http = {
sizeof(http_data), http_data
};
struct params params = {
.ttl = 8,
.split = 3,
.sfdelay = 3000,
.attack = DESYNC_NONE,
.split_host = 0,
.def_ttl = 0,
.mod_http = 0,
.mode = MODE_PROXY_S,
.ipv6 = 1,
.resolve = 1,
.de_known = 0,
.max_open = 512,
.bfsize = 16384,
.send_bfsz = 65536,
.nack_max = 65536 * 2 - 16384,
.debug = 0
};
char *ftob(char *name)
{
char *buffer = 0;
FILE *file = fopen(name, "rb");
if (!file)
return 0;
do {
if (fseek(file, 0, SEEK_END)) {
break;
}
long size = ftell(file);
if (!size || fseek(file, 0, SEEK_SET)) {
break;
}
if (!(buffer = malloc(size))) {
break;
}
if (fread(buffer, 1, size, file) != size) {
free(buffer);
buffer = 0;
}
} while (0);
fclose(file);
return buffer;
}
void daemonize(void)
{
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
else if (pid) {
exit(0);
}
if (setsid() < 0) {
exit(1);
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null", O_RDWR);
dup(0);
dup(0);
}
int main(int argc, char **argv)
{
struct sockaddr_ina s = {
.in = {
.sin_family = AF_INET,
.sin_port = htons(1080)
}};
char daemon = 0;
char *pidfile = 0;
const char help_text[] = {
//"Proxy:\n"
" -i, --ip, <ip> Listening IP address\n"
" -p, --port <num> Listening port num\n"
" -D, --daemon Daemonize\n"
" -f, --pidfile <file> Write pid to file\n"
" -c, --max-conn <count> Connection count limit, default 512\n"
#ifdef __linux__
" -T, --transparent Get address with getsockopt\n"
#endif
" -N, --no-domain Deny domain resolving\n"
" -K, --desync-known Desync only HTTP and TLS with SNI\n"
//"Desync:\n"
" -m, --method <s|d|f> Desync method: split,disorder,fake\n"
" -s, --split-pos <offset> Split position, default 3\n"
" -H, --split-at-host Add Host/SNI offset to split position\n"
" -t, --ttl <num> TTL of fake packets, default 8\n"
" -l, --fake-tls <file>\n"
" -o, --fake-http <file> Set custom fake packet\n"
" -n, --tls-sni <str> Change SNI in fake CH\n"
" -M, --mod-http <h,d,r> Modify http: hcsmix,dcsmix,rmspace\n"
};
const struct option options[] = {
{"daemon", 0, 0, 'D'},
{"transparent", 0, 0, 'T'},
{"http", 0, 0, 'P'}, //
{"socks", 0, 0, 'O'},
{"no-domain", 0, 0, 'N'},
{"no-ipv6", 0, 0, 'X'}, //
{"desync-known ", 0, 0, 'K'},
{"split-at-host", 0, 0, 'H'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'v'},
{"pidfile", 1, 0, 'f'},
{"ip", 1, 0, 'i'},
{"port", 1, 0, 'p'},
{"bfs", 1, 0, 'b'}, //
{"snd-bfs", 1, 0, 'B'}, //
{"max-conn", 1, 0, 'c'},
{"method", 1, 0, 'm'},
{"split-pos", 1, 0, 's'},
{"ttl", 1, 0, 't'},
{"fake-tls", 1, 0, 'l'},
{"fake-http", 1, 0, 'o'},
{"tls-sni", 1, 0, 'n'},
{"mod-http", 1, 0, 'M'},
{"global-ttl", 1, 0, 'g'}, //
{"delay", 1, 0, 'w'}, //
{"debug", 1, 0, 'x'}, //
{0}
};
int rez;
int invalid = 0;
long val = 0;
char *end = 0;
while (!invalid && (rez = getopt_long_only(argc, argv,
"DTPONXKHhvf:i:p:b:B:c:m:s:t:l:o:n:M:g:w:x:", options, 0)) != -1) {
switch (rez) {
case 'D':
daemon = 1;
break;
case 'f':
pidfile = optarg;
break;
case 'T':
params.mode = MODE_TRANSPARENT;
break;
case 'O':
params.mode = MODE_PROXY_S;
break;
case 'N':
params.resolve = 0;
break;
case 'X':
params.ipv6 = 0;
break;
case 'P':
params.mode = MODE_PROXY_H;
break;
case 'K':
params.de_known = 1;
break;
case 'h':
printf(help_text);
return 0;
case 'v':
printf("%d\n", VERSION);
return 0;
case 'b': //
val = strtol(optarg, &end, 0);
if (val <= 0 || val > INT_MAX/4 || *end)
invalid = 1;
else
params.bfsize = val;
break;
case 'B': //
val = strtol(optarg, &end, 0);
if (val <= 0 || val > INT_MAX || *end)
invalid = 1;
else
params.send_bfsz = val;
break;
case 'i':
if (strchr(optarg, ':'))
s.in.sin_family = AF_INET6;
else
s.in.sin_family = AF_INET;
if (!inet_pton(s.in.sin_family, optarg,
(s.in.sin_family == AF_INET ?
(char *)&s.in.sin_addr : (char *)&s.in6.sin6_addr)))
invalid = 1;
break;
case 'p':
val = strtol(optarg, &end, 0);
if (val <= 0 || val > 0xffff || *end)
invalid = 1;
else
s.in.sin_port = htons(val);
break;
case 'c':
val = strtol(optarg, &end, 0);
if (val <= 0 || val >= (0xffff/2) || *end)
invalid = 1;
else
params.max_open = val;
break;
case 'm':
if (params.attack != DESYNC_NONE) {
fprintf(stderr, "methods incompatible\n");
invalid = 1;
}
else switch (*optarg) {
case 's':
params.attack = DESYNC_SPLIT;
break;
case 'd':
params.attack = DESYNC_DISORDER;
break;
case 'f':
params.attack = DESYNC_FAKE;
break;
default:
invalid = 1;
}
break;
case 's':
val = strtol(optarg, &end, 0);
if (val < INT_MIN || val > INT_MAX || *end)
invalid = 1;
else
params.split = val;
break;
case 'H':
params.split_host = 1;
break;
case 't':
val = strtol(optarg, &end, 0);
if (val <= 0 || val > 255 || *end)
invalid = 1;
else
params.ttl = val;
break;
case 'n':
if (change_tls_sni(optarg, fake_tls.data, fake_tls.size)) {
fprintf(stderr, "error chsni\n");
return -1;
}
printf("sni: %s\n", optarg);
break;
case 'l':
fake_tls.data = ftob(optarg);
if (!fake_tls.data) {
perror("read file");
return -1;
}
break;
case 'o':
fake_http.data = ftob(optarg);
if (!fake_http.data) {
perror("read file");
return -1;
}
break;
case 'M':
end = optarg;
while (end && !invalid) {
switch (*end) {
case 'r':
params.mod_http |= MH_SPACE;
break;
case 'h':
params.mod_http |= MH_HMIX;
break;
case 'd':
params.mod_http |= MH_DMIX;
break;
default:
invalid = 1;
continue;
}
end = strchr(end, ',');
if (end) end++;
}
break;
case 'g': //
val = strtol(optarg, &end, 0);
if (val <= 0 || val > 255 || *end)
invalid = 1;
else
params.def_ttl = val;
break;
case 'w': //
params.sfdelay = strtoul(optarg, &end, 0);
if (optarg == end || *end)
invalid = 1;
break;
case 'x': //
params.debug = strtol(optarg, 0, 0);
if (params.debug < 0)
invalid = 1;
break;
case 0:
break;
case '?':
return -1;
default:
printf("?: %c\n", rez);
return -1;
}
}
if (invalid) {
fprintf(stderr, "invalid value: -%c %s\n", rez, optarg);
return -1;
}
if (params.send_bfsz * 2 <= params.bfsize) {
fprintf(stderr, "send buffer too small\n");
return -1;
} else {
params.nack_max = val * 2 - params.bfsize;
}
FILE *file;
if (pidfile) {
file = fopen(pidfile, "w");
if (!file) {
perror("fopen");
return -1;
}
}
if (daemon) {
daemonize();
}
if (pidfile) {
fprintf(file, "%d", getpid());
fclose(file);
}
if (!params.def_ttl) {
int orig_ttl, fd;
socklen_t tsize = sizeof(orig_ttl);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return -1;
}
if (getsockopt(fd, IPPROTO_IP, IP_TTL,
(char *)&orig_ttl, &tsize) < 0) {
perror("getsockopt IP_TTL");
close(fd);
return -1;
}
close(fd);
params.def_ttl = orig_ttl;
}
return listener(s);
}

214
packets.c Normal file
View file

@ -0,0 +1,214 @@
#define _GNU_SOURCE
#include <packets.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h>
#define ANTOHS(data, i) \
(uint16_t)((data[i] << 8) + (uint8_t)data[i + 1])
char tls_data[517] = {
"\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\x03\x5f"
"\x6f\x2c\xed\x13\x22\xf8\xdc\xb2\xf2\x60\x48\x2d\x72"
"\x66\x6f\x57\xdd\x13\x9d\x1b\x37\xdc\xfa\x36\x2e\xba"
"\xf9\x92\x99\x3a\x20\xf9\xdf\x0c\x2e\x8a\x55\x89\x82"
"\x31\x63\x1a\xef\xa8\xbe\x08\x58\xa7\xa3\x5a\x18\xd3"
"\x96\x5f\x04\x5c\xb4\x62\xaf\x89\xd7\x0f\x8b\x00\x3e"
"\x13\x02\x13\x03\x13\x01\xc0\x2c\xc0\x30\x00\x9f\xcc"
"\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24"
"\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0"
"\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c"
"\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x01"
"\x75\x00\x00\x00\x16\x00\x14\x00\x00\x11\x77\x77\x77"
"\x2e\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x2e\x6f\x72"
"\x67\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x16"
"\x00\x14\x00\x1d\x00\x17\x00\x1e\x00\x19\x00\x18\x01"
"\x00\x01\x01\x01\x02\x01\x03\x01\x04\x00\x10\x00\x0e"
"\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e"
"\x31\x00\x16\x00\x00\x00\x17\x00\x00\x00\x31\x00\x00"
"\x00\x0d\x00\x2a\x00\x28\x04\x03\x05\x03\x06\x03\x08"
"\x07\x08\x08\x08\x09\x08\x0a\x08\x0b\x08\x04\x08\x05"
"\x08\x06\x04\x01\x05\x01\x06\x01\x03\x03\x03\x01\x03"
"\x02\x04\x02\x05\x02\x06\x02\x00\x2b\x00\x09\x08\x03"
"\x04\x03\x03\x03\x02\x03\x01\x00\x2d\x00\x02\x01\x01"
"\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20\x11\x8c\xb8"
"\x8c\xe8\x8a\x08\x90\x1e\xee\x19\xd9\xdd\xe8\xd4\x06"
"\xb1\xd1\xe2\xab\xe0\x16\x63\xd6\xdc\xda\x84\xa4\xb8"
"\x4b\xfb\x0e\x00\x15\x00\xac\x00\x00\x00\x00\x00\x00"
};
char http_data[43] = {
"GET / HTTP/1.1\r\n"
"Host: www.wikipedia.org\r\n\r\n"
};
int find_tls_ext_offset(uint16_t type, char *data, size_t size)
{
if (size < 44) {
return 0;
}
uint8_t sid_len = data[43];
if (size < 44 + sid_len + 2) {
return 0;
}
uint16_t cip_len = ANTOHS(data, 44 + sid_len);
size_t skip = 44 + sid_len + 2 + cip_len + 4;
if (size <= skip) {
return 0;
}
uint16_t ext_len = ANTOHS(data, skip - 2);
if (ext_len < (size - skip)) {
size = ext_len + skip;
}
while ((skip + 4) < size) {
uint16_t epyt = ANTOHS(data, skip);
if (type == epyt) {
return skip;
}
uint16_t len = ANTOHS(data, skip + 2);
skip += (len + 4);
}
return 0;
}
int change_tls_sni(char *host, char *buffer, size_t bsize)
{
int sni_offs, pad_offs;
if (!(sni_offs = find_tls_ext_offset(0x00, buffer, bsize))) {
return -1;
}
if (!(pad_offs = find_tls_ext_offset(0x15, buffer, bsize))) {
return -1;
}
char *sni = &buffer[sni_offs];
char *pad = &buffer[pad_offs];
uint16_t old_sz = ANTOHS(buffer, sni_offs + 2) - 5;
uint16_t free_sz = ANTOHS(buffer, pad_offs + 2);
uint16_t new_sz = strlen(host);
ssize_t diff = new_sz - old_sz;
if ((free_sz != (bsize - pad_offs - 4))
|| free_sz < diff) {
return -1;
}
*(uint16_t *)(sni + 2) = htons(old_sz + diff + 5);
*(uint16_t *)(sni + 4) = htons(old_sz + diff + 3);
*(uint16_t *)(sni + 7) = htons(old_sz + diff);
*(uint16_t *)(pad + 2) = htons(free_sz - diff);
char *host_end = sni + 9 + old_sz;
int oth_sz = bsize - (sni_offs + 9 + old_sz);
memmove(host_end + diff, host_end, oth_sz);
memcpy(sni + 9, host, new_sz);
return 0;
}
int parse_tls(char *buffer, size_t bsize, char **hs)
{
if (ANTOHS(buffer, 0) != 0x1603) {
return 0;
}
int sni_offs = find_tls_ext_offset(0x00, buffer, bsize);
if (!sni_offs || (sni_offs + 12) >= bsize) {
return 0;
}
uint16_t len = ANTOHS(buffer, sni_offs + 7);
if ((sni_offs + 9 + len) > bsize) {
return 0;
}
*hs = &buffer[sni_offs + 9];
return len;
}
int parse_http(char *buffer, size_t bsize, char **hs, uint16_t *port)
{
char *host = buffer, *h_end;
size_t osz = bsize;
if (bsize < 16 || *buffer > 'T' || *buffer < 'C') {
return 0;
}
while (1) {
host = memchr(host, '\n', osz);
if (!host)
return 0;
host++;
osz = bsize - (host - buffer);
if (osz < 6)
return 0;
if (!strncasecmp(host, "Host:", 5))
break;
}
host += 5; osz -= 5;
for (; osz && isblank(*host); host++, osz--) {}
char *l_end = memchr(host, '\n', osz);
if (!l_end) {
return 0;
}
for (; isspace(*(l_end - 1)); l_end--) {}
if (!(isdigit(*(l_end - 1))))
h_end = 0;
else
h_end = memrchr(host, ':', l_end - host);
if (!h_end) {
if (port) *port = 80;
h_end = l_end;
}
else if (port) {
char *end;
long i = strtol(h_end + 1, &end, 10);
if (i <= 0 || end != l_end || i > 0xffff)
return 0;
*port = i;
}
*hs = host;
return h_end - host;
}
int mod_http(char *buffer, size_t bsize, int m)
{
char *host = 0, *par;
int hlen = parse_http(buffer, bsize, &host, 0);
if (!hlen)
return -1;
for (par = host - 1; *par != ':'; par--) {}
par -= 4;
if (m & MH_HMIX) {
par[0] = tolower(par[0]);
par[1] = toupper(par[1]);
par[3] = toupper(par[3]);
}
if (m & MH_DMIX) {
for (int i = 0; i < hlen; i += 2) {
host[i] = toupper(host[i]);
}
}
if (m & MH_SPACE) {
for (; !isspace(*(host + hlen)); hlen++) {}
int sc = host - (par + 5);
memmove(par + 5, host, hlen);
memset(par + 5 + hlen, '\t', sc);
}
return 0;
}

21
packets.h Normal file
View file

@ -0,0 +1,21 @@
#include <stdint.h>
#include <stdlib.h>
#define IS_UNKNOWN 0
#define IS_HTTP 1
#define IS_HTTPS 2
#define MH_HMIX 1
#define MH_SPACE 2
#define MH_DMIX 4
extern char tls_data[517];
extern char http_data[43];
int change_tls_sni(char *host, char *buffer, size_t bsize);
int parse_tls(char *buffer, size_t bsize, char **hs);
int parse_http(char *buffer, size_t bsize, char **hs, uint16_t *port);
int mod_http(char *buffer, size_t bsize, int m);

48
params.h Normal file
View file

@ -0,0 +1,48 @@
enum mode {
MODE_PROXY_H,
MODE_PROXY_S,
MODE_TRANSPARENT
};
enum demode {
DESYNC_NONE,
DESYNC_SPLIT,
DESYNC_DISORDER,
DESYNC_FAKE
};
struct params {
int ttl;
int split;
size_t sfdelay;
enum demode attack;
char split_host;
int def_ttl;
int mod_http;
enum mode mode;
char ipv6;
char resolve;
char de_known;
int max_open;
int debug;
size_t bfsize;
size_t nack_max;
int send_bfsz;
};
extern struct params params;
struct packet {
size_t size;
char *data;
};
extern struct packet fake_tls;
extern struct packet fake_http;
#define LOG_S 1
#define LOG_L 2
#define LOG(s, str, ...) \
if (params.debug >= s) printf(str, ##__VA_ARGS__)

692
proxy.c Normal file
View file

@ -0,0 +1,692 @@
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <proxy.h>
#include <packets.h>
#include <params.h>
#include <conev.h>
#include <desync.h>
#ifdef __linux__
#include <linux/netfilter_ipv4.h>
#endif
int NOT_EXIT = 1;
static void on_cancel(int sig) {
NOT_EXIT = 0;
}
static inline int is_binded_addr(int fd, struct sockaddr_ina *dst)
{
struct sockaddr_ina me;
socklen_t alen = sizeof(me);
if (getsockname(fd, &me.sa, &alen)) {
perror("getsockname");
return -1;
}
if (dst->sa.sa_family != me.sa.sa_family ||
dst->in.sin_port != me.in.sin_port) {
return 0;
}
if (dst->sa.sa_family == AF_INET6 ?
!memcmp(&dst->in6.sin6_addr, &me.in6.sin6_addr, 16) :
dst->in.sin_addr.s_addr == me.in.sin_addr.s_addr) {
return 1;
}
return 0;
}
int resolve(char *host, int len, struct sockaddr_ina *addr)
{
struct addrinfo hints = {0}, *res = 0;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
char rchar = host[len];
host[len] = '\0';
if (getaddrinfo(host, 0, &hints, &res) || !res) {
host[len] = rchar;
return -1;
}
if (res->ai_addr->sa_family == AF_INET6)
addr->in6 = *(struct sockaddr_in6 *)res->ai_addr;
else
addr->in = *(struct sockaddr_in *)res->ai_addr;
freeaddrinfo(res);
host[len] = rchar;
return 0;
}
int auth_socks5(int fd, char *buffer, ssize_t n)
{
if (n <= 2 || (uint8_t)buffer[1] != (n - 2)) {
return -1;
}
buffer[1] = S_AUTH_BAD;
long i = 2;
for (; i < n; i++)
if (buffer[i] == S_AUTH_NONE) {
buffer[1] = S_AUTH_NONE;
break;
}
if (send(fd, buffer, 2, 0) < 0) {
perror("send");
return -1;
}
return i < n ? 0 : -1;
}
int resp_error(int fd, int e, int flag)
{
if (flag & FLAG_HTTP) {
const char *r;
if (e) r = "HTTP/1.1 504\r\n\r\n";
else r = "HTTP/1.1 200\r\n\r\n";
return send(fd, r, 16, 0);
}
else if (flag & FLAG_S4) {
struct s4_req s4r = {
.cmd = e ? S4_ER : S4_OK
};
return send(fd, &s4r, sizeof(s4r), 0);
}
else if (flag & FLAG_S5) {
uint8_t se;
switch (e) {
case 0: se = S_ER_OK;
break;
case ECONNREFUSED:
se = S_ER_CONN;
break;
case EHOSTUNREACH:
case ETIMEDOUT:
se = S_ER_HOST;
break;
case ENETUNREACH:
se = S_ER_NET;
break;
default: se = S_ER_GEN;
}
struct s5_rep s5r = {
.ver = 0x05, .code = se,
.atp = S_ATP_I4
};
return send(fd, &s5r, sizeof(s5r), 0);
}
return 0;
}
int handle_socks4(int fd, char *bf,
size_t n, struct sockaddr_ina *dst)
{
if (n < sizeof(struct s4_req) + 1) {
return -1;
}
struct s4_req *r = (struct s4_req *)bf;
char er = 0;
if (r->cmd != S_CMD_CONN) {
er = 1;
}
else if (ntohl(r->i4.s_addr) <= 255) do {
er = 1;
if (!params.resolve || bf[n - 1])
break;
char *ie = strchr(bf + sizeof(*r), 0);
if (!ie)
break;
int len = (bf + n - ie) - 2;
if (len > 2) {
if (resolve(ie + 1, len, dst)) {
fprintf(stderr, "not resolved: %.*s\n", len, ie + 1);
break;
}
er = 0;
}
} while (0);
else {
dst->in.sin_family = AF_INET;
dst->in.sin_addr = r->i4;
}
if (er) {
struct s4_req s4r = {
.cmd = S4_ER
};
if (send(fd, &s4r, sizeof(s4r), 0) < 0)
perror("send");
return -1;
}
dst->in.sin_port = r->port;
return 0;
}
int handle_socks5(int fd, char *buffer,
size_t n, struct sockaddr_ina *addr)
{
if (n < sizeof(struct s5_rep)) {
return -1;
}
struct s5_req *r = (struct s5_req *)buffer;
uint8_t er = 0;
if (n != (r->atp == S_ATP_I4 ? 10 :
(r->atp == S_ATP_ID ? r->id.len + 7 :
(r->atp == S_ATP_I6 ? 22 : 0)))) {
fprintf(stderr, "ss: bad request\n");
return -1;
}
else if (r->cmd != S_CMD_CONN) {
fprintf(stderr, "ss: unsupported cmd: 0x%x\n", r->cmd);
er = S_ER_CMD;
}
else switch (r->atp) {
case S_ATP_I4:
addr->in.sin_family = AF_INET;
addr->in.sin_addr = r->i4;
break;
case S_ATP_ID:
if (!params.resolve) {
er = S_ER_ATP;
break;
}
if (resolve(r->id.domain, r->id.len, addr)) {
fprintf(stderr, "not resolved: %.*s\n", r->id.len, r->id.domain);
er = S_ER_HOST;
}
break;
case S_ATP_I6:
if (!params.ipv6)
er = S_ER_ATP;
else {
addr->in6.sin6_family = AF_INET6;
addr->in6.sin6_addr = r->i6;
}
}
LOG(LOG_L, "s5r: cmd: 0x%x, atp: 0x%x\n", r->cmd, r->atp);
if (er) {
struct s5_rep s5r = {
.ver = 0x05, .code = er,
.atp = S_ATP_I4
};
if (send(fd, &s5r, sizeof(s5r), 0) < 0)
perror("send");
return -1;
}
addr->in.sin_port = *(uint16_t *)&buffer[n - 2];
return 0;
}
int handle_http(int fd, char *buffer,
size_t bfsize, struct sockaddr_ina *dst)
{
char *host = 0;
uint16_t port = 443;
int cnt = 0;
ssize_t n = recv(fd, buffer, bfsize, MSG_PEEK);
if (n <= 0) {
perror("recv proxy");
return -1;
}
int len = parse_http(buffer, n, &host, &port);
if (len <= 2) {
fprintf(stderr, "parse error\n");
return -1;
}
if (!memcmp(buffer, "CONNECT", 7)) {
if (recv(fd, buffer, n, 0) != n) {
perror("recv");
return -1;
}
cnt = 1;
}
if (*host == '[' && host[len - 1] == ']') {
host++; len -= 2;
}
if (resolve(host, len, dst)) {
fprintf(stderr, "not resolved: %.*s\n", len, host);
return -1;
}
dst->in.sin_port = htons(port);
return cnt;
}
int nsendc(int fd, int *wn)
{
#ifdef SO_NWRITE
socklen_t len = sizeof(*wn);
if (getsockopt(fd, SOL_SOCKET, SO_NWRITE, wn, &len) < 0) {
perror("getsockopt SO_NWRITE");
return -1;
}
#else
if (ioctl(fd, TIOCOUTQ, wn) < 0 ) {
perror("ioctl");
return -1;
}
#endif
return 0;
}
static inline int create_conn(struct poolhd *pool, struct eval *val,
enum eid nxt, struct sockaddr_ina *dst)
{
int sfd = socket(dst->sa.sa_family, SOCK_STREAM, 0);
if (sfd < 0) {
perror("socket");
return -1;
}
#ifdef __linux__
int syn_count = 1;
if (setsockopt(sfd, IPPROTO_TCP,
TCP_SYNCNT, &syn_count, sizeof(syn_count))) {
perror("setsockopt TCP_SYNCNT");
close(sfd);
return -1;
}
#endif
int one = 1;
if (setsockopt(sfd, IPPROTO_TCP,
TCP_NODELAY, (char *)&one, sizeof(one))) {
perror("setsockopt TCP_NODELAY");
close(sfd);
return -1;
}
if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF,
&params.send_bfsz, sizeof(params.send_bfsz)) < 0) {
close(sfd);
perror("setsockopt SO_SNDBUF");
return -1;
}
if (fcntl(sfd, F_SETFL, O_NONBLOCK) < 0) {
perror("fcntl");
close(sfd);
return -1;
}
int status = connect(sfd, &dst->sa, sizeof(*dst));
if (!status || errno != EINPROGRESS) {
perror("connect");
close(sfd);
return -1;
}
struct eval *pair = add_event(pool, nxt, sfd, POLLOUT);
if (!pair) {
close(sfd);
return -1;
}
val->pair = pair;
pair->pair = val;
pair->flag = FLAG_CONN;
return 0;
}
static inline int on_request(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize)
{
struct sockaddr_ina dst = {0};
#ifdef __linux__
if (params.mode == MODE_TRANSPARENT) {
socklen_t alen = sizeof(dst);
if (getsockopt(val->fd, SOL_IP, SO_ORIGINAL_DST, &dst, &alen)) {
perror("getsockopt SO_ORIGINAL_DST");
return -1;
}
if (is_binded_addr(val->fd, &dst)) {
fprintf(stderr, "drop connection to self\n");
return -1;
}
mod_etype(pool, val, POLLIN, 0);
} else
#endif
if (params.mode == MODE_PROXY_H) {
int cnt = handle_http(val->fd, buffer, bfsize, &dst);
if (cnt < 0)
return -1;
else if (cnt)
val->flag = FLAG_HTTP;
else
mod_etype(pool, val, POLLIN, 0);
}
else {
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n < 1) {
if (n) perror("ss recv");
return -1;
}
if (*buffer == S_VER5) {
if (val->flag != FLAG_S5) {
if (auth_socks5(val->fd, buffer, n)) {
return -1;
}
val->flag = FLAG_S5;
return 0;
}
if (handle_socks5(val->fd, buffer, n, &dst)) {
return -1;
}
}
else if (*buffer == S_VER4) {
if (handle_socks4(val->fd, buffer, n, &dst)) {
return -1;
}
val->flag = FLAG_S4;
}
else {
fprintf(stderr, "ss: invalid version: 0x%x (%lu)\n", *buffer, n);
return -1;
}
}
if (create_conn(pool, val, EV_CONNECT, &dst)) {
if (resp_error(val->fd, errno, val->flag) < 0)
perror("send");
return -1;
}
val->type = EV_IGNORE;
return 0;
}
static inline int on_accept(struct poolhd *pool, struct eval *val)
{
struct sockaddr_ina client;
struct eval *rval;
while (1) {
socklen_t len = sizeof(client);
int c = accept(val->fd, &client.sa, &len);
if (c < 0) {
if (errno == EAGAIN)
break;
perror("accept");
return -1;
}
if (fcntl(c, F_SETFL, O_NONBLOCK) < 0) {
perror("fcntl");
close(c);
continue;
}
int one = 1;
if (setsockopt(c, IPPROTO_TCP,
TCP_NODELAY, (char *)&one, sizeof(one))) {
perror("setsockopt TCP_NODELAY");
close(c);
continue;
}
if (setsockopt(c, SOL_SOCKET, SO_SNDBUF,
&params.send_bfsz, sizeof(params.send_bfsz)) < 0) {
perror("setsockopt SO_SNDBUF");
close(c);
continue;
}
if (!(rval = add_event(pool, EV_REQUEST, c, 0))) {
close(c);
continue;
}
}
return 0;
}
static inline int on_data(struct eval *val, char *buffer, size_t bfsize)
{
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n <= 0) {
if (n) perror("recv data");
return -1;
}
if (desync(val->pair->fd, buffer, n)) {
return -1;
}
val->type = EV_TUNNEL;
return 0;
}
static inline int on_connect(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int e)
{
if (val->flag & FLAG_CONN) {
if (!e) {
val->type = EV_TUNNEL;
mod_etype(pool, val, POLLOUT, 0);
}
if (val->pair->flag) {
int error = 0;
socklen_t len = sizeof(error);
if (e) {
if (getsockopt(val->fd, SOL_SOCKET,
SO_ERROR, (char *)&error, &len)) {
perror("getsockopt SO_ERROR");
return -1;
}
}
if (resp_error(val->pair->fd,
error, val->pair->flag) < 0) {
perror("send");
return -1;
}
if (e) return -1;
val->pair->type = EV_CONNECT;
return 0;
}
else if (!e) {
val = val->pair;
mod_etype(pool, val, POLLIN, 1);
}
}
if (e) return -1;
return on_data(val, buffer, bfsize);
}
static inline int on_tunnel(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out)
{
ssize_t n = 0;
int peek = 0;
struct eval *pair = val->pair;
if (val->flag & FLAG_NOSEND && out) {
val->flag &= ~FLAG_NOSEND;
mod_etype(pool, val, POLLOUT, 0);
mod_etype(pool, val->pair, POLLIN, 1);
pair = val;
val = val->pair;
}
do {
if (pair->send_count >= params.nack_max) {
int wn = 0;
if (nsendc(pair->fd, &wn)) {
return -1;
}
pair->send_count = wn;
if (wn) {
LOG(LOG_S, "not ack: %d (fd: %d)\n", wn, pair->fd);
}
if (wn >= params.nack_max) {
peek = MSG_PEEK;
}
}
n = recv(val->fd, buffer, bfsize, peek);
if (n < 0 && errno == EAGAIN)
break;
if (n < 1) {
if (n) perror("recv server");
return -1;
}
if (send(pair->fd, buffer, n, 0) < 0) {
if (errno == EAGAIN && peek) {
LOG(LOG_S, "EAGAIN, set POLLOUT (fd: %d)\n", pair->fd);
mod_etype(pool, val, POLLIN, 0);
mod_etype(pool, pair, POLLOUT, 1);
pair->flag |= FLAG_NOSEND;
break;
}
perror("send");
return -1;
}
if (peek) {
if (recv(val->fd, buffer, n, 0) != n) {
perror("recv");
return -1;
}
}
pair->send_count += n;
} while (n == bfsize);
return 0;
}
int big_loop(int srvfd)
{
size_t bfsize = params.bfsize;
struct poolhd *pool = init_pool(params.max_open * 2 + 1);
if (!pool) {
perror("init pool");
return -1;
}
char *buffer = malloc(params.bfsize);
if (!buffer) {
perror("malloc");
return -1;
}
add_event(pool, EV_ACCEPT, srvfd, 0);
struct eval *val;
int i = -1, etype;
while (NOT_EXIT) {
val = next_event(pool, &i, &etype);
if (!val) {
if (errno == EINTR)
continue;
perror("(e)poll");
break;
}
LOG(LOG_L, "new event: fd: %d, evt: %s\n", val->fd, eid_name[val->type]);
if (!val->fd) {
continue;
}
switch (val->type) {
case EV_ACCEPT:
if (on_accept(pool, val))
NOT_EXIT = 0;
continue;
case EV_REQUEST:
if ((etype & POLLHUP) ||
on_request(pool, val, buffer, bfsize))
del_event(pool, val);
continue;
case EV_TUNNEL:
if ((etype & POLLHUP) ||
on_tunnel(pool, val, buffer, bfsize, etype & POLLOUT))
del_event(pool, val);
continue;
case EV_CONNECT:
if (on_connect(pool, val, buffer, bfsize, etype & POLLERR))
del_event(pool, val);
continue;
case EV_IGNORE:
if (etype & (POLLHUP | POLLERR))
del_event(pool, val);
continue;
default:
fprintf(stderr, "???\n");
NOT_EXIT = 0;
}
}
fprintf(stderr, "exit\n");
free(buffer);
destroy_pool(pool);
return 0;
}
int listener(struct sockaddr_ina srv)
{
if (signal(SIGPIPE, SIG_IGN))
perror("signal SIGPIPE!");
if (signal(SIGINT, on_cancel))
perror("signal SIGINT!");
int srvfd = socket(srv.sa.sa_family, SOCK_STREAM, 0);
if (srvfd < 0) {
perror("socket");
return -1;
}
if (fcntl(srvfd, F_SETFL, O_NONBLOCK) < 0) {
perror("fcntl");
close(srvfd);
return -1;
}
int opt = 1;
if (setsockopt(srvfd, SOL_SOCKET,
SO_REUSEADDR, (char *)&opt, sizeof(opt)) == -1) {
perror("setsockopt");
close(srvfd);
return -1;
}
if (bind(srvfd, &srv.sa, sizeof(srv)) < 0) {
perror("bind");
close(srvfd);
return -1;
}
if (listen(srvfd, 10)) {
perror("listen");
close(srvfd);
return -1;
}
int status = big_loop(srvfd);
close(srvfd);
return status;
}

81
proxy.h Normal file
View file

@ -0,0 +1,81 @@
#include <stdint.h>
#include <arpa/inet.h>
struct sockaddr_ina {
union {
struct sockaddr sa;
struct sockaddr_in in;
struct sockaddr_in6 in6;
};
};
#pragma pack(push, 1)
struct s4_req {
uint8_t ver, cmd;
uint16_t port;
struct in_addr i4;
};
struct s5_req {
uint8_t ver, cmd, zero, atp;
union {
struct in_addr i4;
struct in6_addr i6;
struct {
uint8_t len;
char domain[];
} id;
};
};
struct s5_rep {
uint8_t ver, code, zero, atp;
struct {
struct in_addr i4;
uint16_t port;
};
};
#pragma pack(pop)
enum s_auth {
S_AUTH_NONE = 0x00,
S_AUTH_GSSAPI = 0x01,
S_AUTH_USPA = 0x02,
S_AUTH_BAD = 0xff
};
enum s_atp {
S_ATP_I4 = 0x01,
S_ATP_ID = 0x03,
S_ATP_I6 = 0x04
};
enum s_cmd {
S_CMD_CONN = 0x01,
S_CMD_BIND = 0x02,
S_CMD_AUDP = 0x03
};
enum s_err {
S_ER_OK = 0x00,
S_ER_GEN = 0x01,
S_ER_DENY = 0x02,
S_ER_NET = 0x03,
S_ER_HOST = 0x04,
S_ER_CONN = 0x05,
S_ER_TTL = 0x06,
S_ER_CMD = 0x07,
S_ER_ATP = 0x08
};
enum s4_rep {
S4_OK = 0x5a,
S4_ER = 0x5b
};
#define S_VER5 0x05
#define S_VER4 0x04
int listener(struct sockaddr_ina srv);

37
readme.txt Normal file
View file

@ -0,0 +1,37 @@
Реализация некоторых способов запутывания DPI на Linux в виде SOCKS прокси
В дополнение к "--help":
-m, --method <s|d|f>
Способ десинхронизации, комбинировать нельзя
split:
Разбить первый запрос на два по указанному смещению
Если смещение отрицательное, то считать относительно конца
Реализация: два вызова send
disorder:
Как split, но части отправляются в обратном порядке
Реализация: устанавливаем TTL=1, отправляем первую часть, затем восстанавливаем значение TTL и отправляем вторую
(из-за низкого TTL первая часть не прибудет получателю и ОС выполнит ретрансмисию и снова отправит первую часть, но уже после второй и с нормальным TTL)
fake:
Как disorder, только перед первым запросом отправляется поддельный такого же размера
Реализация: тут используется возможность перезаписи данных после вызове sendfile
помещаем в буффер поддельные данные, вызываем sendfile с таким TTL, чтобы тот не дошел до сервера,
затем перезаписываем данные оригинальными, восстанавливаем TTL и отправляем вторую часть с помощью send
-H, --split-at-host
Если найден SNI или Host, то считать смещение относительно позиции домена
-t, --ttl <num>
TTL для поддельного пакета, чтобы тот не дошел до сервера, но был обработан DPI
-n, --tls-sni <str>
В качестве поддельного пакета для TLS используется заранее записанный ClientHello,
данный параметр модифицирует его, изменяя SNI на указанный
-M, --mod-http <h[,d,r]>
Всякие манипуляции с HTTP пакетом, можно комбинировать
hcsmix:
"Host: name" -> "hOsT: name"
dcsmix:
"Host: name" -> "Host: NaMe"
rmspace:
"Host: name" -> "Host:name\t"