最后一个暑假,因为已经有一个项目在手,不想把自己整得太累,但又不能太闲,于是选择了一个可以拖拖踏踏做的简单事情,做一套端口扫描。这种平时集中精力几天就可以做完的事情,结果真是拖拖踏踏了很久才做完。
进入正题。端口扫描,即是通过一些扫描的手段来探知某些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
#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; }
对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扫描,通过创建原始套接字在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; }
关于应答处理线程与发送线程的通信与同步,这里我采用的是通过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的线程通信方式和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服务器啦。
整个端口扫描系统到此告一段落。人生最后的一个暑期也即将结束,珍惜最后的时间再好好玩玩。