raw socket (续)

来源:互联网 发布:网络渗透测试工程师 编辑:程序博客网 时间:2024/06/05 11:17

最近在学习raw socket,上一篇文章也成功发送了SYN,使得服务端的状态变为SYN_RECV。
http://blog.csdn.net/lizhia1221/article/details/51946592
因此,就想尝试去模拟TCP三次握手,无非就是发送三个数据包嘛,想想好像挺简单的,然后瞬间打脸了。

下面是测试代码:

/*   模拟tcp三次握手,然后收到syn+ack之后,内核也会处理包,   自动发送rst,因此目前虽然可以模拟三次握手,但是提前被内   核结束了,无法建立连接 */#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <errno.h>#include <netinet/ip.h>#include <netinet/tcp.h>#include <arpa/inet.h>#define DEST_PORT 8888#define SRC_PORT 9999#define SRC_ADDRESS "192.168.0.174"#define DEST_ADDRESS "192.168.0.174"//tcp 伪首部struct pseudo_header{    u_int32_t source_address;    u_int32_t dest_address;    u_int8_t placeholder;    u_int8_t protocol;    u_int16_t tcp_length;};/*  Generic checksum calculation function*/unsigned short csum(unsigned short *ptr,int nbytes){    register long sum;    unsigned short oddbyte;    register short answer;    sum=0;    while(nbytes>1) {        sum+=*ptr++;        nbytes-=2;    }    if(nbytes==1) {        oddbyte=0;        *((u_char*)&oddbyte)=*(u_char*)ptr;        sum+=oddbyte;    }    sum = (sum>>16)+(sum & 0xffff);    sum = sum + (sum>>16);    answer=(short)~sum;    return(answer);}int get_ack_seq(unsigned char* buffer,int size,unsigned int &seq);int fill_packet(char *datagram,int dsize,unsigned char* buffer,int bsize,unsigned int ack_seq);int connect(){    int fd=socket(PF_INET,SOCK_RAW,IPPROTO_TCP);    if(fd==-1)    {        perror("Failed to create socket");        exit(1);    }    //Datagram to represent the packet    char datagram[4096],source_ip[32],*pseudogram;    memset(datagram,0,sizeof(datagram));    //IP Header    struct iphdr *iph=(struct iphdr*)datagram;    //TCP Header    struct tcphdr *tcph=(struct tcphdr*)(datagram+sizeof(struct iphdr));    struct pseudo_header psh;    //socket address    struct sockaddr_in sin;    strcpy(source_ip,SRC_ADDRESS);    sin.sin_family=AF_INET;    sin.sin_port=htons(DEST_PORT);    sin.sin_addr.s_addr=inet_addr(DEST_ADDRESS);    //Fill in the IP Header    iph->ihl=5;// 20 byte    iph->version=4;//ipv4    iph->tos=0;    iph->tot_len=sizeof(struct iphdr)+sizeof(struct tcphdr);    iph->id=htonl(54321);//ID    iph->frag_off=0;    iph->ttl=255;    iph->protocol=IPPROTO_TCP;    iph->check=0;    iph->saddr=inet_addr(source_ip);    iph->daddr=sin.sin_addr.s_addr;    //IP checksum    iph->check=csum((unsigned short*)datagram,sizeof(struct iphdr));    //TCP header    tcph->source = htons(SRC_PORT);    tcph->dest = htons(DEST_PORT);    tcph->seq = htonl(1);    tcph->ack_seq = htonl(0);    tcph->doff = 5; //tcp header size    tcph->fin=0;    tcph->syn=1;    tcph->rst=0;    tcph->psh=0;    tcph->ack=0;    tcph->urg=0;    tcph->window = htons (5840);    /* maximum allowed window size */    tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header    tcph->urg_ptr = 0;    //Now the TCP checksum    psh.source_address = inet_addr(source_ip);    psh.dest_address = sin.sin_addr.s_addr;    psh.placeholder = 0;    psh.protocol = IPPROTO_TCP;    psh.tcp_length = htons(sizeof(struct tcphdr));    int psize=sizeof(struct pseudo_header)+sizeof(struct tcphdr);    pseudogram=(char*)malloc(psize);    memcpy(pseudogram,(char*)&psh,sizeof(struct pseudo_header));    memcpy(pseudogram+sizeof(struct pseudo_header),tcph,sizeof(struct tcphdr));    tcph->check=csum((unsigned short*)pseudogram,psize);    //TP_HDRINCL to tell the kernel that headers are included in the pakcet    int one=1;    const int *val=&one;    if(setsockopt(fd,IPPROTO_IP,IP_HDRINCL,val,sizeof(int))<0)    {        perror("Error setting IP_HDRINCL\n");        exit(0);    }    //send SYN    if(sendto(fd,datagram,iph->tot_len,0,(struct sockaddr*)&sin,sizeof(sin))<0)    {        perror("SYN send to failed\n");    }    else    {        printf("SYN Packet seq=%u \n",1);    }    //recv SYN+ACK    struct sockaddr_in saddr;    unsigned char buffer[4096];    unsigned int ack_seq,seq,data_size,saddr_len;    while(1)    {        data_size=recvfrom(fd,buffer,4096,0,(struct sockaddr*)&saddr,(socklen_t*)&saddr_len);        //printf("%d %s\n",saddr_len,inet_ntoa(saddr.sin_addr));        if(data_size<0)            continue;        //为什么saddr返回的端口号为0        if(/*saddr.sin_port!=sin.sin_port             ||*/saddr.sin_addr.s_addr!=sin.sin_addr.s_addr)            continue;        ack_seq=get_ack_seq(buffer,data_size,seq);        if(ack_seq==-1||ack_seq!=2)//syn seq=1            continue;        else        {            printf("SYN+ACK Packet Get seq=%u ack=%u port=%u\n",seq,ack_seq,ntohs(saddr.sin_port));            break;        }    }    //send ACK    fill_packet(datagram,iph->tot_len,buffer,data_size,seq+1);    if(sendto(fd,datagram,iph->tot_len,0,(struct sockaddr*)&sin,sizeof(sin))<0)    {        perror("ACK send to failed\n");    }    else    {        printf("ACK Packet ack=%u \n",seq+1);    }}int get_ack_seq(unsigned char* buffer,int size,unsigned int &seq){    struct iphdr *iph=(struct iphdr*)buffer;    struct tcphdr *tcph=(struct tcphdr*)(buffer+iph->ihl*4);    if(iph->protocol!=6)        return -1;    if(tcph->syn!=1||tcph->ack!=1)        return -1;    // printf("sp=%d\n",ntohs(tcph->source));    seq=ntohl(tcph->seq);    return ntohl(tcph->ack_seq);}int fill_packet(char *datagram,int dsize,unsigned char* buffer,int bsize,unsigned int ack_seq){    struct iphdr *iph=(struct iphdr*)datagram;    struct tcphdr *tcph=(struct tcphdr*)(datagram+iph->ihl*4);    struct iphdr *b_iph=(struct iphdr*)buffer;    struct tcphdr *b_tcph=(struct tcphdr*)(buffer+b_iph->ihl*4);    char *pseudogram;    struct pseudo_header psh;    //IP Header    iph->id=htonl(54322);//ID    iph->check=0;    iph->saddr=b_iph->daddr;    iph->daddr=b_iph->saddr;    //IP checksum    iph->check=csum((unsigned short*)datagram,sizeof(struct iphdr));    //TCP Header    tcph->source = b_tcph->dest;    tcph->dest = b_tcph->source;    tcph->seq = htonl(2);    tcph->ack_seq = htonl(ack_seq);    tcph->syn=0;    tcph->ack=1;    tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header    //Now the TCP checksum    psh.source_address = b_tcph->dest;    psh.dest_address = b_tcph->source;    psh.placeholder = 0;    psh.protocol = IPPROTO_TCP;    psh.tcp_length = htons(sizeof(struct tcphdr));    int psize=sizeof(struct pseudo_header)+sizeof(struct tcphdr);    pseudogram=(char*)malloc(psize);    memcpy(pseudogram,(char*)&psh,sizeof(struct pseudo_header));    memcpy(pseudogram+sizeof(struct pseudo_header),tcph,sizeof(struct tcphdr));    tcph->check=csum((unsigned short*)pseudogram,psize);}int main(){    connect();}

服务端测试代码:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <arpa/inet.h>#include <errno.h>#include <unistd.h>#define PORT 8888int main(){    int fd = socket(PF_INET,SOCK_STREAM,0);    if(fd<0)    {        perror("create socket error");        exit(0);    }    struct sockaddr_in sin;    sin.sin_family=AF_INET;    sin.sin_port=htons(PORT);    sin.sin_addr.s_addr=INADDR_ANY;    if(bind(fd,(struct sockaddr*)&sin,sizeof(struct sockaddr))<0)    {        perror("socket bind error");        exit(0);    }    if(listen(fd,100)<0)    {        perror("socket listen error");        exit(0);    }    printf("start listening\n");    int clifd;    struct sockaddr_in client;    socklen_t len;    while(1)    {        if((clifd=accept(fd,(struct sockaddr*)&client,&len))<0)        {            perror("accept error");        }        else        {            printf("accept success\n");        }    }}

运行结果:
这里写图片描述
wireshark抓包如下:
这里写图片描述
结果意外的多出了一个从9999端口到8888端口的RST包,导致TCP握手提前被终止。

百思不得其解,google查资料咯。大概意思是tcp协议栈也会处理三次握手,当内核收到SYN+ACK时并不知道raw socket发送了SYN包,因此响应RST终止连接。
这里写图片描述

目前没有找到解决方案,下面是有关该问题的一些参考资料:
http://bbs.csdn.net/topics/320208382
http://blog.chinaunix.net/uid-795807-id-3206853.html
http://forums.codeguru.com/showthread.php?320739-Visual-C-Network-Why-do-my-machine-send-an-RST-packet-in-reply-to-a-SYN-ACK-pac

0 0
原创粉丝点击