一.概述
該程序在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);
}