linux下端口扫描的实现(TCP connect、TCP SYN、TCP FIN、UDP四种方式)4 TCP SYN方式

来源:互联网 发布:淘宝网页不显示图片 编辑:程序博客网 时间:2024/05/29 14:27

1.原理

众所周知,当调用connect()时要经历三次握手的过程SYN-SYN&ACK-ACK,而这种SYN扫描方式又叫半开放式扫描,即客户端只发送SYN帧,端口开放回应SYN&ACK帧,端口关闭回应RST帧,缺点就是不可靠,可能会丢包。

2.实现方式及遇到的问题

前面架构篇有讲过,每个扫描线程(tcpSynScanPort)会建立一个线程池,对每个要扫描的端口都创建一个线程tcpSynScanEach(线程配置成detach属性)。tcpSynScanEach负责发送SYN帧到各端口,另有一个线程tcpSynScanRecv负责处理所有收到的数据包。发送的数据包要自己组装,协议可以使用原始套接字,协议选择TCP。

(1)参数传递

        也是主线程里malloc一块,从线程sendto之后就free,只不过不能只像connect一样,传递目的ip和端口,因为攒包的时候要填写本机ip与端口,所以自己定义一个数据结构。

(2)字节序

        网络字节序与主机字节序的转换。

(3)线程安全

        同上一篇

(4)校验和

在填写首部字段时免不了要写校验和字段,需要小心校验的范围,这里做一下总结。

IP校验和只校验20字节的IP报头;

ICMP校验和覆盖整个报文(ICMP报头+ICMP数据);

UDP和TCP校验和不仅覆盖整个报文,而且还有12字节的IP伪首部,包括源IP地址(4字节)、目的IP地址(4字节)、协议(2字节,第一字节补0)和TCP/UDP包长(2字节)。另外UDP、TCP数据报的长度可以为奇数字节,所以在计算校验和时需要在最后增加填充字节0(注意,填充字节只是为了计算校验和,可以不被传送)。

(5)对丢包的处理

        只发SYN和接收ACK或RST帧其实是在ip层进行的,所以丢包的可能性很大。在这种扫描模式下,无论对方端口是打开还是关闭,都会有应答,所以我用一个全局数组记录每个端口的状态,0表示没有应答,1表示ack,2表示收到rst

(6)弱智错误

对于a<x<b的判断,要写if( (a<x) && (x<b) )好嘛,不能直接a<x<b好嘛。。

(7)各线程间的同步

main函数调用端口扫描主线程,main函数pthread_join等待扫描过程结束。

扫描主线程调用各个端口扫描线程,各端口扫描线程发送之后就自动结束了,扫描主线程不用等待。

扫描主线程调用接收线程,通过判断全局变量synCnt来确定是否扫描结束,归零后由扫描主线程kill掉接收线程,用pthread_cancel函数。

(8)tcp帧头

    

struct tcphdr  {    u_int16_t th_sport;/* source port */    u_int16_t th_dport;/* destination port */    tcp_seq th_seq;/* sequence number */    tcp_seq th_ack;/* acknowledgement number */#  if __BYTE_ORDER == __LITTLE_ENDIAN    u_int8_t th_x2:4;/* (unused) */    u_int8_t th_off:4;/* data offset */#  endif#  if __BYTE_ORDER == __BIG_ENDIAN    u_int8_t th_off:4;/* data offset */    u_int8_t th_x2:4;/* (unused) */#  endif    u_int8_t th_flags;#  define TH_FIN0x01#  define TH_SYN0x02#  define TH_RST0x04#  define TH_PUSH0x08#  define TH_ACK0x10#  define TH_URG0x20    u_int16_t th_win;/* window */    u_int16_t th_sum;/* checksum */    u_int16_t th_urp;/* urgent pointer */};# else /* !__FAVOR_BSD */struct tcphdr  {    u_int16_t source;    u_int16_t dest;    u_int32_t seq;    u_int32_t ack_seq;#  if __BYTE_ORDER == __LITTLE_ENDIAN    u_int16_t res1:4;    u_int16_t doff:4;    u_int16_t fin:1;    u_int16_t syn:1;    u_int16_t rst:1;    u_int16_t psh:1;    u_int16_t ack:1;    u_int16_t urg:1;    u_int16_t res2:2;#  elif __BYTE_ORDER == __BIG_ENDIAN    u_int16_t doff:4;    u_int16_t res1:4;    u_int16_t res2:2;    u_int16_t urg:1;    u_int16_t ack:1;    u_int16_t psh:1;    u_int16_t rst:1;    u_int16_t syn:1;    u_int16_t fin:1;#  else#   error "Adjust your <bits/endian.h> defines"#  endif    u_int16_t window;    u_int16_t check;    u_int16_t urg_ptr;};# endif /* __FAVOR_BSD */
我用的平台是linuxmint 13 mate linux内核版本3.2.0属于非FAVOR_BSD

3.实现代码

头文件

#ifndef TCPSYNSCAN_H_H#define TCPSYNSCAN_H_H#include "mysock.h"int synCnt;static pthread_mutex_t syn_printf_mutex = PTHREAD_MUTEX_INITIALIZER;static pthread_mutex_t syn_num_mutex = PTHREAD_MUTEX_INITIALIZER;//extern pthread_mutex_t syn_printf_mutex; //extern pthread_mutex_t syn_num_mutex;void* tcpSynScanPort(void *arg);void* tcpSynScanEach(void *arg);void* tcpSynScanRecv(void *arg);#endif

c文件

#include "tcpSynScan.h"//防火墙不关的话只有open的返回,close的不回应!//u8 flag_port[65535];//u8 flag_err;void* tcpSynScanEach(void *arg){int synfd;uint ip_len, tcp_len, pseu_len, len;struct ScanParam *ss = (struct ScanParam*)arg;u8 sendBuf[200];struct sockaddr_in destAddr;struct PseudoHdr *pPseuH;struct tcphdr *pTcp;pPseuH = (struct PseudoHdr*)sendBuf;pTcp = (struct tcphdr*)(sendBuf + sizeof(struct PseudoHdr));//memset(&destAddr, 0, sizeof(destAddr));destAddr.sin_family = AF_INET;inet_pton(AF_INET, ss->destIP, &destAddr.sin_addr);destAddr.sin_port = htons(ss->destPort);synfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);//原始套接字 tcp协议,这样 接收的都是tcp包if(synfd < 0){pthread_mutex_lock(&syn_printf_mutex);perror("syn socket");pthread_mutex_unlock(&syn_printf_mutex);}inet_pton(AF_INET, ss->destIP, &pPseuH->dIP);inet_pton(AF_INET, ss->sourIP, &pPseuH->sIP);pPseuH->protocol = IPPROTO_TCP;pPseuH->useless = 0;//pPseuH->length = htons(sizeof(struct tcphdr));//!!!pPseuH->length = htons(40);//!!!//memset(pTcp, 0, sizeof(struct tcphdr));memset(pTcp, 0, 60-20);pTcp->source = htons(ss->sourPort);pTcp->dest = htons(ss->destPort);pTcp->seq = htonl(123456+ss->destPort);//??pTcp->ack_seq = 0;pTcp->doff = 10;//!!!长度 windows下不用设置啊,linux不设置不搭理我啊~//pTcp->doff = 0;//!!!长度 windows下不用设置啊,linux不设置不搭理我啊~pTcp->syn = 1;pTcp->ack = 0;pTcp->window = htons(65535);//!!!pTcp->check = 0;//pTcp->check = checksum((u8*)sendBuf, sizeof(struct PseudoHdr)+sizeof(struct tcphdr));pTcp->check = checksum((u8*)sendBuf, sizeof(struct PseudoHdr)+40);len = sendto(synfd, (void *)pTcp, 40, 0, (struct sockaddr*)&destAddr, sizeof(destAddr));if(len <= 0){pthread_mutex_lock(&syn_printf_mutex);perror("sendto");pthread_mutex_unlock(&syn_printf_mutex);}free(ss);close(synfd);//!!!}void* tcpSynScanRecv(void *arg){u8 recvBuf[MAXLINE];char recvIP[INET_ADDRSTRLEN];int len;struct tcphdr *pTcp;struct ip *pIp;struct ScanSock *ss = (struct ScanSock*)arg;int synfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);int num_port = ss->portEnd - ss->portStart + 1;u16 port_now;//u8 flag_port[65535];//memset(flag_port, 0, 65535);while(1)//线程自己一直处在不断接收的状态,知道别的线程杀掉它{memset(recvBuf, 0, MAXLINE);len = recvfrom(synfd, recvBuf, MAXLINE, 0, NULL, NULL);if(len <= 0){pthread_mutex_lock(&syn_printf_mutex);perror("recvfrom\n");pthread_mutex_unlock(&syn_printf_mutex);}else{int i;if(len >= sizeof(struct iphdr) + sizeof(struct tcphdr)){pTcp = (struct tcphdr*)(recvBuf + sizeof(struct ip));port_now = ntohs(pTcp->source);if( (ss->portStart <= port_now) && (port_now <= ss->portEnd) )//!!{pthread_mutex_lock(&syn_printf_mutex);printf("port now = %d\t", port_now);if(flag_err == 1){for(i = 0 ; i < len ; i++){printf("%02x ", recvBuf[i]);if( (i+1)%12 == 0)printf("\n");}printf("\n");}pthread_mutex_unlock(&syn_printf_mutex);if( (pTcp->syn == 1)&&( ntohl(pTcp->ack_seq) == 123456+port_now+1) )//收到ack{pthread_mutex_lock(&syn_printf_mutex);printf("open\n");pthread_mutex_unlock(&syn_printf_mutex);if(flag_port[port_now] == 0)//防止重复记录,所以只对没收到过ack的记录{struct Queue *p = malloc(sizeof(struct Queue));p->data = port_now;p->next = NULL;flag_port[port_now] = 1;pthread_mutex_lock(&syn_num_mutex);synCnt--;p->next = existPort ;existPort = p;pthread_mutex_unlock(&syn_num_mutex);}}else if( pTcp->syn == 0)收到rst{pthread_mutex_lock(&syn_printf_mutex);printf("close\n");pthread_mutex_unlock(&syn_printf_mutex);pthread_mutex_lock(&syn_num_mutex);synCnt--;flag_port[port_now] = 2;pthread_mutex_unlock(&syn_num_mutex);}else{pthread_mutex_lock(&syn_printf_mutex);printf("!!!3\n");pthread_mutex_unlock(&syn_printf_mutex);pthread_mutex_lock(&syn_printf_mutex);for(i = 0 ; i < len ; i++){printf("%02x ", recvBuf[i]);if( (i+1)%12 == 0)printf("\n");}printf("\n");pthread_mutex_unlock(&syn_printf_mutex);}}}}}}void* tcpSynScanPort(void *arg){int cnt_delay = 0; flag_err = 0;struct ScanSock *ss = (struct ScanSock*)arg;int i;struct ScanParam *scanAddr;pthread_t pidTh;int err;pthread_attr_t attr;/*pthread_attr_init(&attr);err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);if(err != 0){printf("pthread_attr_setdetachstate:%s\n", strerror(err));exit(1);}*/int recv_th;recv_th = pthread_create(&pidTh, NULL, tcpSynScanRecv, arg);if(recv_th != 0){printf("pthread_create:%s\n", strerror(recv_th));exit(1);}//pthread_attr_destroy(&attr);memset(flag_port, 0, 65535);synCnt = 0;resend:for(i = ss->portStart ; i <= ss->portEnd ; i++){if(flag_port[i] == 0)//只对还没收到应答的包发送SYN帧{scanAddr = malloc(sizeof(struct ScanSock));strncpy(scanAddr->destIP, ss->destIP, INET_ADDRSTRLEN);strncpy(scanAddr->sourIP, ss->sourIP, INET_ADDRSTRLEN);scanAddr->destPort = i;//!!!scanAddr->sourPort = 1024+i;pthread_attr_init(&attr);err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);if(err != 0){printf("pthread_attr_setdetachstate:%s\n", strerror(err));exit(1);}err = pthread_create(&pidTh, &attr, tcpSynScanEach, (void*)scanAddr);if(err != 0){printf("pthread_create:%s\n", strerror(err));exit(1);}pthread_attr_destroy(&attr);if(flag_err == 0){pthread_mutex_lock(&syn_num_mutex);synCnt++;flag_port[i] = 0;pthread_mutex_unlock(&syn_num_mutex);}while(synCnt > 100)sleep(3);}}while(synCnt > 0)//!!!{sleep(3);cnt_delay++;if(cnt_delay == 10)//很久没归零,则判断丢包的可能性较大{cnt_delay == 0;flag_err = 1;goto resend;//回到发送那一步,对丢包的再次发送SYN包}pthread_mutex_lock(&syn_printf_mutex);printf("\tsynCnt = %d\n", synCnt);printf("---now exist port:\n");struct Queue *tempQ = existPort;while(tempQ != NULL){printf("%d\t",tempQ->data);tempQ = tempQ->next;}printf("\n");pthread_mutex_unlock(&syn_printf_mutex);}//出循环说明已对每个端口状态做了判断pthread_cancel(recv_th);//杀掉接收线程}



0 0