C实现的非阻塞方式命令行端口扫描器源码
该实例是一个C实现的基于命令行模式端口扫描代码,并且是以非阻塞方式来实现对IP和端口的连接测试。为了大家使用和学习方便,已在代码中尽可能多的地方加入了注释,相信对于帮助大家理解C端口扫描有很大帮助。
具体功能代码如下:
#include<afxext.h> #include<winsock.h> //编译时需使用的库 #pragmacomment(lib,"wsock32.lib") //select()成员定义 #defineZERO(fd_set*)0 //变量定义 intmaxth,scanok,scannum; intportip,hoststart,hoststop,startport,endport;//定义了开始I和结束P地址,开始和结束端口 longsearchnum,searched; voidusage(char*);//定义显示使用方法函数 voidplayx(int);//定义状态提示函数 voidsetip2(char*);//定义设置IP函数 voidcustomport(char*,char*,char*);//定义自定义扫描端口函数 voidportscannow(int);//定义端口扫描扫描 intmain(intargc,char*argv[]) { WSADATAwsadata; //清屏 system("cls.exe"); //显示版本信息 printf("\r\n==============命令行端口扫描器PortScannerV1.0=============="); //检查输入 if((argc<3)||(argc>4)) { //显示帮助提示 usage(argv[0]); return-1; } //检测是否为port扫描 if(!(stricmp(strlwr(argv[1]),"-p")==0)) { usage(argv[0]); return-1; } //程序初始化 if(WSAStartup(MAKEWORD(1,1),&wsadata)!=0)//如果初始化错误 { printf("\r\nWsatartuperror");//出错信息 return-1; } //端口扫描参数转换 //如果参数为三个 if(argc==3) { //直接设置IP setip2(argv[2]); } //如果参数为四个 else if(argc==4) { //进入定制端口扫描处理 customport(argv[0],argv[2],argv[3]); } //参数过多显示帮助 else { usage(argv[0]); return-1; } //扫描端口开始 portscannow(argc); WSACleanup(); return0; } //帮助提示函数 voidusage(char*prog) { printf("Usage:%s<Option>",prog); printf("\r\n\n<Option>:"); printf("\r\n-p[Port|StartPort-EndPort]<HostName|IP|StartIP-EndIP>"); printf("\r\n\nExample:"); printf("\r\n%s-p192.168.0.1",prog); printf("\r\n%s-p192.168.0.1-192.168.0.254",prog); printf("\r\n%s-p21-80192.168.0.1",prog); printf("\r\n%s-p21-80192.168.0.1-192.168.0.254\r\n",prog); return; } //进度提示 voidplayx(intplay=0) { //进度条 char*plays[12]= { "|", "/", "-", "\\", "|", "/", "-", "\\", "|", "/", "-", "\\", }; if(searchnum!=0) { for(inti=0;i<=3;i++) { printf("=%s=%d%sCompleted.\r",plays,searched*100/(searchnum+1),"%"); Sleep(5); } } else { printf("=%s=\r",plays[play]);//显示进度 Sleep(10); } } //设置IP voidsetip2(char*cp) { inthost; structhostent*testhost; char*startip="",*endip=""; //判断是否为192.168.0.1-192.168.0.254形式的输入 if(strstr(cp,"-")&&strlen(cp)>15&&strlen(cp)<32) { //提取出结束IP endip=strchr(cp,'-')+1; //提取出开始IP strncpy(startip,cp,strlen(cp)-strlen(strchr(cp,'-'))); //给控制要扫描IP段的变量赋值 hoststart=ntohl(inet_addr(startip)); hoststop=ntohl(inet_addr(endip)); } else { //取得输入的主机地址 testhost=gethostbyname(startip); //如果地址不存在 if(!testhost) { WSACleanup(); printf("\r\nCan'tgetipof:%s",cp); exit(-1); } //给控制要扫描IP段的变量赋值 memcpy(&host,testhost->h_addr,4); hoststop=hoststart=ntohl(host); } } //测试线程是否已满 voidTestThread(intthread=200) { for(;;) { playx(); //测试线程是否已满 if(maxth>thread) Sleep(100); elsebreak; } return; } //等待线程结束函数 voidWaitThreadEnd() { //延时 Sleep(6000); //显示等待提示 printf("\r\r\n"); printf("Wait(%d)Threadend...\r\n",maxth); for(;;) { //判断所有线程是否已经结束 if(maxth>0) { //延时等待线程序结束 Sleep(100); playx(); continue; } elsebreak; } printf("\r\n"); return; } //定制端口扫描参数 voidcustomport(char*cp,char*cp2,char*cp3) { intintport; char*checker; //处理要扫描的端口 //扫描开始端口变量赋值 startport=atoi(cp2); //扫描结束端口变量赋值 endport=atoi(cp2); //判断是否21-80形式 if(strstr(cp2,"-")) { intport=atoi(checker=strchr(cp2,'-')+1); if(intport>0&&intport<65536) //扫描结束端口变量赋值 endport=intport; } //端口大小判断 if(startport<0||startport>65536||endport<0||endport>65535) { usage(cp); exit(-1); } //处理ip地址 setip2(cp3); } //端口扫描函数 UINTportscan(LPVOIDport) { intaddr=portip;//取得要扫描的地址 intsock; structfd_setmask; structtimevaltimeout; structsockaddr_inserver; unsignedlongflag=1; //创建一个sock sock=socket(AF_INET,SOCK_STREAM,0); //创建sock失败处理 if(sock==INVALID_SOCKET) { printf("\r\nSockError:%s",WSAGetLastError()); maxth--; return-1; } //给sock成员赋值 server.sin_family=AF_INET; server.sin_addr.s_addr=htonl(addr);//要扫描的地址 server.sin_port=htons(short(port));//要扫描的端口 //显示进度 playx(); //调用ioctlsocket()设置套接字为非阻塞模式 if(ioctlsocket(sock,FIONBIO,&flag)!=0) { //设置失败处理 printf("\r\nSockError:%s",WSAGetLastError()); closesocket(sock); maxth--; return-1; } //调用connect()连接远程主机端口 connect(sock,(structsockaddr*)&server,sizeof(server)); timeout.tv_sec=18;//超时限制为18秒 timeout.tv_usec=0; FD_ZERO(&mask);//清空集合mask FD_SET(sock,&mask);//将sock放入集合mask中 //用select()处理扫描结果 switch(select(sock+1,ZERO,&mask,ZERO,&timeout)) { case-1: { printf("\r\nSelect()error"); maxth--; return-1; } //sock超时处理 case0: { maxth--; closesocket(sock); return-1; } default: if(FD_ISSET(sock,&mask)) { //禁止sock发送和接受数据 shutdown(sock,0); //设置输出结果格式 printf("[Found:]%sPort:%dopen.\r\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port)); //关闭sock closesocket(sock); scanok++; maxth--; return1; } } return0; } //扫描开始主函数 voidportscannow(intxp) { intsport; char*timenow,timebuf[32]; //默认扫描的端口 char*ports[32]={ "21", "22", "23", "25", "53", "79", "80", "110", "111", "113", "123", "135", "139", "143", "443", "512", "513", "514", "515", "540", "1080", "1433", "1521", "1524", "3306", "3389", "5631", "6000", "6112", "8000", "8080", "12345"//这里你也可以自己要扫描的端口 }; //显示扫描开始的时间 timenow=_strtime(timebuf); printf("\r\nPortScanStartTime:%s\r\n\n",timenow); //计数器初始化. maxth=0; scanok=0; scannum=0; searched=0; //计算要扫描的端口数量 searchnum=hoststop-hoststart+1; if(xp==3) searchnum=searchnum*32; if(xp==4) searchnum=searchnum*(endport-startport+1); //端口扫描开始 for(portip=hoststart;portip<=hoststop;portip++,scannum++) { //*.*.*.0和*.*.*.255地址处理 if((portip%256)==0||(portip%256)==255) { if(xp==3) searchnum=searchnum-32; if(xp==4) searchnum=searchnum-(endport-startport+1); scannum--; playx(); continue; } if(i>11)i=0; //默认端口扫描 //scan192.168.0.1 //scan192.168.0.1-192.168.0.254 if(xp==3) { for(sport=0;sport<32;sport++,maxth++,searched++) { //测试当前线程是否大于180 TestThread(180); //产生新的线程处理端口扫描 CWinThread*pthread=AfxBeginThread(portscan,LPVOID(atoi((char*)ports[sport]))); //延时 Sleep(120); } } //自端口扫描 //scan-p21192.168.0.1 //scan-p21-80192.168.0.1-192.168.0.254 if(xp==4) {//计算要扫描的端口 sport=endport-startport; if(sport>500) { //扫描自的端口 for(sport=startport;sport<=endport;sport++,maxth++,searched++) { TestThread(2000); //产生新的线程处理端口扫描 CWinThread*pthread=AfxBeginThread(portscan,LPVOID(sport)); //延时 Sleep(10); } } else { //扫描自的端口 for(sport=startport;sport<=endport;sport++,maxth++,searched++) { //测试当前线程是否大于250 TestThread(250); //产生新的线程处理端口扫描 CWinThread*pthread=AfxBeginThread(portscan,LPVOID(sport)); //延时 Sleep(100); playx(); } } } } //等待所有的线程结束 WaitThreadEnd(); //显示端口扫描结束时间 timenow=_strtime(timebuf); printf("\r\nPortScanEndTime:%s",timenow); printf("\r\nScan%dHostscompleted.Open%dPorts!\r\n",scannum,scanok); }
为了测试该端口扫描器,可以使用如下连接测试代码进行测试,源码如下:
/*此函数实现判断m_server的m_port端口是否可以连上,超时限制为nTimeOut秒*/ BOOLConnectTest(char*m_server,intm_port) { structhostent*host=NULL; structsockaddr_insaddr; unsignedints=0; BOOLret; time_tstart; interror; host=gethostbyname(m_server); if(host==NULL)returnFALSE; saddr.sin_family=AF_INET; saddr.sin_port=htons(m_port); saddr.sin_addr=*((structin_addr*)host->h_addr); if((s=socket(AF_INET,SOCK_STREAM,0))<0){ returnFALSE; } fcntl(s,F_SETFL,O_NONBLOCK); if(connect(s,(structsockaddr*)&saddr,sizeof(saddr))==-1){ if(errno==EINPROGRESS){//itisintheconnectprocess structtimevaltv; fd_setwritefds; tv.tv_sec=m_nTimeOut; tv.tv_usec=0; FD_ZERO(&writefds); FD_SET(s,&writefds); if(select(s+1,NULL,&writefds,NULL,&tv)>0){ intlen=sizeof(int); //下面的一句一定要,主要针对防火墙 getsockopt(s,SOL_SOCKET,SO_ERROR,&error,&len); if(error==0)ret=TRUE; elseret=FALSE; }elseret=FALSE;//timeoutorerrorhappen }elseret=FALSE; } elseret=TRUE; close(s); returnret; }