端口扫描程序的设计与实现.docx
《端口扫描程序的设计与实现.docx》由会员分享,可在线阅读,更多相关《端口扫描程序的设计与实现.docx(19页珍藏版)》请在冰豆网上搜索。
端口扫描程序的设计与实现
端口扫描程序的设计与实现
摘要
运算机信息网络的进展加速了信息化时期的进程,可是随着社会网络化程度的增加,对运算机网络的依赖也愈来愈大,网络安全问题也日趋明显。
端口扫描技术是发觉安全问题的重要手腕之一。
本程序是在Windows系统中利用C语言用MFC完成的一个端口扫描程序。
此程序主要完成了TCPconnect()扫描和UDP扫描功能。
TCP扫描支持多线程,能对单个指定的主机进行扫描或对指定网段内的主机进行逐个扫描。
能扫描特定的部份端口号或对指定的端口段内的端口进行逐个扫描。
此端口扫描程序能快速地进行TCP扫描,准确地检测出对TCP协议开放的端口。
而对于UDP扫描只支持单线程,速度较慢。
扫描结果以列表的形式直观地展现出来。
关键词:
端口扫描、TCP扫描、UDP扫描、TCP多线程扫描
引言
课题的背景及意义
网络中每台运算机犹如一座城堡,这些城堡中,有些是对外完全开放的,有些却是大门紧闭的。
入侵者们是如何找到,并打开它们的城门呢?
这些城门究竟通向何处?
在网络中,把这些城堡的“城门”称之为运算机的“端口”。
端口扫描是入侵者搜索信息的几种常常利用方式之一,也正是这一种方式最容易暴露入侵者的身份和用意。
一般说来,扫描端口有以下目的:
判断目标主机上开放了哪些服务
判断目标主机的操作系统
若是入侵者掌握了目标主机开放了哪些服务,运行何种操作系统,他们就可以利用相应的手腕实现入侵。
而若是管理员先掌握了这些端口服务的安全漏洞,就可以采取有效的安全办法,防范相应的入侵。
端口扫描现状
运算机信息网络的进展加速了信息化时期的进程,可是随着社会网络化程度的增加,对运算机网络的依赖也愈来愈大,网络安全问题也日趋明显。
端口扫描技术是发觉安全问题的重要手腕之一。
一个端口就是一个潜在的通信通道,也就是一个入侵通道。
对目标运算机进行端口扫描,能取得许多有效的信息。
扫描器通过选用远程TCP/IP不同的端口的服务,并记录目标给予的回答,通过这种方式,能够搜集到很多关于目标主机的各类有效的信息,从而发觉目标机的某些内在的弱点。
系统设计
系统主要目标
本程序主要实现了:
简易的TCPconnect()扫描,支持多线程;
UDP扫描功能;
能对单个指定的主机进行扫描或扫描指定网段内的主机;
能扫描特定的部份端口号或对指定的端口段内的端口进行逐个扫描;
开发环境及工具
测试平台:
WindowsXPProfessional
利用软件:
VisualC++
开发语言:
C语言
功能模块与系统结构
作为端口扫描程序,第一需要完成的功能就是对于系统操作系统的服务端口进行扫描,返回扫描结果。
对于端口的扫描,包括对于本机系统服务端口,局域网内目标机系统,和远程IP的系统服务端口进行扫描。
有些时候,用户并非需要去扫描整个系统的所有端口,因为如此的话不仅会浪费大量的时刻,而且可能致使难以找到自己需要了解的端口的扫描结果。
所以,对于选择性地对端口进行扫描也超级重要。
这固然也是扫描程序需要实现的功能之一。
用户在等待扫描的时候,往往希望明白它的工作进度。
如此用户能够更好地控制自己的操作。
站在用户的角度试探,设置进度是程序需要完成的,如此就可以明白程序扫描的进度。
系统必需提供的服务是功能需求的大体,本着站在用户角度试探的原则,做出如上叙述需求,从简列举如下:
扫描功能;
地址选择功能;
端口选择功能;
进度显示功能;
端口扫描程序功能模块如下图所示:
程序运行流程图:
系统功能程序设计
本程序主要实现了简易的TCPconnect()扫描和UDP扫描功能,对TCP扫描支持多线程扫描,UDP扫描仅支持单线程。
获取本机IP
第一利用winsock中的gethostname()函数获取本地主机的标准主机名,再利用函数gethostbyname()主机名字和地址信息的hostent结构指针,最后通过inet_ntoa()函数将地址转化为字符形式返回给主调函数。
if(gethostname(szHostName,128)==0)
{
pHost=gethostbyname(szHostName);
for(i=0;pHost!
=NULL&&pHost->h_addr_list[i]!
=NULL;i++)
{/*对每一个IP地址进行处置*/
pszAddr=inet_ntoa(*(structin_addr*)pHost->h_addr_list[i]);
break;
}
}
分割字符串函数的实现
由于扫描结果是利用静态字符串保留的,所以最后显示的时候,需要用到分割字符串函数,来将字符串中扫描出的端口号分离出来显示。
本函数主如果通过Find()函数来查找用作分隔符的字符串在待查找的字符串中的位置,然后通过Add()函数将分隔符之间的字符保留进数组中,来达到分割字符的目的。
while(-1!
=pos){
if(-1==pre_pos)
pos=(division,pos);
else
pos=(division,(pos+1));
if(-1==pre_pos){
iFirst=0;
if(-1==pos)
nCount=();
else
nCount=pos;
}else{
iFirst=pre_pos+len;
if(-1!
=pos)
nCount=pos-pre_pos-len;
else
nCount=()-pre_pos-len;
}
(iFirst,nCount));
pre_pos=pos;
}
获取待扫描的IP地址
通过判断选择的哪个RadioButton的值,来选择从对应的IPAddress控件中读入用户输入的值,若是单个的IP则将开始地址StartAddr和结束地址EndAddr都赋值为IPAddress控件的值;若是IP范围,则第一个IP地址赋值给StartAddr,最后一个IP地址赋值给EndAddr。
voidCPortScanDlg:
:
setAddr(DWORD&StartAddr,DWORD&EndAddr)
{
switch
{
case0:
break;
case1:
break;
case2:
break;
default:
break;
}
}
获取待扫描的端口号
第一读入用户设置的允许的最大线程数。
再通过判断选择的哪个RadioButton的值,来选择从对应的EditBox控件中读入用户输入的值。
若是指定的端口号,则循环读入EditBox中的端口号(一个端口号占用一行,一次读入一行),每行的字符不超过9字符,再在读入的每行字符的末尾添加字符串结束标记’\0’,再通过atoi()函数把字符型转换为整型,寄存在概念的数组结构中,并保留端口号的总个数。
若是端口范围,则第一个端口号的值和最后一个端口号的值别离读入寄存在概念的结构体中。
voidCPortScanDlg:
:
setPort(tag_PORTS*pScanParam,int&ThreadNum)
{
ThreadNum=;
switch
{
case0:
{
shortnCount=0;
charbuff[10];
for(inti=0;i
{
intlen=buff,9);
if(len!
=0)
{
buff[len]='\0';
pScanParam->nArrOfPorts[nCount++]=atoi(buff);
}
}
pScanParam->nCount=nCount;
pScanParam->bSepecifiedPort=0;
break;
}
case1:
pScanParam->bSepecifiedPort=1;
pScanParam->iStartPort=;
pScanParam->iEndPort=;
break;
default:
break;
}
3.4.1指定端口号的初始化
第一试着读取文件中保留的端口号,若读入成功,则显示该文本文档中保留的值;若该文件不存在或读入异样,则显示默许设置的值。
voidconf_Port:
:
ReadDefaultPorts()
{
try
{
CStdioFilef("",CFile:
:
modeRead|CFile:
:
typeText);
CStrings,ss;
while(ss))
{
s+=ss;
s+="\r\n";
}
GetDlgItem(IDC_EDIT_DesignPort)->SetWindowText(s);
();
}
catch(...)
{
CStrings="13\r\n37\r\n123\r\n135\r\n139\r\n489\r\n1002";
GetDlgItem(IDC_EDIT_DesignPort)->SetWindowText(s);
}
}
3.4.2指定端口号的保留
在运行端口扫描时会将指定端口保留进文本文档中,若此文件不存在,则创建此文件。
voidconf_Port:
:
SaveDefaultPorts()
{
CStdioFilef("",CFile:
:
modeCreate|CFile:
:
modeWrite|CFile:
:
typeText);
charbuff[10];
for(inti=0;i<();i++)
{
intlen=(i,buff,9);
if(len!
=0)
{
buff[len]='\0';
(buff);
("\r\n");
}
}
();
}
TCPconnect()扫描
这是最大体的TCP扫描。
操作系统提供的connect()系统挪用,用来与每一个感兴趣的目标运算机的端口进行连接。
若是端口处于侦听状态,那么connect()就可以成功。
不然,那个端口是不能用的,即没有提供服务。
那个技术的一个最大的长处是,你不需要任何权限。
系统中的任何用户都有权利利用那个挪用。
另一个益处就是速度。
若是对每一个目标端口以线性的方式,利用单独的connect()挪用,那么将会花费相当长的时刻,你能够通过同时打开多个套接字,从而加速扫描。
利用非阻塞I/O允许你设置一个低的时刻用尽周期,同时观察多个套接字。
但这种方式的缺点是很容易被觉察,而且被过滤掉。
目标运算机的logs文件会显示连续串的连接和连接是犯错的服务消息,而且能专门快的使它关闭。
3.5.1大体原理
挪用connect()函数,按照返回值来判断端口是不是打开的,connect()函数返回0说明成立连接成功,说明该端口是打开的,就将该端口保留进静态字符串变量中,然后关闭连接,则线程数减一。
RunThreadNum是用来控制最大线程数量的。
UINTCPortScanDlg:
:
DoScanPort_TCP(LPVOIDlp)
{
………………………
ret=connect(sock,(structsockaddr*)&sin,sizeof(sin));
if(ret==0)
{
("%d",ntohs);
showout_tcp+=str+"|";
}
closesocket(sock);
RunThreadNum--;
………………………
}
3.5.2扫描多个主机多端口多线程的实现
扫描多个主机是利用for循环来实现逐个扫描的,多端口也是利用for循环来实现逐个扫描的。
最大线程数量是通过变量RunThreadNum的值来控制的,当其值大于允许的最大线程数maxthread时,便Sleep等待,直到存活的线程数小于maxthread,通过测试最大线程数maxthread设置为150效果较好,不宜大于200。
多线程的创建是通过挪用AfxBeginThread()函数来实现的。
for(nowAddr=StartAddr;nowAddr<=EndAddr;nowAddr++)
{
…………………
for(inti=0;i{
…………………
while(RunThreadNum>maxthread)
{Sleep(20);}
…………………………hThreadTcp=AfxBeginThread(DoScanPort_TCP,&inforabout,0,0,0,NULL);
CloseHandle(hThreadTcp);
Sleep(10);
}
………………………….
}
3.5.3扫描结果的显示
第一判断静态字符串变量showout_tcp是不是为空,若为空说明没有端口是打开的。
若存在打开的端口,则挪用分割字符串函数将打开的端口号提掏出来寄存到Array数组中,再别离显示出来。
if(showout_tcp!
="")
{
………………………
ArrayNum=Split_CString(showout_tcp,Array,"|");
temp=0;
while(temp{
…………………………(intRow,strId,strIp,strPort,ProtocolTCP,strOpen);
temp++;
intRow++;
}
}
UDP扫描
这种方式由于利用的是UDP协议。
由于那个协议很简单,所以扫描变得相对比较困难。
这是由于打开的端口对扫描探测并非发送一个确认,关闭的端口也并非需要发送一个错误数据包。
幸运的是,许多主机在你向一个未打开的UDP端口发送一个数据包时,会返回一个ICMP_PORT_UNREACH错误。
如此就可以发觉哪个端口是关闭的。
UDP和ICMP错误都不保证能抵达,因此这种扫描就不那么靠得住。
而且这种扫描方式是很慢的,因为RFC对ICMP错误消息的产生速度做了规定,而且本程序的UDP扫描只支持单线程。
3.6.1大体原理
第一利用socket()函数创建套接字,再用bind()函数绑定套接字,然后向扫描的目的主机的目的端口发送UDP数据包,再等待目的主机的目的端口是不是返回ICMP_PORT_UNREACH错误数据报,若收到返回的错误数据包,则说明该端口是关闭着的,不然该端口是打开的,再将打开的端口保留进静态数组中,以方便显示结果。
UINTCPortScanDlg:
:
DoScanPort_UDP(LPVOIDlp)3.6.23.6.33.6.4果没有则返回1给主调函数,表示没有收到返回的ICMP_PORT_UNREACH错误数据报,则说明该端口是打开的。
2.若是有可读的套接字,就再通过FD_ISSET()函数检查需要读取的套接字是不是在可读的套接字集合中:
(1)若是可读的套接字中没有需要读取的套接字,则返回1给主调函数,表示没有收到返回的ICMP_PORT_UNREACH错误数据报,则说明该端口是打开的。
(2)若是可读的套接字中有需要读取的套接字,则挪用recvfrom()函数从套接口上接收数据,若是没有数据,则说明该套接口不是用来传送数据的,则能够判断为返回的ICMP_PORT_UNREACH错误数据报,则说明该端口是关闭的。
USHORTCPortScanDlg:
:
Get_ICMPinfo(UINTICMP_SOCK,LPSOCKADDR_INSOURCE_ADDRESS)
{
……………………
FD_ZERO(&rset);
FD_SET(ICMP_SOCK,&rset);
if((RECV_MSG=select(ICMP_SOCK,&rset,NULL,NULL,&tv))>0)
{
if(FD_ISSET(ICMP_SOCK,&rset))
{
RECV_MSG=recvfrom(ICMP_SOCK,(char*)&icmpheader,sizeof(I_IPDATA),0,(LPSOCKADDR)&SOURCE_ADDRESS,&Recv_Slen);
if(RECV_MSG<=0)
return0;
}
}
return1;
}
测试报告
TCP扫描检测
4.1.1扫描本机
设置
(1)IP设置为本地IP():
(2)IP设置为:
2.端口设置为0-1024,最大线程数设置为100,协议选择TCP
3.扫描结果
(1)IP设置为本地IP()的扫描结果:
(2)IP设置为的扫描结果:
设置为扫描本地IP和设置为扫描时扫描结果会有不同:
扫描时扫描不出端口139,具体原因还不清楚。
4.1.2扫描网络中其他主机
1.设置IP(通过ping命令发觉IP为的主性能ping通)
2.端口设置为0-1024,最大线程数设置为100,协议选择TCP。
扫描结果如下:
由扫描结果可知能正常扫描,能正确扫描出能进行TCP连接的端口
4.1.3扫描IP段
1.设置IP
2.端口设置为扫描指定端口,最大线程数设置为100,协议选择TCP
3.扫描结果
由扫描结果可知能正常扫描,但由于没有先判断主机是不是在线,所以对不在线的主机也发送了连接请求,致使扫描速度较慢。
UDP扫描检测
4.2.1扫描本机
设置
(1)IP设置为本地IP():
(2)IP设置为:
2.由于是单线程,扫描速度较慢,端口设置为扫描指定端口,协议选择UDP
3.扫描结果:
IP设置为本地IP()的扫描结果和IP设置为的扫描结果相同:
用UDP协议扫描本机的端口,由于收不到发送的ICMP_PORT_UNREACH错误数据报,致使结果误判为所有端口都是打开的。
4.1.2扫描网络中其他主机
1.设置IP(通过ping命令发觉IP为的主性能ping通)
2.由于单线程,扫描速度较慢,端口设置为扫描指定端口,同扫描本机的设置。
协议选择UDP
3.扫描结果如下
由扫描结果可知能正常扫描。
TCP、UDP一路扫描
IP设置为,端口设置为指定端口,协议选择TCP和UDP,扫描结果如下:
由扫描结果可知能正常扫描,但由于TCP扫描利用多线程速度较快,而UDP扫描则是单线程,所以速度较慢,若同时进行两种协议的扫描,会使整体速度下降专门大,所以不建议选择两种协议同时进行扫描。
结论
本设计通过近1个多月的尽力,大体知足了一个端口扫描程序的大体要求。
完成后的程序实现了TCPconnect()扫描和UDP扫描功能,TCP扫描支持多线程,能大大加速扫描速度。
能对单个指定的主机进行扫描或扫描指定网段内的主机。
能对指定的端口段内的端口进行逐个扫描,或扫描特定的部份端口号,以避免在不需要了解的端口号上浪费时刻。
进度条显示,能方便用户随时明白扫描的进度。
系统设计期间,学习到很多课堂上没有的知识,还积累了很多实践经验,增强了动手能力和解决实际问题的能力。
通过这次的课程设计,对网络编程有了更深切的了解,进一步熟悉了TCP和UDP协议的内容,掌握了TCP、UDP扫描端口的大体原理,学会了运用sock套接字构造UDP数据包并发送,和如何接收ICMP数据包。
对编程思想有了进一步的体会,养成了一些良好的编程适应。
系统虽然完成,但还有很多不足的地方,希望自己能不断学习和实践,争取以后做得更好。
虽然此软件实现了支持多线程的TCPconnect()扫描功能,和单线程的UDP扫描功能。
但也还有很多不足的地方,如扫描功能单一:
不支持TCPSYN扫描、TCPFIN扫描和IP段扫描等功能,对UDP扫描不支持多线程功能,而且在扫描主机前没有发送ICMP报文去判断主机是不是在线,还需要进一步完善。
限于作者知识水平和经验有限,此程序还有许多有待完善和更正的地方,恳请列位老师和读者批评指正。
参考文献
[1]甘刚、闫丽丽、盛志伟、冼进.Linux/UNIX网络编程.中国水利水电出版社.2008年第1版。
[2]范建华、胥辉煌、张涛等译.TCP/IP详解卷1:
协议.机械工业出版社.2009年第1版。
[3]黄维通.VisualC++面向对象与可视化程序设计(第2版).清华大学出版社.2007年第2版
[4]郑莉、董渊、张瑞丰.C++语言程序设计(第3版).清华大学出版社.2004年第3版