tcp/ip 详细解析以及网络层简单的发送syn

利用tcp发送syn,我们可以从网络层进行下发,其实就是组装tcp /ip包发送出去.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//#include <asm/types.h>
#include <linux/ip.h>
//#include <linux/tcp_new.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <sys/time.h>

#define getrandom(min, max) ((rand() % (int)(((max)+1) - (min))) + (min))

void send_tcp(int sockfd, struct sockaddr_in *addr);
unsigned short checksum(unsigned short *buffer, int size);
unsigned short random_port(unsigned short minport, unsigned short maxport);
void random_ip(char *str);

//nmap:sudo nmap -sP 192.168.1.* 检测内网ip
//nmap:sudo nmap -sS 192.168.1.2 -D 192.168.1.3 用后面的假ip欺诈前面的内网ip,并获取开放的端口号.D:decoys诱饵
//查看公网ip:curl ident.me
int main(int argc, char **argv) 
{
	int sockfd;
	struct sockaddr_in addr;
	//int dport;
	int on = 1;
	if (argc != 3) 
	{
		printf("usage: <command_name> <target_ip> <port>\n");
		exit(-1);
	}
	bzero(&addr, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	//addr.sin_addr.s_addr=inet_aton(argv[1]);
	inet_pton(AF_INET, argv[1], &addr.sin_addr);
	/*if(inet_aton(argv[1],&addr.sin_addr)==0){
	 * 	host=gethostbyname
	 * 		}*/
	sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);//SOCK_RAW原始套接字,root才可以创建
	if (sockfd<0) 
	{
		printf("Socket error!\n");
		exit(-1);
	}
	setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));//IP_HDRINCL自己编写ip头
	struct timeval tv,tm;
	gettimeofday(&tv,NULL);
	while (1) 
	{
		static int i = 0;
		++i;
		//if(i=5) break;
		send_tcp(sockfd, &addr);
		sleep(1024);
		//gettimeofday(&tm,NULL);
		//if(tv.tv_sec + 10 <= tm.tv_sec) break;
	}
	printf("exit.\n");
	return 0;
}

void send_tcp(int sockfd, struct sockaddr_in *addr) 
{
	char buff[sizeof(struct iphdr) + sizeof(struct tcphdr)];
	memset(buff,0x0,sizeof(buff));
	struct iphdr *ip_header = (struct iphdr *)buff;
	struct tcphdr *tcp_header = (struct tcphdr *)&buff[sizeof(struct iphdr)];
	unsigned short source_port = random_port(1024, 5000);
	char ip_str[50];
	struct in_addr ip;

	//random_ip(ip_str);
	//if (inet_aton(ip_str, &ip) == 0) 
	//{
	//	printf("inet_aton error!\n");
	//	exit(1);
	//}
	ip.s_addr = inet_addr("192.168.0.69");

	//网络层IP
	//ip_header=(struct iphdr*)buff;
	//31                                     0
	//|----|----|------|--|-------------------|----------
	//|ver |ihl | -tos | -|    tot_len        |
	//|----|----|------|--|-------------------|
	//|       id          |   frag_off       -|
	//|---------|---------|-------------------|
	//|   ttl   |protocol |    check          | 20 Bytes
	//|---------|---------|-------------------|
	//|                saddr                  |
	//|---------------------------------------|
	//|                daddr                  |
	//|---------------------------------------|----------
	//|                                       |
	//|                options                | 40 Bytes
	//|                                       |
	//|---------------------------------------|
	//				图:IPv4头部
	//大对大小对小 小端对齐 little endian
	//高位在低地址,低位在高地址,big endian,也是网络字节序:32bit每次传输8bit共4次
	//先传0~7bit 再8~15 16~23 24~31

	//4bit版本号;IPv4
	ip_header->version = 4;
	//4bit首部长度;以32bit为单位 普通ip数据报无选择项,大小是5。5*32/8=20Bytes.
	ip_header->ihl = sizeof(struct iphdr)/4;//0x45
	//8bit服务类型字段;包含三部分:3bit的优先权子字段(现已忽略),4bitTOS字段,1bit未用(但必须置零).其中4bitTOS字段分别代表最小时延,最大吞吐量,最高可靠性,最小费用(4bit中只能设置其中1bit).如果4bit均为0,则为一般服务.
	ip_header->tos = 0;//0x00
	//16bit总长度字段;以bytes为单位 再利用首部长度,可知道数据报中数据的起始位置和长度.总长度最长65535.
	//一些数据链路(如以太网)会填充一些数据以达到最小长度.尽管以太网最小帧长为46bytes,但ip数据可能更短,此时ip层不知道46bytes中有多少是ip数据报的内容,因此总长度字段是必要内容.
	ip_header->tot_len = htons(sizeof(buff));//0x00 0x2e
	//16bit标识字段;唯一地标识主机发送的数据报.每发送一次报文,通常会id+1
	ip_header->id = htons(random());//0xbe 0x55
	//16bit分段偏移Fragment
	//低13位:
	//高3位:第1位保留,必须0.
	//      第2位是"更多分片"(More Fragment)标志.除了最后一片,其他组成数据报的片都应将该位置1.
	//      第3位是"不分片"(Don't Fragment)标志,
	ip_header->frag_off = 0;//0x00 0x00
	//8bit生存时间段;每经过一个路由-1,每一跳(秒)-1,在路由器排队时间较长时计数器多倍递减.到0时数据报被丢弃并发送ICMP报文通知源主机.值通常为32或64.计数单位为s,因此最大生存期为255s.
	//此特性避免数据报长时间逗留在网络中.
	ip_header->ttl = 0x40;//0x1e
	//8bit协议字段;识别是哪个协议向IP传送数据,或将数据报交给哪个进程.UDP,TCP等.
	ip_header->protocol = IPPROTO_TCP;//0x11
	//16bit首部检验和;用32bit计算;计算公式:ip_header全部统计,包括check;接受时如果check不结果不合法,则ip丢掉该数据报,不生成差错报文,由上层(TCP UDP等)去发现丢失的数据报.
	//发送:将ip首部以16bit为单位统计:0x4500+0x002e+0xbe55+0x0000+0x1e11+0x0000+0xdeb7+0x7ee3+0xc0a8+0x127a=0x35250; 11 0101 0010 0101 0000
	//	   check += (check >> 32)    0101 0010 0101 0011//如果仍有17bit高位,重复
	//	                取反(反码)   1010 1101 1010 1100 得0xadac
	//接受:将ip首部以16bit为单位统计:0x4500+0x002e+0xbe55+0x0000+0x1e11+0xadac+0xdeb7+0x7ee3+0xc0a8+0x127a=3fffc;   11 1111 1111 1111 1100
	//     check += (check >> 32)    1111 1111 1111 1111//如果仍有17bit高位,重复
	//     				取反(反码)   0000 0000 0000 0000 结果为0,说明ip首部合法
	ip_header->check = 0;//0x00 0x00 -->0xad 0xac
	//32bit源ip地址,这里随机生成.在内网环境下,非法ip将被路由抛弃.
	ip_header->saddr = ip.s_addr;//0xde 0xb7 0x7e 0xe3 (222.183.126.227)
	//32bit目的ip地址
	ip_header->daddr = addr->sin_addr.s_addr;//0xc0 0xa8 0x12 0x7a (192.168.18.122)

	//传输层TCP
	//tcp_header=(struct tcphdr*)(buff+sizeof(struct iphdr));
	//     |----------------|----------------|-------------
	//     |     source     |     dest       |
	//     |----------------|----------------|
	//     |               seq               |
	//     |---------------------------------|
	//     |               ack_seq           | 20 Bytes
	//     |----|----|------|----------------|
	//     |doff|res1|      |     window     |
	//     |----|----|------|----------------|
	//     |     check      |     urg_ptr    |
	//     |----------------|----------------|-------------
	//     |             options             | 4 Bytes(有选项就4bytes否则0bytes)
	//     |---------------------------------|  
	//					 图:TCP头部
	//struct tcphdr {
	//	  __be16 source; 
	//	  __be16 dest;
	//    __be32 seq;
	//    __be32 ack_seq;
	//#if defined(__LITTLE_ENDIAN_BITFIELD)
	//    __u16 res1:4,
	//    doff:4,
	//    fin:1,
	//    syn:1,
	//    rst:1,
	//    psh:1,
	//    ack:1,
	//    urg:1,
	//    ece:1, 
	//    cwr:1;
	//#elif defined(__BIG_ENDIAN_BITFIELD)
	//    __u16 doff:4,
	//    res1:4,
	//    cwr:1,
	//    ece:1,
	//    urg:1,
	//    ack:1,
	//    psh:1,
	//    rst:1,
	//    syn:1,
	//    fin:1;
	//#else
	//#error "Adjust your <asm/byteorder.h> defines"
	//#endif 
	//    __be16 window;
	//    __sum16 check;
	//    __be16 urg_ptr;
	//};
	//TCP选项:有就4bytes,无就0bytes.
	//typedef struct _TCP_OPTIONS{
	//  char m_ckind;
	//  char m_cLength;
	//  char m_cContext[32];
	//  }__attribute__((packed))TCP_OPTIONS, *PTCP_OPTIONS;

	//16bit源端口号
	tcp_header->source = htons(source_port);
	//16bit目标端口号
	tcp_header->dest = addr->sin_port;
	//32bit序列号;整个报文中,起始序列号为随机数(for safety),后续的序列号=已接收bit数+起始序列号.
	tcp_header->seq =rand();
	//32bit 期望对方下次发送的seq,表示ack_seq-1及之前的数据都正确收到.在seq达到最大时,便从0开始.
	tcp_header->ack_seq = 0;
	//4bitTCP头部长度;指明了TCP头部包含多少个32bit的字.因为options长度是可变的,tcp头部长度也就跟着变化.最长16*32/8=60bytes.
	tcp_header->doff = sizeof(struct tcphdr) / 4;
	//6bit保留位
	tcp_header->res1 = 0;
	//1bit紧急指针标志位;置为1表示ugr_ptr生效.
	tcp_header->urg = 0;
	//1bit确认信息;置为1表示该报文的ack_seq有效.如果为0,ack_seq将被接收方忽略.
	tcp_header->ack = 0;
	//1bit    push;置为1表示该数据应当立即将数据交给应用程序,而不是缓存到缓冲区.
	tcp_header->psh = 0;
	//1bit重置连接;置为1表示接收端有问题,也可用来拒绝连接请求.接受者应当处理.
	tcp_header->rst = 0;
	//1bit建立连接;此时ack_seq不应生效,因此应该让ack=0.
	tcp_header->syn = 1;
	//1bit释放连接;表示发送方数据传输结束.
	tcp_header->fin = 0;
	//16bit滑动窗口;表示从该报文的seq算起,还能接受多少bytes数据.当window=0时,表示已经收到ack_seq-1个bit的数据,现在还不能接收新报文.可以接收新报文时,再重复发一次该报文,但window != 0.即这是TCP的流量控制,最大65535.
    //窗口指发送方的接收窗口。它告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。之所以有此限制是因为接收方的数据缓存空间是有限的。它指出现在允许对方发送数据量。由于接收缓存不断变化,因此窗口值也不断变化
	tcp_header->window = htons(65535);
	//16bit检验和;ip检验和只包括ip头部,而TCP/UDP检验和包括伪头部,头部和数据.
	tcp_header->check = 0;
	//16bit紧急指针;记录了该数据在整个数据报中的位置.
	//紧急数据的seq号:该报文的seq+紧急指针urg_ptr-1.因为不包括syn(不知道为什么),因此-1.
	//紧急数据又称带外数据(out-of-band data),但描述不准确,因为它仍然随着普通数据发送,并无特殊待遇.只是让接受端知道,这是紧急数据.
	//紧急数据会被覆盖:如果发送了一个字节紧急数据如下,在紧急数据被发送出去前,又发送一个紧急数据'B',那么'B'将覆盖'A'发送出去.
	//write(fd,'A',1,URG);发送紧急数据A,使紧急指针指向'A'.
	tcp_header->urg_ptr = 0;

	//TCP伪头部:不应该将tcp/udp校验和归纳于任何层,因为ip实际上tcp不该从ip知道的.96bit的伪头部.能够防止出现路由选择错误.
	//-----------------------------------------
	//|         32bit Source IP address       |
	//-----------------------------------------
	//|         32bit Destination IP addr     |
	//-----------------------------------------
	//|  0   | 8bit Proto| 16bit header length|
	//-----------------------------------------
	//			  图:tcp/udp伪头部
	//send_tcp_segment(&ip_header,&tcp_header,"",0);
	/*struct 
	  {
	  unsigned long saddr;//32bit源ip地址
	  unsigned long daddr;//32bit目的ip地址
	  char mbz;//8bit填充对齐,必须置0
	  char ptcl;//8bitprotocol协议号.(IPPROTO_TCP=6,IPPROTO_UDP=17)
	  unsigned short tcpl;//16bitTCP/UDP头长度(不包含数据部分)
	  }psd_header;*/

#pragma pack(1)
	struct pseudo_header    //needed for checksum calculation
	{
		//伪头部 不发送只校验
		unsigned int source_address;//32bit源ip地址
		unsigned int dest_address;//32bit目的ip地址
		unsigned char placeholder;//8bit填充对齐,必须置0
		unsigned char protocol;//8bitprotocol协议号.(IPPROTO_TCP=6,IPPROTO_UDP=17)
		unsigned short tcp_length;//16bitTCP/UDP头长度(不包含数据部分)
		//头部 随ip发送
		struct tcphdr tcp;
	};
#pragma pack()


	struct pseudo_header psd_header;	

	psd_header.source_address = ip_header->saddr;
	psd_header.dest_address = ip_header->daddr;
	psd_header.placeholder = 0;
	psd_header.protocol = IPPROTO_TCP;
	psd_header.tcp_length = htons(sizeof(struct tcphdr));
	memcpy(&psd_header.tcp,tcp_header,sizeof(struct tcphdr));

    //包括了tcp伪头部 tcp头部 tcp数据.
	tcp_header->check = checksum((unsigned short*)&psd_header, sizeof(psd_header));
	//只包括ip头部.
	ip_header->check = checksum((unsigned short*)buff, (sizeof(int)/sizeof(unsigned short)) * ip_header->ihl);

	ssize_t sentlen = sendto(sockfd, buff, tcp_header->doff * 4 + ip_header->ihl * 4 /*sizeof(buff)*/, 0,
			(struct sockaddr *)addr, sizeof(struct sockaddr_in));
	printf("sentlen:%d,bufflen=%d,tcp/iplen=%d,iplen=%d\n",sentlen,sizeof(buff),tcp_header->doff*4+ip_header->ihl*4,ip_header->ihl*4);
	if(sentlen == -1) perror("sendto");
}

unsigned short checksum(unsigned short *buffer, int size) 
{
	unsigned long cksum = 0;

	//size>1时可以直接以16bit为单位进行累加保存.
	while (size >1) 
	{
		cksum += *buffer++;
		size -= sizeof(unsigned short);
	}

	//最后一个size==1说明有数据剩余小于16bit,累加.
	if (size) cksum += *(unsigned char*)buffer;  //..buffer..size..2......

	//将多出16bit的高位加到低位.
	cksum = (cksum >> 16) + (cksum & 0xffff);
	//可能仍然有高位存在,再次加到低位.
	cksum += (cksum >> 16);

	//取反得到校验和.
	return (unsigned short)(~cksum);

}

unsigned short random_port(unsigned short minport, unsigned short maxport) {
	/*struct time stime;
	 *unsigned seed;
	 *gettime(&stime);
	 *seed=stime.ti_hund*stime.ti_min*stime.ti_hour;
	 * srand(seed);*/
	srand((unsigned)time(NULL));
	return(getrandom(minport, maxport));
}

void random_ip(char *str) {
	int a, b, c, d, i = 0;
	static long j = 0;
	srand((unsigned)time(NULL) + (i++) + (j++));
	a = getrandom(0, 255);
	srand((unsigned)time(NULL) + (i++) + (j++));
	b = getrandom(0, 255);
	srand((unsigned)time(NULL) + (i++) + (j++));
	c = getrandom(0, 255);
	srand((unsigned)time(NULL) + (i++) + (j++));
	d = getrandom(0, 255);
	sprintf(str, "%d.%d.%d.%d", a, b, c, d);
	printf("%s\n", str);
}

猜你喜欢

转载自blog.csdn.net/qq_39937902/article/details/82379125
今日推荐