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
#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);//杀掉接收线程
}