最后一个暑假,因为已经有一个项目在手,不想把自己整得太累,但又不能太闲,于是选择了一个可以拖拖踏踏做的简单事情,做一套端口扫描。这种平时集中精力几天就可以做完的事情,结果真是拖拖踏踏了很久才做完。

  进入正题。端口扫描,即是通过一些扫描的手段来探知某些ip的端口情况,主要为探知端口的开放情况与端口的应用类型。

  由于各个设备的系统与防火墙的差异,某一单一的端口扫描方式难以应用于所有的系统,而且扫描方并不知道扫描对方的系统情况。为了应对不同情况,有必要采用多种扫描方式协同扫描。在此我使用了connect、tcp syn、tcp fin、helo 这几种方式的扫描。具体的介绍可参考 端口扫描技术

  希望自己的扫描能够高效而准确,于是对扫描策略做了如下设计:

    syn、fin扫描方式用于快速端口扫描,使用多线程发送扫描包,另有一处于混杂模式的socket用户接收处理所有的扫描响应。有人说syn扫描方式不适合使用多线程,他的解释是:“使用多线程容易发生数据报的串位现象,也就是原来应该这个线程接收的数据报被另 一个线程接收,接收后,这个数据报就会被丢弃,而等待线程只好在超时之后再发送一个SYN数据报,等待应答。这样,所用的时间反而会增加”。但这并不是问题,注意我这里用的是混杂的原始套接字,所有的数据包都不会漏,只要对这些数据包的处理逻辑写好就没有任何问题。

    快速端口扫描设计图:

    connect、helo方式用于精确扫描,使用多线程connect端口,同时使用这些线程获取反馈信息。最后处理数据并录入到数据库,这里的重点在于确认端口的开放与判断端口的应用类型。通过helo方式,可以获取到端口返回的应用信息,对返回的信息进行处理识别即可。

    精确端口扫描设计图:

 

  扫描结果都将存入数据库:

 

  UI:哈哈,自从在linux上编程,除了我电脑虚拟机的linux,我的服务器上的linux都是没ui的,因此再也没写过任何的C++UI,如果还是用命令行查看端口扫描的结果,未免太寒酸,而且只能自己使用。于是用php粗略地做了个UI。

 

但是使用php执行端口扫描进程需要http服务具有管理员权限,因为我的c++扫描程序使用到了原始套接字,linux下一般必须有root权限才能使用原始套接字。如果没法通过加sudo的方式执行扫描进程,那么就需要修改httpd的源码重新编译安装后再将httpd配置root权限。具体可参考:httpd转root

 

以下讲解一些关键性的代码:

  专用数据库代码的封装:

  

#ifndef SQLUSE_H_INCLUDED
#define SQLUSE_H_INCLUDED

#include <mysql/mysql.h>
#include <string>
using namespace std;

#define LOGINSIZE 32
#define SQLFORMATSIZE 512

#define data_statu_end     0
#define data_statu_wait    1
#define data_statu_rewait  2
#define data_statu_error   3
#define data_statu_run     4

#define data_type_unkonw   0

struct model_data
{
    int id;
    string created_at;
    string updated_at;
    string ip;
    int port;
    string info;
    int type;
    int statu;
};

char *sqlencode(const char *str,const int len);
class sqluse
{
    char sqlhost[LOGINSIZE];
    unsigned int sqlport;
    char dbname[LOGINSIZE];
    char username[LOGINSIZE];
    char userpass[LOGINSIZE];
    char charset[LOGINSIZE];
    MYSQL *con;

public:
    char insertdataformat[SQLFORMATSIZE];
    char waitdataselectformat[SQLFORMATSIZE];
    char statudataupdateformat[SQLFORMATSIZE];
    char infodataupdateformat[SQLFORMATSIZE];

    char suprunidformat[SQLFORMATSIZE];
    char supresultformat[SQLFORMATSIZE];

    bool getsqlset();
    bool getformatdata();
    bool connect();
    bool setcharset();

public:


    sqluse();
    ~sqluse();
    MYSQL_RES *query(char *sql);

    bool insertdata(string ip,int port,int type=1,int statu=1);
    int getwaitdata(model_data *arr,int datanum=1);
    bool resetdata(int id);
    bool seterrordata(int id,int statu);
    bool updateinfodata(int id,string info="",int type=data_type_unkonw);
};

#endif
sqluse.h
#include "sqluse.h"
#include <cstdlib>
#include <cstdio>
#include <cstring>

const char encodechar[]="',;";
char *sqlencode(const char *str,const int len)
{
    if(str==NULL)
    {
        puts("vv");
        return NULL;
    }
    int cnt=0;
    int elen=strlen(encodechar);
    for(int i=0;i<len;i++)
    {
        for(int j=0;j<elen;j++)
        {
            if(str[i]==encodechar[j])
            {
                cnt++;
            }
        }
    }
    char *s=new char[len+cnt*2+1];
    int slen=0;
    for(int i=0;i<len;i++)
    {
        s[slen]=str[i];
        for(int j=0;j<elen;j++)
        {
            if(str[i]==encodechar[j])
            {
                s[slen++]='\\';
                s[slen]=str[i];
                break;
            }
        }
        slen++;
    }
    s[slen]='\0';
    //free(str);
    return s;
}


sqluse::sqluse()
{
    if(!getsqlset())
    {
        puts("a fail sqluse");
        return;
    }
    if(!getformatdata())
    {
        return;
    }
    if(!connect())
    {
        puts("connect to sqlsever error");
    }

    setcharset();
}


bool sqluse::getsqlset()
{
    FILE *f;
    if(!(f=fopen("..//config//sqlset","r")))
    {
        puts("sqlsetdata read fail!");
        return false;
    }
    fscanf(f,"%s",sqlhost);
    fscanf(f,"%u",&sqlport);
    fscanf(f,"%s",dbname);
    fscanf(f,"%s",username);
    fscanf(f,"%s",userpass);
    fscanf(f,"%s",charset);
    return true;
}


static bool readformat(const char *dir,char *format)
{
    FILE *f;
    if(!(f=fopen(dir,"r")))
    {
        puts("formatdata read fail!");
        return false;
    }
    format[0]='\0';
    char s[128]="";
    while(fgets(s,sizeof(s),f))
    {
        strcat(format,s);
    }

    fclose(f);
    return true;
}
bool sqluse::getformatdata()
{
    readformat("..//sqldata//insertdataformat",insertdataformat);
    readformat("..//sqldata//waitdataselectformat",waitdataselectformat);
    readformat("..//sqldata//statudataupdateformat",statudataupdateformat);
    readformat("..//sqldata//infodataupdateformat",infodataupdateformat);
/*
    readformat("..//sqldata//suprunidformat",suprunidformat);
    readformat("..//sqldata//supresultformat",supresultformat);
*/
    return true;
}

bool sqluse::connect()
{
    con=NULL;
    con=mysql_init(NULL);
    if(con==NULL)
    {
        puts("sql init error");
        return false;
    }

    if(!mysql_real_connect(con,sqlhost,
                           username,userpass,
                           dbname,
                           sqlport,NULL,0
                           ))
    {
        printf("sql error: %s\n",mysql_error(con));
        return false;
    }
    puts("connect to sqlsever secc");
    return true;
}

bool sqluse::setcharset()
{
    char s[32];
    sprintf(s,"SET NAMES %s",charset);
    puts(s);
    query(s);

    return true;
}

sqluse::~sqluse()
{
    mysql_close(con);
}


MYSQL_RES *sqluse::query(char *sql)
{
    int state=mysql_query(con,sql);
    if(state!=0)
    {
        connect();
        state=mysql_query(con,sql);
    }
    if(state!=0)
    {
        printf("sql error: %s\n",mysql_error(con));
        connect();
        return NULL;
    }
    return mysql_store_result(con);
}

bool sqluse::insertdata(string ip,int port,int type,int statu)
{
    char querystr[256];
    sprintf(querystr,insertdataformat,ip.c_str(),port,type,statu);
    if(NULL==query(querystr))
    {
        return false;
    }
    return true;
}

int sqluse::getwaitdata(model_data *arr,int datanum)
{
    char querystr[256];
    sprintf(querystr,waitdataselectformat);
    MYSQL_RES *res=query(querystr);
    if(res==NULL)
    {
        return 0;
    }
    MYSQL_ROW row;
    int i=0;
    while((row=mysql_fetch_row(res))!=NULL)
    {
        if(i>=datanum)
        {
            break;
        }

        sscanf(row[0],"%d",&arr[i].id);
        arr[i].ip=row[1];
        sscanf(row[2],"%d",&arr[i].port);
        sscanf(row[3],"%d",&arr[i].statu);
/*
printf("%d %s %d %d\n",arr[i].id,
               arr[i].ip.c_str(),
               arr[i].port,
               arr[i].statu);
*/
        sprintf(querystr,statudataupdateformat,data_statu_run,arr[i].id);
        query(querystr);
        /*if(NULL==query(querystr))
        {
            continue;
        }*/
        i++;
    }
    return i;
}

bool sqluse::resetdata(int id)
{
    char querystr[64];
    sprintf(querystr,statudataupdateformat,data_statu_wait,id);
    if(NULL==query(querystr))
    {
        return false;
    }
    return true;
}

bool sqluse::seterrordata(int id,int statu)
{
    int restatu=data_statu_error;
    if(statu==data_statu_wait)
    {
        restatu=data_statu_rewait;
    }

    //== restatu=statu+1;

    char querystr[64];
    sprintf(querystr,statudataupdateformat,restatu,id);

    if(NULL==query(querystr))
    {
        return false;
    }
    return true;
}

bool sqluse::updateinfodata(int id,string pinfo,int type)
{
    pinfo=sqlencode(pinfo.c_str(),pinfo.length());
    char querystr[2048];
    sprintf(querystr,infodataupdateformat,pinfo.c_str(),type,data_statu_end,id);
    if(NULL==query(querystr))
    {
        return false;
    }
    return true;
}
sqluse.cpp

 

  对socket进行专门用于syn扫描的封装:

  1 class msock_synscan
  2 {
  3 public:
  4     SOCKET sock;
  5     sockaddr_in addr;
  6     socklen_t addrlen;
  7     sockaddr_in saddr;
  8 
  9     char buf[sizeof(PSD_HEADER)+sizeof(TCP_HEADER)+20];
 10     char *synbuf;
 11 
 12     char rbuf[2048];
 13 
 14     msock_synscan(unsigned short port=55555,const char *devname = "eth0")
 15     {
 16         synbuf=buf+sizeof(PSD_HEADER);
 17         sock=socket(AF_INET,SOCK_RAW, IPPROTO_TCP);
 18         if(sock==INVALID_SOCKET)
 19         {
 20             puts("socket build error");
 21             exit(-1);
 22         }
 23 
 24         addr.sin_family=AF_INET;
 25 
 26         saddr.sin_addr.s_addr=getlocalip(devname);
 27         saddr.sin_port=htons(port);
 28     }
 29 
 30     void setioctl(bool x)
 31     {
 32         const int flag = 1;
 33         if (setsockopt(sock,IPPROTO_IP, IP_HDRINCL, &flag, sizeof(flag)) < 0)
 34         {
 35             perror("setsockopt fail \n");
 36             exit(-1);
 37         }
 38         return;
 39 
 40 #ifdef WIN32
 41         if(!x)
 42         {
 43             return;
 44         }
 45         unsigned long ul = 1;
 46         ioctlsocket(sock, FIONBIO, (unsigned long*)&ul);
 47 #else
 48         fcntl(sock, F_SETFL, O_NONBLOCK);
 49 #endif
 50     }
 51 
 52     bool setip(string ip)
 53     {
 54         hostent *hname=gethostbyname(ip.c_str());
 55         if(!hname)
 56         {
 57             puts("can't find address");
 58             return false;
 59         }//puts(inet_ntoa(addr.sin_addr));
 60 #ifdef WIN32
 61         addr.sin_addr.S_un.S_addr=*(u_long *)hname->h_addr_list[0];
 62 #else
 63         addr.sin_addr.s_addr=*(u_long *)hname->h_addr_list[0];
 64 #endif
 65         return true;
 66     }
 67 
 68     void setport(int port)
 69     {
 70         addr.sin_port=htons(port);
 71     }
 72 
 73     int mbind()
 74     {
 75         return bind(sock,(struct sockaddr *)&addr,sizeof(sockaddr));
 76     }
 77 
 78     bool sendsyn()
 79     {
 80         TCP_HEADER *th=(TCP_HEADER *)synbuf;
 81         th->SourPort=saddr.sin_port;
 82         th->DestPort=addr.sin_port;
 83         th->SeqNo=0;
 84         th->AckNo=0;
 85         th->HLen=(char)80;
 86         th->Flag=SYN;
 87         th->WndSize=htons(8192);
 88         th->ChkSum=0;
 89         th->UrgPtr=htons(0);
 90 
 91         PSD_HEADER *ph = (PSD_HEADER *)buf;
 92         ph->saddr=saddr.sin_addr.s_addr;
 93         //inet_addr("192.168.0.4");
 94         ph->daddr=addr.sin_addr.s_addr;
 95         ph->mbz=(char)0;
 96         ph->protocal=IPPROTO_TCP;
 97         ph->nextlen=ntohs(20);
 98         ph->plug=0;
 99 
100         th->ChkSum=check_sum((unsigned short *)ph,sizeof(PSD_HEADER)+sizeof(TCP_HEADER));
101         int slen=sendto(sock,synbuf,sizeof(TCP_HEADER),0,(sockaddr *)&addr,sizeof(sockaddr));
102         //printf("(send: %d)\n",slen);
103 
104         if(slen>=sizeof(TCP_HEADER))
105         {
106             return true;
107         }
108         return false;
109     }
110 
111     tcpflag recvack()
112     {
113         tcpflag fg;
114 
115         int rlen=recvfrom(sock,rbuf,sizeof(rbuf),0,(sockaddr *)&addr,&addrlen);
116 
117         if(rlen>=(sizeof(IP_HEADER)+sizeof(TCP_HEADER)))
118         {
119             IP_HEADER *iph = (IP_HEADER *)rbuf;
120             if(iph->proto==IPPROTO_TCP)
121             {
122                 TCP_HEADER *th = (TCP_HEADER *)(rbuf+sizeof(IP_HEADER));
123                 //printf("(%02x %02x)\n",th->DestPort,saddr.sin_port);
124                 if(th->DestPort == saddr.sin_port)
125                 {
126                     //printf("(_%02x_)",th->Flag);
127 
128                     fg.ip=iph->sourceIP;
129                     fg.port=th->SourPort;
130 
131                     if(th->Flag&SYN)
132                     {
133                         //puts("syn");
134                         fg.issyn=true;
135                     }
136                     if(th->Flag&ACK)
137                     {
138                         //puts("ack");
139                         fg.isack=true;
140                     }
141                     if(th->Flag&RST)
142                     {
143                         //puts("rst");
144                         fg.isrst=true;
145                     }
146                 }
147 
148             }
149         }
150 
151         return fg;
152     }
153 
154     int mclose()
155     {
156 #ifdef WIN32
157         return closesocket(sock);
158 #else
159         return close(sock);
160 #endif
161     }
162 
163 };

  

  1 struct IP_HEADER
  2 {
  3     unsigned char header_len:4;
  4     unsigned char version:4;
  5     unsigned char tos;
  6     unsigned short total_len;
  7     unsigned short ident;
  8     unsigned short flags;
  9     unsigned char ttl;
 10     unsigned char proto;
 11     unsigned short cheacksum;
 12     unsigned int sourceIP;
 13     unsigned int destIP;
 14 };
 15 struct TCP_HEADER
 16 {
 17 #define SYN (char)0x02
 18 #define ACK (char)0x10
 19 #define RST (char)0x04
 20     unsigned short SourPort;
 21     unsigned short DestPort;
 22     unsigned int SeqNo;
 23     unsigned int AckNo;
 24     unsigned char HLen;
 25     unsigned char Flag;
 26     unsigned short WndSize;
 27     unsigned short ChkSum;
 28     unsigned short UrgPtr;
 29 };
 30 struct PSD_HEADER
 31 {
 32     unsigned long saddr;
 33     unsigned long daddr;
 34     char mbz;
 35     char protocal;
 36     unsigned short nextlen;
 37     unsigned int plug;
 38 };
 39 
 40 
 41 
 42 
 43 static unsigned long getlocalip(const char *devname)
 44 {
 45     char ipaddr[20];
 46     struct sockaddr_in addr;
 47     struct hostent *host;
 48     struct ifreq req;
 49     int sock;
 50     char *temp_ip = NULL;
 51     sock = socket(AF_INET, SOCK_DGRAM, 0);
 52     strncpy(req.ifr_name, devname, IFNAMSIZ);
 53     if ( ioctl(sock, SIOCGIFADDR, &req) < 0 )
 54     {
 55         printf("get local addr error\n");
 56         return NULL;
 57     }
 58     temp_ip = (char *)inet_ntoa(*(struct in_addr *) &((struct sockaddr_in *) &req.ifr_addr)->sin_addr);
 59     strcpy(ipaddr,temp_ip);
 60     close(sock);
 61     //puts(ipaddr);
 62     return inet_addr(ipaddr);
 63 }
 64 
 65 struct tcpflag
 66 {
 67     unsigned long ip;
 68     unsigned short port;
 69     bool issyn;
 70     bool isack;
 71     bool isrst;
 72     tcpflag()
 73     {
 74         init();
 75     }
 76     void init()
 77     {
 78         issyn=false;
 79         isack=false;
 80         isrst=false;
 81     }
 82     bool isdata()
 83     {
 84         return issyn||isack||isrst;
 85     }
 86     bool isre()
 87     {
 88         return isack||isrst;
 89     }
 90 };
 91 
 92 static unsigned short check_sum(unsigned short *addr, int len)
 93 {
 94     int nleft = len;
 95     int sum = 0;
 96     unsigned short *w = addr;
 97     short answer = 0;
 98     while (nleft > 1)
 99     {
100         sum += *w++;
101         nleft -=2;
102     }
103     if (nleft == 1)
104     {
105         *(unsigned char *)(&answer) = *(unsigned char *)w;
106         sum += answer;
107     }
108     sum = (sum >> 16) + (sum & 0xffff);
109     sum += (sum >> 16);
110     answer = ~sum;
111     return answer;
112 }
syn 依赖项

 

  该类可用于进行syn扫描,通过创建原始套接字在ip层之上构造自己的tcp syn包并发送。接收函数会接收所有的包并筛选出所在扫描范围类的应答包,因为接收是混杂的,所以使用时只需要一个单独的类用接收即可。此处需要注意校验和的计算,因为syn包是不带任何数据的,所以值计算伪首部与syn包的校验和。

 

  syn扫描的实现类:

  

 1 #ifndef _SYNSCAN_H_
 2 #define _SYNSCAN_H_
 3 
 4 #include "msock.h"
 5 #include <pthread.h>
 6 
 7 struct threadinfo_syn//用于线程通讯的结构体
 8 {
 9     pthread_t thid;
10     unsigned short id;
11 
12     string destip;
13     unsigned short destport;
14     void *classptr;
15 
16     bool isfree;
17     bool isendthread;
18     tcpflag flag;
19 
20     void setwait()
21     {
22         flag.init();
23         isfree = true;
24     }
25     void setcmd(tcpflag _flag)
26     {
27         flag=_flag;
28     }
29     void init(int _id, void *_classptr)
30     {
31         id=_id;
32         classptr=_classptr;
33         setwait();
34     }
35     void setdest(string _destip,unsigned short _destport)
36     {
37         destip=_destip;
38         destport=_destport;
39         isfree = false;
40     }
41 };
42 
43 #define syn_threadnum 100
44 
45 class scan_syn
46 {
47 public:
48     unsigned short localport;
49     string devname;
50 
51     string startip,endip;
52     unsigned short startport,endport;
53 
54     unsigned short threadnum;//thread num of synsend
55     threadinfo_syn *info;
56 
57     scan_syn(unsigned short port=65222,string dev = "eth0");
58     ~scan_syn();
59     void setworkinfo(string _startip,string _endip,unsigned short _startport,unsigned short _endport);
60     void cmain();
61 
62 
63 
64     int findthreadbyflag(tcpflag flag);
65 
66     bool isendackthread;
67     pthread_t ackthid;
68     static void *thread_synsend(void *arg);
69     static void *thread_ackrecv(void *arg);
70 
71     threadinfo_syn *waitfreesendthread();
72 };
73 
74 
75 #endif // _SYNSCAN_H_
#include "synscan.h"

scan_syn::scan_syn(unsigned short port,string dev)
{
    devname=dev;
    localport=port;

    threadnum=syn_threadnum;
    info=new threadinfo_syn[threadnum];

    for(int i=0;i<threadnum;i++)
    {
        info[i].init(i,this);
    }
}
scan_syn::~scan_syn()
{
    delete[](info);
}

int scan_syn::findthreadbyflag(tcpflag flag)
{
    for(int i=0;i<threadnum;i++)
    {
        if(info[i].isfree)
        {
            continue;
        }
        if(inet_addr(info[i].destip.c_str())==flag.ip&&
           ntohs(info[i].destport)==flag.port)
        {
            return i;
        }
    }
    return -1;
}




void scan_syn::setworkinfo(string _startip,string _endip,unsigned short _startport,unsigned short _endport)
{
    startip=_startip;
    endip=_endip;
    startport=_startport;
    endport=_endport;
}

static bool isipend(string endip,string ip)
{
    return ntohl(inet_addr(ip.c_str()))
            <=ntohl(inet_addr(endip.c_str()));
}
static string nextip(string ip)
{
    in_addr_t temp=ntohl(inet_addr(ip.c_str()));
    temp=htonl(temp+1);
    in_addr inaddr;
    inaddr.s_addr=temp;
    ip=inet_ntoa(inaddr);

    return ip;
}
void scan_syn::cmain()
{
    int error;
    error=pthread_create(&ackthid,NULL,scan_syn::thread_ackrecv,this);
    if(error!=0)
    {
        printf("create thread ackrecv error:%s\n",strerror(error));
        exit(-1);
    }

    for(int i=0;i<threadnum;i++)
    {
        error=pthread_create(&info[i].thid,NULL,scan_syn::thread_synsend,&info[i]);
        if(error!=0)
        {
            printf("create thread synsend %d error:%s\n",i,strerror(error));
            exit(-1);
        }
    }

    for(string ip=startip; isipend(endip,ip); ip=nextip(ip))
    {
        for(unsigned short port=startport; port<=endport; port++)
        {
            threadinfo_syn *stinfo=waitfreesendthread();
            if(stinfo!=NULL)
            {
                stinfo->setdest(ip,port);
                //printf("%s %d\n",ip.c_str(),port);
            }
            else
            {
                printf("..\n");
            }
        }
    }


    getchar();
}

void *scan_syn::thread_synsend(void *arg)
{
    threadinfo_syn *info = (threadinfo_syn *)arg;
    scan_syn *that = (scan_syn *)info->classptr;
    msock_synscan synsock(that->localport,
                          that->devname.c_str());
    //synsock.setioctl(true);

    while(!info->isendthread)
    {
        if(info->isfree)
        {
            continue;
        }
        synsock.setip(info->destip);
        synsock.setport(info->destport);
        int cnt=5;
        while(!synsock.sendsyn()&&cnt--)
        {
            sleep(1);
        }

        if(cnt>0)
        {
            //SEND SECC
            cnt=5;
            while(!info->flag.isdata()&&cnt--)
            {
                sleep(1);
            }
            if(cnt>0)
            {
                //UPLOAD ON SQL
                printf("ip:%s port:%d issyn:%d isack:%d isrst:%d\n",
                       info->destip.c_str(),info->destport,
                       info->flag.issyn,
                       info->flag.isack,
                       info->flag.isrst);
            }
        }
        else
        {
            printf("send syn error  ip:%s port:%d\n",
                 info->destip.c_str(),info->destport);
        }

        info->setwait();
    }

    synsock.mclose();

}

void *scan_syn::thread_ackrecv(void *arg)
{
    scan_syn *that=(scan_syn *)arg;

    msock_synscan acksock(that->localport,
                          that->devname.c_str());
    tcpflag reflag;

    while(!that->isendackthread)
    {
        reflag=acksock.recvack();
        if(reflag.isre())
        {
            int threadid = that->findthreadbyflag(reflag);
            if(threadid>=0)
            {
                that->info[threadid].setcmd(reflag);
            }
        }
    }

    acksock.mclose();
}

threadinfo_syn *scan_syn::waitfreesendthread()
{
    int cnt=60;
    while(cnt--)
    {
        for(int i=0;i<threadnum;i++)
        {
            if(info[i].isfree)
            {
                return info+i;
            }
        }
        sleep(1);
    }
    return NULL;
}
syn_scan.cpp

  关于应答处理线程与发送线程的通信与同步,这里我采用的是通过threadinfo_syn结构体来实现双方的通讯与协调,每个发包线程有一个对应的id以及目的ip port,接收线程接收到应答包后,会根据ip port 在threadinfo_syn中查找到对应的线程id并通知其应答结果。当发包线程超时后会将自己从threadinfo_syn中释放掉。

  在这里理论上系统能支持多大的线程数和socket数,就能开多大的线程。但实际上过多的并发syn会被一些防火墙所察觉。

 

  connect扫描类实现:

 1 #ifndef _SYNSCAN_H_
 2 #define _SYNSCAN_H_
 3 
 4 #include "../include/msock.h"
 5 #include "../include/sqluse.h"
 6 #include <queue>
 7 using namespace std;
 8 
 9 struct threadinfo_connect
10 {
11     pthread_t thid;
12     unsigned short id;
13 
14     string destip;
15     unsigned short destport;
16 
17     model_data model;
18 
19     void *classptr;
20 
21     bool isfree;
22     bool isendthread;
23 
24     void setwait()
25     {
26         isfree = true;
27     }
28     void init(int _id, void *_classptr)
29     {
30         id=_id;
31         classptr=_classptr;
32         setwait();
33     }
34     void setdest(string _destip,unsigned short _destport)
35     {
36         destip=_destip;
37         destport=_destport;
38         isfree = false;
39     }
40 };
41 
42 #define connect_threadnum 2
43 #define hellowinfo "lxsb"
44 #define timeoutvalue 2
45 
46 class scan_connect
47 {
48 public:
49     unsigned short threadnum;
50     threadinfo_connect *info;
51 
52     queue<model_data> taskque;
53 
54     scan_connect();
55     ~scan_connect();
56     bool isendmain;
57     void cmain();
58 
59     bool isendgettaskthread;
60     pthread_t gettaskthid;
61     static void *thread_gettask(void *arg);
62     static void *thread_connect(void *arg);
63 
64     threadinfo_connect *waitfreesendthread();
65 };
66 
67 #endif //_SYNSCAN_H_
  1 #include "connectscan.h"
  2 
  3 scan_connect::scan_connect()
  4 {
  5     threadnum=connect_threadnum;
  6     info=new threadinfo_connect[threadnum];
  7 
  8     for(int i=0;i<threadnum;i++)
  9     {
 10         info[i].init(i,this);
 11     }
 12 }
 13 
 14 
 15 scan_connect::~scan_connect()
 16 {
 17     delete[](info);
 18 }
 19 
 20 void scan_connect::cmain()
 21 {
 22     int error;
 23     isendgettaskthread=false;
 24     error=pthread_create(&gettaskthid,NULL,scan_connect::thread_gettask,this);
 25     if(error!=0)
 26     {
 27         isendgettaskthread=true;
 28         printf("create thread gettask error:%s\n",strerror(error));
 29         exit(-1);
 30     }
 31 
 32     for(int i=0;i<threadnum;i++)
 33     {
 34         error=pthread_create(&info[i].thid,NULL,scan_connect::thread_connect,&info[i]);
 35         if(error!=0)
 36         {
 37             printf("create thread connect %d error:%s\n",i,strerror(error));
 38             exit(-1);
 39         }
 40     }
 41 
 42 
 43     while(!this->isendmain)
 44     {
 45         if(this->taskque.empty())
 46         {
 47             sleep(1);
 48             continue;
 49         }
 50         model_data taskdata=taskque.front();
 51         taskque.pop();
 52 
 53         threadinfo_connect *stinfo=waitfreesendthread();
 54         if(stinfo!=NULL)
 55         {
 56             stinfo->model=taskdata;
 57             stinfo->setdest(taskdata.ip,taskdata.port);
 58         }
 59         else
 60         {
 61             printf("..\n");
 62         }
 63     }
 64 }
 65 
 66 
 67 void *scan_connect::thread_gettask(void *arg)
 68 {
 69     puts("thget st");
 70     scan_connect *that=(scan_connect *)arg;
 71 
 72     sqluse sql;
 73 
 74     model_data tdata[10];
 75     int num;
 76     while(!that->isendgettaskthread)
 77     {
 78         num=sql.getwaitdata(tdata,sizeof(tdata));
 79         //printf("(%d)\n",num);
 80         for(int i=0;i<num;i++)
 81         {
 82             printf("%d %s %d %d\n",tdata[i].id,
 83                tdata[i].ip.c_str(),
 84                tdata[i].port,
 85                tdata[i].statu);
 86             if(that->taskque.size()<connect_threadnum*10)
 87             {puts("tdata in");
 88                 that->taskque.push(tdata[i]);
 89             }
 90             else
 91             {
 92                 sql.resetdata(tdata[i].id);
 93                 sleep(1);
 94             }
 95         }
 96         sleep(1);
 97     }
 98 
 99 }
100 
101 void *scan_connect::thread_connect(void *arg)
102 {
103     threadinfo_connect *info = (threadinfo_connect *)arg;
104     scan_connect *that = (scan_connect *)info->classptr;
105 
106     sqluse sql;
107     char rebuf[256];
108     int relen;
109     while(!info->isendthread)
110     {
111         if(info->isfree)
112         {
113             sleep(0.1);
114             continue;
115         }
116         printf("i were gan %s %d\n",info->destip.c_str(),info->destport);
117 
118         msock_tcp tcpsock;
119         tcpsock.settimeout(timeoutvalue);
120         tcpsock.setip(info->destip);
121         tcpsock.setport(info->destport);
122         if(SOCKET_ERROR==tcpsock.mconnect())
123         {
124             sql.seterrordata(info->model.id,info->model.statu);
125         }
126         else
127         {
128             tcpsock.msend(hellowinfo,sizeof(hellowinfo));
129             relen=tcpsock.mrecv(rebuf,sizeof(rebuf));
130             if(relen<0)
131             {
132                 relen=0;
133             }
134             if(relen>=sizeof(rebuf))
135             {
136                 relen=sizeof(rebuf)-1;
137             }
138             rebuf[relen]='\0';
139             sql.updateinfodata(info->model.id,rebuf,data_type_unkonw);
140             tcpsock.mclose();
141         }
142 
143         info->setwait();
144     }
145 }
146 
147 threadinfo_connect *scan_connect::waitfreesendthread()
148 {
149     int cnt=60;
150     while(cnt--)
151     {
152         for(int i=0;i<threadnum;i++)
153         {
154             if(info[i].isfree)
155             {
156                 return info+i;
157             }
158         }
159         sleep(1);
160     }
161     return NULL;
162 }
connectscan.cpp

  connectscan的线程通信方式和synscan相同。

  connectscan通过循环的数据库查询线程来获取connect任务,connect成功后便更新数据库中的该ip poet为开放状态,然后通过helo方式发送tcp helo包,并接收返回的数据,将该数据进行判断处理后更新到数据库中。

  这里每个connect线程所要做的就是在接到命令后connect目标并进行通讯,然后上传数据库,因为是是精确扫描,且都被syn已经过滤了大量无效端口,所以connect的任务量很小,对效率要求不高,这里可以采用阻塞的方式来进行connectscan,把超时时间圈定一个稍小的值即可。

 

  最后是UI部分:

  

 1 <?php
 2 $dsn = 'mysql:host=xxx;dbname=portscan';
 3 $db = new PDO($dsn, 'xxx', 'xxx');
 4 
 5 $selectformat="select * from data 
 6         where `ip`>='%s' and `ip`<='%s' and `port`>='%s' and `port`<='%s'";
 7 $selectbyinfoformat="select * from data 
 8         where `info` like '%s'";
 9 ?>
10 
11 <?php
12 $scanthreadnum=100;
13 ?>
14 
15 
16 
17 <?php
18 $startip='0.0.0.0';
19 $endip='255.255.255.255';
20 $startport=0;
21 $endport=65535;
22 $keyinfo='';
23 
24 function getdata($name,$data='')
25 {
26     if(isset($_GET[$name]))
27     {
28         if(strlen($_GET[$name])>0)
29         {
30             return $_GET[$name];
31         }
32     }
33     return $data;
34 }
35 
36 $startip=getdata('startip',$startip);
37 $endip=getdata('endip',$endip);
38 $startport=getdata('startport',$startport);
39 $endport=getdata('endport',$endport);
40 $keyinfo=getdata('keyinfo',$keyinfo);
41 
42 /*
43 echo $startip.'<br/>';
44 echo $endip.'<br/>';
45 echo $startport.'<br/>';
46 echo $endport.'<br/>';
47 echo $keyinfo.'<br/>';
48 */
49 
50 ?>
51 
52 <table>
53     <tr>
54         <td><a href="main.php">home</a></td>
55         <td><a href="view.php">view</a></td>
56     </tr>
57 </table>
 1 <?php
 2 include 'include.php';
 3 ?>
 4 
 5 
 6 
 7 <?php
 8 
 9 $questr='';
10 if(strlen($keyinfo)>0)
11 {
12     $questr=sprintf($selectbyinfoformat,'%'.$keyinfo.'%');
13 }
14 else
15 {
16     $questr=sprintf($selectformat,$startip,$endip,$startport,$endport);
17 }
18 $re=$db->query($questr);
19 
20 ?>
21 <table border="1">
22     <tr>
23         <th>ip</th>
24         <th>port</th>
25         <th>info</th>
26         <th>update</th>
27     </tr>
28 <?php
29 while($row=$re->fetch())
30 {
31 ?>
32     <tr>
33         <td><?php echo $row['ip'] ?></td>
34         <td><?php echo $row['port'] ?></td>
35         <td><?php echo $row['info'] ?></td>
36         <td><?php echo $row['updated_at'] ?></td>
37     </tr>
38 <?php
39 }
40 ?>
41 </table>
 1 <?php
 2 include 'include.php';
 3 
 4 ?>
 5 
 6 
 7 
 8 <?php
 9 //该处需要以root权限执行portscan进程
10 $execstr='./portscan '.$startip.' '.$endip.
11             ' '.$startport.' '.$endport.' '.$scanthreadnum.' '.$keyinfo;
12 
13 echo $execstr;
14 exec($execstr,$output,$revar);
15 var_dump($output);
16 echo $revar;
17 //等待执行过程的时间可能过长,可以使用ajax实时反馈扫描信息
18 echo 'scan secc';
19 ?>

  这里的查询部分可以通过对端口的描述keyinfo进行查询,比如要找ftp端口,将keyinfo设为ftp即可。

  这样我也可以用来扫描小朋友的mc服务器啦。

  整个端口扫描系统到此告一段落。人生最后的一个暑期也即将结束,珍惜最后的时间再好好玩玩。

posted on 2015-08-15 13:30  wchrt  阅读(1358)  评论(1编辑  收藏  举报