#define _GNU_SOURCE #include #include #include #include #include #include #include #include #ifndef IP_NODEFRAG // for very old toolchains #define IP_NODEFRAG 22 #endif #include "darkmagic.h" #include "helpers.h" #include "params.h" #include "nfqws.h" #ifdef __CYGWIN__ #include #include #endif uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment) { return htonl(ntohl(netorder_value) + cpuorder_increment); } uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment) { return htons(ntohs(netorder_value) + cpuorder_increment); } uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind) { uint8_t *t = (uint8_t *)(tcp + 1); uint8_t *end = (uint8_t *)tcp + (tcp->th_off << 2); while (t < end) { switch (*t) { case 0: // end return NULL; case 1: // noop t++; break; default: // kind,len,data if ((t + 1) >= end || t[1] < 2 || (t + t[1]) > end) return NULL; if (*t == kind) return t; t += t[1]; break; } } return NULL; } uint32_t *tcp_find_timestamps(struct tcphdr *tcp) { uint8_t *t = tcp_find_option(tcp, 8); return (t && t[1] == 10) ? (uint32_t *)(t + 2) : NULL; } uint8_t tcp_find_scale_factor(const struct tcphdr *tcp) { uint8_t *scale = tcp_find_option((struct tcphdr *)tcp, 3); // tcp option 3 - scale factor if (scale && scale[1] == 3) return scale[2]; return SCALE_NONE; } bool tcp_has_fastopen(const struct tcphdr *tcp) { uint8_t *opt; // new style RFC7413 opt = tcp_find_option((struct tcphdr *)tcp, 34); if (opt) return true; // old style RFC6994 opt = tcp_find_option((struct tcphdr *)tcp, 254); return opt && opt[1] >= 4 && opt[2] == 0xF9 && opt[3] == 0x89; } // n prefix (nsport, nwsize) means network byte order static void fill_tcphdr( struct tcphdr *tcp, uint32_t fooling, uint8_t tcp_flags, uint32_t nseq, uint32_t nack_seq, uint16_t nsport, uint16_t ndport, uint16_t nwsize, uint8_t scale_factor, uint32_t *timestamps, uint32_t badseq_increment, uint32_t badseq_ack_increment, uint16_t data_len) { char *tcpopt = (char *)(tcp + 1); uint8_t t = 0; memset(tcp, 0, sizeof(*tcp)); tcp->th_sport = nsport; tcp->th_dport = ndport; if (fooling & FOOL_BADSEQ) { tcp->th_seq = net32_add(nseq, badseq_increment); tcp->th_ack = net32_add(nack_seq, badseq_ack_increment); } else { tcp->th_seq = nseq; tcp->th_ack = nack_seq; } tcp->th_off = 5; if ((fooling & FOOL_DATANOACK) && !(tcp_flags & (TH_SYN | TH_RST)) && data_len) tcp_flags &= ~TH_ACK; *((uint8_t *)tcp + 13) = tcp_flags; tcp->th_win = nwsize; if (fooling & FOOL_MD5SIG) { tcpopt[0] = 19; // kind tcpopt[1] = 18; // len *(uint32_t *)(tcpopt + 2) = random(); *(uint32_t *)(tcpopt + 6) = random(); *(uint32_t *)(tcpopt + 10) = random(); *(uint32_t *)(tcpopt + 14) = random(); t = 18; } if (timestamps || (fooling & FOOL_TS)) { tcpopt[t] = 8; // kind tcpopt[t + 1] = 10; // len // forge only TSecr if orig timestamp is present *(uint32_t *)(tcpopt + t + 2) = timestamps ? timestamps[0] : -1; *(uint32_t *)(tcpopt + t + 6) = (timestamps && !(fooling & FOOL_TS)) ? timestamps[1] : -1; t += 10; } if (scale_factor != SCALE_NONE) { tcpopt[t++] = 3; tcpopt[t++] = 3; tcpopt[t++] = scale_factor; } while (t & 3) tcpopt[t++] = 1; // noop tcp->th_off += t >> 2; tcp->th_sum = 0; } static uint16_t tcpopt_len(uint32_t fooling, const uint32_t *timestamps, uint8_t scale_factor) { uint16_t t = 0; if (fooling & FOOL_MD5SIG) t = 18; if ((fooling & FOOL_TS) || timestamps) t += 10; if (scale_factor != SCALE_NONE) t += 3; return (t + 3) & ~3; } // n prefix (nsport, nwsize) means network byte order static void fill_udphdr(struct udphdr *udp, uint16_t nsport, uint16_t ndport, uint16_t len_payload) { udp->uh_sport = nsport; udp->uh_dport = ndport; udp->uh_ulen = htons(len_payload + sizeof(struct udphdr)); udp->uh_sum = 0; } static void fill_iphdr(struct ip *ip, const struct in_addr *src, const struct in_addr *dst, uint16_t pktlen, uint8_t proto, uint8_t ttl) { ip->ip_tos = 0; ip->ip_sum = 0; ip->ip_off = 0; ip->ip_v = 4; ip->ip_hl = 5; ip->ip_len = htons(pktlen); ip->ip_id = 0; ip->ip_ttl = ttl; ip->ip_p = proto; ip->ip_src = *src; ip->ip_dst = *dst; } static void fill_ip6hdr(struct ip6_hdr *ip6, const struct in6_addr *src, const struct in6_addr *dst, uint16_t payloadlen, uint8_t proto, uint8_t ttl) { ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000); ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(payloadlen); ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = proto; ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl; ip6->ip6_src = *src; ip6->ip6_dst = *dst; } bool prepare_tcp_segment4( const struct sockaddr_in *src, const struct sockaddr_in *dst, uint8_t tcp_flags, uint32_t nseq, uint32_t nack_seq, uint16_t nwsize, uint8_t scale_factor, uint32_t *timestamps, uint8_t ttl, uint32_t fooling, uint32_t badseq_increment, uint32_t badseq_ack_increment, const void *data, uint16_t len, uint8_t *buf, size_t *buflen) { uint16_t tcpoptlen = tcpopt_len(fooling, timestamps, scale_factor); uint16_t ip_payload_len = sizeof(struct tcphdr) + tcpoptlen + len; uint16_t pktlen = sizeof(struct ip) + ip_payload_len; if (pktlen > *buflen) return false; struct ip *ip = (struct ip *)buf; struct tcphdr *tcp = (struct tcphdr *)(ip + 1); uint8_t *payload = (uint8_t *)(tcp + 1) + tcpoptlen; fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_TCP, ttl); fill_tcphdr(tcp, fooling, tcp_flags, nseq, nack_seq, src->sin_port, dst->sin_port, nwsize, scale_factor, timestamps, badseq_increment, badseq_ack_increment, len); memcpy(payload, data, len); tcp4_fix_checksum(tcp, ip_payload_len, &ip->ip_src, &ip->ip_dst); if (fooling & FOOL_BADSUM) tcp->th_sum ^= htons(0xBEAF); *buflen = pktlen; return true; } bool prepare_tcp_segment6( const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, uint8_t tcp_flags, uint32_t nseq, uint32_t nack_seq, uint16_t nwsize, uint8_t scale_factor, uint32_t *timestamps, uint8_t ttl, uint32_t fooling, uint32_t badseq_increment, uint32_t badseq_ack_increment, const void *data, uint16_t len, uint8_t *buf, size_t *buflen) { uint16_t tcpoptlen = tcpopt_len(fooling, timestamps, scale_factor); uint16_t transport_payload_len = sizeof(struct tcphdr) + tcpoptlen + len; uint16_t ip_payload_len = transport_payload_len + 8 * !!((fooling & (FOOL_HOPBYHOP | FOOL_HOPBYHOP2)) == FOOL_HOPBYHOP) + 16 * !!(fooling & FOOL_HOPBYHOP2) + 8 * !!(fooling & FOOL_DESTOPT) + 8 * !!(fooling & FOOL_IPFRAG1); uint16_t pktlen = sizeof(struct ip6_hdr) + ip_payload_len; if (pktlen > *buflen) return false; struct ip6_hdr *ip6 = (struct ip6_hdr *)buf; struct tcphdr *tcp = (struct tcphdr *)(ip6 + 1); uint8_t proto = IPPROTO_TCP, *nexttype = NULL; if (fooling & (FOOL_HOPBYHOP | FOOL_HOPBYHOP2)) { struct ip6_hbh *hbh = (struct ip6_hbh *)tcp; tcp = (struct tcphdr *)((uint8_t *)tcp + 8); memset(hbh, 0, 8); // extra HOPBYHOP header. standard violation if (fooling & FOOL_HOPBYHOP2) { hbh = (struct ip6_hbh *)tcp; tcp = (struct tcphdr *)((uint8_t *)tcp + 8); memset(hbh, 0, 8); } hbh->ip6h_nxt = IPPROTO_TCP; nexttype = &hbh->ip6h_nxt; proto = IPPROTO_HOPOPTS; } if (fooling & FOOL_DESTOPT) { struct ip6_dest *dest = (struct ip6_dest *)tcp; tcp = (struct tcphdr *)((uint8_t *)tcp + 8); memset(dest, 0, 8); dest->ip6d_nxt = IPPROTO_TCP; if (nexttype) *nexttype = IPPROTO_DSTOPTS; else proto = IPPROTO_DSTOPTS; nexttype = &dest->ip6d_nxt; } if (fooling & FOOL_IPFRAG1) { struct ip6_frag *frag = (struct ip6_frag *)tcp; tcp = (struct tcphdr *)((uint8_t *)tcp + sizeof(struct ip6_frag)); frag->ip6f_nxt = IPPROTO_TCP; frag->ip6f_ident = htonl(1 + random() % 0xFFFFFFFF); frag->ip6f_reserved = 0; frag->ip6f_offlg = 0; if (nexttype) *nexttype = IPPROTO_FRAGMENT; else proto = IPPROTO_FRAGMENT; } uint8_t *payload = (uint8_t *)(tcp + 1) + tcpoptlen; fill_ip6hdr(ip6, &src->sin6_addr, &dst->sin6_addr, ip_payload_len, proto, ttl); fill_tcphdr(tcp, fooling, tcp_flags, nseq, nack_seq, src->sin6_port, dst->sin6_port, nwsize, scale_factor, timestamps, badseq_increment, badseq_ack_increment, len); memcpy(payload, data, len); tcp6_fix_checksum(tcp, transport_payload_len, &ip6->ip6_src, &ip6->ip6_dst); if (fooling & FOOL_BADSUM) tcp->th_sum ^= htons(0xBEAF); *buflen = pktlen; return true; } bool prepare_tcp_segment( const struct sockaddr *src, const struct sockaddr *dst, uint8_t tcp_flags, uint32_t nseq, uint32_t nack_seq, uint16_t nwsize, uint8_t scale_factor, uint32_t *timestamps, uint8_t ttl, uint32_t fooling, uint32_t badseq_increment, uint32_t badseq_ack_increment, const void *data, uint16_t len, uint8_t *buf, size_t *buflen) { return (src->sa_family == AF_INET && dst->sa_family == AF_INET) ? prepare_tcp_segment4((struct sockaddr_in *)src, (struct sockaddr_in *)dst, tcp_flags, nseq, nack_seq, nwsize, scale_factor, timestamps, ttl, fooling, badseq_increment, badseq_ack_increment, data, len, buf, buflen) : (src->sa_family == AF_INET6 && dst->sa_family == AF_INET6) ? prepare_tcp_segment6((struct sockaddr_in6 *)src, (struct sockaddr_in6 *)dst, tcp_flags, nseq, nack_seq, nwsize, scale_factor, timestamps, ttl, fooling, badseq_increment, badseq_ack_increment, data, len, buf, buflen) : false; } // padlen<0 means payload shrinking bool prepare_udp_segment4( const struct sockaddr_in *src, const struct sockaddr_in *dst, uint8_t ttl, uint32_t fooling, const uint8_t *padding, size_t padding_size, int padlen, const void *data, uint16_t len, uint8_t *buf, size_t *buflen) { if ((len + padlen) <= 0) padlen = -(int)len + 1; // do not allow payload to be less that 1 byte if ((len + padlen) > 0xFFFF) padlen = 0xFFFF - len; // do not allow payload size to exceed u16 range if (padlen < 0) { len += padlen; padlen = 0; } uint16_t datalen = (uint16_t)(len + padlen); uint16_t ip_payload_len = sizeof(struct udphdr) + datalen; uint16_t pktlen = sizeof(struct ip) + ip_payload_len; if (pktlen > *buflen) return false; struct ip *ip = (struct ip *)buf; struct udphdr *udp = (struct udphdr *)(ip + 1); uint8_t *payload = (uint8_t *)(udp + 1); fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_UDP, ttl); fill_udphdr(udp, src->sin_port, dst->sin_port, datalen); memcpy(payload, data, len); if (padding) fill_pattern(payload + len, padlen, padding, padding_size); else memset(payload + len, 0, padlen); udp4_fix_checksum(udp, ip_payload_len, &ip->ip_src, &ip->ip_dst); if (fooling & FOOL_BADSUM) udp->uh_sum ^= htons(0xBEAF); *buflen = pktlen; return true; } bool prepare_udp_segment6( const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, uint8_t ttl, uint32_t fooling, const uint8_t *padding, size_t padding_size, int padlen, const void *data, uint16_t len, uint8_t *buf, size_t *buflen) { if ((len + padlen) <= 0) padlen = -(int)len + 1; // do not allow payload to be less that 1 byte if ((len + padlen) > 0xFFFF) padlen = 0xFFFF - len; // do not allow payload size to exceed u16 range if (padlen < 0) { len += padlen; padlen = 0; } uint16_t datalen = (uint16_t)(len + padlen); uint16_t transport_payload_len = sizeof(struct udphdr) + datalen; uint16_t ip_payload_len = transport_payload_len + 8 * !!((fooling & (FOOL_HOPBYHOP | FOOL_HOPBYHOP2)) == FOOL_HOPBYHOP) + 16 * !!(fooling & FOOL_HOPBYHOP2) + 8 * !!(fooling & FOOL_DESTOPT) + 8 * !!(fooling & FOOL_IPFRAG1); uint16_t pktlen = sizeof(struct ip6_hdr) + ip_payload_len; if (pktlen > *buflen) return false; struct ip6_hdr *ip6 = (struct ip6_hdr *)buf; struct udphdr *udp = (struct udphdr *)(ip6 + 1); uint8_t proto = IPPROTO_UDP, *nexttype = NULL; if (fooling & (FOOL_HOPBYHOP | FOOL_HOPBYHOP2)) { struct ip6_hbh *hbh = (struct ip6_hbh *)udp; udp = (struct udphdr *)((uint8_t *)udp + 8); memset(hbh, 0, 8); // extra HOPBYHOP header. standard violation if (fooling & FOOL_HOPBYHOP2) { hbh = (struct ip6_hbh *)udp; udp = (struct udphdr *)((uint8_t *)udp + 8); memset(hbh, 0, 8); } hbh->ip6h_nxt = IPPROTO_UDP; nexttype = &hbh->ip6h_nxt; proto = IPPROTO_HOPOPTS; } if (fooling & FOOL_DESTOPT) { struct ip6_dest *dest = (struct ip6_dest *)udp; udp = (struct udphdr *)((uint8_t *)udp + 8); memset(dest, 0, 8); dest->ip6d_nxt = IPPROTO_UDP; if (nexttype) *nexttype = IPPROTO_DSTOPTS; else proto = IPPROTO_DSTOPTS; nexttype = &dest->ip6d_nxt; } if (fooling & FOOL_IPFRAG1) { struct ip6_frag *frag = (struct ip6_frag *)udp; udp = (struct udphdr *)((uint8_t *)udp + sizeof(struct ip6_frag)); frag->ip6f_nxt = IPPROTO_UDP; frag->ip6f_ident = htonl(1 + random() % 0xFFFFFFFF); frag->ip6f_reserved = 0; frag->ip6f_offlg = 0; if (nexttype) *nexttype = IPPROTO_FRAGMENT; else proto = IPPROTO_FRAGMENT; } uint8_t *payload = (uint8_t *)(udp + 1); fill_ip6hdr(ip6, &src->sin6_addr, &dst->sin6_addr, ip_payload_len, proto, ttl); fill_udphdr(udp, src->sin6_port, dst->sin6_port, datalen); memcpy(payload, data, len); if (padding) fill_pattern(payload + len, padlen, padding, padding_size); else memset(payload + len, 0, padlen); udp6_fix_checksum(udp, transport_payload_len, &ip6->ip6_src, &ip6->ip6_dst); if (fooling & FOOL_BADSUM) udp->uh_sum ^= htons(0xBEAF); *buflen = pktlen; return true; } bool prepare_udp_segment( const struct sockaddr *src, const struct sockaddr *dst, uint8_t ttl, uint32_t fooling, const uint8_t *padding, size_t padding_size, int padlen, const void *data, uint16_t len, uint8_t *buf, size_t *buflen) { return (src->sa_family == AF_INET && dst->sa_family == AF_INET) ? prepare_udp_segment4((struct sockaddr_in *)src, (struct sockaddr_in *)dst, ttl, fooling, padding, padding_size, padlen, data, len, buf, buflen) : (src->sa_family == AF_INET6 && dst->sa_family == AF_INET6) ? prepare_udp_segment6((struct sockaddr_in6 *)src, (struct sockaddr_in6 *)dst, ttl, fooling, padding, padding_size, padlen, data, len, buf, buflen) : false; } bool ip6_insert_simple_hdr(uint8_t type, uint8_t *data_pkt, size_t len_pkt, uint8_t *buf, size_t *buflen) { if ((len_pkt + 8) <= *buflen && len_pkt >= sizeof(struct ip6_hdr)) { struct ip6_hdr *ip6 = (struct ip6_hdr *)buf; struct ip6_ext *hdr = (struct ip6_ext *)(ip6 + 1); *ip6 = *(struct ip6_hdr *)data_pkt; memset(hdr, 0, 8); memcpy((uint8_t *)hdr + 8, data_pkt + sizeof(struct ip6_hdr), len_pkt - sizeof(struct ip6_hdr)); hdr->ip6e_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = type; ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = net16_add(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen, 8); *buflen = len_pkt + 8; return true; } return false; } // split IPv4 packet into 2 fragments at data payload position frag_pos bool ip_frag4( const uint8_t *pkt, size_t pkt_size, size_t frag_pos, uint32_t ident, uint8_t *pkt1, size_t *pkt1_size, uint8_t *pkt2, size_t *pkt2_size) { uint16_t hdrlen, payload_len; // frag_pos must be 8-byte aligned if (frag_pos & 7 || pkt_size < sizeof(struct ip)) return false; payload_len = htons(((struct ip *)pkt)->ip_len); hdrlen = ((struct ip *)pkt)->ip_hl << 2; if (payload_len > pkt_size || hdrlen > pkt_size || hdrlen > payload_len) return false; payload_len -= hdrlen; if (frag_pos >= payload_len || *pkt1_size < (hdrlen + frag_pos) || *pkt2_size < (hdrlen + payload_len - frag_pos)) return false; memcpy(pkt1, pkt, hdrlen + frag_pos); ((struct ip *)pkt1)->ip_off = htons(IP_MF); ((struct ip *)pkt1)->ip_len = htons(hdrlen + frag_pos); if (ident != (uint32_t)-1) ((struct ip *)pkt1)->ip_id = (uint16_t)ident; *pkt1_size = hdrlen + frag_pos; ip4_fix_checksum((struct ip *)pkt1); memcpy(pkt2, pkt, hdrlen); memcpy(pkt2 + hdrlen, pkt + hdrlen + frag_pos, payload_len - frag_pos); ((struct ip *)pkt2)->ip_off = htons((uint16_t)frag_pos >> 3 & IP_OFFMASK); ((struct ip *)pkt2)->ip_len = htons(hdrlen + payload_len - frag_pos); if (ident != (uint32_t)-1) ((struct ip *)pkt2)->ip_id = (uint16_t)ident; *pkt2_size = hdrlen + payload_len - frag_pos; ip4_fix_checksum((struct ip *)pkt2); return true; } bool ip_frag6( const uint8_t *pkt, size_t pkt_size, size_t frag_pos, uint32_t ident, uint8_t *pkt1, size_t *pkt1_size, uint8_t *pkt2, size_t *pkt2_size) { size_t payload_len, unfragmentable; uint8_t *last_header_type; uint8_t proto; struct ip6_frag *frag; const uint8_t *payload; if (frag_pos & 7 || pkt_size < sizeof(struct ip6_hdr)) return false; payload_len = sizeof(struct ip6_hdr) + htons(((struct ip6_hdr *)pkt)->ip6_ctlun.ip6_un1.ip6_un1_plen); if (pkt_size < payload_len) return false; payload = pkt; proto_skip_ipv6((uint8_t **)&payload, &payload_len, &proto, &last_header_type); unfragmentable = payload - pkt; // printf("pkt_size=%zu FRAG_POS=%zu payload_len=%zu unfragmentable=%zu dh=%zu\n",pkt_size,frag_pos,payload_len,unfragmentable,last_header_type - pkt); if (frag_pos >= payload_len || *pkt1_size < (unfragmentable + sizeof(struct ip6_frag) + frag_pos) || *pkt2_size < (unfragmentable + sizeof(struct ip6_frag) + payload_len - frag_pos)) { return false; } memcpy(pkt1, pkt, unfragmentable); ((struct ip6_hdr *)pkt1)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(unfragmentable - sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + frag_pos); pkt1[last_header_type - pkt] = IPPROTO_FRAGMENT; frag = (struct ip6_frag *)(pkt1 + unfragmentable); frag->ip6f_nxt = proto; frag->ip6f_reserved = 0; frag->ip6f_offlg = IP6F_MORE_FRAG; frag->ip6f_ident = ident; memcpy(frag + 1, pkt + unfragmentable, frag_pos); *pkt1_size = unfragmentable + sizeof(struct ip6_frag) + frag_pos; memcpy(pkt2, pkt, sizeof(struct ip6_hdr)); ((struct ip6_hdr *)pkt2)->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(unfragmentable - sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + payload_len - frag_pos); pkt2[last_header_type - pkt] = IPPROTO_FRAGMENT; frag = (struct ip6_frag *)(pkt2 + unfragmentable); frag->ip6f_nxt = proto; frag->ip6f_reserved = 0; frag->ip6f_offlg = htons(frag_pos); frag->ip6f_ident = ident; memcpy(frag + 1, pkt + unfragmentable + frag_pos, payload_len - frag_pos); *pkt2_size = unfragmentable + sizeof(struct ip6_frag) + payload_len - frag_pos; return true; } bool ip_frag( const uint8_t *pkt, size_t pkt_size, size_t frag_pos, uint32_t ident, uint8_t *pkt1, size_t *pkt1_size, uint8_t *pkt2, size_t *pkt2_size) { if (proto_check_ipv4(pkt, pkt_size)) return ip_frag4(pkt, pkt_size, frag_pos, ident, pkt1, pkt1_size, pkt2, pkt2_size); else if (proto_check_ipv6(pkt, pkt_size)) return ip_frag6(pkt, pkt_size, frag_pos, ident, pkt1, pkt1_size, pkt2, pkt2_size); else return false; } void rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl) { if (ip) ip->ip_ttl = ttl; if (ip6) ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl; } void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport) { if (sport) *sport = htons(tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0); if (dport) *dport = htons(tcphdr ? tcphdr->th_dport : udphdr ? udphdr->uh_dport : 0); if (proto) *proto = tcphdr ? IPPROTO_TCP : udphdr ? IPPROTO_UDP : -1; } void extract_endpoints(const struct ip *ip, const struct ip6_hdr *ip6hdr, const struct tcphdr *tcphdr, const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst) { if (ip) { struct sockaddr_in *si; if (dst) { si = (struct sockaddr_in *)dst; si->sin_family = AF_INET; si->sin_port = tcphdr ? tcphdr->th_dport : udphdr ? udphdr->uh_dport : 0; si->sin_addr = ip->ip_dst; } if (src) { si = (struct sockaddr_in *)src; si->sin_family = AF_INET; si->sin_port = tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0; si->sin_addr = ip->ip_src; } } else if (ip6hdr) { struct sockaddr_in6 *si; if (dst) { si = (struct sockaddr_in6 *)dst; si->sin6_family = AF_INET6; si->sin6_port = tcphdr ? tcphdr->th_dport : udphdr ? udphdr->uh_dport : 0; si->sin6_addr = ip6hdr->ip6_dst; si->sin6_flowinfo = 0; si->sin6_scope_id = 0; } if (src) { si = (struct sockaddr_in6 *)src; si->sin6_family = AF_INET6; si->sin6_port = tcphdr ? tcphdr->th_sport : udphdr ? udphdr->uh_sport : 0; si->sin6_addr = ip6hdr->ip6_src; si->sin6_flowinfo = 0; si->sin6_scope_id = 0; } } } const char *proto_name(uint8_t proto) { switch (proto) { case IPPROTO_TCP: return "tcp"; case IPPROTO_UDP: return "udp"; case IPPROTO_ICMP: return "icmp"; case IPPROTO_ICMPV6: return "icmp6"; case IPPROTO_IGMP: return "igmp"; case IPPROTO_ESP: return "esp"; case IPPROTO_AH: return "ah"; case IPPROTO_IPV6: return "6in4"; case IPPROTO_IPIP: return "4in4"; #ifdef IPPROTO_GRE case IPPROTO_GRE: return "gre"; #endif #ifdef IPPROTO_SCTP case IPPROTO_SCTP: return "sctp"; #endif default: return NULL; } } static void str_proto_name(char *s, size_t s_len, uint8_t proto) { const char *name = proto_name(proto); if (name) snprintf(s, s_len, "%s", name); else snprintf(s, s_len, "%u", proto); } uint16_t family_from_proto(uint8_t l3proto) { switch (l3proto) { case IPPROTO_IP: return AF_INET; case IPPROTO_IPV6: return AF_INET6; default: return -1; } } static void str_srcdst_ip(char *s, size_t s_len, const void *saddr, const void *daddr) { char s_ip[16], d_ip[16]; *s_ip = *d_ip = 0; inet_ntop(AF_INET, saddr, s_ip, sizeof(s_ip)); inet_ntop(AF_INET, daddr, d_ip, sizeof(d_ip)); snprintf(s, s_len, "%s => %s", s_ip, d_ip); } void str_ip(char *s, size_t s_len, const struct ip *ip) { char ss[35], s_proto[16]; str_srcdst_ip(ss, sizeof(ss), &ip->ip_src, &ip->ip_dst); str_proto_name(s_proto, sizeof(s_proto), ip->ip_p); snprintf(s, s_len, "%s proto=%s ttl=%u", ss, s_proto, ip->ip_ttl); } void print_ip(const struct ip *ip) { char s[66]; str_ip(s, sizeof(s), ip); printf("%s", s); } void str_srcdst_ip6(char *s, size_t s_len, const void *saddr, const void *daddr) { char s_ip[40], d_ip[40]; *s_ip = *d_ip = 0; inet_ntop(AF_INET6, saddr, s_ip, sizeof(s_ip)); inet_ntop(AF_INET6, daddr, d_ip, sizeof(d_ip)); snprintf(s, s_len, "%s => %s", s_ip, d_ip); } void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto) { char ss[83], s_proto[16]; str_srcdst_ip6(ss, sizeof(ss), &ip6hdr->ip6_src, &ip6hdr->ip6_dst); str_proto_name(s_proto, sizeof(s_proto), proto); snprintf(s, s_len, "%s proto=%s ttl=%u", ss, s_proto, ip6hdr->ip6_hlim); } void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto) { char s[128]; str_ip6hdr(s, sizeof(s), ip6hdr, proto); printf("%s", s); } void str_tcphdr(char *s, size_t s_len, const struct tcphdr *tcphdr) { char flags[7], *f = flags; if (tcphdr->th_flags & TH_SYN) *f++ = 'S'; if (tcphdr->th_flags & TH_ACK) *f++ = 'A'; if (tcphdr->th_flags & TH_RST) *f++ = 'R'; if (tcphdr->th_flags & TH_FIN) *f++ = 'F'; if (tcphdr->th_flags & TH_PUSH) *f++ = 'P'; if (tcphdr->th_flags & TH_URG) *f++ = 'U'; *f = 0; snprintf(s, s_len, "sport=%u dport=%u flags=%s seq=%u ack_seq=%u", htons(tcphdr->th_sport), htons(tcphdr->th_dport), flags, htonl(tcphdr->th_seq), htonl(tcphdr->th_ack)); } void print_tcphdr(const struct tcphdr *tcphdr) { char s[80]; str_tcphdr(s, sizeof(s), tcphdr); printf("%s", s); } void str_udphdr(char *s, size_t s_len, const struct udphdr *udphdr) { snprintf(s, s_len, "sport=%u dport=%u", htons(udphdr->uh_sport), htons(udphdr->uh_dport)); } void print_udphdr(const struct udphdr *udphdr) { char s[30]; str_udphdr(s, sizeof(s), udphdr); printf("%s", s); } bool proto_check_ipv4(const uint8_t *data, size_t len) { return len >= 20 && (data[0] & 0xF0) == 0x40 && len >= ((data[0] & 0x0F) << 2); } // move to transport protocol void proto_skip_ipv4(uint8_t **data, size_t *len) { size_t l; l = (**data & 0x0F) << 2; *data += l; *len -= l; } bool proto_check_tcp(const uint8_t *data, size_t len) { return len >= 20 && len >= ((data[12] & 0xF0) >> 2); } void proto_skip_tcp(uint8_t **data, size_t *len) { size_t l; l = ((*data)[12] & 0xF0) >> 2; *data += l; *len -= l; } bool proto_check_udp(const uint8_t *data, size_t len) { return len >= 8 && len >= (data[4] << 8 | data[5]); } void proto_skip_udp(uint8_t **data, size_t *len) { *data += 8; *len -= 8; } bool proto_check_ipv6(const uint8_t *data, size_t len) { return len >= 40 && (data[0] & 0xF0) == 0x60 && (len - 40) >= htons(*(uint16_t *)(data + 4)); // payload length } // move to transport protocol // proto_type = 0 => error void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type, uint8_t **last_header_type) { size_t hdrlen; uint8_t HeaderType; if (proto_type) *proto_type = 0; // put error in advance HeaderType = (*data)[6]; // NextHeader field if (last_header_type) *last_header_type = (*data) + 6; *data += 40; *len -= 40; // skip IPv6 base header while (*len > 0) // need at least one byte for NextHeader field { switch (HeaderType) { case 0: // Hop-by-Hop Options case 43: // routing case 51: // authentication case 60: // Destination Options case 135: // mobility case 139: // Host Identity Protocol Version v2 case 140: // Shim6 if (*len < 2) return; // error hdrlen = 8 + ((*data)[1] << 3); break; case 44: // fragment. length fixed to 8, hdrlen field defined as reserved hdrlen = 8; break; case 59: // no next header return; // error default: // we found some meaningful payload. it can be TCP, UDP, ICMP or some another exotic shit if (proto_type) *proto_type = HeaderType; return; } if (*len < hdrlen) return; // error HeaderType = **data; if (last_header_type) *last_header_type = *data; // advance to the next header location *len -= hdrlen; *data += hdrlen; } // we have garbage } void proto_dissect_l3l4( uint8_t *data, size_t len, struct ip **ip, struct ip6_hdr **ip6, uint8_t *proto, struct tcphdr **tcp, struct udphdr **udp, size_t *transport_len, uint8_t **data_payload, size_t *len_payload) { *ip = NULL; *ip6 = NULL; *proto = 0; *tcp = NULL; *transport_len = 0; *udp = NULL; *data_payload = NULL; *len_payload = 0; if (proto_check_ipv4(data, len)) { *ip = (struct ip *)data; *proto = (*ip)->ip_p; proto_skip_ipv4(&data, &len); } else if (proto_check_ipv6(data, len)) { *ip6 = (struct ip6_hdr *)data; proto_skip_ipv6(&data, &len, proto, NULL); } else { return; } if (*proto == IPPROTO_TCP && proto_check_tcp(data, len)) { *tcp = (struct tcphdr *)data; *transport_len = len; proto_skip_tcp(&data, &len); *data_payload = data; *len_payload = len; } else if (*proto == IPPROTO_UDP && proto_check_udp(data, len)) { *udp = (struct udphdr *)data; *transport_len = len; proto_skip_udp(&data, &len); *data_payload = data; *len_payload = len; } } bool tcp_synack_segment(const struct tcphdr *tcphdr) { /* check for set bits in TCP hdr */ return ((tcphdr->th_flags & (TH_URG | TH_ACK | TH_PUSH | TH_RST | TH_SYN | TH_FIN)) == (TH_ACK | TH_SYN)); } bool tcp_syn_segment(const struct tcphdr *tcphdr) { /* check for set bits in TCP hdr */ return ((tcphdr->th_flags & (TH_URG | TH_ACK | TH_PUSH | TH_RST | TH_SYN | TH_FIN)) == TH_SYN); } bool tcp_ack_segment(const struct tcphdr *tcphdr) { /* check for set bits in TCP hdr */ return ((tcphdr->th_flags & (TH_URG | TH_ACK | TH_PUSH | TH_RST | TH_SYN | TH_FIN)) == TH_ACK); } void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor) { uint8_t *scale, scale_factor_old; if (scale_factor != SCALE_NONE) { scale = tcp_find_option(tcp, 3); // tcp option 3 - scale factor if (scale && scale[1] == 3) // length should be 3 { scale_factor_old = scale[2]; // do not allow increasing scale factor if (scale_factor >= scale_factor_old) DLOG("Scale factor %u unchanged\n", scale_factor_old); else { scale[2] = scale_factor; DLOG("Scale factor change %u => %u\n", scale_factor_old, scale_factor); } } } } // scale_factor=SCALE_NONE - do not change void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor) { uint16_t winsize_old; winsize_old = htons(tcp->th_win); // << scale_factor; tcp->th_win = htons(winsize); DLOG("Window size change %u => %u\n", winsize_old, winsize); tcp_rewrite_wscale(tcp, scale_factor); } #ifdef __CYGWIN__ static HANDLE w_filter = NULL; static OVERLAPPED ovl = {.hEvent = NULL}; static const struct str_list_head *wlan_filter_ssid = NULL, *nlm_filter_net = NULL; static DWORD logical_net_filter_tick = 0; uint32_t w_win32_error = 0; INetworkListManager *pNetworkListManager = NULL; static void guid2str(const GUID *guid, char *str) { snprintf(str, 37, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); } static bool str2guid(const char *str, GUID *guid) { unsigned int u[11], k; if (36 != strlen(str) || 11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", u + 0, u + 1, u + 2, u + 3, u + 4, u + 5, u + 6, u + 7, u + 8, u + 9, u + 10)) return false; guid->Data1 = u[0]; if ((u[1] & 0xFFFF0000) || (u[2] & 0xFFFF0000)) return false; guid->Data2 = (USHORT)u[1]; guid->Data3 = (USHORT)u[2]; for (k = 0; k < 8; k++) { if (u[k + 3] & 0xFFFFFF00) return false; guid->Data4[k] = (UCHAR)u[k + 3]; } return true; } static const char *sNetworkCards = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; // get adapter name from guid string static bool AdapterID2Name(const GUID *guid, char *name, DWORD name_len) { char sguid[39], sidx[32], val[256]; HKEY hkNetworkCards, hkCard; DWORD dwIndex, dwLen; bool bRet = false; WCHAR namew[128]; DWORD namew_len; if (name_len < 2) return false; if ((w_win32_error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, sNetworkCards, 0, KEY_ENUMERATE_SUB_KEYS, &hkNetworkCards)) == ERROR_SUCCESS) { guid2str(guid, sguid + 1); sguid[0] = '{'; sguid[37] = '}'; sguid[38] = '\0'; for (dwIndex = 0;; dwIndex++) { dwLen = sizeof(sidx) - 1; w_win32_error = RegEnumKeyExA(hkNetworkCards, dwIndex, sidx, &dwLen, NULL, NULL, NULL, NULL); if (w_win32_error == ERROR_SUCCESS) { sidx[dwLen] = '\0'; if ((w_win32_error = RegOpenKeyExA(hkNetworkCards, sidx, 0, KEY_QUERY_VALUE, &hkCard)) == ERROR_SUCCESS) { dwLen = sizeof(val) - 1; if ((w_win32_error = RegQueryValueExA(hkCard, "ServiceName", NULL, NULL, val, &dwLen)) == ERROR_SUCCESS) { val[dwLen] = '\0'; if (!strcmp(val, sguid)) { namew_len = sizeof(namew) - sizeof(WCHAR); if ((w_win32_error = RegQueryValueExW(hkCard, L"Description", NULL, NULL, (LPBYTE)namew, &namew_len)) == ERROR_SUCCESS) { namew[namew_len / sizeof(WCHAR)] = L'\0'; if (WideCharToMultiByte(CP_UTF8, 0, namew, -1, name, name_len, NULL, NULL)) bRet = true; } } } RegCloseKey(hkCard); } if (bRet) break; } else break; } RegCloseKey(hkNetworkCards); } return bRet; } bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter) { win_dark_deinit(); if (LIST_EMPTY(ssid_filter)) ssid_filter = NULL; if (LIST_EMPTY(nlm_filter)) nlm_filter = NULL; if (nlm_filter) { if (SUCCEEDED(w_win32_error = CoInitialize(NULL))) { if (FAILED(w_win32_error = CoCreateInstance(&CLSID_NetworkListManager, NULL, CLSCTX_ALL, &IID_INetworkListManager, (LPVOID *)&pNetworkListManager))) { CoUninitialize(); return false; } } else return false; } nlm_filter_net = nlm_filter; wlan_filter_ssid = ssid_filter; return true; } bool win_dark_deinit(void) { if (pNetworkListManager) { pNetworkListManager->lpVtbl->Release(pNetworkListManager); pNetworkListManager = NULL; } if (nlm_filter_net) CoUninitialize(); wlan_filter_ssid = nlm_filter_net = NULL; } bool nlm_list(bool bAll) { bool bRet = true; if (SUCCEEDED(w_win32_error = CoInitialize(NULL))) { INetworkListManager *pNetworkListManager; if (SUCCEEDED(w_win32_error = CoCreateInstance(&CLSID_NetworkListManager, NULL, CLSCTX_ALL, &IID_INetworkListManager, (LPVOID *)&pNetworkListManager))) { IEnumNetworks *pEnumNetworks; if (SUCCEEDED(w_win32_error = pNetworkListManager->lpVtbl->GetNetworks(pNetworkListManager, NLM_ENUM_NETWORK_ALL, &pEnumNetworks))) { INetwork *pNet; INetworkConnection *pConn; IEnumNetworkConnections *pEnumConnections; VARIANT_BOOL bIsConnected, bIsConnectedInet; NLM_NETWORK_CATEGORY category; GUID idNet, idAdapter; BSTR bstrName; char Name[128], Name2[128]; int connected; for (connected = 1; connected >= !bAll; connected--) { for (;;) { if (FAILED(w_win32_error = pEnumNetworks->lpVtbl->Next(pEnumNetworks, 1, &pNet, NULL))) { bRet = false; break; } if (w_win32_error != S_OK) break; if (SUCCEEDED(w_win32_error = pNet->lpVtbl->get_IsConnected(pNet, &bIsConnected)) && SUCCEEDED(w_win32_error = pNet->lpVtbl->get_IsConnectedToInternet(pNet, &bIsConnectedInet)) && SUCCEEDED(w_win32_error = pNet->lpVtbl->GetNetworkId(pNet, &idNet)) && SUCCEEDED(w_win32_error = pNet->lpVtbl->GetCategory(pNet, &category)) && SUCCEEDED(w_win32_error = pNet->lpVtbl->GetName(pNet, &bstrName))) { if (!!bIsConnected == connected) { if (WideCharToMultiByte(CP_UTF8, 0, bstrName, -1, Name, sizeof(Name), NULL, NULL)) { printf("Name : %s", Name); if (bIsConnected) printf(" (connected)"); if (bIsConnectedInet) printf(" (inet)"); printf(" (%s)\n", category == NLM_NETWORK_CATEGORY_PUBLIC ? "public" : category == NLM_NETWORK_CATEGORY_PRIVATE ? "private" : category == NLM_NETWORK_CATEGORY_DOMAIN_AUTHENTICATED ? "domain" : "unknown"); guid2str(&idNet, Name); printf("NetID : %s\n", Name); if (connected && SUCCEEDED(w_win32_error = pNet->lpVtbl->GetNetworkConnections(pNet, &pEnumConnections))) { while ((w_win32_error = pEnumConnections->lpVtbl->Next(pEnumConnections, 1, &pConn, NULL)) == S_OK) { if (SUCCEEDED(w_win32_error = pConn->lpVtbl->GetAdapterId(pConn, &idAdapter))) { guid2str(&idAdapter, Name); if (AdapterID2Name(&idAdapter, Name2, sizeof(Name2))) printf("Adapter : %s (%s)\n", Name2, Name); else printf("Adapter : %s\n", Name); } pConn->lpVtbl->Release(pConn); } pEnumConnections->lpVtbl->Release(pEnumConnections); } printf("\n"); } else { w_win32_error = HRESULT_FROM_WIN32(GetLastError()); bRet = false; } } SysFreeString(bstrName); } else bRet = false; pNet->lpVtbl->Release(pNet); if (!bRet) break; } if (!bRet) break; pEnumNetworks->lpVtbl->Reset(pEnumNetworks); } pEnumNetworks->lpVtbl->Release(pEnumNetworks); } else bRet = false; pNetworkListManager->lpVtbl->Release(pNetworkListManager); } else bRet = false; } else bRet = false; CoUninitialize(); return bRet; } static bool nlm_filter_match(const struct str_list_head *nlm_list) { // no filter given. always matches. if (!nlm_list || LIST_EMPTY(nlm_list)) { w_win32_error = 0; return true; } bool bRet = true, bMatch = false; IEnumNetworks *pEnum; if (SUCCEEDED(w_win32_error = pNetworkListManager->lpVtbl->GetNetworks(pNetworkListManager, NLM_ENUM_NETWORK_CONNECTED, &pEnum))) { INetwork *pNet; GUID idNet, g; BSTR bstrName; char Name[128]; struct str_list *nlm; for (;;) { if (FAILED(w_win32_error = pEnum->lpVtbl->Next(pEnum, 1, &pNet, NULL))) { bRet = false; break; } if (w_win32_error != S_OK) break; if (SUCCEEDED(w_win32_error = pNet->lpVtbl->GetNetworkId(pNet, &idNet)) && SUCCEEDED(w_win32_error = pNet->lpVtbl->GetName(pNet, &bstrName))) { if (WideCharToMultiByte(CP_UTF8, 0, bstrName, -1, Name, sizeof(Name), NULL, NULL)) { LIST_FOREACH(nlm, nlm_list, next) { bMatch = !strcmp(Name, nlm->str) || str2guid(nlm->str, &g) && !memcmp(&idNet, &g, sizeof(GUID)); if (bMatch) break; } } else { w_win32_error = HRESULT_FROM_WIN32(GetLastError()); bRet = false; } SysFreeString(bstrName); } else bRet = false; pNet->lpVtbl->Release(pNet); if (!bRet || bMatch) break; } pEnum->lpVtbl->Release(pEnum); } else bRet = false; return bRet && bMatch; } static bool wlan_filter_match(const struct str_list_head *ssid_list) { DWORD dwCurVersion; HANDLE hClient = NULL; PWLAN_INTERFACE_INFO_LIST pIfList = NULL; PWLAN_INTERFACE_INFO pIfInfo; PWLAN_CONNECTION_ATTRIBUTES pConnectInfo; DWORD connectInfoSize, k; bool bRes; struct str_list *ssid; size_t len; // no filter given. always matches. if (!ssid_list || LIST_EMPTY(ssid_list)) { w_win32_error = 0; return true; } w_win32_error = WlanOpenHandle(2, NULL, &dwCurVersion, &hClient); if (w_win32_error != ERROR_SUCCESS) goto fail; w_win32_error = WlanEnumInterfaces(hClient, NULL, &pIfList); if (w_win32_error != ERROR_SUCCESS) goto fail; for (k = 0; k < pIfList->dwNumberOfItems; k++) { pIfInfo = pIfList->InterfaceInfo + k; if (pIfInfo->isState == wlan_interface_state_connected) { w_win32_error = WlanQueryInterface(hClient, &pIfInfo->InterfaceGuid, wlan_intf_opcode_current_connection, NULL, &connectInfoSize, (PVOID *)&pConnectInfo, NULL); if (w_win32_error != ERROR_SUCCESS) goto fail; // printf("%s\n", pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID); LIST_FOREACH(ssid, ssid_list, next) { len = strlen(ssid->str); if (len == pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength && !memcmp(ssid->str, pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID, len)) { WlanFreeMemory(pConnectInfo); goto found; } } WlanFreeMemory(pConnectInfo); } } w_win32_error = 0; fail: bRes = false; ex: if (pIfList) WlanFreeMemory(pIfList); if (hClient) WlanCloseHandle(hClient, 0); return bRes; found: w_win32_error = 0; bRes = true; goto ex; } bool logical_net_filter_match(void) { return wlan_filter_match(wlan_filter_ssid) && nlm_filter_match(nlm_filter_net); } static bool logical_net_filter_match_rate_limited(void) { DWORD dwTick = GetTickCount() / 1000; if (logical_net_filter_tick == dwTick) return true; logical_net_filter_tick = dwTick; return logical_net_filter_match(); } static HANDLE windivert_init_filter(const char *filter, UINT64 flags) { LPSTR errormessage = NULL; HANDLE h, hMutex; const char *mutex_name = "Global\\winws_windivert_mutex"; // windivert driver start in windivert.dll has race conditions hMutex = CreateMutexA(NULL, TRUE, mutex_name); if (hMutex && GetLastError() == ERROR_ALREADY_EXISTS) WaitForSingleObject(hMutex, INFINITE); h = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags); w_win32_error = GetLastError(); if (hMutex) { ReleaseMutex(hMutex); CloseHandle(hMutex); SetLastError(w_win32_error); } if (h != INVALID_HANDLE_VALUE) return h; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, w_win32_error, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPSTR)&errormessage, 0, NULL); DLOG_ERR("windivert: error opening filter: %s", errormessage); LocalFree(errormessage); if (w_win32_error == ERROR_INVALID_IMAGE_HASH) DLOG_ERR("windivert: try to disable secure boot and install OS patches\n"); return NULL; } void rawsend_cleanup(void) { if (w_filter) { CancelIoEx(w_filter, &ovl); WinDivertClose(w_filter); w_filter = NULL; } if (ovl.hEvent) { CloseHandle(ovl.hEvent); ovl.hEvent = NULL; } } bool windivert_init(const char *filter) { rawsend_cleanup(); w_filter = windivert_init_filter(filter, 0); if (w_filter) { ovl.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (!ovl.hEvent) { w_win32_error = GetLastError(); rawsend_cleanup(); return false; } return true; } return false; } static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa) { UINT recv_len; DWORD err; DWORD rd; char c; if (bQuit) { errno = EINTR; return false; } if (!logical_net_filter_match_rate_limited()) { errno = ENODEV; return false; } usleep(0); if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, NULL, &ovl)) { *len = recv_len; return true; } for (;;) { w_win32_error = GetLastError(); switch (w_win32_error) { case ERROR_IO_PENDING: // make signals working while (WaitForSingleObject(ovl.hEvent, 50) == WAIT_TIMEOUT) { if (bQuit) { errno = EINTR; return false; } if (!logical_net_filter_match_rate_limited()) { errno = ENODEV; return false; } usleep(0); } if (!GetOverlappedResult(hFilter, &ovl, &rd, TRUE)) continue; *len = rd; return true; case ERROR_INSUFFICIENT_BUFFER: errno = ENOBUFS; break; case ERROR_NO_DATA: errno = ESHUTDOWN; break; default: errno = EIO; } break; } return false; } bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa) { return windivert_recv_filter(w_filter, packet, len, wa); } static bool windivert_send_filter(HANDLE hFilter, const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa) { bool b = WinDivertSend(hFilter, packet, (UINT)len, NULL, wa); w_win32_error = GetLastError(); return b; } bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa) { return windivert_send_filter(w_filter, packet, len, wa); } bool rawsend(const struct sockaddr *dst, uint32_t fwmark, const char *ifout, const void *data, size_t len) { WINDIVERT_ADDRESS wa; memset(&wa, 0, sizeof(wa)); // pseudo interface id IfIdx.SubIfIdx if (sscanf(ifout, "%u.%u", &wa.Network.IfIdx, &wa.Network.SubIfIdx) != 2) { errno = EINVAL; return false; } wa.Outbound = 1; wa.IPChecksum = 1; wa.TCPChecksum = 1; wa.UDPChecksum = 1; wa.IPv6 = (dst->sa_family == AF_INET6); return windivert_send(data, len, &wa); } #else // *nix static int rawsend_sock4 = -1, rawsend_sock6 = -1; static bool b_bind_fix4 = false, b_bind_fix6 = false; static void rawsend_clean_sock(int *sock) { if (sock && *sock != -1) { close(*sock); *sock = -1; } } void rawsend_cleanup(void) { rawsend_clean_sock(&rawsend_sock4); rawsend_clean_sock(&rawsend_sock6); } static int *rawsend_family_sock(sa_family_t family) { switch (family) { case AF_INET: return &rawsend_sock4; case AF_INET6: return &rawsend_sock6; default: return NULL; } } #ifdef BSD int socket_divert(sa_family_t family) { int fd; #ifdef __FreeBSD__ // freebsd14+ way // don't want to use ifdefs with os version to make binaries compatible with all versions fd = socket(PF_DIVERT, SOCK_RAW, 0); if (fd == -1 && (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EPFNOSUPPORT)) #endif // freebsd13- or openbsd way fd = socket(family, SOCK_RAW, IPPROTO_DIVERT); return fd; } static int rawsend_socket_divert(sa_family_t family) { // HACK HACK HACK HACK HACK HACK HACK HACK // FreeBSD doesnt allow IP_HDRINCL for IPV6 // OpenBSD doesnt allow rawsending tcp frames // we either have to go to the link layer (its hard, possible problems arise, compat testing, ...) or use some HACKING // from my point of view disabling direct ability to send ip frames is not security. its SHIT int fd = socket_divert(family); if (fd != -1 && !set_socket_buffers(fd, 4096, RAW_SNDBUF)) { close(fd); return -1; } return fd; } static int rawsend_sendto_divert(sa_family_t family, int sock, const void *buf, size_t len) { struct sockaddr_storage sa; socklen_t slen; #ifdef __FreeBSD__ // since FreeBSD 14 it requires hardcoded IPv4 values, although can also send IPv6 frames family = AF_INET; slen = sizeof(struct sockaddr_in); #else // OpenBSD requires correct family and size switch (family) { case AF_INET: slen = sizeof(struct sockaddr_in); break; case AF_INET6: slen = sizeof(struct sockaddr_in6); break; default: return -1; } #endif memset(&sa, 0, slen); sa.ss_family = family; return sendto(sock, buf, len, 0, (struct sockaddr *)&sa, slen); } #endif static int rawsend_socket_raw(int domain, int proto) { int fd = socket(domain, SOCK_RAW, proto); if (fd != -1) { #ifdef __linux__ int s = RAW_SNDBUF / 2; int r = 2048; #else int s = RAW_SNDBUF; int r = 4096; #endif if (!set_socket_buffers(fd, r, s)) { close(fd); return -1; } } return fd; } static bool set_socket_fwmark(int sock, uint32_t fwmark) { #ifdef BSD #ifdef SO_USER_COOKIE if (setsockopt(sock, SOL_SOCKET, SO_USER_COOKIE, &fwmark, sizeof(fwmark)) == -1) { DLOG_PERROR("rawsend: setsockopt(SO_USER_COOKIE)"); return false; } #endif #elif defined(__linux__) if (setsockopt(sock, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark)) == -1) { DLOG_PERROR("rawsend: setsockopt(SO_MARK)"); return false; } #endif return true; } static int rawsend_socket(sa_family_t family) { int *sock = rawsend_family_sock(family); if (!sock) return -1; if (*sock == -1) { int yes = 1, pri = 6; // printf("rawsend_socket: family %d",family); #ifdef __FreeBSD__ // IPPROTO_RAW with IPv6 in FreeBSD always returns EACCES on sendto. // must use IPPROTO_TCP for IPv6. IPPROTO_RAW works for IPv4 // divert sockets are always v4 but accept both v4 and v6 *sock = rawsend_socket_divert(AF_INET); #elif defined(__OpenBSD__) || defined(__APPLE__) // OpenBSD does not allow sending TCP frames through raw sockets // I don't know about macOS. They have dropped ipfw in recent versions and their PF does not support divert-packet *sock = rawsend_socket_divert(family); #else *sock = rawsend_socket_raw(family, IPPROTO_RAW); #endif if (*sock == -1) { DLOG_PERROR("rawsend: socket()"); return -1; } #ifdef __linux__ if (setsockopt(*sock, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri)) == -1) { DLOG_PERROR("rawsend: setsockopt(SO_PRIORITY)"); goto exiterr; } if (family == AF_INET && setsockopt(*sock, IPPROTO_IP, IP_NODEFRAG, &yes, sizeof(yes)) == -1) { DLOG_PERROR("rawsend: setsockopt(IP_NODEFRAG)"); goto exiterr; } if (family == AF_INET && setsockopt(*sock, IPPROTO_IP, IP_FREEBIND, &yes, sizeof(yes)) == -1) { DLOG_PERROR("rawsend: setsockopt(IP_FREEBIND)"); goto exiterr; } if (family == AF_INET6 && setsockopt(*sock, SOL_IPV6, IPV6_FREEBIND, &yes, sizeof(yes)) == -1) { // DLOG_PERROR("rawsend: setsockopt(IPV6_FREEBIND)"); // don't error because it's supported only from kernel 4.15 } #endif } return *sock; exiterr: rawsend_clean_sock(sock); return -1; } bool rawsend_preinit(bool bind_fix4, bool bind_fix6) { b_bind_fix4 = bind_fix4; b_bind_fix6 = bind_fix6; // allow IPv6 disabled systems return rawsend_socket(AF_INET) != -1 && (rawsend_socket(AF_INET6) != -1 || errno == EAFNOSUPPORT); } bool rawsend(const struct sockaddr *dst, uint32_t fwmark, const char *ifout, const void *data, size_t len) { ssize_t bytes; int sock = rawsend_socket(dst->sa_family); if (sock == -1) return false; if (!set_socket_fwmark(sock, fwmark)) return false; int salen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); struct sockaddr_storage dst2; memcpy(&dst2, dst, salen); if (dst->sa_family == AF_INET6) ((struct sockaddr_in6 *)&dst2)->sin6_port = 0; // or will be EINVAL in Linux #if defined(BSD) bytes = rawsend_sendto_divert(dst->sa_family, sock, data, len); if (bytes == -1) { DLOG_PERROR("rawsend: sendto_divert"); return false; } return true; #else #ifdef __linux__ struct sockaddr_storage sa_src; switch (dst->sa_family) { case AF_INET: if (!b_bind_fix4) goto nofix; extract_endpoints(data, NULL, NULL, NULL, &sa_src, NULL); break; case AF_INET6: if (!b_bind_fix6) goto nofix; extract_endpoints(NULL, data, NULL, NULL, &sa_src, NULL); break; default: return false; // should not happen } // printf("family %u dev %s bind : ", dst->sa_family, ifout); print_sockaddr((struct sockaddr *)&sa_src); printf("\n"); if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifout, ifout ? strlen(ifout) + 1 : 0) == -1) { DLOG_PERROR("rawsend: setsockopt(SO_BINDTODEVICE)"); return false; } if (bind(sock, (const struct sockaddr *)&sa_src, dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) { DLOG_PERROR("rawsend: bind (ignoring)"); // do not fail. this can happen regardless of IP_FREEBIND // rebind to any address memset(&sa_src, 0, sizeof(sa_src)); sa_src.ss_family = dst->sa_family; if (bind(sock, (const struct sockaddr *)&sa_src, dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) { DLOG_PERROR("rawsend: bind to any"); return false; } } nofix: #endif // normal raw socket sendto bytes = sendto(sock, data, len, 0, (struct sockaddr *)&dst2, salen); if (bytes == -1) { DLOG_PERROR("rawsend: sendto"); return false; } return true; #endif } #endif // not CYGWIN bool rawsend_rp(const struct rawpacket *rp) { return rawsend((struct sockaddr *)&rp->dst, rp->fwmark, rp->ifout, rp->packet, rp->len); } bool rawsend_queue(struct rawpacket_tailhead *q) { struct rawpacket *rp; bool b; for (b = true; (rp = rawpacket_dequeue(q)); rawpacket_free(rp)) b &= rawsend_rp(rp); return b; } // return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling // ttl = TTL of incoming packet uint8_t autottl_guess(uint8_t ttl, const autottl *attl) { uint8_t orig, path, fake; // 18.65.168.125 ( cloudfront ) 255 // 157.254.246.178 128 // 1.1.1.1 64 // guess original ttl. consider path lengths less than 32 hops if (ttl > 223) orig = 255; else if (ttl < 128 && ttl > 96) orig = 128; else if (ttl < 64 && ttl > 32) orig = 64; else return 0; path = orig - ttl; fake = path > attl->delta ? path - attl->delta : attl->min; if (fake < attl->min) fake = attl->min; else if (fake > attl->max) fake = attl->max; if (fake >= path) return 0; return fake; } void do_nat(bool bOutbound, struct ip *ip, struct ip6_hdr *ip6, struct tcphdr *tcphdr, struct udphdr *udphdr, const struct sockaddr_in *target4, const struct sockaddr_in6 *target6) { uint16_t nport; if (ip && target4) { nport = target4->sin_port; if (bOutbound) ip->ip_dst = target4->sin_addr; else ip->ip_src = target4->sin_addr; ip4_fix_checksum(ip); } else if (ip6 && target6) { nport = target6->sin6_port; if (bOutbound) ip6->ip6_dst = target6->sin6_addr; else ip6->ip6_src = target6->sin6_addr; } else return; if (nport) { if (tcphdr) { if (bOutbound) tcphdr->th_dport = nport; else tcphdr->th_sport = nport; } if (udphdr) { if (bOutbound) udphdr->uh_dport = nport; else udphdr->uh_sport = nport; } } } void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr) { if (!(verdict & VERDICT_NOCSUM)) { // always fix csum for windivert. original can be partial or bad #ifndef __CYGWIN__ #ifdef __FreeBSD__ // FreeBSD tend to pass IPv6 frames with wrong checksum if ((verdict & VERDICT_MASK) == VERDICT_MODIFY || ip6hdr) #else // if original packet was tampered earlier it needs checksum fixed if ((verdict & VERDICT_MASK) == VERDICT_MODIFY) #endif #endif tcp_fix_checksum(tcphdr, transport_len, ip, ip6hdr); } } void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr) { if (!(verdict & VERDICT_NOCSUM)) { // always fix csum for windivert. original can be partial or bad #ifndef __CYGWIN__ #ifdef __FreeBSD__ // FreeBSD tend to pass IPv6 frames with wrong checksum if ((verdict & VERDICT_MASK) == VERDICT_MODIFY || ip6hdr) #else // if original packet was tampered earlier it needs checksum fixed if ((verdict & VERDICT_MASK) == VERDICT_MODIFY) #endif #endif udp_fix_checksum(udphdr, transport_len, ip, ip6hdr); } }