计算机网络课程设计基于ICMPTCP的网段端口扫描C语言编写.docx
- 文档编号:12759984
- 上传时间:2023-04-21
- 格式:DOCX
- 页数:37
- 大小:325.55KB
计算机网络课程设计基于ICMPTCP的网段端口扫描C语言编写.docx
《计算机网络课程设计基于ICMPTCP的网段端口扫描C语言编写.docx》由会员分享,可在线阅读,更多相关《计算机网络课程设计基于ICMPTCP的网段端口扫描C语言编写.docx(37页珍藏版)》请在冰豆网上搜索。
计算机网络课程设计基于ICMPTCP的网段端口扫描C语言编写
课程设计任务书
【设计目的】
加深对TCP/IP协议的理解,熟悉Socket编程
【设计任务】
实现一个扫描器,必须能够完成以下所有功能:
使用TCPconnect、TCPSYN和TCPFIN进行端口扫描,使用ICMPecho扫描实现IP扫描。
并把结果记录下来。
【设计要求】
●Windows或Linux环境下,程序在单机上运行。
●演示:
使用端口扫描对一台主机进行扫描,并显示结果(一台主机上有哪些端口是打开的)。
对一个网段进行IP扫描,显示结果(一个网段内有哪些主机是开机的)。
●友好的用户界面
【设计要求】
●程序源代码,必须有详细的注释。
●项目设计报告。
网段和端口扫描程序
1、概述
1.1端口扫描
端口扫描是指利用TCP协议的面向连接的特性,使用本地计算机试图与目的主机的某个端口建立连接,以此来试探目的主机的一些端口的具体状态,如是否打开,若是打开的又运行什么样的程序(利用熟知端口很容易知道)。
端口扫描有很多技术实现。
其中最常用的有:
connect()扫描,TCPSYN扫描,TCPFIN扫描,TCPNULL扫描,XMAS-TREE扫描(圣诞树扫描)等等。
在本程序中使用的是connect()扫描。
1.2端口扫描常用技术简介
1.2.1connect()扫描
此扫描的原理是,本地计算机利用TCP协议的三次握手原理(RFC793)试图与网络上一台主机或服务器建立TCP连接。
如果目的主机或服务器回送SYN/ACK则说明该端口开放,否则该端口关闭。
具体的过程:
1.Client端发送SYN
2.Server端发送SYN/ACK(该端口开放),或Server端发送RST/ACK(该端口关闭),若无应答可能目的主机或服务器不可达。
3.Client端发送ACK,连接建立。
4.Client端主动断开连接,连接关闭。
过程图如下:
1.2.2SYN扫描
SYN扫描前两步与connect()扫描相同,只是最后一步,Client端发送的不是ACK确认报文而是RST报文,这样三次握手过程就没有完成,Client与Server也就没有建立TCP连接,因此前述过程不会被Sever端记录到系统日志中,扫描更加隐蔽。
过程图如下:
1.2.3XMAS-TREE扫描
通过发送带有以下标志位TCP数据包URG,PSH,FIN来试探主机。
在目标端口开放的情况下不放回任何信息。
端口开放:
发送URG/PSH/FIN,没有响应。
端口关闭:
1.发送URG/PSH/FIN,没有响应。
2响应RST。
1.3网段扫描
网段扫描是指利用ICMP(因特网控制报文协议)对某一网段的所有IP地址发送ICMP报文,测试IP地址所对应的主机具体情况(如是否开机等)。
所以对目的主机回答的ICMP报文进行分析是网段扫描的关键。
常见的报文类型有:
类型(type)
代码(code)
描述
差错
查询
0
0
回显回答
*
3
1
主机不可达
*
3
端口不可达
*
8
0
请求回显
*
二、需求分析
2.1设计功能
实现一个扫描器,使用TCPconnect、TCPSYN或TCPSYN进行端口扫描,使用ICMPecho扫描实现IP扫描,并记录结果。
2.2设计要求
Windows或Linux环境下,程序应在单机上运行。
使用端口扫描对一台主机进行扫描,并显示结果,在一定的端口范围内,该主机由哪些端口是打开的。
对一个网段进行IP扫描,显示结果,该网段的哪些主机是开机的。
友好的界面,便于用户操作,完成全部设计功能。
三、概要设计
3.1设计概念和处理流程
程序使用WindowsSocketsAPI编程,利用winsock2库函数提供的函数实现与主机间的连接,发送ICMP报文。
详见WinsockReferen:
用户首先选择要使用的功能,程序共有两大功能:
端口扫描和IP网段扫描。
端口扫描的主要功能有,根据用户输入的主机名或IP地址以及端口范围进行connect()扫描。
当用户输入完必要的信息并击确认后,程序进行扫描,不过不建议端口区间过于庞大,等待一段时间后用户界面会显示所指定IP地址主机的端口使用情况,哪些端口是打开的,哪些是关闭的。
IP网段扫描的主要功能有,用户输入某个网段起始IP地址和结束IP地址,确认后,程序根据所输入的网段号,对网段类的每一个IP地址发送ICMP请求回显报文,如果主机处于开机状态那么将会回送回答报文;如果主机不可达(在同一个网内),则就能够判断该IP所对应的主机处于关机状态。
当用户选择退出功能时,释放资源,程序关闭。
程序流程图
3.2结构设计
3.2.1数据结构及定义
所使用的宏定义,
#defineICMP_ECHO_REQUEST_TYPE8//ICMP请求类型
#defineICMP_ECHO_REQUEST_CODE0//ICMP请求代码
#defineICMP_ECHO_REPLY_TYPE0
#defineICMP_ECHO_REPLY_CODE0
#defineICMP_MINIMUM_HEADER8
IP数据报首部,首部固定20字节,数据结构定义如下:
typedefstructip_hdr
{
unsignedchariphVerLen;//版本号和头长度(各占4位)
unsignedcharipTOS;//服务类型
unsignedshortipLength;//封包总长度,即整个IP报的长度
unsignedshortipID;//封包标识,惟一标识发送的每一个数据报
unsignedshortipFlags;//标志
unsignedcharipTTL;//生存时间,就是TTL
unsignedcharipProtocol;//协议,可能是TCP、UDP、ICMP等
unsignedshortipChecksum;//校验和
unsignedlongipSource;//源IP地址
unsignedlongipDestination;//目标IP地址
}IP_HDR,*PIP_HDR;
IP数据报格式
ICMP报文首部格式,8个字节,数据结构定义如下:
typedefstructicmp_hdr
{
unsignedcharicmp_type;//类型
unsignedcharicmp_code;//代码
unsignedshorticmp_checksum;//检验和
unsignedshorticmp_id;//唯一请求ID,通常使用进程PID
unsignedshorticmp_sequence;//序列号
unsignedlongicmp_timestamp;//时间戳
}ICMP_HDR,*PICMP_HDR;
3.2.2接口函数
VoidInitializeWinsock(),初始化Winsock动态链接库。
VoidResolove(charhostname[]),解析主机名(或IP地址),该函数是公共接口函数,端口扫描和IP网段扫描都使用该函数解析地址。
函数首先判断用户输入的IP是十进制点分地址还是域名地址,然后分别调用inet_addr()和gethostbyname()库函数处理成unsignedlong型数据。
VoidResoloveIPAddr(charstarthost[],charendhost[],int*start,int*end),此函数用户IP网段扫描,将用户输入的起始IP地址和结束IP地址转换为int类型数据。
Char*Assemble(charstartehost[],intcur),将当前IP地址转化为字符串类型的IP地址。
voidSegmentScan(charstarthost[],charendhost[]),通过发送ICMP,检测某个网段上主机的具体状态。
函数的两个参数starthost[]、endhost[]分别代表用户给定的起始IP地址和结尾IP地址。
该函数内部调用了InitializeWinsock()、Resolove()、ResoloveIPAddr()、Assemble()、以及库函数sendto()和recvfrom()。
通过这些函数的调用实现了对指定网络区间的ICMP扫描。
connectScan(charstartport[],charendport[],hostname),该函数把用户输入的主机名(或IP地址)写入sin_addr.s_addr,起始端口写入sin_port中。
函数调用了InitializeWinsock()、Resolove()以及库函数connect()、shutdown()。
实现了利用connect()系统函数对指定主机端口区间扫描。
四、细设设计
4.1实现原理
4.1.1connect()函数端口扫描的原理
首先要明白TCP报文段的首部格式。
现在分别介绍各个字段的具体含义。
(1)源端口和目的端口各占两个字节。
端口是运输层与应用层的服务。
(2)序号占4个字节。
TCP把传送的数据流中的每一个字节都编上一个序号。
(3)确认号占4个字节。
是指期望对方的下一个报文段的数据的第一个字节的序号。
(5)数据偏移占4位。
它指出TCP报文段的数据起始处距离TCP报文段的起始距离有多远。
(6)保留。
(7)紧急比特URG当URG=1时,表明紧急指针字段有效。
告诉系统有紧急数据,应尽快发送。
(8)确认比特ACK只有当ACK=1时确认号字段才有效。
(9)推送比特PSH将数据流尽快的推送给应用进程。
而不是等到缓冲区满后才将数据送至应用程序。
(10)复位比特RST当RST=1时,表明TCP出现错误,必须释放连接,然后重新建立运输连接。
复位比特还用来拒绝一个非法报文或拒绝打开一个连接。
(11)同步比特SYN当SYN=1而ACK=0是表明这是一个请求连接报文,若同意连接则对方应该回送SYN=1,ACK=1。
(12)终止比特FIN。
用于释放一个连接。
(13)检验和占2个字节。
设客户进程运行在本地计算机上。
它首先向其TCP发出主动打开(activeopen)命令,表明要与某个IP地址的某个端口建立运输连接。
首先发送SYN=1,SEQ=X的报文给目标主机的某端口,当本地计算机收到SYN=1,ACK=1的报文时,表明对方主机同意连接,这是还需发送一个ACK以确认连接。
这样,经过三次握手,一条TCP连接链路就建立完成。
在Winsock2环境下,系统类库提供了connect()接口函数。
一下是函数原型:
connectfunction
intconnect(
_In_SOCKETs,
_In_conststructsockaddr*name,
_In_intnamelen
);
s表示一个为连接的socket,name表示一个指向sockaddr的结构体指针,namelen是sockaddr中name域的比特长度。
利用此函数,可以非常方便的连接一个套接字。
4.1.2ICMP网段扫描的原理
因特网控制报文协议ICMP(InternetControlMessageProtocol)【RFC792】。
ICMP允许主机或路由器报告差错情况和提供有关异常情况的报告。
ICMP报文作为IP层数据报的数据,加上数据报的首部,组成数据发送出去。
ICMP报文的种类有两种,即ICMP差错报告报文和ICMP询问报文。
ICMP报文的前4个字节是统一格式,共有三个字段:
类型,代码和检验和。
对网段的主机扫描就是利用了这三个字段。
根据主机发回的不同类型和代码对主机的状态进行判断。
ICMP的差错报文共有5种:
(1)终点不可达终点不可达分为:
网络不可达,主机不可达,协议不可达,端口不可达。
(2)源站抑制当路由器或主机由于拥塞而丢弃数据包时,就向源站发生源站抑制报文。
(3)时间超时当路由器手打生存时间为零的数据包时,除丢弃该数据报外,还向源站发送时间超过报文。
(4)参数问题
(5)改变路由(重定向)
对一个网段扫描时,主要利用
(1)和(3)两种报文。
ICMP的询问报文主要有4种:
(1)回送请求和回答(Ping)
(2)时间戳和回答(常用来记录一个主机到另一个主机的延迟)
(3)掩码请求和回答
(4)路由询问和回答
同样,主要利用
(1)和
(2)两种报文。
Winsock2提供sendto()和recvfrom()两个函数分别来发送IP层数据报。
函数原型分别为:
sendtofunction
intsendto(
_In_SOCKETs,
_In_constchar*buf,
_In_intlen,
_In_intflags,
_In_conststructsockaddr*to,
_In_inttolen
);
recvfromfunction
intrecvfrom(
_In_SOCKETs,
_Out_char*buf,
_In_intlen,
_In_intflags,
_Out_structsockaddr*from,
_Inout_opt_int*fromlen
);
结合原始套接字可以很方便做到将一个ICMP数据包封装到IP数据包中,以及从一个IP数据报中解析出包含ICMP报文。
下面对原始套接字做下简单的介绍。
原始套接字提供普通的TCP和UDP套接字所不提供的一下3个功能。
v有了原始套接字,进程可以读写ICMP和IGMP等分组。
举例来说ping程序就是使用原始套接字收发ICMP分组,网段扫描也是基于这个原理。
v有了原始套接字,进程可以读写内核不处理其协议字段的IPv4数据包。
v有了原始套接字,进程还可以使用IP_HDRINCL套接字选项自行构造IPv4首部。
这一点是非常强大的。
在利用Winsock编写原始套接字时要注意程序应该取得管理员权限,因为大多数操作系统对原始套接字的创建有较为严格的限制。
如果没有取得管理员权限,那么在创建原始套接字的时候会报出10013--Permissiondenied.的错误,从而无法创建套接字。
4.2函数实现
4.2.1初始化winsock动态链接库
voidInitializeWinsock()
{
intstatus;
WSADATAwsa;
if(status=WSAStartup(MAKEWORD(2,2),&wsa)!
=0)
{
printf("FailedtoWSAStartup():
%d.\n",WSAGetLastError());
exit(EXIT_FAILURE);
}
}
函数调用Winsock2提供的WSAStartup()函数,初始化动态链接库。
4.2.2初始化ICMP首部
voidInitIcmpHeader(ICMP_HDR*icmp_hdr)
{
charbuff[sizeof(ICMP_HDR)+32];
//ICMP报文类型,类型为8,代码为0
icmp_hdr->icmp_type=ICMP_ECHO_REQUEST_TYPE;//请求回显
icmp_hdr->icmp_code=ICMP_ECHO_REQUEST_CODE;
icmp_hdr->icmp_id=(USHORT)GetCurrentProcessId();
icmp_hdr->icmp_checksum=0;
icmp_hdr->icmp_sequence=0;
icmp_hdr->icmp_timestamp=GetTickCount();
//往ICMP头部填充信息
memset(&buff[sizeof(ICMP_HDR)],'E',32);
}
函数为ICMP报文首部中各个字段赋值,达到初始化的目的。
其中比较重要的语句有
icmp_hdr->icmp_type=ICMP_ECHO_REQUEST_TYPE;//请求回显
icmp_hdr->icmp_code=ICMP_ECHO_REQUEST_CODE;
这样赋值后,就把ICMP报文设置成ICMP询问报文,回送请求和回答。
4.2.3地址解析
voidResolove(charhostname[])
{
if(isdigit(hostname[0]))//判断主机名是否为数字
{
//printf("执行inet_addr()...\n");
dest.sin_addr.s_addr=inet_addr(hostname);//将主机地址写入s_addr
//printf("%d\n",WSAGetLastError());
}
elseif((host=gethostbyname(hostname))!
=0)//判断所给主机名是否与host中的一致
{
//printf("执行gethostbyname()...\n");
strncpy((char*)&dest.sin_addr,(char*)host->h_addr_list[0],sizeofdest.sin_addr);
//printf("完成。
\n");
}
else
{
printf("解析主机失败。
\n");
exit(EXIT_FAILURE);
}
}
根据用户输入地址字符串转化成相应的地址字段,写入dest.sin_addr中。
如果用户输入的点分十进制。
则就会调用相应的inet_addr()函数,否则调用gethostbyname(),在写入地址字段。
4.2.4计算检验和
unsignedshortchecksum(unsignedshort*buffer,intsize)
{
unsignedlongcksum=0;
while(size>1)
{
cksum+=*buffer++;
size-=sizeof(USHORT);
}
if(size)
{
cksum+=*(UCHAR*)buffer;
}
cksum=(cksum>>16)+(cksum&0xffff);
cksum+=(cksum>>16);
return(USHORT)(~cksum);
}
根据TCP/IP协议,IP数据报在传输过程前必须计算检验和,对收到的数据也要计算检验和。
该函数实现了首部的检验和计算。
4.2.5网段扫描
主要语句如下:
for(i=startport;i<=endport;i++)
{
sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//创建一个Socket
if(sock==INVALID_SOCKET)
{
printf("Createsocketfailed:
%d\n",WSAGetLastError());
exit(EXIT_FAILURE);
}
//主机字节序转换为网络字节序
dest.sin_port=htons(i);
//用此Socket连接目的主机,核心代码
status=connect(sock,(structsockaddr*)&dest,sizeofdest);
if(status==SOCKET_ERROR)//连接失败
{
switch(WSAGetLastError())
{
case10060:
printf("%s\t%d\tERROR\t\tConnectiontimedout\n",hostname,i);
break;
case10061:
printf("%s\t%d\tERROR\t\tConnectionrefused\n",hostname,i);
break;
default:
printf("%s\t%d\tERROR\tCode:
\n",WSAGetLastError());
break;
}
//fflush(stdout);
}
else//连接成功
{
printf("%s\t%d\tPORTOPEN\n",hostname,i);
//关闭收发服务
if(shutdown(sock,SD_BOTH)==SOCKET_ERROR)
{
printf("Failedtoshutdowntheconnect:
%d\n",WSAGetLastError());
exit(EXIT_FAILURE);
}
}
closesocket(sock);//关闭Socket,回收资源
//WSACleanup();
}
利用一个for(;;)循环,对确定的起始和终止IP地址的内的所有可能存在的主机发送,请求回显的ICMP报文,并对返回的ICMP报文进行分析,提取出type和code字段,根据ICMP报文格式所定义的数值,对各个主机判断,并打印信息。
4.2.6端口扫描
for(i=startport;i<=endport;i++)
{
sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//创建一个Socket
if(sock==INVALID_SOCKET)
{
printf("Createsocketfailed:
%d\n",WSAGetLastError());
exit(EXIT_FAILURE);
}
//主机字节序转换为网络字节序
dest.sin_port=htons(i);
//用此Socket连接目的主机,核心代码
status=connect(sock,(structsockaddr*)&dest,sizeofdest);
if(status==SOCKET_ERROR)//连接失败
{
switch(WSAGetLastError())
{
case10060:
printf("%s\t%d\tERROR\t\tConnectiontimedout\n",hostname,i);
break
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 计算机网络 课程设计 基于 ICMPTCP 网段 端口扫描 语言 编写