简单实现SYN端口扫描

0x00

最近也在学习Linux开发,尤其是网络编程这块。今天就share下前几天写的简易SYN端口扫描的代码,与其说简易,不如说是简陋,不过确实收获不少。

我这里就不介绍全面端口扫描,SYN扫描不同于全连接的扫描,由于这种扫描不需要建立连接,所以扫描的速度会比全连接的扫描速度快很多,附一张图说明:


TCP建立连接的时候需要进行三次握手,总的来说就是SYN--------->SYN+ACK------------>ACK,只要进行了三次握手,那么连接双方就会为这次连接分配相应的资源,如缓冲区等。而SYN扫描明显不是建立全连接,当第二次消息为SYN+ACK,就代表服务端可以进行连接,或者说端口开放着,这次发送一个RST将连接断开即可,因为我们已经获知端口开放的情况。这样进行扫描,速度快是自然的。


0x01

实现的思路就是自己构造相应flag的TCP报文进行发送,如果收到SYN+ACK说明这个端口开放,这时候要再发送一个RST断开连接来避免消耗被扫主机的资源。如果是RST+ACK,说明端口没有开发,继续扫描其它端口。

实现中我也查阅了不少的资料,其中实现的最重要的基础就是基于原始套接字进行发包,就是要自己构造相应的IP或者TCP报文,然后发送,因为使用系统的bind connect 那一套是不能操作TCP首部字段的。关于原始套接字的编程基础我也不打算介绍了,因为网上实在很多优秀的博客。这里就直接分享代码了。

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<errno.h>
#include <arpa/inet.h>
#include <linux/tcp.h>
#include<pthread.h>
#include<fcntl.h>
/*
 * Author:Exploit
 * 这是一个SYN极速扫描的demo
 * 存在的问题:发包的速度要控制    不然丢包很严重
 * 但是在60个端口的范围内有效
 *
 * */


//定义TCP伪报头
typedef struct psd_hdr
{
	unsigned long saddr; //源地址
	unsigned long daddr; //目的地址
	char mbz; char ptcl; //协议类型
	unsigned short tcpl; //TCP长度

}PSD_HEADER;

//定义TCP报头
typedef struct _tcphdr
{
	unsigned short th_sport; //16位源端口
	unsigned short th_dport; //16位目的端口
	unsigned int th_seq; //32位序列号
	unsigned int th_ack; //32位确认号
	unsigned char th_lenres; //4位首部长度/4位保留字
	unsigned char th_flag; //6位标志位
	unsigned short th_win; //16位窗口大小
	unsigned short th_sum; //16位校验和
	unsigned short th_urp; //16位紧急数据偏移量

} TCP_HEADER;


//定义IP报头
typedef struct _iphdr
{
	unsigned char h_lenver ; //长度加版本号
	unsigned char tos;
	unsigned short total_len;
	unsigned short ident;
	unsigned short frag_and_flags;
	unsigned char ttl;
	unsigned char proto;
	unsigned short checksum;
	unsigned int sourceIP;
	unsigned int destIP;

} IP_HEADER;

/**
 * 计算校验和
 */
unsigned short checksum(unsigned short *addr,int len){
		int nleft=len;
	   int sum=0;
	   unsigned short * w=addr;
	   unsigned short answer=0;

	   while (nleft>1)
	   {
	     sum+=*w++;
	     nleft-=2;
	   }

	   if (nleft==1)
	   {
	     *(unsigned char *)(&answer)=*(unsigned char *)w;
	     sum+=answer;
	   }

	   sum=(sum>>16)+(sum & 0xffff);
	   sum+=(sum>>16);
	   answer=~sum;
	   return(answer);
}

/* 攻击目标*/
struct sockaddr_in target;
struct sockaddr_in myaddr;
int sockfd ;
pthread_t pth ;

void TCP_Send(int port,unsigned char flag);
void* recvpackage(void*arg) ;

int main(int args,char* argv[]){

	//参数检查
	if(args < 4){
		printf("Usage:shit targetIP startPort endPort\n") ;
		exit(-1) ;
	}

	char IP[32] ; //目标IP
	strcpy(IP,argv[1]) ;
    int startPort = atoi(argv[2]) ;
	int endPort = atoi(argv[3]) ;

	if((endPort - startPort) > 60){
		printf("The port range must be within 60 considering your bandwith....\n") ;
		exit(-1) ;
	}

	target.sin_family = AF_INET ;
	target.sin_addr.s_addr = inet_addr(IP) ;

	myaddr.sin_family = AF_INET ;
	myaddr.sin_port = htons(60000) ;
	myaddr.sin_addr.s_addr = inet_addr("10.10.10.132") ;

	//TCP报文的socket
	sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_TCP) ;

	if(sockfd == -1){
		printf("socket error:%s\n",strerror(errno)) ;
		exit(-1) ;
	}

	int i ,count=1;
	for(i=startPort;i<endPort;i++){
		TCP_Send(i,2);
	}
	pthread_create(&pth,NULL,recvpackage,NULL) ;
	pthread_join(pth,NULL) ;
	close(sockfd) ;
	return 0 ;
}


void TCP_Send(int port,unsigned char flag){

		//设置目标端口
		target.sin_port = htons(port) ;

		//构造包
		char buffer[256] ;
		memset(buffer,0,256) ;
		struct _tcphdr tcpHeader ;
		struct psd_hdr psdHeader ;

		//填充TCP
		//目的端口
		tcpHeader.th_dport = htons(port) ;
		//源端口
		tcpHeader.th_sport = htons(60000) ;
		//序列号??
		tcpHeader.th_seq = htonl(0x1245678);
		//确认号
		tcpHeader.th_ack = 0;
		//(4位首部长度/4位保留字)
		tcpHeader.th_lenres = (sizeof(tcpHeader) / 4 << 4 | 0);
		//SYN标志
		tcpHeader.th_flag = flag ;//SYN
		//滑动窗口
		tcpHeader.th_win = htons(16384) ;
		//16位紧急数据偏移量
		tcpHeader.th_urp = 0;
		//16位校验和
		tcpHeader.th_sum = 0;

		//psdheader
		psdHeader.saddr = myaddr.sin_addr.s_addr;
		psdHeader.daddr = target.sin_addr.s_addr;
		psdHeader.mbz = 0;  // mbz = must be zero, 用于填充对齐
		psdHeader.ptcl = IPPROTO_TCP ;  //8位协议号
		psdHeader.tcpl = htons(sizeof(tcpHeader)) ;

		//set checksum 使用伪头计算TCP校验和
		memcpy(buffer,&psdHeader,sizeof(psdHeader)) ;
		memcpy(buffer+sizeof(psdHeader),&tcpHeader,sizeof(tcpHeader)) ;
		tcpHeader.th_sum = checksum((unsigned short*)buffer,sizeof(psdHeader)+sizeof(tcpHeader)) ;

		//最终的组包(TCP+IP)
		memcpy(buffer,&tcpHeader,sizeof(tcpHeader)) ;

		//发送的过程   由于IP协议是无连接的协议   所以可以使用sendto
		int ret = sendto(sockfd,buffer,sizeof(tcpHeader),0,(struct sockaddr*)&target,sizeof(target)) ;
		if(ret == -1){
			printf("send error!:%s\n",strerror(errno)) ;
			exit(-1);
		}else{
			//printf("send OK\n") ;
		}

}

/*
 * 线程的回调函数
 * */

void* recvpackage(void*arg){
	//接收的过程recvfrom
	printf("Thread starting...\n") ;
	struct _tcphdr* testtcp ;
	char msg[1024] ;
	int len = sizeof(myaddr);
	int count ,size;
	while(1){
		memset(msg,0,1024) ;
		size = recvfrom(sockfd,msg,sizeof(msg),0,(struct sockaddr*)&myaddr,&len) ;
		if (size == -1) break ;
		//这里的指针是指向IP头部第一个字段的   所以得到TCP头部时要加上相应的偏移量20byte
		testtcp = (struct _tcphdr*)(msg + sizeof(struct _iphdr)) ;
		if (size < (20 + 20)){/*读出的数据小于两个头的最小长度的话continue*/
			continue;
		}
		if(ntohs(testtcp->th_dport) != 60000){
			continue ;
		}

		if(testtcp->th_flag == 20){
			printf("%d 端口未开放\n",ntohs(testtcp->th_sport)) ;
			continue ;
		}
		if(testtcp->th_flag == 18){
			TCP_Send(ntohs(testtcp->th_sport),4) ;
			printf("%d 端口开放!ACK + SYN....\n",ntohs(testtcp->th_sport)) ;
			continue ;
		}
	}

}

这里有个很大的缺陷就是会产生很多丢包现象(1M网络,使用wireshark实验)。按说TCP应该会自动控制发包的速度???如果你知道如何更好的控制并能达到最好的效果,请留言。


0x02

测试下效果:



利用原始套接字可以做很多事情,比如实现ping隧道窃取信息,再比如实现大规模的漏扫,确实应该好好深究下~

如果有不对的地方,还望留言指正。


©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页