diff --git a/desync.c b/desync.c index c1e0731..f1e9e1c 100644 --- a/desync.c +++ b/desync.c @@ -95,14 +95,26 @@ static bool sock_has_notsent(int sfd) } return tcpi.tcpi_notsent_bytes != 0; } + + +static char *alloc_pktd(size_t n) +{ + char *p = mmap(0, n, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + return p == MAP_FAILED ? 0 : p; +} +#define free_pktd(p, n) munmap(p, n) #else #define sock_has_notsent(sfd) 0 + +#define alloc_pktd(n) malloc(n) +#define free_pktd(p, n) free(p) #endif -static struct packet get_tcp_fake(const char *buffer, size_t n, + +static struct packet get_tcp_fake(const char *buffer, ssize_t n, struct proto_info *info, const struct desync_params *opt) { - struct packet pkt; + struct packet pkt = { 0 }; if (opt->fake_data.data) { pkt = opt->fake_data; } @@ -113,12 +125,50 @@ static struct packet get_tcp_fake(const char *buffer, size_t n, } pkt = info->type == IS_HTTP ? fake_http : fake_tls; } - if (opt->fake_offset) { - if (pkt.size > opt->fake_offset) { - pkt.size -= opt->fake_offset; - pkt.data += opt->fake_offset; + if (info->type != IS_HTTP + && (opt->fake_mod || opt->fake_sni_count)) do { + ssize_t ps = n > pkt.size ? n : pkt.size; + char *p = alloc_pktd(ps); + if (!p) { + uniperror("malloc/mmap"); + break; + } + const char *sni = 0; + if (opt->fake_sni_count) { + sni = opt->fake_sni_list[rand() % opt->fake_sni_count]; + } + do { + if ((opt->fake_mod & FM_ORIG) && info->type == IS_HTTPS) { + memcpy(p, buffer, n); + + if (!sni || !change_tls_sni(sni, p, n, n)) { + break; + } + LOG(LOG_E, "change sni error\n"); + } + memcpy(p, pkt.data, pkt.size); + if (sni && change_tls_sni(sni, p, pkt.size, n) < 0) { + free_pktd(p, ps); + p = 0; + break; + } + } while(0); + if (p) { + if (opt->fake_mod & FM_RAND) { + randomize_tls(p, ps); + } + pkt.data = p; + pkt.size = ps; + pkt.dynamic = 1; + } + } while (0); + + if (opt->fake_offset.m) { + pkt.off = gen_offset(opt->fake_offset.pos, + opt->fake_offset.flag, buffer, n, 0, info); + if (pkt.off > pkt.size || pkt.off < 0) { + pkt.off = 0; } - else pkt.size = 0; } return pkt; } @@ -155,24 +205,30 @@ static ssize_t send_fake(int sfd, const char *buffer, return -1; } char *p = 0; + size_t ms = pos > pkt.size ? pos : pkt.size; ssize_t ret = -1; while (1) { - p = mmap(0, pos, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); - if (p == MAP_FAILED) { - uniperror("mmap"); - p = 0; - break; + if (pkt.dynamic) { + p = pkt.data; + } + else { + p = mmap(0, ms, + PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (p == MAP_FAILED) { + uniperror("mmap"); + p = 0; + break; + } + memcpy(p, pkt.data, pkt.size); } - memcpy(p, pkt.data, pkt.size < pos ? pkt.size : pos); - if (setttl(sfd, opt->ttl ? opt->ttl : DEFAULT_TTL) < 0) { break; } if (opt->md5sig && set_md5sig(sfd, 5)) { break; } - struct iovec vec = { .iov_base = p, .iov_len = pos }; + struct iovec vec = { .iov_base = p + pkt.off, .iov_len = pos }; ssize_t len = vmsplice(fds[1], &vec, 1, SPLICE_F_GIFT); if (len < 0) { @@ -184,7 +240,7 @@ static ssize_t send_fake(int sfd, const char *buffer, uniperror("splice"); break; } - memcpy(p, buffer, pos); + memcpy(p + pkt.off, buffer, pos); if (setttl(sfd, params.def_ttl) < 0) { break; @@ -195,7 +251,9 @@ static ssize_t send_fake(int sfd, const char *buffer, ret = len; break; } - if (p) munmap(p, pos); + if (!pkt.dynamic && p) { + munmap(p, ms); + } close(fds[0]); close(fds[1]); return ret; @@ -273,15 +331,15 @@ static ssize_t send_fake(int sfd, const char *buffer, return -1; } s->tfile = hfile; - ssize_t len = -1; + ssize_t len = -1, ps = pkt.size - pkt.off; while (1) { DWORD wrtcnt = 0; - if (!WriteFile(hfile, pkt.data, pkt.size < pos ? pkt.size : pos, &wrtcnt, 0)) { + if (!WriteFile(hfile, pkt.data + pkt.off, ps < pos ? ps : pos, &wrtcnt, 0)) { uniperror("WriteFile"); break; } - if (pkt.size < pos) { + if (ps < pos) { if (SetFilePointer(hfile, pos, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { uniperror("SetFilePointer"); break; @@ -562,9 +620,14 @@ ssize_t desync(struct poolhd *pool, } else switch (part.m) { #ifdef FAKE_SUPPORT - case DESYNC_FAKE: + case DESYNC_FAKE:; + struct packet pkt = get_tcp_fake(buffer, n, &info, &dp); + if (pos != lp) s = send_fake(sfd, - buffer + lp, pos - lp, &dp, get_tcp_fake(buffer, n, &info, &dp)); + buffer + lp, pos - lp, &dp, pkt); + + if (pkt.dynamic) + free_pktd(pkt.data, pkt.size); break; #endif case DESYNC_DISORDER: @@ -680,10 +743,10 @@ ssize_t desync_udp(int sfd, char *buffer, else { pkt = fake_udp; } - if (dp->fake_offset) { - if (pkt.size > dp->fake_offset) { - pkt.size -= dp->fake_offset; - pkt.data += dp->fake_offset; + if (dp->fake_offset.m) { + if (pkt.size > dp->fake_offset.pos) { + pkt.size -= dp->fake_offset.pos; + pkt.data += dp->fake_offset.pos; } else pkt.size = 0; } diff --git a/desync.h b/desync.h index 8917e83..c4cb1c9 100644 --- a/desync.h +++ b/desync.h @@ -26,4 +26,7 @@ struct proto_info { char init, type; int host_len, host_pos; }; + +static long gen_offset(long pos, int flag, + const char *buffer, size_t n, long lp, struct proto_info *info); #endif diff --git a/main.c b/main.c index 01c465d..93a8c90 100644 --- a/main.c +++ b/main.c @@ -32,13 +32,13 @@ ASSERT(sizeof(struct in6_addr) == 16) struct packet fake_tls = { - sizeof(tls_data), tls_data + sizeof(tls_data), tls_data, 0, 0 }, fake_http = { - sizeof(http_data), http_data + sizeof(http_data), http_data, 0, 0 }, fake_udp = { - sizeof(udp_data), udp_data + sizeof(udp_data), udp_data, 0, 0 }; @@ -106,11 +106,13 @@ static const char help_text[] = { #ifdef __linux__ " -S, --md5sig Add MD5 Signature option for fake packets\n" #endif - " -n, --tls-sni Change SNI in fake ClientHello\n" + " -n, --fake-sni Change SNI in fake\n" + " Replaced: ? - rand let, # - rand num, * - rand let/num\n" #endif " -t, --ttl TTL of fake packets, default 8\n" - " -O, --fake-offset Fake data start offset\n" + " -O, --fake-offset Fake data start offset\n" " -l, --fake-data Set custom fake packet\n" + " -Q, --fake-tls-mod Modify fake TLS CH: rand,orig\n" " -e, --oob-data Set custom OOB data\n" " -M, --mod-http Modify HTTP: hcsmix,dcsmix,rmspace\n" " -r, --tlsrec Make TLS record at position\n" @@ -164,11 +166,12 @@ const struct option options[] = { #ifdef __linux__ {"md5sig", 0, 0, 'S'}, #endif - {"tls-sni", 1, 0, 'n'}, + {"fake-sni", 1, 0, 'n'}, #endif {"ttl", 1, 0, 't'}, {"fake-data", 1, 0, 'l'}, {"fake-offset", 1, 0, 'O'}, + {"fake-tls-mod", 1, 0, 'Q'}, {"oob-data", 1, 0, 'e'}, {"mod-http", 1, 0, 'M'}, {"tlsrec", 1, 0, 'r'}, @@ -602,6 +605,10 @@ void clear_params(void) mem_destroy(s.ipset); s.hosts = 0; } + if (s.fake_sni_list != 0) { + free(s.fake_sni_list); + s.fake_sni_list = 0; + } } free(params.dp); params.dp = 0; @@ -916,20 +923,39 @@ int main(int argc, char **argv) break; case 'O': - val = strtol(optarg, &end, 0); - if (val <= 0 || *end) + if (parse_offset(&dp->fake_offset, optarg)) { invalid = 1; - else - dp->fake_offset = val; + break; + } else dp->fake_offset.m = 1; break; - case 'n': - if (change_tls_sni(optarg, fake_tls.data, fake_tls.size)) { - fprintf(stderr, "error chsni\n"); - clear_params(); - return -1; + case 'Q': + end = optarg; + while (end && !invalid) { + switch (*end) { + case 'r': + dp->fake_mod |= FM_RAND; + break; + case 'o': + dp->fake_mod |= FM_ORIG; + break; + default: + invalid = 1; + continue; + } + end = strchr(end, ','); + if (end) end++; } - printf("sni: %s\n", optarg); + break; + + case 'n':; + const char **p = add((void *)&dp->fake_sni_list, + &dp->fake_sni_count, sizeof(optarg)); + if (!p) { + invalid = 1; + continue; + } + *p = optarg; break; case 'l': diff --git a/packets.c b/packets.c index 5c10600..04a7957 100644 --- a/packets.c +++ b/packets.c @@ -102,7 +102,7 @@ static size_t find_tls_ext_offset(uint16_t type, } -static size_t chello_ext_offset(uint16_t type, const char *data, size_t size) +static size_t find_ext_block(const char *data, size_t size) { if (size < 44) { return 0; @@ -114,43 +114,184 @@ static size_t chello_ext_offset(uint16_t type, const char *data, size_t size) uint16_t cip_len = ANTOHS(data, 44 + sid_len); size_t skip = 44 + sid_len + 2 + cip_len + 2; - return find_tls_ext_offset(type, data, size, skip); + return skip > size ? 0 : skip; } -int change_tls_sni(const char *host, char *buffer, size_t bsize) +static int merge_tls_records(char *buffer, ssize_t n) { - size_t sni_offs, pad_offs; + if (n < 5) { + return 0; + } + uint16_t full_sz = 0; + uint16_t r_sz = ANTOHS(buffer, 3); + int i = 0; - if (!(sni_offs = chello_ext_offset(0x00, buffer, bsize))) { + while (1) { + full_sz += r_sz; + if (5 + full_sz > n - 5 + || buffer[5 + full_sz] != *buffer) { + break; + } + r_sz = ANTOHS(buffer, 5 + full_sz + 3); + + if (full_sz + 10 + r_sz > n) { + break; + } + memmove(buffer + 5 + full_sz, + buffer + 10 + full_sz, n - (10 + full_sz)); + i++; + } + SHTONA(buffer, 3, full_sz); + SHTONA(buffer, 7, full_sz - 4); + return i * 5; +} + + +static void copy_name(char *out, const char *name, size_t out_len) +{ + for (size_t i = 0; i < out_len; i++) { + switch (name[i]) { + case '*':; + int r = rand() % (10 + 'z' - 'a' + 1); + out[i] = (r < 10 ? '0' : ('a' - 10)) + r; + break; + case '?': + out[i] = 'a' + (rand() % ('z' - 'a' + 1)); + break; + case '#': + out[i] = '0' + (rand() % 10); + break; + default: + out[i] = name[i]; + } + } +} + + +static int remove_tls_ext(char *buffer, + ssize_t n, size_t skip, uint16_t type) +{ + ssize_t ext_offs = find_tls_ext_offset(type, buffer, n, skip); + if (!ext_offs) { + return 0; + } + uint16_t ext_sz = ANTOHS(buffer, ext_offs + 2); + ssize_t ext_end = ext_offs + 4 + ext_sz; + if (ext_end > n) { + return 0; + } + memmove(buffer + ext_offs, buffer + ext_end, n - ext_end); + return ext_sz + 4; +} + + +static int resize_ech_ext(char *buffer, + ssize_t n, size_t skip, int inc) +{ + ssize_t ech_offs = find_tls_ext_offset(0xfe0d, buffer, n, skip); + if (!ech_offs) { + return 0; + } + uint16_t ech_sz = ANTOHS(buffer, ech_offs + 2); + ssize_t ech_end = ech_offs + 4 + ech_sz; + + if (ech_sz < 12 || ech_end > n) { + return 0; + } + uint16_t enc_sz = ANTOHS(buffer, ech_offs + 4 + 6); + ssize_t pay_offs = ech_offs + 4 + 8 + enc_sz; + uint16_t pay_sz = ech_sz - (8 + enc_sz + 2); + + if (pay_offs + 2 > n || pay_sz < -inc) { + return 0; + } + SHTONA(buffer, ech_offs + 2, ech_sz + inc); + SHTONA(buffer, pay_offs, pay_sz + inc); + + memmove(buffer + ech_end + inc, buffer + ech_end, n - ech_end); + return inc; +} + + +static void resize_sni(char *buffer, ssize_t n, + ssize_t sni_offs, ssize_t sni_sz, ssize_t new_sz) +{ + SHTONA(buffer, sni_offs + 2, new_sz + 5); + SHTONA(buffer, sni_offs + 4, new_sz + 3); + SHTONA(buffer, sni_offs + 7, new_sz); + + ssize_t sni_end = sni_offs + 4 + sni_sz; + memmove(buffer + sni_end + new_sz - (sni_sz - 5), buffer + sni_end, n - sni_end); +} + + +int change_tls_sni(const char *host, char *buffer, ssize_t n, ssize_t nn) +{ + int avail = merge_tls_records(buffer, n); + avail += (nn - n); + + uint16_t r_sz = ANTOHS(buffer, 3); + r_sz += avail; + + size_t skip = find_ext_block(buffer, n); + if (!skip) { return -1; } - if (!(pad_offs = chello_ext_offset(0x15, buffer, bsize))) { + ssize_t sni_offs = find_tls_ext_offset(0x00, buffer, n, skip); + if (!sni_offs) { 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); + uint16_t sni_sz = ANTOHS(buffer, sni_offs + 2); - ssize_t diff = new_sz - old_sz; - - if ((free_sz != (bsize - pad_offs - 4)) - || free_sz < diff) { + if (sni_offs + 4 + sni_sz > n) { return -1; } - SHTONA(sni, 2, old_sz + diff + 5); - SHTONA(sni, 4, old_sz + diff + 3); - SHTONA(sni, 7, old_sz + diff); - SHTONA(pad, 2, free_sz - diff); + int diff = (int )new_sz - (sni_sz - 5); + avail -= diff; - char *host_end = sni + 9 + old_sz; - int oth_sz = bsize - (sni_offs + 9 + old_sz); + if (diff < 0 && avail > 0) { + resize_sni(buffer, n, sni_offs, sni_sz, new_sz); + diff = 0; + } + if (avail) { + avail -= resize_ech_ext(buffer, n, skip, avail); + } + uint16_t exts[] = { + 0x0015, // padding + 0x0031, // post_handshake_auth + 0x0010, // ALPN + 0x001c, // record_size_limit + 0x0023, // session_ticket + 0x0005, // status_request + 0x0022, // delegated_credentials + 0x0012, // signed_certificate_timestamp + 0x001b, // compress_certificate + 0 + }; + for (uint16_t *e = exts; avail && avail < 4; e++) { + if (!*e) { + return -1; + } + avail += remove_tls_ext(buffer, n, skip, *e); + } + if (!(sni_offs = find_tls_ext_offset(0x00, buffer, n, skip))) { + return -1; + } + if (diff) { + resize_sni(buffer, n, sni_offs, sni_sz, new_sz); + } + copy_name(buffer + sni_offs + 9, host, new_sz); - memmove(host_end + diff, host_end, oth_sz); - memcpy(sni + 9, host, new_sz); + if (avail >= 4) { + SHTONA(buffer, 5 + r_sz - avail, 0x0015); + SHTONA(buffer, 5 + r_sz - avail + 2, avail - 4); + memset(buffer + 5 + r_sz - avail + 4, 0, avail - 4); + } + SHTONA(buffer, 3, r_sz); + SHTONA(buffer, 7, r_sz - 4); + SHTONA(buffer, skip, 5 + r_sz - skip - 2); return 0; } @@ -168,7 +309,11 @@ int parse_tls(const char *buffer, size_t bsize, char **hs) if (!is_tls_chello(buffer, bsize)) { return 0; } - size_t sni_offs = chello_ext_offset(0x00, buffer, bsize); + size_t skip = find_ext_block(buffer, bsize); + if (!skip) { + return 0; + } + size_t sni_offs = find_tls_ext_offset(0x00, buffer, bsize, skip); if (!sni_offs || (sni_offs + 12) >= bsize) { return 0; @@ -394,3 +539,48 @@ int part_tls(char *buffer, size_t bsize, ssize_t n, long pos) SHTONA(buffer, 5 + pos + 3, r_sz - pos); return 5; } + + +static void gen_rand_array(char *out, size_t len) +{ + for (; len; len--, out++) { + uint8_t c = rand() % 256; + *((uint8_t *)out) = c; + } +} + + +void randomize_tls(char *buffer, ssize_t n) +{ + if (n < 44) { + return; + } + uint8_t sid_len = buffer[43]; + if (n < (44l + sid_len + 2)) { + return; + } + gen_rand_array(buffer + 11, 32); + gen_rand_array(buffer + 44, sid_len); + + size_t skip = find_ext_block(buffer, n); + if (!skip) { + return; + } + ssize_t ks_offs = find_tls_ext_offset(0x0033, buffer, n, skip); + if (!ks_offs || ks_offs + 6 >= n) { + return; + } + int ks_sz = ANTOHS(buffer, ks_offs + 2); + if (ks_offs + 4 + ks_sz > n) { + return; + } + ssize_t g_offs = ks_offs + 4 + 2; + while (g_offs + 4 < ks_offs + 4 + ks_sz) { + uint16_t g_sz = ANTOHS(buffer, g_offs + 2); + if (ks_offs + 4 + g_sz > n) { + return; + } + gen_rand_array(buffer + g_offs + 4, g_sz); + g_offs += 4 + g_sz; + } +} diff --git a/packets.h b/packets.h index 48ab133..16f002a 100644 --- a/packets.h +++ b/packets.h @@ -22,7 +22,7 @@ extern char tls_data[517]; extern char http_data[43]; extern char udp_data[64]; -int change_tls_sni(const char *host, char *buffer, size_t bsize); +int change_tls_sni(const char *host, char *buffer, ssize_t bsize, ssize_t nn); bool is_tls_chello(const char *buffer, size_t bsize); @@ -42,6 +42,8 @@ bool is_tls_shello(const char *buffer, size_t bsize); int part_tls(char *buffer, size_t bsize, ssize_t n, long pos); +void randomize_tls(char *buffer, ssize_t n); + //bool is_dns_req(char *buffer, size_t n); //bool is_quic_initial(char *buffer, size_t bsize); diff --git a/params.h b/params.h index 2e7b3a4..6035c38 100644 --- a/params.h +++ b/params.h @@ -37,6 +37,9 @@ #define AUTO_NOBUFF -1 #define AUTO_NOSAVE 0 +#define FM_RAND 1 +#define FM_ORIG 2 + enum demode { DESYNC_NONE, DESYNC_SPLIT, @@ -67,6 +70,8 @@ struct part { struct packet { ssize_t size; char *data; + ssize_t off; + bool dynamic; }; struct desync_params { @@ -74,7 +79,10 @@ struct desync_params { bool md5sig; struct packet fake_data; int udp_fake_count; - int fake_offset; + struct part fake_offset; + int fake_sni_count; + const char **fake_sni_list; + int fake_mod; bool drop_sack; char oob_char[2];