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隧道窃取信息,再比如实现大规模的漏扫,确实应该好好深究下~
如果有不对的地方,还望留言指正。