TCP伪装-创新互联

什么是TCP伪装

TCP我们都知道,累积确认,丢包重传,拥塞控制等特性共同保障了它的可靠性。而如果抛弃这些特性,无需保障可靠,像UDP一样直接发送的TCP报文,便可认为是做了TCP伪装了。

目前创新互联公司已为上1000+的企业提供了网站建设、域名、雅安服务器托管绵阳服务器托管、企业网站设计、广河网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。为什么要做TCP伪装

运营商(ISP)通常做转发策略时,默认TCP包的优先级比UDP更高,也即发生网络拥堵时,优先丢弃非TCP的包。为了反制运营商的这种策略,让我们的非TCP报文也能够获更公平、更稳定的传输链路,便有了TCP伪装。毕竟你也不想你的视频卡帧吧-_-!

怎么做

linux提供了原始套接字的接口,通过此接口,我们可以收发原始数据包(带IP包头的)

int StartFakeTcp(const char *ip, short port) {
	// 打开原始套接字
	int sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
	if (sock< 0) {
		perror("socket");
		return -1;
	}

	// 由应用层填充IP头
	const int on =1;
	if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))< 0) {
		perror("setsockopt");
		return -1;
	}

	struct sockaddr_in local;
	SetAddr(ip, port, &local);

	if (bind(sock, (struct sockaddr *)&local, sizeof(local))< 0) {
		perror("bind");
		return -1;
	}

	return sock;
}

从计算机网络分层的角度来看的话,普通socket只能够访问传输层以及传输层以上的数据,这是因为IP层将数据包传递给传输层时下层的数据包头已经被丢弃了。而原始套接字却没有这样做。因此它对上下层的数据都可以访问。所以使用 raw套接字你可以实现上至应用层的数据操作,也可以实现下至链路层的数据操作。

接下来所谓的伪装,其实就是IP和传输层的头部进行重新构造了。对于IP层,struct ip构建IP包头,传输层则可以通过struct tcphdr构建TCP头。

// 构建IP包头
int BuildIpPkt(char* data, int len, struct in_addr *ip_src, struct in_addr *ip_dst, uint8_t proto) {
	struct ip *ip_hdr = (struct ip*)(data - sizeof(struct ip));

	memcpy(&ip_hdr->ip_src, ip_src, sizeof(*ip_src));
	memcpy(&ip_hdr->ip_dst, ip_dst, sizeof(*ip_dst));
	ip_hdr->ip_v = 4;
	ip_hdr->ip_hl = sizeof(*ip_hdr) / 4;
	ip_hdr->ip_len = htons(len + sizeof(*ip_hdr));
	ip_hdr->ip_off = htons(0);
	ip_hdr->ip_ttl = 0xff;
	ip_hdr->ip_p = proto;
	ip_hdr->ip_id = 0;
	ip_hdr->ip_sum = htons(GetIpCheckSum((char*)ip_hdr, sizeof(*ip_hdr)));
	
	return sizeof(struct ip) + len;
}

// 构建TCP包头
int BuildPkt(char* data, int len,
			 struct in_addr *ip_src, short port_src,
			 struct in_addr *ip_dst, short port_dst,
			 uint32_t seq, uint32_t ack_seq, uint8_t syn, uint8_t ack) {
	struct tcphdr *tcp_hdr = (struct tcphdr*)(data - sizeof(struct tcphdr));

	tcp_hdr->doff = sizeof(*tcp_hdr) / 4;
	tcp_hdr->source = htons(port_src);
	tcp_hdr->dest = htons(port_dst);
	tcp_hdr->seq = htonl(seq);
	tcp_hdr->ack_seq = htonl(ack_seq);
	if (syn) tcp_hdr->syn = 1;
	if (ack) tcp_hdr->ack = 1;
	tcp_hdr->th_sum = GetTcpCheckSum((char*)tcp_hdr, sizeof(*tcp_hdr) + len, ip_src, ip_dst);

	return BuildIpPkt((char*)tcp_hdr, len + sizeof(struct tcphdr), ip_src, ip_dst, IPPROTO_TCP);
}

如此一来,我们就有了一个TCP报文,它长的跟正经的TCP报文没什么两样,但就是无连接。那么在运营商的策略看来,它的优先级就高了那么一些。

12:03:26.641749 lo    In  IP (tos 0x0, ttl 255, id 36489, offset 0, flags [none], proto TCP (6), length 40)
    127.0.0.1.6667 >127.0.0.1.6666: Flags [S], cksum 0x4d92 (correct), seq 12345, win 0, length 0
12:03:26.641793 lo    In  IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 44)
    127.0.0.1.6666 >127.0.0.1.6667: Flags [S.], cksum 0xfe20 (incorrect ->0x238a), seq 292882122, ack 12346, win 65495, options [mss 65495], length 0
12:03:26.641797 lo    In  IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    127.0.0.1.6667 >127.0.0.1.6666: Flags [R], cksum 0x4d8f (correct), seq 12346, win 0, length 0
12:03:26.641896 lo    In  IP (tos 0x0, ttl 255, id 36490, offset 0, flags [none], proto TCP (6), length 40)

我这里实际验证时,server(6666)、client(6667)都在同一台机器上,client发syn、server回syn+ack都没有什么问题,第三次时,内核主动发了一个RST报文,这是由于kernel层没有半连接缓存导致的,我通过修改防火墙规则拦截了这个报文,避免假连接被中断:

iptables -I OUTPUT -p tcp --tcp-flags RST RST --dport 6666 -j DROP

但client的ack没有发送出去,这一点原因还待确认。

另外

部分防火墙设备在做转发时,会对数据包进行校验,如果识别出你的TCP包没有对应的连接跟踪,则认为是异常包,会直接丢弃掉。此时我们在真正发送数据之前,先模拟一轮TCP握手即可。

https://github.com/Fireplusplus/FakeTCP

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享文章:TCP伪装-创新互联
本文链接:http://azwzsj.com/article/ddojgs.html