網絡掃描程序的詳細分析與實現


1.網絡掃描簡介

  網絡掃描是一種自動化程序,用於檢測遠程或本地主機的弱點和漏洞。漏洞掃描是入侵防范最基本的工作,攻擊者正式利用各種漏洞入侵系統。借助自動化的掃描工作,在攻擊者之前發現漏洞問題,並給予相應的修正程序。

  一名攻擊者入侵系統,一般分為四個步驟:系統發現,漏洞探測,漏洞利用和痕跡清除。

  本文的重點就是在於系統發現與漏洞探測方面。

2.端口掃描技術

  端口掃描能夠用來查找目標主機已開放的端口,包括TCP和UDP端口。當前針對TCP端口的掃描技術有三種,分別為:全連接掃描,SYN掃描和FIN掃描。針對UDP端口的掃描技術一般是采用ICMP報文中端口不可達的信息來識別UDP端口是否開放。

  • 全連接掃描

  作為最基本的TCP掃描方式,它利用connect()函數,向每一個目標主機的端口發起連接,若端口處於偵聽狀態,則函數返回成功;否則可以判斷出該端口沒有開放。

  全連接掃描有兩個好處,首先它不許要得到系統權限,任何用戶都可以使用,另外一個是,可以采用多線程並發掃描,掃描的速度也不慢。

  • SYN掃描

  SYN掃描通常認為是“半開放”的掃描,這是因為掃描程序不必打開一個完全的TCP連接。掃描程序構造並發送一個SYN數據包,執行TCP三次握手的第一步,若返回一個SYN|ACK數據包,則表示端口處於偵聽狀態,否則返回一個RST數據包,表示端口沒有開放。這種方式需要root權限,並且它不會在主機上留下記錄。

  • FIN掃描

  FIN掃描與SYN掃描類似,也是構造數據包,通過識別回應來判斷端口狀態。發送FIN數據包后,如果返回一個RST數據包,則表示端口沒有開放,處於關閉狀態,否則,開放端口會忽略這種報文。這種方式難以被發現,但是這種方式可能不准確。

  • UDP的ICMP端口不可達掃描

  UDP掃描的方法並不常見。雖然UDP協議相對比較簡單,無論UDP端口是否開放,對於接受到的探測包,他本身默認是不會發送回應信息的。不過UDP掃描有一種方法,就是利用主機ICMP報文的回應信息來識別,當一個關閉的UDP端口發送一個數據包時,會返回一個ICMP_PORT_UNREACH的錯誤。但由於UDP不可靠,ICMP報文也是不可靠,因此存在數據包中途丟失的可能。

3.具體實現

  首先介紹全連接掃描模塊的設計。

  具體流程:

  1. 判斷端口是否處於掃描范圍
  2. 初始化套接字,發起連接
  3. 調用getservbyport函數,獲取端口映射的TCP服務
  4. 輸出結果
  5. 退出程序

實現代碼:

//全連接掃描
void scan(char *ip,int minport,int maxport)
{
    if(maxport<minport){
        printf("error:can't scan int this condition\n");
        return;
    }
    printf("test ip:%s,port:%d,port:%d\n",ip,minport,maxport);
    
    int i=0;
    printf("Now start scanning.....\n");
    for(i=minport;i<maxport;++i){
        struct sockaddr_in clientaddr;
        
        clientaddr.sin_family=AF_INET;
        clientaddr.sin_addr.s_addr=inet_addr(ip);
        clientaddr.sin_port=htons(i);
        
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0){
            perror("error:create socket\n");
            return;
        }
        
        int error=connect(sock,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
        if(error<0){
            printf("Port:%5d | Tatus:closed\n",i);
            fflush(stdout);//清空緩存
        }
        else{
            struct servent* sptr;
            if((sptr=getservbyport(htons(i),"tcp"))!=NULL)
                printf("Port:%5d | Server:%s | Status:open\n",i,sptr->s_name);
            else
                printf("Port:%5d | Status:open\n",i);
        }
        close(sock);
    }
}

  可以對上述代碼進行改進,可以加入多線程的的方式來解決掃描速度較慢的問題。

  在這里需要定義一個結構體,來為線程傳遞必要的參數。

typedef struct threadpara{
    char ip[20];
    int minport;
    int maxport;
}tp;

  這樣的話,我們就可以定義一個線程函數用於連接不同的端口。

void *conntthread(void *threadp)
{
    struct threadpara *pThreadpara=(struct threadpara*)threadp;
    
    int i;
    printf("minport:%d maxport:%d thread....\n",pThreadpara->minport,pThreadpara->maxport);
    
    for(i=pThreadpara->minport;i<pThreadpara->maxport;++i){
        struct sockaddr_in clientaddr;
        
        clientaddr.sin_family=AF_INET;
        clientaddr.sin_addr.s_addr=inet_addr(pThreadpara->ip);
        clientaddr.sin_port=htons(i);
        
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0){
            perror("error:create socket\n");
            return 0;
        }
        
        int error=connect(sock,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
        if(error<0){
            printf("Port:%5d | Tatus:closed\n",i);
            fflush(stdout);//清空緩存
        }
        else{
            struct servent* sptr;
            if((sptr=getservbyport(htons(i),"tcp"))!=NULL)
                printf("Port:%5d | Server:%s | Status:open\n",i,sptr->s_name);
            else
                printf("Port:%5d | Status:open\n",i);
        }
        close(sock);
    }
    pthread_exit(NULL);
}

  接下來,就是在主掃描程序中添加啟動線程的模塊,主要代碼如下:

int count=(maxport-minport)/thread_num;
    
    printf("count:%d num:%d ip:%s,port:%d,port:%d\n",count,thread_num,ip,minport,maxport);
    printf("Now start scanning....\n");
    
    int i;
    for(i=0;i<count;++i){
        struct threadpara threadpara;
        strcpy(threadpara.ip,ip);
        threadpara.minport=minport+count*i;
        if(i==thread_num-1)
            threadpara.maxport=maxport;
        else
            threadpara.maxport=threadpara.minport+count;
            
        printf("i:%d maxport:%d,minport:%d\n",i,threadpara.maxport,threadpara.minport);
        
        int temp;
        pthread_t threadid;
        temp=pthread_create(&threadid,NULL,conntthread,(void*)&threadpara);
        if(temp<0)
            printf("create thread error...\n");
        pthread_join(threadid,NULL);
    }

  接下來,再介紹一個SYN半連接掃描的例子。

  掃描模塊的設計流程圖如下:

  具體流程:

  1. 初始化套接字
  2. 設置信號量以及全局變量flag(控制掃描狀態)
  3. 啟動接收線程
  4. 構造SYN數據包,開始掃描

  首先,構造並發送syn包模塊的代碼如下: 

int sendSyn(int sendSocket,u_long sourceIP,struct sockaddr_in *dest)
{
    unsigned char netPacket[sizeof(struct tcphdr)];
    struct tcphdr* tcp;
    u_char * pPseudoHead;
    u_char pseudoHead[12+sizeof(struct tcphdr)];
    u_short tcpHeadLen;
    
    memset(netPacket,0,sizeof(struct tcphdr));    
    //構造syn數據包
    tcpHeadLen=htons(sizeof(struct tcphdr));
    tcp=(struct tcphdr*)netPacket;
    tcp->source=htons(10240);
    tcp->dest=dest->sin_port;
    tcp->seq=htonl(12345);
    tcp->ack_seq=0;
    tcp->doff=5;
    tcp->syn=1;
    tcp->window=htons(10052);
    tcp->check=0;
    tcp->urg_ptr=0;
    pPseudoHead=pseudoHead;
    
    memset(pPseudoHead,0,12+sizeof(struct tcphdr));
    memcpy(pPseudoHead,&sourceIP,4);
    
    pPseudoHead+=4;
    
    memcpy(pPseudoHead,&(dest->sin_addr),4);
    pPseudoHead+=5;
    
    memset(pPseudoHead,6,1);
    pPseudoHead++;
    
    memcpy(pPseudoHead,&tcpHeadLen,2);
    pPseudoHead+=2;
    
    memcpy(pPseudoHead,tcp,sizeof(struct tcphdr));
    tcp->check=checksum((u_short*)pPseudoHead,sizeof(struct tcphdr)+12);
    
    int temp=sendto(sendSocket,netPacket,sizeof(struct tcphdr),0,(struct sockaddr*)dest,sizeof(struct sockaddr_in));
    return temp;
}

  TCP校驗和代碼:

unsigned short checksum(unsigned short *addr,int len)//TCP校驗和
{
    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);
}

  接收返回數據包的代碼如下:

void* recv_packet(void* arg)
{
    struct sockaddr_in *in1;
    char *srcaddr;
    int size;
    u_char readbuff[1600];
    struct sockaddr from;
    int from_len;
    struct tcphdr* tcp;
    struct servent* sptr;
    tcp=(struct tcphdr*)(readbuff+20);
    int fd=*((int*)arg);
    
    while(1){
        if(flag==1)
            return 0;
            
        size=recvfrom(fd,(char*)readbuff,1600,0,&from,(socklen_t *)&from_len);
        if(size<40)
            continue;
        printf("data is ok...ack:seq:%u,destport:%u\n",ntohl(tcp->ack_seq),ntohs(tcp->dest));
        if((ntohl(tcp->ack_seq)!=123456)||(ntohs(tcp->dest)!=10240))
            continue;
        if(tcp->rst&&tcp->ack){
            printf("port:%5d | Status: closed\n",htons(tcp->source));
            continue;
        }
        if(tcp->ack&&tcp->syn){
            in1=(struct sockaddr_in*)&from;
            srcaddr=inet_ntoa(in1->sin_addr);
            printf("SERVER:%s\r",srcaddr);
        }
        if((sptr=getservbyport(tcp->source,"tcp"))!=NULL){
            printf("Port:%d Server:%s | Status: open\n",htons(tcp->source),sptr->s_name);
        }
        else
            printf("Port:%5d | Status:open\n",htons(tcp->source));
        fflush(stdout);
        continue;
    }
}

  掃描程序的主要程序:

void synscan(char* ip,int minport,int maxport)
{
    pthread_t tid;
    struct ifreq if_data;
    int fd;
    u_long addr_p;
    
    struct sockaddr_in clientaddr;
    fd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);//SOCK_RAW原始套接子(數據包式)
    if(fd<0){
        perror("error:create raw socket\n");
        return;
    }
    
    signal(SIGALRM,Alarm);
    
    strcpy(if_data.ifr_name,"eth0");
    if(ioctl(fd,SIOCGIFADDR,&if_data)<0){
        perror("error:ioctl\n");
        return;
    }
    memcpy((void*)&addr_p,(void*)&if_data.ifr_addr.sa_data+2,4);
    
    bzero(&clientaddr,sizeof(clientaddr));
    clientaddr.sin_family=AF_INET;
    clientaddr.sin_addr.s_addr=inet_addr(ip);
    
    printf("Now start scannning...\n");
    int err;
    err = pthread_create(&tid,NULL,recv_packet,(void*)&fd);
    if(err<0)
        perror("error:create thread\n");
    int i=minport;
    for(;i<maxport;++i){
        clientaddr.sin_port=htons(i);
        if(sendSyn(fd,addr_p,&clientaddr)<0){
            perror("error:send syn\n");
        }
        alarm(3);
    }
    pthread_join(tid,NULL);
}

 

最后附上之前的IP地址掃描代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM