转载

linux原始套接字(3)-构造IP_TCP发送与接收

一.概述                                                   

tcp报文 封装在 ip报文中,创建tcp的原始套接字如下:

 1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); 

此时只能构造tcp报文,如果想 进一步 构造ip首部,那么就要开启sockfd的 IP_HDRINCL 选项:

 1 int on = 1; 2 setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); 

ip报文格式:

linux原始套接字(3)-构造IP_TCP发送与接收

ip首部结构定义在 netinet/ip.h

  1 struct ip  2 {  3 #if __BYTE_ORDER == __LITTLE_ENDIAN  4     unsigned int ip_hl:4;        /* header length */  5     unsigned int ip_v:4;        /* version */  6 #endif  7 #if __BYTE_ORDER == __BIG_ENDIAN  8     unsigned int ip_v:4;        /* version */  9     unsigned int ip_hl:4;        /* header length */ 10 #endif 11     u_int8_t ip_tos;            /* type of service */ 12     u_short ip_len;            /* total length */ 13     u_short ip_id;            /* identification */ 14     u_short ip_off;            /* fragment offset field */ 15 #define    IP_RF 0x8000            /* reserved fragment flag */ 16 #define    IP_DF 0x4000            /* dont fragment flag */ 17 #define    IP_MF 0x2000            /* more fragments flag */ 18 #define    IP_OFFMASK 0x1fff        /* mask for fragmenting bits */ 19     u_int8_t ip_ttl;            /* time to live */ 20     u_int8_t ip_p;            /* protocol */ 21     u_short ip_sum;            /* checksum */ 22     struct in_addr ip_src, ip_dst;    /* source and dest address */ 23 }; 

tcp报文格式:

linux原始套接字(3)-构造IP_TCP发送与接收

tcp首部结构定义在 netinet/tcp.h

  1 struct tcphdr  2   {  3     __extension__ union  4     {  5       struct  6       {  7     u_int16_t th_sport;        /* source port */  8     u_int16_t th_dport;        /* destination port */  9     tcp_seq th_seq;        /* sequence number */ 10     tcp_seq th_ack;        /* acknowledgement number */ 11 # if __BYTE_ORDER == __LITTLE_ENDIAN 12     u_int8_t th_x2:4;        /* (unused) */ 13     u_int8_t th_off:4;        /* data offset */ 14 # endif 15 # if __BYTE_ORDER == __BIG_ENDIAN 16     u_int8_t th_off:4;        /* data offset */ 17     u_int8_t th_x2:4;        /* (unused) */ 18 # endif 19     u_int8_t th_flags; 20 # define TH_FIN    0x01 21 # define TH_SYN    0x02 22 # define TH_RST    0x04 23 # define TH_PUSH    0x08 24 # define TH_ACK    0x10 25 # define TH_URG    0x20 26     u_int16_t th_win;        /* window */ 27     u_int16_t th_sum;        /* checksum */ 28     u_int16_t th_urp;        /* urgent pointer */ 29       }; 30       struct 31       { 32     u_int16_t source; 33     u_int16_t dest; 34     u_int32_t seq; 35     u_int32_t ack_seq; 36 # if __BYTE_ORDER == __LITTLE_ENDIAN 37     u_int16_t res1:4; 38     u_int16_t doff:4; 39     u_int16_t fin:1; 40     u_int16_t syn:1; 41     u_int16_t rst:1; 42     u_int16_t psh:1; 43     u_int16_t ack:1; 44     u_int16_t urg:1; 45     u_int16_t res2:2; 46 # elif __BYTE_ORDER == __BIG_ENDIAN 47     u_int16_t doff:4; 48     u_int16_t res1:4; 49     u_int16_t res2:2; 50     u_int16_t urg:1; 51     u_int16_t ack:1; 52     u_int16_t psh:1; 53     u_int16_t rst:1; 54     u_int16_t syn:1; 55     u_int16_t fin:1; 56 # else 57 #  error "Adjust your <bits/endian.h> defines" 58 # endif 59     u_int16_t window; 60     u_int16_t check; 61     u_int16_t urg_ptr; 62       }; 63     }; 64 }; 

对照结构的 定义 和上面 结构图 很容易理解。注意:ip和tcp的 四位首部长度 都是指占多少个32bit。如果普通ip首部长度是20字节,4字节占32位,那么这个值就是20/4=5。

二.构造IP_TCP发送                              

   1 /**   2  * @file ip_tcp_send.c   3  */   4    5 #include <stdio.h>   6 #include <stdlib.h>   7 #include <string.h>   8 #include <unistd.h>   9 #include <sys/socket.h>  10 #include <arpa/inet.h>  11 #include <netinet/in.h>  12 #include <netinet/ip.h>  13 #include <netinet/tcp.h>  14   15 /* ip首部长度 */  16 #define IP_HEADER_LEN sizeof(struct ip)  17 /* tcp首部长度 */  18 #define TCP_HEADER_LEN sizeof(struct tcphdr)  19 /* ip首部 + tcp首部长度 */  20 #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN  21   22 void err_exit(const char *err_msg)  23 {  24     perror(err_msg);  25     exit(1);  26 }  27   28 /* 填充ip首部 */  29 struct ip *fill_ip_header(const char *src_ip, const char *dst_ip, int ip_packet_len)  30 {  31     struct ip *ip_header;  32   33     ip_header = (struct ip *)malloc(IP_HEADER_LEN);  34     ip_header->ip_v = IPVERSION;  35     ip_header->ip_hl = sizeof(struct ip) / 4;        /* 这里注意,ip首部长度是指占多个32位的数量,4字节=32位,所以除以4 */  36     ip_header->ip_tos = 0;  37     ip_header->ip_len = htons(ip_packet_len);        /* 整个IP数据报长度,包括普通数据 */  38     ip_header->ip_id = 0;                            /* 让内核自己填充标识位 */  39     ip_header->ip_off = 0;  40     ip_header->ip_ttl = MAXTTL;  41     ip_header->ip_p = IPPROTO_TCP;                   /* ip包封装的协议类型 */  42     ip_header->ip_sum = 0;                           /* 让内核自己计算校验和 */  43     ip_header->ip_src.s_addr = inet_addr(src_ip);    /* 源IP地址 */  44     ip_header->ip_dst.s_addr = inet_addr(dst_ip);    /* 目标IP地址 */  45   46     return ip_header;  47 }  48   49 /* 填充tcp首部 */  50 struct tcphdr *fill_tcp_header(int src_port, int dst_port)  51 {  52     struct tcphdr *tcp_header;  53   54     tcp_header = (struct tcphdr *)malloc(TCP_HEADER_LEN);  55     tcp_header->source = htons(src_port);   56     tcp_header->dest = htons(dst_port);  57     /* 同IP首部一样,这里是占32位的字节多少个 */  58     tcp_header->doff = sizeof(struct tcphdr) / 4;  59     /* 发起连接 */  60     tcp_header->syn = 1;  61     tcp_header->window = 4096;  62     tcp_header->check = 0;  63   64     return tcp_header;  65 }  66   67 /* 发送ip_tcp报文 */  68 void ip_tcp_send(const char *src_ip, int src_port, const char *dst_ip, int dst_port, const char *data)  69 {  70     struct ip *ip_header;  71     struct tcphdr *tcp_header;  72     struct sockaddr_in dst_addr;  73     socklen_t sock_addrlen = sizeof(struct sockaddr_in);  74   75     int data_len = strlen(data);  76     int ip_packet_len = IP_TCP_HEADER_LEN + data_len;  77     char buf[ip_packet_len];  78     int sockfd, ret_len, on = 1;  79   80     bzero(&dst_addr, sock_addrlen);  81     dst_addr.sin_family = PF_INET;  82     dst_addr.sin_addr.s_addr = inet_addr(dst_ip);  83     dst_addr.sin_port = htons(dst_port);  84   85     /* 创建tcp原始套接字 */  86     if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1)  87         err_exit("socket()");  88   89     /* 开启IP_HDRINCL,自定义IP首部 */  90     if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1)  91         err_exit("setsockopt()");  92   93     /* ip首部 */  94     ip_header = fill_ip_header(src_ip, dst_ip, ip_packet_len);  95     /* tcp首部 */  96     tcp_header = fill_tcp_header(src_port, dst_port);  97   98     bzero(buf, ip_packet_len);  99     memcpy(buf, ip_header, IP_HEADER_LEN); 100     memcpy(buf + IP_HEADER_LEN, tcp_header, TCP_HEADER_LEN); 101     memcpy(buf + IP_TCP_HEADER_LEN, data, data_len); 102  103     /* 发送报文 */ 104     ret_len = sendto(sockfd, buf, ip_packet_len, 0, (struct sockaddr *)&dst_addr, sock_addrlen); 105     if (ret_len > 0) 106         printf("sendto() ok!!!/n"); 107     else printf("sendto() failed/n"); 108  109     close(sockfd); 110     free(ip_header); 111     free(tcp_header); 112 } 113  114 int main(int argc, const char *argv[]) 115 { 116     if (argc != 6) 117     { 118         printf("usage:%s src_ip src_port dst_ip dst_port data/n", argv[0]); 119         exit(1); 120     } 121  122     /* 发送ip_tcp报文 */ 123     ip_tcp_send(argv[1], atoi(argv[2]), argv[3], atoi(argv[4]), argv[5]); 124  125     return 0; 126 } 

流程:命令行接收的参数分别是: 源ip地址源端口目标ip地址目标端口普通数据 。然后通过 源/目标ip 构造ip首部,通过 源/目标端口 构造tcp首部。把目标ip/端口填充到sockaddr_in,最后把构造的ip首部,tcp首部,普通数据 全部复制 到缓冲区,一并发送到刚刚的sockaddr_in的地址!!!

三.接收IP_TCP                                      

  1 /**  2  * @file ip_tcp_recv.c  3  */  4   5 #include <stdio.h>  6 #include <stdlib.h>  7 #include <string.h>  8 #include <unistd.h>  9 #include <sys/socket.h> 10 #include <arpa/inet.h> 11 #include <netinet/in.h> 12 #include <netinet/ip.h> 13 #include <netinet/tcp.h> 14  15 /* ip首部长度 */ 16 #define IP_HEADER_LEN sizeof(struct ip) 17 /* tcp首部长度 */ 18 #define TCP_HEADER_LEN sizeof(struct tcphdr) 19 /* ip首部 + tcp首部长度 */ 20 #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN 21 /* 接收数据缓冲大小 */ 22 #define BUFFER_SIZE 1024 23 /* ip首部 + tcp首部 + 数据缓冲区大小 */ 24 #define IP_TCP_BUFF_SIZE IP_TCP_HEADER_LEN + BUFFER_SIZE 25  26 void err_exit(const char *err_msg) 27 { 28     perror(err_msg); 29     exit(1); 30 } 31  32 /* 原始套接字接收 */ 33 void raw_socket_recv() 34 { 35     struct ip *ip_header; 36     struct tcphdr *tcp_header; 37     int sock_raw_fd, ret_len; 38     char buf[IP_TCP_BUFF_SIZE]; 39  40     if ((sock_raw_fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1) 41         err_exit("socket()"); 42  43     /* 接收数据 */ 44     while (1) 45     { 46         bzero(buf, IP_TCP_BUFF_SIZE); 47         ret_len = recv(sock_raw_fd, buf, IP_TCP_BUFF_SIZE, 0); 48         if (ret_len > 0) 49         { 50             /* 取出ip首部 */ 51             ip_header = (struct ip *)buf; 52             /* 取出tcp首部 */ 53             tcp_header = (struct tcphdr *)(buf + IP_HEADER_LEN); 54             printf("=======================================/n"); 55             printf("from ip:%s/n", inet_ntoa(ip_header->ip_src)); 56             printf("from port:%d/n", ntohs(tcp_header->source)); 57             /* 取出数据 */ 58             printf("get data:%s/n", buf + IP_TCP_HEADER_LEN); 59         } 60     } 61      62     close(sock_raw_fd); 63 } 64  65 int main(void) 66 { 67     /* 原始套接字接收 */ 68     raw_socket_recv(); 69  70     return 0; 71 } 

流程:创建TCP类型的原始套接,原始套接字是 点对点 传输,不像TCP/UDP是 端对端 ,故原始套接字 不存在端口 概念,可以直接接收。这里接收的是整个IP报文,里面包含了TCP报文。接收后 依次 取出ip首部,tcp首部,普通数据!!!

四.实验                                                  

发送端和接收端都在本机,我们打开wireshark监听 lo 接口。以root身份运行2个程序:

linux原始套接字(3)-构造IP_TCP发送与接收 linux原始套接字(3)-构造IP_TCP发送与接收

上面发送了2个tcp包,每次的源ip,源端口都是 伪造 的。DDOS程序就是这个原理。

wireshark结果

linux原始套接字(3)-构造IP_TCP发送与接收

原文  http://www.cnblogs.com/yuuyuu/p/5169931.html
正文到此结束
Loading...