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;
}