今天花了一天时间,研究了一下如何利用原始套接字模拟TCP的三次握手。因为前几天一直在关于linux下c语言的socket编程,也看的差不多了,今天就敲了一天的代码。但是遇到了一个问题,当我利用raw socket发送SYN报文请求连接后,也能收到对方发来的SYN+ACK报文,当我发送ACK之前,本机系统会自动发送一个RST报文。然后,我在网上搜了一下,也有人遇到我这样的情况,有人给出如下解决方法:
一、反编译内核,提取connect的信息,进行分析,看怎么记录连接信息的,自己加连接信息,我晕,不太好办。
二、做个IP报文拦截器,直接过滤掉rst报文。NND太暴了:),咱是文化人,不能用这招。
三、不让TCP层收到ACK响应,那么我们就可以拦截这个报文并且把它过滤掉,这种方案也是可行的。
四、用winpcap拦截过滤,有兴趣去官方网站下载,比较简单。
也不知道这些法子到底可不可以,这两天再看看查阅别的资料试试,或者问问老师看看。
下面贴出我的实验代码供大家分享:
#include
#include
#include
#include
#include
#include
#include
#include
#define DST "202.196.64.206" //目的IP
#define SRC "192.168.230.135" //源IP
#define SPORT 6666 //源端口号
#define DPORT 80 //目的端口
struct iphead{ //IP首部
unsigned char ip_hl:4, ip_version:4;
unsigned char ip_tos;
unsigned short int ip_len;
unsigned short int ip_id;
unsigned short int ip_off;
unsigned char ip_ttl;
unsigned char ip_pro;
unsigned short int ip_sum;
unsigned int ip_src;
unsigned int ip_dst;
};
struct tcphead{ //TCP首部
unsigned short tcp_sport;
unsigned short tcp_dport;
unsigned int tcp_seq;
unsigned int tcp_ack;
unsigned char tcp_off:4, tcp_len:4;
unsigned char tcp_flag;
unsigned short tcp_win;
unsigned short tcp_sum;
unsigned short tcp_urp;
};
struct psdhead{ //TCP伪首部
unsigned int saddr; //源地址
unsigned int daddr; //目的地址
unsigned char mbz;//置空
unsigned char ptcl; //协议类型
unsigned short tcpl; //TCP长度
};
unsigned short cksum(unsigned char packet[], int len){ //校验函数
unsigned long sum = 0;
unsigned short * temp;
unsigned short answer;
temp = (unsigned short *)packet;
for( ; temp < packet+len; temp += 1)
sum += *temp;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
/*
长度可能奇数,此处需完善
*/
}
int conn(int sendsockfd, int recsockfd, struct sockaddr_in seraddr){ // 三次握手
unsigned char packet[sizeof(struct iphead) + sizeof(struct tcphead)];
struct iphead* ip;
struct tcphead* tcp;
ip = (struct iphead*)packet;
tcp = (struct tcphead*)(packet+sizeof(struct iphead));
memset(packet, 0, sizeof(packet));
/*以下分别设置IP,和TCP的首部,然后发送SYN报文段*/
/*设置IP首部*/
ip->ip_hl = 5;
ip->ip_version = 4;
ip->ip_tos = 0;
ip->ip_len = htons(sizeof(struct iphead) + sizeof(struct tcphead));
ip->ip_id = htons(13542); // random()
ip->ip_off = htons(0x4000);
ip->ip_ttl = 64;
ip->ip_pro = IPPROTO_TCP;
ip->ip_src = inet_addr(SRC);
ip->ip_dst = inet_addr(DST);
ip->ip_sum = cksum(packet, 20); //计算IP首部的校验和,必须在其他字段都赋值后再赋值该字段,赋值前为0
/*设置TCP首部*/
int my_seq = 0; //TCP序号
tcp->tcp_sport = htons(SPORT);
tcp->tcp_dport = htons(DPORT);
tcp->tcp_seq = htonl(my_seq);
tcp->tcp_ack = htons(0);
tcp->tcp_len = 5; //发送SYN报文段时,设置TCP首部长度为20字节
tcp->tcp_off = 0;
tcp->tcp_flag = 0x02; //SYN置位
tcp->tcp_win = htons(29200);
tcp->tcp_urp = htons(0);
/*设置tcp伪首部,用于计算TCP报文段校验和*/
struct psdhead psd;
psd.saddr = inet_addr(SRC); //源IP地址
psd.daddr = inet_addr(DST); //目的IP地址
psd.mbz = 0;
psd.ptcl = 6;
psd.tcpl = htons(20);
unsigned char buffer[1000]; //用于存储TCP伪首部和TCP报文,计算校验码
memcpy(buffer, &psd, sizeof(psd));
memcpy(buffer+sizeof(psd), tcp, sizeof(struct tcphead));
tcp->tcp_sum = cksum(buffer, sizeof(psd) + sizeof(struct tcphead)); //计算检验码
/*发送SYN报文段*/
int send = sendto(sendsockfd, packet, htons(ip->ip_len), 0,(struct sockaddr *)&seraddr, sizeof(seraddr));
if(send < 0){
printf("send failed sendcode=%d\n", send);
return -1;
}
unsigned char rec[1024];
int n = recvfrom(recsockfd, rec, 1024, 0, NULL, NULL); //接收SYN和ACK报文
printf("receive %d bytes:\n", n); //将接受的IP数据报输出
for(int i=0; i> 4; //因为TCP报文长度只占该字节的高四位,需要取出该四位的值
tcpheadlen *= 4; //以四个字节为单位
printf("tcpheadlen:%d\n", tcpheadlen);
buffer[11] = tcpheadlen; //将TCP长度存入
for(int i=0; iip_hl = 5;
ip->ip_version = 4;
ip->ip_tos = 0;
ip->ip_len = htons(sizeof(struct iphead) + sizeof(struct tcphead));
ip->ip_id = htons(13543); // random()
ip->ip_off = htons(0x4000);
ip->ip_ttl = 64;
ip->ip_pro = IPPROTO_TCP;
ip->ip_src = inet_addr(SRC);
ip->ip_dst = inet_addr(DST);
ip->ip_sum = cksum(packet, 20); //计算IP首部的校验和,必须在其他字段都赋值后再赋值该字段,赋值前为0
/*设置TCP首部*/
my_seq ++;
tcp->tcp_sport = htons(SPORT);
tcp->tcp_dport = htons(DPORT);
tcp->tcp_seq = htonl(my_seq);
printf("op_seq:%d\n", op_seq);
tcp->tcp_ack = ntohl(op_seq+1);
tcp->tcp_len = 5; //发送SYN报文段时,设置TCP首部长度为20字节
tcp->tcp_off = 0;
tcp->tcp_flag = 0x10; //SYN置位
tcp->tcp_win = htons(1000);
tcp->tcp_urp = htons(0);
/*设置tcp伪首部,用于计算TCP报文段校验和*/
// struct psdhead psd;
psd.saddr = inet_addr(SRC); //源IP地址
psd.daddr = inet_addr(DST); //目的IP地址
psd.mbz = 0;
psd.ptcl = 6;
psd.tcpl = htons(20);
unsigned char buffer[1000]; //用于存储TCP伪首部和TCP报文,计算校验码
memcpy(buffer, &psd, sizeof(psd));
memcpy(buffer+sizeof(psd), tcp, sizeof(struct tcphead));
tcp->tcp_sum = cksum(buffer, sizeof(psd) + sizeof(struct tcphead)); //计算检验码
/*发送SYN报文段*/
int send = sendto(sendsockfd, packet, htons(ip->ip_len), 0,(struct sockaddr *)&seraddr, sizeof(seraddr));
if(send < 0){
printf("send failed sendcode=%d\n", send);
return -1;
}
printf("已发送ACK报文,已创建TCP连接\n");
n = recvfrom(recsockfd, rec, 1024, 0, NULL, NULL); //接收IP数据报
printf("receive %d bytes:\n", n); //将接受的IP数据报输出
for(int i=0; i