linux下端口掃描的實現(TCP connect、TCP SYN、TCP FIN、UDP四種方式)2整體架構篇

一.概述

該程序在linux下用c語言實現TCP的三種端口掃描方式(connect、SYN、FIN)和UDP端口掃描;我用一個函數數組存放四個實現函數,根據選擇的端口掃描方式,在創建線程的時候選擇不同的函數來執行,主線程掛起等待掃描線程結束,最後打印開放的端口。我用一個全局的隊列來存放存在的指針,鏈表實現。除了connect方式,其它的方式需要知道源主機的ip,因此在創建掃描線程之前,先獲得本機的ip地址,作爲參數傳給掃描線程。

每個掃描線程(tcpXXXScanPort)會建立一個線程池,對每個要掃描的端口都創建一個線程tcpXXXScanEach(線程配置成detach屬性),具體的實現每種方式有些差別,後幾篇會講到。

二.實現過程

1.數據結構

    由於調用線程只能傳遞一個參數,所以我把要傳遞的信息放在一個數據結構裏面,用指針傳給子線程。以下是自己定義的要傳遞的數據結構和全局變量。

#include "mysock.h"
#include "ping.h"
#include "tcpConScan.h"
#include "tcpSynScan.h"
#include "tcpFinScan.h"
#include "udpIcmpScan.h"
int main(int argc, char *argv[])
{
	char destIPStr[] = "192.168.1.21";
	char port1Str[] = "4000";//掃描的初始端口
	char port2Str[] = "4010";//掃描的結束端口
	int cmd;
	struct ScanSock s;
	cmd = 4;//掃描方式
	s.portStart = atoi(port1Str);
	s.portEnd = atoi(port2Str);
	strncpy(s.destIP, destIPStr, sizeof(destIPStr));
	if(ping(s.destIP) == -1)//在掃描端口前先確定主機是否可達
	{
		printf("destination host does not exist!\n");
		return 0;
	}
	getMyIp(s.sourIP);//獲取本機ip
	printf("my IP is %s\n", s.sourIP);
	pthread_t pidTh;
	int err;
	void* (*scanFunc[4])(void *arg);//函數數組
	scanFunc[0] = tcpConScanPort;
	scanFunc[1] = tcpSynScanPort;
	scanFunc[2] = tcpFinScanPort;
	scanFunc[3] = udpIcmpScanPort;
	err = pthread_create(&pidTh, NULL, scanFunc[cmd-1], (void*)&s);
	if(err != 0)
	{
		printf("pthread_create:%s\n", strerror(err));
		exit(1);
	}
	err = pthread_join(pidTh, NULL);//掛起等待掃面線程結束
	if(err != 0)
	{
		printf("pthread_join:%s\n", strerror(err));
		exit(1);
	}
	printf("---exist port:\n");//打印開放的端口
	struct Queue *tempQ;
	while(existPort != NULL)
	{
		printf("%d\n",existPort->data);
		tempQ = existPort;
		existPort = existPort->next;
		free(tempQ);
	}
	printf(" scan finish\n");
}


(2)ping函數與getIP實現

    ping原理可見我前一篇

#include "ping.h"


int ping(char *strIp)
{
	u8 sendBuf[MAXLINE];
	u8 recvBuf[MAXLINE];
	struct sockaddr_in destAddr;
	int sockfd;
	if( (sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	{
		perror("socket");
		exit(1);
	}
	memset(&destAddr, 0, sizeof(destAddr));
	destAddr.sin_family = AF_INET;
	if( inet_pton( AF_INET, strIp, &(destAddr.sin_addr)) != 1)
	{
		perror("inet_pton:");
		exit(1);
	}

	struct sigaction newact, oldact;
	newact.sa_handler = alarm_timer;
	sigemptyset(&newact.sa_mask);
	newact.sa_flags = 0;
	sigaction(SIGALRM, &newact, &oldact);
	alarm(30);

	struct icmp *pIcmp;
	struct ip *pIp;
	int seq = 0;
	struct timeval *tvStart, *tvEnd;
	pid_t pid = getpid();
	pingFlag = 0;
	while(pingFlag == 0)
	{
		memset(sendBuf, 0, MAXLINE);
		pIcmp = (struct icmp*)sendBuf;
		pIcmp->icmp_type = ICMP_ECHO;
		pIcmp->icmp_code = 0;
		pIcmp->icmp_cksum = 0;
		pIcmp->icmp_id = pid;
		pIcmp->icmp_seq = seq++;
		tvStart = (struct timeval* )pIcmp->icmp_data;
		gettimeofday(tvStart, NULL);
		pIcmp->icmp_cksum = checksum( (u8 *)pIcmp, PINGDATA+8);

		sendto(sockfd, sendBuf, PINGDATA+8, 0, (struct sockaddr* )&destAddr, sizeof(destAddr));

		uint lenIphd, lenIcmp;
		u16 sumRecv, sumCal;
		double deltSec;
		char ipSource[INET_ADDRSTRLEN];
		int n;
		memset(recvBuf, 0, MAXLINE);
		n = recvfrom(sockfd, recvBuf, MAXLINE, 0, NULL, NULL);
		if(n < 0)
		{
			if(errno == EINTR)
				continue;
			else
			{
				perror("recvfrom");
				exit(1);
			}
		}
		if( (tvEnd = malloc(sizeof(struct timeval))) < 0)
		{
			perror("malloc tvEnd:");
			exit(1);
		}
		gettimeofday(tvEnd, NULL);
		pIp = (struct ip*)recvBuf;
		lenIphd = (pIp->ip_hl)*4;
		lenIcmp = ntohs(pIp->ip_len)-lenIphd;//icmp字段長度
		pIcmp = (struct icmp*)( (u8*)pIp + lenIphd);//必須強制轉換!
		sumRecv = pIcmp->icmp_cksum;
		pIcmp->icmp_cksum = 0;
		sumCal = checksum( (u8*)pIcmp, lenIcmp);
		if(sumCal != sumRecv)
		{
			printf("checksum error\tsum_recv = %d\tsum_cal = %d\n",sumRecv, sumCal);
		}
		else
		{
			switch(pIcmp->icmp_type)
			{
				case ICMP_ECHOREPLY:
					{
						pid_t pidNow, pidRev;
						pidRev = (pIcmp->icmp_id);
						pidNow = getpid();
						if(pidRev != pidNow )
						{
							printf("pid not match!pin_now = %d, pin_rev = %d\n", pidNow, pidRev);
						}
						else
						{
							pingFlag = 1;
						}
						inet_ntop(AF_INET, (void*)&(pIp->ip_src), ipSource, INET_ADDRSTRLEN);
						tvStart = (struct timeval*)pIcmp->icmp_data;
						deltSec = (tvEnd->tv_sec - tvStart->tv_sec) + (tvEnd->tv_usec - tvStart->tv_usec)/1000000.0;
						printf("%d bytes from %s: icmp_req=%d ttl=%d time=%4.2f ms\n", lenIcmp, ipSource, pIcmp->icmp_seq, pIp->ip_ttl, deltSec*1000);//想用整型打印的話必須強制轉換!
						break;
					}
				case ICMP_TIME_EXCEEDED:
					{
						printf("time out!\n");
						pingFlag = -1;
						break;
					}
				case ICMP_DEST_UNREACH:
					{
						inet_ntop(AF_INET, (void*)&(pIp->ip_src), ipSource, INET_ADDRSTRLEN);
						printf("From %s icmp_seq=%d Destination Host Unreachable\n", ipSource, pIcmp->icmp_seq);
						pingFlag = -1;
						break;
					}
				default:
					{
						printf("recv error!\n");
						pingFlag = -1;
						break;
					}
			}
		}

	}
	alarm(0);
	sigaction(SIGALRM, &oldact, NULL);
	return pingFlag;
}
unsigned short checksum(unsigned char*buf, unsigned int len)//對每16位進行反碼求和(高位溢出位會加到低位),即先對每16位求和,在將得到的和轉爲反碼
{
	unsigned long sum = 0; 
	unsigned short *pbuf;
	pbuf = (unsigned short*)buf;//轉化成指向16位的指針
	while(len > 1)//求和
	{
		sum+=*pbuf++;
		len-=2;
	}
	if(len)//如果len爲奇數,則最後剩一位要求和
		sum += *(unsigned char*)pbuf;
	sum = (sum>>16)+(sum & 0xffff);//
	sum += (sum>>16);//上一步可能產生溢出
	return (unsigned short)(~sum);
}
void getMyIp(char *sourIP)
{
	FILE *ipFd;
	ipFd = popen("/sbin/ifconfig|grep 'inet '|grep -v 127|awk -F ':' '{print $2}'|cut -d ' ' -f1", "r");
	if(ipFd == NULL)
	{
		perror("popen");
		exit(0);
	}
	fscanf(ipFd, "%20s", sourIP);
//	fscanf(ipFd, "%INET_ADDRSTRLENs", sourIP);
}
void alarm_timer(int signo)
{
	pingFlag = -1;
	alarm(0);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章