winpcap发送数据包模拟TCP连接和断开的7次握手

 

上次我用winpcap成功发送一个UDP数据包到服务器。 相比较TCP要复杂的多, tcp不但要完成开始连接的三次握手, 还要处理数据包的序列号。 下面是tcp七次握手示意图:

连接时三次握手

 断开四次握手

 七次握手数据包

下面我就来模拟一个TCP客服端,发送一个连接到服务器, 服务器之后断开。这样就是7次握手, 不含其他数据包。

首先我们做一个tcp服务器, 最好是一个公网的, 这样连接就更有真实性。

服务器代码:

 

  1. #include <Winsock2.h>    
  2. #include <process.h>   
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5.   
  6. #pragma comment (lib,"ws2_32.lib")  
  7.   
  8. SOCKET tcpServerSocket = 0;  
  9. SOCKADDR_IN tcpServerAddr = { 0 };  
  10. const int backlog = SOMAXCONN;  
  11. WSADATA wsaData = { 0 };   
  12. int ret_code;  
  13.   
  14. #define PORT 7456                       //server port  
  15. #define IPSTRING "122.*.*.*"        //server ip  
  16.   
  17. //³ö´í´¦Àíº¯Êý  
  18. void error(const char* errstr, int errcode)  
  19. {  
  20.     printf("Error:      %s\n", errstr);  
  21.     printf("Error Code: %s\n", errcode);  
  22. }  
  23.   
  24. void error_exit(const char* errstr, int errcode)  
  25. {  
  26.     error(errstr, errcode);  
  27.     exit(errcode);  
  28. }  
  29.   
  30. void main()  
  31. {  
  32.     ret_code = WSAStartup( MAKEWORD( 2, 2 ), &wsaData);     
  33.     if ( ret_code != 0 ) {                                    
  34.         error_exit("WSAStartup error", ret_code);  
  35.     }   
  36.       
  37.     //´´½¨Ì×½Ó×Ö     
  38.     if((tcpServerSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)  
  39.         error_exit("socket error", WSAGetLastError());  
  40.       
  41.     //°ó¶¨IPºÍ¶Ë¿Ú  
  42.     tcpServerAddr.sin_family = AF_INET;  
  43.     tcpServerAddr.sin_port = htons(PORT);  
  44.     tcpServerAddr.sin_addr.S_un.S_addr = inet_addr(IPSTRING);  
  45.     if((bind(tcpServerSocket, (struct sockaddr *)&tcpServerAddr, sizeof(tcpServerAddr))) == SOCKET_ERROR)  
  46.         error_exit("bind error", WSAGetLastError());  
  47.       
  48.     if((listen(tcpServerSocket, backlog)) == SOCKET_ERROR)  
  49.         error_exit("listen error", WSAGetLastError());  
  50.       
  51.     while(true)  
  52.     {  
  53.   
  54.         SOCKET tcpClientSocket = 0;  
  55.         SOCKADDR_IN tcpClientAddr = { 0 };  
  56.         int tcpClientAddrLen = sizeof(SOCKADDR_IN);  
  57.   
  58.         if((tcpClientSocket = accept(tcpServerSocket, (struct sockaddr *)&tcpClientAddr, &tcpClientAddrLen)) == INVALID_SOCKET)  
  59.         {  
  60.             error_exit("accept error", WSAGetLastError());  
  61.             continue;  
  62.         }  
  63.   
  64.         printf("From IP = %s Port = %d\n", inet_ntoa(tcpClientAddr.sin_addr), ntohs(tcpClientAddr.sin_port));  
  65.   
  66.         //·¢ËͺͽÓÊÕÊý¾Ý  
  67.         //recv  
  68.         //send  
  69.           
  70.         closesocket(tcpClientSocket);  
  71.     }  
  72. }  

 

好了服务器是做好了, 现在主要是做客服端了。在发送数据包之前, 我们要知道数据包的结构, 对一个TCP数据包结构像这个样子:

【以太网头】【IP头】【TCP头】【数据】

由于握手过程数据可以空, 所以只有三个部分。 各种数据包头如下声明(注意我这里给出的是小端模式), 大部分数从linux系统里面copy过来。 对数据包详细信息, 这里就不多说了。

 

  1. #include <pcap.h>  
  2. #pragma comment(lib, "wpcap.lib")  
  3. #include <stdio.h>  
  4. #pragma comment(lib, "Ws2_32.lib")  
  5.   
  6.   
  7. #define ETHER_ADDR_LEN 6  
  8. //from linux's ethernet.h  
  9. #define ETHERTYPE_IP            0x0800          /* IP */  
  10.   
  11.   
  12. //以太网数据头  
  13. struct   ether_header{   
  14.     u_char   ether_dhost[ETHER_ADDR_LEN];   
  15.     u_char   ether_shost[ETHER_ADDR_LEN];   
  16.     u_short   ether_type;  //如果上一层为IP协议。则ether_type的值就是0x0800  
  17. };  
  18.   
  19. //IP数据头  
  20. struct ip_header  //小端模式  
  21. {   
  22.     unsigned   char     ihl:4;              //ip   header   length      
  23.     unsigned   char     version:4;          //version     
  24.     u_char              tos;                //type   of   service     
  25.     u_short             tot_len;            //total   length     
  26.     u_short             id;                 //identification     
  27.     u_short             frag_off;           //fragment   offset     
  28.     u_char              ttl;                //time   to   live     
  29.     u_char              protocol;           //protocol   type     
  30.     u_short             check;              //check   sum     
  31.     u_int               saddr;              //source   address     
  32.     u_int               daddr;              //destination   address     
  33. };  
  34.   
  35. //tcp数据头  
  36. struct tcp_header //小端模式  
  37. {  
  38.     u_int16_t   source;  
  39.     u_int16_t   dest;  
  40.     u_int32_t   seq;  
  41.     u_int32_t   ack_seq;  
  42.     u_int16_t   res1:4;  
  43.     u_int16_t   doff:4;  
  44.     u_int16_t   fin:1;  
  45.     u_int16_t   syn:1;  
  46.     u_int16_t   rst:1;  
  47.     u_int16_t   psh:1;  
  48.     u_int16_t   ack:1;  
  49.     u_int16_t   urg:1;  
  50.     u_int16_t   res2:2;  
  51.     u_int16_t   window;  
  52.     u_int16_t   check;  
  53.     u_int16_t   urg_ptr;  
  54. };  
  55.   
  56. //tcp和udp计算校验和是的伪头  
  57. struct psd_header {  
  58.     u_int32_t   sourceip;       //源IP地址  
  59.     u_int32_t   destip;         //目的IP地址  
  60.     u_char      mbz;            //置空(0)  
  61.     u_char      ptcl;           //协议类型  
  62.     u_int16_t   plen;           //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)  
  63. };   

 

 好了, 现在可以发送数据包了, 我们发送四个数据包, 服务器回复三个数据包。

1.  client发送SYN数据包

2.  接收server syn和ack包

3.  发送ack包

4.  接收fin数据包

5. 回复ack

6. 发送fin

7. 接收ack

这里我用两个线程来处理, 主线程接收数据包(pcap_loop), 一个子线程负责发送数据包(send_packet_handle)。 在构建数据报的时候一定要注意 TCP的seq 和ack_seq的更新, 下一个发送的seq值等于上一个收到的ack_seq。 下一个seq_ack为上一个接收的seq+1.  这里只是一个简单的模拟程序, 没有考虑两个线程之间的数据同步。

 

  1. char* device = "\\Device\\NPF_{06864041-9387-44DC-AF44-37779B0F2E9E}";  
  2. pcap_t* adhandle = NULL;  
  3. char errbuf[PCAP_ERRBUF_SIZE] = { 0 };  
  4. unsigned int netmask=0xffffff;   
  5. struct bpf_program fcode;  
  6. char* filter = "tcp";  
  7.   
  8. #include <process.h>  
  9.   
  10. #define DSTPORT 7456  
  11. #define SRCPORT 7456  
  12. #define DSTIP   "*.*.*.*"  
  13. #define SRCIP   "*.*.*.*"  
  14.   
  15. bool isShouldReply = false;  
  16. //step = 0, 接收 syn ack  
  17. //step = 1, 接收 fin  
  18. //step = 2,接收 fin之后ack  
  19.   
  20. int step = 0;   
  21.   
  22. //分别表示发送段和接收端开始系列号  
  23. unsigned int next_seq = 0x42345678;  
  24. unsigned int next_ack_seq = 0;  
  25.   
  26.   
  27. //用一个线程检测从目的地发过来的数据包.  
  28. //另一个线程向目的地发送数据包.  
  29.   
  30. //负责发送数据包  
  31. void send_packet_handle(void* arg);  
  32. //负责接收数据包, pcap_loop参数用于回调  
  33. void my_pcap_handler(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);  
  34.   
  35. unsigned short do_check_sum(void* buffer, int len);  
  36. char* uint_to_addr(u_int addr);  
  37. void main()  
  38. {  
  39.     if((adhandle = pcap_open(device, 0x10000, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf)) == NULL)  
  40.     {  
  41.         printf("[pcap_open error] : %s\n", errbuf);  
  42.         return;  
  43.     }  
  44.   
  45.     if(pcap_compile(adhandle, &fcode, filter, 1, netmask) == -1)  
  46.     {  
  47.         printf("[pcap_compile error] : %s\n", pcap_geterr(adhandle));  
  48.         return;  
  49.     }  
  50.   
  51.     if(pcap_setfilter(adhandle, &fcode) == -1)  
  52.     {  
  53.         printf("[pcap_setfilter error] : %s\n", pcap_geterr(adhandle));  
  54.         return;  
  55.     }  
  56.     unsigned long p = _beginthread(send_packet_handle, 0, NULL);  
  57.   
  58.     pcap_loop(adhandle, 0, my_pcap_handler, NULL);  
  59. }  
  60.   
  61. void my_pcap_handler(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)  
  62. {  
  63.     ether_header* pe_header = (ether_header*)pkt_data;  
  64.   
  65.     if(pe_header->ether_type  == htons(ETHERTYPE_IP))  
  66.     {  
  67.         ip_header* p_ip_header = (ip_header*)(pkt_data + sizeof(ether_header));  
  68.   
  69.         if(p_ip_header->saddr == p_ip_header->daddr)  
  70.         {  
  71.             printf("%s\n", uint_to_addr(p_ip_header->saddr));  
  72.         }  
  73.           
  74.         //IP地址检验  
  75.         if(p_ip_header->protocol == IPPROTO_TCP ||  
  76.             (p_ip_header->saddr == inet_addr(DSTIP) && p_ip_header->daddr == inet_addr(SRCIP)) ||  
  77.             (p_ip_header->saddr == inet_addr(SRCIP) && p_ip_header->daddr == inet_addr(DSTIP)))  
  78.         {  
  79.             tcp_header* ptcpHeader = (tcp_header*)(pkt_data + sizeof(ether_header) + p_ip_header->ihl * 4);  
  80.   
  81.             //端口检验  
  82.             if(ptcpHeader->dest == htons(DSTPORT) || ptcpHeader->source == htons(DSTPORT) )  
  83.             {  
  84.                 //打印信息  
  85.                 printf("[%s:%d \t-> ", uint_to_addr(p_ip_header->saddr), ntohs(ptcpHeader->source));  
  86.                 printf("%s:%d] len = %u type: ", uint_to_addr(p_ip_header->daddr),ntohs(ptcpHeader->dest),  
  87.                     ntohs(p_ip_header->tot_len) - sizeof(ip_header) - sizeof(tcp_header));  
  88.                 if(ptcpHeader->syn == 1) printf("SYN = %u ", ntohl(ptcpHeader->seq));  
  89.                 if(ptcpHeader->ack == 1) printf("ACK = %u ", ntohl(ptcpHeader->ack_seq));  
  90.                 if(ptcpHeader->fin == 1) printf("FIN = %u ", ntohl(ptcpHeader->seq));  
  91.                 if(ptcpHeader->rst == 1) printf("RST ");  
  92.                 if(ptcpHeader->psh == 1) printf("PSH ");  
  93.                 printf("\n");  
  94.   
  95.                 if(step == 0 && ptcpHeader->syn == 1 && ptcpHeader->ack == 1 && ptcpHeader->ack_seq == htonl(next_seq + 1))   
  96.                 {  
  97.                     next_ack_seq = ntohl(ptcpHeader->seq) + 1;  
  98.                     next_seq = ntohl(ptcpHeader->ack_seq);  
  99.                     isShouldReply = true;  
  100.                 }  
  101.                 else if(step == 1 && ptcpHeader->fin == 1)  
  102.                 {  
  103.                     next_ack_seq = ntohl(ptcpHeader->seq) + 1;  
  104.                     next_seq = ntohl(ptcpHeader->ack_seq);  
  105.                     isShouldReply = true;  
  106.                 }  
  107.                 /* 
  108.                 else if(step == 2 && ptcpHeader->ack == 1 && ptcpHeader->seq == next_ack_seq) 
  109.                 { 
  110.                     isShouldReply = true; 
  111.                 }*/  
  112.                   
  113.             }  
  114.         }  
  115.     }  
  116.   
  117. }  
  118.   
  119. void send_packet_handle(void* arg)  
  120. {  
  121.     //三次握手不含数据  
  122.     char tcp_buffer[54] = { 0 };  //14 以太网头部, 20 ip头, 20 tcp头, 没有其它数据  
  123.   
  124.     ether_header* pether_header =   (ether_header*)tcp_buffer;  
  125.     ip_header* pip_header       =   (ip_header*)(tcp_buffer + sizeof(ether_header));  
  126.     tcp_header* ptcp_header         =   (tcp_header*)(tcp_buffer + sizeof(ether_header) + sizeof(ip_header));  
  127.   
  128.     //设置以太网头部  
  129.     //所有MAC位置1  
  130.     //unsigned char mac[] = {0,238,238,129,65,238,0,29,96,102,102,15};  
  131.     //memcpy(pether_header, mac, 12);  
  132.     memset(pether_header, 1, 12);  
  133.     pether_header->ether_type =  htons(ETHERTYPE_IP);  
  134.   
  135.     //设置IP头  
  136.     pip_header->ihl = sizeof(ip_header) / 4;  
  137.     pip_header->version = 4;  
  138.     pip_header->tos = 0;  
  139.     pip_header->tot_len = htons(sizeof(tcp_buffer) - sizeof(ether_header)); //12288  
  140.     pip_header->id = 616;                                         //616  
  141.     pip_header->frag_off = 64;  
  142.     pip_header->ttl = 128;  
  143.     pip_header->protocol = IPPROTO_TCP;  
  144.     pip_header->check = 0;  
  145.     pip_header->saddr = inet_addr(SRCIP);  
  146.     pip_header->daddr = inet_addr(DSTIP);  
  147.     pip_header->check  = in_cksum((u_int16_t*)pip_header, sizeof(ip_header));  
  148.   
  149.     //设置TCP头  
  150.     ptcp_header->source = htons(SRCPORT);  
  151.     ptcp_header->dest = htons(DSTPORT);  
  152.     ptcp_header->seq = htonl(next_seq);  
  153.     ptcp_header->ack_seq = htonl(next_ack_seq);  
  154.     ptcp_header->doff = sizeof(tcp_header) / 4;  
  155.     ptcp_header->res1 = 0;  
  156.     ptcp_header->res2 = 0;  
  157.     ptcp_header->fin = 0;  
  158.     ptcp_header->syn = 1;  
  159.     ptcp_header->rst = 0;  
  160.     ptcp_header->psh = 0;  
  161.     ptcp_header->ack = 0;  
  162.     ptcp_header->urg = 0;  
  163.     ptcp_header->window = 65535;  
  164.     ptcp_header->check = 0;  
  165.     ptcp_header->urg_ptr = 0;  
  166.   
  167.     ptcp_header->check = do_check_sum(ptcp_header, sizeof(tcp_header));  
  168.   
  169.   
  170.     //发送一个SYN包到对方  
  171.     if(pcap_sendpacket(adhandle, (const u_char*)tcp_buffer, sizeof(tcp_buffer)) == -1)  
  172.     {  
  173.         printf("[pcap_sendpacket error]\n");  
  174.         return;  
  175.     }  
  176.   
  177.     while(true)  
  178.     {  
  179.         if(isShouldReply)  
  180.             break;  
  181.         Sleep(100);  
  182.     }  
  183.   
  184.     //发送一个ACK包到对方  
  185.     ptcp_header->syn = 0;  
  186.     ptcp_header->ack = 1;  
  187.     ptcp_header->seq = htonl(next_seq);  
  188.     ptcp_header->ack_seq = htonl(next_ack_seq);  
  189.     ptcp_header->check = 0;  
  190.     ptcp_header->check = do_check_sum(ptcp_header, sizeof(tcp_header));  
  191.       
  192.     isShouldReply = false;  
  193.     step  = 1;  
  194.     if(pcap_sendpacket(adhandle, (const u_char*)tcp_buffer, sizeof(tcp_buffer)) == -1)  
  195.     {  
  196.         printf("[pcap_sendpacket error]\n");  
  197.         return;  
  198.     }  
  199.   
  200.     while(true)  
  201.     {  
  202.         if(isShouldReply)  
  203.             break;  
  204.         Sleep(100);  
  205.     }  
  206.   
  207.     //发送fin回复的ack包  
  208.     ptcp_header->ack = 1;  
  209.     ptcp_header->seq = htonl(next_seq);  
  210.     ptcp_header->ack_seq = htonl(next_ack_seq);  
  211.     ptcp_header->check = 0;  
  212.     ptcp_header->check = do_check_sum(ptcp_header, sizeof(tcp_header));  
  213.     isShouldReply = false;  
  214.     if(pcap_sendpacket(adhandle, (const u_char*)tcp_buffer, sizeof(tcp_buffer)) == -1)  
  215.     {  
  216.         printf("[pcap_sendpacket error]\n");  
  217.         return;  
  218.     }  
  219.   
  220.     //接着发送一个fin, ack包  
  221.     ptcp_header->ack = 1;  
  222.     ptcp_header->fin = 1;  
  223.     ptcp_header->seq = htonl(next_seq);  
  224.     ptcp_header->ack_seq = htonl(next_ack_seq);  
  225.     ptcp_header->check = 0;  
  226.     ptcp_header->check = do_check_sum(ptcp_header, sizeof(tcp_header));  
  227.     isShouldReply = false;  
  228.     step = 2;  
  229.     if(pcap_sendpacket(adhandle, (const u_char*)tcp_buffer, sizeof(tcp_buffer)) == -1)  
  230.     {  
  231.         printf("[pcap_sendpacket error]\n");  
  232.         return;  
  233.     }  
  234.     printf("Finished\n");  
  235. }  
  236.   
  237.   
  238. char* uint_to_addr(u_int addr)  
  239. {  
  240.     in_addr inaddr;  
  241.     inaddr.S_un.S_addr =  addr;  
  242.     return inet_ntoa(inaddr);  
  243. }  
  244.   
  245. unsigned short do_check_sum(void* buffer, int len)  
  246. {  
  247.     char buffer2[128] = { 0 };  
  248.     psd_header* psd = (psd_header*)buffer2;  
  249.     psd->sourceip = inet_addr(SRCIP);  
  250.     psd->destip = inet_addr(DSTIP);  
  251.     psd->ptcl = IPPROTO_TCP;  
  252.     psd->plen =  htons(sizeof(tcp_header));  
  253.   
  254.     memcpy(buffer2 + sizeof(psd_header), buffer, sizeof(tcp_header));  
  255.   
  256.     return in_cksum((u_int16_t*)buffer2, sizeof(psd_header) + sizeof(tcp_header));  
  257. }  

 

运行结果服务器成功收到:

From IP = 116.227.*.* Port = 10999

说明我们的连接成功了。

注意事项:

1. 很可能在收到服务器的数据包时候操作系统会发送一个RST给服务器, 这样连接就失败。 我还没有很好的办法阻止这个数据包的发送。 在我的机器上如果打开防火墙, 就不会产生RST数据包, 如果关闭就会产生。

2. 如果有些值不知道怎么设置, 可以查看一些资料。 同时也可以用socket可以自己先做一个client, connect到服务器, 获取数据包, 看看这些数据包是怎么设置的。

3. 两个mac地址我全部设置了1, 目前我还不确定这些有什么用, 以后有时间再研究一下。

4. 注意很有可能一个数据包可能发送多次, 主要是服务器在一定时间没有收到答复, 会重新发送。 注意我的程序没有处理这样的情况。

5. 注意字节序的问题, 我开始就是一个地方忘了转换, 导致数据包不能成功收到。

ok了。

 

转:http://blog.csdn.net/liqinghua1653/archive/2009/04/11/4064464.aspx

posted on 2011-03-01 16:21  意扬  阅读(1644)  评论(0)    收藏  举报

导航