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

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

(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_FIN	0x01
#  define TH_SYN	0x02
#  define TH_RST	0x04
#  define TH_PUSH	0x08
#  define TH_ACK	0x10
#  define TH_URG	0x20
    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  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);//杀掉接收线程

}



你可能感兴趣的:(网络作业)